diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..b57922af --- /dev/null +++ b/.dockerignore @@ -0,0 +1,18 @@ +node_modules +.next +.git +.gitignore +*.md +.env +.env.* +!.env.example +.vscode +.idea +coverage +playwright-report +test-results +backups +*.log +*.tmp +Dockerfile +docker-compose*.yml diff --git a/.env.example b/.env.example new file mode 100644 index 00000000..be61efc2 --- /dev/null +++ b/.env.example @@ -0,0 +1,45 @@ +# ========================================== +# ERP System Environment Configuration +# ========================================== + +# Database Configuration +DB_HOST=127.0.0.1 +DB_PORT=3306 +DB_USER=your_db_user +DB_PASSWORD=your_secure_password +DB_NAME=vnerp + +# JWT Configuration +# Generate a strong secret: node -e "console.log(require('crypto').randomBytes(64).toString('hex'))" +JWT_SECRET=your-super-secret-jwt-key-change-this-in-production + +# Application Configuration +NODE_ENV=development +DEBUG_DB=true + +# 开发环境允许的源(逗号分隔,next.config.ts allowedDevOrigins) +# 示例:DEV_ORIGINS=192.168.0.155,192.168.0.157,localhost +DEV_ORIGINS=localhost + +# CORS 允许的源(默认 * 用于开发;生产应配置具体域名,如 https://erp.example.com) +CORS_ALLOW_ORIGIN=* + +# Event Bus Configuration +# 事件总线类型: +# - memory: 纯内存模式(默认),事件不持久化,重启丢失。适合开发/单实例部署 +# - db: 数据库持久化模式,事件落库 domain_event_outbox 表,OutboxPoller 自动消费。 +# 适合生产环境,保证事件不丢失,支持重试/死信/指数退避 +# 切换需重启进程;db 模式需先执行 migrations 中 domain_event_outbox 表建表脚本 +EVENT_BUS_TYPE=memory + +# Redis Configuration(强烈建议生产环境配置) +# 用途:token 黑名单/refresh token/用户级 token 撤销/缓存共享 +# 未配置时降级为进程内内存(仅单实例可用,revokeAllUserTokens 不跨进程生效) +# docker-compose 启动:docker-compose up -d redis +REDIS_URL=redis://localhost:6379 + +# Setup API 开关(高危:建表/初始化数据接口) +# - true: 允许管理员通过 POST /api/setup/create-tables 调用(需 admin token) +# - false: 接口完全禁用,返回 404(生产环境必须为 false 或不设置) +# 仅在需要初始化数据库时临时开启,完成后立即关闭 +ALLOW_SETUP_API=false diff --git a/.env.production.example b/.env.production.example new file mode 100644 index 00000000..4189b3ee --- /dev/null +++ b/.env.production.example @@ -0,0 +1,35 @@ +# ========================================== +# ERP System Production Environment Configuration +# 复制为 .env.production 并填入真实值(切勿提交到 git) +# ========================================== + +# Database Configuration +DB_HOST=mysql +DB_PORT=3306 +DB_USER=vnerp_app +DB_PASSWORD=change-this-to-a-strong-password +DB_NAME=vnerp + +# MySQL Root(仅 docker-compose 初始化用) +MYSQL_ROOT_PASSWORD=change-this-to-a-strong-root-password + +# JWT Configuration +# 生成: node -e "console.log(require('crypto').randomBytes(64).toString('hex'))" +JWT_SECRET=replace-with-64-byte-hex-secret + +# Application Configuration +NODE_ENV=production +DEBUG_DB=false + +# CORS 允许的源(生产必须配置具体域名,禁止 *) +CORS_ALLOW_ORIGIN=https://erp.your-domain.com + +# 开发环境允许的源(生产通常不需要,留空即可) +DEV_ORIGINS= + +# Redis Configuration(docker-compose 内置 redis 服务) +REDIS_URL=redis://redis:6379 + +# Next.js +PORT=5000 +HOSTNAME=0.0.0.0 diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index a3783d47..c8daac6c 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,7 +1,3 @@ -## PR 标题 - - - ## 变更说明 简要描述本次 PR 的改动内容。 @@ -20,41 +16,10 @@ Closes # -## 事件发布检查清单 - -> 本次变更若涉及领域事件(Domain Event)发布,请逐项确认;Transactional Outbox 模式要求聚合写入与事件写入必须在同一事务内。 - -- [ ] 本次变更不涉及领域事件发布(如勾选此项可跳过下列项) -- [ ] 聚合写入与 `persistEvents()` 在同一 `transaction()` 内 -- [ ] 事务提交后调用 `order.clearDomainEvents()` -- [ ] Handler 实现了幂等性(INSERT IGNORE + affectedRows 检查,或类似机制) -- [ ] 已补充/更新对应事件的单元测试或集成测试 -- [ ] 已更新 `docs/印前模块文档.md` / `生产模块文档.md` / `打样模块文档.md` 中的事件列表(如适用) - -## 测试清单 - -- [ ] 单元测试通过 `pnpm test:unit:run` -- [ ] 集成测试通过(如适用) -- [ ] E2E 测试通过(如适用) -- [ ] 覆盖率未下降 `pnpm test:coverage` - -## 通用检查清单 +## 检查清单 - [ ] 代码通过 `pnpm lint` - [ ] 类型检查通过 `pnpm ts-check` +- [ ] 单元测试通过 `pnpm test:unit` - [ ] 提交信息遵循 Conventional Commits 规范 - [ ] 无硬编码密钥或敏感信息 - -## Breaking Changes - -- [ ] 本次变更包含 Breaking Change - - -## 部署注意事项 - - - -- [ ] 需要 DB migration -- [ ] 需要新增/修改环境变量 -- [ ] 需要调整 Redis 配置 -- [ ] 需要数据回填脚本 diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000..3eff181d --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,43 @@ +# Dependabot 依赖自动更新 +# 每周检查 npm、Docker 基础镜像、GitHub Actions 版本 +version: 2 + +updates: + # npm 依赖 + - package-ecosystem: npm + directory: / + schedule: + interval: weekly + day: monday + time: '09:00' + timezone: Asia/Shanghai + open-pull-requests-limit: 5 + labels: + - dependencies + - npm + commit-message: + prefix: build(deps) + + # Docker 基础镜像(node:20-alpine) + - package-ecosystem: docker + directory: / + schedule: + interval: weekly + day: monday + labels: + - dependencies + - docker + commit-message: + prefix: build(docker) + + # GitHub Actions 版本 + - package-ecosystem: github-actions + directory: / + schedule: + interval: weekly + day: monday + labels: + - dependencies + - github-actions + commit-message: + prefix: ci(deps) diff --git a/.github/workflows/chromatic.yml b/.github/workflows/chromatic.yml new file mode 100644 index 00000000..8e55066f --- /dev/null +++ b/.github/workflows/chromatic.yml @@ -0,0 +1,36 @@ +name: Chromatic Visual Testing + +on: + push: + branches: [ main, develop ] + pull_request: + branches: [ main, develop ] + +jobs: + chromatic: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Setup pnpm + uses: pnpm/action-setup@v4 + # 不指定 version,自动读取 package.json 的 packageManager 字段 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + cache: 'pnpm' + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Publish to Chromatic + uses: chromaui/action@latest + with: + projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }} + buildScriptName: build-storybook + exitOnceUploaded: true diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b726be4f..18881d32 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,52 +6,11 @@ on: pull_request: branches: [main, develop] -env: - REGISTRY: ghcr.io - IMAGE_NAME: ${{ github.repository }} - jobs: # 单元测试和代码质量检查 unit-test: runs-on: ubuntu-latest - services: - # MySQL 8.0:单元测试依赖数据库 - mysql: - image: mysql:8.0 - env: - MYSQL_ROOT_PASSWORD: root - MYSQL_DATABASE: vnerp_test - MYSQL_USER: test - MYSQL_PASSWORD: test123 - ports: - - 3306:3306 - options: >- - --health-cmd="mysqladmin ping -h 127.0.0.1 -uroot -proot --silent" - --health-interval=10s - --health-timeout=5s - --health-retries=10 - # Redis 7:缓存与会话依赖 - redis: - image: redis:7 - ports: - - 6379:6379 - options: >- - --health-cmd="redis-cli ping" - --health-interval=10s - --health-timeout=5s - --health-retries=10 - - env: - DB_HOST: 127.0.0.1 - DB_PORT: 3306 - DB_USER: test - DB_PASSWORD: test123 - DB_NAME: vnerp_test - REDIS_URL: redis://127.0.0.1:6379 - JWT_SECRET: test-jwt-secret - NODE_ENV: test - steps: - name: Checkout code uses: actions/checkout@v4 @@ -69,15 +28,9 @@ jobs: - name: Install dependencies run: pnpm install --frozen-lockfile - - name: Drizzle guard check (block FK-drop footguns) - run: node scripts/guard-drizzle-kit.cjs check - - name: Run linter run: pnpm lint - - name: Run lint gate - run: pnpm lint:gate - - name: Run TypeScript check run: pnpm ts-check @@ -99,43 +52,6 @@ jobs: runs-on: ubuntu-latest needs: unit-test - services: - # MySQL 8.0:E2E 测试通过应用访问数据库 - mysql: - image: mysql:8.0 - env: - MYSQL_ROOT_PASSWORD: root - MYSQL_DATABASE: vnerp_test - MYSQL_USER: test - MYSQL_PASSWORD: test123 - ports: - - 3306:3306 - options: >- - --health-cmd="mysqladmin ping -h 127.0.0.1 -uroot -proot --silent" - --health-interval=10s - --health-timeout=5s - --health-retries=10 - # Redis 7:缓存与会话依赖 - redis: - image: redis:7 - ports: - - 6379:6379 - options: >- - --health-cmd="redis-cli ping" - --health-interval=10s - --health-timeout=5s - --health-retries=10 - - env: - DB_HOST: 127.0.0.1 - DB_PORT: 3306 - DB_USER: test - DB_PASSWORD: test123 - DB_NAME: vnerp_test - REDIS_URL: redis://127.0.0.1:6379 - JWT_SECRET: test-jwt-secret - NODE_ENV: test - steps: - name: Checkout code uses: actions/checkout@v4 @@ -158,8 +74,10 @@ jobs: - name: Build application run: pnpm build - # 服务器由 Playwright webServer 配置自动管理(见 playwright.config.ts) - # 移除手动 pnpm start & + wait-on,避免与 webServer 端口冲突 + - name: Start application + run: | + pnpm start & + npx wait-on http://localhost:5000 --timeout 60000 - name: Run E2E tests run: pnpm test @@ -173,85 +91,33 @@ jobs: playwright-report/ test-results/ - # ============================================================ - # Docker 镜像构建并推送到 GHCR(GitHub Container Registry) - # 仅 main 分支推送时触发 - # ============================================================ - docker-publish: + # 构建和部署 + build: runs-on: ubuntu-latest needs: [unit-test, e2e-test] - if: github.ref == 'refs/heads/main' && github.event_name == 'push' - permissions: - contents: read - packages: write + if: github.ref == 'refs/heads/main' steps: - name: Checkout code uses: actions/checkout@v4 - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - - name: Log in to GitHub Container Registry - uses: docker/login-action@v3 - with: - registry: ${{ env.REGISTRY }} - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} + - name: Setup pnpm + uses: pnpm/action-setup@v4 - - name: Extract metadata for Docker - id: meta - uses: docker/metadata-action@v5 + - name: Setup Node.js + uses: actions/setup-node@v4 with: - images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} - tags: | - type=sha,prefix=sha- - type=raw,value=latest - type=raw,value={{date('YYYYMMDD')}} + node-version: '20' + cache: 'pnpm' - - name: Build and push Docker image - uses: docker/build-push-action@v6 - with: - context: . - file: ./Dockerfile - push: true - tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} - cache-from: type=gha - cache-to: type=gha,mode=max + - name: Install dependencies + run: pnpm install --frozen-lockfile - # ============================================================ - # 自动部署到生产服务器 - # 通过 SSH 连接服务器,拉取最新镜像并滚动更新 - # 需配置 GitHub Secrets(见 docs/04-运维指南/Docker部署指南.md) - # ============================================================ - deploy: - runs-on: ubuntu-latest - needs: docker-publish - if: github.ref == 'refs/heads/main' && github.event_name == 'push' - environment: production + - name: Build application + run: pnpm build - steps: - - name: Deploy to production server - uses: appleboy/ssh-action@v1 + - name: Upload build artifacts + uses: actions/upload-artifact@v4 with: - host: ${{ secrets.DEPLOY_HOST }} - username: ${{ secrets.DEPLOY_USER }} - key: ${{ secrets.DEPLOY_SSH_KEY }} - port: ${{ secrets.DEPLOY_PORT || 22 }} - script: | - cd ${{ secrets.DEPLOY_PATH || '/opt/vnerp' }} - git pull origin main - docker compose -f docker-compose.prod.yml --env-file .env.production pull app - docker compose -f docker-compose.prod.yml --env-file .env.production up -d --no-deps --force-recreate app - echo "等待 app 健康检查(最多 60 秒)..." - for i in $(seq 1 30); do - if docker inspect --format='{{.State.Health.Status}}' vnerp-app-prod 2>/dev/null | grep -q healthy; then - echo "✅ 部署成功:app 已健康运行" - docker image prune -f - exit 0 - fi - sleep 2 - done - echo "⚠️ 警告:app 未通过健康检查(60秒超时),请手动检查日志" - docker compose -f docker-compose.prod.yml logs --tail=50 app + name: build-output + path: .next/ diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 00000000..7a28a544 --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,53 @@ +# 生产环境自动部署 +# 触发:Docker 镜像发布成功后自动执行,或手动触发 +# 方式:SSH 登录服务器,拉取最新镜像并滚动重启 +# +# 前置配置(GitHub Secrets): +# DEPLOY_HOST - 部署服务器 IP/域名 +# DEPLOY_USER - SSH 登录用户 +# DEPLOY_SSH_KEY - SSH 私钥(需加入服务器 authorized_keys) +# DEPLOY_PATH - 服务器上项目路径(如 /opt/vnerp) +# DEPLOY_PORT - SSH 端口(默认 22,可选) +name: Deploy to Production + +on: + workflow_run: + workflows: ['Docker Publish'] + types: [completed] + branches: [main] + workflow_dispatch: + +jobs: + deploy: + runs-on: ubuntu-latest + # 仅在镜像发布成功时部署;手动触发也执行 + if: ${{ github.event.workflow_run.conclusion == 'success' || github.event_name == 'workflow_dispatch' }} + environment: production + + steps: + - name: Deploy via SSH + uses: appleboy/ssh-action@v1.2.0 + with: + host: ${{ secrets.DEPLOY_HOST }} + username: ${{ secrets.DEPLOY_USER }} + key: ${{ secrets.DEPLOY_SSH_KEY }} + port: ${{ secrets.DEPLOY_PORT || 22 }} + script: | + set -e + cd ${{ secrets.DEPLOY_PATH }} + + echo "==> 拉取最新代码与镜像" + git pull --ff-only origin main + docker compose -f docker-compose.prod.yml pull app + + echo "==> 滚动重启应用" + docker compose -f docker-compose.prod.yml up -d --no-deps app + + echo "==> 清理旧镜像" + docker image prune -f --filter "until=24h" + + echo "==> 健康检查" + sleep 5 + curl -fsS http://localhost:5000/api/health || (echo "健康检查失败" && exit 1) + + echo "==> 部署完成" diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml new file mode 100644 index 00000000..e5f8d1bf --- /dev/null +++ b/.github/workflows/docker-publish.yml @@ -0,0 +1,62 @@ +# Docker 镜像构建与发布 +# 触发:CI 流水线通过后自动执行(main 分支),或打 tag v*(版本发布),或手动触发 +# 产物:ghcr.io/snqig/vnerp:{latest | version | sha} +name: Docker Publish + +on: + workflow_run: + workflows: ['CI/CD Pipeline'] + types: [completed] + branches: [main] + push: + tags: ['v*'] + workflow_dispatch: + +env: + REGISTRY: ghcr.io + IMAGE_NAME: ${{ github.repository }} + +jobs: + build-and-push: + runs-on: ubuntu-latest + # 仅在 CI 成功时构建(workflow_run 场景);tag/手动触发不受此限制 + if: ${{ github.event_name != 'workflow_run' || github.event.workflow_run.conclusion == 'success' }} + permissions: + contents: read + packages: write + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract metadata (tags, labels) + id: meta + uses: docker/metadata-action@v6 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + tags: | + type=raw,value=latest,enable={{is_default_branch}} + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + type=sha,format=short + + - name: Build and push Docker image + uses: docker/build-push-action@v6 + with: + context: . + file: ./Dockerfile + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml deleted file mode 100644 index a85d32e9..00000000 --- a/.github/workflows/release.yml +++ /dev/null @@ -1,131 +0,0 @@ -name: Release - -# 仅当推送符合语义化版本的 tag(如 v1.2.3)时触发, -# 自动创建 GitHub Release 并将对应版本的 Docker 镜像推送到 GHCR。 -on: - push: - tags: - - 'v*' - -permissions: - contents: write - packages: write - -env: - REGISTRY: ghcr.io - IMAGE_NAME: ${{ github.repository }} - -jobs: - release: - runs-on: ubuntu-latest - - services: - # 质量门禁:单元测试需要数据库 - mysql: - image: mysql:8.0 - env: - MYSQL_ROOT_PASSWORD: root - MYSQL_DATABASE: vnerp_test - MYSQL_USER: test - MYSQL_PASSWORD: test123 - ports: - - 3306:3306 - options: >- - --health-cmd="mysqladmin ping -h 127.0.0.1 -uroot -proot --silent" - --health-interval=10s - --health-timeout=5s - --health-retries=10 - redis: - image: redis:7 - ports: - - 6379:6379 - options: >- - --health-cmd="redis-cli ping" - --health-interval=10s - --health-timeout=5s - --health-retries=10 - - env: - DB_HOST: 127.0.0.1 - DB_PORT: 3306 - DB_USER: test - DB_PASSWORD: test123 - DB_NAME: vnerp_test - REDIS_URL: redis://127.0.0.1:6379 - JWT_SECRET: test-jwt-secret - NODE_ENV: test - - steps: - - name: Checkout code - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - # ============================================================ - # 质量门禁:只有通过 lint / 类型检查 / 单元测试的代码才允许发布 - # 确保只有 CI 通过的代码才能发布 - # ============================================================ - - name: Setup pnpm - uses: pnpm/action-setup@v4 - - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version: '20' - cache: 'pnpm' - - - name: Install dependencies - run: pnpm install --frozen-lockfile - - - name: Run linter - run: pnpm lint - - - name: Run lint gate - run: pnpm lint:gate - - - name: Run TypeScript check - run: pnpm ts-check - - - name: Run unit tests - run: pnpm test:unit:run - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - - name: Log in to GitHub Container Registry - uses: docker/login-action@v3 - with: - registry: ${{ env.REGISTRY }} - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Extract metadata for Docker - id: meta - uses: docker/metadata-action@v5 - with: - images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} - tags: | - type=semver,pattern={{version}} - type=semver,pattern={{major}}.{{minor}} - type=raw,value=latest - - - name: Build and push Docker image - uses: docker/build-push-action@v6 - with: - context: . - file: ./Dockerfile - push: true - tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} - cache-from: type=gha - cache-to: type=gha,mode=max - - - name: Create GitHub Release - uses: softprops/action-gh-release@v2 - with: - tag_name: ${{ github.ref_name }} - name: Release ${{ github.ref_name }} - generate_release_notes: true - prerelease: ${{ contains(github.ref_name, '-') }} - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..d2953771 --- /dev/null +++ b/.gitignore @@ -0,0 +1,120 @@ +# 排除 . 开头的文件和目录 +.* + +# 但保留以下文件(取消注释以启用) +# !.gitignore +# !.env.example +# !.eslintrc.js +# !.prettierrc.js + +# 保留工程治理相关配置(需纳入版本控制) +!.github/ +!.husky/ +!.trae/ + +# 明确排除 IDE/工具/Agent 相关目录(与项目无关,不同步) +.agentic-tools-mcp/ +.agents/ +.augment.claude/ +.continue/ +.cursor/ +.iflow/ +.ro0/ +.superdesign/ +.venv/ +.VS/ +.agency-agents/ +agency-agents +-github/ +trae/ + +# Trae IDE 自动化脚本与文档(与项目无关,不同步) +scripts/trae-auto-continue* +scripts/trae-desktop-auto-continue* +docs/TRADE-AUTO-CONTINUE.md +docs/TRADE-DESKTOP-AUTO-CONTINUE.md + +# 依赖目录 +node_modules/ +__pycache__/ +.pytest_cache/ + +# 构建输出 +.next/ +out/ +build/ +dist/ +coverage/ +tsconfig.tsbuildinfo +*.tsbuildinfo + +# 环境变量 +.env +.env.local +.env.*.local + +# 日志 +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# 编辑器 +.vscode/ +.idea/ +*.swp +*.swo + +# 操作系统 +.DS_Store +Thumbs.db + +# 临时文件 +*.tmp +*.temp +.cache/ + +# 临时生成的 i18n 缺失清单 +i18n-missing.json + +# 调试脚本与测试截图(根目录) +/test-*.js +/test-*.ps1 +/test-*.py +/debug-*.ps1 +/scan-*.js +/patch-*.js +/fix_*.py +/test_*.png +/sample-*.png +/orders-*.png + +# 数据库备份 +/backups/ +*.bak + +# 基线报告(CI 生成,不需跟踪) +docs/baseline/ + +# 临时脚本目录 +scripts/temp/ + +# 密钥文件 +*.pem +*.key + +# 测试运行产物(E2E 截图目录、Playwright 结果目录) +/test-screenshots/ +/test-results/ +/test-results.json +/playwright-report/ + +# i18n 检查脚本输出目录(截图、result.json) +/scripts/i18n-check/ + +# 临时覆盖率/调试脚本 +/scripts/tmp-cov.js + +# AI 智能体实验目录(与 ERP 核心业务无关) +/agency-agents/ +agency-agents diff --git a/.husky/commit-msg b/.husky/commit-msg index e93cf785..6a2e4c84 100644 --- a/.husky/commit-msg +++ b/.husky/commit-msg @@ -1,4 +1 @@ -#!/usr/bin/env sh -# 本沙箱中 pnpm 路径被错拼不可用,且 node 会把 /d/... 路径误读成 D:\d\..., -# 故改用 node 直调本地 commitlint,并通过 stdin 传入消息(避开文件路径拼接问题)。 -cat "$1" | node node_modules/@commitlint/cli/cli.js +pnpm exec commitlint --edit $1 diff --git a/.husky/pre-commit b/.husky/pre-commit index 4c50bf27..5ee7abd8 100644 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -1 +1 @@ -node node_modules/lint-staged/bin/lint-staged.js --no-stash +pnpm exec lint-staged diff --git a/.npmrc b/.npmrc new file mode 100644 index 00000000..52a560d1 --- /dev/null +++ b/.npmrc @@ -0,0 +1,23 @@ +loglevel=error +registry=https://registry.npmmirror.com + +strictStorePkgContentCheck=false +verifyStoreIntegrity=false + +# 网络优化 +network-concurrency=16 +fetch-retries=3 +fetch-timeout=60000 + +# 严格使用 peer dependencies +strict-peer-dependencies=false + +# 自动生成 lockfile +auto-install-peers=true + +# lockfile 配置 +lockfile=true +prefer-frozen-lockfile=true + +# 如果 lockfile 存在但过期,更新而不是失败 +resolution-mode=highest diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 00000000..0f6a28f3 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,10 @@ +node_modules +.next +dist +build +out +coverage +pnpm-lock.yaml +*.min.js +*.min.css +public/animations/ diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 00000000..3de448eb --- /dev/null +++ b/.prettierrc @@ -0,0 +1,7 @@ +{ + "semi": true, + "trailingComma": "es5", + "singleQuote": true, + "printWidth": 100, + "tabWidth": 2 +} diff --git a/CHANGELOG.md b/CHANGELOG.md deleted file mode 100644 index e50d3bb0..00000000 --- a/CHANGELOG.md +++ /dev/null @@ -1,69 +0,0 @@ -# 更新日志 - -## 2026-07-17 - 多币种功能 Phase 2b - -### 新增功能 - -**财务模块** -- 应收/应付支持多币种显示 -- 收款/付款 API 支持币种字段 -- 财务应用服务层集成多币种逻辑 - -**销售模块** -- 销售订单页面支持多币种展示 -- 配送单页面支持多币种展示 -- 退货单页面支持多币种展示 -- 对账单页面支持多币种展示 - -**仓储模块** -- 入库单页面支持多币种展示 -- 出库单页面支持多币种展示 -- 入库对话框支持采购订单搜索与多币种 -- 入库验证规则更新 - -### 技术更新 - -**API 路由** -- `/api/orders/sales` - 销售订单 API -- `/api/finance/payment` - 付款 API -- `/api/finance/receipt` - 收款 API -- `/api/warehouse/inbound` - 入库 API -- `/api/warehouse/outbound` - 出库 API - -**应用服务** -- `FinanceApplicationService` - 财务应用服务 -- `DeliveryApplicationService` - 配送应用服务 -- `ReturnOrderApplicationService` - 退货应用服务 -- `ReconciliationApplicationService` - 对账应用服务 - -**数据层** -- 更新 Drizzle schema 支持多币种字段 -- 添加数据库迁移脚本 066-068 - -### Git 配置优化 - -为解决网络连接问题,建议团队成员配置 Git HTTP 协议版本: - -```bash -git config --global http.version HTTP/1.1 -git config --global http.postBuffer 524288000 -git config --global http.lowSpeedLimit 0 -git config --global http.lowSpeedTime 999999 -``` - -或直接使用项目根目录的 `.gitconfig` 文件。 - -### 提交记录 - -| Commit | 说明 | -|--------|------| -| `7140afd` | 更新 UI 页面和 API 路由支持多币种显示 | -| `41aab94` | Phase 2b 多币种支持 - 销售/库存/财务模块 | -| `e3baba4` | 合并 feature/multi-currency-phase1 分支 | -| `a7b6d47` | Phase 2a 采购多币种实现 | - -### 注意事项 - -1. 数据库迁移需要在部署前执行 -2. 需要配置汇率基础数据后才能正常使用多币种功能 -3. 历史数据已通过迁移脚本回填默认币种 \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 52fbc8e0..1ce81394 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,96 +1,69 @@ # 贡献指南 -感谢您对 vnERP 项目的关注!本文档描述了参与项目开发的流程与规范。 +感谢参与 VNERP 项目!请阅读以下规范确保协作顺畅。 -## 开发环境准备 +## 开发环境 -1. Node.js 20+(建议使用 [.nvmrc](.nvmrc) 锁定版本) -2. pnpm 9+(`corepack enable && corepack prepare pnpm@latest --activate`) -3. MySQL 8.0+(utf8mb4 字符集) -4. Redis(可选,缺失时自动降级为内存模式) +- Node.js 18+ +- MySQL 8.0+ +- pnpm 9.0+ ```bash pnpm install -cp .env.example .env -# 编辑 .env 配置数据库连接等 -pnpm setup:db +pnpm setup:db --seed pnpm dev ``` -## 开发流程 +## 提交规范 -### 分支策略 +本项目遵循 [Conventional Commits](https://www.conventionalcommits.org/) 规范。 -- `main`:生产分支,仅通过 PR 合入 -- `develop`:开发集成分支 -- 功能分支:`feat/-`(如 `feat/standard-card-i18n`) -- 修复分支:`fix/-` - -### 提交规范(Conventional Commits) +### 提交格式 ``` [scope]: - -[optional body] ``` -| Type | 用途 | +### 类型 + +| type | 用途 | |------|------| | feat | 新功能 | | fix | Bug 修复 | -| refactor | 重构 | | docs | 文档 | +| refactor | 重构 | | test | 测试 | -| chore | 构建/工具 | -| perf | 性能优化 | - -### 质量门禁 - -提交前自动运行(husky pre-commit + lint-staged): +| build | 构建/依赖 | +| ci | CI 配置 | +| chore | 维护 | -1. ESLint(`pnpm lint`) -2. Prettier 格式化 -3. TypeScript 类型检查(`pnpm ts-check`) +### 示例 -提交前手动运行: - -```bash -pnpm test:coverage # 单元测试 + 覆盖率 -pnpm build # 生产构建验证 +``` +feat(warehouse): 添加入库单审核功能 +fix(auth): 修复 401 刷新并发问题 +docs(readme): 更新快速开始步骤 ``` -### PR 要求 - -1. PR 标题遵循 Conventional Commits 格式 -2. 描述包含:变更摘要、测试方案 -3. CI 所有检查通过(lint + type-check + unit-test + e2e) -4. 至少一名 Reviewer 批准 - -## 架构规范 - -项目采用 DDD 分层架构,依赖方向:app → application → domain ← infrastructure - -- **domain**:领域模型,不依赖任何外部层 -- **application**:应用服务/事件处理器,依赖 domain 接口 -- **infrastructure**:基础设施实现,实现 domain 接口 -- **app**:Next.js 页面和 API 路由(Thin Controller) +## 提交门禁 -详见 [docs/07-决策记录/](docs/07-决策记录/) 中的架构决策记录。 +本项目已配置 husky hooks,提交时自动执行: -## 国际化(i18n) +- **pre-commit**:lint-staged 对暂存文件运行 eslint + prettier +- **commit-msg**:commitlint 校验提交信息格式 -- 所有用户可见文本必须使用 next-intl 翻译函数 -- 4 语言文件保持同步:en / zh-CN / zh-TW / vi -- 禁止硬编码中文(ESLint 规则会检测) +## 质量检查 -## 数据库迁移 +提交前请确保: -- 迁移文件位于 `database/migrations/`,按序号命名 -- Drizzle schema 定义在 `src/lib/db/schema.ts` -- 使用 `pnpm setup:db` 执行迁移 -- 禁止 SQL 与 Drizzle 双轨维护 +- `pnpm lint` 通过 +- `pnpm ts-check` 通过 +- `pnpm test:unit` 通过 +- 新增功能有对应测试 -## 问题反馈 +## 分支策略 -- Bug 报告:使用 GitHub Issue,附复现步骤和错误日志 -- 功能建议:先开 Issue 讨论,确认后再实现 +- `main`:生产分支,仅接受 PR 合入 +- `develop`:开发分支 +- 功能分支:`feat/-` +- 修复分支:`fix/-` diff --git a/Dockerfile b/Dockerfile index dccc852c..920475e8 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,22 +1,15 @@ -# ============================================================================= -# Next.js 生产镜像 — standalone 模式 -# ============================================================================= -# 关键点:output: 'standalone' 时,必须用 `node server.js` 启动, -# 而非 `next start`(后者与 standalone 不兼容,会触发警告)。 -# standalone 产物已内嵌最小化 node_modules,镜像体积约 ~150MB。 -# ============================================================================= +# 多阶段构建:Next.js 生产镜像(standalone 输出) +# 基于 pnpm,需 next.config.ts 中 output: 'standalone' FROM node:20-alpine AS base -RUN apk add --no-cache libc6-compat tini +RUN apk add --no-cache libc6-compat RUN corepack enable && corepack prepare pnpm@9.0.0 --activate # --- 依赖安装阶段 --- FROM base AS deps WORKDIR /app COPY package.json pnpm-lock.yaml ./ -# BuildKit 缓存挂载:pnpm store 跨构建复用,加速 CI -RUN --mount=type=cache,id=pnpm-store,target=/root/.local/share/pnpm/store \ - pnpm install --frozen-lockfile +RUN pnpm install --frozen-lockfile # --- 构建阶段 --- FROM base AS builder @@ -24,44 +17,27 @@ WORKDIR /app COPY --from=deps /app/node_modules ./node_modules COPY . . ENV NEXT_TELEMETRY_DISABLED=1 -# NEXT_PUBLIC_* 环境变量在构建时内联到客户端代码,生产构建需在此传入 -# ARG NEXT_PUBLIC_API_BASE_URL -# ENV NEXT_PUBLIC_API_BASE_URL=$NEXT_PUBLIC_API_BASE_URL RUN pnpm build -# --- 运行阶段(最终镜像) --- +# --- 运行阶段 --- FROM base AS runner WORKDIR /app - ENV NODE_ENV=production ENV NEXT_TELEMETRY_DISABLED=1 ENV PORT=5000 ENV HOSTNAME=0.0.0.0 -# 非 root 用户运行(安全最佳实践) +# 非 root 用户运行 RUN addgroup --system --gid 1001 nodejs && \ adduser --system --uid 1001 nextjs -# standalone 模式只需复制 server.js + 最小化 node_modules + 静态资源 -# server.js 已内嵌路由处理,无需 next start +# standalone 模式只需复制 server.js + 静态资源 + public + messages COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./ COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static COPY --from=builder --chown=nextjs:nodejs /app/public ./public COPY --from=builder --chown=nextjs:nodejs /app/messages ./messages -# 创建可写目录(用于上传文件、日志等) -RUN mkdir -p /app/uploads /app/logs && \ - chown -R nextjs:nodejs /app/uploads /app/logs - USER nextjs - EXPOSE 5000 -# 健康检查 — 命中 /api/health 端点,检查 DB + 内存状态 -HEALTHCHECK --interval=30s --timeout=5s --start-period=60s --retries=3 \ - CMD node -e "require('http').get('http://127.0.0.1:5000/api/health', res => { process.exit(res.statusCode === 200 ? 0 : 1) }).on('error', () => process.exit(1))" - -# tini 作为 PID 1:正确传递 SIGTERM/SIGINT 信号,避免僵尸进程 -ENTRYPOINT ["/sbin/tini", "--"] -# standalone 启动命令(非 next start) CMD ["node", "server.js"] diff --git a/LICENSE b/LICENSE deleted file mode 100644 index f6a54c74..00000000 --- a/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2026 vnERP Contributors - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/README.md b/README.md index c2c04b07..8bb52bb8 100644 --- a/README.md +++ b/README.md @@ -1,36 +1,15 @@ -# 印刷生产经营信息管理系统 Print MIS +# VNERP 印刷行业 ERP 系统 [![CI](https://github.com/snqig/vnerp/actions/workflows/ci.yml/badge.svg)](https://github.com/snqig/vnerp/actions/workflows/ci.yml) -[![License](https://img.shields.io/badge/license-MIT-blue)](#许可证) - -> 印刷生产经营信息管理系统 Print MIS — 基于 Next.js 16 + DDD + Drizzle ORM 的印刷行业综合管理系统,支持中文(简/繁)、英文、越南语四语言国际化。 - -## 目录 - -- [快速开始](#快速开始) - - [环境要求](#环境要求) - - [安装与启动](#安装与启动) - - [常用命令](#常用命令) -- [技术栈](#技术栈) -- [架构与模块](#架构与模块) - - [DDD 分层架构](#ddd-分层架构) - - [业务模块](#业务模块) - - [多币种支持](#多币种支持) -- [国际化 i18n](#国际化-i18n) -- [数据库与部署](#数据库与部署) - - [数据库初始化](#数据库初始化) - - [Docker 部署](#docker-部署) - - [环境变量](#环境变量) -- [安全说明](#安全说明) -- [提交规范与 CI/CD](#提交规范与-ci-cd) -- [文档](#文档) -- [许可证](#许可证) +[![License](https://img.shields.io/badge/license-Internal-blue)](#许可证) + +> 越南达昌印刷 ERP — 基于 Next.js 16 + DDD + Drizzle ORM 的印刷行业综合管理系统,支持中文(简/繁)、英文、越南语四语言国际化。 ## 快速开始 ### 环境要求 -- Node.js 20+ +- Node.js 18+ - MySQL 8.0+ - pnpm 9.0+(项目已配置 `preinstall: only-allow pnpm`,禁用 npm/yarn) @@ -60,49 +39,43 @@ pnpm dev **默认账号**(种子数据):用户名 `admin` / 密码 `admin123` -> ⚠️ **安全警告**:默认口令 `admin123` 仅供初始化使用。`first_login=1` 标记会强制 admin 首次登录时修改密码,修改前无法访问任何业务页面。**生产部署前必须确认默认口令已被更改**,并配置 `system.force_change_password=true` 与 `system.password_expire_days` 策略。 - ### 常用命令 -| 命令 | 说明 | -| -------------------------- | --------------------------------------------------- | -| `pnpm dev` | 启动开发服务器(端口 5000,Turbopack) | -| `pnpm build` | 生产构建 | -| `pnpm start` | 启动生产服务器 | -| `pnpm lint` | ESLint 检查(含 `i18n/no-chinese-hardcode` 规则) | -| `pnpm ts-check` | TypeScript 类型检查 | -| `pnpm test:unit` | Vitest 单元测试(watch 模式) | -| `pnpm test:unit:run` | Vitest 单元测试(单次运行) | -| `pnpm test:coverage` | 单元测试 + 覆盖率报告 | -| `pnpm test` | Playwright E2E 测试 | -| `pnpm setup:db --seed` | 数据库初始化 + 种子数据 | -| `pnpm setup:db` | 数据库初始化(建表) | -| `pnpm migrate` | 执行数据库迁移 | -| `pnpm migrate:status` | 查看迁移状态 | -| `pnpm db:studio` | Drizzle Studio 可视化管理 | -| `pnpm i18n:check` | i18n key 缺失校验 | -| `pnpm i18n:check:strict` | i18n key 校验(CI 卡点模式,缺失则失败) | -| `pnpm backup` | 数据库备份 | +| 命令 | 说明 | +|------|------| +| `pnpm dev` | 启动开发服务器(端口 5000,Turbopack) | +| `pnpm build` | 生产构建 | +| `pnpm start` | 启动生产服务器 | +| `pnpm lint` | ESLint 检查(含 `i18n/no-chinese-hardcode` 规则) | +| `pnpm ts-check` | TypeScript 类型检查 | +| `pnpm test:unit` | Vitest 单元测试(watch 模式) | +| `pnpm test:unit:run` | Vitest 单元测试(单次运行) | +| `pnpm test:coverage` | 单元测试 + 覆盖率报告 | +| `pnpm test` | Playwright E2E 测试 | +| `pnpm setup:db --seed` | 数据库初始化 + 种子数据 | +| `pnpm db:generate` | Drizzle 生成迁移文件 | +| `pnpm db:push` | Drizzle 推送 schema 到数据库 | +| `pnpm db:studio` | Drizzle Studio 可视化管理 | ## 技术栈 -| 类别 | 技术 | -| -------- | ------------------------------------------------ | -| 前端框架 | Next.js 16.1.1(App Router + Turbopack) | -| UI 组件 | React 19 + shadcn/ui + Radix UI + Tailwind CSS 4 | -| 编程语言 | TypeScript 5(严格模式) | -| 数据库 | MySQL 8.0 + Drizzle ORM 0.45 + mysql2 连接池 | -| 国际化 | next-intl(zh-CN / zh-TW / en / vi 四语言) | -| 认证 | JWT (jose) + bcryptjs + 401 无感刷新 | -| 表单校验 | React Hook Form + Zod | -| 状态管理 | React Hooks + Context | -| 图表 | Recharts | -| 单元测试 | Vitest(覆盖率阈值 80%) | -| E2E 测试 | Playwright | -| 代码规范 | ESLint + Prettier + 自定义 i18n 规则 | -| 提交规范 | husky + lint-staged + commitlint | -| 包管理 | pnpm 9.0(lockfile 锁定) | -| 容器化 | Docker + docker-compose(多阶段构建) | +| 类别 | 技术 | +|------|------| +| 前端框架 | Next.js 16.1.1(App Router + Turbopack) | +| UI 组件 | React 19 + shadcn/ui + Radix UI + Tailwind CSS 4 | +| 编程语言 | TypeScript 5(严格模式) | +| 数据库 | MySQL 8.0 + Drizzle ORM 0.45 + mysql2 连接池 | +| 国际化 | next-intl(zh-CN / zh-TW / en / vi 四语言) | +| 认证 | JWT (jose) + bcryptjs + 401 无感刷新 | +| 表单校验 | React Hook Form + Zod | +| 状态管理 | React Hooks + Context | +| 图表 | Recharts | +| 单元测试 | Vitest(覆盖率阈值 80%) | +| E2E 测试 | Playwright | +| 代码规范 | ESLint + Prettier + 自定义 i18n 规则 | +| 提交规范 | husky + lint-staged + commitlint | +| 包管理 | pnpm 9.0(lockfile 锁定) | +| 容器化 | Docker + docker-compose(多阶段构建) | ## 架构与模块 @@ -116,7 +89,6 @@ src/ │ ├── production/ # 生产领域 │ ├── sales/ # 销售领域 │ ├── purchase/ # 采购领域 -│ ├── hr/ # 人力资源领域 │ └── standard-card/ # 标准卡领域 ├── application/ # 应用层:应用服务 + 事件处理器 │ ├── handlers/ # 审计/缓存失效/财务凭证/库存同步/二维码 @@ -126,7 +98,7 @@ src/ │ ├── api/ # API 路由(Thin Controller) │ └── (dashboard)/ # 业务页面 ├── components/ui/ # shadcn/ui 基础组件 -├── lib/ # 工具库(db/auth/fifo/state-machine/hr) +├── lib/ # 工具库(db/auth/fifo/state-machine) └── i18n/ # next-intl 配置 ``` @@ -134,102 +106,96 @@ src/ ### 业务模块 -| 模块 | 路径前缀 | 说明 | -| -------- | --------------- | -------------------------------------------------------- | -| 看板中心 | `/dashboard` | 综合仪表盘、CEO/生产/财务/质量/销售/仓库看板 | -| 仓储管理 | `/warehouse` | 入库、出库、库存、调拨、盘点、批次追溯(多币种) | -| 生产管理 | `/production` | 工单、排产、领料、退料、MRP | -| 销售管理 | `/sales` | 发货、对账、退货(多币种) | -| 采购管理 | `/purchase` | 采购订单、申请、供应商、退换货(多币种) | -| 印前管理 | `/dcprint` | 刀模、油墨、网版、工艺卡、追溯 | -| 质量管理 | `/quality` | 来料/过程/成品检验、SPC、SGS、客诉 | -| 设备管理 | `/equipment` | 校准、维护、维修、报废 | -| 财务管理 | `/finance` | 应收应付、成本、报表(多币种) | -| 人力资源 | `/hr` | 员工、考勤、薪资核算(计件/绩效/加班/社保/个税)、培训 | -| 打样管理 | `/sample` | 样品订单、标准色卡 | -| 系统设置 | `/settings` | 组织架构、用户、角色、菜单、字典、配置、日志、币种、汇率 | +| 模块 | 路径前缀 | 说明 | +|------|---------|------| +| 看板中心 | `/dashboard` | 综合仪表盘、CEO/生产/财务/质量/销售/仓库看板 | +| 仓储管理 | `/warehouse` | 入库、出库、库存、调拨、盘点、批次追溯 | +| 生产管理 | `/production` | 工单、排产、领料、退料、MRP | +| 销售管理 | `/sales` | 发货、对账、退货 | +| 采购管理 | `/purchase` | 采购订单、申请、供应商、退换货 | +| 印前管理 | `/dcprint` | 刀模、油墨、网版、工艺卡、追溯 | +| 质量管理 | `/quality` | 来料/过程/成品检验、SPC、SGS、客诉 | +| 设备管理 | `/equipment` | 校准、维护、维修、报废 | +| 财务管理 | `/finance` | 应收应付、成本、报表 | +| 人力资源 | `/hr` | 员工、考勤、薪资、培训 | +| 打样管理 | `/sample` | 样品订单、标准色卡 | +| 系统设置 | `/settings` | 组织架构、用户、角色、菜单、字典、配置、日志 | 完整接口清单见 [docs/10-接口文档/API.md](docs/10-接口文档/API.md)。 -### 多币种支持 - -系统支持多币种业务处理(Phase 2b 已完成),覆盖采购、销售、仓储、财务四大模块: +## i18n 国际化 -- **币种管理**:`/settings/currency` — 币种 CRUD(代码、名称、符号、小数位) -- **汇率管理**:`/settings/exchange-rate` — 汇率 CRUD 与历史记录 -- **双币种显示**:`MoneyDisplay` 组件同时展示原币种与本位币金额 -- **领域层**:`CurrencySnapshot` 不可变值对象,记录下单时的汇率快照 -- **数据迁移**:迁移脚本 064-068 已添加多币种字段并回填历史数据 +项目支持四种语言,路由前缀策略为 `as-needed`(zh-CN 无前缀,其他语言带前缀): -设计文档详见 [docs/superpowers/specs/](docs/superpowers/specs/)。 +| 语言 | locale | 路由示例 | +|------|--------|---------| +| 简体中文(默认) | `zh-CN` | `/dashboard` | +| 繁体中文 | `zh-TW` | `/zh-TW/dashboard` | +| 英文 | `en` | `/en/dashboard` | +| 越南语 | `vi` | `/vi/dashboard` | -### HR 人力资源模块 +**开发约束**:UI 文本必须通过 `useTranslations` Hook 读取,禁止硬编码中文。ESLint 自定义规则 `i18n/no-chinese-hardcode` 会在提交时自动检测,详见 [docs/11-开发指南/i18n规范.md](docs/11-开发指南/i18n规范.md)。 -人力资源模块已从基础员工档案管理升级为**制造型印刷企业薪酬核算引擎**: +## 提交规范与 CI/CD -**核心功能**: -- **组织架构**:6 级组织树(集团→法人主体→工厂→车间→班组→岗位) -- **员工档案**:入职/调岗/离职/返聘全生命周期管理 -- **排班考勤**:班次定义、排班计划、打卡记录与统计 -- **薪酬核算**:计件工资、加班费、绩效评分、社保公积金、个税累计预扣 -- **培训认证**:技能矩阵、证书管理、到期预警、培训记录 -- **报表分析**:人工成本、薪酬结构、离职率分析 +### 提交规范 -**薪酬核算引擎架构**: +遵循 [Conventional Commits](https://www.conventionalcommits.org/) 规范: ``` -前端 (shadcn/ui) → API Routes → 薪酬核算引擎 → Drizzle ORM → MySQL - ├── piece-calculator.ts — 计件工资 - ├── overtime-calculator.ts — 加班费 - ├── performance-calculator.ts — 绩效评分 - ├── insurance-calculator.ts — 社保公积金 - ├── tax-calculator.ts — 个税累计预扣 - └── salary-engine.ts — 核算引擎入口 +[scope]: ``` -**关键特性**: -- 计件工资:支持印刷行业多工序、多工价核算,与 MES 数据同步 -- 加班计算:1.5/2/3 倍倍率,按法定节假日自动匹配 -- 个税计算:累计预扣预缴法,支持专项附加扣除 -- 批量核算:全月一键批量核算,核算-确认-发放三级工作流 -- MES 集成:产量数据、不良率自动拉取 +| type | 用途 | +|------|------| +| feat | 新功能 | +| fix | Bug 修复 | +| docs | 文档 | +| refactor | 重构 | +| test | 测试 | +| build | 构建/依赖 | +| ci | CI 配置 | +| chore | 维护 | -## 国际化 i18n +示例:`feat(warehouse): 添加入库单审核功能` -项目支持四种语言,路由前缀策略为 `as-needed`(zh-CN 无前缀): +### 提交门禁(husky hooks) -| 语言 | locale | 路由示例 | -| ---------------- | --------- | -------------------- | -| 简体中文(默认) | `zh-CN` | `/dashboard` | -| 繁体中文 | `zh-TW` | `/zh-TW/dashboard` | -| 英文 | `en` | `/en/dashboard` | -| 越南语 | `vi` | `/vi/dashboard` | +| Hook | 作用 | +|------|------| +| `pre-commit` | lint-staged 对暂存文件运行 ESLint + Prettier | +| `commit-msg` | commitlint 校验提交信息格式 | -**开发约束**:UI 文本必须通过 `useTranslations` Hook 读取,禁止硬编码中文。ESLint 自定义规则 `i18n/no-chinese-hardcode` 会在提交时自动检测。 +### CI 流水线 -**i18n key 校验**: +GitHub Actions 流水线([.github/workflows/ci.yml](.github/workflows/ci.yml))在 push/PR 到 `main`/`develop` 时触发,按顺序执行: -```bash -node scripts/i18n-key-check.mjs # 检查代码引用的 key 是否在 messages 中存在 -node scripts/i18n-key-check.mjs --strict # CI 卡点模式 -node scripts/i18n-key-check.mjs --unused # 报告未使用的孤立 key -``` +1. **lint** — ESLint 检查 +2. **ts-check** — TypeScript 类型检查 +3. **test:coverage** — Vitest 单元测试 + 覆盖率(阈值 80%) +4. **e2e-test** — Playwright E2E 测试(依赖单元测试通过,PR 与 main 均执行) +5. **build** — Next.js 构建(仅 `main` 分支触发,PR 不跑以节省资源) + +视觉回归测试通过 Chromatic 单独流水线([chromatic.yml](.github/workflows/chromatic.yml))。 + +提交贡献流程详见 [CONTRIBUTING.md](CONTRIBUTING.md)。 ## 数据库与部署 -### 数据库初始化 +### 数据库 -两条初始化路径: +两条初始化路径,按场景选择: -| 场景 | 命令 | 说明 | -| ----------------- | ------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- | -| 首次搭建/重置 | `pnpm setup:db --seed` | 执行 `database/vnerpdacahng_schema.sql`(含 CREATE DATABASE IF NOT EXISTS / INSERT IGNORE 容错),建表 + 种子数据 | -| 迭代开发改 schema | `pnpm setup:db` → `pnpm migrate` | 基于 `src/lib/db/schema.ts` 执行数据库迁移 | +| 场景 | 命令 | 说明 | +|------|------|------| +| 首次搭建/重置 | `pnpm setup:db --seed` | 执行 `database/vnerpdacahng_schema.sql`(含 CREATE DATABASE IF NOT EXISTS / INSERT IGNORE 容错),一次性建表 + 灌种子数据 | +| 迭代开发改 schema | `pnpm db:generate` → `pnpm db:push` | 基于 `src/lib/db/schema.ts` 生成 Drizzle 迁移文件并应用 | + +> ⚠️ `vnerpdacahng_schema.sql`(业务表,含 `inv_*`/`crm_*`/`fin_*` 前缀)与 `schema.ts`(Drizzle 定义)目前存在表名不一致,详见 [docs/03-技术规范/已知问题.md](docs/03-技术规范/已知问题.md) DATA-001。 - **ORM**:Drizzle ORM 0.45,schema 定义在 `src/lib/db/schema.ts` - **可视化**:`pnpm db:studio` 启动 Drizzle Studio - -> ⚠️ `vnerpdacahng_schema.sql` 与 `schema.ts` 目前存在表名不一致,详见 [docs/03-技术规范/已知问题.md](docs/03-技术规范/已知问题.md) DATA-001。 +- **已废弃**:旧的 `/api/setup/create-tables` HTTP 接口已弃用,请使用 CLI 脚本 ### Docker 部署 @@ -247,75 +213,27 @@ docker-compose -f docker-compose.prod.yml up -d 参考 [.env.example](.env.example),关键字段: -| 变量 | 说明 | -| ----------------------------------------------------------------------- | ------------------ | -| `DB_HOST` / `DB_PORT` / `DB_USER` / `DB_PASSWORD` / `DB_NAME` | MySQL 连接 | -| `JWT_SECRET` | JWT 签名密钥 | -| `NEXT_PUBLIC_APP_NAME` | 应用名称 | -| `DEV_ORIGINS` | 开发环境跨域白名单 | +| 变量 | 说明 | +|------|------| +| `DB_HOST` / `DB_PORT` / `DB_USER` / `DB_PASSWORD` / `DB_NAME` | MySQL 连接 | +| `JWT_SECRET` | JWT 签名密钥 | +| `NEXT_PUBLIC_APP_NAME` | 应用名称 | +| `DEV_ORIGINS` | 开发环境跨域白名单 | ## 安全说明 - 密码使用 bcryptjs 加盐哈希存储 -- API 通过 JWT (jose) 鉴权,支持 401 无感刷新(并发锁防重复刷新) +- API 通过 JWT (jose) 鉴权,支持 401 无感刷新(`authFetch` 并发锁防重复刷新) - 登录限流(IP 维度 15 分钟 20 次)+ 失败锁定(5 次/15 分钟) - 所有 SQL 使用参数化查询防注入 -- 生产环境屏蔽 `/debug`、`/test-api`、`/diagnostic` 等调试路由 - -## 提交规范与 CI/CD - -### 提交规范 - -遵循 [Conventional Commits](https://www.conventionalcommits.org/): - -``` -[scope]: -``` - -| type | 用途 | -| -------- | --------- | -| feat | 新功能 | -| fix | Bug 修复 | -| docs | 文档 | -| refactor | 重构 | -| test | 测试 | -| build | 构建/依赖 | -| ci | CI 配置 | -| chore | 维护 | - -示例:`feat(warehouse): 添加入库单审核功能` - -### 提交门禁 - -| Hook | 作用 | -| -------------- | -------------------------------------------- | -| `pre-commit` | lint-staged 对暂存文件运行 ESLint + Prettier | -| `commit-msg` | commitlint 校验提交信息格式 | - -### CI 流水线 - -GitHub Actions 流水线([.github/workflows/ci.yml](.github/workflows/ci.yml)): - -1. **lint** — ESLint 检查 -2. **ts-check** — TypeScript 类型检查 -3. **test:coverage** — Vitest 单元测试 + 覆盖率(阈值 80%) -4. **e2e-test** — Playwright E2E 测试 -5. **build** — Next.js 构建(仅 `main` 分支) +- 生产环境屏蔽 `/debug`、`/test-api`、`/diagnostic` 等调试路由(`src/middleware.ts`) ## 文档 完整项目文档位于 [docs/](docs/),入口索引见 [docs/README.md](docs/README.md)。 -工具脚本说明见 [scripts/README.md](scripts/README.md),涵盖数据库管理、i18n 工具、部署脚本、诊断调试等 9 大类脚本。 - ## 许可证 -本报告及其所含全部内容(包括但不限于文字、图表、代码示例、数据分析结论、架构设计描述等)的版权归 **落尘** 所有,保留一切权利。未经书面授权,任何单位与个人不得以任何形式复制、转载、摘编、镜像或用于商业用途。 - -- **开发者:** 落尘 -- **联系邮箱:** [364301747@qq.com](mailto:364301747@qq.com) -- **报告版本:** 1.0 -- **生成日期:** 2026-07-07 -- **项目名称:** 印刷生产经营信息管理系统 Print MIS +本项目仅供内部使用。 -> 最后更新:2026-07-20 \ No newline at end of file +> 最后更新:2026-06-30 diff --git a/_chain_result.txt b/_chain_result.txt deleted file mode 100644 index 76405d0a..00000000 --- a/_chain_result.txt +++ /dev/null @@ -1,22 +0,0 @@ -{ - "sample": 10, - "stdcard": 10, - "transfer": 10, - "wo": 10, - "pr": 10, - "po": 10, - "ib": 10, - "qcin": 10, - "cut": 10, - "pick": 10, - "wr": 10, - "fo": 10, - "qcf": 10, - "so": 10, - "dl": 10, - "recv": 10, - "batch": 30, - "inv": 20, - "txn": 40, - "log": 10 -} \ No newline at end of file diff --git a/_dbprobe.cjs b/_dbprobe.cjs deleted file mode 100644 index 99ecdf84..00000000 --- a/_dbprobe.cjs +++ /dev/null @@ -1,14 +0,0 @@ -const mysql = require('mysql2/promise'); -(async () => { - const pwds = [process.env.DB_PASSWORD || '', 'Snqig521223']; - for (const pwd of pwds) { - try { - const c = await mysql.createConnection({host:'127.0.0.1',port:3306,user:'root',password:pwd,database:'vnerpdacahng',connectTimeout:3000}); - const [r] = await c.query('SELECT DATABASE() AS db, (SELECT COUNT(*) FROM inv_inbound_order) AS t'); - console.log('DB OK pwdLen='+pwd.length, JSON.stringify(r[0])); - await c.end(); - process.exit(0); - } catch(e){ console.log('fail pwdLen='+pwd.length, String(e.message).split('\n')[0]); } - } - process.exit(2); -})(); diff --git a/_devlog.txt b/_devlog.txt deleted file mode 100644 index 5639aa6a..00000000 --- a/_devlog.txt +++ /dev/null @@ -1,9488 +0,0 @@ - -> vnerp@0.1.0 dev -> next dev -p 5000 - -▲ Next.js 16.1.1 (Turbopack) -- Local: http://localhost:5000 -- Network: http://192.168.0.157:5000 -- Environments: .env.local, .env -- Experiments (use with caution): - · optimizePackageImports - -✓ Starting... -✓ Ready in 4.7s -[2026-08-27 11:27:23.510 +0800] INFO: [instrumentation] Server started. OutboxPoller will auto-start on first API request. -○ Compiling /[locale]/login ... - GET /login?next=%2F 200 in 7.1s (compile: 6.1s, proxy.ts: 15ms, generate-params: 1595ms, render: 938ms) - GET /login?next=%2F 200 in 161ms (compile: 42ms, proxy.ts: 10ms, generate-params: 12ms, render: 109ms) - GET /en/login 200 in 161ms (compile: 11ms, proxy.ts: 8ms, generate-params: 37µs, render: 142ms) - GET /en/login 200 in 192ms (compile: 23ms, proxy.ts: 9ms, generate-params: 12ms, render: 160ms) - GET /en/login 200 in 183ms (compile: 29ms, proxy.ts: 10ms, generate-params: 13ms, render: 145ms) - GET /en/login 200 in 210ms (compile: 20ms, proxy.ts: 10ms, generate-params: 10ms, render: 181ms) -[2026-08-27 11:29:25.984 +0800] DEBUG: [auth.login] 用户登录 - ctx: { - "module": "auth", - "action": "login", - "traceId": "mtayrun4-hl2qdul" - } - step: "用户登录" - data: { - "clientIP": "::1" - } -[2026-08-27 11:29:26.179 +0800] DEBUG: [auth.login] 密码验证: 密码正确 -> PASS - ctx: { - "module": "auth", - "action": "login", - "traceId": "mtayrun4-hl2qdul" - } - branch: "密码验证" - condition: "密码正确" - status: "PASS" - data: { - "userId": 1 - } - POST /api/auth/login 200 in 1191ms (compile: 900ms, proxy.ts: 6ms, render: 286ms) -[2026-08-27 11:29:26.235 +0800] INFO: CacheManager: 使用 InMemoryCacheManager(未配置 REDIS_URL) - GET /api/auth/menus 200 in 246ms (compile: 116ms, proxy.ts: 11ms, render: 119ms) - GET /en/dashboard 200 in 2.0s (compile: 1923ms, proxy.ts: 11ms, generate-params: 1739ms, render: 81ms) - GET /en/dashboard 200 in 115ms (compile: 54ms, proxy.ts: 13ms, generate-params: 15ms, render: 48ms) - GET /api/system/notice?page=1&pageSize=10 200 in 281ms (compile: 216ms, proxy.ts: 15ms, render: 49ms) - GET /api/system/config?pageSize=200 200 in 301ms (compile: 88ms, proxy.ts: 9ms, render: 204ms) - GET /api/dashboard 200 in 307ms (compile: 144ms, proxy.ts: 14ms, render: 149ms) - GET /api/system/notice?page=1&pageSize=10 200 in 173ms (compile: 34ms, proxy.ts: 16ms, render: 124ms) - GET /api/system/config?pageSize=200 200 in 176ms (compile: 33ms, proxy.ts: 23ms, render: 121ms) - GET /api/organization?type=company 200 in 173ms (compile: 75ms, proxy.ts: 41ms, render: 57ms) - GET /api/dashboard 200 in 182ms (compile: 12ms, proxy.ts: 41ms, render: 128ms) - GET /api/organization?type=company 200 in 47ms (compile: 9ms, proxy.ts: 20ms, render: 17ms) - GET /api/system/config?pageSize=200 200 in 75ms (compile: 11ms, proxy.ts: 15ms, render: 49ms) - GET /api/system/config?pageSize=200 200 in 39ms (compile: 5ms, proxy.ts: 7ms, render: 27ms) - GET /en/warehouse/inbound 200 in 2.6s (compile: 1947ms, proxy.ts: 6ms, generate-params: 1676ms, render: 632ms) - GET /api/auth/menus 200 in 42ms (compile: 13ms, proxy.ts: 10ms, render: 19ms) - GET /api/system/config?pageSize=200 200 in 65ms (compile: 5ms, proxy.ts: 10ms, render: 49ms) - GET /api/system/notice?page=1&pageSize=10 200 in 118ms (compile: 33ms, proxy.ts: 12ms, render: 73ms) - GET /api/system/config?pageSize=200 200 in 136ms (compile: 14ms, proxy.ts: 13ms, render: 109ms) -[RepoRegistry] active impl = mysql (default; set REPOSITORY_IMPL=drizzle to switch) - GET /api/organization/warehouse-category 200 in 572ms (compile: 200ms, proxy.ts: 20ms, render: 351ms) - GET /api/purchase/suppliers?pageSize=1000 200 in 576ms (compile: 143ms, proxy.ts: 23ms, render: 410ms) -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) -[2026-08-27 11:29:33.558 +0800] INFO: Event registry initialized - inboundApprovedHandlers: 6 - purchaseReceivedHandlers: 1 - salesShippedHandlers: 4 - deliveryShippedHandlers: 4 - returnOrderCompletedHandlers: 3 - reconciliationWrittenOffHandlers: 3 - purchaseReturnCompletedHandlers: 3 - purchaseReconWrittenOffHandlers: 3 - workorderCompletedHandlers: 8 - qualityUnqualifiedCompletedHandlers: 2 -[2026-08-27 11:29:33.559 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:29:33.559 +0800] INFO: Outbox poller starting - intervalMs: 5000 - reclaimIntervalMs: 60000 - cleanupIntervalMs: 86400000 - dispatchingTimeoutMinutes: 10 -[2026-08-27 11:29:33.561 +0800] INFO: OutboxPoller auto-started (EVENT_BUS_TYPE=db) -[2026-08-27 11:29:33.561 +0800] INFO: StreamConsumer not started (Redis unavailable, OutboxPoller using direct publish) - GET /api/warehouse/inbound/labels?pageSize=1000 200 in 494ms (compile: 210ms, proxy.ts: 15ms, render: 270ms) - GET /api/warehouse?all=true 200 in 607ms (compile: 250ms, proxy.ts: 23ms, render: 334ms) - GET /api/warehouse/inbound?page=1&pageSize=1000 200 in 646ms (compile: 512ms, proxy.ts: 36ms, render: 99ms) - GET /api/system/notice?page=1&pageSize=10 200 in 550ms (compile: 426ms, proxy.ts: 13ms, render: 111ms) - GET /api/organization?type=company 200 in 137ms (compile: 25ms, proxy.ts: 37ms, render: 74ms) - GET /api/organization/warehouse-category 200 in 139ms (compile: 29ms, proxy.ts: 38ms, render: 71ms) - GET /api/purchase/suppliers?pageSize=1000 200 in 132ms (compile: 34ms, proxy.ts: 29ms, render: 69ms) - GET /api/warehouse/inbound/labels?pageSize=1000 200 in 128ms (compile: 28ms, proxy.ts: 34ms, render: 66ms) - GET /api/system/config?pageSize=200 200 in 172ms (compile: 22ms, proxy.ts: 37ms, render: 113ms) - GET /api/warehouse?all=true 200 in 127ms (compile: 23ms, proxy.ts: 44ms, render: 60ms) -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) -[2026-08-27 11:29:33.791 +0800] DEBUG: Event registry already initialized - GET /api/warehouse/inbound?page=1&pageSize=1000 200 in 66ms (compile: 11ms, proxy.ts: 24ms, render: 31ms) - GET /api/system/config?pageSize=200 200 in 52ms (compile: 6ms, proxy.ts: 17ms, render: 29ms) - GET /api/system/config?pageSize=200 200 in 33ms (compile: 4ms, proxy.ts: 4ms, render: 25ms) - GET /api/organization?type=company 200 in 22ms (compile: 3ms, proxy.ts: 5ms, render: 14ms) - GET /en/login 200 in 140ms (compile: 21ms, proxy.ts: 8ms, generate-params: 10ms, render: 112ms) -[2026-08-27 11:29:38.348 +0800] DEBUG: [auth.login] 用户登录 - ctx: { - "module": "auth", - "action": "login", - "traceId": "mtays46k-qvjhd3z" - } - step: "用户登录" - data: { - "clientIP": "::1" - } -[2026-08-27 11:29:38.444 +0800] DEBUG: [auth.login] 密码验证: 密码正确 -> PASS - ctx: { - "module": "auth", - "action": "login", - "traceId": "mtays46k-qvjhd3z" - } - branch: "密码验证" - condition: "密码正确" - status: "PASS" - data: { - "userId": 1 - } - POST /api/auth/login 200 in 144ms (compile: 5ms, proxy.ts: 5ms, render: 135ms) - GET /api/auth/menus 200 in 79ms (compile: 9ms, proxy.ts: 7ms, render: 63ms) -[2026-08-27 11:29:38.563 +0800] DEBUG: Event registry already initialized - GET /en/dashboard 200 in 84ms (compile: 25ms, proxy.ts: 15ms, generate-params: 10ms, render: 44ms) -[2026-08-27 11:29:38.573 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" - GET /en/dashboard 200 in 46ms (compile: 11ms, proxy.ts: 7ms, generate-params: 50µs, render: 28ms) - GET /api/system/notice?page=1&pageSize=10 200 in 56ms (compile: 8ms, proxy.ts: 17ms, render: 31ms) - GET /api/system/config?pageSize=200 200 in 69ms (compile: 10ms, proxy.ts: 5ms, render: 54ms) - GET /api/dashboard 200 in 69ms (compile: 11ms, proxy.ts: 9ms, render: 50ms) - GET /api/system/notice?page=1&pageSize=10 200 in 60ms (compile: 9ms, proxy.ts: 12ms, render: 39ms) - GET /api/system/config?pageSize=200 200 in 67ms (compile: 14ms, proxy.ts: 10ms, render: 42ms) - GET /api/dashboard 200 in 54ms (compile: 5ms, proxy.ts: 12ms, render: 36ms) - GET /api/system/config?pageSize=200 200 in 38ms (compile: 5ms, proxy.ts: 9ms, render: 25ms) - GET /api/system/config?pageSize=200 200 in 41ms (compile: 4ms, proxy.ts: 8ms, render: 30ms) - GET /api/organization?type=company 200 in 32ms (compile: 4ms, proxy.ts: 9ms, render: 19ms) - GET /api/organization?type=company 200 in 23ms (compile: 4ms, proxy.ts: 5ms, render: 15ms) - GET /en/warehouse/inbound 200 in 217ms (compile: 18ms, proxy.ts: 5ms, generate-params: 9ms, render: 195ms) - GET /api/auth/menus 200 in 31ms (compile: 10ms, proxy.ts: 6ms, render: 15ms) - GET /api/system/config?pageSize=200 200 in 44ms (compile: 5ms, proxy.ts: 7ms, render: 32ms) -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) -[2026-08-27 11:29:40.862 +0800] DEBUG: Event registry already initialized - GET /api/system/notice?page=1&pageSize=10 200 in 118ms (compile: 36ms, proxy.ts: 19ms, render: 64ms) - GET /api/organization/warehouse-category 200 in 104ms (compile: 27ms, proxy.ts: 30ms, render: 48ms) - GET /api/system/config?pageSize=200 200 in 139ms (compile: 23ms, proxy.ts: 14ms, render: 103ms) - GET /api/purchase/suppliers?pageSize=1000 200 in 113ms (compile: 27ms, proxy.ts: 35ms, render: 51ms) - GET /api/warehouse/inbound?page=1&pageSize=1000 200 in 129ms (compile: 19ms, proxy.ts: 33ms, render: 78ms) - GET /api/warehouse?all=true 200 in 127ms (compile: 24ms, proxy.ts: 30ms, render: 73ms) - GET /api/warehouse/inbound/labels?pageSize=1000 200 in 43ms (compile: 6ms, proxy.ts: 16ms, render: 21ms) - GET /api/system/config?pageSize=200 200 in 44ms (compile: 11ms, proxy.ts: 8ms, render: 25ms) -[2026-08-27 11:29:43.563 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:29:43.566 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" - GET /api/warehouse?all=true&status=active 200 in 32ms (compile: 7ms, proxy.ts: 10ms, render: 15ms) - GET /api/system/currency?active=true 200 in 131ms (compile: 65ms, proxy.ts: 11ms, render: 55ms) - GET /api/warehouse/categories 200 in 136ms (compile: 107ms, proxy.ts: 9ms, render: 20ms) - GET /api/warehouse?all=true&status=active 200 in 112ms (compile: 4ms, proxy.ts: 5ms, render: 103ms) - GET /api/warehouse/categories 200 in 25ms (compile: 8ms, proxy.ts: 6ms, render: 11ms) - GET /api/system/currency?active=true 200 in 29ms (compile: 4ms, proxy.ts: 7ms, render: 18ms) -[2026-08-27 11:29:45.819 +0800] INFO: [warehouse/inbound] 开始物料分类校验 - itemCount: 1 - materialIds: [] -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) -[2026-08-27 11:29:45.822 +0800] INFO: [warehouse/inbound] 物料分类校验完成 - blocked: false - uncategorizedCount: 0 -[2026-08-27 11:29:45.822 +0800] DEBUG: Event registry already initialized - POST /api/warehouse/inbound 200 in 54ms (compile: 3ms, proxy.ts: 5ms, render: 46ms) -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) -[2026-08-27 11:29:45.877 +0800] DEBUG: Event registry already initialized - GET /api/warehouse/inbound?page=1&pageSize=1000 200 in 27ms (compile: 4ms, proxy.ts: 5ms, render: 18ms) - GET /en/login 200 in 131ms (compile: 17ms, proxy.ts: 9ms, generate-params: 9ms, render: 104ms) -[2026-08-27 11:29:48.563 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:29:48.566 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:29:48.679 +0800] DEBUG: [auth.login] 用户登录 - ctx: { - "module": "auth", - "action": "login", - "traceId": "mtaysc5j-ya9tvxm" - } - step: "用户登录" - data: { - "clientIP": "::1" - } -[2026-08-27 11:29:48.790 +0800] DEBUG: [auth.login] 密码验证: 密码正确 -> PASS - ctx: { - "module": "auth", - "action": "login", - "traceId": "mtaysc5j-ya9tvxm" - } - branch: "密码验证" - condition: "密码正确" - status: "PASS" - data: { - "userId": 1 - } - POST /api/auth/login 200 in 159ms (compile: 5ms, proxy.ts: 4ms, render: 149ms) - GET /api/auth/menus 200 in 99ms (compile: 11ms, proxy.ts: 14ms, render: 74ms) - GET /en/dashboard 200 in 106ms (compile: 40ms, proxy.ts: 13ms, generate-params: 15ms, render: 52ms) - GET /en/dashboard 200 in 51ms (compile: 13ms, proxy.ts: 10ms, generate-params: 34µs, render: 28ms) - GET /api/system/notice?page=1&pageSize=10 200 in 66ms (compile: 9ms, proxy.ts: 21ms, render: 36ms) - GET /api/system/config?pageSize=200 200 in 75ms (compile: 14ms, proxy.ts: 6ms, render: 54ms) - GET /api/dashboard 200 in 87ms (compile: 13ms, proxy.ts: 19ms, render: 55ms) - GET /api/system/notice?page=1&pageSize=10 200 in 74ms (compile: 11ms, proxy.ts: 13ms, render: 50ms) - GET /api/system/config?pageSize=200 200 in 85ms (compile: 18ms, proxy.ts: 12ms, render: 54ms) - GET /api/dashboard 200 in 81ms (compile: 9ms, proxy.ts: 12ms, render: 60ms) - GET /api/system/config?pageSize=200 200 in 50ms (compile: 7ms, proxy.ts: 8ms, render: 35ms) - GET /api/organization?type=company 200 in 33ms (compile: 5ms, proxy.ts: 7ms, render: 21ms) - GET /api/system/config?pageSize=200 200 in 57ms (compile: 6ms, proxy.ts: 9ms, render: 43ms) - GET /api/organization?type=company 200 in 28ms (compile: 5ms, proxy.ts: 6ms, render: 18ms) - GET /en/warehouse/inbound 200 in 343ms (compile: 28ms, proxy.ts: 7ms, generate-params: 12ms, render: 307ms) - GET /api/auth/menus 200 in 50ms (compile: 14ms, proxy.ts: 11ms, render: 26ms) - GET /api/system/config?pageSize=200 200 in 69ms (compile: 9ms, proxy.ts: 12ms, render: 48ms) - GET /api/system/config?pageSize=200 200 in 147ms (compile: 13ms, proxy.ts: 12ms, render: 123ms) -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) -[2026-08-27 11:29:51.557 +0800] DEBUG: Event registry already initialized - GET /api/system/notice?page=1&pageSize=10 200 in 146ms (compile: 42ms, proxy.ts: 13ms, render: 91ms) - GET /api/organization/warehouse-category 200 in 126ms (compile: 32ms, proxy.ts: 29ms, render: 65ms) - GET /api/warehouse/inbound?page=1&pageSize=1000 200 in 149ms (compile: 19ms, proxy.ts: 35ms, render: 95ms) - GET /api/warehouse?all=true 200 in 147ms (compile: 25ms, proxy.ts: 33ms, render: 89ms) - GET /api/purchase/suppliers?pageSize=1000 200 in 137ms (compile: 37ms, proxy.ts: 29ms, render: 71ms) - GET /api/warehouse/inbound/labels?pageSize=1000 200 in 126ms (compile: 24ms, proxy.ts: 33ms, render: 69ms) -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) -[2026-08-27 11:29:51.709 +0800] DEBUG: Event registry already initialized - GET /api/organization/warehouse-category 200 in 108ms (compile: 22ms, proxy.ts: 35ms, render: 51ms) - GET /api/system/notice?page=1&pageSize=10 200 in 123ms (compile: 29ms, proxy.ts: 26ms, render: 68ms) - GET /api/warehouse/inbound?page=1&pageSize=1000 200 in 138ms (compile: 26ms, proxy.ts: 35ms, render: 77ms) - GET /api/system/config?pageSize=200 200 in 179ms (compile: 30ms, proxy.ts: 32ms, render: 117ms) - GET /api/warehouse?all=true 200 in 144ms (compile: 30ms, proxy.ts: 34ms, render: 80ms) - GET /api/purchase/suppliers?pageSize=1000 200 in 124ms (compile: 29ms, proxy.ts: 50ms, render: 46ms) - GET /api/organization?type=company 200 in 102ms (compile: 20ms, proxy.ts: 44ms, render: 37ms) - GET /api/warehouse/inbound/labels?pageSize=1000 200 in 72ms (compile: 16ms, proxy.ts: 24ms, render: 32ms) - GET /api/system/config?pageSize=200 200 in 80ms (compile: 20ms, proxy.ts: 25ms, render: 36ms) - GET /api/system/config?pageSize=200 200 in 46ms (compile: 5ms, proxy.ts: 8ms, render: 34ms) - GET /api/organization?type=company 200 in 26ms (compile: 4ms, proxy.ts: 6ms, render: 16ms) -[2026-08-27 11:29:53.568 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:29:53.571 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" - GET /api/warehouse/categories 200 in 34ms (compile: 7ms, proxy.ts: 8ms, render: 19ms) - GET /api/system/currency?active=true 200 in 36ms (compile: 13ms, proxy.ts: 8ms, render: 15ms) - GET /api/warehouse?all=true&status=active 200 in 41ms (compile: 10ms, proxy.ts: 8ms, render: 23ms) - GET /api/warehouse/categories 200 in 48ms (compile: 7ms, proxy.ts: 11ms, render: 30ms) - GET /api/system/currency?active=true 200 in 45ms (compile: 9ms, proxy.ts: 8ms, render: 28ms) - GET /api/warehouse?all=true&status=active 200 in 48ms (compile: 15ms, proxy.ts: 8ms, render: 25ms) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) -[2026-08-27 11:29:56.654 +0800] DEBUG: Event registry already initialized -[PurchaseRepository] 未知采购单状态码 status=2 (order id=137, po_no=PO_1787788536155_5),降级为 draft -[PurchaseRepository] 未知采购单状态码 status=2 (order id=136, po_no=PO_1787788536129_4),降级为 draft -[PurchaseRepository] 未知采购单状态码 status=2 (order id=135, po_no=PO_1787788536081_3),降级为 draft -[PurchaseRepository] 未知采购单状态码 status=2 (order id=134, po_no=PO_1787788536031_2),降级为 draft -[PurchaseRepository] 未知采购单状态码 status=2 (order id=133, po_no=PO_1787788535988_1),降级为 draft -[PurchaseRepository] 未知采购单状态码 status=2 (order id=132, po_no=PO_1787788535949_0),降级为 draft -[PurchaseRepository] 未知采购单状态码 status=2 (order id=129, po_no=PO_1787711404257_3),降级为 draft -[PurchaseRepository] 未知采购单状态码 status=2 (order id=128, po_no=PO_1787711404236_2),降级为 draft -[PurchaseRepository] 未知采购单状态码 status=2 (order id=127, po_no=PO_1787711404206_1),降级为 draft -[PurchaseRepository] 未知采购单状态码 status=2 (order id=126, po_no=PO_1787711404179_0),降级为 draft -[PurchaseRepository] 未知采购单状态码 status=2 (order id=131, po_no=PO_1787711404307_5),降级为 draft -[PurchaseRepository] 未知采购单状态码 status=2 (order id=130, po_no=PO_1787711404285_4),降级为 draft -[PurchaseRepository] 未知采购单状态码 status=2 (order id=122, po_no=PO_1787711344664_2),降级为 draft -[PurchaseRepository] 未知采购单状态码 status=2 (order id=123, po_no=PO_1787711344680_3),降级为 draft -[PurchaseRepository] 未知采购单状态码 status=2 (order id=124, po_no=PO_1787711344710_4),降级为 draft -[PurchaseRepository] 未知采购单状态码 status=2 (order id=125, po_no=PO_1787711344724_5),降级为 draft -[PurchaseRepository] 未知采购单状态码 status=2 (order id=120, po_no=PO_1787711344588_0),降级为 draft -[PurchaseRepository] 未知采购单状态码 status=2 (order id=121, po_no=PO_1787711344607_1),降级为 draft -[PurchaseRepository] 未知采购单状态码 status=2 (order id=119, po_no=PO_1787711276136_5),降级为 draft -[PurchaseRepository] 未知采购单状态码 status=2 (order id=118, po_no=PO_1787711276106_4),降级为 draft - GET /api/purchase/orders?keyword=PO&pageSize=20 200 in 122ms (compile: 84ms, proxy.ts: 5ms, render: 33ms) - GET /en/login 200 in 122ms (compile: 22ms, proxy.ts: 7ms, generate-params: 13ms, render: 93ms) -[2026-08-27 11:29:57.812 +0800] DEBUG: [auth.login] 用户登录 - ctx: { - "module": "auth", - "action": "login", - "traceId": "mtaysj78-0vpiw32" - } - step: "用户登录" - data: { - "clientIP": "::1" - } -[2026-08-27 11:29:57.905 +0800] DEBUG: [auth.login] 密码验证: 密码正确 -> PASS - ctx: { - "module": "auth", - "action": "login", - "traceId": "mtaysj78-0vpiw32" - } - branch: "密码验证" - condition: "密码正确" - status: "PASS" - data: { - "userId": 1 - } - POST /api/auth/login 200 in 141ms (compile: 4ms, proxy.ts: 6ms, render: 131ms) - GET /api/auth/menus 200 in 70ms (compile: 6ms, proxy.ts: 6ms, render: 58ms) - GET /en/dashboard 200 in 72ms (compile: 24ms, proxy.ts: 11ms, generate-params: 11ms, render: 37ms) - GET /en/dashboard 200 in 43ms (compile: 10ms, proxy.ts: 5ms, generate-params: 75µs, render: 27ms) - GET /api/system/notice?page=1&pageSize=10 200 in 51ms (compile: 14ms, proxy.ts: 11ms, render: 27ms) - GET /api/system/config?pageSize=200 200 in 76ms (compile: 10ms, proxy.ts: 9ms, render: 57ms) - GET /api/dashboard 200 in 86ms (compile: 14ms, proxy.ts: 15ms, render: 58ms) - GET /api/system/notice?page=1&pageSize=10 200 in 70ms (compile: 7ms, proxy.ts: 18ms, render: 45ms) - GET /api/system/config?pageSize=200 200 in 63ms (compile: 11ms, proxy.ts: 14ms, render: 38ms) - GET /api/dashboard 200 in 51ms (compile: 8ms, proxy.ts: 15ms, render: 29ms) - GET /api/system/config?pageSize=200 200 in 37ms (compile: 5ms, proxy.ts: 7ms, render: 25ms) - GET /api/organization?type=company 200 in 43ms (compile: 6ms, proxy.ts: 10ms, render: 28ms) - GET /api/system/config?pageSize=200 200 in 60ms (compile: 13ms, proxy.ts: 9ms, render: 38ms) - GET /api/organization?type=company 200 in 35ms (compile: 8ms, proxy.ts: 12ms, render: 15ms) -[2026-08-27 11:29:58.568 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:29:58.571 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" - GET /en/warehouse/inbound 200 in 345ms (compile: 39ms, proxy.ts: 5ms, generate-params: 13ms, render: 301ms) - GET /api/auth/menus 200 in 60ms (compile: 16ms, proxy.ts: 19ms, render: 25ms) - GET /api/system/config?pageSize=200 200 in 78ms (compile: 11ms, proxy.ts: 19ms, render: 48ms) - GET /api/system/notice?page=1&pageSize=10 200 in 126ms (compile: 27ms, proxy.ts: 10ms, render: 90ms) -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) -[2026-08-27 11:30:00.841 +0800] DEBUG: Event registry already initialized - GET /api/system/config?pageSize=200 200 in 156ms (compile: 9ms, proxy.ts: 13ms, render: 134ms) - GET /api/organization/warehouse-category 200 in 119ms (compile: 35ms, proxy.ts: 30ms, render: 54ms) - GET /api/warehouse/inbound?page=1&pageSize=1000 200 in 141ms (compile: 23ms, proxy.ts: 30ms, render: 87ms) - GET /api/purchase/suppliers?pageSize=1000 200 in 129ms (compile: 40ms, proxy.ts: 25ms, render: 65ms) - GET /api/warehouse?all=true 200 in 143ms (compile: 28ms, proxy.ts: 29ms, render: 86ms) - GET /api/warehouse/inbound/labels?pageSize=1000 200 in 153ms (compile: 40ms, proxy.ts: 42ms, render: 71ms) -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) -[2026-08-27 11:30:01.013 +0800] DEBUG: Event registry already initialized - GET /api/organization/warehouse-category 200 in 126ms (compile: 30ms, proxy.ts: 45ms, render: 50ms) - GET /api/system/notice?page=1&pageSize=10 200 in 141ms (compile: 33ms, proxy.ts: 31ms, render: 76ms) - GET /api/purchase/suppliers?pageSize=1000 200 in 137ms (compile: 36ms, proxy.ts: 42ms, render: 59ms) - GET /api/warehouse/inbound?page=1&pageSize=1000 200 in 152ms (compile: 32ms, proxy.ts: 42ms, render: 77ms) - GET /api/system/config?pageSize=200 200 in 177ms (compile: 37ms, proxy.ts: 33ms, render: 108ms) - GET /api/warehouse?all=true 200 in 119ms (compile: 26ms, proxy.ts: 34ms, render: 60ms) - GET /api/organization?type=company 200 in 88ms (compile: 22ms, proxy.ts: 23ms, render: 43ms) - GET /api/warehouse/inbound/labels?pageSize=1000 200 in 80ms (compile: 19ms, proxy.ts: 27ms, render: 35ms) - GET /api/system/config?pageSize=200 200 in 71ms (compile: 15ms, proxy.ts: 20ms, render: 36ms) - GET /api/system/config?pageSize=200 200 in 36ms (compile: 5ms, proxy.ts: 6ms, render: 25ms) - GET /api/organization?type=company 200 in 31ms (compile: 5ms, proxy.ts: 7ms, render: 19ms) -[2026-08-27 11:30:03.577 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:30:03.579 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" - GET /en/warehouse/inbound/cutting 200 in 2.6s (compile: 2.1s, proxy.ts: 5ms, generate-params: 1476ms, render: 491ms) - GET /api/auth/menus 200 in 31ms (compile: 11ms, proxy.ts: 6ms, render: 13ms) - GET /api/system/config?pageSize=200 200 in 40ms (compile: 5ms, proxy.ts: 6ms, render: 28ms) - GET /api/system/notice?page=1&pageSize=10 200 in 97ms (compile: 14ms, proxy.ts: 14ms, render: 69ms) - GET /api/warehouse/inbound/cutting?page=1&pageSize=20 200 in 110ms (compile: 63ms, proxy.ts: 13ms, render: 34ms) - GET /api/system/config?pageSize=200 200 in 133ms (compile: 9ms, proxy.ts: 16ms, render: 108ms) - GET /api/system/notice?page=1&pageSize=10 200 in 74ms (compile: 12ms, proxy.ts: 25ms, render: 38ms) - GET /api/warehouse/inbound/cutting?page=1&pageSize=20 200 in 56ms (compile: 16ms, proxy.ts: 13ms, render: 27ms) - GET /api/system/config?pageSize=200 200 in 56ms (compile: 15ms, proxy.ts: 15ms, render: 25ms) - GET /api/system/config?pageSize=200 200 in 35ms (compile: 5ms, proxy.ts: 7ms, render: 23ms) - GET /api/organization?type=company 200 in 32ms (compile: 5ms, proxy.ts: 8ms, render: 19ms) - GET /api/system/config?pageSize=200 200 in 44ms (compile: 9ms, proxy.ts: 8ms, render: 27ms) - GET /api/organization?type=company 200 in 29ms (compile: 9ms, proxy.ts: 7ms, render: 13ms) -[2026-08-27 11:30:08.583 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:30:08.586 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:30:13.598 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:30:13.601 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:30:18.612 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:30:18.615 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:30:23.624 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:30:23.626 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:30:28.624 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:30:28.626 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:30:33.624 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:30:33.627 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:30:38.630 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:30:38.632 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:30:43.641 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:30:43.643 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:30:48.644 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:30:48.646 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:30:53.654 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:30:53.657 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:30:58.714 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:30:58.777 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:31:03.730 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:31:03.734 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:31:08.740 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:31:08.744 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:31:13.745 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:31:13.759 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:31:18.746 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:31:18.751 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:31:23.751 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:31:23.757 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:31:28.756 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:31:28.761 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:31:33.829 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:31:33.834 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:31:38.846 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:31:38.956 +0800] DEBUG: OutboxPoller: claimed pending events - count: 2 - eventIds: [ - 9002168, - 9002169 - ] - mode: "memory" -[2026-08-27 11:31:38.957 +0800] DEBUG: No audit log mapping for event - eventType: "purchase.received" -[2026-08-27 11:31:38.957 +0800] DEBUG: OutboxPoller: event published (memory mode) - eventId: 9002168 - eventType: "purchase.received" -[2026-08-27 11:31:38.967 +0800] DEBUG: OutboxPoller: event marked as processed - eventId: 9002168 -[2026-08-27 11:31:38.967 +0800] DEBUG: No audit log mapping for event - eventType: "purchase.received" -[2026-08-27 11:31:38.967 +0800] DEBUG: OutboxPoller: event published (memory mode) - eventId: 9002169 - eventType: "purchase.received" -[2026-08-27 11:31:38.977 +0800] DEBUG: OutboxPoller: event marked as processed - eventId: 9002169 -[2026-08-27 11:31:38.977 +0800] INFO: Outbox poll cycle - processed: 2 - failed: 0 - retried: 0 - mode: "memory" -[2026-08-27 11:31:43.859 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:31:43.877 +0800] DEBUG: OutboxPoller: claimed pending events - count: 2 - eventIds: [ - 9002170, - 9002171 - ] - mode: "memory" -[2026-08-27 11:31:43.877 +0800] DEBUG: No audit log mapping for event - eventType: "purchase.received" -[2026-08-27 11:31:43.877 +0800] DEBUG: OutboxPoller: event published (memory mode) - eventId: 9002170 - eventType: "purchase.received" -[2026-08-27 11:31:43.887 +0800] DEBUG: OutboxPoller: event marked as processed - eventId: 9002170 -[2026-08-27 11:31:43.887 +0800] DEBUG: No audit log mapping for event - eventType: "purchase.received" -[2026-08-27 11:31:43.887 +0800] DEBUG: OutboxPoller: event published (memory mode) - eventId: 9002171 - eventType: "purchase.received" -[2026-08-27 11:31:43.897 +0800] DEBUG: OutboxPoller: event marked as processed - eventId: 9002171 -[2026-08-27 11:31:43.897 +0800] INFO: Outbox poll cycle - processed: 2 - failed: 0 - retried: 0 - mode: "memory" -[2026-08-27 11:31:48.865 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:31:48.868 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:31:53.880 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:31:53.887 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:31:58.895 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:31:58.904 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:32:03.909 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:32:03.913 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:32:08.914 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:32:08.922 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:32:13.916 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:32:13.920 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:32:18.924 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:32:18.929 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:32:23.925 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:32:23.930 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:32:28.930 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:32:28.935 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:32:33.934 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:32:33.937 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:32:38.934 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:32:38.937 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:32:43.940 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:32:43.942 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:32:48.955 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:32:48.957 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:32:53.964 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:32:53.966 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:32:58.973 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:32:58.975 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:33:03.985 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:33:03.990 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:33:08.991 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:33:08.994 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:33:13.994 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:33:13.996 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:33:18.995 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:33:18.997 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:33:23.999 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:33:24.001 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:33:29.007 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:33:29.009 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:33:34.020 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:33:34.022 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:33:39.020 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:33:39.022 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:33:44.024 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:33:44.027 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:33:49.039 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:33:49.042 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:33:54.047 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:33:54.050 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:33:59.055 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:33:59.058 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:34:04.070 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:34:04.073 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:34:09.069 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:34:09.071 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:34:14.078 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:34:14.081 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:34:19.086 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:34:19.089 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:34:24.096 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:34:24.098 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:34:29.099 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:34:29.101 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:34:34.105 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:34:34.107 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:34:39.118 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:34:39.120 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:34:44.126 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:34:44.128 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:34:49.135 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:34:49.138 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:34:54.140 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:34:54.142 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:34:59.153 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:34:59.156 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:35:04.161 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:35:04.163 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:35:09.168 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:35:09.170 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:35:14.175 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:35:14.177 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:35:19.175 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:35:19.177 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:35:24.184 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:35:24.187 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:35:29.193 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:35:29.196 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:35:34.197 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:35:34.199 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:35:39.202 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:35:39.204 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:35:44.213 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:35:44.216 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:35:49.224 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:35:49.227 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:35:54.235 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:35:54.237 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:35:59.236 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:35:59.238 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:36:04.248 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:36:04.250 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:36:09.257 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:36:09.259 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:36:14.261 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:36:14.263 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:36:19.264 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:36:19.266 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:36:24.265 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:36:24.268 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:36:29.276 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:36:29.279 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:36:34.287 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:36:34.289 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:36:39.290 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:36:39.292 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:36:44.298 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:36:44.300 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:36:49.305 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:36:49.310 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:36:54.305 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:36:54.308 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:36:59.308 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:36:59.310 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:37:04.309 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:37:04.311 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:37:09.324 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:37:09.327 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:37:14.328 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:37:14.330 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:37:19.336 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:37:19.339 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:37:24.343 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:37:24.356 +0800] DEBUG: OutboxPoller: claimed pending events - count: 4 - eventIds: [ - 9002172, - 9002173, - 9002174, - 9002175 - ] - mode: "memory" -[2026-08-27 11:37:24.356 +0800] DEBUG: No audit log mapping for event - eventType: "purchase.received" -[2026-08-27 11:37:24.356 +0800] DEBUG: OutboxPoller: event published (memory mode) - eventId: 9002172 - eventType: "purchase.received" -[2026-08-27 11:37:24.363 +0800] DEBUG: OutboxPoller: event marked as processed - eventId: 9002172 -[2026-08-27 11:37:24.363 +0800] DEBUG: No audit log mapping for event - eventType: "purchase.received" -[2026-08-27 11:37:24.363 +0800] DEBUG: OutboxPoller: event published (memory mode) - eventId: 9002173 - eventType: "purchase.received" -[2026-08-27 11:37:24.370 +0800] DEBUG: OutboxPoller: event marked as processed - eventId: 9002173 -[2026-08-27 11:37:24.370 +0800] DEBUG: No audit log mapping for event - eventType: "purchase.received" -[2026-08-27 11:37:24.370 +0800] DEBUG: OutboxPoller: event published (memory mode) - eventId: 9002174 - eventType: "purchase.received" -[2026-08-27 11:37:24.378 +0800] DEBUG: OutboxPoller: event marked as processed - eventId: 9002174 -[2026-08-27 11:37:24.378 +0800] DEBUG: No audit log mapping for event - eventType: "purchase.received" -[2026-08-27 11:37:24.378 +0800] DEBUG: OutboxPoller: event published (memory mode) - eventId: 9002175 - eventType: "purchase.received" -[2026-08-27 11:37:24.385 +0800] DEBUG: OutboxPoller: event marked as processed - eventId: 9002175 -[2026-08-27 11:37:24.385 +0800] INFO: Outbox poll cycle - processed: 4 - failed: 0 - retried: 0 - mode: "memory" -[2026-08-27 11:37:29.345 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:37:29.347 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:37:34.354 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:37:34.357 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:37:39.356 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:37:39.358 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:37:44.357 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:37:44.360 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:37:49.367 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:37:49.369 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:37:54.377 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:37:54.379 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:37:59.391 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:37:59.393 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:38:04.394 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:38:04.397 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:38:09.408 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:38:09.410 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:38:14.420 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:38:14.422 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:38:19.436 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:38:19.439 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:38:24.438 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:38:24.441 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:38:29.439 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:38:29.440 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:38:34.445 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:38:34.447 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:38:39.458 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:38:39.460 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:38:44.466 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:38:44.468 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:38:49.476 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:38:49.478 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:38:54.479 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:38:54.481 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:38:59.490 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:38:59.492 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:39:04.503 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:39:04.504 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:39:09.503 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:39:09.505 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:39:14.508 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:39:14.511 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:39:19.511 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:39:19.513 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:39:24.514 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:39:24.516 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:39:29.524 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:39:29.525 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:39:34.524 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:39:34.526 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:39:39.539 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:39:39.541 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:39:44.541 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:39:44.543 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:39:49.553 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:39:49.554 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:39:54.563 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:39:54.565 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:39:59.577 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:39:59.580 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:40:04.592 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:40:04.594 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:40:09.602 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:40:09.604 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:40:14.614 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:40:14.616 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:40:19.614 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:40:19.615 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:40:24.613 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:40:24.615 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:40:29.618 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:40:29.621 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:40:34.634 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:40:34.636 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:40:39.646 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:40:39.649 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:40:44.649 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:40:44.650 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:40:49.652 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:40:49.654 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:40:54.665 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:40:54.667 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:40:59.669 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:40:59.670 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:41:04.677 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:41:04.680 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:41:09.693 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:41:09.695 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:41:14.701 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:41:14.703 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:41:19.716 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:41:19.718 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:41:24.722 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:41:24.724 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:41:29.735 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:41:29.738 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:41:34.738 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:41:34.741 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:41:39.753 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:41:39.755 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:41:44.760 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:41:44.763 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:41:49.763 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:41:49.764 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:41:54.774 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:41:54.776 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:41:59.785 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:41:59.787 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:42:04.794 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:42:04.796 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:42:09.801 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:42:09.803 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:42:14.801 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:42:14.803 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:42:19.804 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:42:19.806 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:42:24.809 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:42:24.811 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:42:29.822 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:42:29.824 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:42:34.834 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:42:34.837 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:42:39.843 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:42:39.845 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:42:44.854 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:42:44.855 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:42:49.860 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:42:49.861 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:42:54.874 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:42:54.875 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:42:59.886 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:42:59.888 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:43:04.899 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:43:04.901 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:43:09.900 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:43:09.902 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:43:14.906 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:43:14.908 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:43:19.910 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:43:19.912 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:43:24.924 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:43:24.925 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:43:29.926 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:43:29.928 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:43:34.930 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:43:34.932 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:43:39.938 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:43:39.940 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:43:44.945 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:43:44.948 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:43:49.955 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:43:49.957 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:43:54.971 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:43:54.973 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:43:59.985 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:43:59.987 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:44:04.992 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:44:04.994 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:44:09.994 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:44:09.995 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:44:15.004 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:44:15.006 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:44:20.012 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:44:20.014 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:44:25.021 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:44:25.023 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:44:30.028 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:44:30.030 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:44:35.033 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:44:35.036 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:44:40.040 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:44:40.042 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:44:45.048 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:44:45.051 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:44:50.051 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:44:50.053 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:44:55.062 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:44:55.064 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:45:00.076 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:45:00.078 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:45:05.085 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:45:05.087 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:45:10.090 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:45:10.092 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:45:15.101 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:45:15.103 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:45:20.101 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:45:20.104 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:45:25.110 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:45:25.113 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:45:30.120 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:45:30.123 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:45:35.121 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:45:35.124 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" - GET /login?next=%2F 200 in 114ms (compile: 24ms, proxy.ts: 5ms, generate-params: 12ms, render: 85ms) - GET /login?next=%2F 200 in 84ms (compile: 7ms, proxy.ts: 3ms, generate-params: 22µs, render: 74ms) - GET /en/login 200 in 99ms (compile: 6ms, proxy.ts: 4ms, generate-params: 24µs, render: 88ms) - GET /en/login 200 in 118ms (compile: 17ms, proxy.ts: 5ms, generate-params: 11ms, render: 96ms) -[2026-08-27 11:45:40.136 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:45:40.138 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" - GET /en/login 200 in 109ms (compile: 12ms, proxy.ts: 3ms, generate-params: 7ms, render: 93ms) - GET /en/login 200 in 172ms (compile: 22ms, proxy.ts: 8ms, generate-params: 13ms, render: 142ms) - GET /en/login 200 in 215ms (compile: 17ms, proxy.ts: 10ms, generate-params: 27µs, render: 188ms) -[2026-08-27 11:45:45.022 +0800] DEBUG: [auth.login] 用户登录 - ctx: { - "module": "auth", - "action": "login", - "traceId": "mtazcu2m-irqwlqd" - } - step: "用户登录" - data: { - "clientIP": "::1" - } -[2026-08-27 11:45:45.159 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:45:45.161 +0800] DEBUG: [auth.login] 密码验证: 密码正确 -> PASS - ctx: { - "module": "auth", - "action": "login", - "traceId": "mtazcu2m-irqwlqd" - } - branch: "密码验证" - condition: "密码正确" - status: "PASS" - data: { - "userId": 1 - } -[2026-08-27 11:45:45.169 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" - POST /api/auth/login 200 in 199ms (compile: 5ms, proxy.ts: 8ms, render: 187ms) - GET /api/auth/menus 200 in 54ms (compile: 12ms, proxy.ts: 10ms, render: 31ms) -[2026-08-27 11:45:45.580 +0800] DEBUG: [auth.login] 用户登录 - ctx: { - "module": "auth", - "action": "login", - "traceId": "mtazcui4-yvowl1d" - } - step: "用户登录" - data: { - "clientIP": "::1" - } -[2026-08-27 11:45:45.703 +0800] DEBUG: [auth.login] 密码验证: 密码正确 -> PASS - ctx: { - "module": "auth", - "action": "login", - "traceId": "mtazcui4-yvowl1d" - } - branch: "密码验证" - condition: "密码正确" - status: "PASS" - data: { - "userId": 1 - } - POST /api/auth/login 200 in 160ms (compile: 4ms, proxy.ts: 7ms, render: 150ms) - GET /api/auth/menus 200 in 37ms (compile: 8ms, proxy.ts: 11ms, render: 19ms) - GET /en/dashboard 200 in 427ms (compile: 280ms, proxy.ts: 9ms, generate-params: 37µs, render: 138ms) - GET /en/dashboard 200 in 948ms (compile: 763ms, proxy.ts: 12ms, generate-params: 13ms, render: 173ms) - GET /en/dashboard 200 in 118ms (compile: 31ms, proxy.ts: 18ms, generate-params: 37µs, render: 68ms) - GET /en/dashboard 200 in 117ms (compile: 56ms, proxy.ts: 18ms, generate-params: 42µs, render: 42ms) - GET /api/system/notice?page=1&pageSize=10 200 in 250ms (compile: 60ms, proxy.ts: 64ms, render: 125ms) - GET /api/system/notice?page=1&pageSize=10 200 in 226ms (compile: 67ms, proxy.ts: 51ms, render: 109ms) - GET /api/system/config?pageSize=200 200 in 310ms (compile: 55ms, proxy.ts: 75ms, render: 180ms) - GET /api/system/config?pageSize=200 200 in 284ms (compile: 66ms, proxy.ts: 42ms, render: 176ms) - GET /api/dashboard 200 in 307ms (compile: 66ms, proxy.ts: 43ms, render: 198ms) - GET /api/dashboard 200 in 341ms (compile: 71ms, proxy.ts: 53ms, render: 217ms) - GET /api/system/notice?page=1&pageSize=10 200 in 257ms (compile: 41ms, proxy.ts: 52ms, render: 164ms) - GET /api/organization?type=company 200 in 209ms (compile: 52ms, proxy.ts: 47ms, render: 110ms) - GET /api/system/notice?page=1&pageSize=10 200 in 253ms (compile: 55ms, proxy.ts: 47ms, render: 150ms) - GET /api/organization?type=company 200 in 193ms (compile: 57ms, proxy.ts: 52ms, render: 84ms) - GET /api/system/config?pageSize=200 200 in 233ms (compile: 45ms, proxy.ts: 47ms, render: 142ms) - GET /api/system/config?pageSize=200 200 in 244ms (compile: 68ms, proxy.ts: 45ms, render: 131ms) - GET /api/dashboard 200 in 239ms (compile: 53ms, proxy.ts: 59ms, render: 127ms) - GET /api/dashboard 200 in 209ms (compile: 31ms, proxy.ts: 49ms, render: 130ms) - GET /api/organization?type=company 200 in 118ms (compile: 32ms, proxy.ts: 29ms, render: 57ms) - GET /api/organization?type=company 200 in 102ms (compile: 29ms, proxy.ts: 33ms, render: 40ms) - GET /api/system/config?pageSize=200 200 in 145ms (compile: 28ms, proxy.ts: 29ms, render: 88ms) - GET /api/system/config?pageSize=200 200 in 130ms (compile: 31ms, proxy.ts: 25ms, render: 74ms) - GET /api/system/config?pageSize=200 200 in 124ms (compile: 12ms, proxy.ts: 17ms, render: 96ms) - GET /api/system/config?pageSize=200 200 in 120ms (compile: 16ms, proxy.ts: 12ms, render: 92ms) - GET /en/warehouse/inbound 200 in 450ms (compile: 40ms, proxy.ts: 9ms, generate-params: 19ms, render: 402ms) - GET /en/warehouse/inbound 200 in 526ms (compile: 32ms, proxy.ts: 14ms, generate-params: 55µs, render: 480ms) - GET /api/auth/menus 200 in 116ms (compile: 35ms, proxy.ts: 28ms, render: 53ms) - GET /api/system/config?pageSize=200 200 in 150ms (compile: 27ms, proxy.ts: 29ms, render: 94ms) - GET /api/auth/menus 200 in 44ms (compile: 16ms, proxy.ts: 11ms, render: 17ms) - GET /api/system/config?pageSize=200 200 in 55ms (compile: 11ms, proxy.ts: 12ms, render: 32ms) - GET /api/system/notice?page=1&pageSize=10 200 in 113ms (compile: 23ms, proxy.ts: 12ms, render: 78ms) -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) - GET /api/system/config?pageSize=200 200 in 127ms (compile: 7ms, proxy.ts: 7ms, render: 114ms) -[2026-08-27 11:45:49.266 +0800] DEBUG: Event registry already initialized - GET /api/organization/warehouse-category 200 in 114ms (compile: 27ms, proxy.ts: 23ms, render: 64ms) - GET /api/purchase/suppliers?pageSize=1000 200 in 129ms (compile: 33ms, proxy.ts: 23ms, render: 73ms) - GET /api/warehouse/inbound?page=1&pageSize=1000 200 in 149ms (compile: 16ms, proxy.ts: 23ms, render: 111ms) - GET /api/warehouse?all=true 200 in 150ms (compile: 21ms, proxy.ts: 23ms, render: 106ms) -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) -[2026-08-27 11:45:49.500 +0800] DEBUG: Event registry already initialized - GET /api/organization/warehouse-category 200 in 230ms (compile: 61ms, proxy.ts: 49ms, render: 119ms) - GET /api/purchase/suppliers?pageSize=1000 200 in 224ms (compile: 70ms, proxy.ts: 45ms, render: 109ms) - GET /api/system/notice?page=1&pageSize=10 200 in 250ms (compile: 46ms, proxy.ts: 51ms, render: 153ms) - GET /api/warehouse/inbound/labels?pageSize=1000 200 in 230ms (compile: 75ms, proxy.ts: 45ms, render: 111ms) - GET /api/warehouse/inbound?page=1&pageSize=1000 200 in 256ms (compile: 51ms, proxy.ts: 50ms, render: 155ms) - GET /api/warehouse?all=true 200 in 256ms (compile: 56ms, proxy.ts: 49ms, render: 151ms) - GET /api/system/notice?page=1&pageSize=10 200 in 283ms (compile: 81ms, proxy.ts: 45ms, render: 158ms) - GET /api/system/config?pageSize=200 200 in 325ms (compile: 50ms, proxy.ts: 47ms, render: 228ms) - GET /api/organization/warehouse-category 200 in 252ms (compile: 58ms, proxy.ts: 63ms, render: 131ms) - GET /api/organization?type=company 200 in 246ms (compile: 61ms, proxy.ts: 57ms, render: 127ms) - GET /api/purchase/suppliers?pageSize=1000 200 in 259ms (compile: 42ms, proxy.ts: 85ms, render: 132ms) - GET /api/system/config?pageSize=200 200 in 339ms (compile: 84ms, proxy.ts: 41ms, render: 214ms) -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) -[2026-08-27 11:45:49.789 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:45:49.792 +0800] DEBUG: Event registry already initialized - GET /api/organization/warehouse-category 200 in 248ms (compile: 61ms, proxy.ts: 50ms, render: 136ms) - GET /api/warehouse/inbound/labels?pageSize=1000 200 in 271ms (compile: 54ms, proxy.ts: 53ms, render: 163ms) - GET /api/purchase/suppliers?pageSize=1000 200 in 263ms (compile: 51ms, proxy.ts: 50ms, render: 162ms) - GET /api/system/notice?page=1&pageSize=10 200 in 278ms (compile: 77ms, proxy.ts: 51ms, render: 151ms) - GET /api/warehouse/inbound?page=1&pageSize=1000 200 in 285ms (compile: 66ms, proxy.ts: 50ms, render: 168ms) - GET /api/warehouse/inbound?page=1&pageSize=1000 200 in 286ms (compile: 82ms, proxy.ts: 52ms, render: 153ms) - GET /api/warehouse/inbound/labels?pageSize=1000 200 in 261ms (compile: 85ms, proxy.ts: 35ms, render: 141ms) - GET /api/warehouse?all=true 200 in 275ms (compile: 69ms, proxy.ts: 36ms, render: 170ms) - GET /api/warehouse?all=true 200 in 279ms (compile: 77ms, proxy.ts: 35ms, render: 167ms) - GET /api/organization?type=company 200 in 226ms (compile: 39ms, proxy.ts: 56ms, render: 131ms) - GET /api/system/config?pageSize=200 200 in 249ms (compile: 29ms, proxy.ts: 57ms, render: 163ms) - GET /api/organization?type=company 200 in 130ms (compile: 23ms, proxy.ts: 50ms, render: 57ms) - GET /api/warehouse/inbound/labels?pageSize=1000 200 in 143ms (compile: 29ms, proxy.ts: 50ms, render: 64ms) - GET /api/system/config?pageSize=200 200 in 189ms (compile: 28ms, proxy.ts: 56ms, render: 105ms) - GET /api/system/config?pageSize=200 200 in 102ms (compile: 22ms, proxy.ts: 37ms, render: 44ms) - GET /api/system/config?pageSize=200 200 in 61ms (compile: 11ms, proxy.ts: 13ms, render: 37ms) - GET /api/system/config?pageSize=200 200 in 37ms (compile: 4ms, proxy.ts: 6ms, render: 27ms) -[2026-08-27 11:45:50.165 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:45:50.173 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" - GET /api/organization?type=company 200 in 28ms (compile: 5ms, proxy.ts: 7ms, render: 16ms) - GET /en/login 200 in 90ms (compile: 11ms, proxy.ts: 4ms, generate-params: 7ms, render: 75ms) -[2026-08-27 11:45:53.821 +0800] DEBUG: [auth.login] 用户登录 - ctx: { - "module": "auth", - "action": "login", - "traceId": "mtazd0v1-2ywvykc" - } - step: "用户登录" - data: { - "clientIP": "::1" - } -[2026-08-27 11:45:53.935 +0800] DEBUG: [auth.login] 密码验证: 密码正确 -> PASS - ctx: { - "module": "auth", - "action": "login", - "traceId": "mtazd0v1-2ywvykc" - } - branch: "密码验证" - condition: "密码正确" - status: "PASS" - data: { - "userId": 1 - } - POST /api/auth/login 200 in 159ms (compile: 5ms, proxy.ts: 6ms, render: 147ms) - GET /api/auth/menus 200 in 31ms (compile: 6ms, proxy.ts: 7ms, render: 18ms) - GET /en/dashboard 200 in 788ms (compile: 603ms, proxy.ts: 12ms, generate-params: 10ms, render: 173ms) - GET /en/dashboard 200 in 119ms (compile: 39ms, proxy.ts: 38ms, generate-params: 25µs, render: 41ms) - GET /en/login 200 in 720ms (compile: 539ms, proxy.ts: 6ms, generate-params: 8ms, render: 174ms) - GET /api/system/notice?page=1&pageSize=10 200 in 70ms (compile: 18ms, proxy.ts: 19ms, render: 33ms) - GET /api/system/config?pageSize=200 200 in 86ms (compile: 18ms, proxy.ts: 9ms, render: 58ms) - GET /api/dashboard 200 in 100ms (compile: 17ms, proxy.ts: 24ms, render: 59ms) - GET /api/system/notice?page=1&pageSize=10 200 in 66ms (compile: 9ms, proxy.ts: 19ms, render: 38ms) - GET /api/system/config?pageSize=200 200 in 72ms (compile: 15ms, proxy.ts: 14ms, render: 43ms) - GET /api/dashboard 200 in 88ms (compile: 6ms, proxy.ts: 10ms, render: 71ms) - GET /api/organization?type=company 200 in 33ms (compile: 6ms, proxy.ts: 13ms, render: 14ms) -[2026-08-27 11:45:55.164 +0800] DEBUG: Event registry already initialized - GET /api/system/config?pageSize=200 200 in 93ms (compile: 16ms, proxy.ts: 18ms, render: 60ms) -[2026-08-27 11:45:55.182 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" - GET /api/organization?type=company 200 in 58ms (compile: 14ms, proxy.ts: 20ms, render: 25ms) - GET /api/system/config?pageSize=200 200 in 51ms (compile: 5ms, proxy.ts: 19ms, render: 27ms) -[2026-08-27 11:45:55.457 +0800] DEBUG: [auth.login] 用户登录 - ctx: { - "module": "auth", - "action": "login", - "traceId": "mtazd24h-30xufnw" - } - step: "用户登录" - data: { - "clientIP": "::1" - } -[2026-08-27 11:45:55.581 +0800] DEBUG: [auth.login] 密码验证: 密码正确 -> PASS - ctx: { - "module": "auth", - "action": "login", - "traceId": "mtazd24h-30xufnw" - } - branch: "密码验证" - condition: "密码正确" - status: "PASS" - data: { - "userId": 1 - } - POST /api/auth/login 200 in 159ms (compile: 3ms, proxy.ts: 6ms, render: 151ms) - GET /api/auth/menus 200 in 67ms (compile: 9ms, proxy.ts: 9ms, render: 49ms) - GET /en/dashboard 200 in 62ms (compile: 19ms, proxy.ts: 9ms, generate-params: 29µs, render: 34ms) - GET /en/dashboard 200 in 1165ms (compile: 1115ms, proxy.ts: 10ms, generate-params: 17ms, render: 41ms) - GET /api/system/notice?page=1&pageSize=10 200 in 133ms (compile: 30ms, proxy.ts: 31ms, render: 71ms) - GET /api/system/config?pageSize=200 200 in 156ms (compile: 23ms, proxy.ts: 32ms, render: 101ms) - GET /api/dashboard 200 in 168ms (compile: 21ms, proxy.ts: 25ms, render: 122ms) - GET /en/warehouse/inbound 200 in 514ms (compile: 28ms, proxy.ts: 7ms, generate-params: 16ms, render: 479ms) - GET /api/system/notice?page=1&pageSize=10 200 in 144ms (compile: 25ms, proxy.ts: 19ms, render: 100ms) - GET /api/organization?type=company 200 in 137ms (compile: 28ms, proxy.ts: 31ms, render: 78ms) - GET /api/system/config?pageSize=200 200 in 193ms (compile: 27ms, proxy.ts: 22ms, render: 144ms) - GET /api/dashboard 200 in 197ms (compile: 12ms, proxy.ts: 21ms, render: 163ms) - GET /api/organization?type=company 200 in 121ms (compile: 32ms, proxy.ts: 32ms, render: 57ms) - GET /api/system/config?pageSize=200 200 in 152ms (compile: 23ms, proxy.ts: 34ms, render: 95ms) - GET /api/system/config?pageSize=200 200 in 65ms (compile: 8ms, proxy.ts: 15ms, render: 42ms) - GET /api/auth/menus 200 in 49ms (compile: 15ms, proxy.ts: 15ms, render: 19ms) - GET /api/system/config?pageSize=200 200 in 70ms (compile: 11ms, proxy.ts: 16ms, render: 43ms) -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) - GET /api/system/notice?page=1&pageSize=10 200 in 138ms (compile: 32ms, proxy.ts: 19ms, render: 88ms) -[2026-08-27 11:45:58.120 +0800] DEBUG: Event registry already initialized - GET /api/system/config?pageSize=200 200 in 153ms (compile: 12ms, proxy.ts: 8ms, render: 133ms) - GET /api/organization/warehouse-category 200 in 122ms (compile: 32ms, proxy.ts: 34ms, render: 56ms) - GET /api/warehouse/inbound?page=1&pageSize=1000 200 in 148ms (compile: 32ms, proxy.ts: 27ms, render: 90ms) - GET /api/purchase/suppliers?pageSize=1000 200 in 141ms (compile: 37ms, proxy.ts: 35ms, render: 69ms) - GET /api/warehouse?all=true 200 in 148ms (compile: 26ms, proxy.ts: 32ms, render: 90ms) - GET /api/warehouse/inbound/labels?pageSize=1000 200 in 151ms (compile: 31ms, proxy.ts: 40ms, render: 79ms) -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) -[2026-08-27 11:45:58.297 +0800] DEBUG: Event registry already initialized - GET /api/system/notice?page=1&pageSize=10 200 in 159ms (compile: 36ms, proxy.ts: 40ms, render: 83ms) - GET /api/organization/warehouse-category 200 in 129ms (compile: 39ms, proxy.ts: 33ms, render: 57ms) - GET /api/purchase/suppliers?pageSize=1000 200 in 129ms (compile: 36ms, proxy.ts: 43ms, render: 49ms) - GET /api/warehouse/inbound?page=1&pageSize=1000 200 in 142ms (compile: 30ms, proxy.ts: 44ms, render: 68ms) - GET /api/system/config?pageSize=200 200 in 171ms (compile: 34ms, proxy.ts: 35ms, render: 102ms) - GET /api/warehouse/inbound/labels?pageSize=1000 200 in 111ms (compile: 30ms, proxy.ts: 30ms, render: 50ms) - GET /api/organization?type=company 200 in 97ms (compile: 33ms, proxy.ts: 26ms, render: 38ms) - GET /api/warehouse?all=true 200 in 127ms (compile: 26ms, proxy.ts: 34ms, render: 67ms) - GET /api/system/config?pageSize=200 200 in 102ms (compile: 21ms, proxy.ts: 38ms, render: 42ms) - GET /api/system/config?pageSize=200 200 in 38ms (compile: 5ms, proxy.ts: 6ms, render: 27ms) - GET /en/warehouse/inbound 200 in 460ms (compile: 15ms, proxy.ts: 9ms, generate-params: 53µs, render: 437ms) - GET /api/auth/menus 200 in 70ms (compile: 21ms, proxy.ts: 11ms, render: 38ms) - GET /api/system/config?pageSize=200 200 in 88ms (compile: 11ms, proxy.ts: 13ms, render: 64ms) - GET /api/organization?type=company 200 in 63ms (compile: 8ms, proxy.ts: 27ms, render: 28ms) - GET /api/system/notice?page=1&pageSize=10 200 in 133ms (compile: 31ms, proxy.ts: 17ms, render: 85ms) -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) -[2026-08-27 11:45:59.539 +0800] DEBUG: Event registry already initialized - GET /api/organization/warehouse-category 200 in 114ms (compile: 27ms, proxy.ts: 34ms, render: 53ms) - GET /api/purchase/suppliers?pageSize=1000 200 in 119ms (compile: 34ms, proxy.ts: 31ms, render: 55ms) - GET /api/warehouse/inbound?page=1&pageSize=1000 200 in 138ms (compile: 18ms, proxy.ts: 33ms, render: 87ms) - GET /api/warehouse?all=true 200 in 139ms (compile: 24ms, proxy.ts: 33ms, render: 82ms) - GET /api/system/config?pageSize=200 200 in 274ms (compile: 10ms, proxy.ts: 9ms, render: 255ms) - GET /api/warehouse/inbound/labels?pageSize=1000 200 in 147ms (compile: 25ms, proxy.ts: 33ms, render: 89ms) -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) -[2026-08-27 11:45:59.703 +0800] DEBUG: Event registry already initialized - GET /api/organization/warehouse-category 200 in 134ms (compile: 39ms, proxy.ts: 24ms, render: 71ms) - GET /api/system/notice?page=1&pageSize=10 200 in 156ms (compile: 33ms, proxy.ts: 30ms, render: 93ms) - GET /api/purchase/suppliers?pageSize=1000 200 in 146ms (compile: 34ms, proxy.ts: 33ms, render: 79ms) - GET /api/warehouse/inbound?page=1&pageSize=1000 200 in 160ms (compile: 40ms, proxy.ts: 33ms, render: 86ms) - GET /api/warehouse?all=true 200 in 103ms (compile: 28ms, proxy.ts: 16ms, render: 59ms) - GET /api/warehouse/inbound/labels?pageSize=1000 200 in 52ms (compile: 12ms, proxy.ts: 20ms, render: 20ms) - GET /api/system/config?pageSize=200 200 in 65ms (compile: 8ms, proxy.ts: 20ms, render: 37ms) - GET /api/system/config?pageSize=200 200 in 38ms (compile: 5ms, proxy.ts: 7ms, render: 26ms) - GET /api/system/config?pageSize=200 200 in 42ms (compile: 5ms, proxy.ts: 7ms, render: 30ms) -[2026-08-27 11:46:00.177 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:46:00.180 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" - GET /api/organization?type=company 200 in 25ms (compile: 4ms, proxy.ts: 6ms, render: 15ms) - GET /api/organization?type=company 200 in 24ms (compile: 4ms, proxy.ts: 6ms, render: 14ms) - GET /en/login 200 in 138ms (compile: 21ms, proxy.ts: 6ms, generate-params: 13ms, render: 111ms) -[2026-08-27 11:46:02.530 +0800] DEBUG: [auth.login] 用户登录 - ctx: { - "module": "auth", - "action": "login", - "traceId": "mtazd7ky-pnay5v0" - } - step: "用户登录" - data: { - "clientIP": "::1" - } -[2026-08-27 11:46:02.634 +0800] DEBUG: [auth.login] 密码验证: 密码正确 -> PASS - ctx: { - "module": "auth", - "action": "login", - "traceId": "mtazd7ky-pnay5v0" - } - branch: "密码验证" - condition: "密码正确" - status: "PASS" - data: { - "userId": 1 - } - POST /api/auth/login 200 in 150ms (compile: 3ms, proxy.ts: 11ms, render: 135ms) - GET /api/auth/menus 200 in 78ms (compile: 7ms, proxy.ts: 10ms, render: 61ms) - GET /en/dashboard 200 in 79ms (compile: 33ms, proxy.ts: 9ms, generate-params: 13ms, render: 38ms) - GET /en/dashboard 200 in 46ms (compile: 9ms, proxy.ts: 7ms, generate-params: 39µs, render: 30ms) - GET /api/system/notice?page=1&pageSize=10 200 in 49ms (compile: 14ms, proxy.ts: 12ms, render: 23ms) - GET /api/system/config?pageSize=200 200 in 68ms (compile: 11ms, proxy.ts: 13ms, render: 44ms) - GET /api/dashboard 200 in 69ms (compile: 18ms, proxy.ts: 12ms, render: 40ms) - GET /api/system/notice?page=1&pageSize=10 200 in 60ms (compile: 13ms, proxy.ts: 15ms, render: 32ms) - GET /api/system/config?pageSize=200 200 in 58ms (compile: 8ms, proxy.ts: 18ms, render: 32ms) - GET /api/dashboard 200 in 59ms (compile: 10ms, proxy.ts: 15ms, render: 34ms) - GET /api/organization?type=company 200 in 29ms (compile: 5ms, proxy.ts: 10ms, render: 14ms) - GET /api/system/config?pageSize=200 200 in 49ms (compile: 6ms, proxy.ts: 10ms, render: 34ms) - GET /api/organization?type=company 200 in 39ms (compile: 7ms, proxy.ts: 10ms, render: 22ms) - GET /api/system/config?pageSize=200 200 in 43ms (compile: 11ms, proxy.ts: 9ms, render: 23ms) - GET /api/warehouse/categories 200 in 70ms (compile: 10ms, proxy.ts: 16ms, render: 44ms) - GET /api/system/currency?active=true 200 in 69ms (compile: 23ms, proxy.ts: 15ms, render: 30ms) - GET /api/warehouse?all=true&status=active 200 in 84ms (compile: 17ms, proxy.ts: 17ms, render: 50ms) - GET /api/warehouse/categories 200 in 62ms (compile: 12ms, proxy.ts: 17ms, render: 33ms) - GET /api/system/currency?active=true 200 in 61ms (compile: 17ms, proxy.ts: 15ms, render: 29ms) - GET /api/warehouse?all=true&status=active 200 in 59ms (compile: 21ms, proxy.ts: 13ms, render: 25ms) -[2026-08-27 11:46:05.177 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:46:05.183 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" - GET /en/warehouse/inbound 200 in 275ms (compile: 24ms, proxy.ts: 5ms, generate-params: 14ms, render: 246ms) - GET /api/auth/menus 200 in 35ms (compile: 13ms, proxy.ts: 9ms, render: 13ms) - GET /api/system/config?pageSize=200 200 in 58ms (compile: 8ms, proxy.ts: 11ms, render: 39ms) -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) -[2026-08-27 11:46:05.481 +0800] INFO: [warehouse/inbound] 开始物料分类校验 - itemCount: 1 - materialIds: [] -[2026-08-27 11:46:05.482 +0800] INFO: [warehouse/inbound] 物料分类校验完成 - blocked: false - uncategorizedCount: 0 -[2026-08-27 11:46:05.482 +0800] DEBUG: Event registry already initialized -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) - GET /api/system/notice?page=1&pageSize=10 200 in 134ms (compile: 41ms, proxy.ts: 15ms, render: 77ms) -[2026-08-27 11:46:05.594 +0800] DEBUG: Event registry already initialized - POST /api/warehouse/inbound 200 in 165ms (compile: 5ms, proxy.ts: 7ms, render: 153ms) - GET /api/organization/warehouse-category 200 in 115ms (compile: 28ms, proxy.ts: 37ms, render: 51ms) - GET /api/purchase/suppliers?pageSize=1000 200 in 126ms (compile: 33ms, proxy.ts: 37ms, render: 56ms) - GET /api/warehouse/inbound?page=1&pageSize=1000 200 in 147ms (compile: 26ms, proxy.ts: 35ms, render: 87ms) - GET /api/warehouse?all=true 200 in 146ms (compile: 23ms, proxy.ts: 36ms, render: 86ms) - GET /api/system/config?pageSize=200 200 in 184ms (compile: 31ms, proxy.ts: 10ms, render: 142ms) -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) -[2026-08-27 11:46:05.782 +0800] DEBUG: Event registry already initialized - GET /api/warehouse/inbound/labels?pageSize=1000 200 in 169ms (compile: 30ms, proxy.ts: 43ms, render: 96ms) - GET /api/organization/warehouse-category 200 in 141ms (compile: 40ms, proxy.ts: 30ms, render: 71ms) -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) - GET /api/warehouse/inbound?page=1&pageSize=1000 200 in 173ms (compile: 33ms, proxy.ts: 37ms, render: 104ms) - GET /api/system/notice?page=1&pageSize=10 200 in 177ms (compile: 38ms, proxy.ts: 35ms, render: 104ms) -[2026-08-27 11:46:05.801 +0800] DEBUG: Event registry already initialized - GET /api/purchase/suppliers?pageSize=1000 200 in 165ms (compile: 47ms, proxy.ts: 30ms, render: 88ms) - GET /api/warehouse/inbound?page=1&pageSize=1000 200 in 170ms (compile: 48ms, proxy.ts: 32ms, render: 90ms) - GET /api/warehouse?all=true 200 in 171ms (compile: 54ms, proxy.ts: 31ms, render: 85ms) - GET /api/organization?type=company 200 in 78ms (compile: 16ms, proxy.ts: 32ms, render: 29ms) - GET /api/warehouse/inbound/labels?pageSize=1000 200 in 67ms (compile: 21ms, proxy.ts: 20ms, render: 26ms) - GET /api/system/config?pageSize=200 200 in 91ms (compile: 12ms, proxy.ts: 33ms, render: 47ms) - GET /api/system/config?pageSize=200 200 in 44ms (compile: 9ms, proxy.ts: 7ms, render: 27ms) - GET /api/system/config?pageSize=200 200 in 35ms (compile: 4ms, proxy.ts: 6ms, render: 25ms) - GET /api/organization?type=company 200 in 25ms (compile: 4ms, proxy.ts: 5ms, render: 16ms) - GET /en/login 200 in 122ms (compile: 20ms, proxy.ts: 5ms, generate-params: 12ms, render: 98ms) - GET /en/login 200 in 197ms (compile: 10ms, proxy.ts: 6ms, generate-params: 44µs, render: 181ms) -[2026-08-27 11:46:09.291 +0800] DEBUG: [auth.login] 用户登录 - ctx: { - "module": "auth", - "action": "login", - "traceId": "mtazdcsr-znappfk" - } - step: "用户登录" - data: { - "clientIP": "::1" - } -[2026-08-27 11:46:09.419 +0800] DEBUG: [auth.login] 密码验证: 密码正确 -> PASS - ctx: { - "module": "auth", - "action": "login", - "traceId": "mtazdcsr-znappfk" - } - branch: "密码验证" - condition: "密码正确" - status: "PASS" - data: { - "userId": 1 - } - POST /api/auth/login 200 in 195ms (compile: 5ms, proxy.ts: 6ms, render: 184ms) - GET /api/auth/menus 200 in 106ms (compile: 11ms, proxy.ts: 20ms, render: 75ms) - GET /en/dashboard 200 in 107ms (compile: 43ms, proxy.ts: 15ms, generate-params: 15ms, render: 48ms) - GET /en/dashboard 200 in 55ms (compile: 13ms, proxy.ts: 7ms, generate-params: 44µs, render: 36ms) - GET /api/system/notice?page=1&pageSize=10 200 in 67ms (compile: 21ms, proxy.ts: 16ms, render: 30ms) - GET /api/system/config?pageSize=200 200 in 90ms (compile: 14ms, proxy.ts: 17ms, render: 59ms) - GET /api/dashboard 200 in 90ms (compile: 28ms, proxy.ts: 16ms, render: 47ms) - GET /api/system/notice?page=1&pageSize=10 200 in 74ms (compile: 16ms, proxy.ts: 17ms, render: 40ms) - GET /api/system/config?pageSize=200 200 in 78ms (compile: 11ms, proxy.ts: 21ms, render: 46ms) - GET /api/dashboard 200 in 69ms (compile: 13ms, proxy.ts: 18ms, render: 38ms) - GET /api/organization?type=company 200 in 41ms (compile: 13ms, proxy.ts: 11ms, render: 17ms) - GET /api/system/config?pageSize=200 200 in 72ms (compile: 8ms, proxy.ts: 11ms, render: 54ms) - GET /api/organization?type=company 200 in 55ms (compile: 10ms, proxy.ts: 15ms, render: 30ms) - GET /api/system/config?pageSize=200 200 in 58ms (compile: 9ms, proxy.ts: 16ms, render: 32ms) -[2026-08-27 11:46:10.315 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:46:10.329 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:46:10.350 +0800] DEBUG: [auth.login] 用户登录 - ctx: { - "module": "auth", - "action": "login", - "traceId": "mtazddm6-49c5056" - } - step: "用户登录" - data: { - "clientIP": "::1" - } -[2026-08-27 11:46:10.476 +0800] DEBUG: [auth.login] 密码验证: 密码正确 -> PASS - ctx: { - "module": "auth", - "action": "login", - "traceId": "mtazddm6-49c5056" - } - branch: "密码验证" - condition: "密码正确" - status: "PASS" - data: { - "userId": 1 - } - POST /api/auth/login 200 in 227ms (compile: 6ms, proxy.ts: 9ms, render: 212ms) - GET /api/auth/menus 200 in 79ms (compile: 10ms, proxy.ts: 11ms, render: 59ms) - GET /en/dashboard 200 in 74ms (compile: 25ms, proxy.ts: 11ms, generate-params: 50µs, render: 39ms) - GET /en/dashboard 200 in 43ms (compile: 10ms, proxy.ts: 8ms, generate-params: 45µs, render: 26ms) - GET /api/system/notice?page=1&pageSize=10 200 in 78ms (compile: 19ms, proxy.ts: 20ms, render: 40ms) - GET /api/dashboard 200 in 115ms (compile: 18ms, proxy.ts: 25ms, render: 73ms) - GET /api/system/config?pageSize=200 200 in 145ms (compile: 17ms, proxy.ts: 26ms, render: 101ms) - GET /api/system/notice?page=1&pageSize=10 200 in 98ms (compile: 22ms, proxy.ts: 25ms, render: 51ms) - GET /api/dashboard 200 in 61ms (compile: 9ms, proxy.ts: 15ms, render: 37ms) - GET /api/system/config?pageSize=200 200 in 112ms (compile: 16ms, proxy.ts: 14ms, render: 82ms) - GET /api/organization?type=company 200 in 47ms (compile: 6ms, proxy.ts: 7ms, render: 35ms) - GET /api/system/config?pageSize=200 200 in 62ms (compile: 5ms, proxy.ts: 10ms, render: 48ms) - GET /api/organization?type=company 200 in 40ms (compile: 5ms, proxy.ts: 7ms, render: 28ms) - GET /api/system/config?pageSize=200 200 in 48ms (compile: 7ms, proxy.ts: 9ms, render: 33ms) - GET /en/warehouse/inbound 200 in 812ms (compile: 547ms, proxy.ts: 7ms, generate-params: 58ms, render: 259ms) - GET /api/auth/menus 200 in 44ms (compile: 12ms, proxy.ts: 10ms, render: 21ms) - GET /api/system/config?pageSize=200 200 in 62ms (compile: 7ms, proxy.ts: 11ms, render: 44ms) -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) -[2026-08-27 11:46:12.512 +0800] DEBUG: Event registry already initialized - GET /api/system/notice?page=1&pageSize=10 200 in 138ms (compile: 39ms, proxy.ts: 19ms, render: 79ms) - GET /api/organization/warehouse-category 200 in 119ms (compile: 33ms, proxy.ts: 41ms, render: 45ms) - GET /api/warehouse/inbound?page=1&pageSize=1000 200 in 139ms (compile: 39ms, proxy.ts: 24ms, render: 76ms) - GET /api/purchase/suppliers?pageSize=1000 200 in 129ms (compile: 42ms, proxy.ts: 41ms, render: 46ms) - GET /api/warehouse?all=true 200 in 140ms (compile: 32ms, proxy.ts: 33ms, render: 75ms) - GET /api/system/config?pageSize=200 200 in 264ms (compile: 31ms, proxy.ts: 12ms, render: 221ms) - GET /api/warehouse/inbound/labels?pageSize=1000 200 in 130ms (compile: 24ms, proxy.ts: 35ms, render: 71ms) -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) -[2026-08-27 11:46:12.670 +0800] DEBUG: Event registry already initialized - GET /api/organization/warehouse-category 200 in 116ms (compile: 37ms, proxy.ts: 26ms, render: 53ms) - GET /api/system/notice?page=1&pageSize=10 200 in 143ms (compile: 31ms, proxy.ts: 35ms, render: 77ms) - GET /api/purchase/suppliers?pageSize=1000 200 in 130ms (compile: 45ms, proxy.ts: 25ms, render: 60ms) - GET /api/warehouse/inbound?page=1&pageSize=1000 200 in 141ms (compile: 40ms, proxy.ts: 25ms, render: 76ms) - GET /api/warehouse?all=true 200 in 97ms (compile: 25ms, proxy.ts: 12ms, render: 59ms) - GET /api/warehouse/inbound/labels?pageSize=1000 200 in 60ms (compile: 14ms, proxy.ts: 24ms, render: 22ms) - GET /api/system/config?pageSize=200 200 in 89ms (compile: 9ms, proxy.ts: 37ms, render: 43ms) - GET /api/system/config?pageSize=200 200 in 57ms (compile: 9ms, proxy.ts: 9ms, render: 40ms) - GET /api/system/config?pageSize=200 200 in 60ms (compile: 7ms, proxy.ts: 20ms, render: 33ms) - GET /api/organization?type=company 200 in 29ms (compile: 5ms, proxy.ts: 6ms, render: 18ms) - GET /api/organization?type=company 200 in 25ms (compile: 4ms, proxy.ts: 6ms, render: 15ms) - GET /en/warehouse/inbound 200 in 1242ms (compile: 965ms, proxy.ts: 20ms, generate-params: 15ms, render: 256ms) - GET /api/auth/menus 200 in 38ms (compile: 11ms, proxy.ts: 10ms, render: 17ms) - GET /api/system/config?pageSize=200 200 in 53ms (compile: 6ms, proxy.ts: 12ms, render: 35ms) -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) - GET /api/system/notice?page=1&pageSize=10 200 in 133ms (compile: 35ms, proxy.ts: 16ms, render: 82ms) -[2026-08-27 11:46:14.500 +0800] DEBUG: Event registry already initialized - GET /api/organization/warehouse-category 200 in 122ms (compile: 36ms, proxy.ts: 42ms, render: 43ms) - GET /api/system/config?pageSize=200 200 in 153ms (compile: 23ms, proxy.ts: 16ms, render: 113ms) - GET /api/warehouse/inbound?page=1&pageSize=1000 200 in 145ms (compile: 43ms, proxy.ts: 22ms, render: 79ms) - GET /api/warehouse?all=true 200 in 145ms (compile: 29ms, proxy.ts: 42ms, render: 75ms) - GET /api/purchase/suppliers?pageSize=1000 200 in 144ms (compile: 44ms, proxy.ts: 40ms, render: 59ms) - GET /api/warehouse/inbound/labels?pageSize=1000 200 in 131ms (compile: 26ms, proxy.ts: 33ms, render: 71ms) - GET /api/organization/warehouse-category 200 in 110ms (compile: 39ms, proxy.ts: 25ms, render: 46ms) -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) - GET /api/system/notice?page=1&pageSize=10 200 in 132ms (compile: 31ms, proxy.ts: 30ms, render: 71ms) -[2026-08-27 11:46:14.656 +0800] DEBUG: Event registry already initialized - GET /api/warehouse/inbound?page=1&pageSize=1000 200 in 128ms (compile: 38ms, proxy.ts: 30ms, render: 60ms) - GET /api/warehouse?all=true 200 in 136ms (compile: 42ms, proxy.ts: 30ms, render: 64ms) - GET /api/system/config?pageSize=200 200 in 157ms (compile: 43ms, proxy.ts: 25ms, render: 89ms) - GET /api/organization?type=company 200 in 113ms (compile: 26ms, proxy.ts: 34ms, render: 54ms) - GET /api/purchase/suppliers?pageSize=1000 200 in 119ms (compile: 19ms, proxy.ts: 34ms, render: 67ms) - GET /api/warehouse/inbound/labels?pageSize=1000 200 in 115ms (compile: 33ms, proxy.ts: 33ms, render: 50ms) - GET /api/system/config?pageSize=200 200 in 97ms (compile: 13ms, proxy.ts: 37ms, render: 47ms) - GET /api/system/config?pageSize=200 200 in 42ms (compile: 5ms, proxy.ts: 9ms, render: 28ms) - GET /api/organization?type=company 200 in 26ms (compile: 4ms, proxy.ts: 6ms, render: 16ms) -[2026-08-27 11:46:15.319 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:46:15.322 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" - GET /api/warehouse/categories 200 in 58ms (compile: 10ms, proxy.ts: 12ms, render: 37ms) - GET /api/system/currency?active=true 200 in 59ms (compile: 26ms, proxy.ts: 12ms, render: 21ms) - GET /api/warehouse?all=true&status=active 200 in 72ms (compile: 20ms, proxy.ts: 12ms, render: 40ms) - GET /api/warehouse/categories 200 in 51ms (compile: 11ms, proxy.ts: 19ms, render: 21ms) - GET /api/system/currency?active=true 200 in 43ms (compile: 11ms, proxy.ts: 12ms, render: 19ms) - GET /api/warehouse?all=true&status=active 200 in 42ms (compile: 14ms, proxy.ts: 12ms, render: 17ms) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) -[2026-08-27 11:46:17.584 +0800] DEBUG: Event registry already initialized -[PurchaseRepository] 未知采购单状态码 status=2 (order id=148, po_no=PO_1787801486174_5),降级为 draft -[PurchaseRepository] 未知采购单状态码 status=2 (order id=145, po_no=PO_1787801486130_2),降级为 draft -[PurchaseRepository] 未知采购单状态码 status=2 (order id=144, po_no=PO_1787801486115_1),降级为 draft -[PurchaseRepository] 未知采购单状态码 status=2 (order id=143, po_no=PO_1787801486090_0),降级为 draft -[PurchaseRepository] 未知采购单状态码 status=2 (order id=146, po_no=PO_1787801486144_3),降级为 draft -[PurchaseRepository] 未知采购单状态码 status=2 (order id=147, po_no=PO_1787801486160_4),降级为 draft -[PurchaseRepository] 未知采购单状态码 status=2 (order id=137, po_no=PO_1787788536155_5),降级为 draft -[PurchaseRepository] 未知采购单状态码 status=2 (order id=136, po_no=PO_1787788536129_4),降级为 draft -[PurchaseRepository] 未知采购单状态码 status=2 (order id=135, po_no=PO_1787788536081_3),降级为 draft -[PurchaseRepository] 未知采购单状态码 status=2 (order id=134, po_no=PO_1787788536031_2),降级为 draft -[PurchaseRepository] 未知采购单状态码 status=2 (order id=133, po_no=PO_1787788535988_1),降级为 draft -[PurchaseRepository] 未知采购单状态码 status=2 (order id=132, po_no=PO_1787788535949_0),降级为 draft -[PurchaseRepository] 未知采购单状态码 status=2 (order id=131, po_no=PO_1787711404307_5),降级为 draft -[PurchaseRepository] 未知采购单状态码 status=2 (order id=130, po_no=PO_1787711404285_4),降级为 draft -[PurchaseRepository] 未知采购单状态码 status=2 (order id=129, po_no=PO_1787711404257_3),降级为 draft -[PurchaseRepository] 未知采购单状态码 status=2 (order id=128, po_no=PO_1787711404236_2),降级为 draft -[PurchaseRepository] 未知采购单状态码 status=2 (order id=127, po_no=PO_1787711404206_1),降级为 draft -[PurchaseRepository] 未知采购单状态码 status=2 (order id=126, po_no=PO_1787711404179_0),降级为 draft -[PurchaseRepository] 未知采购单状态码 status=2 (order id=122, po_no=PO_1787711344664_2),降级为 draft -[PurchaseRepository] 未知采购单状态码 status=2 (order id=123, po_no=PO_1787711344680_3),降级为 draft - GET /api/purchase/orders?keyword=PO&pageSize=20 200 in 199ms (compile: 18ms, proxy.ts: 123ms, render: 58ms) - GET /en/warehouse/inbound 200 in 372ms (compile: 28ms, proxy.ts: 6ms, generate-params: 15ms, render: 337ms) - GET /api/auth/menus 200 in 52ms (compile: 17ms, proxy.ts: 18ms, render: 18ms) - GET /api/system/config?pageSize=200 200 in 65ms (compile: 16ms, proxy.ts: 11ms, render: 38ms) -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) - GET /api/system/notice?page=1&pageSize=10 200 in 125ms (compile: 29ms, proxy.ts: 9ms, render: 87ms) -[2026-08-27 11:46:18.051 +0800] DEBUG: Event registry already initialized - GET /api/system/config?pageSize=200 200 in 152ms (compile: 17ms, proxy.ts: 11ms, render: 124ms) - GET /api/organization/warehouse-category 200 in 122ms (compile: 32ms, proxy.ts: 33ms, render: 56ms) - GET /api/warehouse/inbound?page=1&pageSize=1000 200 in 141ms (compile: 21ms, proxy.ts: 37ms, render: 83ms) - GET /api/purchase/suppliers?pageSize=1000 200 in 127ms (compile: 39ms, proxy.ts: 30ms, render: 57ms) - GET /api/warehouse?all=true 200 in 150ms (compile: 29ms, proxy.ts: 39ms, render: 83ms) - GET /api/warehouse/inbound/labels?pageSize=1000 200 in 63ms (compile: 10ms, proxy.ts: 26ms, render: 27ms) - GET /api/system/config?pageSize=200 200 in 55ms (compile: 16ms, proxy.ts: 12ms, render: 27ms) - GET /en/login 200 in 165ms (compile: 22ms, proxy.ts: 6ms, generate-params: 12ms, render: 137ms) -[2026-08-27 11:46:19.345 +0800] DEBUG: [auth.login] 用户登录 - ctx: { - "module": "auth", - "action": "login", - "traceId": "mtazdkk1-ykinkct" - } - step: "用户登录" - data: { - "clientIP": "::1" - } -[2026-08-27 11:46:19.477 +0800] DEBUG: [auth.login] 密码验证: 密码正确 -> PASS - ctx: { - "module": "auth", - "action": "login", - "traceId": "mtazdkk1-ykinkct" - } - branch: "密码验证" - condition: "密码正确" - status: "PASS" - data: { - "userId": 1 - } - POST /api/auth/login 200 in 176ms (compile: 5ms, proxy.ts: 7ms, render: 164ms) - GET /api/auth/menus 200 in 94ms (compile: 8ms, proxy.ts: 10ms, render: 77ms) - GET /en/dashboard 200 in 98ms (compile: 38ms, proxy.ts: 9ms, generate-params: 15ms, render: 51ms) - GET /en/dashboard 200 in 58ms (compile: 17ms, proxy.ts: 11ms, generate-params: 49µs, render: 30ms) - GET /api/system/notice?page=1&pageSize=10 200 in 65ms (compile: 19ms, proxy.ts: 15ms, render: 31ms) - GET /api/system/config?pageSize=200 200 in 86ms (compile: 13ms, proxy.ts: 14ms, render: 60ms) - GET /api/dashboard 200 in 102ms (compile: 18ms, proxy.ts: 20ms, render: 64ms) - GET /api/system/notice?page=1&pageSize=10 200 in 78ms (compile: 18ms, proxy.ts: 14ms, render: 45ms) - GET /api/system/config?pageSize=200 200 in 69ms (compile: 9ms, proxy.ts: 19ms, render: 41ms) - GET /api/dashboard 200 in 63ms (compile: 6ms, proxy.ts: 15ms, render: 42ms) - GET /api/organization?type=company 200 in 42ms (compile: 12ms, proxy.ts: 12ms, render: 18ms) - GET /api/system/config?pageSize=200 200 in 58ms (compile: 7ms, proxy.ts: 15ms, render: 36ms) - GET /api/organization?type=company 200 in 44ms (compile: 8ms, proxy.ts: 14ms, render: 22ms) - GET /api/system/config?pageSize=200 200 in 63ms (compile: 8ms, proxy.ts: 8ms, render: 46ms) -[2026-08-27 11:46:20.323 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:46:20.327 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" - GET /en/warehouse/inbound 200 in 305ms (compile: 31ms, proxy.ts: 7ms, generate-params: 15ms, render: 268ms) - GET /api/auth/menus 200 in 39ms (compile: 12ms, proxy.ts: 9ms, render: 18ms) - GET /api/system/config?pageSize=200 200 in 226ms (compile: 7ms, proxy.ts: 9ms, render: 210ms) -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) -[2026-08-27 11:46:22.070 +0800] DEBUG: Event registry already initialized - GET /api/organization/warehouse-category 200 in 141ms (compile: 52ms, proxy.ts: 34ms, render: 55ms) - GET /api/system/notice?page=1&pageSize=10 200 in 182ms (compile: 32ms, proxy.ts: 48ms, render: 102ms) - GET /api/warehouse/inbound?page=1&pageSize=1000 200 in 167ms (compile: 39ms, proxy.ts: 34ms, render: 94ms) - GET /api/purchase/suppliers?pageSize=1000 200 in 156ms (compile: 57ms, proxy.ts: 32ms, render: 66ms) - GET /api/warehouse?all=true 200 in 171ms (compile: 46ms, proxy.ts: 34ms, render: 91ms) - GET /api/warehouse/inbound/labels?pageSize=1000 200 in 180ms (compile: 11ms, proxy.ts: 65ms, render: 103ms) -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) -[2026-08-27 11:46:22.356 +0800] DEBUG: Event registry already initialized - GET /api/organization/warehouse-category 200 in 252ms (compile: 55ms, proxy.ts: 47ms, render: 150ms) - GET /api/warehouse/inbound?page=1&pageSize=1000 200 in 252ms (compile: 92ms, proxy.ts: 42ms, render: 118ms) - GET /api/purchase/suppliers?pageSize=1000 200 in 261ms (compile: 109ms, proxy.ts: 42ms, render: 111ms) - GET /api/system/notice?page=1&pageSize=10 200 in 274ms (compile: 84ms, proxy.ts: 44ms, render: 146ms) - GET /api/warehouse?all=true 200 in 236ms (compile: 65ms, proxy.ts: 72ms, render: 99ms) - GET /api/warehouse/inbound/labels?pageSize=1000 200 in 55ms (compile: 9ms, proxy.ts: 22ms, render: 24ms) - GET /api/system/config?pageSize=200 200 in 428ms (compile: 42ms, proxy.ts: 45ms, render: 341ms) - GET /api/system/config?pageSize=200 200 in 36ms (compile: 4ms, proxy.ts: 6ms, render: 26ms) - GET /api/system/config?pageSize=200 200 in 41ms (compile: 5ms, proxy.ts: 11ms, render: 25ms) - GET /api/system/config?pageSize=200 200 in 36ms (compile: 4ms, proxy.ts: 7ms, render: 25ms) - GET /api/organization?type=company 200 in 29ms (compile: 5ms, proxy.ts: 7ms, render: 17ms) - GET /api/organization?type=company 200 in 26ms (compile: 4ms, proxy.ts: 6ms, render: 15ms) -[2026-08-27 11:46:25.328 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:46:25.331 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" - GET /en/warehouse/inbound/cutting 200 in 279ms (compile: 28ms, proxy.ts: 6ms, generate-params: 14ms, render: 245ms) - GET /api/auth/menus 200 in 47ms (compile: 16ms, proxy.ts: 10ms, render: 21ms) - GET /api/system/config?pageSize=200 200 in 55ms (compile: 8ms, proxy.ts: 10ms, render: 38ms) - GET /api/warehouse/inbound/cutting?page=1&pageSize=20 200 in 55ms (compile: 20ms, proxy.ts: 14ms, render: 20ms) - GET /api/system/notice?page=1&pageSize=10 200 in 64ms (compile: 13ms, proxy.ts: 14ms, render: 37ms) - GET /api/warehouse/inbound/cutting?page=1&pageSize=20 200 in 45ms (compile: 8ms, proxy.ts: 12ms, render: 25ms) - GET /api/system/notice?page=1&pageSize=10 200 in 43ms (compile: 13ms, proxy.ts: 10ms, render: 20ms) - GET /api/system/config?pageSize=200 200 in 294ms (compile: 10ms, proxy.ts: 18ms, render: 266ms) - GET /api/system/config?pageSize=200 200 in 35ms (compile: 4ms, proxy.ts: 6ms, render: 24ms) - GET /api/system/config?pageSize=200 200 in 37ms (compile: 4ms, proxy.ts: 6ms, render: 26ms) - GET /api/system/config?pageSize=200 200 in 40ms (compile: 5ms, proxy.ts: 9ms, render: 25ms) - GET /api/organization?type=company 200 in 27ms (compile: 5ms, proxy.ts: 6ms, render: 16ms) - GET /api/organization?type=company 200 in 25ms (compile: 4ms, proxy.ts: 6ms, render: 15ms) -[2026-08-27 11:46:30.336 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:46:30.339 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:46:35.348 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:46:35.351 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:46:40.353 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:46:40.355 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -Persisting failed: Failed to commit the database compaction - -Caused by: - 拒绝访问。 (os error 5) -[2026-08-27 11:46:45.360 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:46:45.364 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:46:50.373 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:46:50.379 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:46:55.379 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:46:55.382 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:47:00.382 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:47:00.385 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:47:05.383 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:47:05.386 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -Persisting failed: Another write batch or compaction is already active (Only a single write operations is allowed at a time) -[2026-08-27 11:47:10.384 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:47:10.387 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -Persisting failed: Another write batch or compaction is already active (Only a single write operations is allowed at a time) -[2026-08-27 11:47:15.392 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:47:15.394 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:47:20.406 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:47:20.409 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:47:25.420 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:47:25.422 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:47:30.428 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:47:30.430 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:47:35.435 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:47:35.438 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:47:40.445 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:47:40.447 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:47:45.450 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:47:45.454 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:47:50.456 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:47:50.462 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -Persisting failed: Another write batch or compaction is already active (Only a single write operations is allowed at a time) -[2026-08-27 11:47:55.471 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:47:55.475 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:48:00.483 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:48:00.486 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:48:05.497 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:48:05.499 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:48:10.509 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:48:10.512 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:48:15.518 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:48:15.522 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -Persisting failed: Another write batch or compaction is already active (Only a single write operations is allowed at a time) -[2026-08-27 11:48:20.531 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:48:20.534 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:48:25.541 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:48:25.543 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:48:30.544 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:48:30.547 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:48:35.549 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:48:35.553 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:48:40.565 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:48:40.567 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:48:45.568 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:48:45.571 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:48:50.581 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:48:50.584 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:48:55.582 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:48:55.586 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:49:00.589 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:49:00.591 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:49:05.595 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:49:05.598 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:49:10.601 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:49:10.604 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:49:15.606 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:49:15.609 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -Persisting failed: Another write batch or compaction is already active (Only a single write operations is allowed at a time) -[2026-08-27 11:49:20.611 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:49:20.614 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:49:25.625 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:49:25.628 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:49:30.628 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:49:30.630 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:49:35.628 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:49:35.630 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:49:40.631 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:49:40.635 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:49:45.633 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:49:45.636 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:49:50.644 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:49:50.646 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:49:55.652 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:49:55.655 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:50:00.658 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:50:00.661 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:50:05.667 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:50:05.669 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:50:10.680 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:50:10.683 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:50:15.694 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:50:15.697 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:50:20.707 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:50:20.710 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:50:25.713 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:50:25.717 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:50:30.725 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:50:30.728 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:50:35.732 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:50:35.735 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:50:40.741 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:50:40.744 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:50:45.742 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:50:45.745 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:50:50.743 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:50:50.747 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -Persisting failed: Another write batch or compaction is already active (Only a single write operations is allowed at a time) - GET /login?next=%2F 200 in 194ms (compile: 30ms, proxy.ts: 10ms, generate-params: 12ms, render: 153ms) - GET /login?next=%2F 200 in 120ms (compile: 9ms, proxy.ts: 9ms, generate-params: 43µs, render: 102ms) - GET /en/login 200 in 155ms (compile: 13ms, proxy.ts: 8ms, generate-params: 43µs, render: 134ms) -[2026-08-27 11:50:55.753 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:50:55.756 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" - GET /en/login 200 in 158ms (compile: 18ms, proxy.ts: 8ms, generate-params: 10ms, render: 133ms) - GET /en/login 200 in 172ms (compile: 18ms, proxy.ts: 8ms, generate-params: 10ms, render: 146ms) -[2026-08-27 11:51:00.763 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:51:00.767 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" - GET /en/login 200 in 391ms (compile: 24ms, proxy.ts: 8ms, generate-params: 14ms, render: 359ms) - GET /en/login 200 in 362ms (compile: 23ms, proxy.ts: 54ms, generate-params: 44µs, render: 285ms) -[2026-08-27 11:51:05.453 +0800] DEBUG: [auth.login] 用户登录 - ctx: { - "module": "auth", - "action": "login", - "traceId": "mtazjpbh-pbtci10" - } - step: "用户登录" - data: { - "clientIP": "::1" - } -[2026-08-27 11:51:05.606 +0800] DEBUG: [auth.login] 密码验证: 密码正确 -> PASS - ctx: { - "module": "auth", - "action": "login", - "traceId": "mtazjpbh-pbtci10" - } - branch: "密码验证" - condition: "密码正确" - status: "PASS" - data: { - "userId": 1 - } - POST /api/auth/login 200 in 211ms (compile: 9ms, proxy.ts: 8ms, render: 194ms) -[2026-08-27 11:51:05.764 +0800] DEBUG: Event registry already initialized - GET /api/auth/menus 200 in 137ms (compile: 12ms, proxy.ts: 6ms, render: 119ms) -[2026-08-27 11:51:05.789 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" - GET /en/dashboard 200 in 147ms (compile: 40ms, proxy.ts: 21ms, generate-params: 19ms, render: 86ms) - GET /en/dashboard 200 in 76ms (compile: 24ms, proxy.ts: 8ms, generate-params: 48µs, render: 44ms) -[2026-08-27 11:51:06.006 +0800] DEBUG: [auth.login] 用户登录 - ctx: { - "module": "auth", - "action": "login", - "traceId": "mtazjpqu-eqslb4j" - } - step: "用户登录" - data: { - "clientIP": "::1" - } -[2026-08-27 11:51:06.161 +0800] DEBUG: [auth.login] 密码验证: 密码正确 -> PASS - ctx: { - "module": "auth", - "action": "login", - "traceId": "mtazjpqu-eqslb4j" - } - branch: "密码验证" - condition: "密码正确" - status: "PASS" - data: { - "userId": 1 - } - POST /api/auth/login 200 in 287ms (compile: 5ms, proxy.ts: 8ms, render: 275ms) - GET /api/system/notice?page=1&pageSize=10 200 in 137ms (compile: 24ms, proxy.ts: 14ms, render: 98ms) - GET /api/system/config?pageSize=200 200 in 150ms (compile: 14ms, proxy.ts: 15ms, render: 121ms) - GET /api/dashboard 200 in 267ms (compile: 15ms, proxy.ts: 33ms, render: 218ms) - GET /api/auth/menus 200 in 210ms (compile: 32ms, proxy.ts: 41ms, render: 138ms) - GET /api/system/notice?page=1&pageSize=10 200 in 251ms (compile: 30ms, proxy.ts: 32ms, render: 189ms) - GET /api/system/config?pageSize=200 200 in 278ms (compile: 27ms, proxy.ts: 37ms, render: 215ms) - GET /en/dashboard 200 in 312ms (compile: 75ms, proxy.ts: 36ms, generate-params: 52µs, render: 201ms) - GET /api/organization?type=company 200 in 171ms (compile: 28ms, proxy.ts: 31ms, render: 113ms) - GET /api/dashboard 200 in 270ms (compile: 32ms, proxy.ts: 30ms, render: 208ms) - GET /en/dashboard 200 in 205ms (compile: 82ms, proxy.ts: 25ms, generate-params: 47µs, render: 98ms) - GET /api/organization?type=company 200 in 115ms (compile: 11ms, proxy.ts: 72ms, render: 32ms) - GET /api/system/config?pageSize=200 200 in 161ms (compile: 63ms, proxy.ts: 30ms, render: 68ms) - GET /api/system/config?pageSize=200 200 in 62ms (compile: 7ms, proxy.ts: 12ms, render: 43ms) - GET /api/system/notice?page=1&pageSize=10 200 in 83ms (compile: 21ms, proxy.ts: 19ms, render: 43ms) - GET /api/system/config?pageSize=200 200 in 103ms (compile: 19ms, proxy.ts: 12ms, render: 71ms) - GET /api/dashboard 200 in 114ms (compile: 18ms, proxy.ts: 23ms, render: 72ms) - GET /api/system/notice?page=1&pageSize=10 200 in 82ms (compile: 15ms, proxy.ts: 15ms, render: 52ms) - GET /api/system/config?pageSize=200 200 in 89ms (compile: 22ms, proxy.ts: 13ms, render: 53ms) - GET /api/dashboard 200 in 77ms (compile: 10ms, proxy.ts: 18ms, render: 49ms) - GET /api/system/config?pageSize=200 200 in 49ms (compile: 10ms, proxy.ts: 10ms, render: 29ms) - GET /api/system/config?pageSize=200 200 in 69ms (compile: 6ms, proxy.ts: 9ms, render: 54ms) - GET /api/organization?type=company 200 in 52ms (compile: 7ms, proxy.ts: 10ms, render: 36ms) - GET /api/organization?type=company 200 in 37ms (compile: 6ms, proxy.ts: 9ms, render: 21ms) - GET /en/warehouse/inbound 200 in 417ms (compile: 36ms, proxy.ts: 8ms, generate-params: 17ms, render: 373ms) - GET /api/auth/menus 200 in 50ms (compile: 16ms, proxy.ts: 13ms, render: 21ms) - GET /api/system/config?pageSize=200 200 in 65ms (compile: 9ms, proxy.ts: 14ms, render: 42ms) - GET /api/system/config?pageSize=200 200 in 166ms (compile: 10ms, proxy.ts: 10ms, render: 146ms) - GET /api/system/notice?page=1&pageSize=10 200 in 166ms (compile: 35ms, proxy.ts: 11ms, render: 120ms) -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) -[2026-08-27 11:51:08.621 +0800] DEBUG: Event registry already initialized - GET /api/organization/warehouse-category 200 in 161ms (compile: 38ms, proxy.ts: 37ms, render: 86ms) - GET /api/warehouse/inbound?page=1&pageSize=1000 200 in 179ms (compile: 23ms, proxy.ts: 38ms, render: 117ms) - GET /api/warehouse?all=true 200 in 177ms (compile: 31ms, proxy.ts: 37ms, render: 109ms) - GET /api/purchase/suppliers?pageSize=1000 200 in 174ms (compile: 43ms, proxy.ts: 39ms, render: 91ms) - GET /api/warehouse/inbound/labels?pageSize=1000 200 in 89ms (compile: 20ms, proxy.ts: 29ms, render: 40ms) - GET /api/system/config?pageSize=200 200 in 93ms (compile: 18ms, proxy.ts: 34ms, render: 40ms) - GET /en/warehouse/inbound 200 in 342ms (compile: 16ms, proxy.ts: 8ms, generate-params: 38µs, render: 318ms) - GET /api/auth/menus 200 in 47ms (compile: 18ms, proxy.ts: 10ms, render: 19ms) - GET /api/system/config?pageSize=200 200 in 66ms (compile: 7ms, proxy.ts: 11ms, render: 48ms) - GET /api/system/config?pageSize=200 200 in 143ms (compile: 11ms, proxy.ts: 9ms, render: 123ms) - GET /api/system/notice?page=1&pageSize=10 200 in 148ms (compile: 35ms, proxy.ts: 10ms, render: 103ms) -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) -[2026-08-27 11:51:09.937 +0800] DEBUG: Event registry already initialized - GET /api/organization/warehouse-category 200 in 138ms (compile: 32ms, proxy.ts: 44ms, render: 61ms) - GET /api/purchase/suppliers?pageSize=1000 200 in 150ms (compile: 34ms, proxy.ts: 46ms, render: 71ms) - GET /api/warehouse/inbound?page=1&pageSize=1000 200 in 168ms (compile: 24ms, proxy.ts: 43ms, render: 101ms) - GET /api/warehouse?all=true 200 in 169ms (compile: 27ms, proxy.ts: 43ms, render: 99ms) - GET /api/warehouse/inbound/labels?pageSize=1000 200 in 170ms (compile: 44ms, proxy.ts: 27ms, render: 98ms) -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) - GET /api/organization/warehouse-category 200 in 141ms (compile: 23ms, proxy.ts: 48ms, render: 70ms) -[2026-08-27 11:51:10.125 +0800] DEBUG: Event registry already initialized - GET /api/system/notice?page=1&pageSize=10 200 in 170ms (compile: 37ms, proxy.ts: 43ms, render: 90ms) - GET /api/purchase/suppliers?pageSize=1000 200 in 154ms (compile: 27ms, proxy.ts: 45ms, render: 82ms) - GET /api/system/config?pageSize=200 200 in 216ms (compile: 38ms, proxy.ts: 43ms, render: 135ms) - GET /api/warehouse/inbound?page=1&pageSize=1000 200 in 164ms (compile: 31ms, proxy.ts: 40ms, render: 93ms) - GET /api/warehouse?all=true 200 in 142ms (compile: 44ms, proxy.ts: 26ms, render: 72ms) - GET /api/organization?type=company 200 in 97ms (compile: 20ms, proxy.ts: 36ms, render: 40ms) - GET /api/warehouse/inbound/labels?pageSize=1000 200 in 103ms (compile: 15ms, proxy.ts: 37ms, render: 51ms) - GET /api/system/config?pageSize=200 200 in 90ms (compile: 34ms, proxy.ts: 17ms, render: 39ms) - GET /api/system/config?pageSize=200 200 in 38ms (compile: 5ms, proxy.ts: 6ms, render: 28ms) -[2026-08-27 11:51:10.771 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:51:10.775 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" - GET /api/organization?type=company 200 in 26ms (compile: 4ms, proxy.ts: 8ms, render: 15ms) - GET /en/login 200 in 289ms (compile: 22ms, proxy.ts: 8ms, generate-params: 12ms, render: 259ms) - GET /en/login 200 in 253ms (compile: 14ms, proxy.ts: 13ms, generate-params: 39µs, render: 226ms) -[2026-08-27 11:51:14.317 +0800] DEBUG: [auth.login] 用户登录 - ctx: { - "module": "auth", - "action": "login", - "traceId": "mtazjw5p-oc0mse5" - } - step: "用户登录" - data: { - "clientIP": "::1" - } -[2026-08-27 11:51:14.459 +0800] DEBUG: [auth.login] 密码验证: 密码正确 -> PASS - ctx: { - "module": "auth", - "action": "login", - "traceId": "mtazjw5p-oc0mse5" - } - branch: "密码验证" - condition: "密码正确" - status: "PASS" - data: { - "userId": 1 - } - POST /api/auth/login 200 in 183ms (compile: 4ms, proxy.ts: 7ms, render: 172ms) - GET /api/auth/menus 200 in 96ms (compile: 8ms, proxy.ts: 10ms, render: 78ms) - GET /en/dashboard 200 in 101ms (compile: 29ms, proxy.ts: 14ms, generate-params: 17ms, render: 58ms) - GET /en/dashboard 200 in 71ms (compile: 12ms, proxy.ts: 11ms, generate-params: 43µs, render: 47ms) -[2026-08-27 11:51:14.840 +0800] DEBUG: [auth.login] 用户登录 - ctx: { - "module": "auth", - "action": "login", - "traceId": "mtazjwk8-9jasbd0" - } - step: "用户登录" - data: { - "clientIP": "::1" - } -[2026-08-27 11:51:14.988 +0800] DEBUG: [auth.login] 密码验证: 密码正确 -> PASS - ctx: { - "module": "auth", - "action": "login", - "traceId": "mtazjwk8-9jasbd0" - } - branch: "密码验证" - condition: "密码正确" - status: "PASS" - data: { - "userId": 1 - } - POST /api/auth/login 200 in 259ms (compile: 10ms, proxy.ts: 7ms, render: 242ms) - GET /api/system/notice?page=1&pageSize=10 200 in 107ms (compile: 25ms, proxy.ts: 19ms, render: 63ms) - GET /api/system/config?pageSize=200 200 in 126ms (compile: 21ms, proxy.ts: 16ms, render: 89ms) - GET /api/dashboard 200 in 194ms (compile: 13ms, proxy.ts: 31ms, render: 150ms) - GET /api/auth/menus 200 in 184ms (compile: 35ms, proxy.ts: 30ms, render: 118ms) - GET /api/system/notice?page=1&pageSize=10 200 in 202ms (compile: 41ms, proxy.ts: 26ms, render: 135ms) - GET /api/organization?type=company 200 in 175ms (compile: 65ms, proxy.ts: 38ms, render: 73ms) - GET /api/system/config?pageSize=200 200 in 220ms (compile: 36ms, proxy.ts: 32ms, render: 152ms) - GET /en/dashboard 200 in 252ms (compile: 83ms, proxy.ts: 36ms, generate-params: 42µs, render: 133ms) - GET /api/dashboard 200 in 218ms (compile: 13ms, proxy.ts: 63ms, render: 142ms) - GET /api/organization?type=company 200 in 131ms (compile: 22ms, proxy.ts: 26ms, render: 82ms) - GET /en/dashboard 200 in 164ms (compile: 57ms, proxy.ts: 37ms, generate-params: 41µs, render: 71ms) - GET /api/system/config?pageSize=200 200 in 155ms (compile: 16ms, proxy.ts: 31ms, render: 108ms) - GET /api/system/config?pageSize=200 200 in 43ms (compile: 7ms, proxy.ts: 8ms, render: 27ms) -[2026-08-27 11:51:15.777 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:51:15.787 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" - GET /api/system/config?pageSize=200 200 in 102ms (compile: 23ms, proxy.ts: 10ms, render: 70ms) - GET /api/system/notice?page=1&pageSize=10 200 in 100ms (compile: 16ms, proxy.ts: 33ms, render: 51ms) - GET /api/dashboard 200 in 128ms (compile: 19ms, proxy.ts: 29ms, render: 79ms) - GET /api/system/notice?page=1&pageSize=10 200 in 76ms (compile: 17ms, proxy.ts: 18ms, render: 41ms) - GET /api/system/config?pageSize=200 200 in 95ms (compile: 11ms, proxy.ts: 20ms, render: 63ms) - GET /api/dashboard 200 in 85ms (compile: 8ms, proxy.ts: 11ms, render: 65ms) - GET /api/system/config?pageSize=200 200 in 60ms (compile: 8ms, proxy.ts: 11ms, render: 41ms) - GET /api/system/config?pageSize=200 200 in 64ms (compile: 6ms, proxy.ts: 10ms, render: 48ms) - GET /api/organization?type=company 200 in 42ms (compile: 5ms, proxy.ts: 11ms, render: 26ms) - GET /api/organization?type=company 200 in 31ms (compile: 7ms, proxy.ts: 6ms, render: 17ms) - GET /en/warehouse/inbound 200 in 363ms (compile: 31ms, proxy.ts: 10ms, generate-params: 17ms, render: 323ms) - GET /api/auth/menus 200 in 46ms (compile: 15ms, proxy.ts: 14ms, render: 17ms) - GET /api/system/config?pageSize=200 200 in 70ms (compile: 11ms, proxy.ts: 13ms, render: 46ms) - GET /api/system/config?pageSize=200 200 in 146ms (compile: 10ms, proxy.ts: 11ms, render: 125ms) - GET /api/system/notice?page=1&pageSize=10 200 in 146ms (compile: 35ms, proxy.ts: 10ms, render: 101ms) -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) -[2026-08-27 11:51:17.274 +0800] DEBUG: Event registry already initialized - GET /api/organization/warehouse-category 200 in 134ms (compile: 33ms, proxy.ts: 34ms, render: 68ms) - GET /api/purchase/suppliers?pageSize=1000 200 in 143ms (compile: 39ms, proxy.ts: 34ms, render: 70ms) - GET /api/warehouse/inbound?page=1&pageSize=1000 200 in 165ms (compile: 20ms, proxy.ts: 37ms, render: 109ms) - GET /api/warehouse?all=true 200 in 166ms (compile: 23ms, proxy.ts: 36ms, render: 107ms) - GET /api/warehouse/inbound/labels?pageSize=1000 200 in 163ms (compile: 41ms, proxy.ts: 35ms, render: 88ms) -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) -[2026-08-27 11:51:17.444 +0800] DEBUG: Event registry already initialized - GET /api/organization/warehouse-category 200 in 121ms (compile: 25ms, proxy.ts: 43ms, render: 53ms) - GET /api/system/notice?page=1&pageSize=10 200 in 148ms (compile: 41ms, proxy.ts: 34ms, render: 74ms) - GET /api/purchase/suppliers?pageSize=1000 200 in 134ms (compile: 27ms, proxy.ts: 44ms, render: 63ms) - GET /api/system/config?pageSize=200 200 in 198ms (compile: 39ms, proxy.ts: 42ms, render: 117ms) - GET /api/warehouse/inbound?page=1&pageSize=1000 200 in 153ms (compile: 34ms, proxy.ts: 43ms, render: 76ms) - GET /api/warehouse?all=true 200 in 136ms (compile: 29ms, proxy.ts: 40ms, render: 67ms) - GET /api/warehouse/inbound/labels?pageSize=1000 200 in 104ms (compile: 25ms, proxy.ts: 29ms, render: 50ms) - GET /api/organization?type=company 200 in 86ms (compile: 25ms, proxy.ts: 28ms, render: 34ms) - GET /api/system/config?pageSize=200 200 in 100ms (compile: 20ms, proxy.ts: 27ms, render: 53ms) - GET /api/system/config?pageSize=200 200 in 37ms (compile: 4ms, proxy.ts: 7ms, render: 26ms) - GET /en/warehouse/inbound 200 in 343ms (compile: 19ms, proxy.ts: 8ms, generate-params: 41µs, render: 315ms) - GET /api/organization?type=company 200 in 54ms (compile: 8ms, proxy.ts: 11ms, render: 34ms) - GET /api/auth/menus 200 in 49ms (compile: 16ms, proxy.ts: 14ms, render: 19ms) - GET /api/system/config?pageSize=200 200 in 66ms (compile: 8ms, proxy.ts: 17ms, render: 41ms) - GET /api/system/config?pageSize=200 200 in 147ms (compile: 9ms, proxy.ts: 11ms, render: 127ms) - GET /api/system/notice?page=1&pageSize=10 200 in 145ms (compile: 37ms, proxy.ts: 9ms, render: 99ms) -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) -[2026-08-27 11:51:18.768 +0800] DEBUG: Event registry already initialized - GET /api/organization/warehouse-category 200 in 140ms (compile: 29ms, proxy.ts: 34ms, render: 76ms) - GET /api/purchase/suppliers?pageSize=1000 200 in 148ms (compile: 34ms, proxy.ts: 35ms, render: 80ms) - GET /api/warehouse/inbound?page=1&pageSize=1000 200 in 164ms (compile: 18ms, proxy.ts: 38ms, render: 109ms) - GET /api/warehouse?all=true 200 in 171ms (compile: 24ms, proxy.ts: 36ms, render: 112ms) - GET /api/warehouse/inbound/labels?pageSize=1000 200 in 190ms (compile: 41ms, proxy.ts: 38ms, render: 111ms) - GET /api/organization/warehouse-category 200 in 150ms (compile: 29ms, proxy.ts: 46ms, render: 75ms) -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) -[2026-08-27 11:51:18.975 +0800] DEBUG: Event registry already initialized - GET /api/system/notice?page=1&pageSize=10 200 in 179ms (compile: 42ms, proxy.ts: 34ms, render: 102ms) - GET /api/system/config?pageSize=200 200 in 223ms (compile: 46ms, proxy.ts: 37ms, render: 140ms) - GET /api/purchase/suppliers?pageSize=1000 200 in 171ms (compile: 45ms, proxy.ts: 46ms, render: 80ms) - GET /api/warehouse/inbound?page=1&pageSize=1000 200 in 175ms (compile: 50ms, proxy.ts: 41ms, render: 85ms) - GET /api/warehouse?all=true 200 in 108ms (compile: 17ms, proxy.ts: 38ms, render: 52ms) - GET /api/organization?type=company 200 in 92ms (compile: 19ms, proxy.ts: 26ms, render: 48ms) - GET /api/warehouse/inbound/labels?pageSize=1000 200 in 84ms (compile: 20ms, proxy.ts: 22ms, render: 42ms) - GET /api/system/config?pageSize=200 200 in 96ms (compile: 26ms, proxy.ts: 23ms, render: 48ms) - GET /api/system/config?pageSize=200 200 in 42ms (compile: 6ms, proxy.ts: 8ms, render: 27ms) - GET /api/organization?type=company 200 in 28ms (compile: 5ms, proxy.ts: 6ms, render: 17ms) -[2026-08-27 11:51:20.783 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:51:20.786 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" - GET /api/warehouse/categories 200 in 50ms (compile: 11ms, proxy.ts: 9ms, render: 30ms) - GET /api/system/currency?active=true 200 in 50ms (compile: 20ms, proxy.ts: 10ms, render: 19ms) - GET /api/warehouse?all=true&status=active 200 in 60ms (compile: 16ms, proxy.ts: 10ms, render: 34ms) - GET /api/warehouse/categories 200 in 55ms (compile: 11ms, proxy.ts: 16ms, render: 28ms) - GET /api/system/currency?active=true 200 in 56ms (compile: 16ms, proxy.ts: 17ms, render: 23ms) - GET /api/warehouse?all=true&status=active 200 in 52ms (compile: 19ms, proxy.ts: 11ms, render: 21ms) - GET /en/login 200 in 184ms (compile: 22ms, proxy.ts: 8ms, generate-params: 12ms, render: 154ms) -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) -[2026-08-27 11:51:24.503 +0800] INFO: [warehouse/inbound] 开始物料分类校验 - itemCount: 1 - materialIds: [] -[2026-08-27 11:51:24.503 +0800] INFO: [warehouse/inbound] 物料分类校验完成 - blocked: false - uncategorizedCount: 0 -[2026-08-27 11:51:24.503 +0800] DEBUG: Event registry already initialized - POST /api/warehouse/inbound 200 in 59ms (compile: 5ms, proxy.ts: 8ms, render: 46ms) -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) -[2026-08-27 11:51:24.565 +0800] DEBUG: Event registry already initialized - GET /api/warehouse/inbound?page=1&pageSize=1000 200 in 38ms (compile: 6ms, proxy.ts: 9ms, render: 23ms) -[2026-08-27 11:51:24.688 +0800] DEBUG: [auth.login] 用户登录 - ctx: { - "module": "auth", - "action": "login", - "traceId": "mtazk45s-ih3dver" - } - step: "用户登录" - data: { - "clientIP": "::1" - } -[2026-08-27 11:51:24.824 +0800] DEBUG: [auth.login] 密码验证: 密码正确 -> PASS - ctx: { - "module": "auth", - "action": "login", - "traceId": "mtazk45s-ih3dver" - } - branch: "密码验证" - condition: "密码正确" - status: "PASS" - data: { - "userId": 1 - } - POST /api/auth/login 200 in 480ms (compile: 5ms, proxy.ts: 8ms, render: 468ms) - GET /api/auth/menus 200 in 108ms (compile: 10ms, proxy.ts: 11ms, render: 87ms) - GET /en/dashboard 200 in 105ms (compile: 37ms, proxy.ts: 19ms, generate-params: 16ms, render: 49ms) - GET /en/dashboard 200 in 52ms (compile: 15ms, proxy.ts: 8ms, generate-params: 44µs, render: 29ms) - GET /api/system/config?pageSize=200 200 in 92ms (compile: 13ms, proxy.ts: 8ms, render: 71ms) - GET /api/system/notice?page=1&pageSize=10 200 in 84ms (compile: 15ms, proxy.ts: 32ms, render: 38ms) - GET /api/dashboard 200 in 90ms (compile: 19ms, proxy.ts: 16ms, render: 55ms) - GET /api/system/notice?page=1&pageSize=10 200 in 71ms (compile: 16ms, proxy.ts: 15ms, render: 40ms) - GET /api/system/config?pageSize=200 200 in 85ms (compile: 11ms, proxy.ts: 16ms, render: 58ms) - GET /api/dashboard 200 in 79ms (compile: 8ms, proxy.ts: 12ms, render: 60ms) - GET /api/system/config?pageSize=200 200 in 55ms (compile: 8ms, proxy.ts: 12ms, render: 36ms) -[2026-08-27 11:51:25.785 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:51:25.791 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" - GET /api/system/config?pageSize=200 200 in 50ms (compile: 8ms, proxy.ts: 9ms, render: 33ms) - GET /api/organization?type=company 200 in 39ms (compile: 6ms, proxy.ts: 14ms, render: 19ms) - GET /api/organization?type=company 200 in 30ms (compile: 5ms, proxy.ts: 8ms, render: 16ms) - GET /en/warehouse/inbound 200 in 332ms (compile: 28ms, proxy.ts: 7ms, generate-params: 15ms, render: 298ms) - GET /api/auth/menus 200 in 37ms (compile: 12ms, proxy.ts: 9ms, render: 16ms) - GET /api/system/config?pageSize=200 200 in 56ms (compile: 7ms, proxy.ts: 9ms, render: 40ms) -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) -[2026-08-27 11:51:28.415 +0800] DEBUG: Event registry already initialized - GET /api/organization/warehouse-category 200 in 186ms (compile: 58ms, proxy.ts: 60ms, render: 69ms) - GET /api/system/notice?page=1&pageSize=10 200 in 236ms (compile: 51ms, proxy.ts: 78ms, render: 108ms) - GET /api/purchase/suppliers?pageSize=1000 200 in 170ms (compile: 52ms, proxy.ts: 44ms, render: 74ms) - GET /api/warehouse/inbound?page=1&pageSize=1000 200 in 222ms (compile: 50ms, proxy.ts: 62ms, render: 111ms) - GET /api/warehouse?all=true 200 in 221ms (compile: 53ms, proxy.ts: 60ms, render: 108ms) - GET /api/system/config?pageSize=200 200 in 277ms (compile: 46ms, proxy.ts: 77ms, render: 154ms) - GET /en/login 200 in 453ms (compile: 31ms, proxy.ts: 8ms, generate-params: 20ms, render: 414ms) - GET /api/warehouse/inbound/labels?pageSize=1000 200 in 115ms (compile: 18ms, proxy.ts: 42ms, render: 56ms) - GET /api/system/config?pageSize=200 200 in 119ms (compile: 11ms, proxy.ts: 24ms, render: 84ms) -[2026-08-27 11:51:29.353 +0800] DEBUG: [auth.login] 用户登录 - ctx: { - "module": "auth", - "action": "login", - "traceId": "mtazk7rd-1kdv6fr" - } - step: "用户登录" - data: { - "clientIP": "::1" - } -[2026-08-27 11:51:29.510 +0800] DEBUG: [auth.login] 密码验证: 密码正确 -> PASS - ctx: { - "module": "auth", - "action": "login", - "traceId": "mtazk7rd-1kdv6fr" - } - branch: "密码验证" - condition: "密码正确" - status: "PASS" - data: { - "userId": 1 - } - POST /api/auth/login 200 in 213ms (compile: 7ms, proxy.ts: 9ms, render: 197ms) - GET /api/auth/menus 200 in 117ms (compile: 10ms, proxy.ts: 7ms, render: 100ms) - GET /en/dashboard 200 in 129ms (compile: 37ms, proxy.ts: 19ms, generate-params: 19ms, render: 73ms) - GET /en/dashboard 200 in 69ms (compile: 16ms, proxy.ts: 9ms, generate-params: 54µs, render: 44ms) - GET /api/system/notice?page=1&pageSize=10 200 in 68ms (compile: 18ms, proxy.ts: 17ms, render: 32ms) - GET /api/system/config?pageSize=200 200 in 92ms (compile: 17ms, proxy.ts: 14ms, render: 61ms) - GET /api/dashboard 200 in 94ms (compile: 14ms, proxy.ts: 22ms, render: 58ms) - GET /api/system/notice?page=1&pageSize=10 200 in 95ms (compile: 17ms, proxy.ts: 17ms, render: 61ms) - GET /api/system/config?pageSize=200 200 in 102ms (compile: 25ms, proxy.ts: 16ms, render: 62ms) - GET /api/dashboard 200 in 91ms (compile: 9ms, proxy.ts: 22ms, render: 59ms) - GET /api/system/config?pageSize=200 200 in 47ms (compile: 10ms, proxy.ts: 7ms, render: 30ms) - GET /api/system/config?pageSize=200 200 in 47ms (compile: 5ms, proxy.ts: 9ms, render: 34ms) - GET /api/organization?type=company 200 in 32ms (compile: 5ms, proxy.ts: 8ms, render: 19ms) - GET /api/organization?type=company 200 in 31ms (compile: 5ms, proxy.ts: 7ms, render: 19ms) -[2026-08-27 11:51:30.793 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:51:30.797 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" - GET /en/warehouse/inbound 200 in 346ms (compile: 29ms, proxy.ts: 8ms, generate-params: 17ms, render: 309ms) - GET /api/auth/menus 200 in 53ms (compile: 12ms, proxy.ts: 14ms, render: 28ms) - GET /api/system/config?pageSize=200 200 in 68ms (compile: 7ms, proxy.ts: 14ms, render: 46ms) -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) - GET /api/system/notice?page=1&pageSize=10 200 in 139ms (compile: 36ms, proxy.ts: 12ms, render: 91ms) -[2026-08-27 11:51:32.306 +0800] DEBUG: Event registry already initialized - GET /api/system/config?pageSize=200 200 in 150ms (compile: 11ms, proxy.ts: 13ms, render: 125ms) - GET /api/organization/warehouse-category 200 in 129ms (compile: 32ms, proxy.ts: 32ms, render: 65ms) - GET /api/warehouse/inbound?page=1&pageSize=1000 200 in 145ms (compile: 23ms, proxy.ts: 34ms, render: 88ms) - GET /api/purchase/suppliers?pageSize=1000 200 in 139ms (compile: 35ms, proxy.ts: 34ms, render: 70ms) - GET /api/warehouse?all=true 200 in 150ms (compile: 26ms, proxy.ts: 35ms, render: 89ms) - GET /api/warehouse/inbound/labels?pageSize=1000 200 in 65ms (compile: 8ms, proxy.ts: 28ms, render: 28ms) - GET /api/system/config?pageSize=200 200 in 78ms (compile: 12ms, proxy.ts: 29ms, render: 37ms) - GET /en/login 200 in 265ms (compile: 38ms, proxy.ts: 11ms, generate-params: 24ms, render: 216ms) -[2026-08-27 11:51:34.310 +0800] DEBUG: [auth.login] 用户登录 - ctx: { - "module": "auth", - "action": "login", - "traceId": "mtazkbl2-lvmj3zb" - } - step: "用户登录" - data: { - "clientIP": "::1" - } -[2026-08-27 11:51:34.469 +0800] DEBUG: [auth.login] 密码验证: 密码正确 -> PASS - ctx: { - "module": "auth", - "action": "login", - "traceId": "mtazkbl2-lvmj3zb" - } - branch: "密码验证" - condition: "密码正确" - status: "PASS" - data: { - "userId": 1 - } - POST /api/auth/login 200 in 215ms (compile: 7ms, proxy.ts: 9ms, render: 199ms) - GET /api/auth/menus 200 in 98ms (compile: 9ms, proxy.ts: 7ms, render: 82ms) - GET /en/dashboard 200 in 107ms (compile: 31ms, proxy.ts: 17ms, generate-params: 17ms, render: 60ms) - GET /en/dashboard 200 in 62ms (compile: 14ms, proxy.ts: 10ms, generate-params: 57µs, render: 39ms) - GET /api/system/notice?page=1&pageSize=10 200 in 61ms (compile: 11ms, proxy.ts: 21ms, render: 30ms) - GET /api/system/config?pageSize=200 200 in 86ms (compile: 14ms, proxy.ts: 9ms, render: 63ms) - GET /api/dashboard 200 in 103ms (compile: 16ms, proxy.ts: 20ms, render: 66ms) - GET /api/system/notice?page=1&pageSize=10 200 in 89ms (compile: 22ms, proxy.ts: 15ms, render: 51ms) - GET /api/system/config?pageSize=200 200 in 97ms (compile: 24ms, proxy.ts: 20ms, render: 53ms) - GET /api/dashboard 200 in 76ms (compile: 7ms, proxy.ts: 19ms, render: 50ms) - GET /api/system/config?pageSize=200 200 in 44ms (compile: 8ms, proxy.ts: 9ms, render: 27ms) - GET /api/system/config?pageSize=200 200 in 45ms (compile: 4ms, proxy.ts: 8ms, render: 33ms) - GET /api/organization?type=company 200 in 35ms (compile: 6ms, proxy.ts: 8ms, render: 21ms) - GET /api/organization?type=company 200 in 31ms (compile: 5ms, proxy.ts: 8ms, render: 19ms) -[2026-08-27 11:51:35.797 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:51:35.801 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" - GET /api/warehouse/categories 200 in 69ms (compile: 14ms, proxy.ts: 14ms, render: 41ms) - GET /api/system/currency?active=true 200 in 69ms (compile: 24ms, proxy.ts: 15ms, render: 29ms) - GET /api/warehouse?all=true&status=active 200 in 82ms (compile: 18ms, proxy.ts: 15ms, render: 49ms) - GET /api/warehouse/categories 200 in 69ms (compile: 12ms, proxy.ts: 17ms, render: 40ms) - GET /api/system/currency?active=true 200 in 68ms (compile: 17ms, proxy.ts: 15ms, render: 35ms) - GET /api/warehouse?all=true&status=active 200 in 68ms (compile: 27ms, proxy.ts: 12ms, render: 30ms) - GET /en/warehouse/inbound 200 in 649ms (compile: 43ms, proxy.ts: 8ms, generate-params: 15ms, render: 598ms) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) -[2026-08-27 11:51:37.889 +0800] DEBUG: Event registry already initialized -[PurchaseRepository] 未知采购单状态码 status=2 (order id=148, po_no=PO_1787801486174_5),降级为 draft -[PurchaseRepository] 未知采购单状态码 status=2 (order id=145, po_no=PO_1787801486130_2),降级为 draft -[PurchaseRepository] 未知采购单状态码 status=2 (order id=144, po_no=PO_1787801486115_1),降级为 draft -[PurchaseRepository] 未知采购单状态码 status=2 (order id=143, po_no=PO_1787801486090_0),降级为 draft -[PurchaseRepository] 未知采购单状态码 status=2 (order id=146, po_no=PO_1787801486144_3),降级为 draft -[PurchaseRepository] 未知采购单状态码 status=2 (order id=147, po_no=PO_1787801486160_4),降级为 draft -[PurchaseRepository] 未知采购单状态码 status=2 (order id=137, po_no=PO_1787788536155_5),降级为 draft -[PurchaseRepository] 未知采购单状态码 status=2 (order id=136, po_no=PO_1787788536129_4),降级为 draft -[PurchaseRepository] 未知采购单状态码 status=2 (order id=135, po_no=PO_1787788536081_3),降级为 draft -[PurchaseRepository] 未知采购单状态码 status=2 (order id=134, po_no=PO_1787788536031_2),降级为 draft -[PurchaseRepository] 未知采购单状态码 status=2 (order id=133, po_no=PO_1787788535988_1),降级为 draft -[PurchaseRepository] 未知采购单状态码 status=2 (order id=132, po_no=PO_1787788535949_0),降级为 draft -[PurchaseRepository] 未知采购单状态码 status=2 (order id=131, po_no=PO_1787711404307_5),降级为 draft -[PurchaseRepository] 未知采购单状态码 status=2 (order id=130, po_no=PO_1787711404285_4),降级为 draft -[PurchaseRepository] 未知采购单状态码 status=2 (order id=129, po_no=PO_1787711404257_3),降级为 draft -[PurchaseRepository] 未知采购单状态码 status=2 (order id=128, po_no=PO_1787711404236_2),降级为 draft -[PurchaseRepository] 未知采购单状态码 status=2 (order id=127, po_no=PO_1787711404206_1),降级为 draft -[PurchaseRepository] 未知采购单状态码 status=2 (order id=126, po_no=PO_1787711404179_0),降级为 draft -[PurchaseRepository] 未知采购单状态码 status=2 (order id=122, po_no=PO_1787711344664_2),降级为 draft -[PurchaseRepository] 未知采购单状态码 status=2 (order id=123, po_no=PO_1787711344680_3),降级为 draft - GET /api/purchase/orders?keyword=PO&pageSize=20 200 in 125ms (compile: 14ms, proxy.ts: 17ms, render: 93ms) - GET /api/auth/menus 200 in 66ms (compile: 30ms, proxy.ts: 14ms, render: 23ms) - GET /api/system/config?pageSize=200 200 in 89ms (compile: 11ms, proxy.ts: 18ms, render: 60ms) - GET /api/system/config?pageSize=200 200 in 175ms (compile: 15ms, proxy.ts: 11ms, render: 149ms) -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) - GET /api/system/notice?page=1&pageSize=10 200 in 173ms (compile: 60ms, proxy.ts: 12ms, render: 102ms) -[2026-08-27 11:51:38.517 +0800] DEBUG: Event registry already initialized - GET /api/organization/warehouse-category 200 in 134ms (compile: 35ms, proxy.ts: 38ms, render: 62ms) - GET /api/warehouse/inbound?page=1&pageSize=1000 200 in 176ms (compile: 25ms, proxy.ts: 56ms, render: 95ms) - GET /api/warehouse?all=true 200 in 158ms (compile: 26ms, proxy.ts: 38ms, render: 93ms) - GET /api/purchase/suppliers?pageSize=1000 200 in 154ms (compile: 37ms, proxy.ts: 40ms, render: 77ms) - GET /api/warehouse/inbound/labels?pageSize=1000 200 in 180ms (compile: 39ms, proxy.ts: 39ms, render: 101ms) -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) -[2026-08-27 11:51:38.736 +0800] DEBUG: Event registry already initialized - GET /api/organization/warehouse-category 200 in 179ms (compile: 33ms, proxy.ts: 51ms, render: 95ms) - GET /api/system/notice?page=1&pageSize=10 200 in 211ms (compile: 41ms, proxy.ts: 40ms, render: 129ms) - GET /api/system/config?pageSize=200 200 in 257ms (compile: 46ms, proxy.ts: 40ms, render: 171ms) - GET /api/warehouse?all=true 200 in 204ms (compile: 43ms, proxy.ts: 46ms, render: 114ms) - GET /api/warehouse/inbound?page=1&pageSize=1000 200 in 230ms (compile: 38ms, proxy.ts: 53ms, render: 139ms) - GET /api/organization?type=company 200 in 226ms (compile: 44ms, proxy.ts: 63ms, render: 119ms) - GET /api/purchase/suppliers?pageSize=1000 200 in 284ms (compile: 59ms, proxy.ts: 75ms, render: 151ms) - GET /api/warehouse/inbound/labels?pageSize=1000 200 in 214ms (compile: 34ms, proxy.ts: 63ms, render: 117ms) - GET /api/system/config?pageSize=200 200 in 207ms (compile: 52ms, proxy.ts: 53ms, render: 101ms) - GET /api/system/config?pageSize=200 200 in 76ms (compile: 14ms, proxy.ts: 26ms, render: 37ms) - GET /api/organization?type=company 200 in 35ms (compile: 4ms, proxy.ts: 8ms, render: 24ms) -[2026-08-27 11:51:40.804 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:51:40.807 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" - GET /en/warehouse/inbound 200 in 1207ms (compile: 899ms, proxy.ts: 9ms, generate-params: 14ms, render: 299ms) - GET /api/auth/menus 200 in 39ms (compile: 12ms, proxy.ts: 10ms, render: 16ms) - GET /api/system/config?pageSize=200 200 in 52ms (compile: 7ms, proxy.ts: 10ms, render: 35ms) -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) -[2026-08-27 11:51:42.939 +0800] DEBUG: Event registry already initialized - GET /api/organization/warehouse-category 200 in 250ms (compile: 78ms, proxy.ts: 70ms, render: 103ms) - GET /api/warehouse/inbound?page=1&pageSize=1000 200 in 283ms (compile: 89ms, proxy.ts: 62ms, render: 131ms) - GET /api/system/notice?page=1&pageSize=10 200 in 298ms (compile: 92ms, proxy.ts: 57ms, render: 150ms) - GET /api/purchase/suppliers?pageSize=1000 200 in 262ms (compile: 84ms, proxy.ts: 65ms, render: 113ms) - GET /api/warehouse?all=true 200 in 278ms (compile: 69ms, proxy.ts: 68ms, render: 140ms) - GET /api/system/config?pageSize=200 200 in 325ms (compile: 79ms, proxy.ts: 58ms, render: 188ms) - GET /api/warehouse/inbound/labels?pageSize=1000 200 in 57ms (compile: 12ms, proxy.ts: 15ms, render: 30ms) - GET /api/system/config?pageSize=200 200 in 62ms (compile: 14ms, proxy.ts: 15ms, render: 33ms) -[2026-08-27 11:51:45.812 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:51:45.815 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -Persisting failed: Another write batch or compaction is already active (Only a single write operations is allowed at a time) -[2026-08-27 11:51:50.825 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:51:50.828 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:51:55.827 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:51:55.829 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:52:00.835 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:52:00.839 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:52:05.852 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:52:05.855 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" - GET /en/login 200 in 193ms (compile: 25ms, proxy.ts: 8ms, generate-params: 13ms, render: 161ms) -[2026-08-27 11:52:09.978 +0800] DEBUG: [auth.login] 用户登录 - ctx: { - "module": "auth", - "action": "login", - "traceId": "mtazl33u-3drrwj8" - } - step: "用户登录" - data: { - "clientIP": "::1" - } -[2026-08-27 11:52:10.095 +0800] DEBUG: [auth.login] 密码验证: 密码正确 -> PASS - ctx: { - "module": "auth", - "action": "login", - "traceId": "mtazl33u-3drrwj8" - } - branch: "密码验证" - condition: "密码正确" - status: "PASS" - data: { - "userId": 1 - } - POST /api/auth/login 200 in 164ms (compile: 5ms, proxy.ts: 6ms, render: 153ms) - GET /api/auth/menus 200 in 94ms (compile: 10ms, proxy.ts: 12ms, render: 73ms) - GET /en/dashboard 200 in 98ms (compile: 37ms, proxy.ts: 10ms, generate-params: 17ms, render: 50ms) - GET /en/dashboard 200 in 58ms (compile: 17ms, proxy.ts: 7ms, generate-params: 31µs, render: 35ms) - GET /api/system/notice?page=1&pageSize=10 200 in 69ms (compile: 9ms, proxy.ts: 27ms, render: 34ms) - GET /api/system/config?pageSize=200 200 in 81ms (compile: 14ms, proxy.ts: 6ms, render: 61ms) - GET /api/dashboard 200 in 90ms (compile: 15ms, proxy.ts: 25ms, render: 50ms) - GET /api/system/notice?page=1&pageSize=10 200 in 75ms (compile: 21ms, proxy.ts: 13ms, render: 41ms) - GET /api/system/config?pageSize=200 200 in 84ms (compile: 22ms, proxy.ts: 16ms, render: 46ms) - GET /api/dashboard 200 in 75ms (compile: 9ms, proxy.ts: 24ms, render: 43ms) - GET /api/system/config?pageSize=200 200 in 50ms (compile: 8ms, proxy.ts: 8ms, render: 33ms) - GET /api/organization?type=company 200 in 44ms (compile: 9ms, proxy.ts: 11ms, render: 25ms) - GET /api/system/config?pageSize=200 200 in 62ms (compile: 13ms, proxy.ts: 10ms, render: 39ms) - GET /api/organization?type=company 200 in 40ms (compile: 12ms, proxy.ts: 12ms, render: 17ms) -[2026-08-27 11:52:10.856 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:52:10.859 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" - GET /en/warehouse/inbound 200 in 361ms (compile: 26ms, proxy.ts: 8ms, generate-params: 14ms, render: 328ms) - GET /api/auth/menus 200 in 49ms (compile: 20ms, proxy.ts: 9ms, render: 19ms) - GET /api/system/config?pageSize=200 200 in 65ms (compile: 8ms, proxy.ts: 10ms, render: 46ms) - GET /api/system/notice?page=1&pageSize=10 200 in 130ms (compile: 33ms, proxy.ts: 13ms, render: 84ms) -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) -[2026-08-27 11:52:12.810 +0800] DEBUG: Event registry already initialized - GET /api/organization/warehouse-category 200 in 120ms (compile: 36ms, proxy.ts: 35ms, render: 48ms) - GET /api/system/config?pageSize=200 200 in 157ms (compile: 25ms, proxy.ts: 14ms, render: 118ms) - GET /api/purchase/suppliers?pageSize=1000 200 in 130ms (compile: 41ms, proxy.ts: 36ms, render: 53ms) - GET /api/warehouse/inbound?page=1&pageSize=1000 200 in 148ms (compile: 24ms, proxy.ts: 36ms, render: 88ms) - GET /api/warehouse?all=true 200 in 148ms (compile: 30ms, proxy.ts: 35ms, render: 83ms) - GET /api/warehouse/inbound/labels?pageSize=1000 200 in 66ms (compile: 12ms, proxy.ts: 31ms, render: 23ms) - GET /api/system/config?pageSize=200 200 in 51ms (compile: 12ms, proxy.ts: 14ms, render: 25ms) -[2026-08-27 11:52:15.858 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:52:15.861 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" - GET /en/warehouse/inbound/cutting 200 in 220ms (compile: 23ms, proxy.ts: 6ms, generate-params: 11ms, render: 191ms) - GET /api/auth/menus 200 in 39ms (compile: 12ms, proxy.ts: 10ms, render: 17ms) - GET /api/system/notice?page=1&pageSize=10 200 in 37ms (compile: 5ms, proxy.ts: 9ms, render: 23ms) - GET /api/warehouse/inbound/cutting?page=1&pageSize=20 200 in 35ms (compile: 9ms, proxy.ts: 7ms, render: 19ms) - GET /api/system/config?pageSize=200 200 in 244ms (compile: 7ms, proxy.ts: 9ms, render: 227ms) - GET /api/system/config?pageSize=200 200 in 41ms (compile: 4ms, proxy.ts: 7ms, render: 30ms) - GET /api/system/config?pageSize=200 200 in 36ms (compile: 5ms, proxy.ts: 7ms, render: 24ms) -[2026-08-27 11:52:20.867 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:52:20.869 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:52:25.878 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:52:25.881 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -Persisting failed: Another write batch or compaction is already active (Only a single write operations is allowed at a time) -[2026-08-27 11:52:30.881 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:52:30.885 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:52:35.893 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:52:35.896 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:52:40.902 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:52:40.905 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:52:45.907 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:52:45.909 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:52:50.908 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:52:50.911 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:52:55.915 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:52:55.918 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:53:00.925 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:53:00.928 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:53:05.934 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:53:05.937 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:53:10.936 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:53:10.938 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:53:15.939 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:53:15.941 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:53:20.947 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:53:20.950 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:53:25.955 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:53:25.957 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:53:30.964 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:53:30.967 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:53:35.974 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:53:35.976 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -Persisting failed: Another write batch or compaction is already active (Only a single write operations is allowed at a time) -[2026-08-27 11:53:40.975 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:53:40.977 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:53:45.983 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:53:45.985 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:53:50.984 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:53:50.986 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:53:55.988 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:53:55.990 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:54:00.995 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:54:00.998 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:54:06.001 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:54:06.003 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:54:11.013 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:54:11.016 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:54:16.028 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:54:16.031 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:54:21.038 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:54:21.040 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:54:26.039 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:54:26.042 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:54:31.044 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:54:31.047 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:54:36.046 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:54:36.049 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -Persisting failed: Another write batch or compaction is already active (Only a single write operations is allowed at a time) -[2026-08-27 11:54:41.056 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:54:41.058 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:54:46.071 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:54:46.073 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:54:51.084 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:54:51.087 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:54:56.089 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:54:56.092 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:55:01.095 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:55:01.098 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:55:06.101 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:55:06.103 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:55:11.107 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:55:11.109 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:55:16.108 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:55:16.111 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:55:21.113 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:55:21.116 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:55:26.125 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:55:26.128 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:55:31.129 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:55:31.132 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:55:36.137 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:55:36.139 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:55:41.141 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:55:41.143 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:55:46.149 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:55:46.151 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:55:51.153 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:55:51.155 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:55:56.164 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:55:56.166 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:56:01.176 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:56:01.178 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:56:06.185 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:56:06.188 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:56:11.198 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:56:11.200 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:56:16.199 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:56:16.202 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:56:21.207 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:56:21.209 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:56:26.221 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:56:26.223 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:56:31.231 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:56:31.233 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:56:36.244 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:56:36.247 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -Persisting failed: Another write batch or compaction is already active (Only a single write operations is allowed at a time) -Persisting failed: Another write batch or compaction is already active (Only a single write operations is allowed at a time) -[2026-08-27 11:56:41.250 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:56:41.252 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:56:46.252 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:56:46.255 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:56:51.252 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:56:51.255 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:56:56.258 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:56:56.261 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:57:01.264 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:57:01.267 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:57:06.267 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:57:06.269 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:57:11.275 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:57:11.279 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:57:16.280 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:57:16.282 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:57:21.281 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:57:21.284 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:57:26.290 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:57:26.292 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:57:31.298 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:57:31.300 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:57:36.302 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:57:36.304 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:57:41.312 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:57:41.314 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:57:46.317 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:57:46.319 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:57:51.327 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:57:51.330 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:57:56.330 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:57:56.335 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:58:01.342 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:58:01.344 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:58:06.347 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:58:06.348 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:58:11.359 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:58:11.361 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:58:16.363 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:58:16.365 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:58:21.363 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:58:21.365 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:58:26.371 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:58:26.373 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:58:31.374 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:58:31.376 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:58:36.379 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:58:36.381 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:58:41.390 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:58:41.392 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:58:46.407 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:58:46.408 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:58:51.409 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:58:51.412 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:58:56.417 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:58:56.419 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:59:01.428 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:59:01.433 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:59:06.440 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:59:06.445 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:59:11.448 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:59:11.451 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:59:16.454 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:59:16.456 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:59:21.460 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:59:21.463 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:59:26.462 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:59:26.464 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:59:31.476 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:59:31.478 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:59:36.490 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:59:36.492 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:59:41.492 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:59:41.494 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:59:46.499 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:59:46.500 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:59:51.501 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:59:51.503 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 11:59:56.512 +0800] DEBUG: Event registry already initialized -[2026-08-27 11:59:56.514 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:00:01.519 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:00:01.521 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:00:06.525 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:00:06.527 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:00:11.533 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:00:11.536 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:00:16.543 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:00:16.546 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:00:21.546 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:00:21.549 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:00:26.557 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:00:26.559 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:00:31.563 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:00:31.564 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:00:36.571 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:00:36.573 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:00:41.573 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:00:41.575 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:00:46.578 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:00:46.580 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:00:51.582 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:00:51.584 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:00:56.596 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:00:56.599 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:01:01.605 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:01:01.607 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:01:06.616 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:01:06.618 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:01:11.621 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:01:11.623 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:01:16.632 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:01:16.636 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:01:21.637 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:01:21.639 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:01:26.638 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:01:26.641 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -Persisting failed: Another write batch or compaction is already active (Only a single write operations is allowed at a time) -[2026-08-27 12:01:31.641 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:01:31.644 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:01:36.648 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:01:36.650 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:01:41.660 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:01:41.664 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:01:46.671 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:01:46.675 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:01:51.685 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:01:51.688 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:01:56.687 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:01:56.690 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:02:01.702 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:02:01.704 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:02:06.715 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:02:06.717 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:02:11.721 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:02:11.723 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:02:16.731 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:02:16.734 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:02:21.743 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:02:21.746 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:02:26.754 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:02:26.756 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:02:31.764 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:02:31.766 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:02:36.769 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:02:36.771 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:02:41.778 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:02:41.780 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:02:46.783 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:02:46.785 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:02:51.799 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:02:51.801 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:02:56.813 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:02:56.815 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:03:01.818 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:03:01.820 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:03:06.818 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:03:06.821 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:03:11.825 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:03:11.827 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:03:16.832 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:03:16.834 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:03:21.839 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:03:21.841 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:03:26.850 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:03:26.854 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -Persisting failed: Another write batch or compaction is already active (Only a single write operations is allowed at a time) -[2026-08-27 12:03:31.860 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:03:31.862 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:03:36.871 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:03:36.873 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:03:41.873 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:03:41.875 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:03:46.877 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:03:46.879 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:03:51.890 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:03:51.892 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:03:56.906 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:03:56.909 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:04:01.918 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:04:01.921 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:04:06.930 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:04:06.932 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:04:11.932 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:04:11.934 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -Persisting failed: Another write batch or compaction is already active (Only a single write operations is allowed at a time) -[2026-08-27 12:04:16.934 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:04:16.936 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:04:21.943 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:04:21.945 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:04:26.953 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:04:26.955 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:04:31.956 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:04:31.959 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:04:36.966 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:04:36.968 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:04:41.976 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:04:41.978 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:04:46.983 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:04:46.985 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:04:51.985 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:04:51.987 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:04:56.984 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:04:56.987 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:05:01.988 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:05:01.992 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:05:06.995 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:05:06.998 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:05:12.008 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:05:12.011 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:05:17.009 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:05:17.011 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:05:22.024 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:05:22.027 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:05:27.037 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:05:27.040 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:05:32.050 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:05:32.053 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:05:37.054 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:05:37.057 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:05:42.063 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:05:42.066 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:05:47.076 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:05:47.079 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:05:52.084 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:05:52.087 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:05:57.090 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:05:57.092 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:06:02.098 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:06:02.100 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:06:07.100 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:06:07.102 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:06:12.112 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:06:12.115 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:06:17.125 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:06:17.126 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:06:22.131 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:06:22.133 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -Persisting failed: Another write batch or compaction is already active (Only a single write operations is allowed at a time) -[2026-08-27 12:06:27.137 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:06:27.139 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:06:32.149 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:06:32.151 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:06:37.157 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:06:37.158 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:06:42.159 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:06:42.162 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:06:47.171 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:06:47.173 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:06:52.171 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:06:52.172 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:06:57.179 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:06:57.181 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:07:02.186 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:07:02.188 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -Persisting failed: Another write batch or compaction is already active (Only a single write operations is allowed at a time) -[2026-08-27 12:07:07.197 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:07:07.199 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:07:12.207 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:07:12.210 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -Persisting failed: Another write batch or compaction is already active (Only a single write operations is allowed at a time) -[2026-08-27 12:07:17.212 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:07:17.215 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:07:22.221 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:07:22.223 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:07:27.227 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:07:27.228 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:07:32.232 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:07:32.234 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:07:37.240 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:07:37.242 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:07:42.254 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:07:42.256 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:07:47.254 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:07:47.256 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:07:52.269 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:07:52.271 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:07:57.277 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:07:57.279 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:08:02.278 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:08:02.279 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:08:07.283 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:08:07.285 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:08:12.297 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:08:12.299 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:08:17.302 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:08:17.304 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:08:22.314 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:08:22.317 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:08:27.319 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:08:27.321 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:08:32.334 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:08:32.336 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:08:37.342 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:08:37.344 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:08:42.347 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:08:42.349 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:08:47.362 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:08:47.363 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:08:52.375 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:08:52.377 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:08:57.389 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:08:57.391 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:09:02.399 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:09:02.401 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:09:07.412 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:09:07.414 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:09:12.424 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:09:12.426 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:09:17.433 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:09:17.434 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:09:22.436 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:09:22.438 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:09:27.447 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:09:27.450 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:09:32.452 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:09:32.454 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:09:37.465 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:09:37.467 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:09:42.469 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:09:42.471 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:09:47.472 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:09:47.474 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:09:52.471 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:09:52.473 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:09:57.478 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:09:57.481 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:10:02.493 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:10:02.495 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:10:07.498 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:10:07.500 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:10:12.505 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:10:12.507 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:10:17.519 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:10:17.521 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:10:22.522 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:10:22.523 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:10:27.531 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:10:27.533 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:10:32.544 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:10:32.546 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:10:37.544 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:10:37.546 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:10:42.547 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:10:42.549 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:10:47.563 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:10:47.565 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:10:52.572 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:10:52.573 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -Persisting failed: Another write batch or compaction is already active (Only a single write operations is allowed at a time) -[2026-08-27 12:10:57.581 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:10:57.584 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:11:02.593 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:11:02.595 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:11:07.607 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:11:07.609 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:11:12.621 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:11:12.622 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:11:17.625 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:11:17.626 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:11:22.629 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:11:22.631 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:11:27.643 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:11:27.645 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:11:32.654 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:11:32.657 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:11:37.663 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:11:37.665 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:11:42.669 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:11:42.670 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:11:47.671 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:11:47.673 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:11:52.674 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:11:52.676 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:11:57.681 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:11:57.683 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:12:02.684 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:12:02.686 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:12:07.689 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:12:07.691 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:12:12.698 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:12:12.701 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:12:17.702 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:12:17.704 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:12:22.716 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:12:22.718 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:12:27.725 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:12:27.727 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:12:32.727 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:12:32.729 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:12:37.733 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:12:37.735 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:12:42.740 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:12:42.742 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:12:47.748 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:12:47.750 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:12:52.758 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:12:52.760 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:12:57.762 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:12:57.764 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:13:02.768 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:13:02.769 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:13:07.778 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:13:07.780 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:13:12.779 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:13:12.782 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:13:17.793 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:13:17.795 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:13:22.803 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:13:22.806 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:13:27.809 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:13:27.811 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:13:32.809 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:13:32.811 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:13:37.819 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:13:37.822 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:13:42.832 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:13:42.833 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:13:47.847 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:13:47.849 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:13:52.847 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:13:52.848 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:13:57.853 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:13:57.855 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:14:02.866 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:14:02.868 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:14:07.877 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:14:07.879 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:14:12.886 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:14:12.888 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:14:17.894 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:14:17.895 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:14:22.897 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:14:22.899 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:14:27.896 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:14:27.898 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:14:32.911 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:14:32.913 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:14:37.915 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:14:37.917 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:14:42.926 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:14:42.928 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:14:47.932 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:14:47.934 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:14:52.938 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:14:52.939 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:14:57.946 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:14:57.950 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:15:02.954 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:15:02.956 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:15:07.966 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:15:07.968 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:15:12.973 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:15:12.975 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:15:17.987 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:15:17.989 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:15:22.991 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:15:22.993 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:15:27.996 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:15:27.998 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:15:33.004 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:15:33.007 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:15:38.017 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:15:38.019 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:15:43.017 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:15:43.019 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:15:48.027 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:15:48.029 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:15:53.038 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:15:53.041 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:15:58.051 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:15:58.053 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:16:03.059 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:16:03.061 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:16:08.058 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:16:08.062 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:16:13.065 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:16:13.066 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:16:18.077 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:16:18.079 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:16:23.084 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:16:23.086 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:16:28.091 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:16:28.092 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:16:33.102 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:16:33.104 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:16:38.111 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:16:38.113 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:16:43.116 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:16:43.118 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:16:48.127 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:16:48.128 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:16:53.134 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:16:53.136 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:16:58.141 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:16:58.143 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:17:03.152 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:17:03.154 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:17:08.154 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:17:08.155 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:17:13.167 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:17:13.170 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:17:18.171 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:17:18.173 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:17:23.176 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:17:23.178 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:17:28.182 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:17:28.183 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:17:33.193 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:17:33.195 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:17:38.202 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:17:38.204 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:17:43.208 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:17:43.210 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:17:48.217 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:17:48.220 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:17:53.229 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:17:53.231 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:17:58.244 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:17:58.246 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:18:03.246 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:18:03.247 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:18:08.256 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:18:08.257 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:18:13.257 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:18:13.259 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:18:18.260 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:18:18.262 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:18:23.273 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:18:23.275 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:18:28.277 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:18:28.279 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:18:33.281 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:18:33.282 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:18:38.285 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:18:38.286 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:18:43.290 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:18:43.292 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:18:48.297 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:18:48.298 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:18:53.303 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:18:53.305 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:18:58.303 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:18:58.305 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:19:03.312 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:19:03.314 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:19:08.315 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:19:08.317 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:19:13.326 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:19:13.327 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:19:18.331 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:19:18.333 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:19:23.331 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:19:23.333 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:19:28.335 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:19:28.337 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:19:33.345 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:19:33.347 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:19:38.354 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:19:38.356 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:19:43.366 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:19:43.368 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:19:48.374 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:19:48.375 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:19:53.378 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:19:53.380 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:19:58.392 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:19:58.394 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:20:03.398 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:20:03.401 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:20:08.411 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:20:08.414 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:20:13.423 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:20:13.425 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:20:18.427 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:20:18.430 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:20:23.430 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:20:23.432 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:20:28.434 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:20:28.436 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:20:33.436 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:20:33.438 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:20:38.448 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:20:38.451 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:20:43.456 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:20:43.458 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:20:48.469 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:20:48.470 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:20:53.478 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:20:53.480 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:20:58.493 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:20:58.495 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:21:03.505 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:21:03.508 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:21:08.506 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:21:08.507 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:21:13.514 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:21:13.515 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:21:18.525 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:21:18.527 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:21:23.529 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:21:23.531 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:21:28.530 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:21:28.532 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:21:33.535 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:21:33.537 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:21:38.538 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:21:38.540 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:21:43.542 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:21:43.544 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:21:48.556 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:21:48.558 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:21:53.560 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:21:53.561 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:21:58.564 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:21:58.566 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:22:03.575 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:22:03.577 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:22:08.579 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:22:08.581 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:22:13.591 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:22:13.593 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:22:18.602 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:22:18.604 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:22:23.616 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:22:23.619 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:22:28.625 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:22:28.627 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:22:33.636 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:22:33.638 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:22:38.652 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:22:38.655 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:22:43.653 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:22:43.655 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:22:48.660 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:22:48.661 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:22:53.675 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:22:53.677 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:22:58.679 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:22:58.681 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:23:03.686 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:23:03.688 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:23:08.692 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:23:08.693 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:23:13.700 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:23:13.702 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:23:18.713 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:23:18.715 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:23:23.722 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:23:23.725 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:23:28.733 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:23:28.736 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:23:33.741 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:23:33.743 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:23:38.746 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:23:38.747 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:23:43.758 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:23:43.760 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:23:48.766 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:23:48.768 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:23:53.773 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:23:53.775 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:23:58.776 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:23:58.778 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:24:03.790 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:24:03.792 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:24:08.801 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:24:08.804 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:24:13.804 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:24:13.807 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:24:18.818 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:24:18.820 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:24:23.827 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:24:23.828 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:24:28.837 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:24:28.839 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:24:33.840 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:24:33.842 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:24:38.844 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:24:38.846 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:24:43.854 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:24:43.856 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:24:48.864 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:24:48.866 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:24:53.878 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:24:53.879 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:24:58.879 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:24:58.881 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:25:03.890 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:25:03.892 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:25:08.902 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:25:08.903 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:25:13.905 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:25:13.907 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:25:18.908 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:25:18.910 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:25:23.912 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:25:23.914 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:25:28.916 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:25:28.918 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:25:33.921 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:25:33.924 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:25:38.932 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:25:38.934 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:25:43.947 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:25:43.949 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:25:48.949 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:25:48.952 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:25:53.962 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:25:53.964 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:25:58.967 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:25:58.969 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:26:03.976 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:26:03.978 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:26:08.981 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:26:08.982 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:26:13.982 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:26:13.984 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:26:18.994 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:26:18.996 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:26:24.003 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:26:24.006 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:26:29.007 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:26:29.008 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:26:34.020 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:26:34.022 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:26:39.027 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:26:39.028 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:26:44.031 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:26:44.033 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:26:49.039 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:26:49.041 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:26:54.051 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:26:54.053 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:26:59.051 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:26:59.054 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:27:04.064 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:27:04.066 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:27:09.067 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:27:09.069 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:27:14.069 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:27:14.071 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:27:19.081 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:27:19.082 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:27:24.084 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:27:24.086 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:27:29.097 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:27:29.098 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:27:34.107 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:27:34.108 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:27:39.121 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:27:39.123 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:27:44.125 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:27:44.127 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:27:49.130 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:27:49.132 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:27:54.136 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:27:54.138 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:27:59.149 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:27:59.150 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:28:04.155 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:28:04.157 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:28:09.162 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:28:09.165 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:28:14.175 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:28:14.177 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:28:19.185 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:28:19.187 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:28:24.186 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:28:24.187 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:28:29.192 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:28:29.195 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:28:34.202 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:28:34.204 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:28:39.205 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:28:39.207 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:28:44.205 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:28:44.206 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:28:49.214 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:28:49.216 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:28:54.223 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:28:54.225 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:28:59.237 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:28:59.239 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:29:04.250 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:29:04.252 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:29:09.251 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:29:09.253 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:29:14.258 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:29:14.261 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:29:19.260 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:29:19.261 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:29:24.268 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:29:24.270 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:29:29.277 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:29:29.279 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:29:34.288 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:29:34.290 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:29:39.297 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:29:39.299 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:29:44.311 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:29:44.313 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:29:49.323 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:29:49.326 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:29:54.333 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:29:54.336 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:29:59.346 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:29:59.348 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:30:04.350 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:30:04.351 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:30:09.352 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:30:09.354 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:30:14.361 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:30:14.363 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:30:19.366 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:30:19.367 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:30:24.380 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:30:24.383 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:30:29.381 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:30:29.383 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:30:34.392 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:30:34.394 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:30:39.435 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:30:39.661 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -Persisting failed: Another write batch or compaction is already active (Only a single write operations is allowed at a time) -[2026-08-27 12:30:44.449 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:30:44.451 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:30:49.459 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:30:49.461 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:30:54.467 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:30:54.470 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:30:59.476 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:30:59.478 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:31:04.481 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:31:04.483 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:31:09.485 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:31:09.488 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:31:14.494 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:31:14.496 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:31:19.500 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:31:19.503 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:31:24.509 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:31:24.511 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:31:29.525 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:31:29.526 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:31:34.535 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:31:34.536 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:31:39.537 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:31:39.539 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:31:44.545 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:31:44.547 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:31:49.552 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:31:49.555 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:31:54.562 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:31:54.564 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:31:59.577 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:31:59.579 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:32:04.584 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:32:04.587 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:32:09.587 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:32:09.589 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:32:14.598 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:32:14.600 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:32:19.601 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:32:19.603 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:32:24.602 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:32:24.604 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:32:29.607 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:32:29.609 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:32:34.609 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:32:34.611 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:32:39.616 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:32:39.618 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:32:44.617 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:32:44.619 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:32:49.626 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:32:49.628 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:32:54.641 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:32:54.643 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:32:59.645 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:32:59.647 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:33:04.646 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:33:04.647 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:33:09.655 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:33:09.657 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:33:14.658 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:33:14.660 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:33:19.668 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:33:19.670 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:33:24.674 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:33:24.675 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:33:29.680 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:33:29.683 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:33:34.684 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:33:34.686 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:33:39.698 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:33:39.700 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:33:44.711 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:33:44.713 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:33:49.723 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:33:49.725 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:33:54.726 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:33:54.728 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:33:59.740 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:33:59.742 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:34:04.754 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:34:04.756 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:34:09.756 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:34:09.757 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:34:14.757 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:34:14.759 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:34:19.772 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:34:19.774 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:34:24.776 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:34:24.777 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:34:29.790 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:34:29.792 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:34:34.804 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:34:34.806 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:34:39.807 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:34:39.809 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:34:44.817 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:34:44.819 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:34:49.824 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:34:49.826 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:34:54.827 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:34:54.829 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:34:59.840 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:34:59.842 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:35:04.851 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:35:04.853 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:35:09.854 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:35:09.856 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:35:14.856 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:35:14.859 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:35:19.868 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:35:19.870 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:35:24.873 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:35:24.874 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:35:29.887 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:35:29.888 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:35:34.901 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:35:34.903 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:35:39.907 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:35:39.909 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:35:44.915 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:35:44.917 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:35:49.926 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:35:49.928 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:35:54.932 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:35:54.934 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:35:59.947 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:35:59.949 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:36:04.962 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:36:04.964 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:36:09.977 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:36:09.979 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:36:14.984 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:36:14.986 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:36:19.988 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:36:19.990 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:36:25.001 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:36:25.004 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:36:30.003 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:36:30.005 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:36:35.016 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:36:35.018 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:36:40.022 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:36:40.024 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:36:45.032 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:36:45.034 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:36:50.047 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:36:50.049 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:36:55.050 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:36:55.053 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:37:00.056 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:37:00.057 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:37:05.059 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:37:05.061 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:37:10.059 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:37:10.061 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:37:15.062 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:37:15.064 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:37:20.066 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:37:20.067 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:37:25.075 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:37:25.076 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:37:30.075 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:37:30.077 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:37:35.089 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:37:35.090 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:37:40.101 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:37:40.103 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:37:45.112 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:37:45.114 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:37:50.114 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:37:50.116 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:37:55.122 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:37:55.123 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:38:00.136 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:38:00.137 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:38:05.147 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:38:05.150 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:38:10.154 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:38:10.156 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:38:15.159 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:38:15.161 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:38:20.162 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:38:20.164 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:38:25.171 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:38:25.173 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:38:30.174 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:38:30.176 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:38:35.185 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:38:35.186 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:38:40.192 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:38:40.193 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:38:45.192 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:38:45.194 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:38:50.195 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:38:50.197 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:38:55.198 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:38:55.200 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:39:00.200 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:39:00.201 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:39:05.204 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:39:05.206 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:39:10.209 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:39:10.210 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:39:15.218 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:39:15.222 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:39:20.221 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:39:20.222 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:39:25.223 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:39:25.225 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:39:30.224 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:39:30.225 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:39:35.232 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:39:35.233 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:39:40.237 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:39:40.239 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:39:45.245 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:39:45.247 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:39:50.250 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:39:50.252 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:39:55.257 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:39:55.259 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:40:00.258 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:40:00.260 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:40:05.267 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:40:05.269 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:40:10.276 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:40:10.278 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:40:15.285 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:40:15.287 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:40:20.293 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:40:20.294 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:40:25.301 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:40:25.304 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:40:30.309 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:40:30.311 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:40:35.315 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:40:35.317 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:40:40.324 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:40:40.326 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:40:45.325 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:40:45.327 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:40:50.327 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:40:50.329 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:40:55.337 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:40:55.339 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:41:00.337 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:41:00.339 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:41:05.346 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:41:05.348 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:41:10.362 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:41:10.363 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:41:15.370 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:41:15.372 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:41:20.385 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:41:20.387 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:41:25.399 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:41:25.401 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:41:30.407 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:41:30.410 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:41:35.410 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:41:35.411 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:41:40.424 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:41:40.425 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:41:45.435 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:41:45.437 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:41:50.437 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:41:50.439 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:41:55.444 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:41:55.446 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:42:00.451 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:42:00.452 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:42:05.453 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:42:05.455 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:42:10.454 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:42:10.456 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:42:15.459 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:42:15.461 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:42:20.471 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:42:20.473 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:42:25.482 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:42:25.483 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:42:30.485 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:42:30.487 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:42:35.493 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:42:35.495 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:42:40.507 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:42:40.509 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:42:45.508 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:42:45.510 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:42:50.522 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:42:50.524 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:42:55.530 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:42:55.531 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:43:00.534 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:43:00.536 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:43:05.538 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:43:05.540 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:43:10.553 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:43:10.555 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:43:15.563 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:43:15.565 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:43:20.569 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:43:20.571 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:43:25.568 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:43:25.570 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:43:30.575 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:43:30.577 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:43:35.582 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:43:35.584 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:43:40.597 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:43:40.599 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:43:45.600 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:43:45.602 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:43:50.614 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:43:50.616 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:43:55.615 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:43:55.618 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:44:00.616 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:44:00.619 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:44:05.622 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:44:05.624 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:44:10.629 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:44:10.630 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:44:15.640 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:44:15.641 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:44:20.640 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:44:20.641 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:44:25.652 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:44:25.655 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:44:30.654 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:44:30.656 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:44:35.668 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:44:35.669 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:44:40.673 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:44:40.674 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:44:45.675 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:44:45.676 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:44:50.678 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:44:50.680 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:44:55.684 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:44:55.686 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:45:00.687 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:45:00.689 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:45:05.702 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:45:05.704 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:45:10.703 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:45:10.705 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:45:15.711 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:45:15.713 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:45:20.712 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:45:20.713 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:45:25.718 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:45:25.719 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:45:30.726 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:45:30.728 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:45:35.731 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:45:35.733 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:45:40.743 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:45:40.744 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:45:45.751 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:45:45.752 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:45:50.764 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:45:50.766 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:45:55.767 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:45:55.769 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:46:00.777 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:46:00.779 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:46:05.781 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:46:05.784 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:46:10.788 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:46:10.790 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:46:15.789 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:46:15.791 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:46:20.800 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:46:20.801 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:46:25.815 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:46:25.817 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:46:30.831 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:46:30.832 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:46:35.833 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:46:35.835 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:46:40.847 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:46:40.849 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:46:45.861 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:46:45.862 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:46:50.865 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:46:50.867 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:46:55.870 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:46:55.872 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:47:00.872 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:47:00.874 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:47:05.879 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:47:05.881 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:47:10.884 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:47:10.886 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:47:15.886 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:47:15.888 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:47:20.892 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:47:20.894 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:47:25.904 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:47:25.906 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:47:30.914 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:47:30.916 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:47:35.916 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:47:35.917 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:47:40.930 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:47:40.932 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:47:45.935 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:47:45.938 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:47:50.936 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:47:50.937 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:47:55.937 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:47:55.939 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:48:00.939 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:48:00.940 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:48:05.946 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:48:05.948 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:48:10.950 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:48:10.952 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:48:15.964 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:48:15.965 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:48:20.975 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:48:20.977 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:48:25.981 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:48:25.983 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:48:30.985 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:48:30.987 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:48:35.995 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:48:35.997 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:48:40.999 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:48:41.000 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:48:46.000 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:48:46.002 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:48:51.006 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:48:51.008 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:48:56.015 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:48:56.017 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:49:01.016 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:49:01.018 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:49:06.017 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:49:06.019 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:49:11.022 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:49:11.024 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:49:16.029 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:49:16.030 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:49:21.042 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:49:21.043 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:49:26.045 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:49:26.047 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:49:31.050 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:49:31.052 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:49:36.055 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:49:36.056 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:49:41.070 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:49:41.072 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:49:46.071 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:49:46.073 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:49:51.072 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:49:51.074 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:49:56.084 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:49:56.086 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:50:01.094 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:50:01.096 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:50:06.095 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:50:06.098 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:50:11.104 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:50:11.106 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:50:16.107 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:50:16.109 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:50:21.114 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:50:21.115 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:50:26.116 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:50:26.117 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:50:31.124 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:50:31.126 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:50:36.125 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:50:36.127 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:50:41.138 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:50:41.140 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:50:46.149 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:50:46.150 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:50:51.159 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:50:51.161 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:50:56.168 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:50:56.170 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:51:01.168 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:51:01.170 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:51:06.169 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:51:06.170 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:51:11.176 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:51:11.178 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:51:16.191 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:51:16.192 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:51:21.193 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:51:21.195 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:51:26.205 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:51:26.207 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:51:31.219 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:51:31.221 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:51:36.225 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:51:36.227 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:51:41.236 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:51:41.237 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:51:46.243 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:51:46.245 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:51:51.254 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:51:51.256 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:51:56.255 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:51:56.257 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:52:01.271 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:52:01.272 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:52:06.278 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:52:06.280 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:52:11.292 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:52:11.294 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:52:16.294 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:52:16.296 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:52:21.307 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:52:21.310 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:52:26.309 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:52:26.311 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:52:31.310 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:52:31.311 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:52:36.313 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:52:36.314 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:52:41.325 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:52:41.327 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:52:46.334 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:52:46.336 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:52:51.341 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:52:51.343 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:52:56.342 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:52:56.344 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:53:01.357 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:53:01.359 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:53:06.361 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:53:06.364 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:53:11.370 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:53:11.372 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:53:16.381 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:53:16.383 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:53:21.383 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:53:21.385 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:53:26.392 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:53:26.394 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:53:31.397 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:53:31.399 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:53:36.399 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:53:36.401 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:53:41.407 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:53:41.409 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:53:46.414 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:53:46.415 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:53:51.423 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:53:51.425 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:53:56.437 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:53:56.439 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:54:01.442 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:54:01.443 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:54:06.454 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:54:06.457 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:54:11.458 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:54:11.460 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:54:16.464 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:54:16.466 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:54:21.474 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:54:21.476 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:54:26.473 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:54:26.475 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:54:31.479 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:54:31.481 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:54:36.487 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:54:36.490 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:54:41.491 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:54:41.492 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:54:46.498 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:54:46.500 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:54:51.498 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:54:51.500 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:54:56.508 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:54:56.510 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:55:01.510 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:55:01.512 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:55:06.525 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:55:06.527 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:55:11.528 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:55:11.530 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:55:16.540 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:55:16.542 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:55:21.551 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:55:21.553 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:55:26.566 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:55:26.568 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:55:31.575 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:55:31.576 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:55:36.577 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:55:36.579 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:55:41.592 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:55:41.594 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:55:46.602 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:55:46.604 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:55:51.601 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:55:51.603 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:55:56.614 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:55:56.615 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:56:01.619 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:56:01.620 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:56:06.625 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:56:06.626 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:56:11.626 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:56:11.628 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:56:16.639 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:56:16.641 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:56:21.652 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:56:21.653 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:56:26.652 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:56:26.654 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:56:31.656 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:56:31.657 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:56:36.672 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:56:36.673 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:56:41.679 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:56:41.681 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:56:46.681 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:56:46.683 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:56:51.695 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:56:51.696 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:56:56.701 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:56:56.703 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:57:01.706 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:57:01.708 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:57:06.707 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:57:06.710 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:57:11.722 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:57:11.723 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:57:16.724 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:57:16.726 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:57:21.733 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:57:21.736 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:57:26.740 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:57:26.742 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:57:31.746 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:57:31.748 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:57:36.747 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:57:36.749 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:57:41.748 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:57:41.750 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:57:46.761 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:57:46.763 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:57:51.772 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:57:51.773 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:57:56.771 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:57:56.773 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:58:01.784 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:58:01.786 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:58:06.789 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:58:06.792 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:58:11.795 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:58:11.796 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:58:16.800 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:58:16.802 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:58:21.816 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:58:21.818 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:58:26.825 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:58:26.827 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:58:31.835 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:58:31.836 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:58:36.841 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:58:36.843 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:58:41.842 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:58:41.845 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:58:46.844 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:58:46.846 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:58:51.847 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:58:51.849 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:58:56.862 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:58:56.863 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -Persisting failed: Another write batch or compaction is already active (Only a single write operations is allowed at a time) -[2026-08-27 12:59:01.872 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:59:01.873 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:59:06.877 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:59:06.880 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:59:11.882 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:59:11.883 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:59:16.897 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:59:16.899 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:59:21.904 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:59:21.906 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:59:26.909 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:59:26.911 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:59:31.910 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:59:31.912 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:59:36.923 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:59:36.925 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:59:41.927 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:59:41.929 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:59:46.940 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:59:46.943 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:59:51.943 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:59:51.946 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 12:59:56.954 +0800] DEBUG: Event registry already initialized -[2026-08-27 12:59:56.955 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:00:01.958 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:00:01.960 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:00:06.963 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:00:06.965 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:00:11.965 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:00:11.967 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:00:16.969 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:00:16.971 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:00:21.978 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:00:21.981 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:00:26.983 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:00:26.985 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:00:31.985 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:00:31.987 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:00:36.992 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:00:36.994 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:00:41.996 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:00:41.997 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:00:47.001 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:00:47.003 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:00:52.007 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:00:52.010 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:00:57.014 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:00:57.016 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:01:02.024 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:01:02.026 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:01:07.026 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:01:07.028 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:01:12.027 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:01:12.029 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:01:17.031 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:01:17.033 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:01:22.037 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:01:22.039 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:01:27.049 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:01:27.051 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:01:32.053 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:01:32.056 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:01:37.062 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:01:37.064 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:01:42.077 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:01:42.080 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:01:47.092 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:01:47.094 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:01:52.098 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:01:52.099 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:01:57.111 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:01:57.113 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:02:02.124 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:02:02.126 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:02:07.124 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:02:07.127 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:02:12.131 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:02:12.133 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:02:17.136 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:02:17.138 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:02:22.139 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:02:22.141 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:02:27.148 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:02:27.150 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:02:32.163 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:02:32.165 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:02:37.178 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:02:37.180 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:02:42.183 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:02:42.185 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:02:47.190 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:02:47.192 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:02:52.191 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:02:52.193 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:02:57.194 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:02:57.196 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:03:02.199 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:03:02.201 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:03:07.211 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:03:07.213 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:03:12.224 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:03:12.226 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:03:17.239 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:03:17.241 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:03:22.243 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:03:22.245 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:03:27.254 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:03:27.255 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:03:32.259 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:03:32.261 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:03:37.263 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:03:37.264 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:03:42.269 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:03:42.271 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:03:47.279 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:03:47.281 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:03:52.280 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:03:52.283 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:03:57.283 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:03:57.284 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:04:02.291 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:04:02.293 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:04:07.302 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:04:07.303 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:04:12.304 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:04:12.305 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:04:17.318 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:04:17.320 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:04:22.328 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:04:22.331 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:04:27.336 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:04:27.337 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:04:32.346 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:04:32.348 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:04:37.352 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:04:37.354 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:04:42.366 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:04:42.368 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:04:47.372 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:04:47.373 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:04:52.374 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:04:52.376 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:04:57.380 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:04:57.383 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:05:02.394 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:05:02.395 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:05:07.397 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:05:07.400 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:05:12.411 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:05:12.413 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:05:17.424 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:05:17.426 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:05:22.434 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:05:22.436 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:05:27.438 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:05:27.440 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:05:32.450 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:05:32.453 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:05:37.454 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:05:37.455 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:05:42.454 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:05:42.456 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:05:47.456 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:05:47.458 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:05:52.460 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:05:52.461 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:05:57.460 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:05:57.462 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:06:02.461 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:06:02.462 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:06:07.472 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:06:07.475 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:06:12.487 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:06:12.489 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:06:17.499 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:06:17.500 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:06:22.508 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:06:22.510 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:06:27.518 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:06:27.520 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:06:32.521 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:06:32.522 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:06:37.536 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:06:37.538 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:06:42.542 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:06:42.544 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:06:47.544 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:06:47.546 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:06:52.552 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:06:52.554 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:06:57.562 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:06:57.564 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:07:02.569 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:07:02.571 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:07:07.578 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:07:07.580 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:07:12.582 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:07:12.585 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:07:17.592 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:07:17.594 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:07:22.602 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:07:22.604 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:07:27.608 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:07:27.610 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:07:32.610 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:07:32.613 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:07:37.622 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:07:37.624 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:07:42.633 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:07:42.634 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:07:47.647 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:07:47.650 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:07:52.650 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:07:52.652 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:07:57.659 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:07:57.661 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:08:02.667 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:08:02.669 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:08:07.681 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:08:07.684 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:08:12.687 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:08:12.689 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:08:17.692 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:08:17.695 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:08:22.696 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:08:22.698 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:08:27.702 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:08:27.703 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:08:32.715 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:08:32.716 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:08:37.730 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:08:37.732 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:08:42.746 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:08:42.747 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:08:47.755 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:08:47.757 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:08:52.763 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:08:52.765 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:08:57.768 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:08:57.771 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:09:02.769 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:09:02.771 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:09:07.780 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:09:07.782 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:09:12.787 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:09:12.789 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:09:17.798 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:09:17.800 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:09:22.808 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:09:22.810 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:09:27.809 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:09:27.811 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:09:32.818 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:09:32.821 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:09:37.822 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:09:37.824 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:09:42.833 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:09:42.835 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:09:47.849 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:09:47.851 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:09:52.858 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:09:52.859 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:09:57.866 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:09:57.869 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:10:02.873 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:10:02.874 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:10:07.887 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:10:07.890 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:10:12.901 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:10:12.903 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:10:17.910 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:10:17.912 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:10:22.921 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:10:22.922 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:10:27.923 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:10:27.925 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:10:32.927 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:10:32.929 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:10:37.931 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:10:37.934 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:10:42.933 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:10:42.935 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:10:47.946 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:10:47.948 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:10:52.959 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:10:52.961 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:10:57.963 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:10:57.964 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:11:02.963 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:11:02.965 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:11:07.970 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:11:07.971 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:11:12.978 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:11:12.980 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:11:17.978 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:11:17.980 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:11:22.979 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:11:22.981 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:11:27.994 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:11:27.996 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:11:33.002 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:11:33.004 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:11:38.014 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:11:38.016 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:11:43.019 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:11:43.021 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:11:48.019 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:11:48.022 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:11:53.029 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:11:53.031 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:11:58.043 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:11:58.045 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:12:03.045 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:12:03.047 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:12:08.056 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:12:08.058 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:12:13.066 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:12:13.068 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:12:18.077 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:12:18.079 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:12:23.079 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:12:23.080 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:12:28.084 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:12:28.086 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:12:33.085 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:12:33.086 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:12:38.097 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:12:38.099 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:12:43.111 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:12:43.113 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:12:48.118 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:12:48.120 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:12:53.122 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:12:53.124 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:12:58.128 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:12:58.130 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:13:03.129 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:13:03.130 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:13:08.131 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:13:08.133 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:13:13.133 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:13:13.134 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:13:18.144 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:13:18.145 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:13:23.157 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:13:23.159 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:13:28.170 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:13:28.172 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:13:33.175 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:13:33.177 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:13:38.190 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:13:38.191 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:13:43.198 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:13:43.200 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:13:48.206 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:13:48.208 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:13:53.217 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:13:53.219 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:13:58.231 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:13:58.233 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:14:03.235 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:14:03.237 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:14:08.243 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:14:08.245 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:14:13.249 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:14:13.251 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:14:18.251 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:14:18.253 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:14:23.254 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:14:23.256 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:14:28.260 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:14:28.262 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:14:33.270 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:14:33.271 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:14:38.279 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:14:38.281 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:14:43.288 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:14:43.290 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:14:48.297 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:14:48.299 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:14:53.310 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:14:53.312 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:14:58.324 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:14:58.326 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -Persisting failed: Another write batch or compaction is already active (Only a single write operations is allowed at a time) -[2026-08-27 13:15:03.338 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:15:03.340 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:15:08.347 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:15:08.349 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:15:13.358 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:15:13.360 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:15:18.362 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:15:18.364 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:15:23.366 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:15:23.368 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:15:28.367 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:15:28.369 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:15:33.371 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:15:33.372 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:15:38.374 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:15:38.376 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:15:43.384 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:15:43.386 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:15:48.386 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:15:48.387 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:15:53.400 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:15:53.401 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:15:58.403 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:15:58.405 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:16:03.418 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:16:03.420 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:16:08.428 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:16:08.430 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:16:13.432 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:16:13.434 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:16:18.439 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:16:18.441 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:16:23.454 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:16:23.456 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:16:28.460 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:16:28.461 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:16:33.468 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:16:33.469 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:16:38.475 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:16:38.477 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:16:43.482 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:16:43.484 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:16:48.493 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:16:48.495 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:16:53.509 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:16:53.511 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:16:58.510 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:16:58.511 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:17:03.512 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:17:03.513 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:17:08.520 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:17:08.523 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:17:13.531 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:17:13.533 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:17:18.535 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:17:18.537 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:17:23.536 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:17:23.538 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:17:28.543 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:17:28.545 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:17:33.548 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:17:33.550 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:17:38.549 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:17:38.551 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:17:43.561 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:17:43.563 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:17:48.570 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:17:48.573 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:17:53.573 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:17:53.575 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:17:58.581 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:17:58.583 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:18:03.584 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:18:03.585 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:18:08.588 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:18:08.590 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:18:13.594 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:18:13.596 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:18:18.604 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:18:18.605 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:18:23.614 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:18:23.616 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:18:28.623 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:18:28.626 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:18:33.625 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:18:33.626 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:18:38.639 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:18:38.641 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:18:43.651 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:18:43.653 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:18:48.652 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:18:48.654 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:18:53.663 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:18:53.666 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:18:58.671 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:18:58.673 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:19:03.675 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:19:03.677 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:19:08.683 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:19:08.685 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:19:13.686 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:19:13.688 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:19:18.700 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:19:18.702 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:19:23.712 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:19:23.714 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:19:28.720 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:19:28.722 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:19:33.728 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:19:33.730 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:19:38.738 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:19:38.740 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:19:43.748 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:19:43.749 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:19:48.763 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:19:48.765 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:19:53.767 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:19:53.768 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:19:58.773 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:19:58.775 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:20:03.782 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:20:03.784 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:20:08.788 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:20:08.791 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:20:13.797 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:20:13.799 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:20:18.805 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:20:18.806 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:20:23.818 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:20:23.821 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:20:28.834 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:20:28.836 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:20:33.838 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:20:33.840 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:20:38.841 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:20:38.842 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -Persisting failed: Another write batch or compaction is already active (Only a single write operations is allowed at a time) -[2026-08-27 13:20:43.841 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:20:43.842 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:20:48.850 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:20:48.852 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:20:53.860 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:20:53.861 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:20:58.874 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:20:58.876 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:21:03.875 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:21:03.877 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:21:08.888 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:21:08.889 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:21:13.895 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:21:13.897 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:21:18.900 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:21:18.903 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:21:23.912 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:21:23.913 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:21:28.926 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:21:28.928 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:21:33.937 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:21:33.939 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:21:38.949 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:21:38.951 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:21:43.954 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:21:43.955 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:21:48.958 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:21:48.961 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:21:53.965 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:21:53.967 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:21:58.966 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:21:58.968 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:22:03.972 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:22:03.973 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:22:08.973 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:22:08.974 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:22:13.980 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:22:13.981 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:22:18.985 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:22:18.987 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:22:23.997 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:22:23.999 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:22:29.007 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:22:29.010 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:22:34.023 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:22:34.024 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:22:39.026 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:22:39.028 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:22:44.033 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:22:44.036 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:22:49.044 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:22:49.045 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:22:54.044 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:22:54.045 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:22:59.047 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:22:59.049 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:23:04.057 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:23:04.059 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:23:09.059 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:23:09.060 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:23:14.071 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:23:14.073 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:23:19.074 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:23:19.076 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:23:24.088 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:23:24.090 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:23:29.091 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:23:29.092 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -Persisting failed: Another write batch or compaction is already active (Only a single write operations is allowed at a time) -[2026-08-27 13:23:34.094 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:23:34.096 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:23:39.101 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:23:39.102 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:23:44.104 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:23:44.105 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:23:49.104 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:23:49.106 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:23:54.111 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:23:54.114 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -Persisting failed: Another write batch or compaction is already active (Only a single write operations is allowed at a time) -[2026-08-27 13:23:59.112 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:23:59.114 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:24:04.119 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:24:04.120 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:24:09.133 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:24:09.135 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -Persisting failed: Another write batch or compaction is already active (Only a single write operations is allowed at a time) -[2026-08-27 13:24:14.147 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:24:14.149 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:24:19.154 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:24:19.156 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:24:24.165 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:24:24.167 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:24:29.179 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:24:29.181 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:24:34.184 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:24:34.186 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:24:39.186 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:24:39.188 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -Persisting failed: Another write batch or compaction is already active (Only a single write operations is allowed at a time) -[2026-08-27 13:24:44.196 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:24:44.198 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:24:49.208 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:24:49.210 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -Persisting failed: Another write batch or compaction is already active (Only a single write operations is allowed at a time) -[2026-08-27 13:24:54.221 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:24:54.224 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -Persisting failed: Another write batch or compaction is already active (Only a single write operations is allowed at a time) -Persisting failed: Another write batch or compaction is already active (Only a single write operations is allowed at a time) -[2026-08-27 13:24:59.232 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:24:59.241 +0800] DEBUG: OutboxPoller: claimed pending events - count: 4 - eventIds: [ - 9002176, - 9002177, - 9002178, - 9002179 - ] - mode: "memory" -[2026-08-27 13:24:59.242 +0800] DEBUG: No audit log mapping for event - eventType: "purchase.received" -[2026-08-27 13:24:59.242 +0800] DEBUG: OutboxPoller: event published (memory mode) - eventId: 9002176 - eventType: "purchase.received" -[2026-08-27 13:24:59.247 +0800] DEBUG: OutboxPoller: event marked as processed - eventId: 9002176 -[2026-08-27 13:24:59.247 +0800] DEBUG: No audit log mapping for event - eventType: "purchase.received" -[2026-08-27 13:24:59.247 +0800] DEBUG: OutboxPoller: event published (memory mode) - eventId: 9002177 - eventType: "purchase.received" -[2026-08-27 13:24:59.254 +0800] DEBUG: OutboxPoller: event marked as processed - eventId: 9002177 -[2026-08-27 13:24:59.254 +0800] DEBUG: No audit log mapping for event - eventType: "purchase.received" -[2026-08-27 13:24:59.254 +0800] DEBUG: OutboxPoller: event published (memory mode) - eventId: 9002178 - eventType: "purchase.received" -[2026-08-27 13:24:59.261 +0800] DEBUG: OutboxPoller: event marked as processed - eventId: 9002178 -[2026-08-27 13:24:59.261 +0800] DEBUG: No audit log mapping for event - eventType: "purchase.received" -[2026-08-27 13:24:59.261 +0800] DEBUG: OutboxPoller: event published (memory mode) - eventId: 9002179 - eventType: "purchase.received" -[2026-08-27 13:24:59.267 +0800] DEBUG: OutboxPoller: event marked as processed - eventId: 9002179 -[2026-08-27 13:24:59.267 +0800] INFO: Outbox poll cycle - processed: 4 - failed: 0 - retried: 0 - mode: "memory" -[2026-08-27 13:25:04.242 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:25:04.243 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:25:09.245 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:25:09.247 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:25:14.255 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:25:14.257 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:25:19.255 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:25:19.256 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -Persisting failed: Another write batch or compaction is already active (Only a single write operations is allowed at a time) -[2026-08-27 13:25:24.269 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:25:24.271 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:25:29.278 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:25:29.280 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:25:34.286 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:25:34.303 +0800] DEBUG: OutboxPoller: claimed pending events - count: 4 - eventIds: [ - 9002180, - 9002181, - 9002182, - 9002183 - ] - mode: "memory" -[2026-08-27 13:25:34.303 +0800] DEBUG: No audit log mapping for event - eventType: "purchase.received" -[2026-08-27 13:25:34.303 +0800] DEBUG: OutboxPoller: event published (memory mode) - eventId: 9002180 - eventType: "purchase.received" -[2026-08-27 13:25:34.310 +0800] DEBUG: OutboxPoller: event marked as processed - eventId: 9002180 -[2026-08-27 13:25:34.310 +0800] DEBUG: No audit log mapping for event - eventType: "purchase.received" -[2026-08-27 13:25:34.310 +0800] DEBUG: OutboxPoller: event published (memory mode) - eventId: 9002181 - eventType: "purchase.received" -[2026-08-27 13:25:34.316 +0800] DEBUG: OutboxPoller: event marked as processed - eventId: 9002181 -[2026-08-27 13:25:34.316 +0800] DEBUG: No audit log mapping for event - eventType: "purchase.received" -[2026-08-27 13:25:34.316 +0800] DEBUG: OutboxPoller: event published (memory mode) - eventId: 9002182 - eventType: "purchase.received" -[2026-08-27 13:25:34.324 +0800] DEBUG: OutboxPoller: event marked as processed - eventId: 9002182 -[2026-08-27 13:25:34.325 +0800] DEBUG: No audit log mapping for event - eventType: "purchase.received" -[2026-08-27 13:25:34.325 +0800] DEBUG: OutboxPoller: event published (memory mode) - eventId: 9002183 - eventType: "purchase.received" -[2026-08-27 13:25:34.331 +0800] DEBUG: OutboxPoller: event marked as processed - eventId: 9002183 -[2026-08-27 13:25:34.331 +0800] INFO: Outbox poll cycle - processed: 4 - failed: 0 - retried: 0 - mode: "memory" -Persisting failed: Another write batch or compaction is already active (Only a single write operations is allowed at a time) -[2026-08-27 13:25:39.292 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:25:39.294 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:25:44.293 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:25:44.294 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:25:49.300 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:25:49.302 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:25:54.309 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:25:54.310 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -Persisting failed: Another write batch or compaction is already active (Only a single write operations is allowed at a time) -[2026-08-27 13:25:59.312 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:25:59.314 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:26:04.318 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:26:04.320 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:26:09.323 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:26:09.325 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:26:14.335 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:26:14.337 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:26:19.349 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:26:19.350 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:26:24.360 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:26:24.361 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:26:29.370 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:26:29.371 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:26:34.384 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:26:34.386 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:26:39.397 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:26:39.399 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:26:44.406 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:26:44.408 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:26:49.421 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:26:49.423 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:26:54.430 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:26:54.431 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:26:59.439 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:26:59.440 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:27:04.449 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:27:04.450 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:27:09.458 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:27:09.460 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:27:14.461 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:27:14.462 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:27:19.469 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:27:19.471 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:27:24.476 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:27:24.478 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:27:29.482 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:27:29.484 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:27:34.491 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:27:34.492 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:27:39.505 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:27:39.506 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:27:44.512 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:27:44.514 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:27:49.516 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:27:49.517 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:27:54.531 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:27:54.533 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:27:59.537 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:27:59.538 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:28:04.546 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:28:04.548 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -Persisting failed: Another write batch or compaction is already active (Only a single write operations is allowed at a time) -[2026-08-27 13:28:09.557 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:28:09.559 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -Persisting failed: Another write batch or compaction is already active (Only a single write operations is allowed at a time) -[2026-08-27 13:28:14.562 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:28:14.563 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:28:19.567 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:28:19.569 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:28:24.580 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:28:24.581 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:28:29.591 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:28:29.592 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:28:34.599 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:28:34.600 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:28:39.602 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:28:39.604 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:28:44.605 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:28:44.607 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:28:49.620 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:28:49.622 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -Persisting failed: Another write batch or compaction is already active (Only a single write operations is allowed at a time) -[2026-08-27 13:28:54.634 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:28:54.636 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:28:59.643 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:28:59.645 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:29:04.646 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:29:04.648 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:29:09.650 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:29:09.652 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:29:14.651 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:29:14.653 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:29:19.661 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:29:19.662 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:29:24.670 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:29:24.672 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -Persisting failed: Another write batch or compaction is already active (Only a single write operations is allowed at a time) -[2026-08-27 13:29:29.678 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:29:29.680 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:29:34.682 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:29:34.684 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:29:39.687 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:29:39.688 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:29:44.697 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:29:44.699 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:29:49.707 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:29:49.709 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:29:54.710 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:29:54.711 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:29:59.717 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:29:59.719 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:30:04.717 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:30:04.719 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:30:09.728 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:30:09.729 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:30:14.732 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:30:14.733 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:30:19.733 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:30:19.736 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:30:24.734 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:30:24.736 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:30:29.746 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:30:29.748 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:30:34.753 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:30:34.755 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:30:39.760 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:30:39.762 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:30:44.771 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:30:44.775 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:30:49.784 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:30:49.786 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:30:54.783 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:30:54.786 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:30:59.790 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:30:59.792 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -Persisting failed: Another write batch or compaction is already active (Only a single write operations is allowed at a time) -[2026-08-27 13:31:04.801 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:31:04.803 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:31:09.814 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:31:09.816 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:31:14.825 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:31:14.827 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:31:19.830 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:31:19.833 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:31:24.835 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:31:24.837 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:31:29.844 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:31:29.846 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:31:34.858 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:31:34.859 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:31:39.867 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:31:39.869 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:31:44.878 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:31:44.881 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:31:49.892 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:31:49.894 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:31:54.894 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:31:54.898 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:31:59.895 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:31:59.898 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:32:04.896 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:32:04.898 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:32:09.903 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:32:09.905 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:32:14.913 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:32:14.915 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:32:19.918 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:32:19.920 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:32:24.932 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:32:24.934 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:32:29.941 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:32:29.943 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:32:34.942 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:32:34.943 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:32:39.945 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:32:39.947 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:32:44.959 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:32:44.961 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:32:49.967 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:32:49.969 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:32:54.970 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:32:54.971 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:32:59.974 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:32:59.977 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:33:04.973 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:33:04.975 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:33:09.974 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:33:09.976 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:33:14.981 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:33:14.983 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:33:19.981 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:33:19.982 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:33:24.984 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:33:24.986 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:33:29.988 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:33:29.990 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" - ------ -FATAL: An unexpected Turbopack error occurred. A panic log has been written to C:\Users\snqig\AppData\Local\Temp\next-panic-538841427aa5b18325741b2a059cd834.log. - -To help make Turbopack better, report this error by clicking here: https://github.com/vercel/next.js/discussions/new?category=turbopack-error-report&title=Turbopack%20Error%3A%20failed%20to%20write%20to%20D%3A%5Cdcprint%5Cerp-project%5C.next%5Cdev%5Cserver%5Cchunks%5Cssr%5Csrc_lib_db_aa2132b7._.js.map&body=Turbopack%20version%3A%20%60d1bd5b58%60%0ANext.js%20version%3A%20%600.0.0%60%0A%0AError%20message%3A%0A%60%60%60%0ATurbopack%20Error%3A%20failed%20to%20write%20to%20D%3A%5Cdcprint%5Cerp-project%5C.next%5Cdev%5Cserver%5Cchunks%5Cssr%5Csrc_lib_db_aa2132b7._.js.map%0A%60%60%60&labels=Turbopack,Turbopack%20Panic%20Backtrace ------ - - GET /en/login 500 in 6.1s (compile: 5.8s, proxy.ts: 8ms, render: 256ms) -[2026-08-27 13:33:34.998 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:33:35.001 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -Persisting failed: Another write batch or compaction is already active (Only a single write operations is allowed at a time) - ------ -FATAL: An unexpected Turbopack error occurred. A panic log has been written to C:\Users\snqig\AppData\Local\Temp\next-panic-538841427aa5b18325741b2a059cd834.log. - -To help make Turbopack better, report this error by clicking here: https://github.com/vercel/next.js/discussions/new?category=turbopack-error-report&title=Turbopack%20Error%3A%20failed%20to%20write%20to%20D%3A%5Cdcprint%5Cerp-project%5C.next%5Cdev%5Cserver%5Cchunks%5Cssr%5Csrc_lib_db_aa2132b7._.js.map&body=Turbopack%20version%3A%20%60d1bd5b58%60%0ANext.js%20version%3A%20%600.0.0%60%0A%0AError%20message%3A%0A%60%60%60%0ATurbopack%20Error%3A%20failed%20to%20write%20to%20D%3A%5Cdcprint%5Cerp-project%5C.next%5Cdev%5Cserver%5Cchunks%5Cssr%5Csrc_lib_db_aa2132b7._.js.map%0A%60%60%60&labels=Turbopack,Turbopack%20Panic%20Backtrace ------ - - GET /en/login 500 in 40ms (compile: 21ms, proxy.ts: 6ms, render: 13ms) - ------ -FATAL: An unexpected Turbopack error occurred. A panic log has been written to C:\Users\snqig\AppData\Local\Temp\next-panic-538841427aa5b18325741b2a059cd834.log. - -To help make Turbopack better, report this error by clicking here: https://github.com/vercel/next.js/discussions/new?category=turbopack-error-report&title=Turbopack%20Error%3A%20failed%20to%20write%20to%20D%3A%5Cdcprint%5Cerp-project%5C.next%5Cdev%5Cserver%5Cchunks%5Cssr%5Csrc_lib_db_aa2132b7._.js.map&body=Turbopack%20version%3A%20%60d1bd5b58%60%0ANext.js%20version%3A%20%600.0.0%60%0A%0AError%20message%3A%0A%60%60%60%0ATurbopack%20Error%3A%20failed%20to%20write%20to%20D%3A%5Cdcprint%5Cerp-project%5C.next%5Cdev%5Cserver%5Cchunks%5Cssr%5Csrc_lib_db_aa2132b7._.js.map%0A%60%60%60&labels=Turbopack,Turbopack%20Panic%20Backtrace ------ - - GET /en/login 500 in 23ms (compile: 10ms, proxy.ts: 5ms, render: 8ms) -[2026-08-27 13:33:40.006 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:33:40.009 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" - ------ -FATAL: An unexpected Turbopack error occurred. A panic log has been written to C:\Users\snqig\AppData\Local\Temp\next-panic-538841427aa5b18325741b2a059cd834.log. - -To help make Turbopack better, report this error by clicking here: https://github.com/vercel/next.js/discussions/new?category=turbopack-error-report&title=Turbopack%20Error%3A%20failed%20to%20write%20to%20D%3A%5Cdcprint%5Cerp-project%5C.next%5Cdev%5Cserver%5Cchunks%5Cssr%5Csrc_lib_db_aa2132b7._.js.map&body=Turbopack%20version%3A%20%60d1bd5b58%60%0ANext.js%20version%3A%20%600.0.0%60%0A%0AError%20message%3A%0A%60%60%60%0ATurbopack%20Error%3A%20failed%20to%20write%20to%20D%3A%5Cdcprint%5Cerp-project%5C.next%5Cdev%5Cserver%5Cchunks%5Cssr%5Csrc_lib_db_aa2132b7._.js.map%0A%60%60%60&labels=Turbopack,Turbopack%20Panic%20Backtrace ------ - - GET /en/login 500 in 44ms (compile: 16ms, proxy.ts: 11ms, render: 17ms) - GET /en/login 500 in 30ms (compile: 13ms, proxy.ts: 7ms, render: 11ms) -Persisting failed: Another write batch or compaction is already active (Only a single write operations is allowed at a time) -[2026-08-27 13:33:45.012 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:33:45.014 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:33:50.025 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:33:50.027 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:33:55.032 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:33:55.035 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:34:00.034 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:34:00.036 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:34:05.046 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:34:05.048 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:34:10.054 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:34:10.056 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:34:15.065 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:34:15.067 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:34:20.068 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:34:20.070 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:34:25.075 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:34:25.077 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:34:30.079 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:34:30.081 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:34:35.086 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:34:35.088 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:34:40.089 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:34:40.091 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" - ------ -FATAL: An unexpected Turbopack error occurred. A panic log has been written to C:\Users\snqig\AppData\Local\Temp\next-panic-538841427aa5b18325741b2a059cd834.log. - -To help make Turbopack better, report this error by clicking here: https://github.com/vercel/next.js/discussions/new?category=turbopack-error-report&title=Turbopack%20Error%3A%20failed%20to%20write%20to%20D%3A%5Cdcprint%5Cerp-project%5C.next%5Cdev%5Cserver%5Cchunks%5Cssr%5Csrc_lib_db_aa2132b7._.js.map&body=Turbopack%20version%3A%20%60d1bd5b58%60%0ANext.js%20version%3A%20%600.0.0%60%0A%0AError%20message%3A%0A%60%60%60%0ATurbopack%20Error%3A%20failed%20to%20write%20to%20D%3A%5Cdcprint%5Cerp-project%5C.next%5Cdev%5Cserver%5Cchunks%5Cssr%5Csrc_lib_db_aa2132b7._.js.map%0A%60%60%60&labels=Turbopack,Turbopack%20Panic%20Backtrace ------ - - GET /en/login 500 in 27ms (compile: 13ms, proxy.ts: 5ms, render: 10ms) - GET /en/login 500 in 33ms (compile: 17ms, proxy.ts: 5ms, render: 11ms) -[2026-08-27 13:34:45.103 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:34:45.105 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:34:50.108 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:34:50.110 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:34:55.114 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:34:55.116 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:35:00.123 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:35:00.124 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:35:05.128 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:35:05.130 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:35:10.137 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:35:10.139 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:35:15.144 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:35:15.145 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -Persisting failed: Another write batch or compaction is already active (Only a single write operations is allowed at a time) -[2026-08-27 13:35:20.159 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:35:20.161 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:35:25.168 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:35:25.170 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -Persisting failed: Another write batch or compaction is already active (Only a single write operations is allowed at a time) -[2026-08-27 13:35:30.169 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:35:30.171 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:35:35.183 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:35:35.185 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:35:40.186 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:35:40.188 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:35:45.189 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:35:45.191 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" - ------ -FATAL: An unexpected Turbopack error occurred. A panic log has been written to C:\Users\snqig\AppData\Local\Temp\next-panic-538841427aa5b18325741b2a059cd834.log. - -To help make Turbopack better, report this error by clicking here: https://github.com/vercel/next.js/discussions/new?category=turbopack-error-report&title=Turbopack%20Error%3A%20failed%20to%20write%20to%20D%3A%5Cdcprint%5Cerp-project%5C.next%5Cdev%5Cserver%5Cchunks%5Cssr%5Csrc_lib_db_aa2132b7._.js.map&body=Turbopack%20version%3A%20%60d1bd5b58%60%0ANext.js%20version%3A%20%600.0.0%60%0A%0AError%20message%3A%0A%60%60%60%0ATurbopack%20Error%3A%20failed%20to%20write%20to%20D%3A%5Cdcprint%5Cerp-project%5C.next%5Cdev%5Cserver%5Cchunks%5Cssr%5Csrc_lib_db_aa2132b7._.js.map%0A%60%60%60&labels=Turbopack,Turbopack%20Panic%20Backtrace ------ - - GET /en/login 500 in 55ms (compile: 35ms, proxy.ts: 7ms, render: 13ms) - ------ -FATAL: An unexpected Turbopack error occurred. A panic log has been written to C:\Users\snqig\AppData\Local\Temp\next-panic-538841427aa5b18325741b2a059cd834.log. - -To help make Turbopack better, report this error by clicking here: https://github.com/vercel/next.js/discussions/new?category=turbopack-error-report&title=Turbopack%20Error%3A%20failed%20to%20write%20to%20D%3A%5Cdcprint%5Cerp-project%5C.next%5Cdev%5Cserver%5Cchunks%5Cssr%5Csrc_lib_db_aa2132b7._.js.map&body=Turbopack%20version%3A%20%60d1bd5b58%60%0ANext.js%20version%3A%20%600.0.0%60%0A%0AError%20message%3A%0A%60%60%60%0ATurbopack%20Error%3A%20failed%20to%20write%20to%20D%3A%5Cdcprint%5Cerp-project%5C.next%5Cdev%5Cserver%5Cchunks%5Cssr%5Csrc_lib_db_aa2132b7._.js.map%0A%60%60%60&labels=Turbopack,Turbopack%20Panic%20Backtrace ------ - - GET /en/login 500 in 64ms (compile: 38ms, proxy.ts: 7ms, render: 19ms) -[2026-08-27 13:35:50.197 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:35:50.199 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:35:55.212 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:35:55.214 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:36:00.218 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:36:00.221 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:36:05.225 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:36:05.227 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:36:10.240 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:36:10.242 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:36:15.240 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:36:15.242 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:36:20.252 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:36:20.254 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:36:25.257 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:36:25.259 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -Persisting failed: Another write batch or compaction is already active (Only a single write operations is allowed at a time) -[2026-08-27 13:36:30.259 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:36:30.260 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:36:35.268 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:36:35.270 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:36:40.275 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:36:40.277 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:36:45.280 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:36:45.282 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" - ------ -FATAL: An unexpected Turbopack error occurred. A panic log has been written to C:\Users\snqig\AppData\Local\Temp\next-panic-538841427aa5b18325741b2a059cd834.log. - -To help make Turbopack better, report this error by clicking here: https://github.com/vercel/next.js/discussions/new?category=turbopack-error-report&title=Turbopack%20Error%3A%20failed%20to%20write%20to%20D%3A%5Cdcprint%5Cerp-project%5C.next%5Cdev%5Cserver%5Cchunks%5Cssr%5Csrc_lib_db_aa2132b7._.js.map&body=Turbopack%20version%3A%20%60d1bd5b58%60%0ANext.js%20version%3A%20%600.0.0%60%0A%0AError%20message%3A%0A%60%60%60%0ATurbopack%20Error%3A%20failed%20to%20write%20to%20D%3A%5Cdcprint%5Cerp-project%5C.next%5Cdev%5Cserver%5Cchunks%5Cssr%5Csrc_lib_db_aa2132b7._.js.map%0A%60%60%60&labels=Turbopack,Turbopack%20Panic%20Backtrace ------ - - GET /en/login 500 in 22ms (compile: 11ms, proxy.ts: 4ms, render: 7ms) -[2026-08-27 13:36:50.293 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:36:50.295 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" - ------ -FATAL: An unexpected Turbopack error occurred. A panic log has been written to C:\Users\snqig\AppData\Local\Temp\next-panic-538841427aa5b18325741b2a059cd834.log. - -To help make Turbopack better, report this error by clicking here: https://github.com/vercel/next.js/discussions/new?category=turbopack-error-report&title=Turbopack%20Error%3A%20failed%20to%20write%20to%20D%3A%5Cdcprint%5Cerp-project%5C.next%5Cdev%5Cserver%5Cchunks%5Cssr%5Csrc_lib_db_aa2132b7._.js.map&body=Turbopack%20version%3A%20%60d1bd5b58%60%0ANext.js%20version%3A%20%600.0.0%60%0A%0AError%20message%3A%0A%60%60%60%0ATurbopack%20Error%3A%20failed%20to%20write%20to%20D%3A%5Cdcprint%5Cerp-project%5C.next%5Cdev%5Cserver%5Cchunks%5Cssr%5Csrc_lib_db_aa2132b7._.js.map%0A%60%60%60&labels=Turbopack,Turbopack%20Panic%20Backtrace ------ - - GET /en/login 500 in 225ms (compile: 210ms, proxy.ts: 7ms, render: 7ms) -Persisting failed: Another write batch or compaction is already active (Only a single write operations is allowed at a time) -[2026-08-27 13:36:55.295 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:36:55.297 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -Persisting failed: Another write batch or compaction is already active (Only a single write operations is allowed at a time) -[2026-08-27 13:37:00.307 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:37:00.308 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:37:05.317 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:37:05.318 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:37:10.330 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:37:10.332 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:37:15.330 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:37:15.332 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -Persisting failed: Another write batch or compaction is already active (Only a single write operations is allowed at a time) -[2026-08-27 13:37:20.345 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:37:20.347 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -Persisting failed: Another write batch or compaction is already active (Only a single write operations is allowed at a time) -[2026-08-27 13:37:25.346 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:37:25.348 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:37:30.357 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:37:30.359 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:37:35.368 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:37:35.370 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:37:40.372 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:37:40.374 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:37:45.378 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:37:45.379 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -Persisting failed: Another write batch or compaction is already active (Only a single write operations is allowed at a time) -[2026-08-27 13:37:50.392 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:37:50.393 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:37:55.406 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:37:55.408 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" - ------ -FATAL: An unexpected Turbopack error occurred. A panic log has been written to C:\Users\snqig\AppData\Local\Temp\next-panic-538841427aa5b18325741b2a059cd834.log. - -To help make Turbopack better, report this error by clicking here: https://github.com/vercel/next.js/discussions/new?category=turbopack-error-report&title=Turbopack%20Error%3A%20failed%20to%20write%20to%20D%3A%5Cdcprint%5Cerp-project%5C.next%5Cdev%5Cserver%5Cchunks%5Cssr%5Csrc_lib_db_aa2132b7._.js.map&body=Turbopack%20version%3A%20%60d1bd5b58%60%0ANext.js%20version%3A%20%600.0.0%60%0A%0AError%20message%3A%0A%60%60%60%0ATurbopack%20Error%3A%20failed%20to%20write%20to%20D%3A%5Cdcprint%5Cerp-project%5C.next%5Cdev%5Cserver%5Cchunks%5Cssr%5Csrc_lib_db_aa2132b7._.js.map%0A%60%60%60&labels=Turbopack,Turbopack%20Panic%20Backtrace ------ - - GET /en/login 500 in 23ms (compile: 9ms, proxy.ts: 5ms, render: 9ms) -[2026-08-27 13:38:00.422 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:38:00.424 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:38:05.436 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:38:05.437 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:38:10.445 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:38:10.448 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -Persisting failed: Another write batch or compaction is already active (Only a single write operations is allowed at a time) -[2026-08-27 13:38:15.448 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:38:15.450 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -Persisting failed: Another write batch or compaction is already active (Only a single write operations is allowed at a time) -[2026-08-27 13:38:20.455 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:38:20.457 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-27 13:38:25.458 +0800] DEBUG: Event registry already initialized -[2026-08-27 13:38:25.460 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[?25h diff --git a/_devlog_e2e.txt b/_devlog_e2e.txt deleted file mode 100644 index a6a07cb4..00000000 --- a/_devlog_e2e.txt +++ /dev/null @@ -1,11741 +0,0 @@ - -> vnerp@0.1.0 dev -> next dev -p 5000 - -▲ Next.js 16.1.1 (Turbopack) -- Local: http://localhost:5000 -- Network: http://192.168.0.159:5000 -- Environments: .env.local, .env -- Experiments (use with caution): - · optimizePackageImports - -✓ Starting... -✓ Ready in 4.3s -[2026-08-26 10:27:04.951 +0800] INFO: [instrumentation] Server started. OutboxPoller will auto-start on first API request. -[2026-08-26 10:27:47.117 +0800] DEBUG: [auth.login] 用户登录 - ctx: { - "module": "auth", - "action": "login", - "traceId": "mt9h4pws-0fcgd9o" - } - step: "用户登录" - data: { - "clientIP": "::1" - } -[2026-08-26 10:27:47.240 +0800] DEBUG: [auth.login] 密码验证: 密码正确 -> PASS - ctx: { - "module": "auth", - "action": "login", - "traceId": "mt9h4pws-0fcgd9o" - } - branch: "密码验证" - condition: "密码正确" - status: "PASS" - data: { - "userId": 1 - } - POST /api/auth/login 200 in 1256ms (compile: 874ms, proxy.ts: 188ms, render: 195ms) -[2026-08-26 10:27:47.287 +0800] INFO: CacheManager: 使用 InMemoryCacheManager(未配置 REDIS_URL) -[2026-08-26 10:28:34.397 +0800] DEBUG: [auth.login] 用户登录 - ctx: { - "module": "auth", - "action": "login", - "traceId": "mt9h5qe5-0vjwgc1" - } - step: "用户登录" - data: { - "clientIP": "::1" - } -[2026-08-26 10:28:34.510 +0800] DEBUG: [auth.login] 密码验证: 密码正确 -> PASS - ctx: { - "module": "auth", - "action": "login", - "traceId": "mt9h5qe5-0vjwgc1" - } - branch: "密码验证" - condition: "密码正确" - status: "PASS" - data: { - "userId": 1 - } - POST /api/auth/login 200 in 169ms (compile: 7ms, proxy.ts: 9ms, render: 153ms) -[2026-08-26 10:29:06.773 +0800] DEBUG: [auth.login] 用户登录 - ctx: { - "module": "auth", - "action": "login", - "traceId": "mt9h6fdh-f9syjpb" - } - step: "用户登录" - data: { - "clientIP": "::1" - } -[2026-08-26 10:29:06.947 +0800] DEBUG: [auth.login] 密码验证: 密码正确 -> PASS - ctx: { - "module": "auth", - "action": "login", - "traceId": "mt9h6fdh-f9syjpb" - } - branch: "密码验证" - condition: "密码正确" - status: "PASS" - data: { - "userId": 1 - } - POST /api/auth/login 200 in 271ms (compile: 10ms, proxy.ts: 18ms, render: 243ms) -[RepoRegistry] active impl = mysql (default; set REPOSITORY_IMPL=drizzle to switch) -[2026-08-26 10:29:08.042 +0800] INFO: [warehouse/inbound] 开始物料分类校验 - itemCount: 1 - materialIds: [] -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) -[2026-08-26 10:29:08.057 +0800] INFO: [warehouse/inbound] 物料分类校验完成 - blocked: false - uncategorizedCount: 0 -[2026-08-26 10:29:08.060 +0800] INFO: Event registry initialized - inboundApprovedHandlers: 6 - purchaseReceivedHandlers: 1 - salesShippedHandlers: 4 - deliveryShippedHandlers: 4 - returnOrderCompletedHandlers: 3 - reconciliationWrittenOffHandlers: 3 - purchaseReturnCompletedHandlers: 3 - purchaseReconWrittenOffHandlers: 3 - workorderCompletedHandlers: 8 - qualityUnqualifiedCompletedHandlers: 2 -[2026-08-26 10:29:08.063 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:29:08.063 +0800] INFO: Outbox poller starting - intervalMs: 5000 - reclaimIntervalMs: 60000 - cleanupIntervalMs: 86400000 - dispatchingTimeoutMinutes: 10 -[2026-08-26 10:29:08.065 +0800] INFO: OutboxPoller auto-started (EVENT_BUS_TYPE=db) -[2026-08-26 10:29:08.065 +0800] INFO: StreamConsumer not started (Redis unavailable, OutboxPoller using direct publish) - POST /api/warehouse/inbound 200 in 1077ms (compile: 918ms, proxy.ts: 16ms, render: 143ms) -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) -[2026-08-26 10:29:08.177 +0800] DEBUG: Event registry already initialized - PUT /api/warehouse/inbound 200 in 98ms (compile: 12ms, proxy.ts: 13ms, render: 73ms) -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) -[2026-08-26 10:29:08.282 +0800] DEBUG: Event registry already initialized - GET /api/warehouse/inbound?id=166 200 in 69ms (compile: 11ms, proxy.ts: 13ms, render: 45ms) -[2026-08-26 10:29:13.081 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:29:13.101 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:29:18.085 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:29:18.090 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:29:23.102 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:29:23.106 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:29:28.121 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:29:28.125 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:29:33.131 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:29:33.134 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:29:35.288 +0800] DEBUG: [auth.login] 用户登录 - ctx: { - "module": "auth", - "action": "login", - "traceId": "mt9h71dk-002olf1" - } - step: "用户登录" - data: { - "clientIP": "::1" - } -[2026-08-26 10:29:35.390 +0800] DEBUG: [auth.login] 密码验证: 密码正确 -> PASS - ctx: { - "module": "auth", - "action": "login", - "traceId": "mt9h71dk-002olf1" - } - branch: "密码验证" - condition: "密码正确" - status: "PASS" - data: { - "userId": 1 - } - POST /api/auth/login 200 in 155ms (compile: 5ms, proxy.ts: 8ms, render: 142ms) -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) -[2026-08-26 10:29:35.464 +0800] INFO: [warehouse/inbound] 开始物料分类校验 - itemCount: 1 - materialIds: [] -[2026-08-26 10:29:35.464 +0800] INFO: [warehouse/inbound] 物料分类校验完成 - blocked: false - uncategorizedCount: 0 -[2026-08-26 10:29:35.464 +0800] DEBUG: Event registry already initialized - POST /api/warehouse/inbound 200 in 43ms (compile: 4ms, proxy.ts: 6ms, render: 33ms) -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) -[2026-08-26 10:29:35.513 +0800] DEBUG: Event registry already initialized - PUT /api/warehouse/inbound 200 in 48ms (compile: 4ms, proxy.ts: 6ms, render: 38ms) -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) -[2026-08-26 10:29:35.559 +0800] DEBUG: Event registry already initialized - GET /api/warehouse/inbound?pageSize=200 200 in 33ms (compile: 3ms, proxy.ts: 6ms, render: 24ms) -[2026-08-26 10:29:38.139 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:29:38.141 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:29:43.147 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:29:43.149 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:29:48.163 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:29:48.168 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:29:53.177 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:29:53.182 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:29:58.181 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:29:58.184 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:30:03.191 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:30:03.195 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:30:08.194 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:30:08.198 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:30:13.207 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:30:13.212 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:30:17.797 +0800] DEBUG: [auth.login] 用户登录 - ctx: { - "module": "auth", - "action": "login", - "traceId": "mt9h7y6d-5jhqmti" - } - step: "用户登录" - data: { - "clientIP": "::1" - } -[2026-08-26 10:30:17.963 +0800] DEBUG: [auth.login] 密码验证: 密码正确 -> PASS - ctx: { - "module": "auth", - "action": "login", - "traceId": "mt9h7y6d-5jhqmti" - } - branch: "密码验证" - condition: "密码正确" - status: "PASS" - data: { - "userId": 1 - } - POST /api/auth/login 200 in 259ms (compile: 8ms, proxy.ts: 11ms, render: 240ms) -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) -[2026-08-26 10:30:18.104 +0800] INFO: [warehouse/inbound] 开始物料分类校验 - itemCount: 1 - materialIds: [] -[2026-08-26 10:30:18.104 +0800] INFO: [warehouse/inbound] 物料分类校验完成 - blocked: false - uncategorizedCount: 0 -[2026-08-26 10:30:18.104 +0800] DEBUG: Event registry already initialized - POST /api/warehouse/inbound 200 in 73ms (compile: 9ms, proxy.ts: 10ms, render: 54ms) -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) -[2026-08-26 10:30:18.188 +0800] DEBUG: Event registry already initialized - PUT /api/warehouse/inbound 200 in 82ms (compile: 9ms, proxy.ts: 13ms, render: 60ms) -[2026-08-26 10:30:18.212 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:30:18.228 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) -[2026-08-26 10:30:18.282 +0800] DEBUG: Event registry already initialized - GET /api/warehouse/inbound?pageSize=200 200 in 68ms (compile: 7ms, proxy.ts: 11ms, render: 50ms) -[2026-08-26 10:30:23.216 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:30:23.220 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:30:28.223 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:30:28.227 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:30:33.235 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:30:33.247 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:30:38.234 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:30:38.236 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:30:43.237 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:30:43.240 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:30:48.237 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:30:48.239 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:30:48.414 +0800] DEBUG: [auth.login] 用户登录 - ctx: { - "module": "auth", - "action": "login", - "traceId": "mt9h8lsu-pxt1ane" - } - step: "用户登录" - data: { - "clientIP": "::1" - } -[2026-08-26 10:30:48.490 +0800] DEBUG: [auth.login] 密码验证: 密码正确 -> PASS - ctx: { - "module": "auth", - "action": "login", - "traceId": "mt9h8lsu-pxt1ane" - } - branch: "密码验证" - condition: "密码正确" - status: "PASS" - data: { - "userId": 1 - } - POST /api/auth/login 200 in 121ms (compile: 3ms, proxy.ts: 3ms, render: 115ms) -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) -[2026-08-26 10:30:48.554 +0800] DEBUG: Event registry already initialized - GET /api/warehouse/inbound?pageSize=200 200 in 21ms (compile: 3ms, proxy.ts: 4ms, render: 13ms) -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) -[2026-08-26 10:30:48.578 +0800] DEBUG: Event registry already initialized - DELETE /api/warehouse/inbound?id=174 200 in 27ms (compile: 3ms, proxy.ts: 5ms, render: 20ms) -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) -[2026-08-26 10:30:48.617 +0800] DEBUG: Event registry already initialized - DELETE /api/warehouse/inbound?id=167 200 in 34ms (compile: 9ms, proxy.ts: 6ms, render: 18ms) -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) -[2026-08-26 10:30:48.646 +0800] DEBUG: Event registry already initialized - DELETE /api/warehouse/inbound?id=166 200 in 23ms (compile: 1999µs, proxy.ts: 4ms, render: 17ms) -[2026-08-26 10:30:53.241 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:30:53.242 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:30:58.254 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:30:58.256 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:31:03.259 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:31:03.261 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:31:08.261 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:31:08.263 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:31:13.263 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:31:13.265 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:31:18.277 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:31:18.280 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:31:23.281 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:31:23.284 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:31:28.293 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:31:28.296 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:31:33.295 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:31:33.298 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:31:38.301 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:31:38.303 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:31:43.311 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:31:43.313 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:31:48.314 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:31:48.316 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:31:53.324 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:31:53.326 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:31:58.332 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:31:58.334 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:32:03.348 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:32:03.350 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:32:08.355 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:32:08.357 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:32:13.365 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:32:13.368 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:32:18.371 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:32:18.373 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:32:23.381 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:32:23.383 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:32:28.393 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:32:28.395 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:32:33.402 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:32:33.404 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:32:38.411 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:32:38.414 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:32:43.419 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:32:43.421 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:32:48.422 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:32:48.424 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:32:53.428 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:32:53.430 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:32:58.431 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:32:58.433 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:33:03.432 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:33:03.434 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:33:08.434 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:33:08.437 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:33:13.445 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:33:13.447 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:33:18.460 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:33:18.462 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:33:23.461 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:33:23.463 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:33:28.468 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:33:28.470 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:33:33.470 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:33:33.472 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:33:38.484 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:33:38.486 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:33:43.488 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:33:43.490 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:33:48.497 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:33:48.499 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:33:53.511 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:33:53.514 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:33:58.520 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:33:58.521 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:34:03.530 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:34:03.532 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:34:08.536 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:34:08.539 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:34:13.538 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:34:13.540 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:34:18.546 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:34:18.547 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:34:23.561 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:34:23.563 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:34:28.572 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:34:28.574 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:34:33.572 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:34:33.574 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:34:38.587 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:34:38.589 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:34:43.602 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:34:43.604 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:34:48.607 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:34:48.609 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:34:53.621 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:34:53.624 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:34:58.630 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:34:58.631 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:35:03.644 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:35:03.646 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:35:08.645 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:35:08.647 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:35:13.651 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:35:13.653 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:35:18.665 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:35:18.667 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:35:23.668 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:35:23.670 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:35:28.679 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:35:28.681 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:35:33.690 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:35:33.692 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:35:38.690 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:35:38.692 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:35:43.693 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:35:43.696 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:35:48.695 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:35:48.698 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:35:53.705 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:35:53.708 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:35:58.714 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:35:58.716 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:36:03.725 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:36:03.727 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:36:08.728 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:36:08.730 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:36:13.740 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:36:13.742 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:36:18.743 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:36:18.745 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:36:23.758 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:36:23.759 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:36:28.764 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:36:28.766 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:36:33.771 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:36:33.773 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:36:38.774 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:36:38.776 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:36:43.781 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:36:43.784 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:36:48.784 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:36:48.786 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:36:53.790 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:36:53.792 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:36:58.795 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:36:58.798 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:37:03.797 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:37:03.799 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:37:08.807 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:37:08.808 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:37:13.814 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:37:13.816 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:37:18.817 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:37:18.819 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:37:23.824 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:37:23.825 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:37:28.830 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:37:28.832 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:37:33.836 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:37:33.838 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:37:38.845 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:37:38.847 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:37:43.860 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:37:43.862 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:37:48.864 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:37:48.866 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:37:53.871 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:37:53.873 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:37:58.873 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:37:58.875 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:38:03.877 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:38:03.878 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:38:08.881 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:38:08.884 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:38:13.891 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:38:13.893 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:38:18.891 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:38:18.893 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:38:23.900 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:38:23.902 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:38:28.908 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:38:28.910 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:38:33.920 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:38:33.923 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:38:38.930 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:38:38.932 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:38:43.936 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:38:43.938 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:38:48.942 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:38:48.944 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:38:53.945 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:38:53.947 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:38:58.947 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:38:58.949 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:39:03.962 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:39:03.964 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:39:08.976 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:39:08.978 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:39:13.982 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:39:13.984 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:39:18.996 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:39:18.998 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:39:24.009 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:39:24.011 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:39:29.013 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:39:29.016 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:39:34.019 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:39:34.022 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:39:39.032 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:39:39.034 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:39:44.046 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:39:44.047 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:39:49.053 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:39:49.055 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:39:54.059 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:39:54.062 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:39:59.061 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:39:59.063 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:40:04.064 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:40:04.067 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:40:09.077 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:40:09.079 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:40:14.086 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:40:14.088 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:40:19.100 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:40:19.102 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:40:24.108 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:40:24.111 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:40:29.108 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:40:29.109 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:40:34.109 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:40:34.111 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:40:39.112 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:40:39.115 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:40:44.122 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:40:44.124 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:40:49.131 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:40:49.134 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:40:54.139 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:40:54.142 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:40:59.154 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:40:59.156 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:41:04.155 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:41:04.157 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:41:09.164 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:41:09.166 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:41:14.166 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:41:14.169 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:41:19.176 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:41:19.178 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:41:24.188 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:41:24.190 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:41:29.201 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:41:29.205 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:41:34.204 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:41:34.208 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:41:39.209 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:41:39.211 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:41:44.220 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:41:44.224 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:41:49.231 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:41:49.233 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:41:54.239 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:41:54.242 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:41:59.251 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:41:59.254 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:42:04.261 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:42:04.264 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:42:09.264 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:42:09.268 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:42:14.266 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:42:14.268 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:42:19.278 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:42:19.281 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:42:24.291 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:42:24.293 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:42:29.293 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:42:29.297 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:42:34.300 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:42:34.303 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:42:39.302 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:42:39.309 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:42:44.304 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:42:44.306 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:42:49.314 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:42:49.317 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:42:54.326 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:42:54.329 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:42:59.328 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:42:59.330 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:43:04.341 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:43:04.343 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:43:09.345 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:43:09.348 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:43:14.347 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:43:14.352 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:43:19.356 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:43:19.359 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:43:24.363 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:43:24.367 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:43:29.367 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:43:29.371 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:43:34.381 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:43:34.385 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:43:39.387 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:43:39.390 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:43:44.390 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:43:44.393 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:43:49.403 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:43:49.407 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:43:54.411 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:43:54.413 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:43:59.415 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:43:59.418 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:44:04.426 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:44:04.430 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:44:09.427 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:44:09.431 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:44:14.430 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:44:14.433 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:44:19.435 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:44:19.439 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:44:24.440 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:44:24.445 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:44:29.446 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:44:29.452 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:44:34.448 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:44:34.454 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:44:39.463 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:44:39.466 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:44:44.478 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:44:44.483 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:44:49.478 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:44:49.482 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:44:54.483 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:44:54.486 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:44:59.488 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:44:59.491 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:45:04.499 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:45:04.502 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:45:09.508 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:45:09.510 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:45:14.511 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:45:14.513 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:45:19.513 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:45:19.515 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:45:24.516 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:45:24.518 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:45:29.528 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:45:29.530 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:45:34.542 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:45:34.544 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:45:39.556 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:45:39.558 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:45:44.572 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:45:44.573 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:45:49.581 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:45:49.583 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:45:54.584 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:45:54.585 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:45:59.592 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:45:59.595 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:46:04.595 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:46:04.597 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:46:09.599 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:46:09.600 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:46:14.602 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:46:14.604 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:46:19.613 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:46:19.614 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:46:24.620 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:46:24.622 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:46:29.625 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:46:29.627 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:46:34.633 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:46:34.636 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:46:39.634 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:46:39.636 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:46:44.635 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:46:44.637 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:46:49.634 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:46:49.636 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:46:54.642 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:46:54.644 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:46:59.648 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:46:59.650 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:47:04.656 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:47:04.658 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:47:09.662 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:47:09.664 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:47:14.672 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:47:14.675 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:47:19.672 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:47:19.675 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:47:24.685 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:47:24.687 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:47:29.699 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:47:29.701 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:47:34.713 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:47:34.715 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:47:39.719 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:47:39.723 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:47:44.733 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:47:44.735 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:47:49.742 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:47:49.744 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:47:54.742 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:47:54.744 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:47:59.743 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:47:59.745 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:48:04.746 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:48:04.748 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:48:09.759 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:48:09.761 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:48:14.760 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:48:14.763 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:48:19.768 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:48:19.770 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:48:24.771 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:48:24.773 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:48:29.787 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:48:29.789 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:48:34.790 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:48:34.792 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:48:39.802 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:48:39.804 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:48:44.809 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:48:44.810 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:48:49.821 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:48:49.823 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:48:54.821 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:48:54.823 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:48:59.825 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:48:59.827 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:49:04.826 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:49:04.828 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:49:09.828 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:49:09.829 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:49:14.829 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:49:14.831 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:49:19.831 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:49:19.834 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:49:24.832 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:49:24.833 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:49:29.841 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:49:29.843 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:49:34.854 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:49:34.856 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:49:39.865 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:49:39.867 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:49:44.875 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:49:44.877 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:49:49.875 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:49:49.887 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:49:54.883 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:49:54.885 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:49:59.886 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:49:59.888 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:50:04.891 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:50:04.894 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:50:09.899 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:50:09.904 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:50:14.905 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:50:14.907 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:50:19.920 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:50:19.922 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:50:24.929 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:50:24.932 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:50:29.932 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:50:29.934 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:50:34.946 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:50:34.948 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:50:39.960 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:50:39.963 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:50:44.970 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:50:44.973 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:50:49.981 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:50:49.983 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:50:54.992 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:50:54.994 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:50:59.997 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:50:59.999 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:51:05.004 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:51:05.006 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:51:10.014 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:51:10.017 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:51:15.024 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:51:15.027 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:51:20.033 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:51:20.036 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:51:25.048 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:51:25.050 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:51:30.054 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:51:30.057 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:51:35.063 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:51:35.065 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:51:40.072 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:51:40.074 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:51:45.086 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:51:45.088 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:51:50.088 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:51:50.091 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:51:55.097 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:51:55.099 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:52:00.109 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:52:00.111 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:52:05.118 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:52:05.122 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:52:10.125 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:52:10.127 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:52:15.133 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:52:15.135 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:52:20.137 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:52:20.139 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:52:25.150 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:52:25.153 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:52:30.158 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:52:30.161 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:52:35.161 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:52:35.164 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:52:40.175 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:52:40.177 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:52:45.186 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:52:45.188 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:52:50.186 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:52:50.189 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:52:55.203 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:52:55.205 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:53:00.206 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:53:00.209 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:53:05.218 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:53:05.220 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:53:10.223 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:53:10.226 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:53:15.237 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:53:15.240 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:53:20.238 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:53:20.240 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:53:25.252 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:53:25.254 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:53:30.257 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:53:30.259 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:53:35.262 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:53:35.265 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:53:40.272 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:53:40.275 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:53:45.284 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:53:45.286 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:53:50.292 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:53:50.295 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:53:55.299 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:53:55.301 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:54:00.301 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:54:00.302 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:54:05.314 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:54:05.316 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:54:10.329 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:54:10.331 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:54:15.342 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:54:15.343 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:54:20.351 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:54:20.353 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:54:25.356 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:54:25.358 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:54:30.369 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:54:30.370 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:54:35.376 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:54:35.378 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:54:40.388 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:54:40.390 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:54:45.394 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:54:45.396 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:54:50.407 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:54:50.409 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:54:55.412 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:54:55.414 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:55:00.421 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:55:00.423 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:55:05.430 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:55:05.433 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:55:10.438 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:55:10.440 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:55:15.450 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:55:15.452 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:55:20.454 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:55:20.456 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:55:25.458 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:55:25.460 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:55:30.464 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:55:30.466 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:55:35.474 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:55:35.475 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:55:40.480 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:55:40.483 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:55:45.494 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:55:45.496 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:55:50.509 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:55:50.511 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:55:55.509 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:55:55.511 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:56:00.517 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:56:00.518 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:56:05.524 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:56:05.526 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:56:10.525 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:56:10.526 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:56:15.536 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:56:15.538 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:56:20.539 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:56:20.541 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:56:25.546 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:56:25.548 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:56:30.551 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:56:30.553 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:56:35.551 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:56:35.553 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:56:40.556 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:56:40.558 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:56:45.558 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:56:45.560 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:56:50.571 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:56:50.573 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:56:55.583 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:56:55.585 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:57:00.592 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:57:00.595 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:57:05.596 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:57:05.598 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:57:10.600 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:57:10.602 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:57:15.607 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:57:15.609 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:57:20.622 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:57:20.624 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:57:25.629 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:57:25.631 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:57:30.637 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:57:30.639 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:57:35.637 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:57:35.639 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:57:40.648 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:57:40.650 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:57:45.656 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:57:45.659 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:57:50.668 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:57:50.670 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:57:55.675 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:57:55.677 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:58:00.681 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:58:00.683 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:58:05.686 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:58:05.687 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:58:10.697 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:58:10.699 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:58:15.704 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:58:15.707 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:58:20.704 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:58:20.707 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:58:25.717 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:58:25.719 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:58:30.718 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:58:30.720 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:58:35.732 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:58:35.735 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:58:40.733 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:58:40.735 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:58:45.743 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:58:45.745 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:58:50.746 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:58:50.748 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:58:55.749 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:58:55.751 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:59:00.753 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:59:00.755 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:59:05.760 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:59:05.762 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:59:10.766 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:59:10.768 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:59:15.771 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:59:15.774 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:59:20.775 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:59:20.776 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:59:25.777 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:59:25.779 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:59:30.788 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:59:30.790 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:59:35.791 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:59:35.793 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:59:40.798 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:59:40.800 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:59:45.799 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:59:45.801 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:59:50.810 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:59:50.812 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 10:59:55.814 +0800] DEBUG: Event registry already initialized -[2026-08-26 10:59:55.815 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:00:00.822 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:00:00.824 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:00:05.837 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:00:05.839 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:00:10.843 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:00:10.845 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:00:15.852 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:00:15.854 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:00:20.858 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:00:20.860 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:00:25.864 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:00:25.866 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:00:30.874 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:00:30.876 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:00:35.889 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:00:35.891 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:00:40.894 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:00:40.895 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:00:45.902 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:00:45.905 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:00:50.912 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:00:50.914 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:00:55.924 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:00:55.926 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:01:00.936 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:01:00.937 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:01:05.952 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:01:05.954 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:01:10.961 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:01:10.962 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:01:15.972 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:01:15.974 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:01:20.972 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:01:20.974 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:01:25.982 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:01:25.984 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:01:30.989 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:01:30.991 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:01:36.000 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:01:36.002 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:01:41.002 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:01:41.004 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:01:46.016 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:01:46.018 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:01:51.018 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:01:51.019 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:01:56.019 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:01:56.020 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:02:01.032 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:02:01.034 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:02:06.035 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:02:06.037 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:02:11.039 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:02:11.040 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:02:16.045 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:02:16.046 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:02:21.050 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:02:21.051 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:02:26.061 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:02:26.062 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:02:31.076 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:02:31.078 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:02:36.081 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:02:36.082 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:02:41.091 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:02:41.093 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:02:46.104 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:02:46.106 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:02:51.117 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:02:51.119 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:02:56.132 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:02:56.134 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:03:01.144 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:03:01.146 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:03:06.144 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:03:06.146 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:03:11.150 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:03:11.152 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:03:16.156 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:03:16.157 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:03:21.165 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:03:21.167 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:03:26.170 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:03:26.171 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:03:31.174 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:03:31.176 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:03:36.182 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:03:36.184 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:03:41.191 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:03:41.193 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:03:46.198 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:03:46.200 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:03:51.208 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:03:51.209 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:03:56.212 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:03:56.214 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:04:01.223 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:04:01.225 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:04:06.232 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:04:06.234 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:04:11.233 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:04:11.235 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:04:16.242 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:04:16.243 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:04:21.244 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:04:21.245 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:04:26.257 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:04:26.259 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:04:31.264 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:04:31.265 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:04:36.270 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:04:36.272 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:04:41.279 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:04:41.281 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:04:46.279 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:04:46.281 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:04:51.281 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:04:51.282 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:04:56.295 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:04:56.298 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:05:01.303 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:05:01.305 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:05:06.314 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:05:06.316 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:05:11.329 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:05:11.331 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:05:16.331 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:05:16.332 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:05:21.333 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:05:21.334 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:05:26.345 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:05:26.346 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:05:31.349 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:05:31.350 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:05:36.358 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:05:36.360 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:05:41.367 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:05:41.369 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:05:46.379 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:05:46.381 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:05:51.393 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:05:51.394 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:05:56.396 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:05:56.397 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:06:01.397 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:06:01.398 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:06:06.403 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:06:06.404 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:06:11.405 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:06:11.406 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:06:16.416 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:06:16.418 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:06:21.417 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:06:21.419 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:06:26.429 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:06:26.431 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:06:31.435 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:06:31.437 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:06:36.440 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:06:36.442 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:06:41.445 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:06:41.447 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:06:46.455 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:06:46.457 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:06:51.457 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:06:51.459 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:06:56.469 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:06:56.471 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:07:01.470 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:07:01.471 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:07:06.474 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:07:06.476 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:07:11.481 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:07:11.483 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:07:16.495 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:07:16.497 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:07:21.507 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:07:21.509 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:07:26.509 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:07:26.511 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:07:31.520 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:07:31.521 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:07:36.530 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:07:36.532 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:07:41.540 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:07:41.542 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:07:46.551 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:07:46.553 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:07:51.554 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:07:51.555 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:07:56.558 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:07:56.560 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:08:01.567 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:08:01.568 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:08:06.574 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:08:06.575 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:08:11.579 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:08:11.581 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:08:16.584 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:08:16.586 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:08:21.586 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:08:21.588 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:08:26.591 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:08:26.593 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:08:31.592 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:08:31.593 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:08:36.604 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:08:36.606 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:08:41.607 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:08:41.609 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:08:46.613 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:08:46.614 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:08:51.619 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:08:51.621 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:08:56.627 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:08:56.628 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:09:01.636 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:09:01.637 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:09:06.645 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:09:06.648 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:09:11.654 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:09:11.656 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:09:16.665 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:09:16.667 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:09:21.679 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:09:21.681 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:09:26.692 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:09:26.694 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:09:31.696 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:09:31.697 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:09:36.697 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:09:36.698 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:09:41.705 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:09:41.707 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:09:46.719 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:09:46.721 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:09:51.731 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:09:51.733 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:09:56.735 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:09:56.737 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:10:01.742 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:10:01.743 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:10:06.747 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:10:06.749 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:10:11.762 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:10:11.764 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:10:16.765 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:10:16.767 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:10:21.766 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:10:21.767 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:10:26.771 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:10:26.773 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:10:31.779 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:10:31.781 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:10:36.782 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:10:36.784 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:10:41.795 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:10:41.797 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:10:46.811 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:10:46.812 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:10:51.824 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:10:51.826 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:10:56.826 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:10:56.829 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:11:01.835 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:11:01.837 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:11:06.848 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:11:06.850 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:11:11.853 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:11:11.856 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:11:16.854 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:11:16.856 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:11:21.868 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:11:21.869 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:11:26.869 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:11:26.871 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:11:31.871 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:11:31.874 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:11:36.875 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:11:36.877 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:11:41.876 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:11:41.879 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:11:46.888 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:11:46.890 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:11:51.891 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:11:51.893 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:11:56.893 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:11:56.895 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:12:01.904 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:12:01.905 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:12:06.918 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:12:06.920 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:12:11.931 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:12:11.933 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:12:16.939 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:12:16.940 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:12:21.952 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:12:21.954 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:12:26.965 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:12:26.967 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:12:31.972 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:12:31.974 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:12:36.979 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:12:36.981 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:12:41.988 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:12:41.990 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:12:47.003 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:12:47.005 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:12:52.004 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:12:52.006 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:12:57.018 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:12:57.020 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:13:02.033 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:13:02.036 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:13:07.040 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:13:07.042 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:13:12.052 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:13:12.054 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:13:17.060 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:13:17.062 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:13:22.073 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:13:22.075 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:13:27.088 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:13:27.090 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:13:32.093 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:13:32.094 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:13:37.103 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:13:37.105 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:13:42.116 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:13:42.118 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:13:47.128 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:13:47.130 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:13:52.139 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:13:52.140 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:13:57.139 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:13:57.141 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:14:02.139 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:14:02.140 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:14:07.150 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:14:07.152 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:14:12.152 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:14:12.155 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:14:17.159 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:14:17.161 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:14:22.166 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:14:22.167 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:14:27.177 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:14:27.178 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:14:32.189 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:14:32.191 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:14:37.198 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:14:37.200 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:14:42.209 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:14:42.211 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:14:47.224 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:14:47.227 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:14:52.231 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:14:52.233 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:14:57.241 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:14:57.243 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:15:02.256 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:15:02.258 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:15:07.256 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:15:07.258 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:15:12.262 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:15:12.264 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:15:17.264 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:15:17.266 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:15:22.274 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:15:22.276 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:15:27.278 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:15:27.280 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:15:32.286 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:15:32.288 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:15:37.296 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:15:37.298 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:15:42.301 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:15:42.304 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:15:47.306 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:15:47.308 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:15:52.312 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:15:52.314 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:15:57.316 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:15:57.318 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:16:02.317 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:16:02.319 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:16:07.333 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:16:07.334 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:16:12.345 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:16:12.348 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:16:17.348 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:16:17.350 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:16:22.357 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:16:22.359 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:16:27.364 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:16:27.366 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:16:32.364 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:16:32.366 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:16:37.374 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:16:37.377 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:16:42.384 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:16:42.386 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:16:47.384 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:16:47.386 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:16:52.399 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:16:52.400 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:16:57.414 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:16:57.416 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:17:02.428 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:17:02.429 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:17:07.432 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:17:07.434 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:17:12.444 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:17:12.446 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:17:17.454 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:17:17.456 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:17:22.460 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:17:22.461 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:17:27.460 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:17:27.462 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:17:32.472 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:17:32.474 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:17:37.474 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:17:37.476 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:17:42.488 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:17:42.490 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:17:47.502 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:17:47.504 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:17:52.514 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:17:52.516 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:17:57.517 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:17:57.519 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:18:02.520 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:18:02.522 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:18:07.526 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:18:07.528 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:18:12.538 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:18:12.540 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:18:17.539 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:18:17.542 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:18:22.552 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:18:22.554 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:18:27.552 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:18:27.554 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:18:32.566 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:18:32.568 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:18:37.576 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:18:37.578 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:18:42.580 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:18:42.582 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:18:47.584 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:18:47.586 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:18:52.592 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:18:52.594 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:18:57.608 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:18:57.610 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:19:02.611 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:19:02.613 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:19:07.614 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:19:07.617 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:19:12.620 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:19:12.621 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:19:17.627 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:19:17.629 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:19:22.640 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:19:22.643 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:19:27.651 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:19:27.653 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:19:32.658 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:19:32.660 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:19:37.659 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:19:37.661 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:19:42.674 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:19:42.676 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:19:47.674 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:19:47.676 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:19:52.681 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:19:52.683 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:19:57.692 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:19:57.694 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:20:02.696 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:20:02.697 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:20:07.708 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:20:07.710 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:20:12.713 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:20:12.717 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:20:17.723 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:20:17.725 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:20:22.731 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:20:22.733 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:20:27.745 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:20:27.747 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:20:32.756 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:20:32.758 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:20:37.762 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:20:37.765 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:20:42.764 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:20:42.766 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:20:47.779 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:20:47.781 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:20:52.794 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:20:52.796 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:20:57.805 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:20:57.807 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:21:02.806 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:21:02.807 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:21:07.816 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:21:07.818 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:21:12.824 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:21:12.826 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:21:17.829 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:21:17.831 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:21:22.842 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:21:22.844 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:21:27.846 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:21:27.848 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:21:32.852 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:21:32.854 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:21:37.866 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:21:37.868 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:21:42.869 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:21:42.871 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:21:47.884 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:21:47.886 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:21:52.891 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:21:52.893 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:21:57.895 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:21:57.897 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:22:02.897 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:22:02.899 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:22:07.897 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:22:07.899 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:22:12.903 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:22:12.905 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:22:17.907 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:22:17.909 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:22:22.917 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:22:22.919 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:22:27.931 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:22:27.933 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:22:32.945 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:22:32.947 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:22:37.959 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:22:37.961 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:22:42.967 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:22:42.968 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:22:47.972 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:22:47.974 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:22:52.987 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:22:52.989 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:22:57.987 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:22:57.989 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:23:02.997 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:23:02.999 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:23:07.997 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:23:07.999 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:23:13.011 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:23:13.013 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:23:18.026 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:23:18.029 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:23:23.034 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:23:23.036 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:23:28.048 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:23:28.050 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:23:33.048 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:23:33.050 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:23:38.057 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:23:38.059 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:23:43.063 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:23:43.066 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:23:48.069 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:23:48.072 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:23:53.083 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:23:53.084 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:23:58.091 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:23:58.092 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:24:03.095 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:24:03.097 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:24:08.109 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:24:08.110 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:24:13.112 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:24:13.114 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:24:18.114 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:24:18.116 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:24:23.123 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:24:23.125 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:24:28.137 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:24:28.139 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:24:33.140 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:24:33.141 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:24:38.153 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:24:38.155 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:24:43.168 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:24:43.169 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:24:48.181 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:24:48.183 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:24:53.195 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:24:53.196 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:24:58.202 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:24:58.204 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:25:03.204 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:25:03.206 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:25:08.204 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:25:08.206 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:25:13.215 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:25:13.217 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:25:18.222 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:25:18.224 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:25:23.234 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:25:23.236 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:25:28.237 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:25:28.239 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:25:33.250 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:25:33.252 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:25:38.258 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:25:38.260 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:25:43.270 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:25:43.273 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:25:48.281 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:25:48.282 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:25:53.288 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:25:53.289 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:25:58.301 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:25:58.303 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:26:03.302 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:26:03.303 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:26:08.312 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:26:08.315 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:26:13.314 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:26:13.316 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:26:18.319 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:26:18.321 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:26:23.332 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:26:23.334 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:26:28.336 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:26:28.337 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:26:33.345 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:26:33.347 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:26:38.360 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:26:38.361 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:26:43.373 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:26:43.375 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:26:48.383 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:26:48.384 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:26:53.384 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:26:53.386 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:26:58.387 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:26:58.389 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:27:03.398 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:27:03.400 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:27:08.410 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:27:08.411 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:27:13.411 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:27:13.413 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:27:18.426 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:27:18.428 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:27:23.430 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:27:23.432 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:27:28.443 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:27:28.444 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:27:33.453 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:27:33.456 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:27:38.466 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:27:38.468 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:27:43.475 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:27:43.477 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:27:48.490 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:27:48.492 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:27:53.500 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:27:53.502 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:27:58.513 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:27:58.515 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:28:03.521 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:28:03.523 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:28:08.525 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:28:08.528 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:28:13.539 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:28:13.541 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:28:18.545 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:28:18.546 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:28:23.546 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:28:23.548 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:28:28.557 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:28:28.559 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:28:33.569 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:28:33.571 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:28:38.572 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:28:38.574 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:28:43.573 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:28:43.575 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:28:48.576 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:28:48.578 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:28:53.581 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:28:53.583 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:28:58.588 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:28:58.590 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:29:03.597 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:29:03.600 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:29:08.603 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:29:08.606 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:29:13.604 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:29:13.606 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:29:18.617 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:29:18.620 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:29:23.618 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:29:23.620 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:29:28.624 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:29:28.626 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:29:33.627 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:29:33.629 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:29:38.634 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:29:38.636 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:29:43.636 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:29:43.638 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:29:48.645 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:29:48.647 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:29:53.650 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:29:53.652 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:29:58.651 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:29:58.654 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:30:03.664 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:30:03.666 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:30:08.671 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:30:08.672 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:30:13.681 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:30:13.683 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:30:18.693 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:30:18.695 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:30:23.700 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:30:23.702 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:30:28.712 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:30:28.713 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:30:33.727 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:30:33.729 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:30:38.735 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:30:38.737 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:30:43.747 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:30:43.749 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:30:48.761 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:30:48.762 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:30:53.764 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:30:53.766 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:30:58.776 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:30:58.778 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:31:03.782 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:31:03.785 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:31:08.786 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:31:08.788 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:31:13.791 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:31:13.793 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:31:18.796 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:31:18.798 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:31:23.807 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:31:23.809 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:31:28.819 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:31:28.821 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:31:33.833 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:31:33.835 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:31:38.847 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:31:38.849 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:31:43.852 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:31:43.854 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:31:48.866 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:31:48.868 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:31:53.879 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:31:53.881 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:31:58.894 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:31:58.897 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:32:03.900 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:32:03.902 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:32:08.903 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:32:08.905 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:32:13.919 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:32:13.921 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:32:18.926 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:32:18.927 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:32:23.941 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:32:23.943 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:32:28.955 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:32:28.957 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:32:33.961 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:32:33.964 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:32:38.963 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:32:38.964 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:32:43.970 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:32:43.972 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:32:48.982 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:32:48.984 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:32:53.994 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:32:53.996 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:32:59.003 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:32:59.006 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:33:04.017 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:33:04.018 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:33:09.017 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:33:09.019 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:33:14.033 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:33:14.035 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:33:19.034 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:33:19.036 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:33:24.046 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:33:24.048 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:33:29.049 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:33:29.051 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:33:34.053 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:33:34.055 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:33:39.065 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:33:39.066 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:33:44.072 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:33:44.074 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:33:49.076 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:33:49.078 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:33:54.082 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:33:54.085 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:33:59.096 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:33:59.098 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:34:04.098 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:34:04.100 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:34:09.100 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:34:09.102 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:34:14.100 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:34:14.103 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:34:19.111 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:34:19.113 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:34:24.116 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:34:24.118 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:34:29.127 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:34:29.130 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:34:34.138 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:34:34.140 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:34:39.141 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:34:39.143 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:34:44.152 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:34:44.154 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:34:49.163 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:34:49.165 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:34:54.169 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:34:54.171 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:34:59.169 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:34:59.171 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:35:04.173 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:35:04.175 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:35:09.181 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:35:09.183 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:35:14.195 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:35:14.196 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:35:19.203 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:35:19.205 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:35:24.217 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:35:24.219 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:35:29.219 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:35:29.221 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:35:34.223 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:35:34.224 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:35:39.238 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:35:39.240 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:35:44.241 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:35:44.244 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:35:49.250 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:35:49.253 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:35:54.253 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:35:54.255 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:35:59.265 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:35:59.267 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:36:04.271 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:36:04.276 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:36:09.276 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:36:09.279 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:36:14.280 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:36:14.282 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:36:19.293 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:36:19.295 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:36:24.305 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:36:24.307 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:36:29.306 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:36:29.308 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:36:34.308 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:36:34.310 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:36:39.319 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:36:39.321 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:36:44.326 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:36:44.327 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:36:49.334 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:36:49.336 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:36:54.335 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:36:54.337 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:36:59.337 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:36:59.339 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:37:04.350 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:37:04.353 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:37:09.349 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:37:09.350 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:37:14.360 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:37:14.362 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:37:19.369 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:37:19.371 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:37:24.374 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:37:24.376 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:37:29.387 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:37:29.389 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:37:34.389 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:37:34.391 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:37:39.389 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:37:39.391 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:37:44.393 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:37:44.395 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:37:49.396 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:37:49.398 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:37:54.405 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:37:54.407 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:37:59.418 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:37:59.420 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:38:04.423 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:38:04.424 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:38:09.435 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:38:09.437 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:38:14.436 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:38:14.438 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:38:19.438 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:38:19.439 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:38:24.443 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:38:24.444 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:38:29.453 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:38:29.456 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:38:34.468 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:38:34.469 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:38:39.483 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:38:39.484 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:38:44.485 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:38:44.486 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:38:49.488 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:38:49.490 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:38:54.488 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:38:54.490 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:38:59.502 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:38:59.504 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:39:04.515 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:39:04.517 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:39:09.528 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:39:09.529 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:39:14.540 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:39:14.541 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:39:19.547 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:39:19.549 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:39:24.552 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:39:24.554 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:39:29.562 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:39:29.563 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:39:34.569 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:39:34.571 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:39:39.579 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:39:39.581 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:39:44.579 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:39:44.581 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:39:49.580 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:39:49.582 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:39:54.595 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:39:54.596 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:39:59.600 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:39:59.602 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:40:04.602 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:40:04.604 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:40:09.613 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:40:09.615 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:40:14.620 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:40:14.622 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:40:19.620 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:40:19.622 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:40:24.632 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:40:24.634 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:40:29.636 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:40:29.638 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:40:34.649 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:40:34.650 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:40:39.651 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:40:39.653 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:40:44.660 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:40:44.662 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:40:49.664 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:40:49.666 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:40:54.672 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:40:54.673 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:40:59.682 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:40:59.683 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:41:04.691 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:41:04.692 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:41:09.698 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:41:09.700 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:41:14.699 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:41:14.701 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:41:19.710 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:41:19.712 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:41:24.713 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:41:24.715 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:41:29.719 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:41:29.720 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:41:34.733 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:41:34.735 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:41:39.740 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:41:39.742 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:41:44.749 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:41:44.751 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:41:49.755 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:41:49.757 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:41:54.763 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:41:54.765 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:41:59.770 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:41:59.772 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:42:04.770 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:42:04.772 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:42:09.774 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:42:09.776 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:42:14.777 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:42:14.779 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:42:19.783 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:42:19.785 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:42:24.786 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:42:24.788 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:42:29.787 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:42:29.788 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:42:34.796 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:42:34.797 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:42:39.796 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:42:39.797 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:42:44.810 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:42:44.811 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:42:49.821 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:42:49.823 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:42:54.824 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:42:54.826 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:42:59.837 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:42:59.839 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:43:04.845 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:43:04.846 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:43:09.848 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:43:09.849 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:43:14.853 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:43:14.854 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:43:19.855 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:43:19.856 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:43:24.856 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:43:24.858 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:43:29.866 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:43:29.868 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:43:34.868 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:43:34.869 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:43:39.882 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:43:39.884 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:43:44.882 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:43:44.884 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:43:49.894 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:43:49.896 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:43:54.896 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:43:54.897 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:43:59.902 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:43:59.904 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:44:04.916 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:44:04.918 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:44:09.918 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:44:09.921 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:44:14.923 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:44:14.925 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:44:19.927 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:44:19.930 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:44:24.941 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:44:24.943 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:44:29.952 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:44:29.954 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:44:34.962 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:44:34.964 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:44:39.971 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:44:39.973 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:44:44.974 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:44:44.975 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:44:49.975 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:44:49.977 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:44:54.984 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:44:54.985 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:44:59.993 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:44:59.995 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:45:04.996 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:45:04.998 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:45:10.000 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:45:10.002 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:45:15.004 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:45:15.006 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:45:20.018 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:45:20.020 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:45:25.023 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:45:25.025 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:45:30.035 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:45:30.037 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:45:35.042 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:45:35.044 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:45:40.043 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:45:40.044 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:45:45.048 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:45:45.049 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:45:50.061 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:45:50.063 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:45:55.071 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:45:55.073 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:46:00.082 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:46:00.083 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:46:05.093 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:46:05.095 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:46:10.102 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:46:10.104 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:46:15.111 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:46:15.113 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:46:20.111 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:46:20.113 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:46:25.126 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:46:25.128 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:46:30.137 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:46:30.140 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:46:35.147 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:46:35.149 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:46:40.148 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:46:40.150 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:46:45.162 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:46:45.164 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:46:50.166 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:46:50.171 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:46:55.168 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:46:55.172 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:47:00.171 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:47:00.174 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:47:05.182 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:47:05.183 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:47:10.195 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:47:10.196 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:47:15.196 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:47:15.198 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:47:20.205 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:47:20.207 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:47:25.209 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:47:25.211 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:47:30.211 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:47:30.213 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:47:35.216 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:47:35.218 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:47:40.224 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:47:40.225 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:47:45.238 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:47:45.240 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:47:50.252 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:47:50.254 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:47:55.253 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:47:55.266 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:48:00.268 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:48:00.270 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:48:05.283 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:48:05.285 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:48:10.292 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:48:10.294 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:48:15.302 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:48:15.304 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:48:20.310 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:48:20.312 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:48:25.315 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:48:25.316 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:48:30.329 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:48:30.331 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:48:35.342 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:48:35.344 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:48:40.350 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:48:40.352 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:48:45.357 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:48:45.359 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:48:50.367 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:48:50.368 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:48:55.381 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:48:55.383 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:49:00.393 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:49:00.395 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:49:05.402 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:49:05.404 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:49:10.416 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:49:10.419 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:49:15.420 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:49:15.421 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:49:20.427 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:49:20.429 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:49:25.429 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:49:25.431 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:49:30.441 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:49:30.443 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:49:35.448 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:49:35.450 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:49:40.448 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:49:40.450 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:49:45.453 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:49:45.455 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:49:50.458 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:49:50.460 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:49:55.460 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:49:55.462 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:50:00.461 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:50:00.462 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:50:05.464 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:50:05.466 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:50:10.478 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:50:10.479 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:50:15.480 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:50:15.482 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:50:20.481 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:50:20.483 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:50:25.489 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:50:25.491 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:50:30.495 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:50:30.497 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:50:35.510 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:50:35.512 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:50:40.525 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:50:40.527 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:50:45.530 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:50:45.532 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:50:50.533 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:50:50.535 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:50:55.540 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:50:55.542 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:51:00.544 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:51:00.545 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:51:05.547 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:51:05.548 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:51:10.562 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:51:10.565 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:51:15.562 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:51:15.564 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:51:20.564 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:51:20.566 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:51:25.567 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:51:25.570 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:51:30.568 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:51:30.570 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:51:35.577 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:51:35.579 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:51:40.577 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:51:40.579 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:51:45.588 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:51:45.590 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:51:50.603 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:51:50.605 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:51:55.616 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:51:55.618 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:52:00.624 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:52:00.627 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:52:05.628 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:52:05.630 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:52:10.628 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:52:10.630 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:52:15.629 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:52:15.631 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:52:20.643 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:52:20.645 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:52:25.656 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:52:25.658 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:52:30.663 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:52:30.665 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:52:35.675 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:52:35.677 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:52:40.680 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:52:40.681 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:52:45.688 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:52:45.690 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:52:50.690 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:52:50.692 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:52:55.700 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:52:55.702 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:53:00.707 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:53:00.708 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:53:05.723 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:53:05.724 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:53:10.723 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:53:10.725 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:53:15.733 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:53:15.734 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:53:20.734 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:53:20.736 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:53:25.742 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:53:25.744 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:53:30.756 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:53:30.758 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:53:35.758 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:53:35.760 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:53:40.765 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:53:40.767 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:53:45.771 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:53:45.773 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:53:50.773 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:53:50.775 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:53:55.784 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:53:55.786 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:54:00.787 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:54:00.788 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:54:05.796 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:54:05.798 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:54:10.805 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:54:10.806 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:54:15.816 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:54:15.817 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:54:20.821 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:54:20.823 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:54:25.825 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:54:25.826 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:54:30.826 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:54:30.827 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:54:35.829 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:54:35.830 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:54:40.839 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:54:40.841 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:54:45.840 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:54:45.841 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:54:50.848 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:54:50.850 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:54:55.863 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:54:55.865 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:55:00.863 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:55:00.865 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:55:05.872 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:55:05.874 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:55:10.876 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:55:10.877 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:55:15.885 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:55:15.886 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:55:20.893 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:55:20.894 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:55:25.898 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:55:25.900 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:55:30.908 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:55:30.910 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:55:35.910 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:55:35.912 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:55:40.913 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:55:40.915 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:55:45.928 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:55:45.930 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:55:50.929 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:55:50.931 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:55:55.932 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:55:55.934 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:56:00.944 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:56:00.945 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:56:05.947 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:56:05.949 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:56:10.957 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:56:10.959 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:56:15.965 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:56:15.967 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:56:20.975 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:56:20.977 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:56:25.985 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:56:25.986 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:56:30.987 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:56:30.988 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:56:36.002 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:56:36.004 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:56:41.008 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:56:41.010 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:56:46.020 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:56:46.022 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:56:51.033 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:56:51.035 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:56:56.047 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:56:56.050 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:57:01.054 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:57:01.057 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:57:06.065 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:57:06.067 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:57:11.071 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:57:11.073 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:57:16.078 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:57:16.080 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:57:21.092 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:57:21.094 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:57:26.096 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:57:26.097 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:57:31.107 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:57:31.109 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:57:36.109 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:57:36.111 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:57:41.123 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:57:41.125 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:57:46.125 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:57:46.127 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:57:51.127 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:57:51.130 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:57:56.142 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:57:56.145 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:58:01.149 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:58:01.151 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:58:06.155 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:58:06.157 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:58:11.159 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:58:11.161 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:58:16.174 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:58:16.176 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:58:21.182 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:58:21.184 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:58:26.186 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:58:26.188 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:58:31.191 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:58:31.193 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:58:36.201 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:58:36.203 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:58:41.213 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:58:41.215 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:58:46.219 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:58:46.221 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:58:51.223 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:58:51.225 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:58:56.225 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:58:56.227 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:59:01.232 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:59:01.234 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:59:06.245 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:59:06.247 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:59:11.251 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:59:11.253 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:59:16.265 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:59:16.267 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:59:21.279 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:59:21.281 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:59:26.295 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:59:26.297 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:59:31.297 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:59:31.299 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:59:36.299 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:59:36.300 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:59:41.303 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:59:41.305 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:59:46.310 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:59:46.312 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:59:51.315 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:59:51.316 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 11:59:56.328 +0800] DEBUG: Event registry already initialized -[2026-08-26 11:59:56.330 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:00:01.330 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:00:01.331 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:00:06.339 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:00:06.341 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:00:11.341 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:00:11.343 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:00:16.344 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:00:16.346 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:00:21.359 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:00:21.361 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:00:26.359 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:00:26.362 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:00:31.367 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:00:31.369 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:00:36.380 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:00:36.382 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:00:41.386 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:00:41.387 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:00:46.399 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:00:46.401 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:00:51.404 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:00:51.406 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:00:56.411 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:00:56.413 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:01:01.416 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:01:01.418 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:01:06.420 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:01:06.422 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:01:11.427 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:01:11.429 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:01:16.441 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:01:16.443 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:01:21.444 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:01:21.446 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:01:26.458 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:01:26.460 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:01:31.462 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:01:31.464 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:01:36.476 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:01:36.478 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:01:41.479 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:01:41.481 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:01:46.485 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:01:46.487 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:01:51.498 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:01:51.500 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:01:56.501 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:01:56.503 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:02:01.508 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:02:01.509 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:02:06.516 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:02:06.518 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:02:11.521 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:02:11.523 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:02:16.531 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:02:16.534 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:02:21.542 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:02:21.544 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:02:26.556 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:02:26.558 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:02:31.557 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:02:31.559 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:02:36.568 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:02:36.570 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:02:41.571 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:02:41.573 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:02:46.580 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:02:46.582 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:02:51.590 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:02:51.592 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:02:56.594 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:02:56.596 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:03:01.608 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:03:01.611 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:03:06.613 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:03:06.615 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:03:11.621 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:03:11.623 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:03:16.628 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:03:16.630 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:03:21.641 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:03:21.642 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:03:26.649 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:03:26.651 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:03:31.653 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:03:31.656 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:03:36.666 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:03:36.667 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:03:41.672 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:03:41.674 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:03:46.678 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:03:46.679 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:03:51.685 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:03:51.687 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:03:56.698 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:03:56.700 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:04:01.701 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:04:01.703 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:04:06.706 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:04:06.708 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:04:11.719 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:04:11.721 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:04:16.722 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:04:16.724 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:04:21.733 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:04:21.735 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:04:26.743 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:04:26.745 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:04:31.753 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:04:31.755 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:04:36.754 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:04:36.756 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:04:41.755 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:04:41.757 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:04:46.770 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:04:46.772 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:04:51.783 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:04:51.785 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:04:56.786 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:04:56.788 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:05:01.795 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:05:01.798 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:05:06.801 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:05:06.803 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:05:11.816 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:05:11.818 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:05:16.818 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:05:16.820 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:05:21.818 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:05:21.820 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:05:26.820 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:05:26.822 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:05:31.823 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:05:31.825 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:05:36.835 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:05:36.837 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:05:41.836 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:05:41.838 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:05:46.846 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:05:46.849 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:05:51.847 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:05:51.849 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:05:56.853 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:05:56.855 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:06:01.868 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:06:01.870 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:06:06.869 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:06:06.871 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:06:11.880 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:06:11.883 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:06:16.884 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:06:16.886 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:06:21.891 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:06:21.893 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:06:26.893 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:06:26.895 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:06:31.895 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:06:31.897 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:06:36.902 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:06:36.905 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:06:41.913 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:06:41.915 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:06:46.919 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:06:46.921 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:06:51.926 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:06:51.929 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:06:56.935 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:06:56.937 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:07:01.948 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:07:01.950 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:07:06.958 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:07:06.960 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:07:11.966 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:07:11.968 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:07:16.971 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:07:16.973 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:07:21.972 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:07:21.974 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:07:26.984 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:07:26.985 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:07:31.985 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:07:31.988 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:07:36.991 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:07:36.993 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:07:42.005 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:07:42.006 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:07:47.015 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:07:47.016 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:07:52.019 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:07:52.020 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:07:57.034 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:07:57.036 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:08:02.043 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:08:02.045 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:08:07.056 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:08:07.058 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:08:12.068 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:08:12.070 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:08:17.071 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:08:17.073 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:08:22.086 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:08:22.088 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:08:27.086 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:08:27.088 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:08:32.089 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:08:32.091 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:08:37.091 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:08:37.093 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:08:42.107 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:08:42.109 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:08:47.118 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:08:47.120 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:08:52.131 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:08:52.132 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:08:57.137 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:08:57.139 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:09:02.151 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:09:02.153 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:09:07.160 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:09:07.163 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:09:12.163 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:09:12.165 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:09:17.172 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:09:17.174 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:09:22.181 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:09:22.183 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:09:27.188 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:09:27.191 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:09:32.200 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:09:32.202 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:09:37.204 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:09:37.206 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:09:42.217 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:09:42.220 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:09:47.219 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:09:47.221 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:09:52.230 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:09:52.233 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:09:57.242 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:09:57.245 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:10:02.251 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:10:02.253 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:10:07.255 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:10:07.257 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:10:12.261 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:10:12.263 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:10:17.262 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:10:17.265 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:10:22.271 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:10:22.273 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:10:27.285 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:10:27.287 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:10:32.288 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:10:32.290 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:10:37.290 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:10:37.293 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:10:42.298 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:10:42.299 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:10:47.302 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:10:47.304 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:10:52.308 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:10:52.309 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:10:57.312 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:10:57.314 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:11:02.317 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:11:02.319 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:11:07.322 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:11:07.324 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:11:12.328 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:11:12.330 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:11:17.341 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:11:17.344 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:11:22.343 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:11:22.345 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:11:27.356 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:11:27.357 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:11:32.365 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:11:32.366 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:11:37.373 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:11:37.375 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:11:42.382 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:11:42.384 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:11:47.395 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:11:47.396 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:11:52.403 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:11:52.405 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:11:57.419 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:11:57.420 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:12:02.423 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:12:02.424 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:12:07.430 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:12:07.432 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:12:12.438 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:12:12.440 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:12:17.449 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:12:17.450 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:12:22.462 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:12:22.464 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:12:27.468 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:12:27.470 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:12:32.472 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:12:32.474 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:12:37.472 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:12:37.474 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:12:42.480 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:12:42.482 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:12:47.486 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:12:47.488 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:12:52.496 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:12:52.498 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:12:57.497 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:12:57.499 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:13:02.508 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:13:02.510 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:13:07.514 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:13:07.516 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:13:12.526 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:13:12.528 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:13:17.526 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:13:17.528 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:13:22.532 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:13:22.534 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:13:27.544 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:13:27.546 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:13:32.556 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:13:32.559 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:13:37.571 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:13:37.573 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:13:42.576 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:13:42.578 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:13:47.591 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:13:47.593 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:13:52.604 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:13:52.606 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:13:57.607 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:13:57.610 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:14:02.614 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:14:02.616 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:14:07.618 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:14:07.620 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:14:12.630 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:14:12.631 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:14:17.645 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:14:17.648 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:14:22.648 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:14:22.650 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:14:27.659 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:14:27.661 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:14:32.671 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:14:32.672 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:14:37.679 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:14:37.681 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:14:42.680 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:14:42.682 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:14:47.682 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:14:47.684 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:14:52.685 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:14:52.687 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:14:57.695 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:14:57.697 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:15:02.700 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:15:02.702 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:15:07.708 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:15:07.710 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:15:12.722 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:15:12.723 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:15:17.727 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:15:17.729 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:15:22.739 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:15:22.741 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:15:27.744 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:15:27.746 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:15:32.760 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:15:32.761 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:15:37.760 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:15:37.762 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:15:42.771 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:15:42.773 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:15:47.780 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:15:47.782 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:15:52.786 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:15:52.788 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:15:57.798 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:15:57.800 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:16:02.808 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:16:02.810 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:16:07.809 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:16:07.811 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:16:12.818 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:16:12.820 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:16:17.819 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:16:17.821 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:16:22.824 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:16:22.825 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:16:27.833 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:16:27.835 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:16:32.837 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:16:32.838 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:16:37.839 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:16:37.841 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:16:42.843 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:16:42.845 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:16:47.850 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:16:47.852 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:16:52.853 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:16:52.855 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:16:57.858 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:16:57.860 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:17:02.868 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:17:02.870 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:17:07.872 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:17:07.874 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:17:12.877 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:17:12.879 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:17:17.880 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:17:17.882 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:17:22.890 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:17:22.893 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:17:27.898 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:17:27.900 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:17:32.903 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:17:32.905 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:17:37.918 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:17:37.920 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:17:42.927 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:17:42.929 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:17:47.940 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:17:47.942 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:17:52.945 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:17:52.947 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:17:57.950 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:17:57.952 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:18:02.957 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:18:02.959 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:18:07.959 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:18:07.961 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:18:12.974 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:18:12.976 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:18:17.988 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:18:17.991 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:18:22.997 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:18:23.000 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:18:27.999 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:18:28.001 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:18:33.006 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:18:33.008 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:18:38.012 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:18:38.014 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:18:43.025 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:18:43.027 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:18:48.027 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:18:48.029 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:18:53.028 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:18:53.030 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:18:58.033 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:18:58.035 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:19:03.035 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:19:03.037 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:19:08.044 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:19:08.046 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:19:13.056 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:19:13.059 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:19:18.070 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:19:18.072 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:19:23.072 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:19:23.075 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:19:28.074 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:19:28.076 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:19:33.081 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:19:33.083 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:19:38.087 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:19:38.089 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:19:43.093 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:19:43.095 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:19:48.096 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:19:48.098 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:19:53.100 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:19:53.102 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:19:58.105 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:19:58.106 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:20:03.119 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:20:03.121 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:20:08.127 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:20:08.130 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:20:13.142 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:20:13.144 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:20:18.147 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:20:18.149 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:20:23.147 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:20:23.149 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:20:28.160 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:20:28.162 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:20:33.163 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:20:33.165 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:20:38.173 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:20:38.175 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:20:43.175 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:20:43.177 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:20:48.183 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:20:48.185 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:20:53.198 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:20:53.200 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:20:58.201 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:20:58.202 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:21:03.204 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:21:03.205 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:21:08.207 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:21:08.208 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:21:13.211 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:21:13.213 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:21:18.215 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:21:18.217 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:21:23.216 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:21:23.219 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:21:28.225 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:21:28.228 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:21:33.233 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:21:33.235 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:21:38.237 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:21:38.239 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:21:43.252 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:21:43.254 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:21:48.252 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:21:48.254 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:21:53.262 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:21:53.264 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:21:58.265 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:21:58.267 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:22:03.267 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:22:03.269 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:22:08.276 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:22:08.278 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:22:13.282 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:22:13.283 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:22:18.289 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:22:18.292 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:22:23.298 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:22:23.301 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:22:28.302 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:22:28.303 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:22:33.309 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:22:33.310 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:22:38.322 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:22:38.325 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:22:43.335 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:22:43.338 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:22:48.346 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:22:48.348 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:22:53.350 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:22:53.352 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:22:58.356 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:22:58.358 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:23:03.358 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:23:03.360 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:23:08.360 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:23:08.362 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:23:13.366 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:23:13.368 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:23:18.367 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:23:18.369 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:23:23.377 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:23:23.379 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:23:28.383 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:23:28.385 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:23:33.395 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:23:33.398 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:23:38.410 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:23:38.411 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:23:43.416 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:23:43.418 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:23:48.425 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:23:48.427 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:23:53.430 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:23:53.432 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:23:58.431 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:23:58.433 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:24:03.444 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:24:03.445 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:24:08.452 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:24:08.454 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:24:13.455 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:24:13.457 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:24:18.462 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:24:18.464 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:24:23.463 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:24:23.465 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:24:28.476 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:24:28.478 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:24:33.480 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:24:33.482 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:24:38.488 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:24:38.490 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:24:43.498 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:24:43.500 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:24:48.501 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:24:48.503 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:24:53.513 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:24:53.515 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:24:58.517 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:24:58.519 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:25:03.525 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:25:03.527 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:25:08.531 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:25:08.533 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:25:13.544 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:25:13.546 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:25:18.559 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:25:18.560 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:25:23.574 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:25:23.576 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:25:28.579 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:25:28.580 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:25:33.593 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:25:33.596 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:25:38.608 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:25:38.610 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:25:43.608 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:25:43.610 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:25:48.611 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:25:48.613 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:25:53.619 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:25:53.621 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:25:58.621 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:25:58.623 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:26:03.623 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:26:03.625 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:26:08.626 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:26:08.628 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:26:13.631 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:26:13.633 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:26:18.638 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:26:18.640 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:26:23.644 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:26:23.646 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:26:28.645 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:26:28.646 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:26:33.646 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:26:33.647 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:26:38.653 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:26:38.655 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:26:43.658 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:26:43.662 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:26:48.665 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:26:48.667 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:26:53.677 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:26:53.679 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:26:58.680 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:26:58.682 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:27:03.683 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:27:03.685 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:27:08.688 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:27:08.690 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:27:13.703 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:27:13.705 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:27:18.708 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:27:18.710 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:27:23.713 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:27:23.714 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:27:28.719 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:27:28.721 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:27:33.725 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:27:33.726 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:27:38.725 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:27:38.727 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:27:43.736 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:27:43.737 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:27:48.738 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:27:48.741 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:27:53.753 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:27:53.756 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:27:58.766 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:27:58.770 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:28:03.777 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:28:03.779 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:28:08.782 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:28:08.784 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:28:13.787 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:28:13.789 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:28:18.800 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:28:18.801 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:28:23.801 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:28:23.803 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:28:28.805 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:28:28.807 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:28:33.812 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:28:33.814 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:28:38.820 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:28:38.821 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:28:43.824 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:28:43.826 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:28:48.836 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:28:48.838 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:28:53.850 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:28:53.852 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:28:58.853 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:28:58.855 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:29:03.864 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:29:03.866 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:29:08.867 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:29:08.868 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:29:13.877 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:29:13.879 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:29:18.877 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:29:18.880 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:29:23.892 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:29:23.895 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:29:28.899 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:29:28.900 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:29:33.904 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:29:33.906 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:29:38.910 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:29:38.913 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:29:43.915 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:29:43.917 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:29:48.922 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:29:48.924 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:29:53.931 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:29:53.932 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:29:58.942 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:29:58.943 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:30:03.951 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:30:03.953 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:30:08.959 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:30:08.961 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:30:13.971 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:30:13.972 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:30:18.975 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:30:18.977 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:30:23.978 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:30:23.980 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:30:28.992 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:30:28.993 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:30:33.993 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:30:33.995 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:30:38.994 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:30:38.995 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:30:44.007 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:30:44.008 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:30:49.007 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:30:49.009 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:30:54.019 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:30:54.021 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:30:59.019 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:30:59.021 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:31:04.035 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:31:04.036 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:31:09.040 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:31:09.041 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:31:14.055 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:31:14.057 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:31:19.068 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:31:19.069 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:31:24.072 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:31:24.074 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:31:29.080 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:31:29.082 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:31:34.083 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:31:34.085 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:31:39.096 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:31:39.098 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:31:44.109 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:31:44.111 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:31:49.116 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:31:49.118 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:31:54.128 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:31:54.129 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:31:59.133 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:31:59.134 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:32:04.143 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:32:04.145 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:32:09.150 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:32:09.152 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:32:14.159 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:32:14.161 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:32:19.168 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:32:19.170 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:32:24.168 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:32:24.170 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:32:29.175 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:32:29.176 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:32:34.177 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:32:34.180 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:32:39.183 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:32:39.185 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:32:44.194 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:32:44.196 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:32:49.196 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:32:49.198 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:32:54.201 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:32:54.203 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:32:59.207 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:32:59.209 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:33:04.212 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:33:04.213 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:33:09.218 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:33:09.220 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:33:14.228 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:33:14.230 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:33:19.240 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:33:19.242 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:33:24.245 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:33:24.247 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:33:29.255 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:33:29.258 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:33:34.259 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:33:34.260 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:33:39.273 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:33:39.274 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:33:44.284 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:33:44.286 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:33:49.293 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:33:49.295 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:33:54.304 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:33:54.306 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:33:59.310 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:33:59.313 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:34:04.315 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:34:04.317 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:34:09.319 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:34:09.321 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:34:14.332 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:34:14.333 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:34:19.346 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:34:19.348 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:34:24.347 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:34:24.348 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:34:29.351 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:34:29.353 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:34:34.364 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:34:34.366 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:34:39.376 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:34:39.378 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:34:44.378 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:34:44.379 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:34:49.384 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:34:49.386 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:34:54.395 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:34:54.396 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:34:59.402 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:34:59.403 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:35:04.406 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:35:04.407 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:35:09.410 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:35:09.412 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:35:14.420 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:35:14.421 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:35:19.427 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:35:19.429 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:35:24.441 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:35:24.442 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:35:29.456 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:35:29.458 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:35:34.462 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:35:34.463 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:35:39.462 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:35:39.464 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:35:44.469 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:35:44.471 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:35:49.470 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:35:49.472 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:35:54.482 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:35:54.484 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:35:59.494 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:35:59.495 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:36:04.509 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:36:04.510 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:36:09.519 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:36:09.521 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:36:14.522 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:36:14.524 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:36:19.527 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:36:19.529 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:36:24.531 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:36:24.532 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:36:29.545 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:36:29.546 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:36:34.551 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:36:34.553 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:36:39.563 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:36:39.565 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:36:44.574 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:36:44.576 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:36:49.574 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:36:49.576 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:36:54.581 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:36:54.583 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:36:59.587 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:36:59.589 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:37:04.588 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:37:04.589 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:37:09.596 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:37:09.598 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:37:14.603 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:37:14.605 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:37:19.614 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:37:19.616 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:37:24.617 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:37:24.620 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:37:29.620 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:37:29.622 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:37:34.633 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:37:34.635 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:37:39.642 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:37:39.643 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:37:44.655 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:37:44.657 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:37:49.663 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:37:49.665 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:37:54.676 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:37:54.678 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:37:59.683 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:37:59.685 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:38:04.687 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:38:04.690 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:38:09.698 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:38:09.699 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:38:14.704 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:38:14.705 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:38:19.706 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:38:19.707 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:38:24.711 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:38:24.712 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:38:29.715 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:38:29.717 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:38:34.722 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:38:34.724 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:38:39.727 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:38:39.729 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:38:44.733 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:38:44.735 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:38:49.741 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:38:49.743 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:38:54.749 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:38:54.751 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:38:59.751 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:38:59.753 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:39:04.756 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:39:04.759 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:39:09.767 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:39:09.769 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:39:14.772 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:39:14.774 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:39:19.775 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:39:19.776 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:39:24.775 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:39:24.776 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:39:29.784 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:39:29.785 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:39:34.792 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:39:34.794 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:39:39.806 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:39:39.808 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:39:44.819 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:39:44.821 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:39:49.825 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:39:49.827 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:39:54.833 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:39:54.835 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:39:59.839 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:39:59.840 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:40:04.852 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:40:04.853 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:40:09.856 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:40:09.859 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:40:14.864 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:40:14.866 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:40:19.879 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:40:19.881 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:40:24.893 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:40:24.894 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:40:29.908 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:40:29.910 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:40:34.922 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:40:34.924 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:40:39.929 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:40:39.931 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:40:44.930 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:40:44.932 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:40:49.935 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:40:49.938 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:40:54.938 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:40:54.940 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:40:59.941 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:40:59.943 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:41:04.950 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:41:04.951 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:41:09.955 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:41:09.957 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:41:14.963 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:41:14.965 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:41:19.971 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:41:19.973 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:41:24.979 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:41:24.980 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:41:29.990 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:41:29.991 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:41:34.995 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:41:34.997 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:41:40.002 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:41:40.004 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:41:45.012 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:41:45.014 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:41:50.026 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:41:50.027 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:41:55.037 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:41:55.039 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:42:00.045 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:42:00.046 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:42:05.058 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:42:05.060 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:42:10.073 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:42:10.074 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:42:15.075 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:42:15.077 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:42:20.076 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:42:20.078 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:42:25.077 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:42:25.079 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:42:30.092 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:42:30.093 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:42:35.100 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:42:35.102 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:42:40.102 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:42:40.103 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:42:45.108 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:42:45.110 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:42:50.120 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:42:50.122 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:42:55.129 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:42:55.131 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:43:00.140 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:43:00.141 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:43:05.151 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:43:05.153 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:43:10.159 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:43:10.161 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:43:15.163 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:43:15.165 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:43:20.179 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:43:20.181 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:43:25.181 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:43:25.183 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:43:30.183 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:43:30.185 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:43:35.186 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:43:35.189 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:43:40.197 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:43:40.198 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:43:45.201 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:43:45.202 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:43:50.208 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:43:50.211 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:43:55.210 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:43:55.212 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:44:00.218 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:44:00.219 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:44:05.222 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:44:05.223 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:44:10.228 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:44:10.230 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:44:15.237 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:44:15.239 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:44:20.250 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:44:20.252 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:44:25.255 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:44:25.257 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:44:30.258 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:44:30.261 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:44:35.261 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:44:35.262 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:44:40.275 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:44:40.277 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:44:45.290 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:44:45.292 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:44:50.294 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:44:50.296 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:44:55.304 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:44:55.306 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:45:00.315 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:45:00.317 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:45:05.318 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:45:05.320 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:45:10.330 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:45:10.332 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:45:15.339 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:45:15.341 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:45:20.348 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:45:20.350 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:45:25.362 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:45:25.363 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:45:30.365 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:45:30.367 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:45:35.371 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:45:35.373 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:45:40.385 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:45:40.387 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:45:45.395 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:45:45.397 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:45:50.397 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:45:50.398 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:45:55.398 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:45:55.401 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:46:00.399 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:46:00.401 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:46:05.409 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:46:05.411 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:46:10.410 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:46:10.412 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:46:15.411 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:46:15.413 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:46:20.418 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:46:20.420 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:46:25.424 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:46:25.426 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:46:30.432 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:46:30.434 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:46:35.445 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:46:35.448 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:46:40.459 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:46:40.461 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:46:45.473 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:46:45.475 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:46:50.475 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:46:50.477 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:46:55.483 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:46:55.485 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:47:00.490 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:47:00.492 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:47:05.497 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:47:05.499 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:47:10.510 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:47:10.511 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:47:15.521 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:47:15.523 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:47:20.526 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:47:20.529 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:47:25.532 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:47:25.533 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:47:30.536 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:47:30.538 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:47:35.550 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:47:35.552 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:47:40.559 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:47:40.561 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:47:45.563 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:47:45.565 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:47:50.568 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:47:50.570 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:47:55.574 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:47:55.576 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:48:00.580 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:48:00.582 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:48:05.588 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:48:05.589 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:48:10.599 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:48:10.601 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:48:15.613 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:48:15.615 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:48:20.616 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:48:20.619 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:48:25.623 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:48:25.625 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:48:30.625 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:48:30.627 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:48:35.628 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:48:35.630 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:48:40.635 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:48:40.637 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:48:45.638 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:48:45.640 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:48:50.647 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:48:50.650 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:48:55.662 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:48:55.664 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:49:00.664 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:49:00.666 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:49:05.674 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:49:05.675 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:49:10.688 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:49:10.690 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:49:15.703 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:49:15.705 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:49:20.713 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:49:20.715 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:49:25.718 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:49:25.721 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:49:30.728 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:49:30.730 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:49:35.734 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:49:35.736 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:49:40.742 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:49:40.744 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:49:45.747 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:49:45.749 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:49:50.752 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:49:50.753 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:49:55.763 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:49:55.765 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:50:00.764 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:50:00.765 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:50:05.772 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:50:05.774 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:50:10.783 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:50:10.785 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:50:15.783 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:50:15.785 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:50:20.783 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:50:20.785 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:50:25.795 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:50:25.797 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:50:30.804 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:50:30.806 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:50:35.816 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:50:35.818 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:50:40.822 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:50:40.823 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:50:45.836 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:50:45.838 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:50:50.850 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:50:50.851 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:50:55.860 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:50:55.863 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:51:00.866 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:51:00.868 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:51:05.874 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:51:05.876 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:51:10.880 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:51:10.882 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:51:15.887 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:51:15.888 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:51:20.888 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:51:20.890 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:51:25.902 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:51:25.904 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:51:30.906 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:51:30.908 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:51:35.911 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:51:35.913 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:51:40.922 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:51:40.923 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:51:45.923 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:51:45.926 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:51:50.924 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:51:50.926 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:51:55.936 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:51:55.938 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:52:00.938 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:52:00.940 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:52:05.944 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:52:05.946 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:52:10.945 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:52:10.946 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:52:15.958 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:52:15.960 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:52:20.971 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:52:20.973 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:52:25.985 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:52:25.986 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:52:30.987 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:52:30.989 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:52:35.990 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:52:35.992 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:52:40.998 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:52:41.000 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:52:46.001 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:52:46.003 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:52:51.010 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:52:51.011 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:52:56.018 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:52:56.020 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:53:01.029 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:53:01.030 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:53:06.032 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:53:06.034 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:53:11.045 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:53:11.047 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:53:16.059 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:53:16.061 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:53:21.069 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:53:21.071 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:53:26.079 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:53:26.080 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:53:31.094 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:53:31.096 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:53:36.097 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:53:36.099 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:53:41.107 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:53:41.109 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:53:46.111 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:53:46.114 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:53:51.116 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:53:51.118 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:53:56.119 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:53:56.121 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:54:01.123 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:54:01.125 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:54:06.123 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:54:06.125 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:54:11.133 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:54:11.135 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:54:16.139 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:54:16.140 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:54:21.145 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:54:21.147 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:54:26.150 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:54:26.152 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:54:31.159 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:54:31.161 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:54:36.172 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:54:36.174 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:54:41.182 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:54:41.183 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:54:46.186 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:54:46.188 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:54:51.195 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:54:51.196 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:54:56.208 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:54:56.210 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:55:01.221 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:55:01.223 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:55:06.230 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:55:06.232 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:55:11.236 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:55:11.238 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:55:16.242 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:55:16.244 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:55:21.256 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:55:21.258 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:55:26.258 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:55:26.260 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:55:31.263 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:55:31.264 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:55:36.273 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:55:36.275 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:55:41.276 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:55:41.279 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:55:46.282 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:55:46.283 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:55:51.293 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:55:51.294 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:55:56.303 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:55:56.305 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:56:01.309 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:56:01.310 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:56:06.310 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:56:06.312 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:56:11.318 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:56:11.321 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:56:16.320 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:56:16.323 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:56:21.325 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:56:21.329 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:56:26.334 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:56:26.336 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:56:31.336 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:56:31.338 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:56:36.349 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:56:36.350 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:56:41.350 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:56:41.351 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:56:46.352 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:56:46.354 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:56:51.362 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:56:51.363 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:56:56.373 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:56:56.376 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:57:01.373 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:57:01.375 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:57:06.387 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:57:06.389 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:57:11.391 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:57:11.393 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:57:16.398 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:57:16.399 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:57:21.407 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:57:21.409 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:57:26.422 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:57:26.423 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:57:31.428 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:57:31.430 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:57:36.441 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:57:36.443 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:57:41.455 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:57:41.457 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:57:46.464 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:57:46.466 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:57:51.470 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:57:51.471 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:57:56.479 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:57:56.480 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:58:01.481 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:58:01.483 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:58:06.483 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:58:06.485 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:58:11.486 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:58:11.488 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:58:16.499 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:58:16.501 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:58:21.502 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:58:21.505 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:58:26.516 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:58:26.519 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:58:31.525 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:58:31.527 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:58:36.527 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:58:36.529 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:58:41.531 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:58:41.532 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:58:46.535 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:58:46.537 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:58:51.545 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:58:51.548 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:58:56.548 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:58:56.550 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:59:01.548 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:59:01.550 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:59:06.553 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:59:06.555 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:59:11.565 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:59:11.567 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:59:16.577 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:59:16.580 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:59:21.589 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:59:21.590 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:59:26.590 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:59:26.592 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:59:31.600 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:59:31.602 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:59:36.605 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:59:36.607 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:59:41.607 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:59:41.608 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:59:46.621 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:59:46.623 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:59:51.622 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:59:51.625 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 12:59:56.630 +0800] DEBUG: Event registry already initialized -[2026-08-26 12:59:56.632 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:00:01.645 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:00:01.646 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:00:06.648 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:00:06.650 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:00:11.663 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:00:11.664 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:00:16.671 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:00:16.672 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:00:21.673 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:00:21.675 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:00:26.679 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:00:26.681 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:00:31.681 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:00:31.683 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:00:36.694 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:00:36.695 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:00:41.707 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:00:41.708 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:00:46.715 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:00:46.716 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:00:51.720 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:00:51.721 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:00:56.731 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:00:56.733 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:01:01.731 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:01:01.733 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:01:06.734 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:01:06.736 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:01:11.745 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:01:11.748 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:01:16.761 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:01:16.762 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:01:21.774 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:01:21.776 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:01:26.785 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:01:26.788 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:01:31.800 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:01:31.802 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:01:36.801 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:01:36.803 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:01:41.804 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:01:41.807 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:01:46.815 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:01:46.816 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:01:51.821 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:01:51.823 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:01:56.836 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:01:56.837 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:02:01.837 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:02:01.838 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:02:06.838 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:02:06.840 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:02:11.853 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:02:11.854 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:02:16.863 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:02:16.864 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:02:21.872 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:02:21.874 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:02:26.886 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:02:26.888 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:02:31.893 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:02:31.894 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:02:36.901 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:02:36.903 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:02:41.906 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:02:41.908 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:02:46.920 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:02:46.922 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:02:51.923 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:02:51.926 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:02:56.923 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:02:56.925 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:03:01.929 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:03:01.930 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:03:06.935 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:03:06.937 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:03:11.948 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:03:11.950 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:03:16.963 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:03:16.965 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:03:21.965 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:03:21.966 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:03:26.972 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:03:26.973 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:03:31.984 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:03:31.986 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:03:36.997 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:03:36.999 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:03:41.999 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:03:42.000 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:03:46.999 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:03:47.001 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:03:52.013 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:03:52.015 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:03:57.021 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:03:57.023 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:04:02.021 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:04:02.024 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:04:07.026 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:04:07.028 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:04:12.039 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:04:12.041 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:04:17.052 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:04:17.053 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:04:22.060 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:04:22.062 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:04:27.073 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:04:27.075 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:04:32.084 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:04:32.085 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:04:37.096 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:04:37.098 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:04:42.096 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:04:42.098 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:04:47.098 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:04:47.100 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:04:52.102 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:04:52.104 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:04:57.115 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:04:57.116 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:05:02.116 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:05:02.117 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:05:07.117 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:05:07.118 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:05:12.128 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:05:12.130 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:05:17.136 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:05:17.138 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:05:22.138 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:05:22.140 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:05:27.141 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:05:27.143 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:05:32.149 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:05:32.151 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:05:37.157 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:05:37.159 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:05:42.158 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:05:42.160 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:05:47.165 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:05:47.166 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:05:52.179 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:05:52.181 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:05:57.194 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:05:57.195 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:06:02.200 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:06:02.202 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:06:07.213 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:06:07.214 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:06:12.216 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:06:12.217 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:06:17.229 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:06:17.231 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:06:22.242 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:06:22.244 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:06:27.256 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:06:27.257 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:06:32.258 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:06:32.260 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:06:37.264 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:06:37.266 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:06:42.270 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:06:42.271 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:06:47.276 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:06:47.278 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:06:52.285 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:06:52.287 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:06:57.294 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:06:57.297 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:07:02.307 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:07:02.309 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:07:07.316 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:07:07.318 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:07:12.328 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:07:12.329 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:07:17.339 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:07:17.341 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:07:22.348 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:07:22.350 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:07:27.353 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:07:27.355 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:07:32.362 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:07:32.365 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:07:37.375 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:07:37.377 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:07:42.390 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:07:42.391 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:07:47.404 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:07:47.406 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:07:52.419 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:07:52.420 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:07:57.422 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:07:57.424 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:08:02.434 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:08:02.436 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:08:07.448 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:08:07.450 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:08:12.454 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:08:12.455 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:08:17.456 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:08:17.458 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:08:22.470 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:08:22.471 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:08:27.476 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:08:27.478 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:08:32.489 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:08:32.491 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:08:37.495 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:08:37.497 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:08:42.506 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:08:42.507 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:08:47.511 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:08:47.513 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:08:52.525 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:08:52.527 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:08:57.531 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:08:57.532 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:09:02.543 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:09:02.545 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:09:07.549 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:09:07.550 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:09:12.555 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:09:12.557 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:09:17.568 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:09:17.569 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:09:22.581 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:09:22.582 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:09:27.581 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:09:27.583 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:09:32.585 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:09:32.587 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:09:37.591 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:09:37.593 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:09:42.599 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:09:42.600 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:09:47.609 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:09:47.611 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:09:52.612 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:09:52.614 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:09:57.619 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:09:57.621 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:10:02.630 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:10:02.633 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:10:07.640 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:10:07.641 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:10:12.646 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:10:12.648 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:10:17.656 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:10:17.658 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:10:22.666 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:10:22.668 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:10:27.671 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:10:27.672 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:10:32.680 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:10:32.682 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:10:37.689 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:10:37.691 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:10:42.692 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:10:42.694 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:10:47.693 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:10:47.694 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:10:52.703 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:10:52.705 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:10:57.707 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:10:57.708 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:11:02.708 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:11:02.710 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:11:07.723 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:11:07.725 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:11:12.737 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:11:12.739 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:11:17.741 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:11:17.742 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:11:22.743 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:11:22.745 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:11:27.745 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:11:27.747 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:11:32.756 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:11:32.758 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:11:37.763 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:11:37.764 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:11:42.771 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:11:42.773 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:11:47.776 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:11:47.779 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:11:52.781 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:11:52.783 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:11:57.796 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:11:57.797 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:12:02.809 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:12:02.812 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:12:07.812 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:12:07.814 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:12:12.824 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:12:12.826 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:12:17.833 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:12:17.834 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:12:22.848 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:12:22.850 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:12:27.858 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:12:27.860 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:12:32.871 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:12:32.873 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:12:37.885 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:12:37.886 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:12:42.889 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:12:42.891 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:12:47.893 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:12:47.895 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:12:52.907 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:12:52.908 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:12:57.911 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:12:57.914 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:13:02.922 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:13:02.924 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:13:07.934 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:13:07.935 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:13:12.941 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:13:12.943 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:13:17.950 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:13:17.951 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:13:22.961 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:13:22.963 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:13:27.970 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:13:27.972 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:13:32.984 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:13:32.986 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:13:37.993 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:13:37.994 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:13:43.008 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:13:43.009 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:13:48.009 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:13:48.010 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:13:53.024 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:13:53.025 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:13:58.039 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:13:58.041 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:14:03.047 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:14:03.049 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:14:08.061 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:14:08.062 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:14:13.070 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:14:13.072 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:14:18.072 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:14:18.074 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:14:23.078 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:14:23.080 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:14:28.091 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:14:28.093 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:14:33.099 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:14:33.101 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:14:38.106 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:14:38.107 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:14:43.109 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:14:43.111 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:14:48.123 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:14:48.124 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:14:53.128 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:14:53.131 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:14:58.138 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:14:58.140 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:15:03.139 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:15:03.141 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:15:08.152 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:15:08.153 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:15:13.163 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:15:13.164 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:15:18.169 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:15:18.171 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:15:23.178 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:15:23.180 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:15:28.179 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:15:28.181 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:15:33.194 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:15:33.196 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:15:38.197 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:15:38.199 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:15:43.205 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:15:43.207 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:15:48.212 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:15:48.214 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:15:53.224 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:15:53.226 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:15:58.236 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:15:58.237 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:16:03.243 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:16:03.244 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:16:08.258 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:16:08.260 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:16:13.270 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:16:13.271 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:16:18.278 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:16:18.280 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:16:23.289 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:16:23.291 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:16:28.303 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:16:28.305 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:16:33.316 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:16:33.317 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:16:38.325 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:16:38.326 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:16:43.337 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:16:43.339 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:16:48.340 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:16:48.342 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:16:53.344 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:16:53.346 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:16:58.356 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:16:58.358 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:17:03.367 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:17:03.369 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:17:08.375 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:17:08.376 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:17:13.384 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:17:13.386 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:17:18.390 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:17:18.392 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:17:23.405 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:17:23.407 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:17:28.406 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:17:28.408 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:17:33.408 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:17:33.409 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:17:38.420 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:17:38.422 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:17:43.421 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:17:43.423 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:17:48.431 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:17:48.432 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:17:53.436 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:17:53.437 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:17:58.444 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:17:58.446 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:18:03.448 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:18:03.450 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:18:08.451 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:18:08.453 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:18:13.459 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:18:13.461 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:18:18.463 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:18:18.465 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:18:23.473 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:18:23.476 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:18:28.477 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:18:28.478 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:18:33.488 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:18:33.490 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:18:38.489 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:18:38.491 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:18:43.502 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:18:43.504 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:18:48.518 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:18:48.520 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:18:53.527 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:18:53.529 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:18:58.542 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:18:58.544 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:19:03.549 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:19:03.551 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:19:08.562 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:19:08.564 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:19:13.577 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:19:13.579 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:19:18.590 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:19:18.591 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:19:23.603 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:19:23.604 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:19:28.613 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:19:28.615 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:19:33.615 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:19:33.617 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:19:38.630 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:19:38.631 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:19:43.633 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:19:43.635 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:19:48.637 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:19:48.639 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:19:53.645 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:19:53.647 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:19:58.651 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:19:58.652 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:20:03.654 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:20:03.656 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:20:08.666 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:20:08.669 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:20:13.678 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:20:13.680 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:20:18.684 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:20:18.687 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:20:23.698 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:20:23.700 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:20:28.713 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:20:28.715 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:20:33.727 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:20:33.729 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:20:38.731 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:20:38.733 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:20:43.733 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:20:43.734 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:20:48.739 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:20:48.740 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:20:53.749 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:20:53.751 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:20:58.760 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:20:58.762 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:21:03.762 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:21:03.764 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:21:08.775 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:21:08.777 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:21:13.789 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:21:13.790 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:21:18.796 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:21:18.799 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:21:23.802 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:21:23.803 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:21:28.809 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:21:28.811 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:21:33.815 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:21:33.817 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:21:38.816 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:21:38.818 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:21:43.831 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:21:43.832 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:21:48.836 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:21:48.838 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:21:53.841 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:21:53.843 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:21:58.856 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:21:58.858 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:22:03.872 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:22:03.873 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:22:08.871 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:22:08.873 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:22:13.882 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:22:13.884 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:22:18.886 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:22:18.887 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:22:23.900 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:22:23.902 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:22:28.901 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:22:28.902 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:22:33.906 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:22:33.908 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:22:38.914 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:22:38.917 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:22:43.925 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:22:43.927 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:22:48.935 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:22:48.936 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:22:53.938 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:22:53.940 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:22:58.942 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:22:58.944 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:23:03.946 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:23:03.948 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:23:08.954 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:23:08.957 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:23:13.963 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:23:13.964 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:23:18.978 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:23:18.980 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:23:23.983 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:23:23.984 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:23:28.993 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:23:28.995 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:23:34.002 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:23:34.004 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:23:39.007 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:23:39.009 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:23:44.011 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:23:44.013 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:23:49.021 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:23:49.023 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:23:54.029 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:23:54.031 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:23:59.031 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:23:59.033 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:24:04.034 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:24:04.036 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:24:09.048 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:24:09.050 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:24:14.063 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:24:14.066 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:24:19.077 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:24:19.080 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:24:24.079 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:24:24.081 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:24:29.080 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:24:29.082 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:24:34.090 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:24:34.092 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:24:39.094 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:24:39.095 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:24:44.102 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:24:44.104 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:24:49.103 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:24:49.105 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:24:54.105 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:24:54.107 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:24:59.114 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:24:59.116 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:25:04.118 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:25:04.120 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:25:09.129 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:25:09.131 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:25:14.134 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:25:14.136 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:25:19.139 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:25:19.141 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:25:24.150 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:25:24.151 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:25:29.155 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:25:29.156 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:25:34.160 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:25:34.162 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:25:39.162 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:25:39.164 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:25:44.162 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:25:44.165 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:25:49.168 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:25:49.170 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:25:54.174 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:25:54.176 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:25:59.185 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:25:59.187 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:26:04.195 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:26:04.197 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:26:09.202 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:26:09.203 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:26:14.204 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:26:14.206 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:26:19.210 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:26:19.211 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:26:24.210 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:26:24.211 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:26:29.222 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:26:29.224 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:26:34.236 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:26:34.239 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:26:39.250 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:26:39.252 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:26:44.253 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:26:44.254 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:26:49.264 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:26:49.266 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:26:54.267 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:26:54.268 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:26:59.279 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:26:59.281 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:27:04.283 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:27:04.285 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:27:09.297 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:27:09.298 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:27:14.303 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:27:14.304 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:27:19.318 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:27:19.320 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:27:24.323 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:27:24.324 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:27:29.328 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:27:29.330 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:27:34.330 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:27:34.332 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:27:39.336 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:27:39.338 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:27:44.342 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:27:44.343 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:27:49.355 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:27:49.357 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:27:54.363 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:27:54.364 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:27:59.368 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:27:59.370 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:28:04.376 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:28:04.378 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:28:09.383 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:28:09.384 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:28:14.394 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:28:14.395 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:28:19.399 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:28:19.400 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:28:24.405 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:28:24.406 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:28:29.416 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:28:29.418 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:28:34.428 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:28:34.430 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:28:39.432 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:28:39.433 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:28:44.439 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:28:44.440 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:28:49.439 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:28:49.441 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:28:54.448 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:28:54.450 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:28:59.457 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:28:59.459 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:29:04.458 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:29:04.460 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:29:09.469 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:29:09.471 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:29:14.472 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:29:14.473 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:29:19.482 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:29:19.484 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:29:24.487 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:29:24.489 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:29:29.502 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:29:29.504 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:29:34.515 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:29:34.516 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:29:39.517 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:29:39.519 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:29:44.527 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:29:44.529 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:29:49.530 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:29:49.531 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:29:54.543 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:29:54.544 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:29:59.558 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:29:59.559 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:30:04.572 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:30:04.574 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:30:09.576 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:30:09.577 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:30:14.582 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:30:14.583 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:30:19.587 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:30:19.589 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:30:24.601 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:30:24.603 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:30:29.603 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:30:29.604 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:30:34.613 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:30:34.614 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:30:39.627 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:30:39.628 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:30:44.636 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:30:44.638 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:30:49.649 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:30:49.650 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:30:54.660 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:30:54.662 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:30:59.673 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:30:59.674 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:31:04.680 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:31:04.682 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:31:09.684 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:31:09.686 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:31:14.697 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:31:14.698 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:31:19.704 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:31:19.706 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:31:24.719 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:31:24.721 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:31:29.733 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:31:29.735 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:31:34.736 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:31:34.738 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:31:39.748 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:31:39.750 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:31:44.754 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:31:44.756 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:31:49.754 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:31:49.756 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:31:54.760 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:31:54.762 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:31:59.771 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:31:59.773 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:32:04.787 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:32:04.789 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:32:09.801 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:32:09.803 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:32:14.810 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:32:14.811 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:32:19.822 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:32:19.824 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:32:24.836 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:32:24.838 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:32:29.849 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:32:29.850 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:32:34.856 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:32:34.857 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:32:39.871 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:32:39.873 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:32:44.884 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:32:44.886 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:32:49.895 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:32:49.897 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:32:54.901 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:32:54.902 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:32:59.900 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:32:59.902 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:33:04.909 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:33:04.910 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:33:09.911 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:33:09.913 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:33:14.915 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:33:14.917 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:33:19.930 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:33:19.931 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:33:24.934 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:33:24.936 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:33:29.943 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:33:29.944 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:33:34.949 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:33:34.951 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:33:39.953 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:33:39.954 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:33:44.955 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:33:44.956 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:33:49.959 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:33:49.960 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:33:54.964 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:33:54.966 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:33:59.977 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:33:59.979 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:34:04.982 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:34:04.983 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:34:09.991 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:34:09.993 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:34:14.998 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:34:15.000 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:34:20.006 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:34:20.008 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:34:25.020 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:34:25.021 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:34:30.021 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:34:30.023 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:34:35.028 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:34:35.030 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:34:40.039 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:34:40.041 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:34:45.048 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:34:45.050 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:34:50.056 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:34:50.057 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:34:55.062 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:34:55.064 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:35:00.072 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:35:00.074 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:35:05.077 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:35:05.078 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:35:10.078 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:35:10.080 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:35:15.092 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:35:15.094 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:35:20.095 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:35:20.097 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:35:25.096 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:35:25.097 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:35:30.100 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:35:30.102 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:35:35.107 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:35:35.110 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:35:40.112 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:35:40.113 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:35:45.112 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:35:45.114 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:35:50.119 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:35:50.120 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:35:55.122 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:35:55.125 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:36:00.129 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:36:00.130 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:36:05.135 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:36:05.136 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:36:10.138 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:36:10.139 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:36:15.143 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:36:15.145 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:36:20.154 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:36:20.157 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:36:25.162 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:36:25.163 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:36:30.172 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:36:30.174 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:36:35.182 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:36:35.184 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:36:40.193 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:36:40.194 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:36:45.200 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:36:45.201 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:36:50.211 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:36:50.213 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:36:55.219 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:36:55.221 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:37:00.219 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:37:00.221 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:37:05.233 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:37:05.235 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:37:10.240 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:37:10.241 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:37:15.242 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:37:15.244 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:37:20.252 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:37:20.254 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:37:25.252 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:37:25.253 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:37:30.253 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:37:30.255 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:37:35.260 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:37:35.262 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:37:40.261 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:37:40.263 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:37:45.274 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:37:45.276 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -○ Compiling /[locale]/login ... -[2026-08-26 13:37:50.279 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:37:50.282 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:37:55.280 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:37:55.283 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" - ------ -FATAL: An unexpected Turbopack error occurred. A panic log has been written to C:\Users\snqig\AppData\Local\Temp\next-panic-ba469b6add27f7143f2ec3c12cc3e6f2.log. - -To help make Turbopack better, report this error by clicking here: https://github.com/vercel/next.js/discussions/new?category=turbopack-error-report&title=Turbopack%20Error%3A%20failed%20to%20write%20to%20D%3A%5Cdcprint%5Cerp-project%5C.next%5Cdev%5Cserver%5Cchunks%5Cssr%5C%5Broot-of-the-server%5D__76d94956._.js.map&body=Turbopack%20version%3A%20%60d1bd5b58%60%0ANext.js%20version%3A%20%600.0.0%60%0A%0AError%20message%3A%0A%60%60%60%0ATurbopack%20Error%3A%20failed%20to%20write%20to%20D%3A%5Cdcprint%5Cerp-project%5C.next%5Cdev%5Cserver%5Cchunks%5Cssr%5C%5Broot-of-the-server%5D__76d94956._.js.map%0A%60%60%60&labels=Turbopack,Turbopack%20Panic%20Backtrace ------ - -[2026-08-26 13:38:00.294 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:38:00.297 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" - GET /login?next=%2F 500 in 14.2s (compile: 13.8s, proxy.ts: 13ms, render: 329ms) -[2026-08-26 13:38:05.298 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:38:05.301 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:38:10.310 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:38:10.312 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:38:15.320 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:38:15.323 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" - ------ -FATAL: An unexpected Turbopack error occurred. A panic log has been written to C:\Users\snqig\AppData\Local\Temp\next-panic-ba469b6add27f7143f2ec3c12cc3e6f2.log. - -To help make Turbopack better, report this error by clicking here: https://github.com/vercel/next.js/discussions/new?category=turbopack-error-report&title=Turbopack%20Error%3A%20failed%20to%20write%20to%20D%3A%5Cdcprint%5Cerp-project%5C.next%5Cdev%5Cserver%5Cchunks%5Cssr%5C%5Broot-of-the-server%5D__76d94956._.js.map&body=Turbopack%20version%3A%20%60d1bd5b58%60%0ANext.js%20version%3A%20%600.0.0%60%0A%0AError%20message%3A%0A%60%60%60%0ATurbopack%20Error%3A%20failed%20to%20write%20to%20D%3A%5Cdcprint%5Cerp-project%5C.next%5Cdev%5Cserver%5Cchunks%5Cssr%5C%5Broot-of-the-server%5D__76d94956._.js.map%0A%60%60%60&labels=Turbopack,Turbopack%20Panic%20Backtrace ------ - - GET /login?next=%2F 500 in 2.0s (compile: 2.0s, proxy.ts: 6ms, render: 27ms) -[2026-08-26 13:38:20.328 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:38:20.330 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" - ------ -FATAL: An unexpected Turbopack error occurred. A panic log has been written to C:\Users\snqig\AppData\Local\Temp\next-panic-ba469b6add27f7143f2ec3c12cc3e6f2.log. - -To help make Turbopack better, report this error by clicking here: https://github.com/vercel/next.js/discussions/new?category=turbopack-error-report&title=Turbopack%20Error%3A%20failed%20to%20write%20to%20D%3A%5Cdcprint%5Cerp-project%5C.next%5Cdev%5Cserver%5Cchunks%5Cssr%5C%5Broot-of-the-server%5D__76d94956._.js.map&body=Turbopack%20version%3A%20%60d1bd5b58%60%0ANext.js%20version%3A%20%600.0.0%60%0A%0AError%20message%3A%0A%60%60%60%0ATurbopack%20Error%3A%20failed%20to%20write%20to%20D%3A%5Cdcprint%5Cerp-project%5C.next%5Cdev%5Cserver%5Cchunks%5Cssr%5C%5Broot-of-the-server%5D__76d94956._.js.map%0A%60%60%60&labels=Turbopack,Turbopack%20Panic%20Backtrace ------ - - GET /login 500 in 31ms (compile: 13ms, proxy.ts: 5ms, render: 12ms) -[2026-08-26 13:38:25.338 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:38:25.340 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:38:30.353 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:38:30.357 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:38:35.353 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:38:35.355 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:38:40.363 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:38:40.365 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:38:45.365 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:38:45.366 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" - ------ -FATAL: An unexpected Turbopack error occurred. A panic log has been written to C:\Users\snqig\AppData\Local\Temp\next-panic-ba469b6add27f7143f2ec3c12cc3e6f2.log. - -To help make Turbopack better, report this error by clicking here: https://github.com/vercel/next.js/discussions/new?category=turbopack-error-report&title=Turbopack%20Error%3A%20failed%20to%20write%20to%20D%3A%5Cdcprint%5Cerp-project%5C.next%5Cdev%5Cserver%5Cchunks%5Cssr%5C%5Broot-of-the-server%5D__76d94956._.js.map&body=Turbopack%20version%3A%20%60d1bd5b58%60%0ANext.js%20version%3A%20%600.0.0%60%0A%0AError%20message%3A%0A%60%60%60%0ATurbopack%20Error%3A%20failed%20to%20write%20to%20D%3A%5Cdcprint%5Cerp-project%5C.next%5Cdev%5Cserver%5Cchunks%5Cssr%5C%5Broot-of-the-server%5D__76d94956._.js.map%0A%60%60%60&labels=Turbopack,Turbopack%20Panic%20Backtrace ------ - - GET /login 500 in 25ms (compile: 13ms, proxy.ts: 4ms, render: 7ms) -[2026-08-26 13:38:50.368 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:38:50.370 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:38:55.374 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:38:55.376 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:39:00.376 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:39:00.378 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:39:05.389 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:39:05.391 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:39:10.398 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:39:10.400 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:39:15.402 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:39:15.404 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:39:20.409 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:39:20.411 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:39:25.420 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:39:25.421 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:39:30.420 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:39:30.422 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:39:35.431 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:39:35.433 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:39:40.438 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:39:40.440 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:39:45.440 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:39:45.442 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:39:50.454 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:39:50.456 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:39:55.469 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:39:55.470 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:40:00.469 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:40:00.471 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:40:05.473 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:40:05.475 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:40:10.478 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:40:10.481 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:40:15.492 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:40:15.493 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:40:20.507 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:40:20.509 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:40:25.510 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:40:25.511 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-26 13:40:30.520 +0800] DEBUG: Event registry already initialized -[2026-08-26 13:40:30.521 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[?25h diff --git a/_devlog_runtime.txt b/_devlog_runtime.txt deleted file mode 100644 index 43c4db28..00000000 --- a/_devlog_runtime.txt +++ /dev/null @@ -1,910 +0,0 @@ - -> vnerp@0.1.0 dev -> next dev -p 5000 - -▲ Next.js 16.1.1 (Turbopack) -- Local: http://localhost:5000 -- Network: http://192.168.0.144:5000 -- Environments: .env.local, .env -- Experiments (use with caution): - · optimizePackageImports - -✓ Starting... -✓ Ready in 3.4s -[2026-08-25 11:52:44.677 +0800] INFO: [instrumentation] Server started. OutboxPoller will auto-start on first API request. - GET /api/quality/complaint?pageSize=5 401 in 928ms (compile: 760ms, proxy.ts: 126ms, render: 42ms) - GET /api/finance/receivables?pageSize=5 401 in 137ms (compile: 118ms, proxy.ts: 8ms, render: 11ms) - GET /api/quality/sgs?pageSize=5 401 in 124ms (compile: 106ms, proxy.ts: 8ms, render: 10ms) - GET /api/hr/salary?pageSize=5 401 in 131ms (compile: 114ms, proxy.ts: 5ms, render: 12ms) - GET /api/quality/complaint?pageSize=5 401 in 17ms (compile: 5ms, proxy.ts: 6ms, render: 6ms) - GET /api/finance/receivables?pageSize=5 401 in 14ms (compile: 4ms, proxy.ts: 4ms, render: 5ms) - GET /api/quality/sgs?pageSize=5 401 in 13ms (compile: 3ms, proxy.ts: 6ms, render: 4ms) - GET /api/hr/salary?pageSize=5 401 in 13ms (compile: 3ms, proxy.ts: 4ms, render: 6ms) -[2026-08-25 11:54:04.167 +0800] INFO: CacheManager: 使用 InMemoryCacheManager(未配置 REDIS_URL) -[API Error] 请求处理失败: Error: Unknown column 'deleted' in 'where clause' - at query (src\lib\db\index.ts:113:33) - at (src\app\api\quality\complaint\route.ts:34:39) - at errorMessage (src\lib\api-permissions.ts:961:28) - at (src\lib\api-auth.ts:166:12) - at async (src\lib\api-auth.ts:202:14) - 111 | const _sqlStr = typeof sql === 'string' ? sql : String(sql); - 112 | } -> 113 | const [rows] = await pool.query(sql, values); - | ^ - 114 | if (DEBUG_DB) { - 115 | } - 116 | return rows as T[]; { - code: 'ER_BAD_FIELD_ERROR', - errno: 1054, - sqlState: '42S22', - sqlMessage: "Unknown column 'deleted' in 'where clause'", - sql: 'SELECT COUNT(*) as total FROM qms_complaint WHERE deleted = 0' -} - GET /api/quality/complaint?pageSize=5 500 in 257ms (compile: 3ms, proxy.ts: 5ms, render: 248ms) - GET /api/finance/receivables?pageSize=5 200 in 29ms (compile: 3ms, proxy.ts: 4ms, render: 22ms) - GET /api/quality/sgs?pageSize=5 200 in 22ms (compile: 2ms, proxy.ts: 4ms, render: 15ms) - GET /api/hr/salary?pageSize=5 200 in 27ms (compile: 4ms, proxy.ts: 4ms, render: 18ms) - GET /api/finance/cost?pageSize=3 200 in 175ms (compile: 129ms, proxy.ts: 3ms, render: 43ms) - GET /api/finance/stats?pageSize=3 200 in 198ms (compile: 150ms, proxy.ts: 4ms, render: 44ms) - GET /api/finance/receivables?pageSize=3 200 in 21ms (compile: 4ms, proxy.ts: 5ms, render: 12ms) - GET /api/finance/payable?pageSize=3 200 in 222ms (compile: 202ms, proxy.ts: 4ms, render: 17ms) - GET /api/finance/receipt?pageSize=3 405 in 151ms (compile: 142ms, proxy.ts: 5ms, render: 5ms) - GET /api/finance/payment?pageSize=3 405 in 180ms (compile: 169ms, proxy.ts: 6ms, render: 5ms) - GET /api/customers?pageSize=3 200 in 80ms (compile: 56ms, proxy.ts: 3ms, render: 20ms) -[API Error] 请求处理失败: Error: Unknown column 'default_currency' in 'field list' - at query (src\lib\db\index.ts:113:33) - at queryPaginated (src\lib\db\index.ts:357:22) - at async (src\app\api\purchase\suppliers\route.ts:68:18) - at async errorMessage (src\lib\api-permissions.ts:961:22) - at async (src\lib\api-auth.ts:202:14) - 111 | const _sqlStr = typeof sql === 'string' ? sql : String(sql); - 112 | } -> 113 | const [rows] = await pool.query(sql, values); - | ^ - 114 | if (DEBUG_DB) { - 115 | } - 116 | return rows as T[]; { - code: 'ER_BAD_FIELD_ERROR', - errno: 1054, - sqlState: '42S22', - sqlMessage: "Unknown column 'default_currency' in 'field list'", - sql: '\n' + - ' SELECT\n' + - ' id,\n' + - ' supplier_code,\n' + - ' default_currency,\n' + - ' supplier_name,\n' + - ' short_name,\n' + - ' supplier_type,\n' + - ' contact_name,\n' + - ' contact_phone,\n' + - ' contact_email,\n' + - ' address,\n' + - ' credit_level,\n' + - ' cooperation_status,\n' + - ' settlement_method,\n' + - ' payment_terms,\n' + - ' status,\n' + - ' remark,\n' + - ' create_time,\n' + - ' update_time\n' + - ' FROM pur_supplier\n' + - ' WHERE deleted = 0\n' + - ' ORDER BY create_time DESC LIMIT 3 OFFSET 0' -} - GET /api/purchase/suppliers?pageSize=3 500 in 186ms (compile: 46ms, proxy.ts: 4ms, render: 136ms) - GET /api/finance/report?pageSize=3 200 in 184ms (compile: 152ms, proxy.ts: 4ms, render: 27ms) -[API Error] 请求处理失败: Error: Unknown column 'deleted' in 'where clause' - at query (src\lib\db\index.ts:113:33) - at (src\app\api\quality\complaint\route.ts:34:39) - at errorMessage (src\lib\api-permissions.ts:961:28) - at (src\lib\api-auth.ts:166:12) - at async (src\lib\api-auth.ts:202:14) - 111 | const _sqlStr = typeof sql === 'string' ? sql : String(sql); - 112 | } -> 113 | const [rows] = await pool.query(sql, values); - | ^ - 114 | if (DEBUG_DB) { - 115 | } - 116 | return rows as T[]; { - code: 'ER_BAD_FIELD_ERROR', - errno: 1054, - sqlState: '42S22', - sqlMessage: "Unknown column 'deleted' in 'where clause'", - sql: 'SELECT COUNT(*) as total FROM qms_complaint WHERE deleted = 0' -} - GET /api/quality/complaint?pageSize=3 500 in 96ms (compile: 3ms, proxy.ts: 8ms, render: 85ms) - GET /api/quality/final?pageSize=3 200 in 169ms (compile: 148ms, proxy.ts: 4ms, render: 17ms) - GET /api/quality/incoming?pageSize=3 200 in 167ms (compile: 134ms, proxy.ts: 4ms, render: 30ms) -[API Error] 请求处理失败: Error: Unknown column 'deleted' in 'where clause' - at query (src\lib\db\index.ts:113:33) - at (src\app\api\quality\lab-test\route.ts:34:39) - at errorMessage (src\lib\api-permissions.ts:961:28) - at (src\lib\api-auth.ts:166:12) - at async (src\lib\api-auth.ts:202:14) - 111 | const _sqlStr = typeof sql === 'string' ? sql : String(sql); - 112 | } -> 113 | const [rows] = await pool.query(sql, values); - | ^ - 114 | if (DEBUG_DB) { - 115 | } - 116 | return rows as T[]; { - code: 'ER_BAD_FIELD_ERROR', - errno: 1054, - sqlState: '42S22', - sqlMessage: "Unknown column 'deleted' in 'where clause'", - sql: 'SELECT COUNT(*) as total FROM qms_lab_test WHERE deleted = 0' -} - GET /api/quality/lab-test?pageSize=3 500 in 230ms (compile: 112ms, proxy.ts: 6ms, render: 112ms) - GET /api/quality/process?pageSize=3 200 in 158ms (compile: 136ms, proxy.ts: 5ms, render: 17ms) - GET /api/quality/sgs/expiry-warning?pageSize=3 200 in 152ms (compile: 131ms, proxy.ts: 4ms, render: 17ms) - GET /api/quality/sgs?pageSize=3 200 in 14ms (compile: 2ms, proxy.ts: 4ms, render: 9ms) - GET /api/quality/spc?pageSize=3 400 in 136ms (compile: 123ms, proxy.ts: 3ms, render: 11ms) -[API Error] 请求处理失败: Error: Unknown column 'deleted' in 'where clause' - at query (src\lib\db\index.ts:113:33) - at (src\app\api\quality\supplier-audit\route.ts:34:39) - at errorMessage (src\lib\api-permissions.ts:961:28) - at (src\lib\api-auth.ts:166:12) - at async (src\lib\api-auth.ts:202:14) - 111 | const _sqlStr = typeof sql === 'string' ? sql : String(sql); - 112 | } -> 113 | const [rows] = await pool.query(sql, values); - | ^ - 114 | if (DEBUG_DB) { - 115 | } - 116 | return rows as T[]; { - code: 'ER_BAD_FIELD_ERROR', - errno: 1054, - sqlState: '42S22', - sqlMessage: "Unknown column 'deleted' in 'where clause'", - sql: 'SELECT COUNT(*) as total FROM qms_supplier_audit WHERE deleted = 0' -} - GET /api/quality/supplier-audit?pageSize=3 500 in 247ms (compile: 143ms, proxy.ts: 3ms, render: 101ms) - GET /api/dcprint/trace?pageSize=3 200 in 128ms (compile: 104ms, proxy.ts: 3ms, render: 21ms) - GET /api/quality/unqualified?pageSize=3 200 in 176ms (compile: 150ms, proxy.ts: 4ms, render: 22ms) -[API Error] 操作失败: Error: Table 'vnerpdacahng.sys_announcement' doesn't exist - at query (src\lib\db\index.ts:113:33) - at GET.errorMessage (src\app\api\system\announcement\route.ts:32:41) - at errorMessage (src\lib\api-permissions.ts:961:28) - at (src\lib\api-auth.ts:166:12) - at async (src\lib\api-auth.ts:202:14) - 111 | const _sqlStr = typeof sql === 'string' ? sql : String(sql); - 112 | } -> 113 | const [rows] = await pool.query(sql, values); - | ^ - 114 | if (DEBUG_DB) { - 115 | } - 116 | return rows as T[]; { - code: 'ER_NO_SUCH_TABLE', - errno: 1146, - sqlState: '42S02', - sqlMessage: "Table 'vnerpdacahng.sys_announcement' doesn't exist", - sql: "SELECT COUNT(*) as total FROM sys_announcement a WHERE 1=1 AND a.status = 'published' AND (a.expire_time IS NULL OR a.expire_time > NOW())" -} - GET /api/system/announcement?pageSize=3 500 in 224ms (compile: 127ms, proxy.ts: 3ms, render: 94ms) - GET /api/system/config?pageSize=3 200 in 70ms (compile: 45ms, proxy.ts: 4ms, render: 21ms) - GET /api/system/currency?pageSize=3 200 in 63ms (compile: 40ms, proxy.ts: 4ms, render: 19ms) - GET /api/system/dict?pageSize=3 200 in 173ms (compile: 142ms, proxy.ts: 4ms, render: 28ms) - GET /api/system/exchange-rate?pageSize=3 200 in 156ms (compile: 137ms, proxy.ts: 3ms, render: 15ms) -[API Error] 请求处理失败: Error: Table 'vnerpdacahng.label_template' doesn't exist - at query (src\lib\db\index.ts:113:33) - at (src\app\api\trace\label\route.ts:16:27) - at errorMessage (src\lib\api-permissions.ts:961:28) - at (src\lib\api-auth.ts:166:12) - at async (src\lib\api-auth.ts:202:14) - 111 | const _sqlStr = typeof sql === 'string' ? sql : String(sql); - 112 | } -> 113 | const [rows] = await pool.query(sql, values); - | ^ - 114 | if (DEBUG_DB) { - 115 | } - 116 | return rows as T[]; { - code: 'ER_NO_SUCH_TABLE', - errno: 1146, - sqlState: '42S02', - sqlMessage: "Table 'vnerpdacahng.label_template' doesn't exist", - sql: 'SELECT * FROM label_template WHERE status = 1 ORDER BY id ASC' -} - GET /api/trace/label?pageSize=3 500 in 209ms (compile: 118ms, proxy.ts: 3ms, render: 88ms) -[API Error] 请求处理失败: Error: Table 'vnerpdacahng.label_template' doesn't exist - at query (src\lib\db\index.ts:113:33) - at (src\app\api\trace\label\route.ts:16:27) - at errorMessage (src\lib\api-permissions.ts:961:28) - at (src\lib\api-auth.ts:166:12) - at async (src\lib\api-auth.ts:202:14) - 111 | const _sqlStr = typeof sql === 'string' ? sql : String(sql); - 112 | } -> 113 | const [rows] = await pool.query(sql, values); - | ^ - 114 | if (DEBUG_DB) { - 115 | } - 116 | return rows as T[]; { - code: 'ER_NO_SUCH_TABLE', - errno: 1146, - sqlState: '42S02', - sqlMessage: "Table 'vnerpdacahng.label_template' doesn't exist", - sql: 'SELECT * FROM label_template WHERE status = 1 ORDER BY id ASC' -} - GET /api/trace/label?pageSize=3 500 in 69ms (compile: 3ms, proxy.ts: 3ms, render: 63ms) - GET /api/system/login-log?pageSize=3 200 in 108ms (compile: 93ms, proxy.ts: 3ms, render: 13ms) - GET /api/organization/menu?pageSize=3 200 in 118ms (compile: 103ms, proxy.ts: 3ms, render: 12ms) - GET /api/system/notice?pageSize=3 200 in 55ms (compile: 33ms, proxy.ts: 7ms, render: 15ms) -[API Error] 请求处理失败: Error: Unknown column 'business_type' in 'field list' - at query (src\lib\db\index.ts:113:33) - at GET.logTitle (src\app\api\system\oper-log\route.ts:52:36) - at async errorMessage (src\lib\api-permissions.ts:961:22) - at async (src\lib\api-auth.ts:202:14) - 111 | const _sqlStr = typeof sql === 'string' ? sql : String(sql); - 112 | } -> 113 | const [rows] = await pool.query(sql, values); - | ^ - 114 | if (DEBUG_DB) { - 115 | } - 116 | return rows as T[]; { - code: 'ER_BAD_FIELD_ERROR', - errno: 1054, - sqlState: '42S22', - sqlMessage: "Unknown column 'business_type' in 'field list'", - sql: "SELECT id, COALESCE(operation, '') as title, COALESCE(username, '') as oper_name,\n" + - " COALESCE(operation, '') as oper_type, COALESCE(method, '') as oper_method,\n" + - " COALESCE(request_url, '') as oper_url, COALESCE(ip, '') as oper_ip,\n" + - ' COALESCE(create_time, NOW()) as oper_time, status,\n' + - " COALESCE(business_type, '') as business_type,\n" + - " COALESCE(business_id, '') as business_id,\n" + - " COALESCE(request_params, '') as request_params,\n" + - " COALESCE(response_result, '') as response_result\n" + - ' FROM sys_operation_log WHERE 1=1\n' + - ' ORDER BY create_time DESC\n' + - ' LIMIT 3 OFFSET 0' -} - GET /api/system/oper-log?pageSize=3 500 in 241ms (compile: 165ms, proxy.ts: 4ms, render: 73ms) - GET /api/organization?pageSize=3 400 in 53ms (compile: 37ms, proxy.ts: 3ms, render: 13ms) - GET /api/organization/department?pageSize=3 200 in 55ms (compile: 37ms, proxy.ts: 3ms, render: 16ms) - GET /api/organization/role?pageSize=3 200 in 134ms (compile: 116ms, proxy.ts: 3ms, render: 15ms) - GET /api/system/profile?pageSize=3 200 in 169ms (compile: 155ms, proxy.ts: 3ms, render: 11ms) - GET /api/system/profile/password?pageSize=3 405 in 197ms (compile: 185ms, proxy.ts: 5ms, render: 6ms) - GET /api/menu?pageSize=3 200 in 147ms (compile: 128ms, proxy.ts: 4ms, render: 15ms) - GET /api/role-permissions?pageSize=3 400 in 216ms (compile: 202ms, proxy.ts: 4ms, render: 11ms) - GET /api/role-permissions/buttons?pageSize=3 400 in 125ms (compile: 107ms, proxy.ts: 4ms, render: 15ms) - GET /api/system/data-scope?pageSize=3 400 in 120ms (compile: 105ms, proxy.ts: 3ms, render: 12ms) - GET /api/auth/cache/clear?pageSize=3 405 in 124ms (compile: 116ms, proxy.ts: 2ms, render: 5ms) -○ Compiling /_not-found/page ... - GET /api/warehouse/list?pageSize=3 404 in 3.8s (compile: 3.7s, proxy.ts: 3ms, render: 126ms) - GET /api/suppliers?pageSize=3 404 in 48ms (compile: 15ms, proxy.ts: 3ms, render: 29ms) -[API Error] 操作失败: Error: Table 'vnerpdacahng.sys_scheduled_task' doesn't exist - at query (src\lib\db\index.ts:113:33) - at GET.errorMessage (src\app\api\system\scheduler\route.ts:33:41) - at errorMessage (src\lib\api-permissions.ts:961:28) - at (src\lib\api-auth.ts:166:12) - at async (src\lib\api-auth.ts:202:14) - 111 | const _sqlStr = typeof sql === 'string' ? sql : String(sql); - 112 | } -> 113 | const [rows] = await pool.query(sql, values); - | ^ - 114 | if (DEBUG_DB) { - 115 | } - 116 | return rows as T[]; { - code: 'ER_NO_SUCH_TABLE', - errno: 1146, - sqlState: '42S02', - sqlMessage: "Table 'vnerpdacahng.sys_scheduled_task' doesn't exist", - sql: 'SELECT COUNT(*) as total FROM sys_scheduled_task WHERE 1=1' -} - GET /api/system/scheduler?pageSize=3 500 in 191ms (compile: 107ms, proxy.ts: 3ms, render: 81ms) - GET /api/init/settings-seed?pageSize=3 405 in 298ms (compile: 287ms, proxy.ts: 3ms, render: 8ms) - GET /api/init/business-seed?pageSize=3 405 in 129ms (compile: 115ms, proxy.ts: 6ms, render: 9ms) - GET /api/settings/system?pageSize=3 200 in 289ms (compile: 135ms, proxy.ts: 5ms, render: 149ms) - GET /api/system/user?pageSize=3 200 in 82ms (compile: 55ms, proxy.ts: 6ms, render: 22ms) - GET /api/system/roles?pageSize=3 200 in 58ms (compile: 41ms, proxy.ts: 3ms, render: 14ms) - GET /api/organization/employee?pageSize=3 200 in 75ms (compile: 49ms, proxy.ts: 5ms, render: 22ms) - GET /api/hr/attendance?pageSize=3 200 in 162ms (compile: 132ms, proxy.ts: 5ms, render: 25ms) - GET /api/hr/certificates/expiring?pageSize=3 200 in 153ms (compile: 125ms, proxy.ts: 3ms, render: 25ms) - GET /api/hr/certificates?pageSize=3 200 in 159ms (compile: 137ms, proxy.ts: 3ms, render: 18ms) - GET /api/upload?pageSize=3 405 in 120ms (compile: 111ms, proxy.ts: 3ms, render: 6ms) - GET /api/hr/mes-sync/piece-work?pageSize=3 200 in 153ms (compile: 132ms, proxy.ts: 3ms, render: 18ms) - GET /api/hr/mes-sync/sync?pageSize=3 404 in 48ms (compile: 10ms, proxy.ts: 3ms, render: 36ms) - GET /api/hr/performance?pageSize=3 200 in 3.0s (compile: 2.2s, proxy.ts: 3ms, generate-params: 972ms, render: 740ms) - GET /api/hr/reports/labor-cost?pageSize=3 200 in 150ms (compile: 120ms, proxy.ts: 5ms, render: 26ms) - GET /api/hr/reports/salary-structure?pageSize=3 200 in 136ms (compile: 113ms, proxy.ts: 4ms, render: 19ms) -[API Error] 获取人力流动报表失败: Error: Failed query: - SELECT - COUNT(*) as totalEmployees, - SUM(CASE WHEN status = 3 THEN 1 ELSE 0 END) as resignedCount - FROM sys_employee WHERE deleted = 0 - -params: - at async GET.errorMessage (src\app\api\hr\reports\turnover\route.ts:10:20) - at async errorMessage (src\lib\api-permissions.ts:961:22) - at async (src\lib\api-auth.ts:202:14) - 8 | - 9 | export const GET = withPermission(async (_request: NextRequest) => { -> 10 | const [totals] = await db.execute(sql` - | ^ - 11 | SELECT - 12 | COUNT(*) as totalEmployees, - 13 | SUM(CASE WHEN status = 3 THEN 1 ELSE 0 END) as resignedCount { - query: '\n' + - ' SELECT\n' + - ' COUNT(*) as totalEmployees,\n' + - ' SUM(CASE WHEN status = 3 THEN 1 ELSE 0 END) as resignedCount\n' + - ' FROM sys_employee WHERE deleted = 0\n' + - ' ', - params: [], - [cause]: Error: Unknown column 'deleted' in 'where clause' - at GET.errorMessage (src\app\api\hr\reports\turnover\route.ts:10:29) - at errorMessage (src\lib\api-permissions.ts:961:28) - at (src\lib\api-auth.ts:166:12) - at async (src\lib\api-auth.ts:202:14) - 8 | - 9 | export const GET = withPermission(async (_request: NextRequest) => { - > 10 | const [totals] = await db.execute(sql` - | ^ - 11 | SELECT - 12 | COUNT(*) as totalEmployees, - 13 | SUM(CASE WHEN status = 3 THEN 1 ELSE 0 END) as resignedCount { - code: 'ER_BAD_FIELD_ERROR', - errno: 1054, - sqlState: '42S22', - sqlMessage: "Unknown column 'deleted' in 'where clause'", - sql: '\n' + - ' SELECT\n' + - ' COUNT(*) as totalEmployees,\n' + - ' SUM(CASE WHEN status = 3 THEN 1 ELSE 0 END) as resignedCount\n' + - ' FROM sys_employee WHERE deleted = 0\n' + - ' ' - } -} - GET /api/hr/reports/turnover?pageSize=3 500 in 307ms (compile: 133ms, proxy.ts: 3ms, render: 171ms) - GET /api/hr/salary/calculate?pageSize=3 400 in 144ms (compile: 127ms, proxy.ts: 4ms, render: 13ms) - GET /api/hr/salary?pageSize=3 200 in 15ms (compile: 1988µs, proxy.ts: 4ms, render: 9ms) - GET /api/salary/payslips?pageSize=3 404 in 46ms (compile: 10ms, proxy.ts: 4ms, render: 32ms) - GET /api/hr/piece-rate?pageSize=3 404 in 37ms (compile: 7ms, proxy.ts: 2ms, render: 28ms) - GET /api/hr/piece-work?pageSize=3 404 in 39ms (compile: 7ms, proxy.ts: 5ms, render: 27ms) - GET /api/hr/schedules?pageSize=3 200 in 138ms (compile: 111ms, proxy.ts: 3ms, render: 24ms) - GET /api/hr/shifts?pageSize=3 200 in 131ms (compile: 113ms, proxy.ts: 3ms, render: 14ms) - GET /api/hr/skills?pageSize=3 200 in 169ms (compile: 138ms, proxy.ts: 3ms, render: 28ms) - GET /api/hr/training?pageSize=3 200 in 138ms (compile: 114ms, proxy.ts: 3ms, render: 21ms) - GET /api/dcprint/process-cards?pageSize=3 200 in 163ms (compile: 144ms, proxy.ts: 4ms, render: 15ms) - GET /api/dcprint/process-cards/detail?pageSize=3 404 in 54ms (compile: 15ms, proxy.ts: 5ms, render: 33ms) - GET /api/dcprint/scan?pageSize=3 405 in 117ms (compile: 107ms, proxy.ts: 2ms, render: 8ms) - GET /api/prepress/die?pageSize=3 200 in 148ms (compile: 116ms, proxy.ts: 3ms, render: 29ms) - GET /api/prepress/ink?pageSize=3 200 in 174ms (compile: 149ms, proxy.ts: 4ms, render: 21ms) - GET /api/dcprint/formula/color?pageSize=3 200 in 197ms (compile: 168ms, proxy.ts: 3ms, render: 26ms) - GET /api/dcprint/formula/version?pageSize=3 400 in 152ms (compile: 131ms, proxy.ts: 3ms, render: 18ms) - GET /api/dcprint/formula/version?pageSize=3 400 in 19ms (compile: 4ms, proxy.ts: 4ms, render: 11ms) - GET /api/dcprint/ink-mixed?pageSize=3 200 in 151ms (compile: 122ms, proxy.ts: 3ms, render: 27ms) - GET /api/dcprint/ink?pageSize=3 200 in 140ms (compile: 118ms, proxy.ts: 5ms, render: 17ms) - GET /api/dcprint/ink-opening?pageSize=3 200 in 146ms (compile: 124ms, proxy.ts: 3ms, render: 19ms) - GET /api/inventory/materials?pageSize=3 200 in 170ms (compile: 127ms, proxy.ts: 3ms, render: 40ms) - GET /api/base-inks?pageSize=3 404 in 50ms (compile: 15ms, proxy.ts: 3ms, render: 32ms) -[API Error] 请求处理失败: Error: Table 'vnerpdacahng.ink_usage' doesn't exist - at query (src\lib\db\index.ts:113:33) - at (src\app\api\ink-usages\route.ts:54:10) - at errorMessage (src\lib\api-permissions.ts:961:28) - at (src\lib\api-auth.ts:166:12) - at async (src\lib\api-auth.ts:202:14) - 111 | const _sqlStr = typeof sql === 'string' ? sql : String(sql); - 112 | } -> 113 | const [rows] = await pool.query(sql, values); - | ^ - 114 | if (DEBUG_DB) { - 115 | } - 116 | return rows as T[]; { - code: 'ER_NO_SUCH_TABLE', - errno: 1146, - sqlState: '42S02', - sqlMessage: "Table 'vnerpdacahng.ink_usage' doesn't exist", - sql: '\n' + - ' SELECT iu.*, bi.ink_code, bi.ink_name, bi.unit, sp.plate_code\n' + - ' FROM ink_usage iu\n' + - ' LEFT JOIN base_ink bi ON iu.ink_id = bi.id\n' + - ' LEFT JOIN prd_screen_plate sp ON iu.screen_plate_id = sp.id\n' + - ' WHERE iu.deleted = 0\n' + - ' ORDER BY iu.usage_date DESC\n' + - ' LIMIT 3 OFFSET 0\n' + - ' ' -} - GET /api/ink-usages?pageSize=3 500 in 216ms (compile: 112ms, proxy.ts: 7ms, render: 97ms) - GET /api/dcprint/labels?pageSize=3 200 in 187ms (compile: 161ms, proxy.ts: 3ms, render: 22ms) - GET /api/warehouse/inbound/cutting?pageSize=3 200 in 65ms (compile: 47ms, proxy.ts: 3ms, render: 16ms) - GET /api/dcprint/sample-card?pageSize=3 200 in 312ms (compile: 279ms, proxy.ts: 4ms, render: 28ms) - GET /api/dcprint/sample-card?pageSize=3 200 in 15ms (compile: 3ms, proxy.ts: 4ms, render: 9ms) - GET /api/screen-plates?pageSize=3 200 in 135ms (compile: 109ms, proxy.ts: 5ms, render: 22ms) - GET /api/screen-plates/history?pageSize=3 400 in 157ms (compile: 138ms, proxy.ts: 3ms, render: 16ms) -[API Error] 请求处理失败: Error: Unknown column 'deleted' in 'where clause' - at query (src\lib\db\index.ts:113:33) - at MysqlToolRepository.findList (src\infrastructure\repositories\MysqlToolRepository.ts:53:34) - at ToolManagementService.listTools (src\application\services\ToolManagementService.ts:148:40) - at GET.logTitle (src\app\api\dcprint\tool\route.ts:20:34) - at errorMessage (src\lib\api-permissions.ts:961:28) - at (src\lib\api-auth.ts:166:12) - at async (src\lib\api-auth.ts:202:14) - 111 | const _sqlStr = typeof sql === 'string' ? sql : String(sql); - 112 | } -> 113 | const [rows] = await pool.query(sql, values); - | ^ - 114 | if (DEBUG_DB) { - 115 | } - 116 | return rows as T[]; { - code: 'ER_BAD_FIELD_ERROR', - errno: 1054, - sqlState: '42S22', - sqlMessage: "Unknown column 'deleted' in 'where clause'", - sql: 'SELECT COUNT(*) as total FROM dcprint_tool WHERE deleted = 0' -} - GET /api/dcprint/tool?pageSize=3 500 in 205ms (compile: 117ms, proxy.ts: 2ms, render: 86ms) -[API Error] 请求处理失败: Error: Unknown column 'deleted' in 'where clause' - at query (src\lib\db\index.ts:113:33) - at MysqlToolRepository.countByStatus (src\infrastructure\repositories\MysqlToolRepository.ts:232:29) - at ToolManagementService.getDashboard (src\application\services\ToolManagementService.ts:670:39) - at GET.logTitle (src\app\api\dcprint\tool\dashboard\route.ts:11:37) - at errorMessage (src\lib\api-permissions.ts:961:28) - at (src\lib\api-auth.ts:166:12) - at async (src\lib\api-auth.ts:202:14) - 111 | const _sqlStr = typeof sql === 'string' ? sql : String(sql); - 112 | } -> 113 | const [rows] = await pool.query(sql, values); - | ^ - 114 | if (DEBUG_DB) { - 115 | } - 116 | return rows as T[]; { - code: 'ER_BAD_FIELD_ERROR', - errno: 1054, - sqlState: '42S22', - sqlMessage: "Unknown column 'deleted' in 'where clause'", - sql: 'SELECT\n' + - ' COUNT(*) as total,\n' + - ' SUM(CASE WHEN status = 2 THEN 1 ELSE 0 END) as active,\n' + - ' SUM(CASE WHEN status = 4 THEN 1 ELSE 0 END) as warning,\n' + - ' SUM(CASE WHEN status = 3 THEN 1 ELSE 0 END) as maintenance,\n' + - ' SUM(CASE WHEN status = 5 THEN 1 ELSE 0 END) as scrapped,\n' + - ' COALESCE(SUM(net_value), 0) as total_net_value\n' + - ' FROM dcprint_tool WHERE deleted = 0' -} - GET /api/dcprint/tool/dashboard?pageSize=3 500 in 233ms (compile: 125ms, proxy.ts: 5ms, render: 103ms) -[API Error] 请求处理失败: Error: Unknown column 'deleted' in 'where clause' - at query (src\lib\db\index.ts:113:33) - at MysqlToolRepository.findList (src\infrastructure\repositories\MysqlToolRepository.ts:53:34) - at ToolManagementService.listTools (src\application\services\ToolManagementService.ts:148:40) - at GET.logTitle (src\app\api\dcprint\tool\route.ts:20:34) - at errorMessage (src\lib\api-permissions.ts:961:28) - at (src\lib\api-auth.ts:166:12) - at async (src\lib\api-auth.ts:202:14) - 111 | const _sqlStr = typeof sql === 'string' ? sql : String(sql); - 112 | } -> 113 | const [rows] = await pool.query(sql, values); - | ^ - 114 | if (DEBUG_DB) { - 115 | } - 116 | return rows as T[]; { - code: 'ER_BAD_FIELD_ERROR', - errno: 1054, - sqlState: '42S22', - sqlMessage: "Unknown column 'deleted' in 'where clause'", - sql: 'SELECT COUNT(*) as total FROM dcprint_tool WHERE deleted = 0' -} - GET /api/dcprint/tool?pageSize=3 500 in 64ms (compile: 3ms, proxy.ts: 3ms, render: 59ms) - GET /api/engineering/sample-to-mass?pageSize=3 200 in 142ms (compile: 118ms, proxy.ts: 3ms, render: 21ms) - GET /api/upload/sop?pageSize=3 405 in 155ms (compile: 142ms, proxy.ts: 3ms, render: 10ms) - GET /api/engineering/sop?pageSize=3 200 in 157ms (compile: 126ms, proxy.ts: 4ms, render: 26ms) - GET /api/dashboard/ceo?pageSize=3 200 in 195ms (compile: 122ms, proxy.ts: 7ms, render: 66ms) - GET /api/dashboard/finance?pageSize=3 200 in 144ms (compile: 116ms, proxy.ts: 4ms, render: 24ms) - GET /api/dashboard?pageSize=3 200 in 64ms (compile: 42ms, proxy.ts: 5ms, render: 18ms) - GET /api/dashboard/production?pageSize=3 200 in 159ms (compile: 132ms, proxy.ts: 2ms, render: 25ms) -[2026-08-25 11:54:57.072 +0800] ERROR: Dashboard query failed - module: "dashboard" - action: "sales" - GET /api/dashboard/sales?pageSize=3 200 in 164ms (compile: 131ms, proxy.ts: 7ms, render: 26ms) -[2026-08-25 11:54:57.251 +0800] ERROR: Dashboard query failed - module: "dashboard" - action: "warehouse" -[2026-08-25 11:54:57.255 +0800] ERROR: Dashboard query failed - module: "dashboard" - action: "warehouse" -[2026-08-25 11:54:57.269 +0800] ERROR: Dashboard query failed - module: "dashboard" - action: "warehouse" - GET /api/dashboard/warehouse?pageSize=3 200 in 184ms (compile: 146ms, proxy.ts: 3ms, render: 36ms) -[API Error] 请求处理失败: Error: Table 'vnerpdacahng.sales_order' doesn't exist - at query (src\lib\db\index.ts:113:33) - at (src\app\api\reports\dashboard\route.ts:19:40) - at errorMessage (src\lib\api-permissions.ts:961:28) - at (src\lib\api-auth.ts:166:12) - at async (src\lib\api-auth.ts:202:14) - 111 | const _sqlStr = typeof sql === 'string' ? sql : String(sql); - 112 | } -> 113 | const [rows] = await pool.query(sql, values); - | ^ - 114 | if (DEBUG_DB) { - 115 | } - 116 | return rows as T[]; { - code: 'ER_NO_SUCH_TABLE', - errno: 1146, - sqlState: '42S02', - sqlMessage: "Table 'vnerpdacahng.sales_order' doesn't exist", - sql: 'SELECT\n' + - ' COUNT(*) as total_orders,\n' + - ' SUM(CASE WHEN status >= 3 THEN 1 ELSE 0 END) as completed_orders,\n' + - ' SUM(CASE WHEN status = 1 THEN 1 ELSE 0 END) as pending_orders,\n' + - ' COALESCE(SUM(total_amount), 0) as total_amount,\n' + - ' COALESCE(SUM(CASE WHEN status >= 3 THEN total_amount ELSE 0 END), 0) as completed_amount\n' + - ' FROM sales_order\n' + - " WHERE deleted = 0 AND order_date >= '2026-07-26'" -} - GET /api/reports/dashboard?pageSize=3 500 in 209ms (compile: 114ms, proxy.ts: 3ms, render: 92ms) - GET /api/reports/purchase-supplier?pageSize=3 200 in 172ms (compile: 138ms, proxy.ts: 3ms, render: 31ms) - GET /api/reports/sales-customer?pageSize=3 200 in 131ms (compile: 110ms, proxy.ts: 5ms, render: 15ms) - GET /api/equipment/calibration?pageSize=3 200 in 155ms (compile: 136ms, proxy.ts: 2ms, render: 17ms) - GET /api/equipment/plan?pageSize=3 200 in 157ms (compile: 123ms, proxy.ts: 3ms, render: 31ms) - GET /api/equipment?pageSize=3 200 in 159ms (compile: 134ms, proxy.ts: 3ms, render: 22ms) - GET /api/equipment/repair?pageSize=3 200 in 193ms (compile: 161ms, proxy.ts: 3ms, render: 29ms) - GET /api/equipment/scrap?pageSize=3 200 in 153ms (compile: 126ms, proxy.ts: 5ms, render: 22ms) -[API Error] 请求处理失败: Error: Illegal mix of collations (utf8mb4_unicode_ci,IMPLICIT) and (utf8mb4_0900_ai_ci,IMPLICIT) for operation '=' - at query (src\lib\db\index.ts:113:33) - at (src\app\api\qrcode\route.ts:38:34) - at async errorMessage (src\lib\api-permissions.ts:961:22) - at async (src\lib\api-auth.ts:202:14) - 111 | const _sqlStr = typeof sql === 'string' ? sql : String(sql); - 112 | } -> 113 | const [rows] = await pool.query(sql, values); - | ^ - 114 | if (DEBUG_DB) { - 115 | } - 116 | return rows as T[]; { - code: 'ER_CANT_AGGREGATE_2COLLATIONS', - errno: 1267, - sqlState: 'HY000', - sqlMessage: "Illegal mix of collations (utf8mb4_unicode_ci,IMPLICIT) and (utf8mb4_0900_ai_ci,IMPLICIT) for operation '='", - sql: 'SELECT q.*, b.available_qty AS batch_available_qty, b.quantity AS batch_total_qty, b.status AS batch_status\n' + - ' FROM qrcode_record q\n' + - ' LEFT JOIN inv_inventory_batch b ON q.batch_no = b.batch_no AND b.deleted = 0\n' + - ' WHERE q.deleted = 0 ORDER BY q.create_time DESC LIMIT 3 OFFSET 0' -} - GET /api/qrcode?pageSize=3 500 in 231ms (compile: 137ms, proxy.ts: 4ms, render: 91ms) -[API Error] 请求处理失败: Error: Unknown column 'deleted' in 'where clause' - at query (src\lib\db\index.ts:113:33) - at (src\app\api\quality\complaint\route.ts:34:39) - at errorMessage (src\lib\api-permissions.ts:961:28) - at (src\lib\api-auth.ts:166:12) - at async (src\lib\api-auth.ts:202:14) - 111 | const _sqlStr = typeof sql === 'string' ? sql : String(sql); - 112 | } -> 113 | const [rows] = await pool.query(sql, values); - | ^ - 114 | if (DEBUG_DB) { - 115 | } - 116 | return rows as T[]; { - code: 'ER_BAD_FIELD_ERROR', - errno: 1054, - sqlState: '42S22', - sqlMessage: "Unknown column 'deleted' in 'where clause'", - sql: 'SELECT COUNT(*) as total FROM qms_complaint WHERE deleted = 0' -} - GET /api/quality/complaint?pageSize=3 500 in 136ms (compile: 7ms, proxy.ts: 4ms, render: 125ms) -[API Error] 请求处理失败: Error: Unknown column 'deleted' in 'where clause' - at query (src\lib\db\index.ts:113:33) - at (src\app\api\quality\lab-test\route.ts:34:39) - at errorMessage (src\lib\api-permissions.ts:961:28) - at (src\lib\api-auth.ts:166:12) - at async (src\lib\api-auth.ts:202:14) - 111 | const _sqlStr = typeof sql === 'string' ? sql : String(sql); - 112 | } -> 113 | const [rows] = await pool.query(sql, values); - | ^ - 114 | if (DEBUG_DB) { - 115 | } - 116 | return rows as T[]; { - code: 'ER_BAD_FIELD_ERROR', - errno: 1054, - sqlState: '42S22', - sqlMessage: "Unknown column 'deleted' in 'where clause'", - sql: 'SELECT COUNT(*) as total FROM qms_lab_test WHERE deleted = 0' -} - GET /api/quality/lab-test?pageSize=3 500 in 80ms (compile: 5ms, proxy.ts: 5ms, render: 70ms) -[API Error] 请求处理失败: Error: Unknown column 'deleted' in 'where clause' - at query (src\lib\db\index.ts:113:33) - at (src\app\api\quality\supplier-audit\route.ts:34:39) - at errorMessage (src\lib\api-permissions.ts:961:28) - at (src\lib\api-auth.ts:166:12) - at async (src\lib\api-auth.ts:202:14) - 111 | const _sqlStr = typeof sql === 'string' ? sql : String(sql); - 112 | } -> 113 | const [rows] = await pool.query(sql, values); - | ^ - 114 | if (DEBUG_DB) { - 115 | } - 116 | return rows as T[]; { - code: 'ER_BAD_FIELD_ERROR', - errno: 1054, - sqlState: '42S22', - sqlMessage: "Unknown column 'deleted' in 'where clause'", - sql: 'SELECT COUNT(*) as total FROM qms_supplier_audit WHERE deleted = 0' -} - GET /api/quality/supplier-audit?pageSize=3 500 in 78ms (compile: 6ms, proxy.ts: 4ms, render: 67ms) -[API Error] 操作失败: Error: Table 'vnerpdacahng.sys_announcement' doesn't exist - at query (src\lib\db\index.ts:113:33) - at GET.errorMessage (src\app\api\system\announcement\route.ts:32:41) - at errorMessage (src\lib\api-permissions.ts:961:28) - at (src\lib\api-auth.ts:166:12) - at async (src\lib\api-auth.ts:202:14) - 111 | const _sqlStr = typeof sql === 'string' ? sql : String(sql); - 112 | } -> 113 | const [rows] = await pool.query(sql, values); - | ^ - 114 | if (DEBUG_DB) { - 115 | } - 116 | return rows as T[]; { - code: 'ER_NO_SUCH_TABLE', - errno: 1146, - sqlState: '42S02', - sqlMessage: "Table 'vnerpdacahng.sys_announcement' doesn't exist", - sql: "SELECT COUNT(*) as total FROM sys_announcement a WHERE 1=1 AND a.status = 'published' AND (a.expire_time IS NULL OR a.expire_time > NOW())" -} - GET /api/system/announcement?pageSize=3 500 in 89ms (compile: 3ms, proxy.ts: 3ms, render: 83ms) -[API Error] 请求处理失败: Error: Table 'vnerpdacahng.label_template' doesn't exist - at query (src\lib\db\index.ts:113:33) - at (src\app\api\trace\label\route.ts:16:27) - at errorMessage (src\lib\api-permissions.ts:961:28) - at (src\lib\api-auth.ts:166:12) - at async (src\lib\api-auth.ts:202:14) - 111 | const _sqlStr = typeof sql === 'string' ? sql : String(sql); - 112 | } -> 113 | const [rows] = await pool.query(sql, values); - | ^ - 114 | if (DEBUG_DB) { - 115 | } - 116 | return rows as T[]; { - code: 'ER_NO_SUCH_TABLE', - errno: 1146, - sqlState: '42S02', - sqlMessage: "Table 'vnerpdacahng.label_template' doesn't exist", - sql: 'SELECT * FROM label_template WHERE status = 1 ORDER BY id ASC' -} - GET /api/trace/label?pageSize=3 500 in 103ms (compile: 5ms, proxy.ts: 4ms, render: 94ms) -[API Error] 请求处理失败: Error: Unknown column 'business_type' in 'field list' - at query (src\lib\db\index.ts:113:33) - at GET.logTitle (src\app\api\system\oper-log\route.ts:52:36) - at async errorMessage (src\lib\api-permissions.ts:961:22) - at async (src\lib\api-auth.ts:202:14) - 111 | const _sqlStr = typeof sql === 'string' ? sql : String(sql); - 112 | } -> 113 | const [rows] = await pool.query(sql, values); - | ^ - 114 | if (DEBUG_DB) { - 115 | } - 116 | return rows as T[]; { - code: 'ER_BAD_FIELD_ERROR', - errno: 1054, - sqlState: '42S22', - sqlMessage: "Unknown column 'business_type' in 'field list'", - sql: "SELECT id, COALESCE(operation, '') as title, COALESCE(username, '') as oper_name,\n" + - " COALESCE(operation, '') as oper_type, COALESCE(method, '') as oper_method,\n" + - " COALESCE(request_url, '') as oper_url, COALESCE(ip, '') as oper_ip,\n" + - ' COALESCE(create_time, NOW()) as oper_time, status,\n' + - " COALESCE(business_type, '') as business_type,\n" + - " COALESCE(business_id, '') as business_id,\n" + - " COALESCE(request_params, '') as request_params,\n" + - " COALESCE(response_result, '') as response_result\n" + - ' FROM sys_operation_log WHERE 1=1\n' + - ' ORDER BY create_time DESC\n' + - ' LIMIT 3 OFFSET 0' -} - GET /api/system/oper-log?pageSize=3 500 in 92ms (compile: 4ms, proxy.ts: 4ms, render: 85ms) -[API Error] 操作失败: Error: Table 'vnerpdacahng.sys_scheduled_task' doesn't exist - at query (src\lib\db\index.ts:113:33) - at GET.errorMessage (src\app\api\system\scheduler\route.ts:33:41) - at errorMessage (src\lib\api-permissions.ts:961:28) - at (src\lib\api-auth.ts:166:12) - at async (src\lib\api-auth.ts:202:14) - 111 | const _sqlStr = typeof sql === 'string' ? sql : String(sql); - 112 | } -> 113 | const [rows] = await pool.query(sql, values); - | ^ - 114 | if (DEBUG_DB) { - 115 | } - 116 | return rows as T[]; { - code: 'ER_NO_SUCH_TABLE', - errno: 1146, - sqlState: '42S02', - sqlMessage: "Table 'vnerpdacahng.sys_scheduled_task' doesn't exist", - sql: 'SELECT COUNT(*) as total FROM sys_scheduled_task WHERE 1=1' -} - GET /api/system/scheduler?pageSize=3 500 in 71ms (compile: 2ms, proxy.ts: 2ms, render: 66ms) -[API Error] 获取人力流动报表失败: Error: Failed query: - SELECT - COUNT(*) as totalEmployees, - SUM(CASE WHEN status = 3 THEN 1 ELSE 0 END) as resignedCount - FROM sys_employee WHERE deleted = 0 - -params: - at async GET.errorMessage (src\app\api\hr\reports\turnover\route.ts:10:20) - at async errorMessage (src\lib\api-permissions.ts:961:22) - at async (src\lib\api-auth.ts:202:14) - 8 | - 9 | export const GET = withPermission(async (_request: NextRequest) => { -> 10 | const [totals] = await db.execute(sql` - | ^ - 11 | SELECT - 12 | COUNT(*) as totalEmployees, - 13 | SUM(CASE WHEN status = 3 THEN 1 ELSE 0 END) as resignedCount { - query: '\n' + - ' SELECT\n' + - ' COUNT(*) as totalEmployees,\n' + - ' SUM(CASE WHEN status = 3 THEN 1 ELSE 0 END) as resignedCount\n' + - ' FROM sys_employee WHERE deleted = 0\n' + - ' ', - params: [], - [cause]: Error: Unknown column 'deleted' in 'where clause' - at GET.errorMessage (src\app\api\hr\reports\turnover\route.ts:10:29) - at errorMessage (src\lib\api-permissions.ts:961:28) - at (src\lib\api-auth.ts:166:12) - at async (src\lib\api-auth.ts:202:14) - 8 | - 9 | export const GET = withPermission(async (_request: NextRequest) => { - > 10 | const [totals] = await db.execute(sql` - | ^ - 11 | SELECT - 12 | COUNT(*) as totalEmployees, - 13 | SUM(CASE WHEN status = 3 THEN 1 ELSE 0 END) as resignedCount { - code: 'ER_BAD_FIELD_ERROR', - errno: 1054, - sqlState: '42S22', - sqlMessage: "Unknown column 'deleted' in 'where clause'", - sql: '\n' + - ' SELECT\n' + - ' COUNT(*) as totalEmployees,\n' + - ' SUM(CASE WHEN status = 3 THEN 1 ELSE 0 END) as resignedCount\n' + - ' FROM sys_employee WHERE deleted = 0\n' + - ' ' - } -} - GET /api/hr/reports/turnover?pageSize=3 500 in 138ms (compile: 3ms, proxy.ts: 4ms, render: 131ms) -[API Error] 请求处理失败: Error: Table 'vnerpdacahng.ink_usage' doesn't exist - at query (src\lib\db\index.ts:113:33) - at (src\app\api\ink-usages\route.ts:54:10) - at errorMessage (src\lib\api-permissions.ts:961:28) - at (src\lib\api-auth.ts:166:12) - at async (src\lib\api-auth.ts:202:14) - 111 | const _sqlStr = typeof sql === 'string' ? sql : String(sql); - 112 | } -> 113 | const [rows] = await pool.query(sql, values); - | ^ - 114 | if (DEBUG_DB) { - 115 | } - 116 | return rows as T[]; { - code: 'ER_NO_SUCH_TABLE', - errno: 1146, - sqlState: '42S02', - sqlMessage: "Table 'vnerpdacahng.ink_usage' doesn't exist", - sql: '\n' + - ' SELECT iu.*, bi.ink_code, bi.ink_name, bi.unit, sp.plate_code\n' + - ' FROM ink_usage iu\n' + - ' LEFT JOIN base_ink bi ON iu.ink_id = bi.id\n' + - ' LEFT JOIN prd_screen_plate sp ON iu.screen_plate_id = sp.id\n' + - ' WHERE iu.deleted = 0\n' + - ' ORDER BY iu.usage_date DESC\n' + - ' LIMIT 3 OFFSET 0\n' + - ' ' -} - GET /api/ink-usages?pageSize=3 500 in 80ms (compile: 3ms, proxy.ts: 2ms, render: 74ms) -[API Error] 请求处理失败: Error: Unknown column 'default_currency' in 'field list' - at query (src\lib\db\index.ts:113:33) - at queryPaginated (src\lib\db\index.ts:357:22) - at async (src\app\api\purchase\suppliers\route.ts:68:18) - at async errorMessage (src\lib\api-permissions.ts:961:22) - at async (src\lib\api-auth.ts:202:14) - 111 | const _sqlStr = typeof sql === 'string' ? sql : String(sql); - 112 | } -> 113 | const [rows] = await pool.query(sql, values); - | ^ - 114 | if (DEBUG_DB) { - 115 | } - 116 | return rows as T[]; { - code: 'ER_BAD_FIELD_ERROR', - errno: 1054, - sqlState: '42S22', - sqlMessage: "Unknown column 'default_currency' in 'field list'", - sql: '\n' + - ' SELECT\n' + - ' id,\n' + - ' supplier_code,\n' + - ' default_currency,\n' + - ' supplier_name,\n' + - ' short_name,\n' + - ' supplier_type,\n' + - ' contact_name,\n' + - ' contact_phone,\n' + - ' contact_email,\n' + - ' address,\n' + - ' credit_level,\n' + - ' cooperation_status,\n' + - ' settlement_method,\n' + - ' payment_terms,\n' + - ' status,\n' + - ' remark,\n' + - ' create_time,\n' + - ' update_time\n' + - ' FROM pur_supplier\n' + - ' WHERE deleted = 0\n' + - ' ORDER BY create_time DESC LIMIT 3 OFFSET 0' -} - GET /api/purchase/suppliers?pageSize=3 500 in 72ms (compile: 3ms, proxy.ts: 2ms, render: 67ms) -[API Error] 请求处理失败: Error: Illegal mix of collations (utf8mb4_unicode_ci,IMPLICIT) and (utf8mb4_0900_ai_ci,IMPLICIT) for operation '=' - at query (src\lib\db\index.ts:113:33) - at (src\app\api\qrcode\route.ts:38:34) - at async errorMessage (src\lib\api-permissions.ts:961:22) - at async (src\lib\api-auth.ts:202:14) - 111 | const _sqlStr = typeof sql === 'string' ? sql : String(sql); - 112 | } -> 113 | const [rows] = await pool.query(sql, values); - | ^ - 114 | if (DEBUG_DB) { - 115 | } - 116 | return rows as T[]; { - code: 'ER_CANT_AGGREGATE_2COLLATIONS', - errno: 1267, - sqlState: 'HY000', - sqlMessage: "Illegal mix of collations (utf8mb4_unicode_ci,IMPLICIT) and (utf8mb4_0900_ai_ci,IMPLICIT) for operation '='", - sql: 'SELECT q.*, b.available_qty AS batch_available_qty, b.quantity AS batch_total_qty, b.status AS batch_status\n' + - ' FROM qrcode_record q\n' + - ' LEFT JOIN inv_inventory_batch b ON q.batch_no = b.batch_no AND b.deleted = 0\n' + - ' WHERE q.deleted = 0 ORDER BY q.create_time DESC LIMIT 3 OFFSET 0' -} - GET /api/qrcode?pageSize=3 500 in 96ms (compile: 3ms, proxy.ts: 4ms, render: 90ms) -[API Error] 请求处理失败: Error: Table 'vnerpdacahng.sales_order' doesn't exist - at query (src\lib\db\index.ts:113:33) - at (src\app\api\reports\dashboard\route.ts:19:40) - at errorMessage (src\lib\api-permissions.ts:961:28) - at (src\lib\api-auth.ts:166:12) - at async (src\lib\api-auth.ts:202:14) - 111 | const _sqlStr = typeof sql === 'string' ? sql : String(sql); - 112 | } -> 113 | const [rows] = await pool.query(sql, values); - | ^ - 114 | if (DEBUG_DB) { - 115 | } - 116 | return rows as T[]; { - code: 'ER_NO_SUCH_TABLE', - errno: 1146, - sqlState: '42S02', - sqlMessage: "Table 'vnerpdacahng.sales_order' doesn't exist", - sql: 'SELECT\n' + - ' COUNT(*) as total_orders,\n' + - ' SUM(CASE WHEN status >= 3 THEN 1 ELSE 0 END) as completed_orders,\n' + - ' SUM(CASE WHEN status = 1 THEN 1 ELSE 0 END) as pending_orders,\n' + - ' COALESCE(SUM(total_amount), 0) as total_amount,\n' + - ' COALESCE(SUM(CASE WHEN status >= 3 THEN total_amount ELSE 0 END), 0) as completed_amount\n' + - ' FROM sales_order\n' + - " WHERE deleted = 0 AND order_date >= '2026-07-26'" -} - GET /api/reports/dashboard?pageSize=3 500 in 71ms (compile: 4ms, proxy.ts: 4ms, render: 62ms) -[API Error] 请求处理失败: Error: Unknown column 'deleted' in 'where clause' - at query (src\lib\db\index.ts:113:33) - at MysqlToolRepository.findList (src\infrastructure\repositories\MysqlToolRepository.ts:53:34) - at ToolManagementService.listTools (src\application\services\ToolManagementService.ts:148:40) - at GET.logTitle (src\app\api\dcprint\tool\route.ts:20:34) - at errorMessage (src\lib\api-permissions.ts:961:28) - at (src\lib\api-auth.ts:166:12) - at async (src\lib\api-auth.ts:202:14) - 111 | const _sqlStr = typeof sql === 'string' ? sql : String(sql); - 112 | } -> 113 | const [rows] = await pool.query(sql, values); - | ^ - 114 | if (DEBUG_DB) { - 115 | } - 116 | return rows as T[]; { - code: 'ER_BAD_FIELD_ERROR', - errno: 1054, - sqlState: '42S22', - sqlMessage: "Unknown column 'deleted' in 'where clause'", - sql: 'SELECT COUNT(*) as total FROM dcprint_tool WHERE deleted = 0' -} - GET /api/dcprint/tool?pageSize=3 500 in 73ms (compile: 5ms, proxy.ts: 5ms, render: 64ms) - GET /api/purchase/return?pageSize=5 200 in 248ms (compile: 204ms, proxy.ts: 4ms, render: 40ms) -[RepoRegistry] active impl = mysql (default; set REPOSITORY_IMPL=drizzle to switch) -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getSalesOrderRepository() → MysqlSalesOrderRepository (mysql) - GET /api/sales/return?pageSize=5 200 in 295ms (compile: 248ms, proxy.ts: 3ms, render: 44ms) - GET /api/production/product-label?pageSize=5 200 in 136ms (compile: 113ms, proxy.ts: 2ms, render: 20ms) - GET /api/quality/incoming?pageSize=5 200 in 16ms (compile: 3ms, proxy.ts: 3ms, render: 10ms) -[?25h diff --git a/_devlog_s31.txt b/_devlog_s31.txt deleted file mode 100644 index 44ac8709..00000000 --- a/_devlog_s31.txt +++ /dev/null @@ -1,491 +0,0 @@ - -> vnerp@0.1.0 dev -> next dev -p 5000 - -▲ Next.js 16.1.1 (Turbopack) -- Local: http://localhost:5000 -- Network: http://192.168.0.144:5000 -- Environments: .env.local, .env -- Experiments (use with caution): - · optimizePackageImports - -✓ Starting... -✓ Ready in 4.1s -[2026-08-25 14:42:07.119 +0800] INFO: [instrumentation] Server started. OutboxPoller will auto-start on first API request. -○ Compiling /[locale] ... -[2026-08-25 14:42:17.144 +0800] INFO: CacheManager: 使用 InMemoryCacheManager(未配置 REDIS_URL) - GET /api/purchase/return?pageSize=3 200 in 1906ms (compile: 1743ms, proxy.ts: 14ms, render: 149ms) -[RepoRegistry] active impl = mysql (default; set REPOSITORY_IMPL=drizzle to switch) -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getSalesOrderRepository() → MysqlSalesOrderRepository (mysql) - GET /api/sales/return?pageSize=3 200 in 2.5s (compile: 1767ms, proxy.ts: 10ms, render: 766ms) - GET / 200 in 11.6s (compile: 9.2s, proxy.ts: 198ms, generate-params: 2.5s, render: 2.3s) - GET /api/production/product-label?pageSize=3 200 in 471ms (compile: 391ms, proxy.ts: 50ms, render: 31ms) - GET /api/system/notice?page=1&pageSize=10 200 in 540ms (compile: 364ms, proxy.ts: 20ms, render: 156ms) - GET /api/orders/sales?pageSize=20 200 in 553ms (compile: 477ms, proxy.ts: 22ms, render: 54ms) - GET /api/system/config?pageSize=200 200 in 601ms (compile: 444ms, proxy.ts: 11ms, render: 146ms) - GET /api/system/config?pageSize=200 200 in 51ms (compile: 6ms, proxy.ts: 11ms, render: 34ms) - GET /api/system/config?pageSize=200 200 in 47ms (compile: 7ms, proxy.ts: 8ms, render: 32ms) - GET /api/system/config?pageSize=200 200 in 69ms (compile: 9ms, proxy.ts: 16ms, render: 44ms) -[2026-08-25 14:42:22.592 +0800] DEBUG: [inventory.list] 查询库存列表 - ctx: { - "module": "inventory", - "action": "list", - "traceId": "mt8as9vk-r73lu9d" - } - step: "查询库存列表" - data: { - "keyword": null, - "categoryId": null, - "warehouseId": null, - "lowStock": null, - "page": 1, - "pageSize": 100 - } - GET /api/warehouse/inventory?pageSize=100 200 in 308ms (compile: 234ms, proxy.ts: 15ms, render: 59ms) -[2026-08-25 14:42:22.628 +0800] DEBUG: [inventory.list] 查询库存列表 DONE - ctx: { - "module": "inventory", - "action": "list", - "traceId": "mt8as9vk-r73lu9d" - } - step: "查询库存列表" - data: { - "total": 4085, - "page": 1, - "pageSize": 100 - } - GET /api/production/orders?pageSize=20 200 in 303ms (compile: 260ms, proxy.ts: 5ms, render: 38ms) - GET /warehouse/inbound 200 in 4.0s (compile: 3.9s, proxy.ts: 8ms, generate-params: 1594ms, render: 82ms) - GET /api/system/notice?page=1&pageSize=10 200 in 108ms (compile: 31ms, proxy.ts: 30ms, render: 47ms) - GET /api/system/config?pageSize=200 200 in 141ms (compile: 24ms, proxy.ts: 32ms, render: 85ms) - GET /api/system/notice?page=1&pageSize=10 200 in 42ms (compile: 4ms, proxy.ts: 10ms, render: 28ms) - GET /api/system/config?pageSize=200 200 in 60ms (compile: 7ms, proxy.ts: 11ms, render: 42ms) - GET /api/organization/warehouse-category 200 in 720ms (compile: 437ms, proxy.ts: 23ms, render: 260ms) - GET /api/purchase/suppliers?pageSize=1000 200 in 746ms (compile: 368ms, proxy.ts: 23ms, render: 355ms) - GET /api/warehouse?all=true 200 in 771ms (compile: 502ms, proxy.ts: 24ms, render: 245ms) - GET /api/organization?type=company 200 in 528ms (compile: 387ms, proxy.ts: 10ms, render: 131ms) - GET /api/warehouse/inbound/labels?pageSize=1000 200 in 683ms (compile: 476ms, proxy.ts: 16ms, render: 191ms) - GET /api/organization/warehouse-category 200 in 315ms (compile: 34ms, proxy.ts: 36ms, render: 245ms) -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) -[2026-08-25 14:42:57.355 +0800] INFO: Event registry initialized - inboundApprovedHandlers: 6 - purchaseReceivedHandlers: 1 - salesShippedHandlers: 4 - deliveryShippedHandlers: 4 - returnOrderCompletedHandlers: 3 - reconciliationWrittenOffHandlers: 3 - purchaseReturnCompletedHandlers: 3 - purchaseReconWrittenOffHandlers: 3 - workorderCompletedHandlers: 8 - qualityUnqualifiedCompletedHandlers: 2 -[2026-08-25 14:42:57.357 +0800] DEBUG: Event registry already initialized -[2026-08-25 14:42:57.357 +0800] INFO: Outbox poller starting - intervalMs: 5000 - reclaimIntervalMs: 60000 - cleanupIntervalMs: 86400000 - dispatchingTimeoutMinutes: 10 -[2026-08-25 14:42:57.360 +0800] INFO: OutboxPoller auto-started (EVENT_BUS_TYPE=db) -[2026-08-25 14:42:57.360 +0800] INFO: StreamConsumer not started (Redis unavailable, OutboxPoller using direct publish) - GET /api/purchase/suppliers?pageSize=1000 200 in 318ms (compile: 37ms, proxy.ts: 26ms, render: 255ms) - GET /api/warehouse/inbound?page=1&pageSize=1000 200 in 1131ms (compile: 1011ms, proxy.ts: 24ms, render: 96ms) - GET /api/organization?type=company 200 in 322ms (compile: 207ms, proxy.ts: 40ms, render: 75ms) - GET /api/warehouse?all=true 200 in 335ms (compile: 204ms, proxy.ts: 32ms, render: 100ms) - GET /api/system/config?pageSize=200 200 in 383ms (compile: 26ms, proxy.ts: 39ms, render: 319ms) -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) - GET /api/warehouse/inbound/labels?pageSize=1000 200 in 121ms (compile: 26ms, proxy.ts: 36ms, render: 59ms) -[2026-08-25 14:42:57.491 +0800] DEBUG: Event registry already initialized - GET /api/warehouse/inbound?page=1&pageSize=1000 200 in 97ms (compile: 20ms, proxy.ts: 23ms, render: 54ms) - GET /api/system/config?pageSize=200 200 in 92ms (compile: 20ms, proxy.ts: 20ms, render: 51ms) -[2026-08-25 14:43:02.371 +0800] DEBUG: Event registry already initialized -[2026-08-25 14:43:02.377 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-25 14:43:07.373 +0800] DEBUG: Event registry already initialized -[2026-08-25 14:43:07.376 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-25 14:43:12.383 +0800] DEBUG: Event registry already initialized -[2026-08-25 14:43:12.385 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-25 14:43:17.395 +0800] DEBUG: Event registry already initialized -[2026-08-25 14:43:17.398 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" - GET /warehouse/transfer 200 in 2.1s (compile: 2.1s, proxy.ts: 6ms, generate-params: 1089ms, render: 40ms) - GET /api/system/notice?page=1&pageSize=10 200 in 45ms (compile: 14ms, proxy.ts: 12ms, render: 19ms) - GET /api/system/config?pageSize=200 200 in 67ms (compile: 11ms, proxy.ts: 14ms, render: 43ms) - GET /api/system/notice?page=1&pageSize=10 200 in 76ms (compile: 9ms, proxy.ts: 15ms, render: 52ms) - GET /api/organization?type=company 200 in 47ms (compile: 7ms, proxy.ts: 20ms, render: 20ms) - GET /api/system/config?pageSize=200 200 in 77ms (compile: 17ms, proxy.ts: 14ms, render: 46ms) - GET /api/organization?type=company 200 in 27ms (compile: 5ms, proxy.ts: 8ms, render: 14ms) - GET /api/system/config?pageSize=200 200 in 48ms (compile: 3ms, proxy.ts: 5ms, render: 40ms) - GET /api/warehouse/transfer?page=1&pageSize=20&transferNo= 200 in 268ms (compile: 230ms, proxy.ts: 11ms, render: 26ms) - GET /api/system/config?pageSize=200 200 in 85ms (compile: 51ms, proxy.ts: 7ms, render: 28ms) - GET /api/warehouse/transfer?page=1&pageSize=20&transferNo= 200 in 33ms (compile: 3ms, proxy.ts: 11ms, render: 19ms) -[2026-08-25 14:43:22.405 +0800] DEBUG: Event registry already initialized -[2026-08-25 14:43:22.409 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-25 14:43:27.420 +0800] DEBUG: Event registry already initialized -[2026-08-25 14:43:27.422 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-25 14:43:32.425 +0800] DEBUG: Event registry already initialized -[2026-08-25 14:43:32.427 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" - GET /warehouse/transfer 200 in 209ms (compile: 12ms, proxy.ts: 4ms, generate-params: 6ms, render: 194ms) - GET /api/system/config?pageSize=200 200 in 28ms (compile: 3ms, proxy.ts: 4ms, render: 21ms) - GET /api/system/notice?page=1&pageSize=10 200 in 43ms (compile: 11ms, proxy.ts: 9ms, render: 24ms) - GET /api/warehouse/transfer?page=1&pageSize=20&transferNo= 200 in 44ms (compile: 20ms, proxy.ts: 8ms, render: 17ms) - GET /api/system/config?pageSize=200 200 in 57ms (compile: 6ms, proxy.ts: 9ms, render: 42ms) -[2026-08-25 14:43:37.425 +0800] DEBUG: Event registry already initialized -[2026-08-25 14:43:37.427 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" - GET /api/system/config?pageSize=200 200 in 35ms (compile: 5ms, proxy.ts: 5ms, render: 24ms) - GET /warehouse/inventory 200 in 2.1s (compile: 2.1s, proxy.ts: 8ms, generate-params: 1105ms, render: 30ms) - GET /api/system/notice?page=1&pageSize=10 200 in 75ms (compile: 15ms, proxy.ts: 16ms, render: 44ms) - GET /api/system/config?pageSize=200 200 in 90ms (compile: 10ms, proxy.ts: 17ms, render: 63ms) - GET /api/warehouse?all=true&status=active 200 in 76ms (compile: 8ms, proxy.ts: 20ms, render: 48ms) - GET /api/system/notice?page=1&pageSize=10 200 in 86ms (compile: 16ms, proxy.ts: 19ms, render: 51ms) - GET /api/warehouse?all=true&status=active 200 in 72ms (compile: 19ms, proxy.ts: 14ms, render: 39ms) - GET /api/organization?type=company 200 in 75ms (compile: 17ms, proxy.ts: 17ms, render: 41ms) - GET /api/system/config?pageSize=200 200 in 87ms (compile: 14ms, proxy.ts: 16ms, render: 57ms) - GET /api/organization?type=company 200 in 26ms (compile: 4ms, proxy.ts: 7ms, render: 15ms) - GET /api/system/config?pageSize=200 200 in 48ms (compile: 4ms, proxy.ts: 6ms, render: 37ms) - GET /api/warehouse/categories 200 in 344ms (compile: 251ms, proxy.ts: 13ms, render: 80ms) - GET /api/inventory?pageSize=100 200 in 296ms (compile: 256ms, proxy.ts: 9ms, render: 30ms) - GET /api/system/config?pageSize=200 200 in 62ms (compile: 5ms, proxy.ts: 7ms, render: 50ms) - GET /api/warehouse/categories 200 in 43ms (compile: 6ms, proxy.ts: 10ms, render: 28ms) - GET /api/inventory?pageSize=100 200 in 47ms (compile: 8ms, proxy.ts: 10ms, render: 29ms) -[2026-08-25 14:43:42.426 +0800] DEBUG: Event registry already initialized -[2026-08-25 14:43:42.428 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-25 14:43:47.433 +0800] DEBUG: Event registry already initialized -[2026-08-25 14:43:47.436 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-25 14:43:52.435 +0800] DEBUG: Event registry already initialized -[2026-08-25 14:43:52.437 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-25 14:43:57.443 +0800] DEBUG: Event registry already initialized -[2026-08-25 14:43:57.447 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-25 14:44:02.446 +0800] DEBUG: Event registry already initialized -[2026-08-25 14:44:02.448 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-25 14:44:07.460 +0800] DEBUG: Event registry already initialized -[2026-08-25 14:44:07.462 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-25 14:44:12.463 +0800] DEBUG: Event registry already initialized -[2026-08-25 14:44:12.465 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-25 14:44:17.476 +0800] DEBUG: Event registry already initialized -[2026-08-25 14:44:17.479 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-25 14:44:22.478 +0800] DEBUG: Event registry already initialized -[2026-08-25 14:44:22.481 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-25 14:44:27.481 +0800] DEBUG: Event registry already initialized -[2026-08-25 14:44:27.484 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-25 14:44:32.491 +0800] DEBUG: Event registry already initialized -[2026-08-25 14:44:32.494 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-25 14:44:37.495 +0800] DEBUG: Event registry already initialized -[2026-08-25 14:44:37.497 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-25 14:44:42.507 +0800] DEBUG: Event registry already initialized -[2026-08-25 14:44:42.510 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-25 14:44:47.512 +0800] DEBUG: Event registry already initialized -[2026-08-25 14:44:47.514 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-25 14:44:52.519 +0800] DEBUG: Event registry already initialized -[2026-08-25 14:44:52.521 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-25 14:44:57.522 +0800] DEBUG: Event registry already initialized -[2026-08-25 14:44:57.524 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-25 14:45:02.522 +0800] DEBUG: Event registry already initialized -[2026-08-25 14:45:02.525 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-25 14:45:07.534 +0800] DEBUG: Event registry already initialized -[2026-08-25 14:45:07.536 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-25 14:45:12.537 +0800] DEBUG: Event registry already initialized -[2026-08-25 14:45:12.539 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-25 14:45:17.552 +0800] DEBUG: Event registry already initialized -[2026-08-25 14:45:17.554 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-25 14:45:22.566 +0800] DEBUG: Event registry already initialized -[2026-08-25 14:45:22.568 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-25 14:45:27.574 +0800] DEBUG: Event registry already initialized -[2026-08-25 14:45:27.576 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-25 14:45:32.587 +0800] DEBUG: Event registry already initialized -[2026-08-25 14:45:32.589 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-25 14:45:37.598 +0800] DEBUG: Event registry already initialized -[2026-08-25 14:45:37.601 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-25 14:45:42.612 +0800] DEBUG: Event registry already initialized -[2026-08-25 14:45:42.614 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-25 14:45:47.624 +0800] DEBUG: Event registry already initialized -[2026-08-25 14:45:47.626 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-25 14:45:52.626 +0800] DEBUG: Event registry already initialized -[2026-08-25 14:45:52.628 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-25 14:45:57.636 +0800] DEBUG: Event registry already initialized -[2026-08-25 14:45:57.638 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-25 14:46:02.639 +0800] DEBUG: Event registry already initialized -[2026-08-25 14:46:02.640 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-25 14:46:07.643 +0800] DEBUG: Event registry already initialized -[2026-08-25 14:46:07.646 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-25 14:46:12.647 +0800] DEBUG: Event registry already initialized -[2026-08-25 14:46:12.649 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-25 14:46:17.650 +0800] DEBUG: Event registry already initialized -[2026-08-25 14:46:17.654 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-25 14:46:22.666 +0800] DEBUG: Event registry already initialized -[2026-08-25 14:46:22.669 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-25 14:46:27.677 +0800] DEBUG: Event registry already initialized -[2026-08-25 14:46:27.681 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-25 14:46:32.686 +0800] DEBUG: Event registry already initialized -[2026-08-25 14:46:32.689 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-25 14:46:37.690 +0800] DEBUG: Event registry already initialized -[2026-08-25 14:46:37.692 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-25 14:46:42.703 +0800] DEBUG: Event registry already initialized -[2026-08-25 14:46:42.706 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-25 14:46:47.713 +0800] DEBUG: Event registry already initialized -[2026-08-25 14:46:47.716 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-25 14:46:52.714 +0800] DEBUG: Event registry already initialized -[2026-08-25 14:46:52.716 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-25 14:46:57.723 +0800] DEBUG: Event registry already initialized -[2026-08-25 14:46:57.725 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-25 14:47:02.730 +0800] DEBUG: Event registry already initialized -[2026-08-25 14:47:02.732 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-25 14:47:07.739 +0800] DEBUG: Event registry already initialized -[2026-08-25 14:47:07.741 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-25 14:47:12.752 +0800] DEBUG: Event registry already initialized -[2026-08-25 14:47:12.755 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" - GET /api/quality/incoming?pageSize=3 200 in 224ms (compile: 198ms, proxy.ts: 5ms, render: 20ms) - GET /api/dcprint/trace?pageSize=3 200 in 211ms (compile: 183ms, proxy.ts: 4ms, render: 24ms) -[2026-08-25 14:47:17.755 +0800] DEBUG: Event registry already initialized -[2026-08-25 14:47:17.757 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-25 14:47:22.764 +0800] DEBUG: Event registry already initialized -[2026-08-25 14:47:22.776 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-25 14:47:27.779 +0800] DEBUG: Event registry already initialized -[2026-08-25 14:47:27.781 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-25 14:47:32.791 +0800] DEBUG: Event registry already initialized -[2026-08-25 14:47:32.793 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-25 14:47:37.806 +0800] DEBUG: Event registry already initialized -[2026-08-25 14:47:37.818 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-25 14:47:42.820 +0800] DEBUG: Event registry already initialized -[2026-08-25 14:47:42.822 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-25 14:47:47.834 +0800] DEBUG: Event registry already initialized -[2026-08-25 14:47:47.836 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-25 14:47:52.845 +0800] DEBUG: Event registry already initialized -[2026-08-25 14:47:52.847 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-25 14:47:57.858 +0800] DEBUG: Event registry already initialized -[2026-08-25 14:47:57.861 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-25 14:48:02.862 +0800] DEBUG: Event registry already initialized -[2026-08-25 14:48:02.863 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-25 14:48:07.873 +0800] DEBUG: Event registry already initialized -[2026-08-25 14:48:07.876 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-25 14:48:12.884 +0800] DEBUG: Event registry already initialized -[2026-08-25 14:48:12.887 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-25 14:48:17.897 +0800] DEBUG: Event registry already initialized -[2026-08-25 14:48:17.898 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-25 14:48:22.911 +0800] DEBUG: Event registry already initialized -[2026-08-25 14:48:22.914 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-25 14:48:27.926 +0800] DEBUG: Event registry already initialized -[2026-08-25 14:48:27.928 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-25 14:48:32.939 +0800] DEBUG: Event registry already initialized -[2026-08-25 14:48:32.941 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-25 14:48:37.943 +0800] DEBUG: Event registry already initialized -[2026-08-25 14:48:37.946 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-25 14:48:42.981 +0800] DEBUG: Event registry already initialized -[2026-08-25 14:48:42.987 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-25 14:48:47.982 +0800] DEBUG: Event registry already initialized -[2026-08-25 14:48:47.984 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[2026-08-25 14:48:52.988 +0800] DEBUG: Event registry already initialized -[2026-08-25 14:48:52.990 +0800] DEBUG: OutboxPoller: claimed pending events - count: 0 - eventIds: [] - mode: "memory" -[?25h diff --git a/_devlog_s32.txt b/_devlog_s32.txt deleted file mode 100644 index 069593fa..00000000 --- a/_devlog_s32.txt +++ /dev/null @@ -1,18 +0,0 @@ - -> vnerp@0.1.0 dev -> next dev -p 5000 - -▲ Next.js 16.1.1 (Turbopack) -- Local: http://localhost:5000 -- Network: http://192.168.0.144:5000 -- Environments: .env.local, .env -- Experiments (use with caution): - · optimizePackageImports - -✓ Starting... -✓ Ready in 1927ms -[2026-08-25 14:56:54.517 +0800] INFO: [instrumentation] Server started. OutboxPoller will auto-start on first API request. -[2026-08-25 14:57:32.589 +0800] INFO: CacheManager: 使用 InMemoryCacheManager(未配置 REDIS_URL) - GET /api/quality/incoming?pageSize=3 200 in 779ms (compile: 581ms, proxy.ts: 122ms, render: 76ms) - GET /api/dcprint/trace?pageSize=3 200 in 207ms (compile: 181ms, proxy.ts: 5ms, render: 22ms) -[?25h diff --git a/_devlog_s33.txt b/_devlog_s33.txt deleted file mode 100644 index 35b1005c..00000000 --- a/_devlog_s33.txt +++ /dev/null @@ -1,25 +0,0 @@ - -> vnerp@0.1.0 dev -> next dev -p 5000 - -▲ Next.js 16.1.1 (Turbopack) -- Local: http://localhost:5000 -- Network: http://192.168.0.144:5000 -- Environments: .env.local, .env -- Experiments (use with caution): - · optimizePackageImports - -✓ Starting... -✓ Ready in 1700ms -[2026-08-25 15:11:17.245 +0800] INFO: [instrumentation] Server started. OutboxPoller will auto-start on first API request. -[2026-08-25 15:12:48.126 +0800] INFO: CacheManager: 使用 InMemoryCacheManager(未配置 REDIS_URL) - GET /api/finance/receivables?pageSize=3 200 in 682ms (compile: 490ms, proxy.ts: 124ms, render: 68ms) - GET /api/system/user?pageSize=3 200 in 195ms (compile: 176ms, proxy.ts: 5ms, render: 14ms) - GET /api/purchase/request?pageSize=3 200 in 150ms (compile: 123ms, proxy.ts: 4ms, render: 23ms) -[RepoRegistry] active impl = mysql (default; set REPOSITORY_IMPL=drizzle to switch) -[RepoRegistry] getSalesOrderRepository() → MysqlSalesOrderRepository (mysql) - GET /api/sales/delivery?pageSize=3 200 in 268ms (compile: 251ms, proxy.ts: 4ms, render: 14ms) - GET /api/sales/reconciliation?pageSize=3 200 in 155ms (compile: 130ms, proxy.ts: 5ms, render: 19ms) - GET /api/outsource/order?pageSize=3 200 in 132ms (compile: 112ms, proxy.ts: 3ms, render: 17ms) - GET /api/sales/orders?pageSize=3 200 in 121ms (compile: 104ms, proxy.ts: 3ms, render: 13ms) -[?25h diff --git a/_devlog_verify.txt b/_devlog_verify.txt deleted file mode 100644 index 00b3fa90..00000000 --- a/_devlog_verify.txt +++ /dev/null @@ -1,69 +0,0 @@ - -> vnerp@0.1.0 dev -> next dev -p 5000 - -▲ Next.js 16.1.1 (Turbopack) -- Local: http://localhost:5000 -- Network: http://192.168.0.144:5000 -- Environments: .env.local, .env -- Experiments (use with caution): - · optimizePackageImports - -✓ Starting... -✓ Ready in 1840ms -[2026-08-25 13:57:29.576 +0800] INFO: [instrumentation] Server started. OutboxPoller will auto-start on first API request. -[2026-08-25 13:57:52.367 +0800] INFO: CacheManager: 使用 InMemoryCacheManager(未配置 REDIS_URL) - GET /api/finance/receipt?pageSize=3 200 in 1087ms (compile: 877ms, proxy.ts: 150ms, render: 60ms) - GET /api/finance/payment?pageSize=3 200 in 269ms (compile: 240ms, proxy.ts: 6ms, render: 24ms) - GET /api/finance/stats 200 in 280ms (compile: 233ms, proxy.ts: 5ms, render: 42ms) - GET /api/finance/cost?pageSize=3 200 in 243ms (compile: 223ms, proxy.ts: 5ms, render: 15ms) - GET /api/finance/receivables?pageSize=3 200 in 153ms (compile: 135ms, proxy.ts: 7ms, render: 11ms) - GET /api/quality/complaint?pageSize=3 200 in 148ms (compile: 130ms, proxy.ts: 4ms, render: 14ms) - GET /api/quality/lab-test?pageSize=3 200 in 136ms (compile: 120ms, proxy.ts: 4ms, render: 12ms) - GET /api/quality/supplier-audit?pageSize=3 200 in 172ms (compile: 148ms, proxy.ts: 4ms, render: 20ms) - GET /api/purchase/suppliers?pageSize=3 200 in 133ms (compile: 111ms, proxy.ts: 6ms, render: 16ms) - GET /api/system/announcement?pageSize=3 200 in 118ms (compile: 100ms, proxy.ts: 5ms, render: 12ms) - GET /api/system/scheduler?pageSize=3 200 in 120ms (compile: 105ms, proxy.ts: 3ms, render: 12ms) - GET /api/trace/label?pageSize=3 200 in 114ms (compile: 97ms, proxy.ts: 3ms, render: 14ms) -[API Error] 请求处理失败: Error: Unknown column 'iu.screen_plate_id' in 'on clause' - at query (src\lib\db\index.ts:113:33) - at (src\app\api\ink-usages\route.ts:54:10) - at errorMessage (src\lib\api-permissions.ts:961:28) - at (src\lib\api-auth.ts:166:12) - at async (src\lib\api-auth.ts:202:14) - 111 | const _sqlStr = typeof sql === 'string' ? sql : String(sql); - 112 | } -> 113 | const [rows] = await pool.query(sql, values); - | ^ - 114 | if (DEBUG_DB) { - 115 | } - 116 | return rows as T[]; { - code: 'ER_BAD_FIELD_ERROR', - errno: 1054, - sqlState: '42S22', - sqlMessage: "Unknown column 'iu.screen_plate_id' in 'on clause'", - sql: '\n' + - ' SELECT iu.*, bi.ink_code, bi.ink_name, bi.unit, sp.plate_code\n' + - ' FROM ink_usage iu\n' + - ' LEFT JOIN base_ink bi ON iu.ink_id = bi.id\n' + - ' LEFT JOIN prd_screen_plate sp ON iu.screen_plate_id = sp.id\n' + - ' WHERE iu.deleted = 0\n' + - ' ORDER BY iu.usage_date DESC\n' + - ' LIMIT 3 OFFSET 0\n' + - ' ' -} - GET /api/ink-usages?pageSize=3 500 in 292ms (compile: 110ms, proxy.ts: 3ms, render: 180ms) - GET /api/finance/receipt?pageSize=3 200 in 29ms (compile: 5ms, proxy.ts: 6ms, render: 19ms) - GET /api/finance/payment?pageSize=3 200 in 18ms (compile: 2ms, proxy.ts: 4ms, render: 12ms) - GET /api/finance/stats 200 in 18ms (compile: 1939µs, proxy.ts: 4ms, render: 13ms) - GET /api/finance/cost?pageSize=3 200 in 14ms (compile: 1720µs, proxy.ts: 4ms, render: 9ms) - GET /api/finance/receivables?pageSize=3 200 in 13ms (compile: 2ms, proxy.ts: 3ms, render: 8ms) - GET /api/quality/complaint?pageSize=3 200 in 13ms (compile: 1705µs, proxy.ts: 3ms, render: 8ms) - GET /api/quality/lab-test?pageSize=3 200 in 14ms (compile: 2ms, proxy.ts: 3ms, render: 9ms) - GET /api/quality/supplier-audit?pageSize=3 200 in 15ms (compile: 1937µs, proxy.ts: 3ms, render: 10ms) - GET /api/purchase/suppliers?pageSize=3 200 in 14ms (compile: 1774µs, proxy.ts: 3ms, render: 9ms) - GET /api/system/announcement?pageSize=3 200 in 12ms (compile: 1566µs, proxy.ts: 2ms, render: 8ms) - GET /api/system/scheduler?pageSize=3 200 in 12ms (compile: 1712µs, proxy.ts: 3ms, render: 7ms) - GET /api/trace/label?pageSize=3 200 in 12ms (compile: 1760µs, proxy.ts: 2ms, render: 8ms) - GET /api/ink-usages?pageSize=3 200 in 17ms (compile: 1892µs, proxy.ts: 3ms, render: 12ms) -[?25h diff --git a/_devlog_verify2.txt b/_devlog_verify2.txt deleted file mode 100644 index 2ae82407..00000000 --- a/_devlog_verify2.txt +++ /dev/null @@ -1,129 +0,0 @@ - -> vnerp@0.1.0 dev -> next dev -p 5000 - -▲ Next.js 16.1.1 (Turbopack) -- Local: http://localhost:5000 -- Network: http://192.168.0.144:5000 -- Environments: .env.local, .env -- Experiments (use with caution): - · optimizePackageImports - -✓ Starting... -✓ Ready in 1773ms -[2026-08-25 14:17:14.227 +0800] INFO: [instrumentation] Server started. OutboxPoller will auto-start on first API request. -[2026-08-25 14:17:36.487 +0800] INFO: CacheManager: 使用 InMemoryCacheManager(未配置 REDIS_URL) - GET /api/qrcode?pageSize=3 200 in 839ms (compile: 651ms, proxy.ts: 130ms, render: 58ms) - GET /api/reports/dashboard 200 in 234ms (compile: 206ms, proxy.ts: 5ms, render: 23ms) - GET /api/dcprint/tool?pageSize=3 200 in 192ms (compile: 170ms, proxy.ts: 6ms, render: 16ms) - GET /api/system/oper-log?pageSize=3 200 in 207ms (compile: 190ms, proxy.ts: 3ms, render: 13ms) - GET /api/hr/reports/turnover 200 in 153ms (compile: 131ms, proxy.ts: 5ms, render: 18ms) - GET /api/orders?pageSize=3 200 in 188ms (compile: 126ms, proxy.ts: 4ms, render: 58ms) -[RepoRegistry] active impl = mysql (default; set REPOSITORY_IMPL=drizzle to switch) -[RepoRegistry] getSalesOrderRepository() → MysqlSalesOrderRepository (mysql) - GET /api/sales/delivery?pageSize=3 200 in 257ms (compile: 236ms, proxy.ts: 5ms, render: 16ms) - GET /api/warehouse/sales-outbound?pageSize=3 200 in 162ms (compile: 141ms, proxy.ts: 4ms, render: 17ms) - GET /api/production/orders?pageSize=3 200 in 114ms (compile: 95ms, proxy.ts: 6ms, render: 13ms) -[2026-08-25 14:20:09.725 +0800] INFO: 查询打样单列表 - module: "sample" - action: "GET_orders" - traceId: "mt89zpfh-7unw88a" -[2026-08-25 14:20:09.725 +0800] DEBUG: [sample.listOrders] 列表查询 - ctx: { - "module": "sample", - "action": "listOrders", - "traceId": "mt89zpfh-23nn890" - } - step: "列表查询" - data: { - "filters": {}, - "page": 1, - "pageSize": 3 - } - GET /api/sample/orders?pageSize=3 200 in 139ms (compile: 118ms, proxy.ts: 3ms, render: 18ms) -[2026-08-25 14:20:09.732 +0800] DEBUG: [sample.listOrders] 列表查询 DONE - ctx: { - "module": "sample", - "action": "listOrders", - "traceId": "mt89zpfh-23nn890" - } - step: "列表查询" - data: { - "total": 10, - "count": 3 - } -[2026-08-25 14:20:09.732 +0800] INFO: 查询结果 - module: "sample" - action: "GET_orders" - traceId: "mt89zpfh-7unw88a" -[API Error] 请求处理失败: Error: Table 'vnerpdacahng.sales_order' doesn't exist - at query (src\lib\db\index.ts:113:33) - at (src\app\api\reports\delivery-rate\route.ts:26:36) - at errorMessage (src\lib\api-permissions.ts:961:28) - at (src\lib\api-auth.ts:166:12) - at async (src\lib\api-auth.ts:202:14) - 111 | const _sqlStr = typeof sql === 'string' ? sql : String(sql); - 112 | } -> 113 | const [rows] = await pool.query(sql, values); - | ^ - 114 | if (DEBUG_DB) { - 115 | } - 116 | return rows as T[]; { - code: 'ER_NO_SUCH_TABLE', - errno: 1146, - sqlState: '42S02', - sqlMessage: "Table 'vnerpdacahng.sales_order' doesn't exist", - sql: 'SELECT\n' + - " DATE_FORMAT(so.order_date, '%Y-%m') as month,\n" + - ' COUNT(*) as total_orders,\n' + - ' SUM(CASE WHEN so.status >= 3 THEN 1 ELSE 0 END) as completed_orders,\n' + - ' SUM(CASE WHEN so.delivery_date >= so.actual_delivery_date THEN 1 ELSE 0 END) as on_time_orders,\n' + - ' COALESCE(SUM(so.total_amount), 0) as total_amount,\n' + - ' COALESCE(SUM(CASE WHEN so.status >= 3 THEN so.total_amount ELSE 0 END), 0) as completed_amount\n' + - ' FROM sales_order so\n' + - ' WHERE so.deleted = 0 \n' + - " GROUP BY DATE_FORMAT(so.order_date, '%Y-%m')\n" + - ' ORDER BY month DESC' -} - GET /api/reports/delivery-rate?pageSize=3 500 in 246ms (compile: 93ms, proxy.ts: 4ms, render: 149ms) -[API Error] 请求处理失败: Error: Table 'vnerpdacahng.sales_order_item' doesn't exist - at query (src\lib\db\index.ts:113:33) - at (src\app\api\finance\cost-variance\route.ts:37:40) - at errorMessage (src\lib\api-permissions.ts:961:28) - at (src\lib\api-auth.ts:166:12) - at async (src\lib\api-auth.ts:202:14) - 111 | const _sqlStr = typeof sql === 'string' ? sql : String(sql); - 112 | } -> 113 | const [rows] = await pool.query(sql, values); - | ^ - 114 | if (DEBUG_DB) { - 115 | } - 116 | return rows as T[]; { - code: 'ER_NO_SUCH_TABLE', - errno: 1146, - sqlState: '42S02', - sqlMessage: "Table 'vnerpdacahng.sales_order_item' doesn't exist", - sql: '\n' + - ' SELECT\n' + - ' wo.id,\n' + - ' wo.work_order_no,\n' + - ' wo.product_name,\n' + - ' wo.quantity as plan_qty,\n' + - ' wo.status,\n' + - ' wo.create_time,\n' + - ' soi.unit_price as sale_unit_price,\n' + - ' soi.total_price as sale_total_price\n' + - ' FROM prod_work_order wo\n' + - ' LEFT JOIN sales_order_item soi ON wo.order_id = soi.order_id\n' + - ' WHERE wo.deleted = 0\n' + - ' ORDER BY wo.create_time DESC\n' + - ' LIMIT 3 OFFSET 0\n' + - ' ' -} - GET /api/finance/cost-variance?pageSize=3 500 in 197ms (compile: 113ms, proxy.ts: 3ms, render: 81ms) - GET /api/qrcode/trace?pageSize=3 400 in 123ms (compile: 111ms, proxy.ts: 4ms, render: 9ms) -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getSalesOrderRepository() → MysqlSalesOrderRepository (mysql) - GET /api/sales/return?pageSize=3 200 in 180ms (compile: 158ms, proxy.ts: 3ms, render: 19ms) - GET /api/sales/orders?pageSize=3 200 in 115ms (compile: 103ms, proxy.ts: 3ms, render: 9ms) -[?25h diff --git a/_devlog_verify3.txt b/_devlog_verify3.txt deleted file mode 100644 index 988f09dc..00000000 --- a/_devlog_verify3.txt +++ /dev/null @@ -1,120 +0,0 @@ - -> vnerp@0.1.0 dev -> next dev -p 5000 - -▲ Next.js 16.1.1 (Turbopack) -- Local: http://localhost:5000 -- Network: http://192.168.0.144:5000 -- Environments: .env.local, .env -- Experiments (use with caution): - · optimizePackageImports - -✓ Starting... -✓ Ready in 3.8s -[2026-08-25 14:28:22.584 +0800] INFO: [instrumentation] Server started. OutboxPoller will auto-start on first API request. -[2026-08-25 14:28:30.631 +0800] INFO: CacheManager: 使用 InMemoryCacheManager(未配置 REDIS_URL) - GET /api/orders?pageSize=3 200 in 914ms (compile: 707ms, proxy.ts: 117ms, render: 90ms) -[RepoRegistry] active impl = mysql (default; set REPOSITORY_IMPL=drizzle to switch) -[RepoRegistry] getSalesOrderRepository() → MysqlSalesOrderRepository (mysql) - GET /api/sales/delivery?pageSize=3 200 in 395ms (compile: 372ms, proxy.ts: 6ms, render: 17ms) - GET /api/warehouse/sales-outbound?pageSize=3 200 in 209ms (compile: 179ms, proxy.ts: 5ms, render: 26ms) - GET /api/production/orders?pageSize=3 200 in 244ms (compile: 226ms, proxy.ts: 5ms, render: 13ms) -[2026-08-25 14:28:31.698 +0800] INFO: 查询打样单列表 - module: "sample" - action: "GET_orders" - traceId: "mt8aagr6-iknsg2o" -[2026-08-25 14:28:31.698 +0800] DEBUG: [sample.listOrders] 列表查询 - ctx: { - "module": "sample", - "action": "listOrders", - "traceId": "mt8aagr6-735gl7s" - } - step: "列表查询" - data: { - "filters": {}, - "page": 1, - "pageSize": 3 - } - GET /api/sample/orders?pageSize=3 200 in 163ms (compile: 140ms, proxy.ts: 6ms, render: 16ms) -[2026-08-25 14:28:31.701 +0800] DEBUG: [sample.listOrders] 列表查询 DONE - ctx: { - "module": "sample", - "action": "listOrders", - "traceId": "mt8aagr6-735gl7s" - } - step: "列表查询" - data: { - "total": 10, - "count": 3 - } -[2026-08-25 14:28:31.701 +0800] INFO: 查询结果 - module: "sample" - action: "GET_orders" - traceId: "mt8aagr6-iknsg2o" - GET /api/reports/delivery-rate?pageSize=3 200 in 137ms (compile: 120ms, proxy.ts: 5ms, render: 12ms) -[API Error] 请求处理失败: Error: Unknown column 'tr.source_no' in 'on clause' - at query (src\lib\db\index.ts:113:33) - at (src\app\api\finance\cost-variance\route.ts:68:48) - at async errorMessage (src\lib\api-permissions.ts:961:22) - at async (src\lib\api-auth.ts:202:14) - 111 | const _sqlStr = typeof sql === 'string' ? sql : String(sql); - 112 | } -> 113 | const [rows] = await pool.query(sql, values); - | ^ - 114 | if (DEBUG_DB) { - 115 | } - 116 | return rows as T[]; { - code: 'ER_BAD_FIELD_ERROR', - errno: 1054, - sqlState: '42S22', - sqlMessage: "Unknown column 'tr.source_no' in 'on clause'", - sql: '\n' + - ' SELECT\n' + - ' COALESCE(SUM(wr.completed_qty * bom.unit_price), 0) as standard_material_cost,\n' + - ' COALESCE(SUM(tr.quantity * tr.unit_price), 0) as actual_material_cost\n' + - ' FROM prd_work_report wr\n' + - ' LEFT JOIN prod_work_order_item woi ON wr.work_order_id = woi.id\n' + - ' LEFT JOIN (\n' + - ' SELECT material_id, unit_price FROM prod_work_order_item WHERE work_order_id = 418\n' + - ' ) bom ON 1=1\n' + - " LEFT JOIN inv_inventory_transaction tr ON tr.source_no = wo.work_order_no AND tr.trans_type = 'out'\n" + - ' WHERE wr.work_order_id = 418 AND wr.deleted = 0\n' + - ' ' -} - GET /api/finance/cost-variance?pageSize=3 500 in 312ms (compile: 134ms, proxy.ts: 4ms, render: 174ms) - GET /api/qrcode/trace?pageSize=3 400 in 175ms (compile: 159ms, proxy.ts: 4ms, render: 12ms) -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getSalesOrderRepository() → MysqlSalesOrderRepository (mysql) - GET /api/sales/return?pageSize=3 200 in 191ms (compile: 169ms, proxy.ts: 5ms, render: 17ms) - GET /api/sales/orders?pageSize=3 200 in 142ms (compile: 125ms, proxy.ts: 4ms, render: 14ms) -[API Error] 请求处理失败: Error: Unknown column 'wo.work_order_no' in 'on clause' - at query (src\lib\db\index.ts:113:33) - at (src\app\api\finance\cost-variance\route.ts:68:48) - at async errorMessage (src\lib\api-permissions.ts:961:22) - at async (src\lib\api-auth.ts:202:14) - 111 | const _sqlStr = typeof sql === 'string' ? sql : String(sql); - 112 | } -> 113 | const [rows] = await pool.query(sql, values); - | ^ - 114 | if (DEBUG_DB) { - 115 | } - 116 | return rows as T[]; { - code: 'ER_BAD_FIELD_ERROR', - errno: 1054, - sqlState: '42S22', - sqlMessage: "Unknown column 'wo.work_order_no' in 'on clause'", - sql: '\n' + - ' SELECT\n' + - ' COALESCE(SUM(wr.completed_qty * bom.unit_price), 0) as standard_material_cost,\n' + - ' COALESCE(SUM(tr.quantity * tr.unit_price), 0) as actual_material_cost\n' + - ' FROM prd_work_report wr\n' + - ' LEFT JOIN prod_work_order_item woi ON wr.work_order_id = woi.id\n' + - ' LEFT JOIN (\n' + - ' SELECT material_id, unit_price FROM prod_work_order_item WHERE work_order_id = 418\n' + - ' ) bom ON 1=1\n' + - " LEFT JOIN inv_inventory_transaction tr ON tr.source_no = wo.work_order_no AND tr.trans_type = 'out'\n" + - ' WHERE wr.work_order_id = 418 AND wr.deleted = 0\n' + - ' ' -} - GET /api/finance/cost-variance?pageSize=3 500 in 94ms (compile: 3ms, proxy.ts: 4ms, render: 87ms) -[?25h diff --git a/_devlog_verify4.txt b/_devlog_verify4.txt deleted file mode 100644 index f608f85e..00000000 --- a/_devlog_verify4.txt +++ /dev/null @@ -1,120 +0,0 @@ - -> vnerp@0.1.0 dev -> next dev -p 5000 - -▲ Next.js 16.1.1 (Turbopack) -- Local: http://localhost:5000 -- Network: http://192.168.0.144:5000 -- Environments: .env.local, .env -- Experiments (use with caution): - · optimizePackageImports - -✓ Starting... -✓ Ready in 1989ms -[2026-08-25 14:30:58.149 +0800] INFO: [instrumentation] Server started. OutboxPoller will auto-start on first API request. -[2026-08-25 14:31:04.410 +0800] INFO: CacheManager: 使用 InMemoryCacheManager(未配置 REDIS_URL) - GET /api/orders?pageSize=3 200 in 914ms (compile: 699ms, proxy.ts: 127ms, render: 88ms) -[RepoRegistry] active impl = mysql (default; set REPOSITORY_IMPL=drizzle to switch) -[RepoRegistry] getSalesOrderRepository() → MysqlSalesOrderRepository (mysql) - GET /api/sales/delivery?pageSize=3 200 in 371ms (compile: 353ms, proxy.ts: 6ms, render: 12ms) - GET /api/warehouse/sales-outbound?pageSize=3 200 in 213ms (compile: 189ms, proxy.ts: 4ms, render: 20ms) - GET /api/production/orders?pageSize=3 200 in 254ms (compile: 239ms, proxy.ts: 4ms, render: 11ms) -[2026-08-25 14:31:05.438 +0800] INFO: 查询打样单列表 - module: "sample" - action: "GET_orders" - traceId: "mt8adrdq-ee830ge" -[2026-08-25 14:31:05.438 +0800] DEBUG: [sample.listOrders] 列表查询 - ctx: { - "module": "sample", - "action": "listOrders", - "traceId": "mt8adrdq-qlxhkrn" - } - step: "列表查询" - data: { - "filters": {}, - "page": 1, - "pageSize": 3 - } - GET /api/sample/orders?pageSize=3 200 in 143ms (compile: 123ms, proxy.ts: 6ms, render: 14ms) -[2026-08-25 14:31:05.441 +0800] DEBUG: [sample.listOrders] 列表查询 DONE - ctx: { - "module": "sample", - "action": "listOrders", - "traceId": "mt8adrdq-qlxhkrn" - } - step: "列表查询" - data: { - "total": 10, - "count": 3 - } -[2026-08-25 14:31:05.442 +0800] INFO: 查询结果 - module: "sample" - action: "GET_orders" - traceId: "mt8adrdq-ee830ge" - GET /api/reports/delivery-rate?pageSize=3 200 in 125ms (compile: 112ms, proxy.ts: 3ms, render: 9ms) - GET /api/finance/cost-variance?pageSize=3 200 in 130ms (compile: 104ms, proxy.ts: 6ms, render: 21ms) - GET /api/qrcode/trace?pageSize=3 400 in 151ms (compile: 138ms, proxy.ts: 3ms, render: 10ms) -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getSalesOrderRepository() → MysqlSalesOrderRepository (mysql) - GET /api/sales/return?pageSize=3 200 in 183ms (compile: 165ms, proxy.ts: 4ms, render: 14ms) - GET /api/sales/orders?pageSize=3 200 in 119ms (compile: 104ms, proxy.ts: 3ms, render: 11ms) - GET /api/finance/receipt?pageSize=3 200 in 157ms (compile: 138ms, proxy.ts: 4ms, render: 15ms) - GET /api/finance/payment?pageSize=3 200 in 118ms (compile: 105ms, proxy.ts: 3ms, render: 10ms) - GET /api/finance/stats 200 in 114ms (compile: 93ms, proxy.ts: 3ms, render: 18ms) - GET /api/finance/cost?pageSize=3 200 in 127ms (compile: 111ms, proxy.ts: 5ms, render: 11ms) - GET /api/finance/receivables?pageSize=3 200 in 121ms (compile: 102ms, proxy.ts: 5ms, render: 15ms) - GET /api/quality/complaint?pageSize=3 200 in 157ms (compile: 140ms, proxy.ts: 6ms, render: 10ms) - GET /api/quality/lab-test?pageSize=3 200 in 113ms (compile: 93ms, proxy.ts: 6ms, render: 13ms) - GET /api/quality/supplier-audit?pageSize=3 200 in 107ms (compile: 90ms, proxy.ts: 2ms, render: 15ms) - GET /api/purchase/suppliers?pageSize=3 200 in 122ms (compile: 105ms, proxy.ts: 4ms, render: 14ms) - GET /api/system/announcement?pageSize=3 200 in 92ms (compile: 81ms, proxy.ts: 2ms, render: 9ms) - GET /api/system/scheduler?pageSize=3 200 in 102ms (compile: 91ms, proxy.ts: 3ms, render: 9ms) - GET /api/trace/label?pageSize=3 200 in 94ms (compile: 83ms, proxy.ts: 3ms, render: 8ms) - GET /api/ink-usages?pageSize=3 200 in 102ms (compile: 87ms, proxy.ts: 3ms, render: 12ms) - GET /api/qrcode?pageSize=3 200 in 105ms (compile: 87ms, proxy.ts: 3ms, render: 15ms) - GET /api/reports/dashboard 200 in 102ms (compile: 80ms, proxy.ts: 5ms, render: 17ms) - GET /api/dcprint/tool?pageSize=3 200 in 135ms (compile: 114ms, proxy.ts: 4ms, render: 17ms) - GET /api/system/oper-log?pageSize=3 200 in 119ms (compile: 105ms, proxy.ts: 3ms, render: 11ms) - GET /api/hr/reports/turnover 200 in 139ms (compile: 120ms, proxy.ts: 3ms, render: 15ms) - GET /api/orders?pageSize=3 200 in 17ms (compile: 2ms, proxy.ts: 3ms, render: 12ms) - GET /api/sales/delivery?pageSize=3 200 in 15ms (compile: 1947µs, proxy.ts: 3ms, render: 10ms) - GET /api/warehouse/sales-outbound?pageSize=3 200 in 15ms (compile: 1764µs, proxy.ts: 4ms, render: 9ms) - GET /api/production/orders?pageSize=3 200 in 12ms (compile: 1597µs, proxy.ts: 2ms, render: 8ms) -[2026-08-25 14:31:28.376 +0800] INFO: 查询打样单列表 - module: "sample" - action: "GET_orders" - traceId: "mt8ae92w-gj6vc5i" -[2026-08-25 14:31:28.376 +0800] DEBUG: [sample.listOrders] 列表查询 - ctx: { - "module": "sample", - "action": "listOrders", - "traceId": "mt8ae92w-9ew7jib" - } - step: "列表查询" - data: { - "filters": {}, - "page": 1, - "pageSize": 3 - } - GET /api/sample/orders?pageSize=3 200 in 18ms (compile: 3ms, proxy.ts: 6ms, render: 9ms) -[2026-08-25 14:31:28.378 +0800] DEBUG: [sample.listOrders] 列表查询 DONE - ctx: { - "module": "sample", - "action": "listOrders", - "traceId": "mt8ae92w-9ew7jib" - } - step: "列表查询" - data: { - "total": 10, - "count": 3 - } -[2026-08-25 14:31:28.378 +0800] INFO: 查询结果 - module: "sample" - action: "GET_orders" - traceId: "mt8ae92w-gj6vc5i" - GET /api/reports/delivery-rate?pageSize=3 200 in 13ms (compile: 2ms, proxy.ts: 2ms, render: 8ms) - GET /api/finance/cost-variance?pageSize=3 200 in 18ms (compile: 1968µs, proxy.ts: 4ms, render: 12ms) - GET /api/qrcode/trace?pageSize=3 400 in 12ms (compile: 2ms, proxy.ts: 4ms, render: 6ms) - GET /api/sales/return?pageSize=3 200 in 11ms (compile: 1632µs, proxy.ts: 2ms, render: 7ms) - GET /api/sales/orders?pageSize=3 200 in 17ms (compile: 2ms, proxy.ts: 6ms, render: 9ms) -[?25h diff --git a/_e2e_full.txt b/_e2e_full.txt deleted file mode 100644 index 264da83f..00000000 --- a/_e2e_full.txt +++ /dev/null @@ -1,17 +0,0 @@ -Error: [safe-delete][SAFE_DELETE_BULK_CONFIRM_REQUIRED] {"count":82,"threshold":50,"scope":"turn","targets":["D:\\dcprint\\erp-project\\node_modules\\.cache\\pw\\test-results"],"targetCount":1} - - at C:\Program Files\WorkBuddy\resources\app.asar.unpacked\cli\vendor\shim\genie-safe-delete.cjs:222 - - 220 | const detail = e && e.stderr ? String(e.stderr).trim() : ''; - 221 | if (detail) { -> 222 | throw new Error(detail); - | ^ - 223 | } - 224 | throw new Error(`[safe-delete][SAFE_DELETE_BULK_GUARD_ERROR] ${(e && e.message) || String(e)}`); - 225 | } - at checkBulkDeleteGuard (C:\Program Files\WorkBuddy\resources\app.asar.unpacked\cli\vendor\shim\genie-safe-delete.cjs:222:19) - at tryTrash (C:\Program Files\WorkBuddy\resources\app.asar.unpacked\cli\vendor\shim\genie-safe-delete.cjs:546:5) - at tryRm (C:\Program Files\WorkBuddy\resources\app.asar.unpacked\cli\vendor\shim\genie-safe-delete.cjs:722:5) - at Object.wrappedPromisesRm (C:\Program Files\WorkBuddy\resources\app.asar.unpacked\cli\vendor\shim\genie-safe-delete.cjs:766:15) - - \ No newline at end of file diff --git a/_e2e_full2.txt b/_e2e_full2.txt deleted file mode 100644 index f6155dc1..00000000 --- a/_e2e_full2.txt +++ /dev/null @@ -1,51 +0,0 @@ -[GlobalSetup] Attempt 1/3 failed, retrying in 2s... Error: reset-lock returned 401: Authentication required - at globalSetup (D:\dcprint\erp-project\tests\global-setup.ts:50:15) - at processTicksAndRejections (node:internal/process/task_queues:103:5) - at Object.setup (D:\dcprint\erp-project\node_modules\.pnpm\playwright@1.59.1\node_modules\playwright\lib\runner\tasks.js:174:27) - at taskLoop (D:\dcprint\erp-project\node_modules\.pnpm\playwright@1.59.1\node_modules\playwright\lib\runner\taskRunner.js:62:11) - at TaskRunner.runDeferCleanup (D:\dcprint\erp-project\node_modules\.pnpm\playwright@1.59.1\node_modules\playwright\lib\runner\taskRunner.js:78:5) - at TaskRunner.run (D:\dcprint\erp-project\node_modules\.pnpm\playwright@1.59.1\node_modules\playwright\lib\runner\taskRunner.js:42:33) - at runTasks (D:\dcprint\erp-project\node_modules\.pnpm\playwright@1.59.1\node_modules\playwright\lib\runner\tasks.js:80:18) - at runAllTestsWithConfig (D:\dcprint\erp-project\node_modules\.pnpm\playwright@1.59.1\node_modules\playwright\lib\runner\testRunner.js:388:18) - at runTests (D:\dcprint\erp-project\node_modules\.pnpm\playwright@1.59.1\node_modules\playwright\lib\testActions.js:96:18) - at i. (D:\dcprint\erp-project\node_modules\.pnpm\playwright@1.59.1\node_modules\playwright\lib\program.js:56:7) -[GlobalSetup] Attempt 2/3 failed, retrying in 2s... Error: reset-lock returned 401: Authentication required - at globalSetup (D:\dcprint\erp-project\tests\global-setup.ts:50:15) - at processTicksAndRejections (node:internal/process/task_queues:103:5) - at Object.setup (D:\dcprint\erp-project\node_modules\.pnpm\playwright@1.59.1\node_modules\playwright\lib\runner\tasks.js:174:27) - at taskLoop (D:\dcprint\erp-project\node_modules\.pnpm\playwright@1.59.1\node_modules\playwright\lib\runner\taskRunner.js:62:11) - at TaskRunner.runDeferCleanup (D:\dcprint\erp-project\node_modules\.pnpm\playwright@1.59.1\node_modules\playwright\lib\runner\taskRunner.js:78:5) - at TaskRunner.run (D:\dcprint\erp-project\node_modules\.pnpm\playwright@1.59.1\node_modules\playwright\lib\runner\taskRunner.js:42:33) - at runTasks (D:\dcprint\erp-project\node_modules\.pnpm\playwright@1.59.1\node_modules\playwright\lib\runner\tasks.js:80:18) - at runAllTestsWithConfig (D:\dcprint\erp-project\node_modules\.pnpm\playwright@1.59.1\node_modules\playwright\lib\runner\testRunner.js:388:18) - at runTests (D:\dcprint\erp-project\node_modules\.pnpm\playwright@1.59.1\node_modules\playwright\lib\testActions.js:96:18) - at i. (D:\dcprint\erp-project\node_modules\.pnpm\playwright@1.59.1\node_modules\playwright\lib\program.js:56:7) -[GlobalSetup] Failed to reset admin lock after all retries: Error: reset-lock returned 401: Authentication required - at globalSetup (D:\dcprint\erp-project\tests\global-setup.ts:50:15) - at processTicksAndRejections (node:internal/process/task_queues:103:5) - at Object.setup (D:\dcprint\erp-project\node_modules\.pnpm\playwright@1.59.1\node_modules\playwright\lib\runner\tasks.js:174:27) - at taskLoop (D:\dcprint\erp-project\node_modules\.pnpm\playwright@1.59.1\node_modules\playwright\lib\runner\taskRunner.js:62:11) - at TaskRunner.runDeferCleanup (D:\dcprint\erp-project\node_modules\.pnpm\playwright@1.59.1\node_modules\playwright\lib\runner\taskRunner.js:78:5) - at TaskRunner.run (D:\dcprint\erp-project\node_modules\.pnpm\playwright@1.59.1\node_modules\playwright\lib\runner\taskRunner.js:42:33) - at runTasks (D:\dcprint\erp-project\node_modules\.pnpm\playwright@1.59.1\node_modules\playwright\lib\runner\tasks.js:80:18) - at runAllTestsWithConfig (D:\dcprint\erp-project\node_modules\.pnpm\playwright@1.59.1\node_modules\playwright\lib\runner\testRunner.js:388:18) - at runTests (D:\dcprint\erp-project\node_modules\.pnpm\playwright@1.59.1\node_modules\playwright\lib\testActions.js:96:18) - at i. (D:\dcprint\erp-project\node_modules\.pnpm\playwright@1.59.1\node_modules\playwright\lib\program.js:56:7) - -Running 8 tests using 2 workers - -[1/8] [chromium] › tests\inbound.spec.ts:41:7 › 入库管理测试 › TC-INBOUND-001: 入库管理页面正确加载 -[2/8] [chromium] › tests\inbound-flow.spec.ts:56:7 › 入库管理端到端流程 › E2E-IN-001: 入库管理页面正确加载 -[3/8] [chromium] › tests\inbound.spec.ts:53:7 › 入库管理测试 › TC-INBOUND-002: 按条件查询入库单 -[4/8] [chromium] › tests\inbound-flow.spec.ts:67:7 › 入库管理端到端流程 › E2E-IN-002 + E2E-IN-006: 新增入库单并出现在列表 -[5/8] [chromium] › tests\inbound.spec.ts:76:7 › 入库管理测试 › UI测试: 入库管理页面元素检查 -[6/8] [chromium] › tests\inbound-flow.spec.ts:110:7 › 入库管理端到端流程 › E2E-IN-006b: 新增对话框内采购订单搜索框可见且可输入 -[chromium] › tests\inbound.spec.ts:76:7 › 入库管理测试 › UI测试: 入库管理页面元素检查 -页面按钮数量: 269 - -[7/8] [chromium] › tests\inbound.spec.ts:97:7 › 入库管理测试 › 性能测试: 入库管理页面加载时间 -[8/8] [chromium] › tests\inbound-flow.spec.ts:126:7 › 入库管理端到端流程 › E2E-IN-009: 切料页面可打开 -[chromium] › tests\inbound.spec.ts:97:7 › 入库管理测试 › 性能测试: 入库管理页面加载时间 -页面加载时间: 2008ms - - 8 passed (56.4s) diff --git a/_e2e_run_0827.txt b/_e2e_run_0827.txt deleted file mode 100644 index 309ba120..00000000 --- a/_e2e_run_0827.txt +++ /dev/null @@ -1,17 +0,0 @@ -Error: [safe-delete][SAFE_DELETE_BULK_CONFIRM_REQUIRED] {"count":91,"threshold":50,"scope":"turn","targets":["D:\\dcprint\\erp-project\\node_modules\\.cache\\pw\\test-results"],"targetCount":1} - - at C:\Program Files\WorkBuddy\resources\app.asar.unpacked\cli\vendor\shim\genie-safe-delete.cjs:222 - - 220 | const detail = e && e.stderr ? String(e.stderr).trim() : ''; - 221 | if (detail) { -> 222 | throw new Error(detail); - | ^ - 223 | } - 224 | throw new Error(`[safe-delete][SAFE_DELETE_BULK_GUARD_ERROR] ${(e && e.message) || String(e)}`); - 225 | } - at checkBulkDeleteGuard (C:\Program Files\WorkBuddy\resources\app.asar.unpacked\cli\vendor\shim\genie-safe-delete.cjs:222:19) - at tryTrash (C:\Program Files\WorkBuddy\resources\app.asar.unpacked\cli\vendor\shim\genie-safe-delete.cjs:546:5) - at tryRm (C:\Program Files\WorkBuddy\resources\app.asar.unpacked\cli\vendor\shim\genie-safe-delete.cjs:722:5) - at Object.wrappedPromisesRm (C:\Program Files\WorkBuddy\resources\app.asar.unpacked\cli\vendor\shim\genie-safe-delete.cjs:766:15) - - \ No newline at end of file diff --git a/_e2e_run_0827b.txt b/_e2e_run_0827b.txt deleted file mode 100644 index 173690cd..00000000 --- a/_e2e_run_0827b.txt +++ /dev/null @@ -1,51 +0,0 @@ -[GlobalSetup] Attempt 1/3 failed, retrying in 2s... Error: reset-lock returned 401: Authentication required - at globalSetup (D:\dcprint\erp-project\tests\global-setup.ts:50:15) - at processTicksAndRejections (node:internal/process/task_queues:103:5) - at Object.setup (D:\dcprint\erp-project\node_modules\.pnpm\playwright@1.59.1\node_modules\playwright\lib\runner\tasks.js:174:27) - at taskLoop (D:\dcprint\erp-project\node_modules\.pnpm\playwright@1.59.1\node_modules\playwright\lib\runner\taskRunner.js:62:11) - at TaskRunner.runDeferCleanup (D:\dcprint\erp-project\node_modules\.pnpm\playwright@1.59.1\node_modules\playwright\lib\runner\taskRunner.js:78:5) - at TaskRunner.run (D:\dcprint\erp-project\node_modules\.pnpm\playwright@1.59.1\node_modules\playwright\lib\runner\taskRunner.js:42:33) - at runTasks (D:\dcprint\erp-project\node_modules\.pnpm\playwright@1.59.1\node_modules\playwright\lib\runner\tasks.js:80:18) - at runAllTestsWithConfig (D:\dcprint\erp-project\node_modules\.pnpm\playwright@1.59.1\node_modules\playwright\lib\runner\testRunner.js:388:18) - at runTests (D:\dcprint\erp-project\node_modules\.pnpm\playwright@1.59.1\node_modules\playwright\lib\testActions.js:96:18) - at i. (D:\dcprint\erp-project\node_modules\.pnpm\playwright@1.59.1\node_modules\playwright\lib\program.js:56:7) -[GlobalSetup] Attempt 2/3 failed, retrying in 2s... Error: reset-lock returned 401: Authentication required - at globalSetup (D:\dcprint\erp-project\tests\global-setup.ts:50:15) - at processTicksAndRejections (node:internal/process/task_queues:103:5) - at Object.setup (D:\dcprint\erp-project\node_modules\.pnpm\playwright@1.59.1\node_modules\playwright\lib\runner\tasks.js:174:27) - at taskLoop (D:\dcprint\erp-project\node_modules\.pnpm\playwright@1.59.1\node_modules\playwright\lib\runner\taskRunner.js:62:11) - at TaskRunner.runDeferCleanup (D:\dcprint\erp-project\node_modules\.pnpm\playwright@1.59.1\node_modules\playwright\lib\runner\taskRunner.js:78:5) - at TaskRunner.run (D:\dcprint\erp-project\node_modules\.pnpm\playwright@1.59.1\node_modules\playwright\lib\runner\taskRunner.js:42:33) - at runTasks (D:\dcprint\erp-project\node_modules\.pnpm\playwright@1.59.1\node_modules\playwright\lib\runner\tasks.js:80:18) - at runAllTestsWithConfig (D:\dcprint\erp-project\node_modules\.pnpm\playwright@1.59.1\node_modules\playwright\lib\runner\testRunner.js:388:18) - at runTests (D:\dcprint\erp-project\node_modules\.pnpm\playwright@1.59.1\node_modules\playwright\lib\testActions.js:96:18) - at i. (D:\dcprint\erp-project\node_modules\.pnpm\playwright@1.59.1\node_modules\playwright\lib\program.js:56:7) -[GlobalSetup] Failed to reset admin lock after all retries: Error: reset-lock returned 401: Authentication required - at globalSetup (D:\dcprint\erp-project\tests\global-setup.ts:50:15) - at processTicksAndRejections (node:internal/process/task_queues:103:5) - at Object.setup (D:\dcprint\erp-project\node_modules\.pnpm\playwright@1.59.1\node_modules\playwright\lib\runner\tasks.js:174:27) - at taskLoop (D:\dcprint\erp-project\node_modules\.pnpm\playwright@1.59.1\node_modules\playwright\lib\runner\taskRunner.js:62:11) - at TaskRunner.runDeferCleanup (D:\dcprint\erp-project\node_modules\.pnpm\playwright@1.59.1\node_modules\playwright\lib\runner\taskRunner.js:78:5) - at TaskRunner.run (D:\dcprint\erp-project\node_modules\.pnpm\playwright@1.59.1\node_modules\playwright\lib\runner\taskRunner.js:42:33) - at runTasks (D:\dcprint\erp-project\node_modules\.pnpm\playwright@1.59.1\node_modules\playwright\lib\runner\tasks.js:80:18) - at runAllTestsWithConfig (D:\dcprint\erp-project\node_modules\.pnpm\playwright@1.59.1\node_modules\playwright\lib\runner\testRunner.js:388:18) - at runTests (D:\dcprint\erp-project\node_modules\.pnpm\playwright@1.59.1\node_modules\playwright\lib\testActions.js:96:18) - at i. (D:\dcprint\erp-project\node_modules\.pnpm\playwright@1.59.1\node_modules\playwright\lib\program.js:56:7) - -Running 8 tests using 2 workers - -[1/8] [chromium] › tests\inbound.spec.ts:41:7 › 入库管理测试 › TC-INBOUND-001: 入库管理页面正确加载 -[2/8] [chromium] › tests\inbound-flow.spec.ts:56:7 › 入库管理端到端流程 › E2E-IN-001: 入库管理页面正确加载 -[3/8] [chromium] › tests\inbound.spec.ts:53:7 › 入库管理测试 › TC-INBOUND-002: 按条件查询入库单 -[4/8] [chromium] › tests\inbound-flow.spec.ts:67:7 › 入库管理端到端流程 › E2E-IN-002 + E2E-IN-006: 新增入库单并出现在列表 -[5/8] [chromium] › tests\inbound.spec.ts:76:7 › 入库管理测试 › UI测试: 入库管理页面元素检查 -[6/8] [chromium] › tests\inbound-flow.spec.ts:110:7 › 入库管理端到端流程 › E2E-IN-006b: 新增对话框内采购订单搜索框可见且可输入 -[chromium] › tests\inbound.spec.ts:76:7 › 入库管理测试 › UI测试: 入库管理页面元素检查 -页面按钮数量: 271 - -[7/8] [chromium] › tests\inbound.spec.ts:97:7 › 入库管理测试 › 性能测试: 入库管理页面加载时间 -[chromium] › tests\inbound.spec.ts:97:7 › 入库管理测试 › 性能测试: 入库管理页面加载时间 -页面加载时间: 2656ms - -[8/8] [chromium] › tests\inbound-flow.spec.ts:126:7 › 入库管理端到端流程 › E2E-IN-009: 切料页面可打开 - 8 passed (1.5m) diff --git a/_schema_dump.txt b/_schema_dump.txt deleted file mode 100644 index ba70dfb8..00000000 --- a/_schema_dump.txt +++ /dev/null @@ -1,877 +0,0 @@ - -### sal_sample_order (34 cols) - id | bigint unsigned | null=NO | key=PRI | def=null - order_no | varchar(50) | null=NO | key=UNI | def=null - notify_date | date | null=YES | key=MUL | def=null - customer_id | bigint unsigned | null=YES | key=MUL | def=null - customer_name | varchar(100) | null=YES | key= | def=null - product_name | varchar(200) | null=YES | key= | def=null - material_no | varchar(50) | null=YES | key= | def=null - version | varchar(20) | null=YES | key= | def=A - size_spec | varchar(200) | null=YES | key= | def=null - material_spec | varchar(200) | null=YES | key= | def=null - specification | varchar(200) | null=YES | key= | def=null - quantity | int | null=YES | key= | def=0 - order_date | date | null=YES | key= | def=null - customer_require_date | date | null=YES | key= | def=null - delivery_date | date | null=YES | key= | def=null - actual_delivery_date | date | null=YES | key= | def=null - delivery_status | varchar(20) | null=YES | key= | def=pending - status | varchar(20) | null=YES | key=MUL | def=pending - remark | text | null=YES | key= | def=null - create_time | datetime | null=YES | key= | def=CURRENT_TIMESTAMP - update_time | datetime | null=YES | key= | def=CURRENT_TIMESTAMP - create_by | bigint unsigned | null=YES | key= | def=null - process_card_id | bigint unsigned | null=YES | key= | def=null - work_order_id | bigint unsigned | null=YES | key= | def=null - sales_order_id | bigint unsigned | null=YES | key= | def=null - sample_fee | decimal(18,4) | null=YES | key= | def=0.0000 - fee_charged | tinyint | null=YES | key= | def=0 - fee_deductible | tinyint | null=YES | key= | def=0 - fee_deducted | tinyint | null=YES | key= | def=0 - sample_version | int | null=YES | key= | def=1 - parent_version_id | bigint unsigned | null=YES | key= | def=null - converted_at | datetime | null=YES | key= | def=null - converted_by | bigint unsigned | null=YES | key= | def=null - deleted | tinyint | null=YES | key= | def=0 - -### prd_standard_card (94 cols) - id | bigint unsigned | null=NO | key=PRI | def=null - card_no | varchar(50) | null=NO | key=UNI | def=null - name | varchar(200) | null=YES | key= | def=null - type | varchar(20) | null=YES | key= | def=process - customer_id | bigint unsigned | null=YES | key=MUL | def=null - customer_name | varchar(100) | null=YES | key= | def=null - customer_code | varchar(50) | null=YES | key= | def=null - product_name | varchar(100) | null=YES | key= | def=null - version | varchar(10) | null=YES | key= | def=null - effective_date | date | null=YES | key= | def=null - expiry_date | date | null=YES | key= | def=null - create_user | int | null=YES | key= | def=null - audit_user | int | null=YES | key= | def=null - date | date | null=YES | key= | def=null - document_code | varchar(50) | null=YES | key= | def=null - finished_size | varchar(50) | null=YES | key= | def=null - tolerance | varchar(50) | null=YES | key= | def=null - material_name | varchar(100) | null=YES | key= | def=null - material_type | varchar(20) | null=YES | key= | def=null - mold_type | varchar(100) | null=YES | key= | def= - layout_type | varchar(50) | null=YES | key= | def=null - spacing | varchar(20) | null=YES | key= | def=null - spacing_value | varchar(20) | null=YES | key= | def=null - sheet_width | varchar(20) | null=YES | key= | def=null - sheet_length | varchar(20) | null=YES | key= | def=null - core_type | varchar(50) | null=YES | key= | def=null - paper_direction | varchar(20) | null=YES | key= | def=null - roll_width | varchar(20) | null=YES | key= | def=null - paper_edge | varchar(20) | null=YES | key= | def=null - standard_usage | varchar(50) | null=YES | key= | def=null - jump_distance | varchar(20) | null=YES | key= | def=null - process_flow1 | varchar(100) | null=YES | key= | def=null - process_flow2 | varchar(100) | null=YES | key= | def=null - print_type | varchar(50) | null=YES | key= | def=null - first_jump_distance | varchar(20) | null=YES | key= | def=null - sequences | json | null=YES | key= | def=null - film_manufacturer | varchar(100) | null=YES | key= | def=null - film_code | varchar(50) | null=YES | key= | def=null - film_size | varchar(50) | null=YES | key= | def=null - process_method | varchar(50) | null=YES | key= | def=null - stamping_method | varchar(50) | null=YES | key= | def=null - mold_code | varchar(50) | null=YES | key= | def=null - back_mold_code | varchar(50) | null=YES | key= | def=null - layout_method | varchar(50) | null=YES | key= | def=null - layout_way | varchar(50) | null=YES | key= | def=null - jump_distance2 | varchar(20) | null=YES | key= | def=null - mylar_material | varchar(100) | null=YES | key= | def=null - mylar_specs | varchar(50) | null=YES | key= | def=null - mylar_layout | varchar(50) | null=YES | key= | def=null - mylar_jump | varchar(20) | null=YES | key= | def=null - adhesive_type | varchar(50) | null=YES | key= | def=null - adhesive_manufacturer | varchar(100) | null=YES | key= | def=null - adhesive_code | varchar(50) | null=YES | key= | def=null - adhesive_size | varchar(50) | null=YES | key= | def=null - adhesive_specs | varchar(50) | null=YES | key= | def=null - dashed_knife | tinyint | null=YES | key= | def=0 - slice_per_row | varchar(20) | null=YES | key= | def=null - slice_per_roll | varchar(20) | null=YES | key= | def=null - slice_per_bundle | varchar(20) | null=YES | key= | def=null - slice_per_bag | varchar(20) | null=YES | key= | def=null - slice_per_box | varchar(20) | null=YES | key= | def=null - packing_qty | varchar(20) | null=YES | key= | def=null - back_knife_mold | varchar(50) | null=YES | key= | def=null - back_mylar_mold | varchar(50) | null=YES | key= | def=null - etch_mold | varchar(100) | null=YES | key= | def= - storage_location | varchar(100) | null=YES | key= | def= - extra_field | varchar(100) | null=YES | key= | def= - release_paper_code | varchar(50) | null=YES | key= | def=null - release_paper_type | varchar(50) | null=YES | key= | def=null - release_paper_category | varchar(50) | null=YES | key= | def=null - release_paper_specs | varchar(50) | null=YES | key= | def=null - padding_material | varchar(100) | null=YES | key= | def=null - packing_material | varchar(100) | null=YES | key= | def=null - special_color | varchar(200) | null=YES | key= | def=null - color_formula | varchar(200) | null=YES | key= | def=null - file_path | varchar(200) | null=YES | key= | def=null - sample_info | varchar(200) | null=YES | key= | def=null - notes | text | null=YES | key= | def=null - remark | text | null=YES | key= | def=null - glue_type | varchar(50) | null=YES | key= | def=null - packing_type | varchar(50) | null=YES | key= | def=null - creator | varchar(50) | null=YES | key= | def=null - reviewer | varchar(50) | null=YES | key= | def=null - factory_manager | varchar(50) | null=YES | key= | def=null - quality_manager | varchar(50) | null=YES | key= | def=null - sales | varchar(50) | null=YES | key= | def=null - approver | varchar(50) | null=YES | key= | def=null - status | tinyint | null=YES | key=MUL | def=1 - create_by | bigint unsigned | null=YES | key=MUL | def=null - reviewer_id | bigint unsigned | null=YES | key=MUL | def=null - create_time | datetime | null=YES | key= | def=CURRENT_TIMESTAMP - update_time | datetime | null=YES | key= | def=CURRENT_TIMESTAMP - deleted | tinyint | null=NO | key= | def=0 - update_by | bigint unsigned | null=YES | key= | def=null - -### sal_sample_inventory (14 cols) - id | bigint unsigned | null=NO | key=PRI | def=null - sample_order_id | bigint unsigned | null=YES | key=MUL | def=null - product_name | varchar(200) | null=YES | key= | def=null - material_no | varchar(50) | null=YES | key= | def=null - quantity | int | null=YES | key= | def=0 - unit | varchar(20) | null=YES | key= | def=pcs - warehouse_id | bigint unsigned | null=YES | key= | def=null - status | varchar(20) | null=YES | key= | def=available - sent_to | varchar(200) | null=YES | key= | def=null - sent_date | date | null=YES | key= | def=null - remark | text | null=YES | key= | def=null - create_time | datetime | null=YES | key= | def=CURRENT_TIMESTAMP - update_time | datetime | null=YES | key= | def=CURRENT_TIMESTAMP - deleted | tinyint | null=YES | key= | def=0 - -### sal_sample_feedback (10 cols) - id | bigint unsigned | null=NO | key=PRI | def=null - sample_order_id | bigint unsigned | null=NO | key=MUL | def=null - round | int | null=NO | key= | def=1 - feedback_content | text | null=YES | key= | def=null - modification_requirements | text | null=YES | key= | def=null - confirmation_status | varchar(20) | null=YES | key= | def=pending - feedback_by | varchar(100) | null=YES | key= | def=null - feedback_time | datetime | null=YES | key= | def=null - create_time | datetime | null=YES | key= | def=CURRENT_TIMESTAMP - deleted | tinyint | null=YES | key= | def=0 - -### sal_sample_quotation -- ERROR: ER_NO_SUCH_TABLE Table 'vnerpdacahng.sal_sample_quotation' doesn't exist - -### prod_work_order (35 cols) - id | bigint unsigned | null=NO | key=PRI | def=null - work_order_no | varchar(50) | null=NO | key=UNI | def=null - order_id | bigint unsigned | null=YES | key= | def=null - order_no | varchar(50) | null=YES | key=MUL | def=null - bom_id | bigint unsigned | null=YES | key= | def=null - customer_name | varchar(200) | null=YES | key= | def=null - product_name | varchar(200) | null=YES | key= | def=null - order_type | tinyint | null=YES | key= | def=0 - quantity | decimal(15,2) | null=YES | key= | def=0.00 - unit | varchar(20) | null=YES | key= | def=null - status | varchar(20) | null=YES | key=MUL | def=pending - priority | varchar(20) | null=YES | key= | def=normal - plan_start_date | date | null=YES | key= | def=null - plan_end_date | date | null=YES | key= | def=null - actual_start_date | date | null=YES | key= | def=null - actual_end_date | date | null=YES | key= | def=null - remark | text | null=YES | key= | def=null - create_by | bigint unsigned | null=YES | key= | def=null - create_time | datetime | null=YES | key=MUL | def=CURRENT_TIMESTAMP - update_by | bigint unsigned | null=YES | key= | def=null - update_time | datetime | null=YES | key= | def=CURRENT_TIMESTAMP - deleted | tinyint | null=YES | key= | def=0 - picked_qty | decimal(10,2) | null=YES | key= | def=0.00 - finished_qty | decimal(10,2) | null=YES | key= | def=0.00 - returned_qty | decimal(10,2) | null=YES | key= | def=0.00 - total_material_cost | decimal(12,2) | null=YES | key= | def=0.00 - total_labor_cost | decimal(18,4) | null=YES | key= | def=0.0000 - total_tool_cost | decimal(18,4) | null=YES | key= | def=0.0000 - total_overhead_cost | decimal(18,4) | null=YES | key= | def=0.0000 - unit_cost | decimal(18,4) | null=YES | key= | def=0.0000 - approved_at | datetime | null=YES | key= | def=null - approved_by | bigint unsigned | null=YES | key= | def=null - cancelled_at | datetime | null=YES | key= | def=null - cancelled_by | bigint unsigned | null=YES | key= | def=null - cancelled_reason | varchar(500) | null=YES | key= | def=null - -### prod_work_order_item (11 cols) - id | bigint unsigned | null=NO | key=PRI | def=null - work_order_id | bigint unsigned | null=NO | key=MUL | def=null - line_no | int | null=YES | key= | def=0 - material_id | bigint unsigned | null=YES | key= | def=null - material_name | varchar(200) | null=YES | key= | def=null - quantity | decimal(15,2) | null=YES | key= | def=0.00 - unit | varchar(20) | null=YES | key= | def=null - unit_price | decimal(15,2) | null=YES | key= | def=0.00 - total_price | decimal(15,2) | null=YES | key= | def=0.00 - remark | text | null=YES | key= | def=null - create_time | datetime | null=YES | key= | def=CURRENT_TIMESTAMP - -### prod_work_order_material_req (8 cols) - id | bigint unsigned | null=NO | key=PRI | def=null - work_order_id | bigint unsigned | null=NO | key=MUL | def=null - bom_line_id | bigint unsigned | null=YES | key= | def=null - material_id | bigint unsigned | null=YES | key=MUL | def=null - material_name | varchar(200) | null=YES | key= | def=null - required_qty | decimal(14,3) | null=YES | key= | def=0.000 - unit | varchar(20) | null=YES | key= | def=null - create_time | datetime | null=YES | key= | def=CURRENT_TIMESTAMP - -### prd_work_order (22 cols) - id | bigint unsigned | null=NO | key=PRI | def=null - work_order_no | varchar(50) | null=NO | key=UNI | def=null - work_order_date | date | null=YES | key= | def=null - sales_order_id | bigint unsigned | null=YES | key=MUL | def=null - material_id | bigint unsigned | null=NO | key=MUL | def=null - plan_qty | decimal(18,4) | null=NO | key= | def=null - completed_qty | decimal(18,4) | null=YES | key= | def=0.0000 - unit | varchar(20) | null=YES | key= | def=null - plan_start_date | date | null=YES | key= | def=null - plan_end_date | date | null=YES | key= | def=null - actual_start_date | date | null=YES | key= | def=null - actual_end_date | date | null=YES | key= | def=null - workshop_id | bigint unsigned | null=YES | key=MUL | def=null - workcenter_id | bigint unsigned | null=YES | key=MUL | def=null - priority | tinyint | null=YES | key= | def=1 - status | tinyint | null=YES | key=MUL | def=1 - remark | text | null=YES | key= | def=null - create_time | datetime | null=YES | key= | def=CURRENT_TIMESTAMP - update_time | datetime | null=YES | key= | def=CURRENT_TIMESTAMP - create_by | bigint unsigned | null=YES | key= | def=null - deleted | tinyint | null=NO | key= | def=0 - update_by | bigint unsigned | null=YES | key= | def=null - -### prd_bom (12 cols) - id | bigint unsigned | null=NO | key=PRI | def=null - bom_name | varchar(200) | null=NO | key= | def=null - product_id | bigint unsigned | null=YES | key=MUL | def=null - version | varchar(20) | null=YES | key= | def=1.0 - total_cost | decimal(18,4) | null=YES | key= | def=0.0000 - status | tinyint | null=YES | key= | def=1 - remark | text | null=YES | key= | def=null - create_time | datetime | null=YES | key= | def=CURRENT_TIMESTAMP - update_time | datetime | null=YES | key= | def=CURRENT_TIMESTAMP - create_by | bigint unsigned | null=YES | key= | def=null - deleted | tinyint | null=YES | key= | def=0 - update_by | bigint unsigned | null=YES | key= | def=null - -### prd_bom_item -- ERROR: ER_NO_SUCH_TABLE Table 'vnerpdacahng.prd_bom_item' doesn't exist - -### pur_request (24 cols) - id | int unsigned | null=NO | key=PRI | def=null - request_no | varchar(50) | null=NO | key=UNI | def=null - request_date | date | null=YES | key=MUL | def=null - request_type | varchar(20) | null=YES | key= | def=material - request_dept_id | int unsigned | null=YES | key=MUL | def=null - request_dept | varchar(100) | null=YES | key= | def=null - requester_id | bigint unsigned | null=YES | key=MUL | def=null - requester_name | varchar(50) | null=YES | key= | def=null - reviewer_id | int unsigned | null=YES | key= | def=null - reviewer_name | varchar(50) | null=YES | key= | def=null - approver_id | bigint unsigned | null=YES | key= | def=null - approver_name | varchar(50) | null=YES | key= | def=null - total_amount | decimal(14,2) | null=YES | key= | def=0.00 - currency | varchar(10) | null=YES | key= | def=CNY - status | tinyint | null=YES | key=MUL | def=0 - priority | tinyint | null=YES | key= | def=1 - expected_date | date | null=YES | key= | def=null - supplier_name | varchar(100) | null=YES | key= | def=null - remark | text | null=YES | key= | def=null - create_by | int unsigned | null=YES | key= | def=null - create_time | datetime | null=YES | key= | def=CURRENT_TIMESTAMP - update_by | int unsigned | null=YES | key= | def=null - update_time | datetime | null=YES | key= | def=CURRENT_TIMESTAMP - deleted | tinyint | null=NO | key= | def=0 - -### pur_request_item (17 cols) - id | int unsigned | null=NO | key=PRI | def=null - request_id | int unsigned | null=NO | key=MUL | def=null - line_no | int unsigned | null=NO | key= | def=null - material_id | int unsigned | null=YES | key=MUL | def=null - material_code | varchar(50) | null=YES | key= | def=null - material_name | varchar(200) | null=YES | key= | def=null - material_spec | varchar(500) | null=YES | key= | def=null - material_unit | varchar(20) | null=YES | key= | def=null - quantity | decimal(14,3) | null=YES | key= | def=0.000 - price | decimal(14,4) | null=YES | key= | def=0.0000 - amount | decimal(14,2) | null=YES | key= | def=0.00 - supplier_name | varchar(100) | null=YES | key= | def=null - expected_date | date | null=YES | key= | def=null - remark | text | null=YES | key= | def=null - create_time | datetime | null=YES | key= | def=CURRENT_TIMESTAMP - update_time | datetime | null=YES | key= | def=CURRENT_TIMESTAMP - deleted | tinyint(1) | null=YES | key= | def=0 - -### pur_purchase_order (35 cols) - id | bigint unsigned | null=NO | key=PRI | def=null - po_no | varchar(50) | null=NO | key=UNI | def=null - supplier_id | bigint unsigned | null=YES | key=MUL | def=null - supplier_name | varchar(100) | null=NO | key= | def=null - supplier_code | varchar(50) | null=YES | key= | def=null - order_date | date | null=NO | key=MUL | def=null - delivery_date | date | null=YES | key= | def=null - currency | varchar(10) | null=YES | key= | def=CNY - exchange_rate | decimal(18,4) | null=YES | key= | def=1.0000 - total_amount | decimal(18,4) | null=YES | key= | def=0.0000 - total_quantity | decimal(18,4) | null=YES | key= | def=0.0000 - tax_rate | decimal(18,4) | null=YES | key= | def=13.0000 - tax_amount | decimal(18,4) | null=YES | key= | def=0.0000 - grand_total | decimal(18,4) | null=YES | key= | def=0.0000 - status | tinyint unsigned | null=YES | key=MUL | def=10 - over_receipt_tolerance | decimal(18,4) | null=YES | key= | def=5.0000 - payment_terms | varchar(100) | null=YES | key= | def=null - delivery_address | text | null=YES | key= | def=null - contact_person | varchar(50) | null=YES | key= | def=null - contact_phone | varchar(50) | null=YES | key= | def=null - remark | text | null=YES | key= | def=null - create_by | bigint unsigned | null=YES | key= | def=null - create_time | datetime | null=YES | key= | def=CURRENT_TIMESTAMP - update_by | bigint unsigned | null=YES | key= | def=null - update_time | datetime | null=YES | key= | def=CURRENT_TIMESTAMP - audit_by | bigint unsigned | null=YES | key= | def=null - audit_time | datetime | null=YES | key= | def=null - close_by | bigint unsigned | null=YES | key= | def=null - close_time | datetime | null=YES | key= | def=null - close_reason | varchar(200) | null=YES | key= | def=null - deleted | tinyint | null=NO | key= | def=0 - base_total_amount | decimal(18,4) | null=NO | key= | def=0.0000 - base_tax_amount | decimal(18,4) | null=NO | key= | def=0.0000 - base_grand_total | decimal(18,4) | null=NO | key= | def=0.0000 - received_quantity | decimal(18,4) | null=NO | key= | def=0.0000 - -### pur_purchase_order_line (26 cols) - id | bigint unsigned | null=NO | key=PRI | def=null - po_id | bigint unsigned | null=NO | key=MUL | def=null - line_no | int unsigned | null=NO | key= | def=null - material_id | bigint unsigned | null=YES | key=MUL | def=null - material_code | varchar(50) | null=NO | key= | def=null - material_name | varchar(200) | null=NO | key= | def=null - material_spec | varchar(500) | null=YES | key= | def=null - unit | varchar(20) | null=YES | key= | def=件 - order_qty | decimal(18,4) | null=NO | key= | def=0.0000 - received_qty | decimal(18,4) | null=YES | key= | def=0.0000 - returned_qty | decimal(18,4) | null=YES | key= | def=0.0000 - unit_price | decimal(18,4) | null=NO | key= | def=0.0000 - amount | decimal(18,4) | null=YES | key= | def=0.0000 - tax_rate | decimal(18,4) | null=YES | key= | def=13.0000 - tax_amount | decimal(18,4) | null=YES | key= | def=0.0000 - line_total | decimal(18,4) | null=YES | key= | def=0.0000 - require_date | date | null=YES | key= | def=null - closed_flag | tinyint(1) | null=YES | key= | def=0 - closed_reason | varchar(200) | null=YES | key= | def=null - remark | text | null=YES | key= | def=null - create_time | datetime | null=YES | key= | def=CURRENT_TIMESTAMP - update_time | datetime | null=YES | key= | def=CURRENT_TIMESTAMP - base_unit_price | decimal(18,4) | null=NO | key= | def=0.0000 - base_amount | decimal(18,4) | null=NO | key= | def=0.0000 - base_tax_amount | decimal(18,4) | null=NO | key= | def=0.0000 - base_line_total | decimal(18,4) | null=NO | key= | def=0.0000 - -### pur_supplier (26 cols) - id | bigint unsigned | null=NO | key=PRI | def=null - supplier_code | varchar(50) | null=NO | key=UNI | def=null - supplier_name | varchar(100) | null=NO | key= | def=null - short_name | varchar(50) | null=YES | key= | def=null - supplier_type | tinyint | null=YES | key= | def=null - province | varchar(50) | null=YES | key= | def=null - city | varchar(50) | null=YES | key= | def=null - address | varchar(255) | null=YES | key= | def=null - contact_name | varchar(50) | null=YES | key= | def=null - contact_phone | varchar(20) | null=YES | key= | def=null - contact_email | varchar(100) | null=YES | key= | def=null - business_license | varchar(50) | null=YES | key= | def=null - tax_number | varchar(50) | null=YES | key= | def=null - bank_name | varchar(100) | null=YES | key= | def=null - bank_account | varchar(50) | null=YES | key= | def=null - credit_level | varchar(20) | null=YES | key= | def=null - cooperation_status | tinyint | null=YES | key= | def=1 - settlement_method | varchar(50) | null=YES | key= | def=null - payment_terms | varchar(100) | null=YES | key= | def=null - status | tinyint | null=YES | key= | def=1 - remark | text | null=YES | key= | def=null - create_time | datetime | null=YES | key= | def=CURRENT_TIMESTAMP - update_time | datetime | null=YES | key= | def=CURRENT_TIMESTAMP - create_by | bigint unsigned | null=YES | key= | def=null - update_by | bigint unsigned | null=YES | key= | def=null - deleted | tinyint | null=NO | key= | def=0 - -### inv_inbound_order (29 cols) - id | bigint unsigned | null=NO | key=PRI | def=null - order_no | varchar(50) | null=NO | key=MUL | def=null - order_type | enum('purchase','return','transfer','other') | null=YES | key= | def=purchase - warehouse_id | bigint unsigned | null=NO | key=MUL | def=null - warehouse_code | varchar(50) | null=YES | key= | def=null - warehouse_name | varchar(100) | null=YES | key= | def=null - supplier_id | int unsigned | null=YES | key= | def=null - supplier_name | varchar(100) | null=YES | key= | def=null - operator_id | bigint unsigned | null=YES | key= | def=null - operator_name | varchar(50) | null=YES | key= | def=null - po_id | int unsigned | null=YES | key=MUL | def=null - po_no | varchar(50) | null=YES | key= | def=null - grn_type | enum('po','blind','return') | null=YES | key= | def=po - total_amount | decimal(18,4) | null=YES | key= | def=null - total_quantity | decimal(18,4) | null=YES | key= | def=0.0000 - status | enum('draft','pending','approved','completed','cancelled') | null=YES | key=MUL | def=draft - qc_status | enum('pending','pass','fail','partial') | null=YES | key= | def=pending - inbound_date | date | null=YES | key= | def=null - remark | text | null=YES | key= | def=null - create_by | bigint unsigned | null=YES | key= | def=null - create_time | datetime | null=YES | key= | def=CURRENT_TIMESTAMP - update_by | bigint unsigned | null=YES | key= | def=null - update_time | datetime | null=YES | key= | def=CURRENT_TIMESTAMP - deleted | tinyint | null=NO | key= | def=0 - currency | varchar(10) | null=YES | key= | def=CNY - exchange_rate | decimal(18,4) | null=YES | key= | def=1.0000 - base_total_amount | decimal(18,4) | null=YES | key= | def=0.0000 - source_type | varchar(20) | null=YES | key=MUL | def=null - source_order_id | bigint unsigned | null=YES | key= | def=null - -### inv_inbound_item (20 cols) - id | bigint unsigned | null=NO | key=PRI | def=null - order_id | bigint unsigned | null=NO | key=MUL | def=null - material_id | bigint unsigned | null=YES | key=MUL | def=null - material_name | varchar(100) | null=NO | key= | def=null - material_spec | varchar(200) | null=YES | key= | def=null - batch_no | varchar(50) | null=YES | key= | def=null - quantity | decimal(18,4) | null=YES | key= | def=null - unit | varchar(20) | null=YES | key= | def=null - unit_price | decimal(18,4) | null=YES | key= | def=null - total_price | decimal(18,4) | null=YES | key= | def=null - warehouse_location | varchar(50) | null=YES | key= | def=null - produce_date | date | null=YES | key= | def=null - expire_date | date | null=YES | key= | def=null - remark | text | null=YES | key= | def=null - create_time | datetime | null=YES | key= | def=CURRENT_TIMESTAMP - deleted | tinyint | null=YES | key= | def=0 - base_unit_price | decimal(18,4) | null=YES | key= | def=0.0000 - base_amount | decimal(18,4) | null=YES | key= | def=0.0000 - purchase_order_item_id | bigint unsigned | null=YES | key= | def=null - purchase_order_line_no | int unsigned | null=YES | key= | def=null - -### inv_warehouse (17 cols) - id | bigint unsigned | null=NO | key=PRI | def=null - warehouse_code | varchar(50) | null=NO | key=UNI | def=null - warehouse_name | varchar(100) | null=NO | key= | def=null - warehouse_type | tinyint | null=YES | key= | def=null - category_id | bigint unsigned | null=YES | key= | def=null - province | varchar(50) | null=YES | key= | def=null - city | varchar(50) | null=YES | key= | def=null - address | varchar(255) | null=YES | key= | def=null - manager_id | bigint unsigned | null=YES | key=MUL | def=null - contact_phone | varchar(20) | null=YES | key= | def=null - status | tinyint | null=YES | key=MUL | def=1 - remark | varchar(255) | null=YES | key= | def=null - create_time | datetime | null=YES | key= | def=CURRENT_TIMESTAMP - update_time | datetime | null=YES | key= | def=CURRENT_TIMESTAMP - deleted | tinyint | null=NO | key= | def=0 - create_by | bigint unsigned | null=YES | key= | def=null - update_by | bigint unsigned | null=YES | key= | def=null - -### qc_incoming_inspection (17 cols) - id | int | null=NO | key=PRI | def=null - inspection_no | varchar(50) | null=NO | key=UNI | def=null - inspection_date | date | null=NO | key=MUL | def=null - supplier_name | varchar(100) | null=NO | key=MUL | def=null - material_code | varchar(50) | null=YES | key= | def=null - material_name | varchar(100) | null=NO | key=MUL | def=null - specification | varchar(100) | null=NO | key= | def=null - batch_no | varchar(50) | null=NO | key=MUL | def=null - quantity | decimal(10,2) | null=NO | key= | def=null - unit | varchar(20) | null=NO | key= | def=null - inspection_type | varchar(20) | null=NO | key= | def=null - inspection_result | varchar(20) | null=NO | key=MUL | def=null - inspector_name | varchar(50) | null=NO | key= | def=null - remark | text | null=YES | key= | def=null - create_time | datetime | null=NO | key= | def=CURRENT_TIMESTAMP - update_time | datetime | null=NO | key= | def=CURRENT_TIMESTAMP - deleted | tinyint | null=NO | key= | def=0 - -### qc_incoming_inspection_item (11 cols) - id | int | null=NO | key=PRI | def=null - inspection_id | int | null=NO | key=MUL | def=null - inspection_no | varchar(50) | null=NO | key=MUL | def=null - item_name | varchar(100) | null=NO | key=MUL | def=null - standard | varchar(255) | null=NO | key= | def=null - actual_value | varchar(255) | null=YES | key= | def=null - result | varchar(20) | null=NO | key=MUL | def=null - remark | text | null=YES | key= | def=null - create_time | datetime | null=NO | key= | def=CURRENT_TIMESTAMP - update_time | datetime | null=NO | key= | def=CURRENT_TIMESTAMP - deleted | tinyint | null=NO | key= | def=0 - -### qc_final_inspection -- ERROR: ER_NO_SUCH_TABLE Table 'vnerpdacahng.qc_final_inspection' doesn't exist - -### qc_process_inspection -- ERROR: ER_NO_SUCH_TABLE Table 'vnerpdacahng.qc_process_inspection' doesn't exist - -### qc_inspection (19 cols) - id | bigint unsigned | null=NO | key=PRI | def=null - inspection_no | varchar(50) | null=NO | key=UNI | def=null - inspection_type | tinyint | null=YES | key=MUL | def=null - source_type | varchar(50) | null=YES | key= | def=null - source_no | varchar(50) | null=YES | key=MUL | def=null - material_id | bigint unsigned | null=YES | key=MUL | def=null - batch_no | varchar(50) | null=YES | key= | def=null - inspection_qty | decimal(18,4) | null=YES | key= | def=0.0000 - qualified_qty | decimal(18,4) | null=YES | key= | def=0.0000 - unqualified_qty | decimal(18,4) | null=YES | key= | def=0.0000 - inspection_result | tinyint | null=YES | key= | def=null - inspector | varchar(50) | null=YES | key= | def=null - inspection_date | date | null=YES | key= | def=null - remark | text | null=YES | key= | def=null - create_time | datetime | null=YES | key= | def=CURRENT_TIMESTAMP - update_time | datetime | null=YES | key= | def=CURRENT_TIMESTAMP - deleted | tinyint | null=NO | key= | def=0 - create_by | bigint unsigned | null=YES | key= | def=null - update_by | bigint unsigned | null=YES | key= | def=null - -### split_order -- ERROR: ER_NO_SUCH_TABLE Table 'vnerpdacahng.split_order' doesn't exist - -### split_order_detail -- ERROR: ER_NO_SUCH_TABLE Table 'vnerpdacahng.split_order_detail' doesn't exist - -### prd_work_report (26 cols) - id | bigint unsigned | null=NO | key=PRI | def=null - report_no | varchar(50) | null=NO | key=UNI | def=null - work_order_id | bigint unsigned | null=NO | key=MUL | def=null - work_order_no | varchar(50) | null=YES | key= | def=null - process_name | varchar(50) | null=YES | key= | def=null - process_seq | int | null=YES | key= | def=null - equipment_id | bigint unsigned | null=YES | key= | def=null - operator_id | bigint unsigned | null=YES | key=MUL | def=null - operator_name | varchar(50) | null=YES | key= | def=null - plan_qty | decimal(18,4) | null=YES | key= | def=null - completed_qty | decimal(18,4) | null=YES | key= | def=0.0000 - qualified_qty | decimal(18,4) | null=YES | key= | def=0.0000 - defective_qty | decimal(18,4) | null=YES | key= | def=0.0000 - scrap_qty | decimal(18,4) | null=YES | key= | def=0.0000 - start_time | datetime | null=YES | key= | def=null - end_time | datetime | null=YES | key= | def=null - work_hours | decimal(10,2) | null=YES | key= | def=0.00 - is_first_piece | tinyint | null=YES | key= | def=0 - first_piece_status | tinyint | null=YES | key= | def=null - first_piece_inspector | varchar(50) | null=YES | key= | def=null - remark | text | null=YES | key= | def=null - tool_id | bigint unsigned | null=YES | key=MUL | def=null - screen_plate_id | bigint unsigned | null=YES | key=MUL | def=null - create_time | datetime | null=YES | key=MUL | def=CURRENT_TIMESTAMP - update_time | datetime | null=YES | key= | def=CURRENT_TIMESTAMP - deleted | tinyint | null=YES | key= | def=0 - -### prd_pick_order (12 cols) - id | bigint unsigned | null=NO | key=PRI | def=null - pick_no | varchar(50) | null=NO | key=UNI | def=null - work_order_id | bigint unsigned | null=NO | key=MUL | def=null - warehouse_id | bigint unsigned | null=YES | key=MUL | def=null - picker_name | varchar(100) | null=YES | key= | def=null - total_qty | decimal(18,4) | null=YES | key= | def=0.0000 - status | tinyint | null=YES | key=MUL | def=1 - remark | text | null=YES | key= | def=null - create_by | bigint unsigned | null=YES | key= | def=null - create_time | datetime | null=YES | key= | def=CURRENT_TIMESTAMP - update_time | datetime | null=YES | key= | def=CURRENT_TIMESTAMP - deleted | tinyint | null=YES | key= | def=0 - -### prd_pick_order_item (13 cols) - id | bigint unsigned | null=NO | key=PRI | def=null - pick_order_id | bigint unsigned | null=NO | key=MUL | def=null - material_id | bigint unsigned | null=YES | key=MUL | def=null - material_name | varchar(200) | null=YES | key= | def=null - material_spec | varchar(200) | null=YES | key= | def=null - required_qty | decimal(18,4) | null=YES | key= | def=0.0000 - actual_qty | decimal(18,4) | null=YES | key= | def=0.0000 - batch_no | varchar(50) | null=YES | key= | def=null - unit_cost | decimal(18,4) | null=YES | key= | def=0.0000 - line_amount | decimal(18,4) | null=YES | key= | def=0.0000 - unit | varchar(20) | null=YES | key= | def=pcs - remark | text | null=YES | key= | def=null - create_time | datetime | null=YES | key= | def=CURRENT_TIMESTAMP - -### prd_finish_order (11 cols) - id | bigint unsigned | null=NO | key=PRI | def=null - finish_no | varchar(50) | null=NO | key=UNI | def=null - work_order_id | bigint unsigned | null=NO | key=MUL | def=null - warehouse_id | bigint unsigned | null=YES | key=MUL | def=null - qualified_qty | decimal(18,4) | null=YES | key= | def=0.0000 - defective_qty | decimal(18,4) | null=YES | key= | def=0.0000 - status | tinyint | null=YES | key=MUL | def=1 - create_by | bigint unsigned | null=YES | key= | def=null - create_time | datetime | null=YES | key= | def=CURRENT_TIMESTAMP - update_time | datetime | null=YES | key= | def=CURRENT_TIMESTAMP - deleted | tinyint | null=YES | key= | def=0 - -### prd_return_order (12 cols) - id | bigint unsigned | null=NO | key=PRI | def=null - return_no | varchar(50) | null=NO | key=UNI | def=null - work_order_id | bigint unsigned | null=NO | key=MUL | def=null - pick_order_id | bigint unsigned | null=YES | key=MUL | def=null - warehouse_id | bigint unsigned | null=YES | key= | def=null - return_reason | varchar(500) | null=YES | key= | def=null - total_qty | decimal(18,4) | null=YES | key= | def=0.0000 - status | tinyint | null=YES | key=MUL | def=1 - create_by | bigint unsigned | null=YES | key= | def=null - create_time | datetime | null=YES | key= | def=CURRENT_TIMESTAMP - update_time | datetime | null=YES | key= | def=CURRENT_TIMESTAMP - deleted | tinyint | null=YES | key= | def=0 - -### inv_inventory_batch (25 cols) - id | int unsigned | null=NO | key=PRI | def=null - batch_no | varchar(50) | null=NO | key= | def=null - material_id | bigint unsigned | null=NO | key=MUL | def=null - material_name | varchar(100) | null=NO | key= | def=null - warehouse_id | bigint unsigned | null=NO | key=MUL | def=null - warehouse_name | varchar(100) | null=YES | key= | def=null - quantity | decimal(12,3) | null=YES | key= | def=0.000 - available_qty | decimal(12,3) | null=YES | key= | def=0.000 - locked_qty | decimal(12,3) | null=YES | key= | def=0.000 - unit | varchar(20) | null=YES | key= | def=件 - unit_price | decimal(12,2) | null=YES | key= | def=0.00 - produce_date | date | null=YES | key= | def=null - expire_date | date | null=YES | key= | def=null - inbound_date | date | null=YES | key= | def=null - status | tinyint | null=NO | key=MUL | def=1 - version | int unsigned | null=YES | key= | def=1 - create_time | datetime | null=YES | key= | def=CURRENT_TIMESTAMP - update_time | datetime | null=YES | key= | def=CURRENT_TIMESTAMP - deleted | tinyint | null=NO | key= | def=0 - alert_level | varchar(20) | null=YES | key=MUL | def=normal - last_alert_time | timestamp | null=YES | key= | def=null - inspection_status | varchar(20) | null=YES | key= | def=pending - quarantine_status | varchar(20) | null=YES | key= | def=none - create_by | bigint unsigned | null=YES | key= | def=null - update_by | bigint unsigned | null=YES | key= | def=null - -### inv_inventory (20 cols) - id | bigint unsigned | null=NO | key=PRI | def=null - material_id | bigint unsigned | null=NO | key=MUL | def=null - material_code | varchar(50) | null=YES | key=MUL | def=null - material_name | varchar(100) | null=YES | key= | def=null - warehouse_id | bigint unsigned | null=NO | key=MUL | def=null - warehouse_name | varchar(100) | null=YES | key= | def=null - quantity | decimal(18,4) | null=YES | key= | def=0.0000 - available_qty | decimal(18,4) | null=YES | key= | def=0.0000 - batch_no | varchar(50) | null=YES | key= | def=null - locked_qty | decimal(18,4) | null=YES | key= | def=0.0000 - unit | varchar(20) | null=YES | key= | def=null - unit_cost | decimal(18,4) | null=YES | key= | def=0.0000 - total_cost | decimal(18,4) | null=YES | key= | def=0.0000 - safety_stock | decimal(18,4) | null=YES | key= | def=0.0000 - version | int unsigned | null=YES | key= | def=1 - create_time | datetime | null=YES | key= | def=CURRENT_TIMESTAMP - update_time | datetime | null=YES | key= | def=CURRENT_TIMESTAMP - deleted | tinyint | null=NO | key= | def=0 - create_by | bigint unsigned | null=YES | key= | def=null - update_by | bigint unsigned | null=YES | key= | def=null - -### inv_inventory_transaction (20 cols) - id | bigint unsigned | null=NO | key=PRI | def=null - trans_no | varchar(50) | null=NO | key=UNI | def=null - trans_type | enum('in','out','transfer','adjust','return') | null=NO | key=MUL | def=null - source_type | varchar(20) | null=YES | key=MUL | def=null - source_id | bigint unsigned | null=YES | key= | def=null - source_line_id | bigint unsigned | null=YES | key= | def=null - material_id | bigint unsigned | null=YES | key=MUL | def=null - material_code | varchar(50) | null=YES | key= | def=null - batch_no | varchar(50) | null=YES | key=MUL | def=null - warehouse_id | bigint unsigned | null=YES | key=MUL | def=null - location_id | bigint unsigned | null=YES | key= | def=null - quantity | decimal(14,3) | null=YES | key= | def=0.000 - unit_cost | decimal(14,4) | null=YES | key= | def=0.0000 - total_cost | decimal(14,2) | null=YES | key= | def=0.00 - unit_price | decimal(14,4) | null=YES | key= | def=0.0000 - total_amount | decimal(14,2) | null=YES | key= | def=0.00 - reference_no | varchar(100) | null=YES | key= | def=null - remark | text | null=YES | key= | def=null - create_by | bigint unsigned | null=YES | key= | def=null - create_time | datetime | null=YES | key=MUL | def=CURRENT_TIMESTAMP - -### inv_inventory_log (8 cols) - id | bigint unsigned | null=NO | key=PRI | def=null - warehouse_id | int unsigned | null=YES | key=MUL | def=null - material_id | int unsigned | null=YES | key=MUL | def=null - change_type | varchar(20) | null=YES | key= | def=null - change_qty | decimal(12,3) | null=YES | key= | def=0.000 - order_no | varchar(50) | null=YES | key= | def=null - remark | text | null=YES | key= | def=null - create_time | datetime | null=YES | key=MUL | def=CURRENT_TIMESTAMP - -### inv_material (27 cols) - id | bigint unsigned | null=NO | key=PRI | def=null - material_code | varchar(50) | null=NO | key=UNI | def=null - material_name | varchar(100) | null=NO | key= | def=null - specification | varchar(255) | null=YES | key= | def=null - category_id | bigint unsigned | null=YES | key=MUL | def=null - material_type | tinyint | null=YES | key=MUL | def=null - unit | varchar(20) | null=YES | key= | def=null - barcode | varchar(50) | null=YES | key= | def=null - brand | varchar(50) | null=YES | key= | def=null - safety_stock | decimal(18,4) | null=YES | key= | def=0.0000 - max_stock | decimal(18,4) | null=YES | key= | def=null - min_stock | decimal(18,4) | null=YES | key= | def=null - purchase_price | decimal(18,4) | null=YES | key= | def=null - sale_price | decimal(18,4) | null=YES | key= | def=null - cost_price | decimal(18,4) | null=YES | key= | def=null - warehouse_id | bigint unsigned | null=YES | key=MUL | def=null - shelf_life | int | null=YES | key= | def=null - warning_days | int | null=YES | key= | def=null - is_batch_managed | tinyint | null=YES | key= | def=0 - is_serial_managed | tinyint | null=YES | key= | def=0 - status | tinyint | null=YES | key= | def=1 - remark | text | null=YES | key= | def=null - create_time | datetime | null=YES | key= | def=CURRENT_TIMESTAMP - update_time | datetime | null=YES | key= | def=CURRENT_TIMESTAMP - create_by | bigint unsigned | null=YES | key= | def=null - update_by | bigint unsigned | null=YES | key= | def=null - deleted | tinyint | null=NO | key= | def=0 - -### sal_delivery (28 cols) - id | bigint unsigned | null=NO | key=PRI | def=null - delivery_no | varchar(50) | null=NO | key=UNI | def=null - delivery_date | date | null=YES | key= | def=null - order_id | bigint unsigned | null=YES | key=MUL | def=null - order_no | varchar(50) | null=YES | key= | def=null - customer_id | bigint unsigned | null=NO | key=MUL | def=null - customer_name | varchar(100) | null=YES | key= | def=null - warehouse_id | bigint unsigned | null=NO | key=MUL | def=null - total_amount | decimal(18,4) | null=YES | key= | def=0.0000 - total_qty | decimal(18,4) | null=YES | key= | def=0.0000 - logistics_company | varchar(100) | null=YES | key= | def=null - tracking_no | varchar(50) | null=YES | key= | def=null - contact_name | varchar(50) | null=YES | key= | def=null - contact_phone | varchar(30) | null=YES | key= | def=null - delivery_address | varchar(500) | null=YES | key= | def=null - status | tinyint | null=YES | key= | def=1 - remark | text | null=YES | key= | def=null - create_time | datetime | null=YES | key= | def=CURRENT_TIMESTAMP - update_time | datetime | null=YES | key= | def=CURRENT_TIMESTAMP - create_by | bigint unsigned | null=YES | key= | def=null - ship_by | bigint unsigned | null=YES | key= | def=null - ship_time | datetime | null=YES | key= | def=null - sign_by | bigint unsigned | null=YES | key= | def=null - sign_time | datetime | null=YES | key= | def=null - sign_status | tinyint | null=YES | key= | def=0 - deleted | tinyint | null=NO | key= | def=0 - version | int | null=YES | key= | def=0 - update_by | bigint unsigned | null=YES | key= | def=null - -### sal_delivery_item -- ERROR: ER_NO_SUCH_TABLE Table 'vnerpdacahng.sal_delivery_item' doesn't exist - -### inv_outbound_order (22 cols) - id | bigint unsigned | null=NO | key=PRI | def=null - order_no | varchar(50) | null=NO | key=UNI | def=null - order_date | date | null=YES | key= | def=null - outbound_type | varchar(20) | null=YES | key= | def=sale - warehouse_id | bigint unsigned | null=YES | key=MUL | def=null - warehouse_code | varchar(50) | null=YES | key= | def=null - warehouse_name | varchar(100) | null=YES | key= | def=null - total_qty | decimal(18,4) | null=YES | key= | def=0.0000 - total_amount | decimal(18,4) | null=YES | key= | def=0.0000 - currency | varchar(10) | null=YES | key= | def=CNY - status | varchar(20) | null=YES | key=MUL | def=draft - remark | text | null=YES | key= | def=null - operator_name | varchar(50) | null=YES | key= | def=null - operator_id | bigint unsigned | null=YES | key=MUL | def=null - audit_status | varchar(20) | null=YES | key= | def=pending - auditor_name | varchar(50) | null=YES | key= | def=null - audit_time | datetime | null=YES | key= | def=null - create_by | bigint unsigned | null=YES | key= | def=null - create_time | datetime | null=YES | key= | def=CURRENT_TIMESTAMP - update_time | datetime | null=YES | key= | def=CURRENT_TIMESTAMP - deleted | tinyint | null=NO | key= | def=0 - version | int | null=YES | key= | def=0 - -### inv_outbound_item (13 cols) - id | bigint unsigned | null=NO | key=PRI | def=null - order_id | bigint unsigned | null=NO | key=MUL | def=null - material_id | bigint unsigned | null=NO | key=MUL | def=null - material_name | varchar(100) | null=YES | key= | def=null - material_spec | varchar(255) | null=YES | key= | def=null - quantity | decimal(18,4) | null=NO | key= | def=null - unit | varchar(20) | null=YES | key= | def=null - unit_price | decimal(18,4) | null=YES | key= | def=null - amount | decimal(18,4) | null=YES | key= | def=null - batch_no | varchar(50) | null=YES | key= | def=null - remark | varchar(255) | null=YES | key= | def=null - create_time | datetime | null=YES | key= | def=CURRENT_TIMESTAMP - deleted | tinyint | null=NO | key= | def=0 - -### sal_order (27 cols) - id | bigint unsigned | null=NO | key=PRI | def=null - order_no | varchar(50) | null=NO | key=UNI | def=null - order_date | date | null=YES | key= | def=null - customer_id | bigint unsigned | null=NO | key=MUL | def=null - contact_name | varchar(50) | null=YES | key= | def=null - contact_phone | varchar(20) | null=YES | key= | def=null - delivery_address | varchar(255) | null=YES | key= | def=null - salesman_id | bigint unsigned | null=YES | key=MUL | def=null - total_amount | decimal(18,4) | null=YES | key= | def=0.0000 - tax_amount | decimal(18,4) | null=YES | key= | def=0.0000 - total_with_tax | decimal(18,4) | null=YES | key= | def=0.0000 - discount_amount | decimal(18,4) | null=YES | key= | def=0.0000 - currency | varchar(10) | null=YES | key= | def=CNY - exchange_rate | decimal(18,4) | null=YES | key= | def=1.0000 - payment_terms | varchar(100) | null=YES | key= | def=null - delivery_date | date | null=YES | key= | def=null - contract_no | varchar(50) | null=YES | key= | def=null - status | tinyint | null=YES | key= | def=1 - remark | text | null=YES | key= | def=null - create_time | datetime | null=YES | key= | def=CURRENT_TIMESTAMP - update_time | datetime | null=YES | key= | def=CURRENT_TIMESTAMP - create_by | bigint unsigned | null=YES | key= | def=null - update_by | bigint unsigned | null=YES | key= | def=null - deleted | tinyint | null=NO | key= | def=0 - base_total_amount | decimal(18,4) | null=YES | key= | def=0.0000 - base_tax_amount | decimal(18,4) | null=YES | key= | def=0.0000 - base_grand_total | decimal(18,4) | null=YES | key= | def=0.0000 - -### sales_order -- ERROR: ER_NO_SUCH_TABLE Table 'vnerpdacahng.sales_order' doesn't exist - -### fin_payable (16 cols) - id | bigint unsigned | null=NO | key=PRI | def=null - payable_no | varchar(50) | null=NO | key=UNI | def=null - source_type | tinyint | null=YES | key= | def=1 - source_no | varchar(50) | null=YES | key=MUL | def=null - supplier_id | bigint unsigned | null=YES | key=MUL | def=null - amount | decimal(18,4) | null=YES | key= | def=0.0000 - paid_amount | decimal(18,4) | null=YES | key= | def=0.0000 - balance | decimal(18,4) | null=YES | key= | def=0.0000 - due_date | date | null=YES | key= | def=null - status | tinyint | null=YES | key=MUL | def=1 - remark | text | null=YES | key= | def=null - create_time | datetime | null=YES | key= | def=CURRENT_TIMESTAMP - update_time | datetime | null=YES | key= | def=CURRENT_TIMESTAMP - deleted | tinyint | null=YES | key= | def=0 - create_by | bigint unsigned | null=YES | key= | def=null - update_by | bigint unsigned | null=YES | key= | def=null - -### fin_payment_record (10 cols) - id | bigint unsigned | null=NO | key=PRI | def=null - payment_no | varchar(50) | null=NO | key= | def=null - payable_id | bigint unsigned | null=YES | key=MUL | def=null - supplier_id | bigint unsigned | null=YES | key=MUL | def=null - amount | decimal(18,4) | null=YES | key= | def=0.0000 - payment_method | varchar(20) | null=YES | key= | def=null - payment_date | date | null=YES | key= | def=null - remark | text | null=YES | key= | def=null - create_time | datetime | null=YES | key= | def=CURRENT_TIMESTAMP - deleted | tinyint | null=YES | key= | def=0 - -### fin_receivable (16 cols) - id | bigint unsigned | null=NO | key=PRI | def=null - receivable_no | varchar(50) | null=NO | key=UNI | def=null - source_type | tinyint | null=YES | key= | def=1 - source_no | varchar(50) | null=YES | key=MUL | def=null - customer_id | bigint unsigned | null=YES | key=MUL | def=null - amount | decimal(18,4) | null=YES | key= | def=0.0000 - received_amount | decimal(18,4) | null=YES | key= | def=0.0000 - balance | decimal(18,4) | null=YES | key= | def=0.0000 - due_date | date | null=YES | key= | def=null - status | tinyint | null=YES | key=MUL | def=1 - remark | text | null=YES | key= | def=null - create_time | datetime | null=YES | key= | def=CURRENT_TIMESTAMP - update_time | datetime | null=YES | key= | def=CURRENT_TIMESTAMP - deleted | tinyint | null=YES | key= | def=0 - create_by | bigint unsigned | null=YES | key= | def=null - update_by | bigint unsigned | null=YES | key= | def=null \ No newline at end of file diff --git a/_schema_extra.txt b/_schema_extra.txt deleted file mode 100644 index 407d43ff..00000000 --- a/_schema_extra.txt +++ /dev/null @@ -1,134 +0,0 @@ - -### inv_cutting_record (14 cols) - id | bigint unsigned | null=NO | key=PRI | def=null - record_no | varchar(50) | null=NO | key=UNI | def=null - source_label_id | bigint unsigned | null=NO | key=MUL | def=null - source_label_no | varchar(50) | null=NO | key= | def=null - cut_width_str | varchar(200) | null=YES | key= | def=null - original_width | decimal(18,2) | null=YES | key= | def=null - cut_total_width | decimal(18,2) | null=YES | key= | def=null - remain_width | decimal(18,2) | null=YES | key= | def=null - operator_id | bigint unsigned | null=YES | key= | def=null - operator_name | varchar(50) | null=YES | key= | def=null - cut_time | datetime | null=YES | key=MUL | def=CURRENT_TIMESTAMP - remark | varchar(500) | null=YES | key= | def=null - status | tinyint | null=YES | key= | def=1 - create_time | datetime | null=YES | key= | def=CURRENT_TIMESTAMP - -### inv_cutting_detail (7 cols) - id | bigint unsigned | null=NO | key=PRI | def=null - record_id | bigint unsigned | null=NO | key=MUL | def=null - new_label_id | bigint unsigned | null=NO | key=MUL | def=null - new_label_no | varchar(50) | null=NO | key= | def=null - cut_width | decimal(18,2) | null=YES | key= | def=null - sequence | int | null=YES | key= | def=0 - create_time | datetime | null=YES | key= | def=CURRENT_TIMESTAMP - -### sal_delivery_detail (16 cols) - id | bigint unsigned | null=NO | key=PRI | def=null - delivery_id | bigint unsigned | null=NO | key=MUL | def=null - line_no | int | null=YES | key= | def=null - material_id | bigint unsigned | null=NO | key=MUL | def=null - material_code | varchar(50) | null=YES | key= | def=null - material_name | varchar(200) | null=YES | key= | def=null - material_spec | varchar(100) | null=YES | key= | def=null - order_detail_id | bigint unsigned | null=YES | key=MUL | def=null - quantity | decimal(18,4) | null=NO | key= | def=null - unit | varchar(20) | null=YES | key= | def=null - unit_price | decimal(18,4) | null=YES | key= | def=null - amount | decimal(18,4) | null=YES | key= | def=null - batch_no | varchar(50) | null=YES | key= | def=null - remark | varchar(255) | null=YES | key= | def=null - create_time | datetime | null=YES | key= | def=CURRENT_TIMESTAMP - deleted | tinyint | null=NO | key= | def=0 - -### sal_order_detail (16 cols) - id | bigint unsigned | null=NO | key=PRI | def=null - order_id | bigint unsigned | null=NO | key=MUL | def=null - material_id | bigint unsigned | null=NO | key=MUL | def=null - material_name | varchar(100) | null=YES | key=MUL | def=null - quantity | decimal(18,4) | null=NO | key= | def=null - unit | varchar(20) | null=YES | key= | def=null - unit_price | decimal(18,4) | null=YES | key= | def=null - tax_rate | decimal(18,4) | null=YES | key= | def=0.0000 - amount | decimal(18,4) | null=YES | key= | def=null - tax_amount | decimal(18,4) | null=YES | key= | def=null - total_amount | decimal(18,4) | null=YES | key= | def=null - delivered_qty | decimal(18,4) | null=YES | key= | def=0.0000 - delivery_date | date | null=YES | key= | def=null - remark | varchar(255) | null=YES | key= | def=null - create_time | datetime | null=YES | key= | def=CURRENT_TIMESTAMP - deleted | tinyint | null=NO | key= | def=0 - -### eng_sample_to_mass (39 cols) - id | bigint unsigned | null=NO | key=PRI | def=null - sample_order_id | bigint unsigned | null=YES | key= | def=null - sample_order_no | varchar(50) | null=NO | key= | def=null - product_id | bigint unsigned | null=YES | key= | def=null - product_name | varchar(200) | null=YES | key= | def=null - customer_id | bigint unsigned | null=YES | key= | def=null - customer_name | varchar(100) | null=YES | key= | def=null - standard_card_id | bigint unsigned | null=YES | key= | def=null - standard_card_no | varchar(50) | null=YES | key= | def=null - process_card_id | bigint unsigned | null=YES | key= | def=null - process_card_no | varchar(50) | null=YES | key= | def=null - status | tinyint | null=YES | key= | def=1 - remark | text | null=YES | key= | def=null - create_time | datetime | null=YES | key= | def=CURRENT_TIMESTAMP - update_time | datetime | null=YES | key= | def=CURRENT_TIMESTAMP - deleted | tinyint | null=YES | key= | def=0 - transfer_no | varchar(50) | null=YES | key= | def=null - product_code | varchar(50) | null=YES | key= | def=null - sample_params | text | null=YES | key= | def=null - mass_params | text | null=YES | key= | def=null - sop_file | varchar(500) | null=YES | key= | def=null - bom_id | bigint unsigned | null=YES | key= | def=null - bom_version | varchar(20) | null=YES | key= | def=null - process_route | varchar(500) | null=YES | key= | def=null - check_standard | text | null=YES | key= | def=null - special_note | text | null=YES | key= | def=null - sample_confirmer | varchar(50) | null=YES | key= | def=null - sample_confirm_date | date | null=YES | key= | def=null - eng_confirmer | varchar(50) | null=YES | key= | def=null - eng_confirm_date | date | null=YES | key= | def=null - prod_confirmer | varchar(50) | null=YES | key= | def=null - prod_confirm_date | date | null=YES | key= | def=null - quality_confirmer | varchar(50) | null=YES | key= | def=null - quality_confirm_date | date | null=YES | key= | def=null - create_by | bigint unsigned | null=YES | key= | def=null - workorder_id | bigint unsigned | null=YES | key= | def=null - workorder_no | varchar(50) | null=YES | key= | def=null - conversion_date | date | null=YES | key= | def=null - approved_by | varchar(50) | null=YES | key= | def=null - -### qc_unqualified (26 cols) - id | bigint unsigned | null=NO | key=PRI | def=null - unqualified_no | varchar(50) | null=NO | key=UNI | def=null - handle_no | varchar(50) | null=YES | key=UNI | def=null - inspection_id | bigint unsigned | null=YES | key=MUL | def=null - source_type | varchar(50) | null=YES | key=MUL | def=null - source_no | varchar(50) | null=YES | key= | def=null - material_id | bigint unsigned | null=YES | key=MUL | def=null - material_code | varchar(50) | null=YES | key= | def=null - material_name | varchar(100) | null=YES | key= | def=null - quantity | decimal(18,4) | null=YES | key= | def=0.0000 - defect_type | varchar(50) | null=YES | key= | def=null - defect_desc | text | null=YES | key= | def=null - handle_type | tinyint | null=YES | key= | def=null - handle_status | tinyint | null=NO | key=MUL | def=1 - responsible_dept | varchar(100) | null=YES | key= | def=null - responsible_person | varchar(50) | null=YES | key= | def=null - handle_result | tinyint | null=YES | key= | def=null - cost_amount | decimal(18,4) | null=YES | key= | def=0.0000 - handler | varchar(50) | null=YES | key= | def=null - handle_date | date | null=YES | key= | def=null - remark | text | null=YES | key= | def=null - create_time | datetime | null=YES | key= | def=CURRENT_TIMESTAMP - update_time | datetime | null=YES | key= | def=CURRENT_TIMESTAMP - create_by | bigint unsigned | null=YES | key=MUL | def=null - update_by | bigint unsigned | null=YES | key=MUL | def=null - deleted | tinyint | null=NO | key= | def=0 - -### qc_inspection.inspection_type distinct values: [2,3] - -### inv_cutting_record sample: [{"id":1,"record_no":"SP-2026-101","source_label_id":1,"source_label_no":"B20260701-PVC","cut_width_str":"500+200","original_width":"1000.00","cut_total_width":"700.00","remain_width":"300.00","operator_id":1001,"operator_name":"张三","cut_time":"2026-07-06T02:00:00.000Z","remark":null,"status":1,"create_time":"2026-07-23T04:31:10.000Z"},{"id":2,"record_no":"SP-2026-102","source_label_id":2,"source_label_no":"B20260703-PE","cut_width_str":"600","original_width":"1000.00","cut_total_width":"600.00","remain_width":"400.00","operator_id":1001,"operator_name":"张三","cut_time":"2026-07-07T02:00:00.000Z","remark":null,"status":1,"create_time":"2026-07-23T04:31:10.000Z"}] \ No newline at end of file diff --git a/_tsc.txt b/_tsc.txt deleted file mode 100644 index 9a04a790..00000000 --- a/_tsc.txt +++ /dev/null @@ -1,4232 +0,0 @@ -error TS5033: Could not write file 'D:/dcprint/erp-project/tsconfig.tsbuildinfo': EPERM: operation not permitted, open 'D:\dcprint\erp-project\tsconfig.tsbuildinfo'. -playwright.e2e.config.ts(27,5): error TS2769: No overload matches this call. - The last overload gave the following error. - Object literal may only specify known properties, and 'outputDir' does not exist in type 'UseOptions, PlaywrightWorkerOptions & CustomProperties>'. -src/app/[locale]/business/contract-review/page.tsx(334,57): error TS2538: Type 'unknown' cannot be used as an index type. -src/app/[locale]/business/contract-review/page.tsx(340,51): error TS2538: Type 'unknown' cannot be used as an index type. -src/app/[locale]/business/contract-review/page.tsx(343,19): error TS2322: Type 'ContractReviewRecord[]' is not assignable to type 'Record[]'. - Type 'ContractReviewRecord' is not assignable to type 'Record'. - Index signature for type 'string' is missing in type 'ContractReviewRecord'. -src/app/[locale]/dcprint/ink/page.tsx(321,49): error TS2538: Type 'unknown' cannot be used as an index type. -src/app/[locale]/dcprint/ink/page.tsx(333,51): error TS2538: Type 'unknown' cannot be used as an index type. -src/app/[locale]/dcprint/ink/page.tsx(336,19): error TS2322: Type 'Item[]' is not assignable to type 'Record[]'. - Type 'Item' is not assignable to type 'Record'. - Index signature for type 'string' is missing in type 'Item'. -src/app/[locale]/finance/report/page.tsx(207,15): error TS2322: Type 'ReportItem[]' is not assignable to type 'Record[]'. - Type 'ReportItem' is not assignable to type 'Record'. - Index signature for type 'string' is missing in type 'ReportItem'. -src/app/[locale]/hr/employee/page.tsx(1176,32): error TS2538: Type 'unknown' cannot be used as an index type. -src/app/[locale]/hr/employee/page.tsx(1180,17): error TS2322: Type 'Employee[]' is not assignable to type 'Record[]'. - Type 'Employee' is not assignable to type 'Record'. - Index signature for type 'string' is missing in type 'Employee'. -src/app/[locale]/hr/salary/page.tsx(656,19): error TS2322: Type 'Salary[]' is not assignable to type 'Record[]'. - Type 'Salary' is not assignable to type 'Record'. - Index signature for type 'string' is missing in type 'Salary'. -src/app/[locale]/orders/customers/page.tsx(558,64): error TS2538: Type 'unknown' cannot be used as an index type. -src/app/[locale]/orders/customers/page.tsx(573,67): error TS2538: Type 'unknown' cannot be used as an index type. -src/app/[locale]/orders/customers/page.tsx(579,58): error TS2538: Type 'unknown' cannot be used as an index type. -src/app/[locale]/orders/customers/page.tsx(582,15): error TS2322: Type 'CustomerListItem[]' is not assignable to type 'Record[]'. - Type 'CustomerListItem' is not assignable to type 'Record'. - Index signature for type 'string' is missing in type 'CustomerListItem'. -src/app/[locale]/orders/sales/page.tsx(924,52): error TS2345: Argument of type 'unknown' is not assignable to parameter of type 'string | null | undefined'. -src/app/[locale]/orders/sales/page.tsx(930,52): error TS2345: Argument of type 'unknown' is not assignable to parameter of type 'string | null | undefined'. -src/app/[locale]/orders/sales/page.tsx(950,34): error TS2538: Type 'unknown' cannot be used as an index type. -src/app/[locale]/orders/sales/page.tsx(959,29): error TS2339: Property 'map' does not exist on type '{}'. -src/app/[locale]/orders/sales/page.tsx(963,19): error TS2322: Type 'Order[]' is not assignable to type 'Record[]'. - Type 'Order' is not assignable to type 'Record'. - Index signature for type 'string' is missing in type 'Order'. -src/app/[locale]/outsource/issue/page.tsx(275,24): error TS2339: Property 'map' does not exist on type '{}'. -src/app/[locale]/outsource/issue/page.tsx(288,47): error TS2538: Type 'unknown' cannot be used as an index type. -src/app/[locale]/outsource/issue/page.tsx(291,15): error TS2322: Type 'OutsourceIssue[]' is not assignable to type 'Record[]'. - Type 'OutsourceIssue' is not assignable to type 'Record'. - Index signature for type 'string' is missing in type 'OutsourceIssue'. -src/app/[locale]/prepress/die-template/page.tsx(918,41): error TS2538: Type 'unknown' cannot be used as an index type. -src/app/[locale]/prepress/die-template/page.tsx(918,69): error TS2538: Type 'unknown' cannot be used as an index type. -src/app/[locale]/prepress/die-template/page.tsx(938,66): error TS2345: Argument of type 'Record' is not assignable to parameter of type 'DieTemplate'. - Type 'Record' is missing the following properties from type 'DieApiResponse': id, templateCode, templateName, templateType, and 17 more. -src/app/[locale]/prepress/die-template/page.tsx(945,41): error TS2538: Type 'unknown' cannot be used as an index type. -src/app/[locale]/prepress/die-template/page.tsx(945,71): error TS2538: Type 'unknown' cannot be used as an index type. -src/app/[locale]/production/product-label/page.tsx(309,52): error TS2345: Argument of type 'unknown' is not assignable to parameter of type 'number'. -src/app/[locale]/production/product-label/page.tsx(312,15): error TS2322: Type 'Item[]' is not assignable to type 'Record[]'. - Type 'Item' is not assignable to type 'Record'. - Index signature for type 'string' is missing in type 'Item'. -src/app/[locale]/production/report/page.tsx(550,17): error TS2322: Type 'WorkReport[]' is not assignable to type 'Record[]'. - Type 'WorkReport' is not assignable to type 'Record'. - Index signature for type 'string' is missing in type 'WorkReport'. -src/app/[locale]/purchase/orders/page.tsx(814,52): error TS2345: Argument of type 'unknown' is not assignable to parameter of type 'string | null | undefined'. -src/app/[locale]/purchase/orders/page.tsx(820,52): error TS2345: Argument of type 'unknown' is not assignable to parameter of type 'string | null | undefined'. -src/app/[locale]/purchase/orders/page.tsx(843,34): error TS2538: Type 'unknown' cannot be used as an index type. -src/app/[locale]/purchase/request/page.tsx(485,50): error TS2345: Argument of type 'unknown' is not assignable to parameter of type 'string'. -src/app/[locale]/purchase/request/page.tsx(494,53): error TS2538: Type 'unknown' cannot be used as an index type. -src/app/[locale]/purchase/request/page.tsx(500,57): error TS2345: Argument of type 'unknown' is not assignable to parameter of type 'number'. -src/app/[locale]/purchase/request/page.tsx(506,51): error TS2538: Type 'unknown' cannot be used as an index type. -src/app/[locale]/purchase/request/page.tsx(509,17): error TS2322: Type 'PurchaseRequest[]' is not assignable to type 'Record[]'. - Type 'PurchaseRequest' is not assignable to type 'Record'. - Index signature for type 'string' is missing in type 'PurchaseRequest'. -src/app/[locale]/purchase/suppliers/page.tsx(506,60): error TS2538: Type 'unknown' cannot be used as an index type. -src/app/[locale]/purchase/suppliers/page.tsx(513,52): error TS2538: Type 'unknown' cannot be used as an index type. -src/app/[locale]/purchase/suppliers/page.tsx(520,19): error TS2322: Type 'Supplier[]' is not assignable to type 'Record[]'. - Type 'Supplier' is not assignable to type 'Record'. - Index signature for type 'string' is missing in type 'Supplier'. -src/app/[locale]/qrcode/page.tsx(289,15): error TS2322: Type 'QRRecord[]' is not assignable to type 'Record[]'. - Type 'QRRecord' is not assignable to type 'Record'. - Index signature for type 'string' is missing in type 'QRRecord'. -src/app/[locale]/quality/complaint/page.tsx(281,55): error TS2538: Type 'unknown' cannot be used as an index type. -src/app/[locale]/quality/complaint/page.tsx(288,51): error TS2538: Type 'unknown' cannot be used as an index type. -src/app/[locale]/quality/complaint/page.tsx(291,17): error TS2322: Type 'ComplaintRecord[]' is not assignable to type 'Record[]'. - Type 'ComplaintRecord' is not assignable to type 'Record'. - Index signature for type 'string' is missing in type 'ComplaintRecord'. -src/app/[locale]/quality/final/page.tsx(518,34): error TS2538: Type 'unknown' cannot be used as an index type. -src/app/[locale]/quality/final/page.tsx(522,19): error TS2322: Type 'FinalInspect[]' is not assignable to type 'Record[]'. - Type 'FinalInspect' is not assignable to type 'Record'. - Index signature for type 'string' is missing in type 'FinalInspect'. -src/app/[locale]/quality/incoming/page.tsx(698,50): error TS2538: Type 'unknown' cannot be used as an index type. -src/app/[locale]/quality/lab-test/page.tsx(213,17): error TS2322: Type 'LabTestRecord[]' is not assignable to type 'Record[]'. - Type 'LabTestRecord' is not assignable to type 'Record'. - Index signature for type 'string' is missing in type 'LabTestRecord'. -src/app/[locale]/quality/process/page.tsx(791,34): error TS2538: Type 'unknown' cannot be used as an index type. -src/app/[locale]/quality/process/page.tsx(795,19): error TS2322: Type 'QualityProcess[]' is not assignable to type 'Record[]'. - Type 'QualityProcess' is not assignable to type 'Record'. - Index signature for type 'string' is missing in type 'QualityProcess'. -src/app/[locale]/quality/sgs/page.tsx(465,15): error TS2322: Type 'Cert[]' is not assignable to type 'Record[]'. - Type 'Cert' is not assignable to type 'Record'. - Index signature for type 'string' is missing in type 'Cert'. -src/app/[locale]/quality/supplier-audit/page.tsx(230,17): error TS2322: Type 'SupplierAuditRecord[]' is not assignable to type 'Record[]'. - Type 'SupplierAuditRecord' is not assignable to type 'Record'. - Index signature for type 'string' is missing in type 'SupplierAuditRecord'. -src/app/[locale]/quality/trace/page.tsx(518,19): error TS2322: Type 'TraceRecord[]' is not assignable to type 'Record[]'. - Type 'TraceRecord' is not assignable to type 'Record'. - Index signature for type 'string' is missing in type 'TraceRecord'. -src/app/[locale]/quality/unqualified/page.tsx(294,52): error TS2538: Type '{}' cannot be used as an index type. -src/app/[locale]/quality/unqualified/page.tsx(302,49): error TS2538: Type 'unknown' cannot be used as an index type. -src/app/[locale]/quality/unqualified/page.tsx(305,15): error TS2322: Type 'Item[]' is not assignable to type 'Record[]'. - Type 'Item' is not assignable to type 'Record'. - Index signature for type 'string' is missing in type 'Item'. -src/app/[locale]/sample/management/page.tsx(381,52): error TS2345: Argument of type 'unknown' is not assignable to parameter of type 'string | Date | null | undefined'. -src/app/[locale]/sample/management/page.tsx(387,52): error TS2345: Argument of type 'unknown' is not assignable to parameter of type 'string | Date | null | undefined'. -src/app/[locale]/sample/management/page.tsx(393,58): error TS2538: Type 'unknown' cannot be used as an index type. -src/app/[locale]/sample/management/page.tsx(396,19): error TS2322: Type 'SampleOrder[]' is not assignable to type 'Record[]'. - Type 'SampleOrder' is not assignable to type 'Record'. - Index signature for type 'string' is missing in type 'SampleOrder'. -src/app/[locale]/sample/orders/page.tsx(580,52): error TS2345: Argument of type 'unknown' is not assignable to parameter of type 'string | Date | null | undefined'. -src/app/[locale]/sample/orders/page.tsx(592,52): error TS2345: Argument of type 'unknown' is not assignable to parameter of type 'string | Date | null | undefined'. -src/app/[locale]/sample/orders/page.tsx(598,62): error TS2538: Type 'unknown' cannot be used as an index type. -src/app/[locale]/sample/orders/page.tsx(601,19): error TS2322: Type 'SampleOrder[]' is not assignable to type 'Record[]'. - Type 'SampleOrder' is not assignable to type 'Record'. - Index signature for type 'string' is missing in type 'SampleOrder'. -src/app/[locale]/srm/evaluation/page.tsx(544,47): error TS2538: Type 'unknown' cannot be used as an index type. -src/app/[locale]/srm/evaluation/page.tsx(555,46): error TS2538: Type 'unknown' cannot be used as an index type. -src/app/[locale]/srm/evaluation/page.tsx(561,51): error TS2538: Type 'unknown' cannot be used as an index type. -src/app/[locale]/srm/evaluation/page.tsx(564,15): error TS2322: Type 'EvalRecord[]' is not assignable to type 'Record[]'. - Type 'EvalRecord' is not assignable to type 'Record'. - Index signature for type 'string' is missing in type 'EvalRecord'. -src/app/[locale]/warehouse/inbound/cutting/page.tsx(245,50): error TS2769: No overload matches this call. - Overload 1 of 4, '(value: string | number | Date): Date', gave the following error. - Argument of type 'unknown' is not assignable to parameter of type 'string | number | Date'. - Overload 2 of 4, '(value: string | number): Date', gave the following error. - Argument of type 'unknown' is not assignable to parameter of type 'string | number'. -src/app/[locale]/warehouse/inbound/cutting/page.tsx(259,19): error TS2322: Type 'CuttingRecord[]' is not assignable to type 'Record[]'. - Type 'CuttingRecord' is not assignable to type 'Record'. - Index signature for type 'string' is missing in type 'CuttingRecord'. -src/app/[locale]/warehouse/outbound/page.tsx(149,57): error TS2571: Object is of type 'unknown'. -src/app/[locale]/warehouse/outbound/page.tsx(159,5): error TS2322: Type 'unknown' is not assignable to type 'string | undefined'. -src/app/[locale]/warehouse/outbound/page.tsx(160,5): error TS2322: Type 'unknown' is not assignable to type 'string'. -src/app/[locale]/warehouse/outbound/page.tsx(165,5): error TS2322: Type 'unknown' is not assignable to type 'number | undefined'. -src/app/[locale]/warehouse/outbound/page.tsx(166,5): error TS2322: Type 'unknown' is not assignable to type 'string | undefined'. -src/app/[locale]/warehouse/outbound/page.tsx(167,5): error TS2322: Type 'unknown' is not assignable to type 'number | undefined'. -src/app/[locale]/warehouse/outbound/page.tsx(168,5): error TS2322: Type 'unknown' is not assignable to type 'string | undefined'. -src/app/[locale]/warehouse/outbound/page.tsx(169,5): error TS2322: Type 'unknown' is not assignable to type 'string'. -src/app/[locale]/warehouse/outbound/page.tsx(172,21): error TS2538: Type 'unknown' cannot be used as an index type. -src/app/[locale]/warehouse/outbound/page.tsx(173,5): error TS2322: Type 'unknown' is not assignable to type 'string'. -src/app/[locale]/warehouse/outbound/page.tsx(174,5): error TS2322: Type 'unknown' is not assignable to type 'string'. -src/app/[locale]/warehouse/outbound/page.tsx(176,5): error TS2322: Type 'unknown' is not assignable to type 'string'. -src/app/[locale]/warehouse/outbound/page.tsx(178,5): error TS2322: Type 'unknown' is not assignable to type 'number | undefined'. -src/app/[locale]/warehouse/outbound/page.tsx(180,5): error TS2322: Type 'unknown' is not assignable to type 'string'. -src/app/[locale]/warehouse/outbound/page.tsx(733,17): error TS2345: Argument of type '(prev: { materialCode: string; materialName: string; specification: string; quantity: string; unit: string; warehouse: string; remark: string; outboundType: string; isRawMaterial: boolean; batchNo: string; width: string; }) => { ...; }' is not assignable to parameter of type 'SetStateAction<{ materialCode: string; materialName: string; specification: string; quantity: string; unit: string; warehouse: string; remark: string; outboundType: string; isRawMaterial: boolean; batchNo: string; width: string; }>'. - Type '(prev: { materialCode: string; materialName: string; specification: string; quantity: string; unit: string; warehouse: string; remark: string; outboundType: string; isRawMaterial: boolean; batchNo: string; width: string; }) => { ...; }' is not assignable to type '(prevState: { materialCode: string; materialName: string; specification: string; quantity: string; unit: string; warehouse: string; remark: string; outboundType: string; isRawMaterial: boolean; batchNo: string; width: string; }) => { ...; }'. - Call signature return types '{ materialCode: string; materialName: {}; specification: {}; batchNo: {}; unit: {}; quantity: string; warehouse: string; remark: string; outboundType: string; isRawMaterial: boolean; width: string; }' and '{ materialCode: string; materialName: string; specification: string; quantity: string; unit: string; warehouse: string; remark: string; outboundType: string; isRawMaterial: boolean; batchNo: string; width: string; }' are incompatible. - The types of 'materialName' are incompatible between these types. - Type '{}' is not assignable to type 'string'. -src/app/[locale]/warehouse/outbound/page.tsx(1310,13): error TS2322: Type 'unknown' is not assignable to type 'ReactNode'. -src/app/[locale]/warehouse/outbound/page.tsx(1312,17): error TS2322: Type '{}' is not assignable to type 'ReactNode'. -src/app/[locale]/warehouse/outbound/page.tsx(1320,26): error TS2322: Type 'unknown' is not assignable to type 'ReactNode'. -src/app/[locale]/warehouse/outbound/page.tsx(1321,22): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type '{}'. - Property '0' does not exist on type '{}'. -src/app/[locale]/warehouse/outbound/page.tsx(1321,57): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type '{}'. - Property '0' does not exist on type '{}'. -src/app/[locale]/warehouse/outbound/page.tsx(1325,40): error TS2339: Property 'materialName' does not exist on type '{}'. -src/app/[locale]/warehouse/outbound/page.tsx(1326,40): error TS2339: Property 'specification' does not exist on type '{}'. -src/app/[locale]/warehouse/outbound/page.tsx(1326,79): error TS2339: Property 'specification' does not exist on type '{}'. -src/app/[locale]/warehouse/outbound/page.tsx(1328,57): error TS2339: Property 'length' does not exist on type '{}'. -src/app/[locale]/warehouse/outbound/page.tsx(1339,44): error TS2339: Property 'map' does not exist on type '{}'. -src/app/[locale]/warehouse/outbound/page.tsx(1494,51): error TS2322: Type '{}' is not assignable to type 'ReactNode'. -src/app/[locale]/warehouse/outbound/page.tsx(1498,27): error TS2322: Type '{}' is not assignable to type 'ReactNode'. -src/app/[locale]/warehouse/outbound/page.tsx(1502,27): error TS2322: Type '{}' is not assignable to type 'ReactNode'. -src/app/[locale]/warehouse/outbound/page.tsx(1506,27): error TS2322: Type '{}' is not assignable to type 'ReactNode'. -src/app/[locale]/warehouse/outbound/page.tsx(1510,27): error TS2322: Type '{}' is not assignable to type 'ReactNode'. -src/app/[locale]/warehouse/outbound/page.tsx(1531,41): error TS2322: Type '{}' is not assignable to type 'ReactNode'. -src/app/[locale]/warehouse/outbound/page.tsx(1610,13): error TS2322: Type 'unknown' is not assignable to type 'ReactNode'. -src/app/[locale]/warehouse/outbound/page.tsx(1612,17): error TS2322: Type '{}' is not assignable to type 'ReactNode'. -src/app/[locale]/warehouse/outbound/page.tsx(1620,26): error TS2322: Type 'unknown' is not assignable to type 'ReactNode'. -src/app/[locale]/warehouse/outbound/page.tsx(1621,22): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type '{}'. - Property '0' does not exist on type '{}'. -src/app/[locale]/warehouse/outbound/page.tsx(1621,57): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type '{}'. - Property '0' does not exist on type '{}'. -src/app/[locale]/warehouse/outbound/page.tsx(1625,40): error TS2339: Property 'materialName' does not exist on type '{}'. -src/app/[locale]/warehouse/outbound/page.tsx(1626,40): error TS2339: Property 'specification' does not exist on type '{}'. -src/app/[locale]/warehouse/outbound/page.tsx(1626,79): error TS2339: Property 'specification' does not exist on type '{}'. -src/app/[locale]/warehouse/outbound/page.tsx(1628,57): error TS2339: Property 'length' does not exist on type '{}'. -src/app/[locale]/warehouse/outbound/page.tsx(1639,44): error TS2339: Property 'map' does not exist on type '{}'. -src/app/[locale]/warehouse/outbound/page.tsx(1777,45): error TS2322: Type 'unknown' is not assignable to type 'string | number | Date'. -src/app/[locale]/warehouse/outbound/page.tsx(1802,49): error TS2322: Type 'unknown' is not assignable to type 'string | number | Date'. -src/app/[locale]/warehouse/outbound/page.tsx(1803,49): error TS2322: Type 'unknown' is not assignable to type 'string | number | Date'. -src/app/[locale]/warehouse/outbound/page.tsx(1834,17): error TS2322: Type 'unknown' is not assignable to type 'string | number | Date'. -src/app/[locale]/warehouse/outbound/page.tsx(1835,17): error TS2322: Type 'unknown' is not assignable to type 'string | number | Date'. -src/app/[locale]/warehouse/outbound/page.tsx(1836,17): error TS2322: Type 'unknown' is not assignable to type 'string | number | Date'. -src/app/[locale]/warehouse/outbound/page.tsx(1853,21): error TS2322: Type 'unknown' is not assignable to type 'ReactNode'. -src/app/[locale]/warehouse/outbound/page.tsx(1859,54): error TS2339: Property 'toFixed' does not exist on type '{}'. -src/app/[locale]/warehouse/outbound/page.tsx(1863,60): error TS18046: 'fifoAllocation.shortage' is of type 'unknown'. -src/app/[locale]/warehouse/outbound/page.tsx(1866,43): error TS18046: 'fifoAllocation.shortage' is of type 'unknown'. -src/app/[locale]/warehouse/outbound/page.tsx(1868,22): error TS18046: 'fifoAllocation.shortage' is of type 'unknown'. -src/app/[locale]/warehouse/outbound/page.tsx(1871,53): error TS18046: 'fifoAllocation.shortage' is of type 'unknown'. -src/app/[locale]/warehouse/outbound/page.tsx(1873,22): error TS18046: 'fifoAllocation.shortage' is of type 'unknown'. -src/app/[locale]/warehouse/outbound/page.tsx(1874,25): error TS18046: 'fifoAllocation.shortage' is of type 'unknown'. -src/app/[locale]/warehouse/outbound/page.tsx(1880,15): error TS2322: Type 'unknown' is not assignable to type 'ReactNode'. -src/app/[locale]/warehouse/outbound/page.tsx(1880,16): error TS18046: 'fifoAllocation.shortage' is of type 'unknown'. -src/app/[locale]/warehouse/outbound/page.tsx(1884,57): error TS18046: 'fifoAllocation.shortage' is of type 'unknown'. -src/app/[locale]/warehouse/outbound/page.tsx(1889,15): error TS2322: Type 'unknown' is not assignable to type 'ReactNode'. -src/app/[locale]/warehouse/outbound/page.tsx(1890,81): error TS2339: Property 'length' does not exist on type '{}'. -src/app/[locale]/warehouse/outbound/page.tsx(1908,57): error TS2339: Property 'map' does not exist on type '{}'. -src/app/[locale]/warehouse/outbound/page.tsx(1930,40): error TS2339: Property 'length' does not exist on type '{}'. -src/app/[locale]/warehouse/outbound/page.tsx(1931,50): error TS2339: Property 'length' does not exist on type '{}'. -src/app/[locale]/warehouse/outbound/page.tsx(1949,51): error TS2339: Property 'map' does not exist on type '{}'. -src/app/[locale]/warehouse/outbound/page.tsx(1974,67): error TS2339: Property 'length' does not exist on type '{}'. -src/app/[locale]/warehouse/outbound/page.tsx(1976,50): error TS2339: Property 'length' does not exist on type '{}'. -src/app/[locale]/warehouse/outbound/page.tsx(1994,13): error TS2322: Type 'unknown' is not assignable to type 'ReactNode'. -src/app/[locale]/warehouse/stock-adjust/page.tsx(210,45): error TS2538: Type 'unknown' cannot be used as an index type. -src/app/[locale]/warehouse/stock-adjust/page.tsx(222,47): error TS2538: Type 'unknown' cannot be used as an index type. -src/app/[locale]/warehouse/stock-adjust/page.tsx(225,15): error TS2322: Type 'Item[]' is not assignable to type 'Record[]'. - Type 'Item' is not assignable to type 'Record'. - Index signature for type 'string' is missing in type 'Item'. -src/app/[locale]/warehouse/stocktaking/page.tsx(303,89): error TS2538: Type 'unknown' cannot be used as an index type. -src/app/[locale]/warehouse/stocktaking/page.tsx(316,48): error TS2538: Type 'unknown' cannot be used as an index type. -src/app/[locale]/warehouse/stocktaking/page.tsx(319,15): error TS2322: Type 'InventoryCheck[]' is not assignable to type 'Record[]'. - Type 'InventoryCheck' is not assignable to type 'Record'. - Index signature for type 'string' is missing in type 'InventoryCheck'. -src/app/api/auth/menus/route.ts(71,5): error TS2345: Argument of type 'unknown[]' is not assignable to parameter of type 'SqlValue[]'. - Type 'unknown' is not assignable to type 'SqlValue'. -src/app/api/auth/menus/route.ts(74,34): error TS2345: Argument of type 'DbRow[]' is not assignable to parameter of type 'MenuRow[]'. - Type 'DbRow' is missing the following properties from type 'MenuRow': id, menu_name, menu_code, menu_type, and 7 more. -src/app/api/auth/menus/route.ts(76,42): error TS2345: Argument of type 'DbRow[]' is not assignable to parameter of type 'MenuRow[]'. - Type 'DbRow' is missing the following properties from type 'MenuRow': id, menu_name, menu_code, menu_type, and 7 more. -src/app/api/auth/register/route.ts(186,32): error TS2339: Property 'insertId' does not exist on type 'QueryResult'. - Property 'insertId' does not exist on type 'ResultSetHeader[]'. -src/app/api/auth/register/route.ts(200,25): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/auth/register/route.ts(203,13): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/biz/contract-review/route.ts(53,24): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/biz/contract-review/route.ts(53,38): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/biz/contract-review/route.ts(70,33): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/biz/contract-review/route.ts(70,46): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/biz/contract-review/route.ts(85,31): error TS2339: Property 'insertId' does not exist on type 'QueryResult'. - Property 'insertId' does not exist on type 'ResultSetHeader[]'. -src/app/api/biz/contract-review/route.ts(110,20): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/biz/contract-review/route.ts(114,20): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/biz/contract-review/route.ts(132,23): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/biz/contract-review/route.ts(133,17): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/customers/route.ts(97,27): error TS2322: Type 'SqlValue[]' is not assignable to type 'DbRow[]'. - Type 'SqlValue' is not assignable to type 'DbRow'. - Type 'undefined' is not assignable to type 'DbRow'. -src/app/api/customers/route.ts(141,64): error TS2345: Argument of type 'DbRow[]' is not assignable to parameter of type 'number | SqlValue[]'. - Type 'DbRow[]' is not assignable to type 'SqlValue[]'. - Type 'DbRow' is not assignable to type 'SqlValue'. - Type 'DbRow' is missing the following properties from type 'Buffer': slice, subarray, write, toJSON, and 102 more. -src/app/api/dashboard/ceo/route.ts(26,51): error TS18046: 'overview' is of type 'unknown'. -src/app/api/dashboard/ceo/route.ts(37,51): error TS18046: 'overview' is of type 'unknown'. -src/app/api/dashboard/ceo/route.ts(49,9): error TS18046: 'overview' is of type 'unknown'. -src/app/api/dashboard/ceo/route.ts(61,9): error TS18046: 'overview' is of type 'unknown'. -src/app/api/dashboard/ceo/route.ts(76,9): error TS18046: 'overview' is of type 'unknown'. -src/app/api/dashboard/ceo/route.ts(99,9): error TS18046: 'production' is of type 'unknown'. -src/app/api/dashboard/ceo/route.ts(100,9): error TS18046: 'production' is of type 'unknown'. -src/app/api/dashboard/ceo/route.ts(114,7): error TS18046: 'production' is of type 'unknown'. -src/app/api/dashboard/ceo/route.ts(134,7): error TS18046: 'production' is of type 'unknown'. -src/app/api/dashboard/ceo/route.ts(148,23): error TS18046: 'production' is of type 'unknown'. -src/app/api/dashboard/ceo/route.ts(151,7): error TS18046: 'production' is of type 'unknown'. -src/app/api/dashboard/ceo/route.ts(152,9): error TS18046: 'production' is of type 'unknown'. -src/app/api/dashboard/ceo/route.ts(153,35): error TS18046: 'production' is of type 'unknown'. -src/app/api/dashboard/ceo/route.ts(167,9): error TS18046: 'production' is of type 'unknown'. -src/app/api/dashboard/ceo/route.ts(184,9): error TS18046: 'quality' is of type 'unknown'. -src/app/api/dashboard/ceo/route.ts(200,9): error TS18046: 'quality' is of type 'unknown'. -src/app/api/dashboard/ceo/route.ts(201,9): error TS18046: 'quality' is of type 'unknown'. -src/app/api/dashboard/ceo/route.ts(202,9): error TS18046: 'quality' is of type 'unknown'. -src/app/api/dashboard/ceo/route.ts(203,9): error TS18046: 'quality' is of type 'unknown'. -src/app/api/dashboard/ceo/route.ts(204,11): error TS18046: 'quality' is of type 'unknown'. -src/app/api/dashboard/ceo/route.ts(205,27): error TS18046: 'quality' is of type 'unknown'. -src/app/api/dashboard/ceo/route.ts(205,55): error TS18046: 'quality' is of type 'unknown'. -src/app/api/dashboard/ceo/route.ts(230,9): error TS18046: 'finance' is of type 'unknown'. -src/app/api/dashboard/ceo/route.ts(232,9): error TS18046: 'finance' is of type 'unknown'. -src/app/api/dashboard/ceo/route.ts(247,9): error TS18046: 'finance' is of type 'unknown'. -src/app/api/dashboard/ceo/route.ts(249,9): error TS18046: 'finance' is of type 'unknown'. -src/app/api/dashboard/ceo/route.ts(271,9): error TS18046: 'inventory' is of type 'unknown'. -src/app/api/dashboard/ceo/route.ts(272,9): error TS18046: 'inventory' is of type 'unknown'. -src/app/api/dashboard/ceo/route.ts(291,51): error TS18046: 'inventory' is of type 'unknown'. -src/app/api/dashboard/ceo/route.ts(303,9): error TS18046: 'inventory' is of type 'unknown'. -src/app/api/dashboard/ceo/route.ts(346,7): error TS2322: Type '{ name: {}; total: number; completed: number; }[]' is not assignable to type 'SqlValue[]'. - Type '{ name: {}; total: number; completed: number; }' is not assignable to type 'SqlValue'. -src/app/api/dashboard/ceo/route.ts(366,7): error TS2322: Type '{ name: {}; qty: number; }[]' is not assignable to type 'SqlValue[]'. - Type '{ name: {}; qty: number; }' is not assignable to type 'SqlValue'. -src/app/api/dashboard/ceo/route.ts(385,7): error TS2322: Type '{ name: {}; qty: number; }[]' is not assignable to type 'SqlValue[]'. - Type '{ name: {}; qty: number; }' is not assignable to type 'SqlValue'. -src/app/api/dashboard/ceo/route.ts(404,7): error TS2322: Type '{ year: number; total: number; }[]' is not assignable to type 'SqlValue[]'. - Type '{ year: number; total: number; }' is not assignable to type 'SqlValue'. -src/app/api/dashboard/ceo/route.ts(432,9): error TS18046: 'shiftData' is of type 'unknown'. -src/app/api/dashboard/ceo/route.ts(433,9): error TS18046: 'shiftData' is of type 'unknown'. -src/app/api/dashboard/ceo/route.ts(434,9): error TS18046: 'shiftData' is of type 'unknown'. -src/app/api/dashboard/ceo/route.ts(435,11): error TS18046: 'shiftData' is of type 'unknown'. -src/app/api/dashboard/ceo/route.ts(436,27): error TS18046: 'shiftData' is of type 'unknown'. -src/app/api/dashboard/ceo/route.ts(436,55): error TS18046: 'shiftData' is of type 'unknown'. -src/app/api/dashboard/ceo/route.ts(440,9): error TS18046: 'shiftData' is of type 'unknown'. -src/app/api/dashboard/ceo/route.ts(441,9): error TS18046: 'shiftData' is of type 'unknown'. -src/app/api/dashboard/ceo/route.ts(442,9): error TS18046: 'shiftData' is of type 'unknown'. -src/app/api/dashboard/ceo/route.ts(443,11): error TS18046: 'shiftData' is of type 'unknown'. -src/app/api/dashboard/ceo/route.ts(444,27): error TS18046: 'shiftData' is of type 'unknown'. -src/app/api/dashboard/ceo/route.ts(444,58): error TS18046: 'shiftData' is of type 'unknown'. -src/app/api/dashboard/ceo/route.ts(448,9): error TS18046: 'shiftData' is of type 'unknown'. -src/app/api/dashboard/ceo/route.ts(449,9): error TS18046: 'shiftData' is of type 'unknown'. -src/app/api/dashboard/ceo/route.ts(450,9): error TS18046: 'shiftData' is of type 'unknown'. -src/app/api/dashboard/ceo/route.ts(451,11): error TS18046: 'shiftData' is of type 'unknown'. -src/app/api/dashboard/ceo/route.ts(452,27): error TS18046: 'shiftData' is of type 'unknown'. -src/app/api/dashboard/ceo/route.ts(452,57): error TS18046: 'shiftData' is of type 'unknown'. -src/app/api/dashboard/ceo/route.ts(472,7): error TS2322: Type 'unknown[]' is not assignable to type 'SqlValue[]'. - Type 'unknown' is not assignable to type 'SqlValue'. -src/app/api/dashboard/finance/route.ts(32,9): error TS18046: 'overview' is of type 'unknown'. -src/app/api/dashboard/finance/route.ts(34,9): error TS18046: 'overview' is of type 'unknown'. -src/app/api/dashboard/finance/route.ts(35,7): error TS18046: 'overview' is of type 'unknown'. -src/app/api/dashboard/finance/route.ts(35,28): error TS18046: 'overview' is of type 'unknown'. -src/app/api/dashboard/finance/route.ts(35,55): error TS18046: 'overview' is of type 'unknown'. -src/app/api/dashboard/finance/route.ts(50,9): error TS18046: 'overview' is of type 'unknown'. -src/app/api/dashboard/finance/route.ts(52,9): error TS18046: 'overview' is of type 'unknown'. -src/app/api/dashboard/finance/route.ts(119,48): error TS2769: No overload matches this call. - Overload 1 of 4, '(value: string | number | Date): Date', gave the following error. - Argument of type 'unknown' is not assignable to parameter of type 'string | number | Date'. - Overload 2 of 4, '(value: string | number): Date', gave the following error. - Argument of type 'unknown' is not assignable to parameter of type 'string | number'. -src/app/api/dashboard/finance/route.ts(119,77): error TS2769: No overload matches this call. - Overload 1 of 4, '(value: string | number | Date): Date', gave the following error. - Argument of type 'unknown' is not assignable to parameter of type 'string | number | Date'. - Overload 2 of 4, '(value: string | number): Date', gave the following error. - Argument of type 'unknown' is not assignable to parameter of type 'string | number'. -src/app/api/dashboard/kpi/route.ts(54,7): error TS18046: 'otd' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(55,7): error TS18046: 'otd' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(56,7): error TS18046: 'otd' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(56,67): error TS18046: 'otd' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(57,7): error TS18046: 'otd' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(58,9): error TS18046: 'otd' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(58,43): error TS18046: 'otd' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(58,62): error TS18046: 'otd' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(93,5): error TS18046: 'inventoryTurnover' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(94,5): error TS18046: 'inventoryTurnover' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(95,5): error TS18046: 'inventoryTurnover' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(96,7): error TS18046: 'inventoryTurnover' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(97,23): error TS18046: 'inventoryTurnover' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(97,55): error TS18046: 'inventoryTurnover' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(100,5): error TS18046: 'inventoryTurnover' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(101,7): error TS18046: 'inventoryTurnover' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(101,53): error TS18046: 'inventoryTurnover' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(133,7): error TS18046: 'oee' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(144,7): error TS18046: 'oee' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(148,7): error TS18046: 'oee' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(152,7): error TS18046: 'oee' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(156,7): error TS18046: 'oee' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(157,22): error TS18046: 'oee' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(157,41): error TS18046: 'oee' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(157,59): error TS18046: 'oee' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(193,5): error TS18046: 'qualityRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(194,5): error TS18046: 'qualityRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(195,5): error TS18046: 'qualityRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(196,7): error TS18046: 'qualityRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(197,23): error TS18046: 'qualityRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(197,52): error TS18046: 'qualityRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(216,5): error TS18046: 'qualityRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(217,5): error TS18046: 'qualityRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(218,5): error TS18046: 'qualityRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(219,7): error TS18046: 'qualityRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(220,23): error TS18046: 'qualityRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(220,51): error TS18046: 'qualityRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(239,5): error TS18046: 'qualityRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(240,5): error TS18046: 'qualityRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(241,5): error TS18046: 'qualityRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(242,7): error TS18046: 'qualityRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(243,23): error TS18046: 'qualityRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(243,49): error TS18046: 'qualityRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(252,5): error TS18046: 'qualityRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(252,33): error TS18046: 'qualityRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(252,60): error TS18046: 'qualityRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(254,5): error TS18046: 'qualityRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(254,34): error TS18046: 'qualityRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(254,62): error TS18046: 'qualityRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(255,3): error TS18046: 'qualityRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(279,7): error TS2322: Type '{ id: unknown; name: unknown; qualityScore: number; deliveryScore: number; priceScore: number; overallScore: number; }[]' is not assignable to type 'SqlValue[]'. - Type '{ id: unknown; name: unknown; qualityScore: number; deliveryScore: number; priceScore: number; overallScore: number; }' is not assignable to type 'SqlValue'. -src/app/api/dashboard/kpi/route.ts(322,7): error TS2322: Type '{ id: unknown; name: unknown; creditLimit: number; creditUsed: number; usageRate: number; }[]' is not assignable to type 'SqlValue[]'. - Type '{ id: unknown; name: unknown; creditLimit: number; creditUsed: number; usageRate: number; }' is not assignable to type 'SqlValue'. -src/app/api/dashboard/kpi/route.ts(369,5): error TS18046: 'fifoCompliance' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(370,5): error TS18046: 'fifoCompliance' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(371,5): error TS18046: 'fifoCompliance' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(372,5): error TS18046: 'fifoCompliance' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(372,35): error TS18046: 'fifoCompliance' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(372,66): error TS18046: 'fifoCompliance' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(373,5): error TS18046: 'fifoCompliance' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(374,7): error TS18046: 'fifoCompliance' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(375,23): error TS18046: 'fifoCompliance' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(375,53): error TS18046: 'fifoCompliance' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(405,5): error TS18046: 'departmentEfficiency' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(406,5): error TS18046: 'departmentEfficiency' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(426,5): error TS18046: 'departmentEfficiency' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(427,5): error TS18046: 'departmentEfficiency' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(459,5): error TS18046: 'inkConsumptionRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(460,5): error TS18046: 'inkConsumptionRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(461,9): error TS18046: 'inkConsumptionRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(462,7): error TS18046: 'inkConsumptionRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(463,21): error TS18046: 'inkConsumptionRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(463,54): error TS18046: 'inkConsumptionRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(465,7): error TS18046: 'inkConsumptionRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(467,16): error TS18046: 'inkConsumptionRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(467,54): error TS18046: 'inkConsumptionRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(494,5): error TS18046: 'inkConsumptionRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(526,5): error TS18046: 'paperUtilizationRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(527,5): error TS18046: 'paperUtilizationRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(528,5): error TS18046: 'paperUtilizationRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(529,52): error TS18046: 'paperUtilizationRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(531,9): error TS18046: 'paperUtilizationRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(532,7): error TS18046: 'paperUtilizationRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(533,21): error TS18046: 'paperUtilizationRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(533,54): error TS18046: 'paperUtilizationRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(559,5): error TS18046: 'paperUtilizationRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(592,5): error TS18046: 'surplusInkReuseRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(593,5): error TS18046: 'surplusInkReuseRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(594,5): error TS18046: 'surplusInkReuseRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(595,5): error TS18046: 'surplusInkReuseRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(597,9): error TS18046: 'surplusInkReuseRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(598,7): error TS18046: 'surplusInkReuseRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(599,21): error TS18046: 'surplusInkReuseRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(599,55): error TS18046: 'surplusInkReuseRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(644,7): error TS18046: 'setupTime' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(650,7): error TS18046: 'setupTime' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(650,31): error TS18046: 'setupTime' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(651,38): error TS18046: 'r.setupCount' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(654,7): error TS18046: 'setupTime' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(654,32): error TS18046: 'setupTime' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(655,38): error TS18046: 'r.avgSetupMinutes' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(655,58): error TS18046: 'r.setupCount' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(658,7): error TS18046: 'setupTime' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(659,9): error TS18046: 'setupTime' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(660,25): error TS18046: 'setupTime' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(660,50): error TS18046: 'setupTime' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(687,5): error TS18046: 'ftq' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(688,5): error TS18046: 'ftq' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(689,5): error TS18046: 'ftq' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(689,28): error TS18046: 'ftq' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(689,50): error TS18046: 'ftq' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(690,5): error TS18046: 'ftq' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(691,7): error TS18046: 'ftq' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(692,23): error TS18046: 'ftq' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(692,46): error TS18046: 'ftq' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(724,5): error TS18046: 'staleInventoryRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(725,5): error TS18046: 'staleInventoryRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(726,5): error TS18046: 'staleInventoryRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(727,5): error TS18046: 'staleInventoryRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(728,5): error TS18046: 'staleInventoryRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(729,7): error TS18046: 'staleInventoryRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(729,41): error TS18046: 'staleInventoryRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(730,5): error TS18046: 'staleInventoryRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(731,7): error TS18046: 'staleInventoryRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(733,14): error TS18046: 'staleInventoryRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(733,46): error TS18046: 'staleInventoryRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(768,5): error TS18046: 'oeeLossAnalysis' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(769,5): error TS18046: 'oeeLossAnalysis' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(770,5): error TS18046: 'oeeLossAnalysis' is of type 'unknown'. -src/app/api/dashboard/production/route.ts(124,9): error TS18046: 'dieStats' is of type 'unknown'. -src/app/api/dashboard/production/route.ts(125,9): error TS18046: 'dieStats' is of type 'unknown'. -src/app/api/dashboard/production/route.ts(126,9): error TS18046: 'dieStats' is of type 'unknown'. -src/app/api/dashboard/production/route.ts(127,9): error TS18046: 'dieStats' is of type 'unknown'. -src/app/api/dashboard/production/route.ts(128,9): error TS18046: 'dieStats' is of type 'unknown'. -src/app/api/dashboard/production/route.ts(128,27): error TS18046: 'dieStats' is of type 'unknown'. -src/app/api/dashboard/production/route.ts(128,44): error TS18046: 'dieStats' is of type 'unknown'. -src/app/api/dashboard/production/route.ts(128,63): error TS18046: 'dieStats' is of type 'unknown'. -src/app/api/dashboard/production/route.ts(128,81): error TS18046: 'dieStats' is of type 'unknown'. -src/app/api/dashboard/production/route.ts(136,43): error TS2339: Property 'total' does not exist on type '{}'. -src/app/api/dashboard/production/route.ts(137,45): error TS2339: Property 'running' does not exist on type '{}'. -src/app/api/dashboard/production/route.ts(145,43): error TS2339: Property 'total_orders' does not exist on type '{}'. -src/app/api/dashboard/production/route.ts(146,44): error TS2339: Property 'active_orders' does not exist on type '{}'. -src/app/api/dashboard/production/route.ts(147,46): error TS2339: Property 'completed_today' does not exist on type '{}'. -src/app/api/dashboard/production/route.ts(152,44): error TS2345: Argument of type '(e: DbRow) => { id: unknown; name: unknown; type: unknown; status: string; efficiency: number; currentOrder: string; operator: string; runtime: number; }' is not assignable to parameter of type '(value: SqlValue, index: number, array: SqlValue[]) => { id: unknown; name: unknown; type: unknown; status: string; efficiency: number; currentOrder: string; operator: string; runtime: number; }'. - Types of parameters 'e' and 'value' are incompatible. - Type 'SqlValue' is not assignable to type 'DbRow'. - Type 'undefined' is not assignable to type 'DbRow'. -src/app/api/dashboard/production/route.ts(169,40): error TS2345: Argument of type '(o: DbRow) => { id: unknown; orderNo: unknown; product: {}; quantity: number; status: unknown; customer: {}; updateTime: unknown; }' is not assignable to parameter of type '(value: SqlValue, index: number, array: SqlValue[]) => { id: unknown; orderNo: unknown; product: {}; quantity: number; status: unknown; customer: {}; updateTime: unknown; }'. - Types of parameters 'o' and 'value' are incompatible. - Type 'SqlValue' is not assignable to type 'DbRow'. - Type 'undefined' is not assignable to type 'DbRow'. -src/app/api/dashboard/production/route.ts(179,41): error TS2339: Property 'total_opened' does not exist on type '{}'. -src/app/api/dashboard/production/route.ts(180,35): error TS2339: Property 'in_use' does not exist on type '{}'. -src/app/api/dashboard/production/route.ts(181,37): error TS2339: Property 'expired' does not exist on type '{}'. -src/app/api/dashboard/production/route.ts(182,42): error TS2339: Property 'expiring_soon' does not exist on type '{}'. -src/app/api/dashboard/production/route.ts(185,35): error TS2339: Property 'total' does not exist on type '{}'. -src/app/api/dashboard/production/route.ts(186,36): error TS2339: Property 'normal' does not exist on type '{}'. -src/app/api/dashboard/production/route.ts(187,37): error TS2339: Property 'warning' does not exist on type '{}'. -src/app/api/dashboard/production/route.ts(188,36): error TS2339: Property 'locked' does not exist on type '{}'. -src/app/api/dashboard/production/route.ts(189,38): error TS2339: Property 'scrapped' does not exist on type '{}'. -src/app/api/dashboard/quality/route.ts(24,9): error TS18046: 'overview' is of type 'unknown'. -src/app/api/dashboard/quality/route.ts(39,9): error TS18046: 'overview' is of type 'unknown'. -src/app/api/dashboard/quality/route.ts(40,9): error TS18046: 'overview' is of type 'unknown'. -src/app/api/dashboard/quality/route.ts(41,9): error TS18046: 'overview' is of type 'unknown'. -src/app/api/dashboard/quality/route.ts(42,11): error TS18046: 'overview' is of type 'unknown'. -src/app/api/dashboard/quality/route.ts(43,27): error TS18046: 'overview' is of type 'unknown'. -src/app/api/dashboard/quality/route.ts(43,56): error TS18046: 'overview' is of type 'unknown'. -src/app/api/dashboard/quality/route.ts(45,9): error TS18046: 'overview' is of type 'unknown'. -src/app/api/dashboard/quality/route.ts(46,11): error TS18046: 'overview' is of type 'unknown'. -src/app/api/dashboard/quality/route.ts(47,27): error TS18046: 'overview' is of type 'unknown'. -src/app/api/dashboard/quality/route.ts(47,56): error TS18046: 'overview' is of type 'unknown'. -src/app/api/dashboard/route.ts(100,11): error TS2353: Object literal may only specify known properties, and 'type' does not exist in type 'Date | Buffer | readonly SqlValue[]'. -src/app/api/dashboard/route.ts(113,11): error TS2353: Object literal may only specify known properties, and 'type' does not exist in type 'Date | Buffer | readonly SqlValue[]'. -src/app/api/dashboard/route.ts(121,11): error TS2353: Object literal may only specify known properties, and 'type' does not exist in type 'Date | Buffer | readonly SqlValue[]'. -src/app/api/dashboard/route.ts(161,40): error TS2345: Argument of type '(o: DbRow) => { id: unknown; orderNo: unknown; customer: {}; product: {}; quantity: number; status: unknown; date: unknown; }' is not assignable to parameter of type '(value: SqlValue, index: number, array: SqlValue[]) => { id: unknown; orderNo: unknown; customer: {}; product: {}; quantity: number; status: unknown; date: unknown; }'. - Types of parameters 'o' and 'value' are incompatible. - Type 'SqlValue' is not assignable to type 'DbRow'. - Type 'undefined' is not assignable to type 'DbRow'. -src/app/api/dashboard/sales/route.ts(28,9): error TS18046: 'overview' is of type 'unknown'. -src/app/api/dashboard/sales/route.ts(29,9): error TS18046: 'overview' is of type 'unknown'. -src/app/api/dashboard/sales/route.ts(30,9): error TS18046: 'overview' is of type 'unknown'. -src/app/api/dashboard/sales/route.ts(31,9): error TS18046: 'overview' is of type 'unknown'. -src/app/api/dashboard/sales/route.ts(45,9): error TS18046: 'overview' is of type 'unknown'. -src/app/api/dashboard/warehouse/route.ts(25,9): error TS18046: 'overview' is of type 'unknown'. -src/app/api/dashboard/warehouse/route.ts(26,9): error TS18046: 'overview' is of type 'unknown'. -src/app/api/dashboard/warehouse/route.ts(27,9): error TS18046: 'overview' is of type 'unknown'. -src/app/api/dashboard/warehouse/route.ts(43,9): error TS18046: 'overview' is of type 'unknown'. -src/app/api/dashboard/warehouse/route.ts(45,9): error TS18046: 'overview' is of type 'unknown'. -src/app/api/dcprint/formula/version/[id]/activate/route.ts(9,3): error TS2345: Argument of type '(request: NextRequest, userInfo: DbRow, { params }: { params: Promise<{ id: string; }>; }) => Promise>>' is not assignable to parameter of type '(request: NextRequest, userInfo: UserInfo, context?: any) => Promise>'. - Types of parameters 'userInfo' and 'userInfo' are incompatible. - Type 'UserInfo' is not assignable to type 'DbRow'. - Index signature for type 'string' is missing in type 'UserInfo'. -src/app/api/dcprint/formula/version/[id]/activate/route.ts(17,41): error TS2345: Argument of type 'unknown' is not assignable to parameter of type 'number'. -src/app/api/dcprint/formula/version/[id]/cancel/route.ts(9,3): error TS2345: Argument of type '(request: NextRequest, userInfo: DbRow, { params }: { params: Promise<{ id: string; }>; }) => Promise>>' is not assignable to parameter of type '(request: NextRequest, userInfo: UserInfo, context?: any) => Promise>'. - Types of parameters 'userInfo' and 'userInfo' are incompatible. - Type 'UserInfo' is not assignable to type 'DbRow'. - Index signature for type 'string' is missing in type 'UserInfo'. -src/app/api/dcprint/formula/version/[id]/cancel/route.ts(19,39): error TS2345: Argument of type 'unknown' is not assignable to parameter of type 'number'. -src/app/api/dcprint/formula/version/[id]/duplicate/route.ts(9,3): error TS2345: Argument of type '(request: NextRequest, userInfo: DbRow, { params }: { params: Promise<{ id: string; }>; }) => Promise> | NextResponse>>' is not assignable to parameter of type '(request: NextRequest, userInfo: UserInfo, context?: any) => Promise>'. - Types of parameters 'userInfo' and 'userInfo' are incompatible. - Type 'UserInfo' is not assignable to type 'DbRow'. - Index signature for type 'string' is missing in type 'UserInfo'. -src/app/api/dcprint/formula/version/[id]/duplicate/route.ts(18,68): error TS2345: Argument of type 'unknown' is not assignable to parameter of type 'number'. -src/app/api/dcprint/ink-consumption/route.ts(167,7): error TS18046: 'summary' is of type 'unknown'. -src/app/api/dcprint/ink-consumption/route.ts(168,7): error TS18046: 'summary' is of type 'unknown'. -src/app/api/dcprint/ink-consumption/route.ts(169,7): error TS18046: 'summary' is of type 'unknown'. -src/app/api/dcprint/ink-consumption/route.ts(170,7): error TS18046: 'summary' is of type 'unknown'. -src/app/api/dcprint/ink-consumption/route.ts(171,7): error TS18046: 'summary' is of type 'unknown'. -src/app/api/dcprint/ink-consumption/route.ts(172,9): error TS18046: 'summary' is of type 'unknown'. -src/app/api/dcprint/ink-consumption/route.ts(174,17): error TS18046: 'summary' is of type 'unknown'. -src/app/api/dcprint/ink-consumption/route.ts(174,42): error TS18046: 'summary' is of type 'unknown'. -src/app/api/dcprint/ink-consumption/route.ts(174,68): error TS18046: 'summary' is of type 'unknown'. -src/app/api/dcprint/ink-consumption/route.ts(177,7): error TS18046: 'summary' is of type 'unknown'. -src/app/api/dcprint/ink-consumption/route.ts(179,25): error TS18046: 'summary' is of type 'unknown'. -src/app/api/dcprint/ink-dispatch/route.ts(115,39): error TS2339: Property 'insertId' does not exist on type 'QueryResult'. - Property 'insertId' does not exist on type 'ResultSetHeader[]'. -src/app/api/dcprint/ink-dispatch/route.ts(128,25): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/dcprint/ink-dispatch/route.ts(129,27): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/dcprint/ink-dispatch/route.ts(197,34): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/dcprint/ink-dispatch/route.ts(197,47): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/dcprint/ink-dispatch/route.ts(245,26): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/dcprint/ink-dispatch/route.ts(245,40): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/dcprint/ink-dispatch/route.ts(246,46): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/dcprint/ink-dispatch/route.ts(249,42): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/dcprint/ink-formula/route.ts(115,38): error TS2339: Property 'insertId' does not exist on type 'QueryResult'. - Property 'insertId' does not exist on type 'ResultSetHeader[]'. -src/app/api/dcprint/ink-init/route.ts(20,7): error TS2345: Argument of type '{ table: string; status: string; message?: undefined; } | { table: string; status: string; message: string; }' is not assignable to parameter of type 'SqlValue'. - Type '{ table: string; status: string; message?: undefined; }' is not assignable to type 'SqlValue'. -src/app/api/dcprint/ink-init/route.ts(51,7): error TS2345: Argument of type '{ table: string; status: string; message?: undefined; } | { table: string; status: string; message: string; }' is not assignable to parameter of type 'SqlValue'. - Type '{ table: string; status: string; message?: undefined; }' is not assignable to type 'SqlValue'. -src/app/api/dcprint/ink-init/route.ts(77,7): error TS2345: Argument of type '{ table: string; status: string; message?: undefined; } | { table: string; status: string; message: string; }' is not assignable to parameter of type 'SqlValue'. - Type '{ table: string; status: string; message?: undefined; }' is not assignable to type 'SqlValue'. -src/app/api/dcprint/ink-init/route.ts(97,7): error TS2345: Argument of type '{ table: string; status: string; message?: undefined; } | { table: string; status: string; message: string; }' is not assignable to parameter of type 'SqlValue'. - Type '{ table: string; status: string; message?: undefined; }' is not assignable to type 'SqlValue'. -src/app/api/dcprint/ink-init/route.ts(136,7): error TS2345: Argument of type '{ table: string; status: string; message?: undefined; } | { table: string; status: string; message: string; }' is not assignable to parameter of type 'SqlValue'. - Type '{ table: string; status: string; message?: undefined; }' is not assignable to type 'SqlValue'. -src/app/api/dcprint/ink-init/route.ts(166,7): error TS2345: Argument of type '{ table: string; status: string; message?: undefined; } | { table: string; status: string; message: string; }' is not assignable to parameter of type 'SqlValue'. - Type '{ table: string; status: string; message?: undefined; }' is not assignable to type 'SqlValue'. -src/app/api/dcprint/ink-init/route.ts(205,13): error TS2488: Type 'ResultSetHeader' must have a '[Symbol.iterator]()' method that returns an iterator. -src/app/api/dcprint/ink-init/route.ts(214,11): error TS2353: Object literal may only specify known properties, and 'table' does not exist in type 'Date | Buffer | readonly SqlValue[]'. -src/app/api/dcprint/ink-init/route.ts(221,9): error TS2353: Object literal may only specify known properties, and 'table' does not exist in type 'Date | Buffer | readonly SqlValue[]'. -src/app/api/dcprint/ink-init/route.ts(229,13): error TS2488: Type 'ResultSetHeader' must have a '[Symbol.iterator]()' method that returns an iterator. -src/app/api/dcprint/ink-init/route.ts(235,11): error TS2353: Object literal may only specify known properties, and 'table' does not exist in type 'Date | Buffer | readonly SqlValue[]'. -src/app/api/dcprint/ink-init/route.ts(242,9): error TS2353: Object literal may only specify known properties, and 'table' does not exist in type 'Date | Buffer | readonly SqlValue[]'. -src/app/api/dcprint/ink-init/route.ts(250,13): error TS2488: Type 'ResultSetHeader' must have a '[Symbol.iterator]()' method that returns an iterator. -src/app/api/dcprint/ink-init/route.ts(255,24): error TS2353: Object literal may only specify known properties, and 'table' does not exist in type 'Date | Buffer | readonly SqlValue[]'. -src/app/api/dcprint/ink-init/route.ts(259,9): error TS2353: Object literal may only specify known properties, and 'table' does not exist in type 'Date | Buffer | readonly SqlValue[]'. -src/app/api/dcprint/ink-opening/route.ts(76,29): error TS2345: Argument of type '{}' is not assignable to parameter of type 'string'. -src/app/api/dcprint/ink-opening/route.ts(77,29): error TS2345: Argument of type '{}' is not assignable to parameter of type 'string'. -src/app/api/dcprint/ink-opening/route.ts(78,31): error TS2345: Argument of type '{}' is not assignable to parameter of type 'string'. -src/app/api/dcprint/ink-opening/route.ts(79,32): error TS2345: Argument of type '{}' is not assignable to parameter of type 'string'. -src/app/api/dcprint/ink-opening/route.ts(80,37): error TS2345: Argument of type '{}' is not assignable to parameter of type 'string'. -src/app/api/dcprint/ink-opening/route.ts(169,23): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/dcprint/ink-opening/route.ts(169,37): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/dcprint/ink-opening/route.ts(170,40): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/dcprint/ink-opening/route.ts(199,37): error TS2339: Property 'insertId' does not exist on type 'QueryResult'. - Property 'insertId' does not exist on type 'ResultSetHeader[]'. -src/app/api/dcprint/ink-query/route.ts(37,5): error TS18046: 'result' is of type 'unknown'. -src/app/api/dcprint/ink-query/route.ts(41,5): error TS18046: 'result' is of type 'unknown'. -src/app/api/dcprint/ink-query/route.ts(45,5): error TS18046: 'result' is of type 'unknown'. -src/app/api/dcprint/ink-query/route.ts(49,5): error TS18046: 'result' is of type 'unknown'. -src/app/api/dcprint/ink-query/route.ts(65,5): error TS18046: 'trace' is of type 'unknown'. -src/app/api/dcprint/ink-query/route.ts(87,9): error TS18046: 'trace' is of type 'unknown'. -src/app/api/dcprint/ink-query/route.ts(112,5): error TS18046: 'trace' is of type 'unknown'. -src/app/api/dcprint/ink-query/route.ts(129,32): error TS18046: 'trace' is of type 'unknown'. -src/app/api/dcprint/ink-query/route.ts(131,5): error TS18046: 'trace' is of type 'unknown'. -src/app/api/dcprint/ink-query/route.ts(139,5): error TS18046: 'trace' is of type 'unknown'. -src/app/api/dcprint/ink-query/route.ts(160,7): error TS18046: 'guide' is of type 'unknown'. -src/app/api/dcprint/ink-query/route.ts(174,11): error TS18046: 'guide' is of type 'unknown'. -src/app/api/dcprint/ink-query/route.ts(188,11): error TS18046: 'guide' is of type 'unknown'. -src/app/api/dcprint/ink-query/route.ts(220,9): error TS18046: 'trace' is of type 'unknown'. -src/app/api/dcprint/ink-query/route.ts(234,5): error TS18046: 'trace' is of type 'unknown'. -src/app/api/dcprint/ink-query/route.ts(254,13): error TS18046: 'trace' is of type 'unknown'. -src/app/api/dcprint/ink-query/route.ts(289,5): error TS18046: 'info' is of type 'unknown'. -src/app/api/dcprint/ink-query/route.ts(299,5): error TS18046: 'info' is of type 'unknown'. -src/app/api/dcprint/ink-query/route.ts(323,5): error TS18046: 'info' is of type 'unknown'. -src/app/api/dcprint/ink-surplus/route.ts(157,34): error TS18049: 'b' is possibly 'null' or 'undefined'. -src/app/api/dcprint/ink-surplus/route.ts(157,36): error TS2339: Property 'match_score' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'match_score' does not exist on type 'string'. -src/app/api/dcprint/ink-surplus/route.ts(157,50): error TS18049: 'a' is possibly 'null' or 'undefined'. -src/app/api/dcprint/ink-surplus/route.ts(157,52): error TS2339: Property 'match_score' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'match_score' does not exist on type 'string'. -src/app/api/dcprint/ink-surplus/route.ts(259,21): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/dcprint/ink-surplus/route.ts(263,21): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/dcprint/ink-surplus/route.ts(300,33): error TS2339: Property 'insertId' does not exist on type 'QueryResult'. - Property 'insertId' does not exist on type 'ResultSetHeader[]'. -src/app/api/dcprint/ink-usage/route.ts(99,20): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/dcprint/ink-usage/route.ts(100,27): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/dcprint/ink-usage/route.ts(113,21): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/dcprint/ink-usage/route.ts(117,21): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/dcprint/ink-usage/route.ts(157,26): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/dcprint/ink-usage/route.ts(158,28): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/dcprint/ink-usage/route.ts(164,27): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/dcprint/ink-usage/route.ts(170,31): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/dcprint/ink-usage/route.ts(170,45): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/dcprint/ink-usage/route.ts(170,77): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/dcprint/ink-usage/route.ts(172,53): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/dcprint/ink-usage/route.ts(172,94): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/dcprint/ink-usage/route.ts(222,33): error TS2339: Property 'insertId' does not exist on type 'QueryResult'. - Property 'insertId' does not exist on type 'ResultSetHeader[]'. -src/app/api/dcprint/labels/route.ts(150,5): error TS2345: Argument of type 'SqlValue[]' is not assignable to parameter of type 'DbRow[]'. - Type 'SqlValue' is not assignable to type 'DbRow'. - Type 'undefined' is not assignable to type 'DbRow'. -src/app/api/dcprint/labels/route.ts(336,46): error TS2322: Type 'DbRow' is not assignable to type 'SqlValue'. - Type 'DbRow' is missing the following properties from type 'Buffer': slice, subarray, write, toJSON, and 102 more. -src/app/api/dcprint/labels/route.ts(336,57): error TS2322: Type 'number | DbRow' is not assignable to type 'SqlValue'. - Type 'DbRow' is not assignable to type 'SqlValue'. - Type 'DbRow' is missing the following properties from type 'Buffer': slice, subarray, write, toJSON, and 102 more. -src/app/api/dcprint/labels/route.ts(336,67): error TS2322: Type 'number | DbRow' is not assignable to type 'SqlValue'. - Type 'DbRow' is not assignable to type 'SqlValue'. - Type 'DbRow' is missing the following properties from type 'Buffer': slice, subarray, write, toJSON, and 102 more. -src/app/api/dcprint/labels/route.ts(337,45): error TS2345: Argument of type 'DbRow[]' is not assignable to parameter of type 'SqlValue[]'. - Type 'DbRow' is not assignable to type 'SqlValue'. - Type 'DbRow' is missing the following properties from type 'Buffer': slice, subarray, write, toJSON, and 102 more. -src/app/api/dcprint/process-cards/route.ts(92,5): error TS2345: Argument of type 'SqlValue[]' is not assignable to parameter of type 'DbRow[]'. - Type 'SqlValue' is not assignable to type 'DbRow'. - Type 'undefined' is not assignable to type 'DbRow'. -src/app/api/dcprint/process-cards/route.ts(147,19): error TS2339: Property 'is_main_material' does not exist on type '{}'. -src/app/api/dcprint/process-cards/route.ts(160,23): error TS2339: Property 'material_code' does not exist on type '{}'. -src/app/api/dcprint/process-cards/route.ts(161,23): error TS2339: Property 'batch_no' does not exist on type '{}'. -src/app/api/dcprint/process-cards/route.ts(190,23): error TS2352: Conversion of type '[QueryResult, FieldPacket[]]' to type 'DbRow' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first. - Index signature for type 'string' is missing in type '[QueryResult, FieldPacket[]]'. -src/app/api/dcprint/process-cards/route.ts(258,12): error TS2339: Property 'lock_status' does not exist on type '{}'. -src/app/api/dcprint/process-cards/route.ts(266,6): error TS2322: Type 'unknown' is not assignable to type 'SqlValue'. -src/app/api/dcprint/process-cards/route.ts(280,12): error TS2339: Property 'id' does not exist on type '{}'. -src/app/api/dcprint/process-cards/route.ts(281,12): error TS2339: Property 'card_no' does not exist on type '{}'. -src/app/api/dcprint/process-cards/route.ts(284,13): error TS2339: Property 'material_code' does not exist on type '{}'. -src/app/api/dcprint/process-cards/route.ts(285,13): error TS2339: Property 'material_name' does not exist on type '{}'. -src/app/api/dcprint/process-cards/route.ts(286,13): error TS2339: Property 'specification' does not exist on type '{}'. -src/app/api/dcprint/process-cards/route.ts(287,13): error TS2339: Property 'batch_no' does not exist on type '{}'. -src/app/api/dcprint/process-cards/route.ts(288,13): error TS2339: Property 'quantity' does not exist on type '{}'. -src/app/api/dcprint/process-cards/route.ts(289,13): error TS2339: Property 'unit' does not exist on type '{}'. -src/app/api/dcprint/process-cards/route.ts(294,76): error TS2322: Type 'unknown' is not assignable to type 'SqlValue'. -src/app/api/dcprint/process-cards/route.ts(336,19): error TS2345: Argument of type '{} | null' is not assignable to parameter of type 'SqlValue'. - Type '{}' is not assignable to type 'SqlValue'. -src/app/api/dcprint/process-cards/route.ts(383,52): error TS2322: Type 'DbRow' is not assignable to type 'SqlValue'. - Type 'DbRow' is missing the following properties from type 'Buffer': slice, subarray, write, toJSON, and 102 more. -src/app/api/dcprint/process-cards/route.ts(383,71): error TS2322: Type 'number | DbRow' is not assignable to type 'SqlValue'. - Type 'DbRow' is not assignable to type 'SqlValue'. - Type 'DbRow' is missing the following properties from type 'Buffer': slice, subarray, write, toJSON, and 102 more. -src/app/api/dcprint/process-cards/route.ts(383,81): error TS2322: Type 'number | DbRow' is not assignable to type 'SqlValue'. - Type 'DbRow' is not assignable to type 'SqlValue'. - Type 'DbRow' is missing the following properties from type 'Buffer': slice, subarray, write, toJSON, and 102 more. -src/app/api/dcprint/process-cards/route.ts(384,45): error TS2345: Argument of type 'DbRow[]' is not assignable to parameter of type 'SqlValue[]'. - Type 'DbRow' is not assignable to type 'SqlValue'. - Type 'DbRow' is missing the following properties from type 'Buffer': slice, subarray, write, toJSON, and 102 more. -src/app/api/dcprint/sample-card/[id]/cancel/route.ts(10,3): error TS2345: Argument of type '(_request: NextRequest, userInfo: DbRow, { params }: { params: Promise<{ id: string; }>; }) => Promise> | NextResponse>>' is not assignable to parameter of type '(request: NextRequest, userInfo: UserInfo, context?: any) => Promise>'. - Types of parameters 'userInfo' and 'userInfo' are incompatible. - Type 'UserInfo' is not assignable to type 'DbRow'. - Index signature for type 'string' is missing in type 'UserInfo'. -src/app/api/dcprint/sample-card/[id]/cancel/route.ts(17,44): error TS2345: Argument of type 'unknown' is not assignable to parameter of type 'number'. -src/app/api/dcprint/sample-card/[id]/confirm/route.ts(10,3): error TS2345: Argument of type '(_request: NextRequest, userInfo: DbRow, { params }: { params: Promise<{ id: string; }>; }) => Promise> | NextResponse>>' is not assignable to parameter of type '(request: NextRequest, userInfo: UserInfo, context?: any) => Promise>'. - Types of parameters 'userInfo' and 'userInfo' are incompatible. - Type 'UserInfo' is not assignable to type 'DbRow'. - Index signature for type 'string' is missing in type 'UserInfo'. -src/app/api/dcprint/sample-card/[id]/confirm/route.ts(17,45): error TS2345: Argument of type 'unknown' is not assignable to parameter of type 'number'. -src/app/api/dcprint/sample-card/[id]/convert-work-order/route.ts(10,3): error TS2345: Argument of type '(request: NextRequest, userInfo: DbRow, { params }: { params: Promise<{ id: string; }>; }) => Promise> | NextResponse>>' is not assignable to parameter of type '(request: NextRequest, userInfo: UserInfo, context?: any) => Promise>'. - Types of parameters 'userInfo' and 'userInfo' are incompatible. - Type 'UserInfo' is not assignable to type 'DbRow'. - Index signature for type 'string' is missing in type 'UserInfo'. -src/app/api/dcprint/sample-card/[id]/convert-work-order/route.ts(26,9): error TS2345: Argument of type 'unknown' is not assignable to parameter of type 'number'. -src/app/api/dcprint/sample-card/[id]/cost-variance/route.ts(10,3): error TS2345: Argument of type '(_request: NextRequest, _userInfo: DbRow, { params }: { params: Promise<{ id: string; }>; }) => Promise> | NextResponse Promise>'. - Types of parameters '_userInfo' and 'userInfo' are incompatible. - Type 'UserInfo' is not assignable to type 'DbRow'. - Index signature for type 'string' is missing in type 'UserInfo'. -src/app/api/dcprint/sample-card/[id]/duplicate/route.ts(10,3): error TS2345: Argument of type '(_request: NextRequest, userInfo: DbRow, { params }: { params: Promise<{ id: string; }>; }) => Promise> | NextResponse>>' is not assignable to parameter of type '(request: NextRequest, userInfo: UserInfo, context?: any) => Promise>'. - Types of parameters 'userInfo' and 'userInfo' are incompatible. - Type 'UserInfo' is not assignable to type 'DbRow'. - Index signature for type 'string' is missing in type 'UserInfo'. -src/app/api/dcprint/sample-card/[id]/duplicate/route.ts(17,68): error TS2345: Argument of type 'unknown' is not assignable to parameter of type 'number'. -src/app/api/dcprint/sample-card/[id]/generate-quote/route.ts(10,3): error TS2345: Argument of type '(request: NextRequest, userInfo: DbRow, { params }: { params: Promise<{ id: string; }>; }) => Promise> | NextResponse>>' is not assignable to parameter of type '(request: NextRequest, userInfo: UserInfo, context?: any) => Promise>'. - Types of parameters 'userInfo' and 'userInfo' are incompatible. - Type 'UserInfo' is not assignable to type 'DbRow'. - Index signature for type 'string' is missing in type 'UserInfo'. -src/app/api/dcprint/sample-card/[id]/generate-quote/route.ts(26,9): error TS2345: Argument of type 'unknown' is not assignable to parameter of type 'number'. -src/app/api/dcprint/sample-card/[id]/route.ts(11,3): error TS2345: Argument of type '(_request: NextRequest, _userInfo: DbRow, { params }: { params: Promise<{ id: string; }>; }) => Promise> | NextResponse>>' is not assignable to parameter of type '(request: NextRequest, userInfo: UserInfo, context?: any) => Promise>'. - Types of parameters '_userInfo' and 'userInfo' are incompatible. - Type 'UserInfo' is not assignable to type 'DbRow'. - Index signature for type 'string' is missing in type 'UserInfo'. -src/app/api/dcprint/sample-card/[id]/route.ts(25,3): error TS2345: Argument of type '(request: NextRequest, userInfo: DbRow, { params }: { params: Promise<{ id: string; }>; }) => Promise> | NextResponse>>' is not assignable to parameter of type '(request: NextRequest, userInfo: UserInfo, context?: any) => Promise>'. - Types of parameters 'userInfo' and 'userInfo' are incompatible. - Type 'UserInfo' is not assignable to type 'DbRow'. - Index signature for type 'string' is missing in type 'UserInfo'. -src/app/api/dcprint/sample-card/[id]/route.ts(41,57): error TS2345: Argument of type 'unknown' is not assignable to parameter of type 'number'. -src/app/api/dcprint/sample-card/[id]/route.ts(51,3): error TS2345: Argument of type '(_request: NextRequest, _userInfo: DbRow, { params }: { params: Promise<{ id: string; }>; }) => Promise> | NextResponse>>' is not assignable to parameter of type '(request: NextRequest, userInfo: UserInfo, context?: any) => Promise>'. - Types of parameters '_userInfo' and 'userInfo' are incompatible. - Type 'UserInfo' is not assignable to type 'DbRow'. - Index signature for type 'string' is missing in type 'UserInfo'. -src/app/api/dcprint/sample-card/[id]/save-as-template/route.ts(11,3): error TS2345: Argument of type '(request: NextRequest, userInfo: DbRow, { params }: { params: Promise<{ id: string; }>; }) => Promise> | NextResponse>>' is not assignable to parameter of type '(request: NextRequest, userInfo: UserInfo, context?: any) => Promise>'. - Types of parameters 'userInfo' and 'userInfo' are incompatible. - Type 'UserInfo' is not assignable to type 'DbRow'. - Index signature for type 'string' is missing in type 'UserInfo'. -src/app/api/dcprint/sample-card/[id]/save-as-template/route.ts(26,9): error TS2345: Argument of type 'unknown' is not assignable to parameter of type 'number'. -src/app/api/dcprint/sample-card/[id]/submit/route.ts(10,3): error TS2345: Argument of type '(_request: NextRequest, userInfo: DbRow, { params }: { params: Promise<{ id: string; }>; }) => Promise> | NextResponse>>' is not assignable to parameter of type '(request: NextRequest, userInfo: UserInfo, context?: any) => Promise>'. - Types of parameters 'userInfo' and 'userInfo' are incompatible. - Type 'UserInfo' is not assignable to type 'DbRow'. - Index signature for type 'string' is missing in type 'UserInfo'. -src/app/api/dcprint/sample-card/[id]/submit/route.ts(17,59): error TS2345: Argument of type 'unknown' is not assignable to parameter of type 'number'. -src/app/api/dcprint/sample-card/template/[id]/route.ts(21,3): error TS2345: Argument of type '(request: NextRequest, userInfo: DbRow, { params }: { params: Promise<{ id: string; }>; }) => Promise>>' is not assignable to parameter of type '(request: NextRequest, userInfo: UserInfo, context?: any) => Promise>'. - Types of parameters 'userInfo' and 'userInfo' are incompatible. - Type 'UserInfo' is not assignable to type 'DbRow'. - Index signature for type 'string' is missing in type 'UserInfo'. -src/app/api/dcprint/sample-card/template/[id]/route.ts(28,52): error TS2345: Argument of type 'unknown' is not assignable to parameter of type 'number'. -src/app/api/dcprint/sample-card/template/route.ts(23,3): error TS2345: Argument of type '(request: NextRequest, userInfo: DbRow) => Promise> | NextResponse>>' is not assignable to parameter of type '(request: NextRequest, userInfo: UserInfo, context?: any) => Promise>'. - Types of parameters 'userInfo' and 'userInfo' are incompatible. - Type 'UserInfo' is not assignable to type 'DbRow'. - Index signature for type 'string' is missing in type 'UserInfo'. -src/app/api/dcprint/sample-card/template/route.ts(28,51): error TS2345: Argument of type 'unknown' is not assignable to parameter of type 'number'. -src/app/api/dcprint/scan/route.ts(25,21): error TS18046: 'qrData' is of type 'unknown'. -src/app/api/dcprint/scan/route.ts(26,18): error TS18046: 'qrData' is of type 'unknown'. -src/app/api/dcprint/scan/route.ts(123,13): error TS2339: Property 'isMainMaterial' does not exist on type '{}'. -src/app/api/dcprint/scan/route.ts(141,14): error TS2339: Property 'id' does not exist on type '{}'. -src/app/api/dcprint/scan/route.ts(143,11): error TS2339: Property 'cuttingRecords' does not exist on type '{}'. -src/app/api/dcprint/scan/route.ts(147,13): error TS2339: Property 'isCut' does not exist on type '{}'. -src/app/api/dcprint/scan/route.ts(159,14): error TS2339: Property 'id' does not exist on type '{}'. -src/app/api/dcprint/scan/route.ts(161,11): error TS2339: Property 'childLabels' does not exist on type '{}'. -src/app/api/dcprint/scan/route.ts(205,16): error TS2339: Property 'id' does not exist on type '{}'. -src/app/api/dcprint/scan/route.ts(208,13): error TS2339: Property 'processCards' does not exist on type '{}'. -src/app/api/dcprint/scan/route.ts(263,11): error TS2339: Property 'id' does not exist on type '{}'. -src/app/api/dcprint/scan/route.ts(266,8): error TS2339: Property 'materials' does not exist on type '{}'. -src/app/api/dcprint/scan/route.ts(267,8): error TS2339: Property 'mainMaterials' does not exist on type '{}'. -src/app/api/dcprint/scan/route.ts(267,42): error TS2769: No overload matches this call. - Overload 1 of 2, '(predicate: (value: unknown[], index: number, array: unknown[][]) => value is unknown[], thisArg?: any): unknown[][]', gave the following error. - Argument of type '(m: DbRow) => boolean' is not assignable to parameter of type '(value: unknown[], index: number, array: unknown[][]) => value is unknown[]'. - Types of parameters 'm' and 'value' are incompatible. - Type 'unknown[]' is not assignable to type 'DbRow'. - Index signature for type 'string' is missing in type 'unknown[]'. - Overload 2 of 2, '(predicate: (value: unknown[], index: number, array: unknown[][]) => unknown, thisArg?: any): unknown[][]', gave the following error. - Argument of type '(m: DbRow) => boolean' is not assignable to parameter of type '(value: unknown[], index: number, array: unknown[][]) => unknown'. - Types of parameters 'm' and 'value' are incompatible. - Type 'unknown[]' is not assignable to type 'DbRow'. - Index signature for type 'string' is missing in type 'unknown[]'. -src/app/api/dcprint/scan/route.ts(268,8): error TS2339: Property 'auxiliaryMaterials' does not exist on type '{}'. -src/app/api/dcprint/scan/route.ts(268,47): error TS2769: No overload matches this call. - Overload 1 of 2, '(predicate: (value: unknown[], index: number, array: unknown[][]) => value is unknown[], thisArg?: any): unknown[][]', gave the following error. - Argument of type '(m: DbRow) => boolean' is not assignable to parameter of type '(value: unknown[], index: number, array: unknown[][]) => value is unknown[]'. - Types of parameters 'm' and 'value' are incompatible. - Type 'unknown[]' is not assignable to type 'DbRow'. - Index signature for type 'string' is missing in type 'unknown[]'. - Overload 2 of 2, '(predicate: (value: unknown[], index: number, array: unknown[][]) => unknown, thisArg?: any): unknown[][]', gave the following error. - Argument of type '(m: DbRow) => boolean' is not assignable to parameter of type '(value: unknown[], index: number, array: unknown[][]) => unknown'. - Types of parameters 'm' and 'value' are incompatible. - Type 'unknown[]' is not assignable to type 'DbRow'. - Index signature for type 'string' is missing in type 'unknown[]'. -src/app/api/dcprint/tool/[id]/activate/route.ts(11,3): error TS2345: Argument of type '(request: NextRequest, _userInfo: DbRow, { params }: { params: Promise<{ id: string; }>; }) => Promise> | NextResponse>>' is not assignable to parameter of type '(request: NextRequest, userInfo: UserInfo, context?: any) => Promise>'. - Types of parameters '_userInfo' and 'userInfo' are incompatible. - Type 'UserInfo' is not assignable to type 'DbRow'. - Index signature for type 'string' is missing in type 'UserInfo'. -src/app/api/dcprint/tool/[id]/maintenance/route.ts(11,3): error TS2345: Argument of type '(request: NextRequest, _userInfo: DbRow, { params }: { params: Promise<{ id: string; }>; }) => Promise>>' is not assignable to parameter of type '(request: NextRequest, userInfo: UserInfo, context?: any) => Promise>'. - Types of parameters '_userInfo' and 'userInfo' are incompatible. - Type 'UserInfo' is not assignable to type 'DbRow'. - Index signature for type 'string' is missing in type 'UserInfo'. -src/app/api/dcprint/tool/[id]/maintenance/route.ts(24,3): error TS2345: Argument of type '(request: NextRequest, userInfo: DbRow, { params }: { params: Promise<{ id: string; }>; }) => Promise> | NextResponse> | NextResponse<...>>' is not assignable to parameter of type '(request: NextRequest, userInfo: UserInfo, context?: any) => Promise>'. - Types of parameters 'userInfo' and 'userInfo' are incompatible. - Type 'UserInfo' is not assignable to type 'DbRow'. - Index signature for type 'string' is missing in type 'UserInfo'. -src/app/api/dcprint/tool/[id]/maintenance/route.ts(45,9): error TS2322: Type 'unknown' is not assignable to type 'number | undefined'. -src/app/api/dcprint/tool/[id]/maintenance/route.ts(46,9): error TS2322: Type 'unknown' is not assignable to type 'string | undefined'. -src/app/api/dcprint/tool/[id]/route.ts(11,3): error TS2345: Argument of type '(request: NextRequest, _userInfo: DbRow, { params }: { params: Promise<{ id: string; }>; }) => Promise> | NextResponse>>' is not assignable to parameter of type '(request: NextRequest, userInfo: UserInfo, context?: any) => Promise>'. - Types of parameters '_userInfo' and 'userInfo' are incompatible. - Type 'UserInfo' is not assignable to type 'DbRow'. - Index signature for type 'string' is missing in type 'UserInfo'. -src/app/api/dcprint/tool/[id]/route.ts(25,3): error TS2345: Argument of type '(request: NextRequest, _userInfo: DbRow, { params }: { params: Promise<{ id: string; }>; }) => Promise>>' is not assignable to parameter of type '(request: NextRequest, userInfo: UserInfo, context?: any) => Promise>'. - Types of parameters '_userInfo' and 'userInfo' are incompatible. - Type 'UserInfo' is not assignable to type 'DbRow'. - Index signature for type 'string' is missing in type 'UserInfo'. -src/app/api/dcprint/tool/[id]/route.ts(39,3): error TS2345: Argument of type '(request: NextRequest, _userInfo: DbRow, { params }: { params: Promise<{ id: string; }>; }) => Promise> | NextResponse>>' is not assignable to parameter of type '(request: NextRequest, userInfo: UserInfo, context?: any) => Promise>'. - Types of parameters '_userInfo' and 'userInfo' are incompatible. - Type 'UserInfo' is not assignable to type 'DbRow'. - Index signature for type 'string' is missing in type 'UserInfo'. -src/app/api/dcprint/tool/[id]/scrap/route.ts(11,3): error TS2345: Argument of type '(request: NextRequest, userInfo: DbRow, { params }: { params: Promise<{ id: string; }>; }) => Promise> | NextResponse>>' is not assignable to parameter of type '(request: NextRequest, userInfo: UserInfo, context?: any) => Promise>'. - Types of parameters 'userInfo' and 'userInfo' are incompatible. - Type 'UserInfo' is not assignable to type 'DbRow'. - Index signature for type 'string' is missing in type 'UserInfo'. -src/app/api/dcprint/tool/[id]/scrap/route.ts(22,9): error TS2322: Type 'unknown' is not assignable to type 'number'. -src/app/api/dcprint/tool/[id]/usage/route.ts(11,3): error TS2345: Argument of type '(request: NextRequest, _userInfo: DbRow, { params }: { params: Promise<{ id: string; }>; }) => Promise>>' is not assignable to parameter of type '(request: NextRequest, userInfo: UserInfo, context?: any) => Promise>'. - Types of parameters '_userInfo' and 'userInfo' are incompatible. - Type 'UserInfo' is not assignable to type 'DbRow'. - Index signature for type 'string' is missing in type 'UserInfo'. -src/app/api/dcprint/tool/[id]/usage/route.ts(24,3): error TS2345: Argument of type '(request: NextRequest, userInfo: DbRow, { params }: { params: Promise<{ id: string; }>; }) => Promise> | NextResponse>>' is not assignable to parameter of type '(request: NextRequest, userInfo: UserInfo, context?: any) => Promise>'. - Types of parameters 'userInfo' and 'userInfo' are incompatible. - Type 'UserInfo' is not assignable to type 'DbRow'. - Index signature for type 'string' is missing in type 'UserInfo'. -src/app/api/dcprint/tool/[id]/usage/route.ts(39,9): error TS2322: Type 'unknown' is not assignable to type 'number | undefined'. -src/app/api/dcprint/tool/[id]/usage/route.ts(40,9): error TS2322: Type 'unknown' is not assignable to type 'string | undefined'. -src/app/api/dcprint/trace/route.ts(106,5): error TS2345: Argument of type 'SqlValue[]' is not assignable to parameter of type 'DbRow[]'. - Type 'SqlValue' is not assignable to type 'DbRow'. - Type 'undefined' is not assignable to type 'DbRow'. -src/app/api/dcprint/trace/route.ts(113,29): error TS2345: Argument of type '(item: DbRow) => { id: unknown; traceNo: unknown; cardNo: unknown; workOrderNo: unknown; productCode: unknown; productName: unknown; mainMaterialCode: unknown; mainMaterialName: unknown; mainBatchNo: unknown; traceType: unknown; operatorName: unknown; traceTime: unknown; remark: unknown; }' is not assignable to parameter of type '(value: unknown[], index: number, array: unknown[][]) => { id: unknown; traceNo: unknown; cardNo: unknown; workOrderNo: unknown; productCode: unknown; productName: unknown; mainMaterialCode: unknown; mainMaterialName: unknown; mainBatchNo: unknown; traceType: unknown; operatorName: unknown; traceTime: unknown; remar...'. - Types of parameters 'item' and 'value' are incompatible. - Type 'unknown[]' is not assignable to type 'DbRow'. - Index signature for type 'string' is missing in type 'unknown[]'. -src/app/api/dcprint/trace/route.ts(186,13): error TS2339: Property 'id' does not exist on type '{}'. -src/app/api/dcprint/trace/route.ts(199,14): error TS2339: Property 'id' does not exist on type '{}'. -src/app/api/dcprint/trace/route.ts(200,14): error TS2339: Property 'card_no' does not exist on type '{}'. -src/app/api/dcprint/trace/route.ts(201,14): error TS2339: Property 'work_order_no' does not exist on type '{}'. -src/app/api/dcprint/trace/route.ts(202,14): error TS2339: Property 'product_code' does not exist on type '{}'. -src/app/api/dcprint/trace/route.ts(203,14): error TS2339: Property 'main_label_id' does not exist on type '{}'. -src/app/api/dcprint/trace/route.ts(243,24): error TS2339: Property 'card_no' does not exist on type '{}'. -src/app/api/dcprint/trace/route.ts(244,29): error TS2339: Property 'work_order_no' does not exist on type '{}'. -src/app/api/dcprint/trace/route.ts(245,29): error TS2339: Property 'product_code' does not exist on type '{}'. -src/app/api/dcprint/trace/route.ts(246,29): error TS2339: Property 'product_name' does not exist on type '{}'. -src/app/api/dcprint/trace/route.ts(249,25): error TS2339: Property 'main_label_no' does not exist on type '{}'. -src/app/api/dcprint/trace/route.ts(250,30): error TS2339: Property 'main_material_code' does not exist on type '{}'. -src/app/api/dcprint/trace/route.ts(251,30): error TS2339: Property 'main_material_name' does not exist on type '{}'. -src/app/api/dcprint/trace/route.ts(252,31): error TS2339: Property 'main_specification' does not exist on type '{}'. -src/app/api/dcprint/trace/route.ts(253,25): error TS2339: Property 'main_batch_no' does not exist on type '{}'. -src/app/api/dcprint/trace/route.ts(254,30): error TS2339: Property 'main_supplier_name' does not exist on type '{}'. -src/app/api/dcprint/trace/route.ts(255,29): error TS2339: Property 'main_receive_date' does not exist on type '{}'. -src/app/api/dcprint/trace/route.ts(256,33): error TS2339: Property 'main_purchase_order_no' does not exist on type '{}'. -src/app/api/dcprint/trace/route.ts(317,12): error TS2339: Property 'id' does not exist on type '{}'. -src/app/api/dcprint/trace/route.ts(339,50): error TS2322: Type 'DbRow' is not assignable to type 'SqlValue'. - Type 'DbRow' is missing the following properties from type 'Buffer': slice, subarray, write, toJSON, and 102 more. -src/app/api/dcprint/trace/route.ts(339,61): error TS2322: Type 'number | DbRow' is not assignable to type 'SqlValue'. - Type 'DbRow' is not assignable to type 'SqlValue'. - Type 'DbRow' is missing the following properties from type 'Buffer': slice, subarray, write, toJSON, and 102 more. -src/app/api/dcprint/trace/route.ts(339,71): error TS2322: Type 'number | DbRow' is not assignable to type 'SqlValue'. - Type 'DbRow' is not assignable to type 'SqlValue'. - Type 'DbRow' is missing the following properties from type 'Buffer': slice, subarray, write, toJSON, and 102 more. -src/app/api/dcprint/trace/route.ts(340,43): error TS2345: Argument of type 'DbRow[]' is not assignable to parameter of type 'SqlValue[]'. - Type 'DbRow' is not assignable to type 'SqlValue'. - Type 'DbRow' is missing the following properties from type 'Buffer': slice, subarray, write, toJSON, and 102 more. -src/app/api/delivery/vehicles/route.ts(65,27): error TS2322: Type 'SqlValue[]' is not assignable to type 'DbRow[]'. - Type 'SqlValue' is not assignable to type 'DbRow'. - Type 'undefined' is not assignable to type 'DbRow'. -src/app/api/delivery/vehicles/route.ts(98,63): error TS2345: Argument of type 'DbRow[]' is not assignable to parameter of type 'number | SqlValue[]'. - Type 'DbRow[]' is not assignable to type 'SqlValue[]'. - Type 'DbRow' is not assignable to type 'SqlValue'. - Type 'DbRow' is missing the following properties from type 'Buffer': slice, subarray, write, toJSON, and 102 more. -src/app/api/engineering/sample-to-mass/route.ts(137,20): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/engineering/sample-to-mass/route.ts(137,34): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/engineering/sample-to-mass/route.ts(147,50): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/engineering/sample-to-mass/route.ts(149,20): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/engineering/sample-to-mass/route.ts(149,34): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/engineering/sample-to-mass/route.ts(177,13): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/engineering/sample-to-mass/route.ts(180,22): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/engineering/sample-to-mass/route.ts(216,33): error TS2339: Property 'insertId' does not exist on type 'QueryResult'. - Property 'insertId' does not exist on type 'ResultSetHeader[]'. -src/app/api/engineering/sample-to-mass/route.ts(240,24): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/engineering/sample-to-mass/route.ts(244,24): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/engineering/sample-to-mass/route.ts(302,28): error TS2352: Conversion of type 'UserInfo' to type 'DbRow' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first. - Index signature for type 'string' is missing in type 'UserInfo'. -src/app/api/engineering/sample-to-mass/route.ts(302,61): error TS2352: Conversion of type 'UserInfo' to type 'DbRow' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first. - Index signature for type 'string' is missing in type 'UserInfo'. -src/app/api/finance/aging/route.ts(75,5): error TS2571: Object is of type 'unknown'. -src/app/api/finance/aging/route.ts(76,5): error TS2571: Object is of type 'unknown'. -src/app/api/finance/aging/route.ts(78,26): error TS2571: Object is of type 'unknown'. -src/app/api/finance/aging/route.ts(95,34): error TS18046: 'summary.total_amount' is of type 'unknown'. -src/app/api/finance/aging/route.ts(96,38): error TS18046: 'summary.remaining_amount' is of type 'unknown'. -src/app/api/finance/aging/route.ts(97,25): error TS18046: 'summary.buckets' is of type 'unknown'. -src/app/api/finance/aging/route.ts(98,41): error TS18046: 'summary.buckets' is of type 'unknown'. -src/app/api/finance/aging/route.ts(99,40): error TS18046: 'summary.buckets' is of type 'unknown'. -src/app/api/finance/cost-variance/route.ts(186,28): error TS2345: Argument of type 'unknown' is not assignable to parameter of type 'SqlValue'. -src/app/api/finance/invoice/route.ts(138,32): error TS2339: Property 'insertId' does not exist on type 'QueryResult'. - Property 'insertId' does not exist on type 'ResultSetHeader[]'. -src/app/api/finance/stats/route.ts(82,34): error TS2345: Argument of type '{}' is not assignable to parameter of type 'string'. -src/app/api/finance/stats/route.ts(83,36): error TS2345: Argument of type '{}' is not assignable to parameter of type 'string'. -src/app/api/finance/stats/route.ts(84,35): error TS2345: Argument of type '{}' is not assignable to parameter of type 'string'. -src/app/api/finance/stats/route.ts(85,36): error TS2345: Argument of type '{}' is not assignable to parameter of type 'string'. -src/app/api/finance/stats/route.ts(86,37): error TS2345: Argument of type '{}' is not assignable to parameter of type 'string'. -src/app/api/finance/stats/route.ts(87,37): error TS2345: Argument of type '{}' is not assignable to parameter of type 'string'. -src/app/api/finance/stats/route.ts(91,34): error TS2345: Argument of type '{}' is not assignable to parameter of type 'string'. -src/app/api/finance/stats/route.ts(92,32): error TS2345: Argument of type '{}' is not assignable to parameter of type 'string'. -src/app/api/finance/stats/route.ts(93,35): error TS2345: Argument of type '{}' is not assignable to parameter of type 'string'. -src/app/api/finance/stats/route.ts(94,36): error TS2345: Argument of type '{}' is not assignable to parameter of type 'string'. -src/app/api/finance/stats/route.ts(95,37): error TS2345: Argument of type '{}' is not assignable to parameter of type 'string'. -src/app/api/finance/stats/route.ts(96,37): error TS2345: Argument of type '{}' is not assignable to parameter of type 'string'. -src/app/api/init/business-seed/route.ts(215,23): error TS2488: Type 'QueryResult' must have a '[Symbol.iterator]()' method that returns an iterator. -src/app/api/init/business-seed/route.ts(243,23): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/business-seed/route.ts(744,23): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/business-seed/route.ts(969,11): error TS2322: Type 'QueryResult' is not assignable to type '{ id: number; material_name: string; }[]'. - Type 'OkPacket' is missing the following properties from type '{ id: number; material_name: string; }[]': length, pop, push, concat, and 35 more. -src/app/api/init/business-seed/route.ts(978,21): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/core-flow-seed/route.ts(88,11): error TS2322: Type 'QueryResult' is not assignable to type 'DbRow[]'. - Type 'OkPacket' is missing the following properties from type 'DbRow[]': length, pop, push, concat, and 35 more. -src/app/api/init/core-flow-seed/route.ts(92,11): error TS2322: Type 'QueryResult' is not assignable to type 'DbRow[]'. - Type 'OkPacket' is missing the following properties from type 'DbRow[]': length, pop, push, concat, and 35 more. -src/app/api/init/core-flow-seed/route.ts(96,32): error TS2339: Property 'find' does not exist on type 'QueryResult'. - Property 'find' does not exist on type 'OkPacket'. -src/app/api/init/core-flow-seed/route.ts(96,78): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/core-flow-seed/route.ts(97,29): error TS2339: Property 'find' does not exist on type 'QueryResult'. - Property 'find' does not exist on type 'OkPacket'. -src/app/api/init/core-flow-seed/route.ts(97,77): error TS7053: Element implicitly has an 'any' type because expression of type '1' can't be used to index type 'QueryResult'. - Property '1' does not exist on type 'QueryResult'. -src/app/api/init/core-flow-seed/route.ts(98,31): error TS2339: Property 'find' does not exist on type 'QueryResult'. - Property 'find' does not exist on type 'OkPacket'. -src/app/api/init/core-flow-seed/route.ts(98,81): error TS7053: Element implicitly has an 'any' type because expression of type '2' can't be used to index type 'QueryResult'. - Property '2' does not exist on type 'QueryResult'. -src/app/api/init/core-flow-seed/route.ts(99,29): error TS2339: Property 'find' does not exist on type 'QueryResult'. - Property 'find' does not exist on type 'OkPacket'. -src/app/api/init/core-flow-seed/route.ts(99,77): error TS7053: Element implicitly has an 'any' type because expression of type '3' can't be used to index type 'QueryResult'. - Property '3' does not exist on type 'QueryResult'. -src/app/api/init/core-flow-seed/route.ts(155,27): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/core-flow-seed/route.ts(219,18): error TS2769: No overload matches this call. - Overload 1 of 2, '(sql: string, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Type '{} | null' is not assignable to type 'ExecuteValues'. - Type '{}' is not assignable to type 'ExecuteValues'. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/init/core-flow-seed/route.ts(241,22): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/core-flow-seed/route.ts(243,13): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/core-flow-seed/route.ts(255,39): error TS2769: No overload matches this call. - Overload 1 of 2, '(predicate: (value: unknown, index: number, array: unknown[]) => value is unknown, thisArg?: any): unknown[]', gave the following error. - Argument of type '(m: DbRow) => boolean' is not assignable to parameter of type '(value: unknown, index: number, array: unknown[]) => value is unknown'. - Types of parameters 'm' and 'value' are incompatible. - Type 'unknown' is not assignable to type 'DbRow'. - Overload 2 of 2, '(predicate: (value: unknown, index: number, array: unknown[]) => unknown, thisArg?: any): unknown[]', gave the following error. - Argument of type '(m: DbRow) => boolean' is not assignable to parameter of type '(value: unknown, index: number, array: unknown[]) => unknown'. - Types of parameters 'm' and 'value' are incompatible. - Type 'unknown' is not assignable to type 'DbRow'. -src/app/api/init/core-flow-seed/route.ts(256,38): error TS2769: No overload matches this call. - Overload 1 of 2, '(predicate: (value: unknown, index: number, array: unknown[]) => value is unknown, thisArg?: any): unknown[]', gave the following error. - Argument of type '(m: DbRow) => boolean' is not assignable to parameter of type '(value: unknown, index: number, array: unknown[]) => value is unknown'. - Types of parameters 'm' and 'value' are incompatible. - Type 'unknown' is not assignable to type 'DbRow'. - Overload 2 of 2, '(predicate: (value: unknown, index: number, array: unknown[]) => unknown, thisArg?: any): unknown[]', gave the following error. - Argument of type '(m: DbRow) => boolean' is not assignable to parameter of type '(value: unknown, index: number, array: unknown[]) => unknown'. - Types of parameters 'm' and 'value' are incompatible. - Type 'unknown' is not assignable to type 'DbRow'. -src/app/api/init/core-flow-seed/route.ts(257,39): error TS2769: No overload matches this call. - Overload 1 of 2, '(predicate: (value: unknown, index: number, array: unknown[]) => value is unknown, thisArg?: any): unknown[]', gave the following error. - Argument of type '(m: DbRow) => boolean' is not assignable to parameter of type '(value: unknown, index: number, array: unknown[]) => value is unknown'. - Types of parameters 'm' and 'value' are incompatible. - Type 'unknown' is not assignable to type 'DbRow'. - Overload 2 of 2, '(predicate: (value: unknown, index: number, array: unknown[]) => unknown, thisArg?: any): unknown[]', gave the following error. - Argument of type '(m: DbRow) => boolean' is not assignable to parameter of type '(value: unknown, index: number, array: unknown[]) => unknown'. - Types of parameters 'm' and 'value' are incompatible. - Type 'unknown' is not assignable to type 'DbRow'. -src/app/api/init/core-flow-seed/route.ts(258,42): error TS2769: No overload matches this call. - Overload 1 of 2, '(predicate: (value: unknown, index: number, array: unknown[]) => value is unknown, thisArg?: any): unknown[]', gave the following error. - Argument of type '(m: DbRow) => boolean' is not assignable to parameter of type '(value: unknown, index: number, array: unknown[]) => value is unknown'. - Types of parameters 'm' and 'value' are incompatible. - Type 'unknown' is not assignable to type 'DbRow'. - Overload 2 of 2, '(predicate: (value: unknown, index: number, array: unknown[]) => unknown, thisArg?: any): unknown[]', gave the following error. - Argument of type '(m: DbRow) => boolean' is not assignable to parameter of type '(value: unknown, index: number, array: unknown[]) => unknown'. - Types of parameters 'm' and 'value' are incompatible. - Type 'unknown' is not assignable to type 'DbRow'. -src/app/api/init/core-flow-seed/route.ts(301,19): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/core-flow-seed/route.ts(333,19): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/core-flow-seed/route.ts(385,34): error TS2339: Property 'sale_price' does not exist on type '{}'. -src/app/api/init/core-flow-seed/route.ts(414,23): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/core-flow-seed/route.ts(421,20): error TS2339: Property 'id' does not exist on type '{}'. -src/app/api/init/core-flow-seed/route.ts(423,20): error TS2339: Property 'unit' does not exist on type '{}'. -src/app/api/init/core-flow-seed/route.ts(435,9): error TS2353: Object literal may only specify known properties, and 'id' does not exist in type 'Date | Buffer | readonly SqlValue[]'. -src/app/api/init/core-flow-seed/route.ts(453,43): error TS18049: 'od' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(453,46): error TS2339: Property 'orderDate' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'orderDate' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(474,11): error TS18049: 'od' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(474,14): error TS2339: Property 'id' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'id' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(475,11): error TS18049: 'od' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(475,14): error TS2339: Property 'orderNo' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'orderNo' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(476,11): error TS18049: 'od' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(476,14): error TS2339: Property 'customerName' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'customerName' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(477,11): error TS18049: 'od' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(477,14): error TS2339: Property 'productName' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'productName' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(478,11): error TS18049: 'od' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(478,14): error TS2339: Property 'qty' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'qty' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(490,18): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/core-flow-seed/route.ts(492,9): error TS2353: Object literal may only specify known properties, and 'id' does not exist in type 'Date | Buffer | readonly SqlValue[]'. -src/app/api/init/core-flow-seed/route.ts(492,13): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/core-flow-seed/route.ts(494,18): error TS18049: 'od' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(494,21): error TS2339: Property 'id' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'id' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(495,18): error TS18049: 'od' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(495,21): error TS2339: Property 'orderNo' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'orderNo' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(496,23): error TS18049: 'od' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(496,26): error TS2339: Property 'customerName' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'customerName' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(497,22): error TS18049: 'od' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(497,25): error TS2339: Property 'productName' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'productName' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(498,14): error TS18049: 'od' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(498,17): error TS2339: Property 'qty' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'qty' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(514,45): error TS18049: 'wo' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(514,48): error TS2339: Property 'planStart' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'planStart' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(518,59): error TS2339: Property 'purchase_price' does not exist on type '{}'. -src/app/api/init/core-flow-seed/route.ts(538,21): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/core-flow-seed/route.ts(546,20): error TS2339: Property 'id' does not exist on type '{}'. -src/app/api/init/core-flow-seed/route.ts(547,20): error TS2339: Property 'material_name' does not exist on type '{}'. -src/app/api/init/core-flow-seed/route.ts(548,20): error TS2339: Property 'specification' does not exist on type '{}'. -src/app/api/init/core-flow-seed/route.ts(551,20): error TS2339: Property 'unit' does not exist on type '{}'. -src/app/api/init/core-flow-seed/route.ts(552,20): error TS2339: Property 'purchase_price' does not exist on type '{}'. -src/app/api/init/core-flow-seed/route.ts(567,20): error TS2339: Property 'material_code' does not exist on type '{}'. -src/app/api/init/core-flow-seed/route.ts(568,20): error TS2339: Property 'material_name' does not exist on type '{}'. -src/app/api/init/core-flow-seed/route.ts(569,20): error TS2339: Property 'specification' does not exist on type '{}'. -src/app/api/init/core-flow-seed/route.ts(570,20): error TS2339: Property 'unit' does not exist on type '{}'. -src/app/api/init/core-flow-seed/route.ts(585,21): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/core-flow-seed/route.ts(587,9): error TS2353: Object literal may only specify known properties, and 'id' does not exist in type 'Date | Buffer | readonly SqlValue[]'. -src/app/api/init/core-flow-seed/route.ts(587,13): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/core-flow-seed/route.ts(589,32): error TS2339: Property 'material_code' does not exist on type '{}'. -src/app/api/init/core-flow-seed/route.ts(590,32): error TS2339: Property 'material_name' does not exist on type '{}'. -src/app/api/init/core-flow-seed/route.ts(609,21): error TS2339: Property 'material_code' does not exist on type '{}'. -src/app/api/init/core-flow-seed/route.ts(610,21): error TS2339: Property 'material_name' does not exist on type '{}'. -src/app/api/init/core-flow-seed/route.ts(611,21): error TS2339: Property 'specification' does not exist on type '{}'. -src/app/api/init/core-flow-seed/route.ts(612,21): error TS2339: Property 'unit' does not exist on type '{}'. -src/app/api/init/core-flow-seed/route.ts(624,23): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/core-flow-seed/route.ts(626,11): error TS2353: Object literal may only specify known properties, and 'id' does not exist in type 'Date | Buffer | readonly SqlValue[]'. -src/app/api/init/core-flow-seed/route.ts(626,15): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/core-flow-seed/route.ts(628,33): error TS2339: Property 'material_code' does not exist on type '{}'. -src/app/api/init/core-flow-seed/route.ts(629,33): error TS2339: Property 'material_name' does not exist on type '{}'. -src/app/api/init/core-flow-seed/route.ts(647,12): error TS18049: 'srcLabel' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(647,21): error TS2339: Property 'width' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'width' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(649,21): error TS18049: 'srcLabel' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(649,30): error TS2339: Property 'width' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'width' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(664,11): error TS18049: 'srcLabel' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(664,20): error TS2339: Property 'id' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'id' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(665,11): error TS18049: 'srcLabel' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(665,20): error TS2339: Property 'labelNo' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'labelNo' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(667,11): error TS18049: 'srcLabel' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(667,20): error TS2339: Property 'width' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'width' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(668,11): error TS18049: 'srcLabel' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(668,20): error TS2339: Property 'width' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'width' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(677,21): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/core-flow-seed/route.ts(680,31): error TS18049: 'srcLabel' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(680,40): error TS2339: Property 'labelNo' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'labelNo' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(685,13): error TS18049: 'srcLabel' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(685,22): error TS2339: Property 'labelNo' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'labelNo' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(686,13): error TS18049: 'srcLabel' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(686,22): error TS2339: Property 'supplierName' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'supplierName' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(687,13): error TS18049: 'srcLabel' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(687,22): error TS2339: Property 'receiveDate' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'receiveDate' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(688,13): error TS18049: 'srcLabel' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(688,22): error TS2339: Property 'materialCode' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'materialCode' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(689,13): error TS18049: 'srcLabel' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(689,22): error TS2339: Property 'materialName' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'materialName' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(692,13): error TS18049: 'srcLabel' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(692,22): error TS2339: Property 'batchNo' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'batchNo' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(693,25): error TS18049: 'srcLabel' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(693,34): error TS2339: Property 'qty' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'qty' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(693,56): error TS18049: 'srcLabel' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(693,65): error TS2339: Property 'width' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'width' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(695,13): error TS18049: 'srcLabel' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(695,22): error TS2339: Property 'warehouseId' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'warehouseId' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(699,13): error TS18049: 'srcLabel' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(699,22): error TS2339: Property 'id' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'id' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(706,30): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/core-flow-seed/route.ts(708,11): error TS2353: Object literal may only specify known properties, and 'id' does not exist in type 'Date | Buffer | readonly SqlValue[]'. -src/app/api/init/core-flow-seed/route.ts(708,15): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/core-flow-seed/route.ts(710,25): error TS18049: 'srcLabel' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(710,34): error TS2339: Property 'materialCode' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'materialCode' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(711,25): error TS18049: 'srcLabel' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(711,34): error TS2339: Property 'materialName' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'materialName' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(712,20): error TS18049: 'srcLabel' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(712,29): error TS2339: Property 'batchNo' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'batchNo' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(713,28): error TS18049: 'srcLabel' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(713,37): error TS2339: Property 'qty' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'qty' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(713,59): error TS18049: 'srcLabel' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(713,68): error TS2339: Property 'width' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'width' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(715,24): error TS18049: 'srcLabel' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(715,33): error TS2339: Property 'warehouseId' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'warehouseId' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(716,25): error TS18049: 'srcLabel' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(716,34): error TS2339: Property 'supplierName' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'supplierName' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(717,24): error TS18049: 'srcLabel' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(717,33): error TS2339: Property 'receiveDate' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'receiveDate' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(722,19): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/core-flow-seed/route.ts(735,67): error TS18049: 'wo' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(735,70): error TS2339: Property 'status' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'status' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(736,26): error TS18049: 'wo' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(736,29): error TS2339: Property 'status' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'status' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(742,11): error TS18049: 'wo' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(742,14): error TS2339: Property 'id' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'id' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(743,11): error TS18049: 'wo' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(743,14): error TS2339: Property 'woNo' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'woNo' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(744,11): error TS18049: 'wo' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(744,14): error TS2339: Property 'productName' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'productName' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(745,11): error TS18049: 'wo' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(745,14): error TS2339: Property 'productName' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'productName' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(746,48): error TS2339: Property 'specification' does not exist on type '{}'. -src/app/api/init/core-flow-seed/route.ts(747,11): error TS18049: 'wo' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(747,14): error TS2339: Property 'planStart' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'planStart' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(748,11): error TS18049: 'wo' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(748,14): error TS2339: Property 'qty' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'qty' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(749,11): error TS18049: 'mainLabel' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(749,21): error TS2339: Property 'id' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'id' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(750,11): error TS18049: 'mainLabel' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(750,21): error TS2339: Property 'labelNo' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'labelNo' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(758,22): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/core-flow-seed/route.ts(761,9): error TS2353: Object literal may only specify known properties, and 'id' does not exist in type 'Date | Buffer | readonly SqlValue[]'. -src/app/api/init/core-flow-seed/route.ts(763,15): error TS18049: 'wo' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(763,18): error TS2339: Property 'id' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'id' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(764,15): error TS18049: 'wo' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(764,18): error TS2339: Property 'woNo' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'woNo' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(765,22): error TS18049: 'wo' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(765,25): error TS2339: Property 'productName' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'productName' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(766,18): error TS18049: 'wo' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(766,21): error TS2339: Property 'qty' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'qty' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(767,22): error TS18049: 'mainLabel' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(767,32): error TS2339: Property 'id' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'id' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(768,22): error TS18049: 'mainLabel' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(768,32): error TS2339: Property 'labelNo' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'labelNo' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(778,11): error TS18049: 'mainLabel' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(778,21): error TS2339: Property 'id' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'id' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(779,11): error TS18049: 'mainLabel' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(779,21): error TS2339: Property 'labelNo' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'labelNo' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(781,11): error TS18049: 'mainLabel' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(781,21): error TS2339: Property 'materialCode' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'materialCode' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(782,11): error TS18049: 'mainLabel' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(782,21): error TS2339: Property 'materialName' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'materialName' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(783,48): error TS2339: Property 'specification' does not exist on type '{}'. -src/app/api/init/core-flow-seed/route.ts(784,11): error TS18049: 'mainLabel' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(784,21): error TS2339: Property 'batchNo' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'batchNo' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(785,11): error TS18049: 'mainLabel' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(785,21): error TS2339: Property 'qty' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'qty' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(792,11): error TS2769: No overload matches this call. - Overload 1 of 2, '(predicate: (value: SqlValue, index: number, obj: SqlValue[]) => value is SqlValue, thisArg?: any): SqlValue', gave the following error. - Argument of type '(l: DbRow) => any' is not assignable to parameter of type '(value: SqlValue, index: number, obj: SqlValue[]) => value is SqlValue'. - Types of parameters 'l' and 'value' are incompatible. - Type 'SqlValue' is not assignable to type 'DbRow'. - Type 'undefined' is not assignable to type 'DbRow'. - Overload 2 of 2, '(predicate: (value: SqlValue, index: number, obj: SqlValue[]) => unknown, thisArg?: any): SqlValue', gave the following error. - Argument of type '(l: DbRow) => any' is not assignable to parameter of type '(value: SqlValue, index: number, obj: SqlValue[]) => unknown'. - Types of parameters 'l' and 'value' are incompatible. - Type 'SqlValue' is not assignable to type 'DbRow'. - Type 'undefined' is not assignable to type 'DbRow'. -src/app/api/init/core-flow-seed/route.ts(792,41): error TS2339: Property 'startsWith' does not exist on type '{}'. -src/app/api/init/core-flow-seed/route.ts(792,66): error TS18046: 'l.id' is of type 'unknown'. -src/app/api/init/core-flow-seed/route.ts(792,73): error TS18049: 'mainLabel' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(792,83): error TS2339: Property 'id' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'id' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(800,24): error TS2339: Property 'id' does not exist on type 'string | number | true | Date | Buffer | readonly SqlValue[]'. - Property 'id' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(801,24): error TS2339: Property 'labelNo' does not exist on type 'string | number | true | Date | Buffer | readonly SqlValue[]'. - Property 'labelNo' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(803,24): error TS2339: Property 'materialCode' does not exist on type 'string | number | true | Date | Buffer | readonly SqlValue[]'. - Property 'materialCode' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(804,24): error TS2339: Property 'materialName' does not exist on type 'string | number | true | Date | Buffer | readonly SqlValue[]'. - Property 'materialName' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(805,50): error TS2339: Property 'specification' does not exist on type '{}'. -src/app/api/init/core-flow-seed/route.ts(806,24): error TS2339: Property 'batchNo' does not exist on type 'string | number | true | Date | Buffer | readonly SqlValue[]'. - Property 'batchNo' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(807,24): error TS2339: Property 'qty' does not exist on type 'string | number | true | Date | Buffer | readonly SqlValue[]'. - Property 'qty' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(824,11): error TS18049: 'label' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(824,17): error TS2339: Property 'labelNo' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'labelNo' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(825,11): error TS18049: 'label' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(825,17): error TS2339: Property 'labelNo' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'labelNo' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(826,11): error TS18049: 'card' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(826,16): error TS2339: Property 'cardNo' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'cardNo' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(843,35): error TS18049: 'mainLabel' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(843,45): error TS2339: Property 'qty' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'qty' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(849,11): error TS18049: 'wo' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(849,14): error TS2339: Property 'id' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'id' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(850,11): error TS18049: 'wo' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(850,14): error TS2339: Property 'woNo' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'woNo' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(861,20): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/core-flow-seed/route.ts(867,48): error TS2339: Property 'id' does not exist on type '{}'. -src/app/api/init/core-flow-seed/route.ts(868,11): error TS18049: 'mainLabel' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(868,21): error TS2339: Property 'materialCode' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'materialCode' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(869,11): error TS18049: 'mainLabel' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(869,21): error TS2339: Property 'materialName' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'materialName' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(870,11): error TS18049: 'mainLabel' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(870,21): error TS2339: Property 'qty' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'qty' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(873,11): error TS18049: 'mainLabel' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(873,21): error TS2339: Property 'batchNo' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'batchNo' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(878,41): error TS2769: No overload matches this call. - Overload 1 of 2, '(predicate: (value: SqlValue, index: number, obj: SqlValue[]) => value is SqlValue, thisArg?: any): SqlValue', gave the following error. - Argument of type '(l: DbRow) => any' is not assignable to parameter of type '(value: SqlValue, index: number, obj: SqlValue[]) => value is SqlValue'. - Types of parameters 'l' and 'value' are incompatible. - Type 'SqlValue' is not assignable to type 'DbRow'. - Type 'undefined' is not assignable to type 'DbRow'. - Overload 2 of 2, '(predicate: (value: SqlValue, index: number, obj: SqlValue[]) => unknown, thisArg?: any): SqlValue', gave the following error. - Argument of type '(l: DbRow) => any' is not assignable to parameter of type '(value: SqlValue, index: number, obj: SqlValue[]) => unknown'. - Types of parameters 'l' and 'value' are incompatible. - Type 'SqlValue' is not assignable to type 'DbRow'. - Type 'undefined' is not assignable to type 'DbRow'. -src/app/api/init/core-flow-seed/route.ts(878,71): error TS2339: Property 'startsWith' does not exist on type '{}'. -src/app/api/init/core-flow-seed/route.ts(884,50): error TS2339: Property 'id' does not exist on type '{}'. -src/app/api/init/core-flow-seed/route.ts(885,24): error TS2339: Property 'materialCode' does not exist on type 'string | number | true | Date | Buffer | readonly SqlValue[]'. - Property 'materialCode' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(886,24): error TS2339: Property 'materialName' does not exist on type 'string | number | true | Date | Buffer | readonly SqlValue[]'. - Property 'materialName' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(887,24): error TS2339: Property 'qty' does not exist on type 'string | number | true | Date | Buffer | readonly SqlValue[]'. - Property 'qty' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(888,35): error TS2339: Property 'qty' does not exist on type 'string | number | true | Date | Buffer | readonly SqlValue[]'. - Property 'qty' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(890,24): error TS2339: Property 'batchNo' does not exist on type 'string | number | true | Date | Buffer | readonly SqlValue[]'. - Property 'batchNo' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(903,34): error TS18049: 'wo' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(903,37): error TS2339: Property 'qty' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'qty' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(913,11): error TS18049: 'wo' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(913,14): error TS2339: Property 'id' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'id' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(914,11): error TS18049: 'wo' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(914,14): error TS2339: Property 'woNo' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'woNo' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(934,22): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/core-flow-seed/route.ts(943,34): error TS18049: 'wo' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(943,37): error TS2339: Property 'qty' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'qty' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(950,11): error TS18049: 'wo' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(950,14): error TS2339: Property 'id' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'id' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(951,11): error TS18049: 'wo' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(951,14): error TS2339: Property 'woNo' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'woNo' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(962,20): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/core-flow-seed/route.ts(969,20): error TS2339: Property 'id' does not exist on type '{}'. -src/app/api/init/core-flow-seed/route.ts(970,20): error TS2339: Property 'material_code' does not exist on type '{}'. -src/app/api/init/core-flow-seed/route.ts(971,20): error TS2339: Property 'material_name' does not exist on type '{}'. -src/app/api/init/core-flow-seed/route.ts(984,11): error TS18049: 'wo' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(984,14): error TS2339: Property 'id' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'id' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(985,11): error TS18049: 'wo' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(985,14): error TS2339: Property 'woNo' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'woNo' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(986,20): error TS2339: Property 'id' does not exist on type '{}'. -src/app/api/init/core-flow-seed/route.ts(987,20): error TS2339: Property 'material_code' does not exist on type '{}'. -src/app/api/init/core-flow-seed/route.ts(988,20): error TS2339: Property 'material_name' does not exist on type '{}'. -src/app/api/init/core-flow-seed/route.ts(1013,11): error TS18049: 'card' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(1013,16): error TS2339: Property 'id' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'id' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(1014,11): error TS18049: 'card' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(1014,16): error TS2339: Property 'cardNo' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'cardNo' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(1015,11): error TS18049: 'wo' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(1015,14): error TS2339: Property 'woNo' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'woNo' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(1016,11): error TS18049: 'wo' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(1016,14): error TS2339: Property 'productName' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'productName' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(1017,11): error TS18049: 'mainLabel' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(1017,21): error TS2339: Property 'id' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'id' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(1025,20): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/core-flow-seed/route.ts(1031,11): error TS18049: 'mainLabel' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(1031,21): error TS2339: Property 'id' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'id' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(1032,11): error TS18049: 'mainLabel' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(1032,21): error TS2339: Property 'labelNo' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'labelNo' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(1033,11): error TS18049: 'mainLabel' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(1033,21): error TS2339: Property 'materialCode' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'materialCode' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(1034,11): error TS18049: 'mainLabel' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(1034,21): error TS2339: Property 'materialName' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'materialName' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(1035,48): error TS2339: Property 'specification' does not exist on type '{}'. -src/app/api/init/core-flow-seed/route.ts(1036,11): error TS18049: 'mainLabel' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(1036,21): error TS2339: Property 'batchNo' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'batchNo' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(1037,11): error TS18049: 'mainLabel' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(1037,21): error TS2339: Property 'supplierName' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'supplierName' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(1038,11): error TS18049: 'mainLabel' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(1038,21): error TS2339: Property 'receiveDate' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'receiveDate' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(1043,39): error TS2769: No overload matches this call. - Overload 1 of 2, '(predicate: (value: SqlValue, index: number, obj: SqlValue[]) => value is SqlValue, thisArg?: any): SqlValue', gave the following error. - Argument of type '(l: DbRow) => any' is not assignable to parameter of type '(value: SqlValue, index: number, obj: SqlValue[]) => value is SqlValue'. - Types of parameters 'l' and 'value' are incompatible. - Type 'SqlValue' is not assignable to type 'DbRow'. - Type 'undefined' is not assignable to type 'DbRow'. - Overload 2 of 2, '(predicate: (value: SqlValue, index: number, obj: SqlValue[]) => unknown, thisArg?: any): SqlValue', gave the following error. - Argument of type '(l: DbRow) => any' is not assignable to parameter of type '(value: SqlValue, index: number, obj: SqlValue[]) => unknown'. - Types of parameters 'l' and 'value' are incompatible. - Type 'SqlValue' is not assignable to type 'DbRow'. - Type 'undefined' is not assignable to type 'DbRow'. -src/app/api/init/core-flow-seed/route.ts(1043,69): error TS2339: Property 'startsWith' does not exist on type '{}'. -src/app/api/init/core-flow-seed/route.ts(1049,22): error TS2339: Property 'id' does not exist on type 'string | number | true | Date | Buffer | readonly SqlValue[]'. - Property 'id' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(1050,22): error TS2339: Property 'labelNo' does not exist on type 'string | number | true | Date | Buffer | readonly SqlValue[]'. - Property 'labelNo' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(1051,22): error TS2339: Property 'materialCode' does not exist on type 'string | number | true | Date | Buffer | readonly SqlValue[]'. - Property 'materialCode' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(1052,22): error TS2339: Property 'materialName' does not exist on type 'string | number | true | Date | Buffer | readonly SqlValue[]'. - Property 'materialName' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(1053,48): error TS2339: Property 'specification' does not exist on type '{}'. -src/app/api/init/core-flow-seed/route.ts(1054,22): error TS2339: Property 'batchNo' does not exist on type 'string | number | true | Date | Buffer | readonly SqlValue[]'. - Property 'batchNo' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(1055,22): error TS2339: Property 'supplierName' does not exist on type 'string | number | true | Date | Buffer | readonly SqlValue[]'. - Property 'supplierName' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(1056,22): error TS2339: Property 'receiveDate' does not exist on type 'string | number | true | Date | Buffer | readonly SqlValue[]'. - Property 'receiveDate' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(1073,11): error TS18046: 'mat' is of type 'unknown'. -src/app/api/init/core-flow-seed/route.ts(1074,11): error TS18046: 'mat' is of type 'unknown'. -src/app/api/init/core-flow-seed/route.ts(1080,11): error TS18046: 'mat' is of type 'unknown'. -src/app/api/init/core-flow-seed/route.ts(1081,11): error TS18046: 'mat' is of type 'unknown'. -src/app/api/init/core-flow-seed/route.ts(1082,28): error TS18046: 'mat' is of type 'unknown'. -src/app/api/init/department/route.ts(94,48): error TS2345: Argument of type '(d: DbRow) => [unknown, unknown]' is not assignable to parameter of type '(value: unknown, index: number, array: unknown[]) => [unknown, unknown]'. - Types of parameters 'd' and 'value' are incompatible. - Type 'unknown' is not assignable to type 'DbRow'. -src/app/api/init/department/route.ts(99,26): error TS2769: No overload matches this call. - Overload 1 of 2, '(sql: string, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Type '{}' is not assignable to type 'ExecuteValues'. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/init/full-business-seed/route.ts(95,11): error TS2322: Type 'QueryResult' is not assignable to type 'DbRow[]'. - Type 'OkPacket' is missing the following properties from type 'DbRow[]': length, pop, push, concat, and 35 more. -src/app/api/init/full-business-seed/route.ts(100,11): error TS2322: Type 'QueryResult' is not assignable to type 'DbRow[]'. - Type 'OkPacket' is missing the following properties from type 'DbRow[]': length, pop, push, concat, and 35 more. -src/app/api/init/full-business-seed/route.ts(105,11): error TS2322: Type 'QueryResult' is not assignable to type 'DbRow[]'. - Type 'OkPacket' is missing the following properties from type 'DbRow[]': length, pop, push, concat, and 35 more. -src/app/api/init/full-business-seed/route.ts(110,11): error TS2322: Type 'QueryResult' is not assignable to type 'DbRow[]'. - Type 'OkPacket' is missing the following properties from type 'DbRow[]': length, pop, push, concat, and 35 more. -src/app/api/init/full-business-seed/route.ts(115,11): error TS2322: Type 'QueryResult' is not assignable to type 'DbRow[]'. - Type 'OkPacket' is missing the following properties from type 'DbRow[]': length, pop, push, concat, and 35 more. -src/app/api/init/full-business-seed/route.ts(120,11): error TS2322: Type 'QueryResult' is not assignable to type 'DbRow[]'. - Type 'OkPacket' is missing the following properties from type 'DbRow[]': length, pop, push, concat, and 35 more. -src/app/api/init/full-business-seed/route.ts(125,9): error TS2322: Type 'QueryResult' is not assignable to type 'DbRow[]'. - Type 'OkPacket' is missing the following properties from type 'DbRow[]': length, pop, push, concat, and 35 more. -src/app/api/init/full-business-seed/route.ts(167,7): error TS2322: Type 'QueryResult' is not assignable to type 'DbRow[]'. - Type 'OkPacket' is missing the following properties from type 'DbRow[]': length, pop, push, concat, and 35 more. -src/app/api/init/full-business-seed/route.ts(173,11): error TS2322: Type 'QueryResult' is not assignable to type 'DbRow[]'. - Type 'OkPacket' is missing the following properties from type 'DbRow[]': length, pop, push, concat, and 35 more. -src/app/api/init/full-business-seed/route.ts(279,9): error TS2769: No overload matches this call. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/init/full-business-seed/route.ts(300,23): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/full-business-seed/route.ts(325,9): error TS2769: No overload matches this call. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/init/full-business-seed/route.ts(369,9): error TS2769: No overload matches this call. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/init/full-business-seed/route.ts(398,9): error TS2769: No overload matches this call. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/init/full-business-seed/route.ts(428,9): error TS2769: No overload matches this call. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/init/full-business-seed/route.ts(446,24): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/full-business-seed/route.ts(464,9): error TS2769: No overload matches this call. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/init/full-business-seed/route.ts(573,23): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/full-business-seed/route.ts(579,26): error TS2339: Property 'includes' does not exist on type '{}'. -src/app/api/init/full-business-seed/route.ts(580,26): error TS2339: Property 'includes' does not exist on type '{}'. -src/app/api/init/full-business-seed/route.ts(581,26): error TS2339: Property 'includes' does not exist on type '{}'. -src/app/api/init/full-business-seed/route.ts(582,26): error TS2339: Property 'includes' does not exist on type '{}'. -src/app/api/init/full-business-seed/route.ts(597,9): error TS2769: No overload matches this call. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/init/full-business-seed/route.ts(630,9): error TS2769: No overload matches this call. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/init/full-business-seed/route.ts(662,9): error TS2769: No overload matches this call. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/init/full-business-seed/route.ts(697,9): error TS2769: No overload matches this call. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/init/full-business-seed/route.ts(726,18): error TS2769: No overload matches this call. - Overload 1 of 2, '(sql: string, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Type 'unknown' is not assignable to type 'ExecuteValues'. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/init/full-business-seed/route.ts(746,19): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/full-business-seed/route.ts(755,9): error TS2769: No overload matches this call. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/init/full-business-seed/route.ts(769,27): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/full-business-seed/route.ts(793,48): error TS2363: The right-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. -src/app/api/init/full-business-seed/route.ts(795,9): error TS2769: No overload matches this call. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/init/full-business-seed/route.ts(825,36): error TS2769: No overload matches this call. - Overload 1 of 2, '(sql: string, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Type 'unknown' is not assignable to type 'ExecuteValues'. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/init/full-business-seed/route.ts(828,24): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/full-business-seed/route.ts(860,24): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/full-business-seed/route.ts(869,44): error TS2363: The right-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. -src/app/api/init/full-business-seed/route.ts(871,9): error TS2769: No overload matches this call. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/init/full-business-seed/route.ts(903,36): error TS2769: No overload matches this call. - Overload 1 of 2, '(sql: string, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Type 'unknown' is not assignable to type 'ExecuteValues'. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/init/full-business-seed/route.ts(906,24): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/full-business-seed/route.ts(929,22): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/full-business-seed/route.ts(938,44): error TS2363: The right-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. -src/app/api/init/full-business-seed/route.ts(940,9): error TS2769: No overload matches this call. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/init/full-business-seed/route.ts(971,9): error TS2769: No overload matches this call. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/init/full-business-seed/route.ts(989,30): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/full-business-seed/route.ts(1000,18): error TS2769: No overload matches this call. - Overload 1 of 2, '(sql: string, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Type 'unknown' is not assignable to type 'ExecuteValues'. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/init/full-business-seed/route.ts(1013,9): error TS2769: No overload matches this call. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/init/full-business-seed/route.ts(1028,30): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/full-business-seed/route.ts(1036,9): error TS2769: No overload matches this call. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/init/full-business-seed/route.ts(1057,9): error TS2769: No overload matches this call. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/init/full-business-seed/route.ts(1094,9): error TS2769: No overload matches this call. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/init/full-business-seed/route.ts(1129,9): error TS2769: No overload matches this call. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/init/full-business-seed/route.ts(1160,9): error TS2769: No overload matches this call. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/init/full-business-seed/route.ts(1184,9): error TS2769: No overload matches this call. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/init/full-business-seed/route.ts(1199,24): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/full-business-seed/route.ts(1207,9): error TS2769: No overload matches this call. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/init/full-business-seed/route.ts(1226,9): error TS2769: No overload matches this call. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/init/full-business-seed/route.ts(1240,27): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/full-business-seed/route.ts(1251,9): error TS2769: No overload matches this call. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/init/full-business-seed/route.ts(1272,9): error TS2769: No overload matches this call. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/init/full-business-seed/route.ts(1286,22): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/full-business-seed/route.ts(1297,9): error TS2769: No overload matches this call. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/init/full-business-seed/route.ts(1316,36): error TS2769: No overload matches this call. - Overload 1 of 2, '(sql: string, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Type 'unknown' is not assignable to type 'ExecuteValues'. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/init/full-business-seed/route.ts(1319,24): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/full-business-seed/route.ts(1338,24): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/full-business-seed/route.ts(1346,9): error TS2769: No overload matches this call. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/init/full-business-seed/route.ts(1374,9): error TS2769: No overload matches this call. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/init/full-business-seed/route.ts(1396,18): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/full-business-seed/route.ts(1406,44): error TS2363: The right-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. -src/app/api/init/full-business-seed/route.ts(1412,9): error TS2769: No overload matches this call. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/init/full-business-seed/route.ts(1445,9): error TS2769: No overload matches this call. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/init/full-business-seed/route.ts(1462,23): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/full-business-seed/route.ts(1472,44): error TS2363: The right-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. -src/app/api/init/full-business-seed/route.ts(1475,9): error TS2769: No overload matches this call. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/init/full-business-seed/route.ts(1502,9): error TS2769: No overload matches this call. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/init/full-business-seed/route.ts(1521,26): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/full-business-seed/route.ts(1543,9): error TS2769: No overload matches this call. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/init/full-business-seed/route.ts(1570,9): error TS2769: No overload matches this call. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/init/full-business-seed/route.ts(1607,9): error TS2769: No overload matches this call. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/init/full-business-seed/route.ts(1648,9): error TS2769: No overload matches this call. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/init/full-business-seed/route.ts(1688,9): error TS2769: No overload matches this call. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/init/full-business-seed/route.ts(1720,9): error TS2769: No overload matches this call. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/init/full-business-seed/route.ts(1760,9): error TS2769: No overload matches this call. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/init/full-business-seed/route.ts(1785,9): error TS2769: No overload matches this call. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/init/full-business-seed/route.ts(1836,18): error TS2769: No overload matches this call. - Overload 1 of 2, '(sql: string, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Type 'unknown' is not assignable to type 'ExecuteValues'. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/init/full-business-seed/route.ts(1852,24): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/full-business-seed/route.ts(1861,9): error TS2769: No overload matches this call. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/init/full-seed/route.ts(88,44): error TS2345: Argument of type 'PoolConnection' is not assignable to parameter of type 'DbConnection'. - Types of property 'execute' are incompatible. - Type '{ (sql: string, values?: ExecuteValues | undefined): Promise<[T, FieldPacket[]]>; (options: QueryOptions, values?: ExecuteValues | undefined): Promise<...>; }' is not assignable to type '(sql: string, values?: unknown[] | undefined) => Promise>'. - Types of parameters 'values' and 'values' are incompatible. - Type 'unknown[] | undefined' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues[]'. - Type 'unknown' is not assignable to type 'ExecuteValues'. -src/app/api/init/full-seed/route.ts(90,100): error TS2345: Argument of type 'PoolConnection' is not assignable to parameter of type 'DbConnection'. - Types of property 'execute' are incompatible. - Type '{ (sql: string, values?: ExecuteValues | undefined): Promise<[T, FieldPacket[]]>; (options: QueryOptions, values?: ExecuteValues | undefined): Promise<...>; }' is not assignable to type '(sql: string, values?: unknown[] | undefined) => Promise>'. - Types of parameters 'values' and 'values' are incompatible. - Type 'unknown[] | undefined' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues[]'. - Type 'unknown' is not assignable to type 'ExecuteValues'. -src/app/api/init/full-seed/route.ts(92,30): error TS2345: Argument of type 'PoolConnection' is not assignable to parameter of type 'DbConnection'. - Types of property 'execute' are incompatible. - Type '{ (sql: string, values?: ExecuteValues | undefined): Promise<[T, FieldPacket[]]>; (options: QueryOptions, values?: ExecuteValues | undefined): Promise<...>; }' is not assignable to type '(sql: string, values?: unknown[] | undefined) => Promise>'. - Types of parameters 'values' and 'values' are incompatible. - Type 'unknown[] | undefined' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues[]'. - Type 'unknown' is not assignable to type 'ExecuteValues'. -src/app/api/init/full-seed/route.ts(93,29): error TS2345: Argument of type 'PoolConnection' is not assignable to parameter of type 'DbConnection'. - Types of property 'execute' are incompatible. - Type '{ (sql: string, values?: ExecuteValues | undefined): Promise<[T, FieldPacket[]]>; (options: QueryOptions, values?: ExecuteValues | undefined): Promise<...>; }' is not assignable to type '(sql: string, values?: unknown[] | undefined) => Promise>'. - Types of parameters 'values' and 'values' are incompatible. - Type 'unknown[] | undefined' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues[]'. - Type 'unknown' is not assignable to type 'ExecuteValues'. -src/app/api/init/full-seed/route.ts(94,27): error TS2345: Argument of type 'PoolConnection' is not assignable to parameter of type 'DbConnection'. - Types of property 'execute' are incompatible. - Type '{ (sql: string, values?: ExecuteValues | undefined): Promise<[T, FieldPacket[]]>; (options: QueryOptions, values?: ExecuteValues | undefined): Promise<...>; }' is not assignable to type '(sql: string, values?: unknown[] | undefined) => Promise>'. - Types of parameters 'values' and 'values' are incompatible. - Type 'unknown[] | undefined' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues[]'. - Type 'unknown' is not assignable to type 'ExecuteValues'. -src/app/api/init/full-seed/route.ts(95,29): error TS2345: Argument of type 'PoolConnection' is not assignable to parameter of type 'DbConnection'. - Types of property 'execute' are incompatible. - Type '{ (sql: string, values?: ExecuteValues | undefined): Promise<[T, FieldPacket[]]>; (options: QueryOptions, values?: ExecuteValues | undefined): Promise<...>; }' is not assignable to type '(sql: string, values?: unknown[] | undefined) => Promise>'. - Types of parameters 'values' and 'values' are incompatible. - Type 'unknown[] | undefined' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues[]'. - Type 'unknown' is not assignable to type 'ExecuteValues'. -src/app/api/init/full-seed/route.ts(96,31): error TS2345: Argument of type 'PoolConnection' is not assignable to parameter of type 'DbConnection'. - Types of property 'execute' are incompatible. - Type '{ (sql: string, values?: ExecuteValues | undefined): Promise<[T, FieldPacket[]]>; (options: QueryOptions, values?: ExecuteValues | undefined): Promise<...>; }' is not assignable to type '(sql: string, values?: unknown[] | undefined) => Promise>'. - Types of parameters 'values' and 'values' are incompatible. - Type 'unknown[] | undefined' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues[]'. - Type 'unknown' is not assignable to type 'ExecuteValues'. -src/app/api/init/full-seed/route.ts(97,36): error TS2345: Argument of type 'PoolConnection' is not assignable to parameter of type 'DbConnection'. - Types of property 'execute' are incompatible. - Type '{ (sql: string, values?: ExecuteValues | undefined): Promise<[T, FieldPacket[]]>; (options: QueryOptions, values?: ExecuteValues | undefined): Promise<...>; }' is not assignable to type '(sql: string, values?: unknown[] | undefined) => Promise>'. - Types of parameters 'values' and 'values' are incompatible. - Type 'unknown[] | undefined' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues[]'. - Type 'unknown' is not assignable to type 'ExecuteValues'. -src/app/api/init/menus/route.ts(34,34): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/menus/route.ts(35,32): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/menus/route.ts(111,34): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/menus/route.ts(111,65): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/menus/route.ts(112,26): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/menus/route.ts(113,24): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/menus/route.ts(130,41): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/menus/route.ts(130,74): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/menus/route.ts(135,15): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/menus/route.ts(159,24): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/menus/route.ts(160,78): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/menus/route.ts(161,68): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/menus/route.ts(178,67): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/menus/route.ts(185,30): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/menus/route.ts(186,23): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/menus/route.ts(191,36): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/menus/route.ts(194,15): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/menus/route.ts(1397,37): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/menus/route.ts(1399,26): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/menus/route.ts(1404,34): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/menus/route.ts(1469,26): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/menus/route.ts(1470,29): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/menus/route.ts(1478,37): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/migrate/route.ts(92,32): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/migrate/route.ts(162,26): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/migrate/route.ts(188,32): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/quality-final-seed/route.ts(433,25): error TS2488: Type 'QueryResult' must have a '[Symbol.iterator]()' method that returns an iterator. -src/app/api/init/quality-final-seed/route.ts(743,13): error TS2322: Type 'QueryResult' is not assignable to type 'DbRow[]'. - Type 'OkPacket' is missing the following properties from type 'DbRow[]': length, pop, push, concat, and 35 more. -src/app/api/init/quality-final-seed/route.ts(766,11): error TS2769: No overload matches this call. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/init/seed-data/route.ts(12,14): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(59,16): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(60,17): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(61,18): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(62,17): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(69,16): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(74,16): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(75,18): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(93,19): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(93,65): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(116,64): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(118,20): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(118,64): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(119,24): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(119,70): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(139,19): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(139,65): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(168,19): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(168,65): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(187,70): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(189,19): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(189,63): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(210,72): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(212,19): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(212,63): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(235,19): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(235,65): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(258,19): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(258,65): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(296,19): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(296,65): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(375,53): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(376,23): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(391,20): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(391,48): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(408,19): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(408,67): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(409,21): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(409,67): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(436,19): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(436,65): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(437,19): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(437,67): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(456,19): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(456,67): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(457,24): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(457,72): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(470,22): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(471,19): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(471,65): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(491,19): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(491,67): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(492,19): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(492,65): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(507,22): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(508,19): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(508,65): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(528,19): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(528,67): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(534,23): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(535,19): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(535,65): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(557,19): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(557,67): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(563,22): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(564,19): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(564,65): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(565,21): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(565,67): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(588,24): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(588,72): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(589,22): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(589,64): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(602,22): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(603,19): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(603,65): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(625,24): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(627,22): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(627,58): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(628,21): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(628,67): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(629,21): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(629,67): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(647,45): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(652,19): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(652,63): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(653,19): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(653,65): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(701,19): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(701,63): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(735,19): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(735,63): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(764,19): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(764,63): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(788,19): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(788,65): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(789,19): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(789,67): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(790,21): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(790,67): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(817,19): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(817,65): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(855,19): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(855,67): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(856,24): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(856,72): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(870,19): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(870,65): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(874,13): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(891,19): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(891,67): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(892,24): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(892,72): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(905,19): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(905,65): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(909,13): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(927,11): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(929,21): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(929,67): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(930,26): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(930,74): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(957,11): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(964,26): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(964,54): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(965,57): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(966,23): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(966,63): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(967,24): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(967,56): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(971,17): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(972,17): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(985,71): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(988,43): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(995,19): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(995,65): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(996,19): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(996,67): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(997,21): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(997,67): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(1025,24): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(1025,72): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(1026,19): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(1026,65): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(1027,20): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(1027,64): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(1039,19): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(1039,65): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(1054,19): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(1054,65): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(1060,13): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(1078,19): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(1078,65): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(1095,13): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(1109,12): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(1118,19): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(1118,65): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(1153,19): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(1153,65): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(1154,19): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(1154,65): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(1181,14): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(1191,24): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(1191,72): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(1192,19): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(1192,65): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(1230,24): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(1230,72): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(1231,19): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(1231,65): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(1261,19): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(1261,65): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(1266,32): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(1267,17): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(1267,65): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/settings-seed/route.ts(373,22): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/settings-seed/route.ts(1350,23): error TS2339: Property 'filter' does not exist on type 'QueryResult'. - Property 'filter' does not exist on type 'OkPacket'. -src/app/api/init/settings-seed/route.ts(1373,29): error TS2339: Property 'map' does not exist on type 'QueryResult'. - Property 'map' does not exist on type 'OkPacket'. -src/app/api/init/supplement-tables/route.ts(1051,20): error TS2339: Property 'cnt' does not exist on type 'QueryResult'. - Property 'cnt' does not exist on type 'OkPacket'. -src/app/api/init/supplement-tables/route.ts(1100,25): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/ink-usages/route.ts(131,35): error TS2352: Conversion of type 'ResultSetHeader' to type 'DbRow' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first. - Index signature for type 'string' is missing in type 'ResultSetHeader'. -src/app/api/inventory/route.ts(122,20): error TS2345: Argument of type 'unknown' is not assignable to parameter of type 'string'. -src/app/api/inventory/route.ts(123,40): error TS2345: Argument of type '{}' is not assignable to parameter of type 'string'. -src/app/api/inventory/route.ts(183,22): error TS2339: Property 'material_name' does not exist on type '{}'. -src/app/api/inventory/route.ts(185,23): error TS2339: Property 'warehouse_name' does not exist on type '{}'. -src/app/api/inventory/route.ts(188,22): error TS2339: Property 'unit' does not exist on type '{}'. -src/app/api/inventory/route.ts(189,22): error TS2339: Property 'purchase_price' does not exist on type '{}'. -src/app/api/inventory/route.ts(194,28): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/inventory/route.ts(196,42): error TS2345: Argument of type 'PoolConnection' is not assignable to parameter of type 'DbConnection'. - Types of property 'execute' are incompatible. - Type '{ (sql: string, values?: ExecuteValues | undefined): Promise<[T, FieldPacket[]]>; (options: QueryOptions, values?: ExecuteValues | undefined): Promise<...>; }' is not assignable to type '(sql: string, values?: unknown[] | undefined) => Promise>'. - Types of parameters 'values' and 'values' are incompatible. - Type 'unknown[] | undefined' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues[]'. - Type 'unknown' is not assignable to type 'ExecuteValues'. -src/app/api/inventory/route.ts(204,31): error TS2339: Property 'purchase_price' does not exist on type '{}'. -src/app/api/inventory/route.ts(205,45): error TS2339: Property 'purchase_price' does not exist on type '{}'. -src/app/api/inventory/route.ts(212,41): error TS2345: Argument of type 'PoolConnection' is not assignable to parameter of type 'DbConnection'. - Types of property 'execute' are incompatible. - Type '{ (sql: string, values?: ExecuteValues | undefined): Promise<[T, FieldPacket[]]>; (options: QueryOptions, values?: ExecuteValues | undefined): Promise<...>; }' is not assignable to type '(sql: string, values?: unknown[] | undefined) => Promise>'. - Types of parameters 'values' and 'values' are incompatible. - Type 'unknown[] | undefined' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues[]'. - Type 'unknown' is not assignable to type 'ExecuteValues'. -src/app/api/inventory/route.ts(214,34): error TS2345: Argument of type 'PoolConnection' is not assignable to parameter of type 'DbConnection'. - Types of property 'execute' are incompatible. - Type '{ (sql: string, values?: ExecuteValues | undefined): Promise<[T, FieldPacket[]]>; (options: QueryOptions, values?: ExecuteValues | undefined): Promise<...>; }' is not assignable to type '(sql: string, values?: unknown[] | undefined) => Promise>'. - Types of parameters 'values' and 'values' are incompatible. - Type 'unknown[] | undefined' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues[]'. - Type 'unknown' is not assignable to type 'ExecuteValues'. -src/app/api/inventory/route.ts(222,26): error TS2339: Property 'unit' does not exist on type '{}'. -src/app/api/inventory/route.ts(290,41): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/inventory/route.ts(294,27): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/inventory/route.ts(314,51): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/inventory/route.ts(314,65): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/inventory/route.ts(324,30): error TS2339: Property 'affectedRows' does not exist on type 'QueryResult'. - Property 'affectedRows' does not exist on type 'ResultSetHeader[]'. -src/app/api/inventory/route.ts(328,46): error TS2345: Argument of type 'PoolConnection' is not assignable to parameter of type 'DbConnection'. - Types of property 'execute' are incompatible. - Type '{ (sql: string, values?: ExecuteValues | undefined): Promise<[T, FieldPacket[]]>; (options: QueryOptions, values?: ExecuteValues | undefined): Promise<...>; }' is not assignable to type '(sql: string, values?: unknown[] | undefined) => Promise>'. - Types of parameters 'values' and 'values' are incompatible. - Type 'unknown[] | undefined' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues[]'. - Type 'unknown' is not assignable to type 'ExecuteValues'. -src/app/api/inventory/route.ts(346,45): error TS2345: Argument of type 'PoolConnection' is not assignable to parameter of type 'DbConnection'. - Types of property 'execute' are incompatible. - Type '{ (sql: string, values?: ExecuteValues | undefined): Promise<[T, FieldPacket[]]>; (options: QueryOptions, values?: ExecuteValues | undefined): Promise<...>; }' is not assignable to type '(sql: string, values?: unknown[] | undefined) => Promise>'. - Types of parameters 'values' and 'values' are incompatible. - Type 'unknown[] | undefined' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues[]'. - Type 'unknown' is not assignable to type 'ExecuteValues'. -src/app/api/inventory/route.ts(348,38): error TS2345: Argument of type 'PoolConnection' is not assignable to parameter of type 'DbConnection'. - Types of property 'execute' are incompatible. - Type '{ (sql: string, values?: ExecuteValues | undefined): Promise<[T, FieldPacket[]]>; (options: QueryOptions, values?: ExecuteValues | undefined): Promise<...>; }' is not assignable to type '(sql: string, values?: unknown[] | undefined) => Promise>'. - Types of parameters 'values' and 'values' are incompatible. - Type 'unknown[] | undefined' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues[]'. - Type 'unknown' is not assignable to type 'ExecuteValues'. -src/app/api/inventory/route.ts(441,51): error TS2345: Argument of type 'PoolConnection' is not assignable to parameter of type 'DbConnection'. - Types of property 'execute' are incompatible. - Type '{ (sql: string, values?: ExecuteValues | undefined): Promise<[T, FieldPacket[]]>; (options: QueryOptions, values?: ExecuteValues | undefined): Promise<...>; }' is not assignable to type '(sql: string, values?: unknown[] | undefined) => Promise>'. - Types of parameters 'values' and 'values' are incompatible. - Type 'unknown[] | undefined' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues[]'. - Type 'unknown' is not assignable to type 'ExecuteValues'. -src/app/api/inventory/route.ts(445,32): error TS2339: Property 'material_name' does not exist on type '{}'. -src/app/api/inventory/route.ts(451,15): error TS2345: Argument of type 'PoolConnection' is not assignable to parameter of type 'DbConnection'. - Types of property 'execute' are incompatible. - Type '{ (sql: string, values?: ExecuteValues | undefined): Promise<[T, FieldPacket[]]>; (options: QueryOptions, values?: ExecuteValues | undefined): Promise<...>; }' is not assignable to type '(sql: string, values?: unknown[] | undefined) => Promise>'. - Types of parameters 'values' and 'values' are incompatible. - Type 'unknown[] | undefined' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues[]'. - Type 'unknown' is not assignable to type 'ExecuteValues'. -src/app/api/inventory/route.ts(458,42): error TS2339: Property 'warehouse_code' does not exist on type '{}'. -src/app/api/inventory/route.ts(465,40): error TS2345: Argument of type 'PoolConnection' is not assignable to parameter of type 'DbConnection'. - Types of property 'execute' are incompatible. - Type '{ (sql: string, values?: ExecuteValues | undefined): Promise<[T, FieldPacket[]]>; (options: QueryOptions, values?: ExecuteValues | undefined): Promise<...>; }' is not assignable to type '(sql: string, values?: unknown[] | undefined) => Promise>'. - Types of parameters 'values' and 'values' are incompatible. - Type 'unknown[] | undefined' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues[]'. - Type 'unknown' is not assignable to type 'ExecuteValues'. -src/app/api/inventory/route.ts(472,27): error TS2362: The left-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. -src/app/api/inventory/route.ts(472,64): error TS18046: 'detail.deducted_qty' is of type 'unknown'. -src/app/api/inventory/route.ts(473,32): error TS2339: Property 'unit' does not exist on type '{}'. -src/app/api/inventory/route.ts(481,48): error TS2345: Argument of type 'PoolConnection' is not assignable to parameter of type 'DbConnection'. - Types of property 'execute' are incompatible. - Type '{ (sql: string, values?: ExecuteValues | undefined): Promise<[T, FieldPacket[]]>; (options: QueryOptions, values?: ExecuteValues | undefined): Promise<...>; }' is not assignable to type '(sql: string, values?: unknown[] | undefined) => Promise>'. - Types of parameters 'values' and 'values' are incompatible. - Type 'unknown[] | undefined' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues[]'. - Type 'unknown' is not assignable to type 'ExecuteValues'. -src/app/api/inventory/route.ts(489,39): error TS2345: Argument of type 'unknown' is not assignable to parameter of type 'string'. -src/app/api/inventory/route.ts(490,41): error TS2345: Argument of type 'unknown' is not assignable to parameter of type 'string'. -src/app/api/inventory/route.ts(498,45): error TS2345: Argument of type 'PoolConnection' is not assignable to parameter of type 'DbConnection'. - Types of property 'execute' are incompatible. - Type '{ (sql: string, values?: ExecuteValues | undefined): Promise<[T, FieldPacket[]]>; (options: QueryOptions, values?: ExecuteValues | undefined): Promise<...>; }' is not assignable to type '(sql: string, values?: unknown[] | undefined) => Promise>'. - Types of parameters 'values' and 'values' are incompatible. - Type 'unknown[] | undefined' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues[]'. - Type 'unknown' is not assignable to type 'ExecuteValues'. -src/app/api/inventory/route.ts(561,39): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/inventory/route.ts(565,25): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/inventory/route.ts(572,47): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/inventory/route.ts(576,35): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/inventory/route.ts(585,28): error TS2339: Property 'affectedRows' does not exist on type 'QueryResult'. - Property 'affectedRows' does not exist on type 'ResultSetHeader[]'. -src/app/api/inventory/route.ts(589,44): error TS2345: Argument of type 'PoolConnection' is not assignable to parameter of type 'DbConnection'. - Types of property 'execute' are incompatible. - Type '{ (sql: string, values?: ExecuteValues | undefined): Promise<[T, FieldPacket[]]>; (options: QueryOptions, values?: ExecuteValues | undefined): Promise<...>; }' is not assignable to type '(sql: string, values?: unknown[] | undefined) => Promise>'. - Types of parameters 'values' and 'values' are incompatible. - Type 'unknown[] | undefined' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues[]'. - Type 'unknown' is not assignable to type 'ExecuteValues'. -src/app/api/inventory/route.ts(605,43): error TS2345: Argument of type 'PoolConnection' is not assignable to parameter of type 'DbConnection'. - Types of property 'execute' are incompatible. - Type '{ (sql: string, values?: ExecuteValues | undefined): Promise<[T, FieldPacket[]]>; (options: QueryOptions, values?: ExecuteValues | undefined): Promise<...>; }' is not assignable to type '(sql: string, values?: unknown[] | undefined) => Promise>'. - Types of parameters 'values' and 'values' are incompatible. - Type 'unknown[] | undefined' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues[]'. - Type 'unknown' is not assignable to type 'ExecuteValues'. -src/app/api/inventory/route.ts(606,43): error TS2345: Argument of type 'PoolConnection' is not assignable to parameter of type 'DbConnection'. - Types of property 'execute' are incompatible. - Type '{ (sql: string, values?: ExecuteValues | undefined): Promise<[T, FieldPacket[]]>; (options: QueryOptions, values?: ExecuteValues | undefined): Promise<...>; }' is not assignable to type '(sql: string, values?: unknown[] | undefined) => Promise>'. - Types of parameters 'values' and 'values' are incompatible. - Type 'unknown[] | undefined' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues[]'. - Type 'unknown' is not assignable to type 'ExecuteValues'. -src/app/api/inventory/route.ts(608,36): error TS2345: Argument of type 'PoolConnection' is not assignable to parameter of type 'DbConnection'. - Types of property 'execute' are incompatible. - Type '{ (sql: string, values?: ExecuteValues | undefined): Promise<[T, FieldPacket[]]>; (options: QueryOptions, values?: ExecuteValues | undefined): Promise<...>; }' is not assignable to type '(sql: string, values?: unknown[] | undefined) => Promise>'. - Types of parameters 'values' and 'values' are incompatible. - Type 'unknown[] | undefined' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues[]'. - Type 'unknown' is not assignable to type 'ExecuteValues'. -src/app/api/linkage/validate/route.ts(120,19): error TS2362: The left-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. -src/app/api/linkage/validate/route.ts(120,35): error TS2362: The left-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. -src/app/api/linkage/validate/route.ts(127,14): error TS2365: Operator '>' cannot be applied to types '{}' and 'number'. -src/app/api/linkage/validate/route.ts(131,14): error TS2365: Operator '>' cannot be applied to types '{}' and 'number'. -src/app/api/linkage/validate/route.ts(136,7): error TS18046: 'newStatus' is of type 'unknown'. -src/app/api/linkage/validate/route.ts(136,19): error TS18046: 'oldStatus' is of type 'unknown'. -src/app/api/linkage/validate/route.ts(138,7): error TS2322: Type 'unknown' is not assignable to type 'SqlValue'. -src/app/api/linkage/validate/route.ts(142,47): error TS2345: Argument of type 'unknown' is not assignable to parameter of type 'string'. -src/app/api/linkage/validate/route.ts(183,11): error TS18046: 'orderLine.status' is of type 'unknown'. -src/app/api/linkage/validate/route.ts(187,11): error TS18046: 'orderLine.status' is of type 'unknown'. -src/app/api/linkage/validate/route.ts(197,22): error TS18046: 'orderLine.req_qty' is of type 'unknown'. -src/app/api/linkage/validate/route.ts(197,47): error TS2362: The left-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. -src/app/api/linkage/validate/route.ts(198,37): error TS18046: 'orderLine.ordered_qty' is of type 'unknown'. -src/app/api/linkage/validate/route.ts(269,13): error TS2769: No overload matches this call. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/linkage/validate/route.ts(276,29): error TS2345: Argument of type 'unknown' is not assignable to parameter of type 'number'. -src/app/api/linkage/validate/route.ts(309,13): error TS2769: No overload matches this call. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/linkage/validate/route.ts(319,13): error TS2769: No overload matches this call. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/linkage/validate/route.ts(326,29): error TS2345: Argument of type 'unknown' is not assignable to parameter of type 'number'. -src/app/api/linkage/validate/route.ts(364,21): error TS18046: 'orderLine.available_to_receive' is of type 'unknown'. -src/app/api/linkage/validate/route.ts(406,51): error TS2345: Argument of type 'unknown' is not assignable to parameter of type 'number'. -src/app/api/linkage/validate/route.ts(467,25): error TS2362: The left-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. -src/app/api/linkage/validate/route.ts(467,40): error TS2363: The right-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. -src/app/api/linkage/validate/route.ts(468,26): error TS2362: The left-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. -src/app/api/linkage/validate/route.ts(468,42): error TS2363: The right-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. -src/app/api/linkage/validate/route.ts(469,29): error TS2365: Operator '>' cannot be applied to types '{}' and 'number'. -src/app/api/linkage/validate/route.ts(469,46): error TS2362: The left-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. -src/app/api/linkage/validate/route.ts(469,62): error TS2363: The right-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. -src/app/api/material-labels/route.ts(176,33): error TS2352: Conversion of type 'ResultSetHeader' to type 'DbRow' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first. - Index signature for type 'string' is missing in type 'ResultSetHeader'. -src/app/api/material-labels/route.ts(190,6): error TS2322: Type '{}' is not assignable to type 'SqlValue'. -src/app/api/material-labels/route.ts(205,23): error TS18046: 'parent.width' is of type 'unknown'. -src/app/api/material-labels/route.ts(213,30): error TS18046: 'parent.width' is of type 'unknown'. -src/app/api/material-labels/route.ts(214,36): error TS18046: 'parent.quantity' is of type 'unknown'. -src/app/api/material-labels/route.ts(235,30): error TS2362: The left-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. -src/app/api/material-labels/route.ts(254,7): error TS2353: Object literal may only specify known properties, and 'id' does not exist in type 'Date | Buffer | readonly SqlValue[]'. -src/app/api/material-labels/route.ts(254,12): error TS2352: Conversion of type 'ResultSetHeader' to type 'DbRow' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first. - Index signature for type 'string' is missing in type 'ResultSetHeader'. -src/app/api/material-labels/route.ts(274,23): error TS18046: 'parent.width' is of type 'unknown'. -src/app/api/material-requisitions/route.ts(105,57): error TS2345: Argument of type 'unknown[]' is not assignable to parameter of type '(number | null | undefined)[]'. - Type 'unknown' is not assignable to type 'number | null | undefined'. -src/app/api/material-returns/route.ts(57,33): error TS2538: Type 'unknown' cannot be used as an index type. -src/app/api/menu/route.ts(12,21): error TS18046: 'a.sort_order' is of type 'unknown'. -src/app/api/menu/route.ts(12,36): error TS18046: 'b.sort_order' is of type 'unknown'. -src/app/api/menu/route.ts(15,38): error TS2345: Argument of type 'unknown' is not assignable to parameter of type 'number | undefined'. -src/app/api/menu/sort-order/route.ts(59,24): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/migrations/six-critical-fixes/route.ts(152,13): error TS2365: Operator '>' cannot be applied to types '{}' and 'number'. -src/app/api/migrations/six-critical-fixes/route.ts(173,13): error TS2365: Operator '>' cannot be applied to types '{}' and 'number'. -src/app/api/migrations/six-critical-fixes/route.ts(192,13): error TS2365: Operator '>' cannot be applied to types '{}' and 'number'. -src/app/api/migrations/six-critical-fixes/route.ts(210,13): error TS2365: Operator '>' cannot be applied to types '{}' and 'number'. -src/app/api/migrations/six-critical-fixes/route.ts(318,13): error TS2365: Operator '>' cannot be applied to types '{}' and 'number'. -src/app/api/migrations/six-critical-fixes/route.ts(335,13): error TS2365: Operator '>' cannot be applied to types '{}' and 'number'. -src/app/api/migrations/six-critical-fixes/route.ts(354,13): error TS2365: Operator '>' cannot be applied to types '{}' and 'number'. -src/app/api/migrations/six-critical-fixes/route.ts(371,13): error TS2365: Operator '>' cannot be applied to types '{}' and 'number'. -src/app/api/migrations/six-critical-fixes/route.ts(390,13): error TS2365: Operator '>' cannot be applied to types '{}' and 'number'. -src/app/api/migrations/six-critical-fixes/route.ts(461,13): error TS2365: Operator '>' cannot be applied to types '{}' and 'number'. -src/app/api/migrations/six-critical-fixes/route.ts(478,13): error TS2365: Operator '>' cannot be applied to types '{}' and 'number'. -src/app/api/migrations/six-critical-fixes/route.ts(495,13): error TS2365: Operator '>' cannot be applied to types '{}' and 'number'. -src/app/api/migrations/six-critical-fixes/route.ts(556,15): error TS2365: Operator '>' cannot be applied to types '{}' and 'number'. -src/app/api/orders/bom/materials/route.ts(121,35): error TS2352: Conversion of type 'any[]' to type 'DbRow' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first. - Index signature for type 'string' is missing in type 'any[]'. -src/app/api/orders/bom/route.ts(182,22): error TS2352: Conversion of type 'QueryResult' to type 'DbRow' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first. - Type '[RowDataPacket[], ResultSetHeader]' is not comparable to type 'DbRow'. - Index signature for type 'string' is missing in type '[RowDataPacket[], ResultSetHeader]'. -src/app/api/orders/bom/route.ts(273,11): error TS18046: 'bom.status' is of type 'unknown'. -src/app/api/orders/bom/route.ts(295,26): error TS2769: No overload matches this call. - Overload 1 of 2, '(sql: string, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'SqlValue[]' is not assignable to parameter of type 'ExecuteValues | undefined'. - Type 'SqlValue[]' is not assignable to type 'ExecuteValues[]'. - Type 'SqlValue' is not assignable to type 'ExecuteValues'. - Type 'undefined' is not assignable to type 'ExecuteValues'. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/orders/bom/route.ts(369,9): error TS18046: 'bomData.status' is of type 'unknown'. -src/app/api/orders/bom/route.ts(379,24): error TS2769: No overload matches this call. - Overload 1 of 2, '(sql: string, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Type 'unknown' is not assignable to type 'ExecuteValues'. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/orders/export/route.ts(58,32): error TS2339: Property 'replace' does not exist on type '{}'. -src/app/api/orders/export/route.ts(86,81): error TS2322: Type 'unknown' is not assignable to type 'SqlValue'. -src/app/api/orders/route.ts(30,83): error TS2322: Type 'unknown' is not assignable to type 'SqlValue'. -src/app/api/orders/route.ts(38,12): error TS2322: Type 'unknown' is not assignable to type 'SqlValue'. -src/app/api/orders/route.ts(50,34): error TS2345: Argument of type 'unknown' is not assignable to parameter of type 'string'. -src/app/api/orders/route.ts(51,59): error TS2345: Argument of type '{}' is not assignable to parameter of type 'string'. -src/app/api/orders/route.ts(56,32): error TS2345: Argument of type 'unknown' is not assignable to parameter of type 'string'. -src/app/api/orders/route.ts(58,34): error TS2345: Argument of type 'unknown' is not assignable to parameter of type 'string'. -src/app/api/orders/route.ts(59,35): error TS2345: Argument of type '{}' is not assignable to parameter of type 'string'. -src/app/api/orders/route.ts(89,85): error TS2322: Type 'unknown' is not assignable to type 'SqlValue'. -src/app/api/orders/route.ts(97,14): error TS2322: Type 'unknown' is not assignable to type 'SqlValue'. -src/app/api/orders/route.ts(109,36): error TS2345: Argument of type 'unknown' is not assignable to parameter of type 'string'. -src/app/api/orders/route.ts(110,61): error TS2345: Argument of type '{}' is not assignable to parameter of type 'string'. -src/app/api/orders/route.ts(115,34): error TS2345: Argument of type 'unknown' is not assignable to parameter of type 'string'. -src/app/api/orders/route.ts(117,36): error TS2345: Argument of type 'unknown' is not assignable to parameter of type 'string'. -src/app/api/orders/route.ts(118,37): error TS2345: Argument of type '{}' is not assignable to parameter of type 'string'. -src/app/api/orders/route.ts(176,43): error TS18046: 'item.quantity' is of type 'unknown'. -src/app/api/orders/route.ts(176,59): error TS18046: 'item.unit_price' is of type 'unknown'. -src/app/api/orders/route.ts(188,22): error TS2352: Conversion of type 'any[]' to type 'DbRow' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first. - Index signature for type 'string' is missing in type 'any[]'. -src/app/api/orders/route.ts(253,25): error TS2345: Argument of type 'unknown' is not assignable to parameter of type 'SqlValue'. -src/app/api/orders/route.ts(288,88): error TS2322: Type 'unknown' is not assignable to type 'SqlValue'. -src/app/api/orders/sales/route.ts(61,30): error TS2345: Argument of type '{}' is not assignable to parameter of type 'string'. -src/app/api/orders/sales/route.ts(62,32): error TS2345: Argument of type '{}' is not assignable to parameter of type 'string'. -src/app/api/orders/sales/route.ts(64,31): error TS2345: Argument of type '{}' is not assignable to parameter of type 'string'. -src/app/api/orders/sales/route.ts(66,35): error TS2345: Argument of type '{}' is not assignable to parameter of type 'string'. -src/app/api/orders/sales/route.ts(67,33): error TS2345: Argument of type '{}' is not assignable to parameter of type 'string'. -src/app/api/orders/sales/route.ts(68,34): error TS2345: Argument of type '{}' is not assignable to parameter of type 'string'. -src/app/api/orders/sales/route.ts(80,8): error TS2322: Type 'unknown[]' is not assignable to type 'SqlValue'. - Type 'unknown[]' is not assignable to type 'readonly SqlValue[]'. - Type 'unknown' is not assignable to type 'SqlValue'. -src/app/api/orders/sales/route.ts(86,70): error TS2538: Type 'unknown' cannot be used as an index type. -src/app/api/orders/sales/route.ts(116,59): error TS2345: Argument of type 'unknown[]' is not assignable to parameter of type '(number | null | undefined)[]'. - Type 'unknown' is not assignable to type 'number | null | undefined'. -src/app/api/organization/employee/route.ts(86,27): error TS2322: Type 'SqlValue[]' is not assignable to type 'DbRow[]'. - Type 'SqlValue' is not assignable to type 'DbRow'. - Type 'undefined' is not assignable to type 'DbRow'. -src/app/api/organization/employee/route.ts(122,64): error TS2345: Argument of type 'DbRow[]' is not assignable to parameter of type 'number | SqlValue[]'. - Type 'DbRow[]' is not assignable to type 'SqlValue[]'. - Type 'DbRow' is not assignable to type 'SqlValue'. - Type 'DbRow' is missing the following properties from type 'Buffer': slice, subarray, write, toJSON, and 102 more. -src/app/api/outsource/issue/route.ts(121,23): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/outsource/issue/route.ts(122,23): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/outsource/issue/route.ts(163,43): error TS2345: Argument of type 'PoolConnection' is not assignable to parameter of type 'DbConnection'. - Types of property 'execute' are incompatible. - Type '{ (sql: string, values?: ExecuteValues | undefined): Promise<[T, FieldPacket[]]>; (options: QueryOptions, values?: ExecuteValues | undefined): Promise<...>; }' is not assignable to type '(sql: string, values?: unknown[] | undefined) => Promise>'. - Types of parameters 'values' and 'values' are incompatible. - Type 'unknown[] | undefined' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues[]'. - Type 'unknown' is not assignable to type 'ExecuteValues'. -src/app/api/outsource/issue/route.ts(192,41): error TS2339: Property 'reduce' does not exist on type 'QueryResult'. - Property 'reduce' does not exist on type 'OkPacket'. -src/app/api/outsource/receive/route.ts(106,25): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/outsource/receive/route.ts(107,25): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/outsource/receive/route.ts(138,41): error TS2345: Argument of type 'PoolConnection' is not assignable to parameter of type 'DbConnection'. - Types of property 'execute' are incompatible. - Type '{ (sql: string, values?: ExecuteValues | undefined): Promise<[T, FieldPacket[]]>; (options: QueryOptions, values?: ExecuteValues | undefined): Promise<...>; }' is not assignable to type '(sql: string, values?: unknown[] | undefined) => Promise>'. - Types of parameters 'values' and 'values' are incompatible. - Type 'unknown[] | undefined' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues[]'. - Type 'unknown' is not assignable to type 'ExecuteValues'. -src/app/api/outsource/settlement/route.ts(114,28): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/outsource/settlement/route.ts(116,28): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/prepress/die-maintenance/route.ts(92,19): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/prepress/die-maintenance/route.ts(192,21): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/prepress/die-maintenance/route.ts(268,22): error TS2769: No overload matches this call. - Overload 1 of 2, '(sql: string, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'SqlValue[]' is not assignable to parameter of type 'ExecuteValues | undefined'. - Type 'SqlValue[]' is not assignable to type 'ExecuteValues[]'. - Type 'SqlValue' is not assignable to type 'ExecuteValues'. - Type 'undefined' is not assignable to type 'ExecuteValues'. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/prepress/die-migrate/route.ts(32,7): error TS2345: Argument of type '{ table: string; status: string; reason?: undefined; message?: undefined; } | { table: string; status: string; reason: string; message?: undefined; } | { table: string; status: string; message: string; reason?: undefined; }' is not assignable to parameter of type 'SqlValue'. - Type '{ table: string; status: string; reason?: undefined; message?: undefined; }' is not assignable to type 'SqlValue'. -src/app/api/prepress/die-migrate/route.ts(39,7): error TS2345: Argument of type '{ table: string; status: string; reason?: undefined; message?: undefined; } | { table: string; status: string; reason: string; message?: undefined; } | { table: string; status: string; message: string; reason?: undefined; }' is not assignable to parameter of type 'SqlValue'. - Type '{ table: string; status: string; reason?: undefined; message?: undefined; }' is not assignable to type 'SqlValue'. -src/app/api/prepress/die-migrate/route.ts(46,7): error TS2345: Argument of type '{ table: string; status: string; reason?: undefined; message?: undefined; } | { table: string; status: string; reason: string; message?: undefined; } | { table: string; status: string; message: string; reason?: undefined; }' is not assignable to parameter of type 'SqlValue'. - Type '{ table: string; status: string; reason?: undefined; message?: undefined; }' is not assignable to type 'SqlValue'. -src/app/api/prepress/die-migrate/route.ts(53,7): error TS2345: Argument of type '{ table: string; status: string; reason?: undefined; message?: undefined; } | { table: string; status: string; reason: string; message?: undefined; } | { table: string; status: string; message: string; reason?: undefined; }' is not assignable to parameter of type 'SqlValue'. - Type '{ table: string; status: string; reason?: undefined; message?: undefined; }' is not assignable to type 'SqlValue'. -src/app/api/prepress/die-migrate/route.ts(60,7): error TS2345: Argument of type '{ table: string; status: string; reason?: undefined; message?: undefined; } | { table: string; status: string; reason: string; message?: undefined; } | { table: string; status: string; message: string; reason?: undefined; }' is not assignable to parameter of type 'SqlValue'. - Type '{ table: string; status: string; reason?: undefined; message?: undefined; }' is not assignable to type 'SqlValue'. -src/app/api/prepress/die-migrate/route.ts(67,7): error TS2345: Argument of type '{ table: string; status: string; reason?: undefined; message?: undefined; } | { table: string; status: string; reason: string; message?: undefined; } | { table: string; status: string; message: string; reason?: undefined; }' is not assignable to parameter of type 'SqlValue'. - Type '{ table: string; status: string; reason?: undefined; message?: undefined; }' is not assignable to type 'SqlValue'. -src/app/api/prepress/die-migrate/route.ts(74,7): error TS2345: Argument of type '{ table: string; status: string; reason?: undefined; message?: undefined; } | { table: string; status: string; reason: string; message?: undefined; } | { table: string; status: string; message: string; reason?: undefined; }' is not assignable to parameter of type 'SqlValue'. - Type '{ table: string; status: string; reason?: undefined; message?: undefined; }' is not assignable to type 'SqlValue'. -src/app/api/prepress/die-migrate/route.ts(81,7): error TS2345: Argument of type '{ table: string; status: string; reason?: undefined; message?: undefined; } | { table: string; status: string; reason: string; message?: undefined; } | { table: string; status: string; message: string; reason?: undefined; }' is not assignable to parameter of type 'SqlValue'. - Type '{ table: string; status: string; reason?: undefined; message?: undefined; }' is not assignable to type 'SqlValue'. -src/app/api/prepress/die-migrate/route.ts(88,7): error TS2345: Argument of type '{ table: string; status: string; reason?: undefined; message?: undefined; } | { table: string; status: string; reason: string; message?: undefined; } | { table: string; status: string; message: string; reason?: undefined; }' is not assignable to parameter of type 'SqlValue'. - Type '{ table: string; status: string; reason?: undefined; message?: undefined; }' is not assignable to type 'SqlValue'. -src/app/api/prepress/die-migrate/route.ts(95,7): error TS2345: Argument of type '{ table: string; status: string; reason?: undefined; message?: undefined; } | { table: string; status: string; reason: string; message?: undefined; } | { table: string; status: string; message: string; reason?: undefined; }' is not assignable to parameter of type 'SqlValue'. - Type '{ table: string; status: string; reason?: undefined; message?: undefined; }' is not assignable to type 'SqlValue'. -src/app/api/prepress/die-migrate/route.ts(102,7): error TS2345: Argument of type '{ table: string; status: string; reason?: undefined; message?: undefined; } | { table: string; status: string; reason: string; message?: undefined; } | { table: string; status: string; message: string; reason?: undefined; }' is not assignable to parameter of type 'SqlValue'. - Type '{ table: string; status: string; reason?: undefined; message?: undefined; }' is not assignable to type 'SqlValue'. -src/app/api/prepress/die-migrate/route.ts(109,7): error TS2345: Argument of type '{ table: string; status: string; reason?: undefined; message?: undefined; } | { table: string; status: string; reason: string; message?: undefined; } | { table: string; status: string; message: string; reason?: undefined; }' is not assignable to parameter of type 'SqlValue'. - Type '{ table: string; status: string; reason?: undefined; message?: undefined; }' is not assignable to type 'SqlValue'. -src/app/api/prepress/die-migrate/route.ts(116,7): error TS2345: Argument of type '{ table: string; status: string; reason?: undefined; message?: undefined; } | { table: string; status: string; reason: string; message?: undefined; } | { table: string; status: string; message: string; reason?: undefined; }' is not assignable to parameter of type 'SqlValue'. - Type '{ table: string; status: string; reason?: undefined; message?: undefined; }' is not assignable to type 'SqlValue'. -src/app/api/prepress/die-migrate/route.ts(123,7): error TS2345: Argument of type '{ table: string; status: string; reason?: undefined; message?: undefined; } | { table: string; status: string; reason: string; message?: undefined; } | { table: string; status: string; message: string; reason?: undefined; }' is not assignable to parameter of type 'SqlValue'. - Type '{ table: string; status: string; reason?: undefined; message?: undefined; }' is not assignable to type 'SqlValue'. -src/app/api/prepress/die-migrate/route.ts(130,7): error TS2345: Argument of type '{ table: string; status: string; message?: undefined; } | { table: string; status: string; message: string; }' is not assignable to parameter of type 'SqlValue'. - Type '{ table: string; status: string; message?: undefined; }' is not assignable to type 'SqlValue'. -src/app/api/prepress/die-migrate/route.ts(158,7): error TS2345: Argument of type '{ table: string; status: string; message?: undefined; } | { table: string; status: string; message: string; }' is not assignable to parameter of type 'SqlValue'. - Type '{ table: string; status: string; message?: undefined; }' is not assignable to type 'SqlValue'. -src/app/api/prepress/die-migrate/route.ts(191,9): error TS2353: Object literal may only specify known properties, and 'action' does not exist in type 'Date | Buffer | readonly SqlValue[]'. -src/app/api/prepress/die-migrate/route.ts(196,22): error TS2353: Object literal may only specify known properties, and 'action' does not exist in type 'Date | Buffer | readonly SqlValue[]'. -src/app/api/prepress/die-migrate/route.ts(210,9): error TS2353: Object literal may only specify known properties, and 'action' does not exist in type 'Date | Buffer | readonly SqlValue[]'. -src/app/api/prepress/die-migrate/route.ts(215,22): error TS2353: Object literal may only specify known properties, and 'action' does not exist in type 'Date | Buffer | readonly SqlValue[]'. -src/app/api/prepress/die-migrate/route.ts(226,22): error TS2353: Object literal may only specify known properties, and 'action' does not exist in type 'Date | Buffer | readonly SqlValue[]'. -src/app/api/prepress/die-migrate/route.ts(229,9): error TS2353: Object literal may only specify known properties, and 'action' does not exist in type 'Date | Buffer | readonly SqlValue[]'. -src/app/api/prepress/die-usage/route.ts(113,19): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/production/material-issue/route.ts(81,59): error TS2345: Argument of type 'unknown[]' is not assignable to parameter of type '(number | null | undefined)[]'. - Type 'unknown' is not assignable to type 'number | null | undefined'. -src/app/api/production/material-issue/route.ts(112,20): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/production/material-issue/route.ts(115,13): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/production/material-issue/route.ts(118,13): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/production/material-issue/route.ts(140,23): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/production/material-issue/route.ts(141,35): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/production/material-issue/route.ts(142,37): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/production/material-issue/route.ts(147,36): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/production/material-issue/route.ts(160,42): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/production/material-issue/route.ts(190,21): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/production/material-issue/route.ts(194,21): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/production/material-issue/route.ts(215,35): error TS2339: Property 'insertId' does not exist on type 'QueryResult'. - Property 'insertId' does not exist on type 'ResultSetHeader[]'. -src/app/api/production/material-issue/route.ts(260,23): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/production/material-issue/route.ts(264,23): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/production/material-issue/route.ts(282,23): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/production/material-issue/route.ts(285,22): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/production/material-issue/route.ts(287,52): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/production/material-issue/route.ts(303,29): error TS2339: Property 'map' does not exist on type 'QueryResult'. - Property 'map' does not exist on type 'OkPacket'. -src/app/api/production/material-return/route.ts(130,13): error TS2322: Type '{ materialId: unknown; materialCode: {} | null; materialName: {} | null; quantity: number; unit: {} | null; batchNo: {} | null; }[]' is not assignable to type '{ materialId: number; materialCode: string | null; materialName: string | null; quantity: number; unit: string | null; batchNo: string | null; unitPrice?: number | undefined; }[]'. - Type '{ materialId: unknown; materialCode: {} | null; materialName: {} | null; quantity: number; unit: {} | null; batchNo: {} | null; }' is not assignable to type '{ materialId: number; materialCode: string | null; materialName: string | null; quantity: number; unit: string | null; batchNo: string | null; unitPrice?: number | undefined; }'. - Types of property 'materialId' are incompatible. - Type 'unknown' is not assignable to type 'number'. -src/app/api/production/orders/route.ts(45,26): error TS2345: Argument of type '{}' is not assignable to parameter of type 'string'. -src/app/api/production/process-route/route.ts(38,8): error TS2322: Type 'unknown' is not assignable to type 'SqlValue'. -src/app/api/production/process-route/route.ts(70,23): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/production/schedule/capacity/route.ts(100,27): error TS18046: 'row.capacity_per_hour' is of type 'unknown'. -src/app/api/production/schedule/capacity/route.ts(101,61): error TS18046: 'row.used_hours' is of type 'unknown'. -src/app/api/production/schedule/capacity/route.ts(121,79): error TS18046: 'r.equipment_count' is of type 'unknown'. -src/app/api/production/schedule/capacity/route.ts(126,70): error TS18046: 'c.utilizationRate' is of type 'unknown'. -src/app/api/production/trace/route.ts(19,5): error TS2322: Type 'unknown[]' is not assignable to type 'SqlValue[]'. - Type 'unknown' is not assignable to type 'SqlValue'. -src/app/api/production/trace/route.ts(26,7): error TS2322: Type 'unknown[]' is not assignable to type 'SqlValue[]'. - Type 'unknown' is not assignable to type 'SqlValue'. -src/app/api/production/trace/route.ts(34,7): error TS2322: Type 'unknown[]' is not assignable to type 'SqlValue[]'. - Type 'unknown' is not assignable to type 'SqlValue'. -src/app/api/production/trace/route.ts(76,20): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/production/trace/route.ts(101,13): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/production/trace/route.ts(104,22): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/production/trace/route.ts(130,33): error TS2339: Property 'insertId' does not exist on type 'QueryResult'. - Property 'insertId' does not exist on type 'ResultSetHeader[]'. -src/app/api/production/trace/route.ts(152,9): error TS2353: Object literal may only specify known properties, and 'level' does not exist in type 'Date | Buffer | readonly SqlValue[]'. -src/app/api/production/trace/route.ts(185,31): error TS18049: 'a' is possibly 'null' or 'undefined'. -src/app/api/production/trace/route.ts(185,33): error TS2339: Property 'level' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'level' does not exist on type 'string'. -src/app/api/production/trace/route.ts(185,41): error TS18049: 'b' is possibly 'null' or 'undefined'. -src/app/api/production/trace/route.ts(185,43): error TS2339: Property 'level' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'level' does not exist on type 'string'. -src/app/api/production/work-report/route.ts(103,24): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/production/work-report/route.ts(110,21): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/products/categories/route.ts(89,23): error TS2352: Conversion of type 'any[]' to type 'DbRow' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first. - Index signature for type 'string' is missing in type 'any[]'. -src/app/api/products/route.ts(152,23): error TS2352: Conversion of type 'any[]' to type 'DbRow' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first. - Index signature for type 'string' is missing in type 'any[]'. -src/app/api/products/route.ts(201,27): error TS2345: Argument of type '{} | null' is not assignable to parameter of type 'SqlValue'. - Type '{}' is not assignable to type 'SqlValue'. -src/app/api/purchase/orders/route.ts(130,59): error TS2345: Argument of type 'unknown[]' is not assignable to parameter of type '(number | null | undefined)[]'. - Type 'unknown' is not assignable to type 'number | null | undefined'. -src/app/api/purchase/reconciliation/route.ts(92,32): error TS2362: The left-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. -src/app/api/purchase/reconciliation/route.ts(162,11): error TS2322: Type '({ sourceType: 1; sourceId: unknown; sourceNo: unknown; sourceDate: string; amount: number; } | { sourceType: 2; sourceId: unknown; sourceNo: unknown; sourceDate: string; amount: number; })[]' is not assignable to type 'PurchaseReconciliationLineProps[]'. - Type '{ sourceType: 1; sourceId: unknown; sourceNo: unknown; sourceDate: string; amount: number; } | { sourceType: 2; sourceId: unknown; sourceNo: unknown; sourceDate: string; amount: number; }' is not assignable to type 'PurchaseReconciliationLineProps'. - Type '{ sourceType: 1; sourceId: unknown; sourceNo: unknown; sourceDate: string; amount: number; }' is not assignable to type 'PurchaseReconciliationLineProps'. - Types of property 'sourceId' are incompatible. - Type 'unknown' is not assignable to type 'number'. -src/app/api/purchase/request/route.ts(130,25): error TS2352: Conversion of type 'PurchaseRequest[]' to type 'DbRow[]' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first. - Type 'PurchaseRequest' is not comparable to type 'DbRow'. - Index signature for type 'string' is missing in type 'PurchaseRequest'. -src/app/api/purchase/request/route.ts(134,7): error TS2345: Argument of type 'unknown[]' is not assignable to parameter of type 'SqlValue[]'. - Type 'unknown' is not assignable to type 'SqlValue'. -src/app/api/purchase/request/route.ts(137,24): error TS2352: Conversion of type 'RequestItem[]' to type 'DbRow[]' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first. - Type 'RequestItem' is not comparable to type 'DbRow'. - Index signature for type 'string' is missing in type 'RequestItem'. -src/app/api/purchase/request/route.ts(143,23): error TS2352: Conversion of type 'PurchaseRequest[]' to type 'DbRow[]' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first. - Type 'PurchaseRequest' is not comparable to type 'DbRow'. - Index signature for type 'string' is missing in type 'PurchaseRequest'. -src/app/api/purchase/request/route.ts(231,26): error TS2352: Conversion of type 'QueryResult' to type 'DbRow' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first. - Type '[RowDataPacket[], ResultSetHeader]' is not comparable to type 'DbRow'. - Index signature for type 'string' is missing in type '[RowDataPacket[], ResultSetHeader]'. -src/app/api/purchase/suppliers/route.ts(130,35): error TS2352: Conversion of type 'any[]' to type 'DbRow' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first. - Index signature for type 'string' is missing in type 'any[]'. -src/app/api/purchase/suppliers/route.ts(174,27): error TS2345: Argument of type '{} | null' is not assignable to parameter of type 'SqlValue'. - Type '{}' is not assignable to type 'SqlValue'. -src/app/api/qrcode/route.ts(127,35): error TS2352: Conversion of type 'QueryResult' to type 'DbRow' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first. - Type '[RowDataPacket[], ResultSetHeader]' is not comparable to type 'DbRow'. - Index signature for type 'string' is missing in type '[RowDataPacket[], ResultSetHeader]'. -src/app/api/qrcode/route.ts(148,25): error TS2339: Property 'affectedRows' does not exist on type 'QueryResult'. - Property 'affectedRows' does not exist on type 'ResultSetHeader[]'. -src/app/api/qrcode/route.ts(243,18): error TS2769: No overload matches this call. - Overload 1 of 2, '(sql: string, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'SqlValue[]' is not assignable to parameter of type 'ExecuteValues | undefined'. - Type 'SqlValue[]' is not assignable to type 'ExecuteValues[]'. - Type 'SqlValue' is not assignable to type 'ExecuteValues'. - Type 'undefined' is not assignable to type 'ExecuteValues'. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/qrcode/trace/route.ts(51,46): error TS2339: Property 'qr_code' does not exist on type '{}'. -src/app/api/qrcode/trace/route.ts(58,13): error TS2339: Property 'qr_code' does not exist on type '{}'. -src/app/api/qrcode/trace/route.ts(63,13): error TS2339: Property 'ref_no' does not exist on type '{}'. -src/app/api/qrcode/trace/route.ts(63,28): error TS2339: Property 'batch_no' does not exist on type '{}'. -src/app/api/qrcode/trace/route.ts(63,51): error TS2339: Property 'material_code' does not exist on type '{}'. -src/app/api/qrcode/trace/route.ts(63,79): error TS2339: Property 'qr_code' does not exist on type '{}'. -src/app/api/qrcode/trace/route.ts(67,14): error TS2339: Property 'batch_no' does not exist on type '{}'. -src/app/api/qrcode/trace/route.ts(73,15): error TS2339: Property 'batch_no' does not exist on type '{}'. -src/app/api/qrcode/trace/route.ts(78,14): error TS2339: Property 'material_id' does not exist on type '{}'. -src/app/api/qrcode/trace/route.ts(81,15): error TS2339: Property 'material_id' does not exist on type '{}'. -src/app/api/qrcode/trace/route.ts(86,14): error TS2339: Property 'batch_no' does not exist on type '{}'. -src/app/api/qrcode/trace/route.ts(92,15): error TS2339: Property 'batch_no' does not exist on type '{}'. -src/app/api/qrcode/trace/route.ts(97,14): error TS2339: Property 'material_id' does not exist on type '{}'. -src/app/api/qrcode/trace/route.ts(97,36): error TS2339: Property 'qr_type' does not exist on type '{}'. -src/app/api/qrcode/trace/route.ts(103,15): error TS2339: Property 'material_id' does not exist on type '{}'. -src/app/api/qrcode/trace/route.ts(108,14): error TS2339: Property 'qr_type' does not exist on type '{}'. -src/app/api/qrcode/trace/route.ts(108,47): error TS2339: Property 'work_order_no' does not exist on type '{}'. -src/app/api/qrcode/trace/route.ts(114,15): error TS2339: Property 'work_order_no' does not exist on type '{}'. -src/app/api/qrcode/trace/route.ts(119,14): error TS2339: Property 'qr_type' does not exist on type '{}'. -src/app/api/qrcode/trace/route.ts(119,46): error TS2339: Property 'ref_no' does not exist on type '{}'. -src/app/api/qrcode/trace/route.ts(125,15): error TS2339: Property 'ref_no' does not exist on type '{}'. -src/app/api/qrcode/trace/route.ts(130,14): error TS2339: Property 'ref_no' does not exist on type '{}'. -src/app/api/qrcode/trace/route.ts(130,31): error TS2339: Property 'ref_no' does not exist on type '{}'. -src/app/api/qrcode/trace/route.ts(132,14): error TS2339: Property 'ref_no' does not exist on type '{}'. -src/app/api/qrcode/trace/route.ts(135,14): error TS2339: Property 'ref_no' does not exist on type '{}'. -src/app/api/qrcode/trace/route.ts(135,31): error TS2339: Property 'ref_no' does not exist on type '{}'. -src/app/api/qrcode/trace/route.ts(138,15): error TS2339: Property 'ref_no' does not exist on type '{}'. -src/app/api/qrcode/trace/route.ts(141,14): error TS2339: Property 'work_order_no' does not exist on type '{}'. -src/app/api/qrcode/trace/route.ts(144,15): error TS2339: Property 'work_order_no' does not exist on type '{}'. -src/app/api/qrcode/trace/route.ts(149,14): error TS2339: Property 'ref_no' does not exist on type '{}'. -src/app/api/qrcode/trace/route.ts(152,15): error TS2339: Property 'batch_no' does not exist on type '{}'. -src/app/api/qrcode/trace/route.ts(152,38): error TS2339: Property 'material_code' does not exist on type '{}'. -src/app/api/qrcode/trace/route.ts(159,34): error TS2345: Argument of type 'unknown' is not assignable to parameter of type 'string'. -src/app/api/qrcode/trace/route.ts(164,43): error TS2769: No overload matches this call. - Overload 1 of 4, '(value: string | number | Date): Date', gave the following error. - Argument of type 'unknown' is not assignable to parameter of type 'string | number | Date'. - Overload 2 of 4, '(value: string | number): Date', gave the following error. - Argument of type 'unknown' is not assignable to parameter of type 'string | number'. -src/app/api/qrcode/trace/route.ts(164,72): error TS2769: No overload matches this call. - Overload 1 of 4, '(value: string | number | Date): Date', gave the following error. - Argument of type 'unknown' is not assignable to parameter of type 'string | number | Date'. - Overload 2 of 4, '(value: string | number): Date', gave the following error. - Argument of type 'unknown' is not assignable to parameter of type 'string | number'. -src/app/api/qrcode/trace/route.ts(166,14): error TS2339: Property 'create_time' does not exist on type '{}'. -src/app/api/qrcode/trace/route.ts(168,20): error TS2339: Property 'create_time' does not exist on type '{}'. -src/app/api/qrcode/trace/route.ts(169,39): error TS2339: Property 'qr_type' does not exist on type '{}'. -src/app/api/qrcode/trace/route.ts(172,30): error TS2339: Property 'qr_code' does not exist on type '{}'. -src/app/api/qrcode/trace/route.ts(178,23): error TS2339: Property 'create_time' does not exist on type '{}'. -src/app/api/qrcode/trace/route.ts(178,48): error TS2339: Property 'created_at' does not exist on type '{}'. -src/app/api/qrcode/trace/route.ts(182,29): error TS2339: Property 'batch_no' does not exist on type '{}'. -src/app/api/qrcode/trace/route.ts(182,58): error TS2339: Property 'quantity' does not exist on type '{}'. -src/app/api/qrcode/trace/route.ts(182,84): error TS2339: Property 'available_qty' does not exist on type '{}'. -src/app/api/qrcode/trace/route.ts(186,34): error TS2339: Property 'length' does not exist on type '{}'. -src/app/api/qrcode/trace/route.ts(187,17): error TS2339: Property 'forEach' does not exist on type '{}'. -src/app/api/qrcode/trace/route.ts(198,50): error TS2769: No overload matches this call. - Overload 1 of 4, '(value: string | number | Date): Date', gave the following error. - Argument of type 'unknown' is not assignable to parameter of type 'string | number | Date'. - Overload 2 of 4, '(value: string | number): Date', gave the following error. - Argument of type 'unknown' is not assignable to parameter of type 'string | number'. -src/app/api/qrcode/trace/route.ts(198,79): error TS2769: No overload matches this call. - Overload 1 of 4, '(value: string | number | Date): Date', gave the following error. - Argument of type 'unknown' is not assignable to parameter of type 'string | number | Date'. - Overload 2 of 4, '(value: string | number): Date', gave the following error. - Argument of type 'unknown' is not assignable to parameter of type 'string | number'. -src/app/api/qrcode/trace/route.ts(214,57): error TS2339: Property 'qr_code' does not exist on type '{}'. -src/app/api/quality/final/route.ts(17,64): error TS2345: Argument of type 'DbRow[]' is not assignable to parameter of type 'SqlValue[]'. - Type 'DbRow' is not assignable to type 'SqlValue'. - Type 'DbRow' is missing the following properties from type 'Buffer': slice, subarray, write, toJSON, and 102 more. -src/app/api/quality/final/route.ts(22,42): error TS2345: Argument of type '(number | DbRow)[]' is not assignable to parameter of type 'SqlValue[]'. - Type 'number | DbRow' is not assignable to type 'SqlValue'. - Type 'DbRow' is not assignable to type 'SqlValue'. - Type 'DbRow' is missing the following properties from type 'Buffer': slice, subarray, write, toJSON, and 102 more. -src/app/api/quality/final/route.ts(93,59): error TS2345: Argument of type 'SqlValue[]' is not assignable to parameter of type 'DbRow[]'. - Type 'SqlValue' is not assignable to type 'DbRow'. - Type 'undefined' is not assignable to type 'DbRow'. -src/app/api/quality/incoming/route.ts(94,7): error TS2345: Argument of type 'unknown[]' is not assignable to parameter of type 'SqlValue[]'. - Type 'unknown' is not assignable to type 'SqlValue'. -src/app/api/quality/incoming/route.ts(164,49): error TS2339: Property 'slice' does not exist on type '{}'. -src/app/api/quality/incoming/route.ts(192,29): error TS2352: Conversion of type 'unknown[]' to type 'DbRow' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first. - Index signature for type 'string' is missing in type 'unknown[]'. -src/app/api/quality/process/route.ts(18,64): error TS2345: Argument of type 'DbRow[]' is not assignable to parameter of type 'SqlValue[]'. - Type 'DbRow' is not assignable to type 'SqlValue'. - Type 'DbRow' is missing the following properties from type 'Buffer': slice, subarray, write, toJSON, and 102 more. -src/app/api/quality/process/route.ts(23,42): error TS2345: Argument of type '(number | DbRow)[]' is not assignable to parameter of type 'SqlValue[]'. - Type 'number | DbRow' is not assignable to type 'SqlValue'. - Type 'DbRow' is not assignable to type 'SqlValue'. - Type 'DbRow' is missing the following properties from type 'Buffer': slice, subarray, write, toJSON, and 102 more. -src/app/api/quality/process/route.ts(111,59): error TS2345: Argument of type 'SqlValue[]' is not assignable to parameter of type 'DbRow[]'. - Type 'SqlValue' is not assignable to type 'DbRow'. - Type 'undefined' is not assignable to type 'DbRow'. -src/app/api/quality/sgs/expiry-warning/route.ts(35,29): error TS2345: Argument of type '(r: DbRow) => unknown' is not assignable to parameter of type '(value: SqlValue, index: number, array: SqlValue[]) => unknown'. - Types of parameters 'r' and 'value' are incompatible. - Type 'SqlValue' is not assignable to type 'DbRow'. - Type 'undefined' is not assignable to type 'DbRow'. -src/app/api/quality/sgs/expiry-warning/route.ts(38,7): error TS2345: Argument of type 'unknown[]' is not assignable to parameter of type 'SqlValue[]'. - Type 'unknown' is not assignable to type 'SqlValue'. -src/app/api/quality/spc/route.ts(30,9): error TS2345: Argument of type 'PoolConnection' is not assignable to parameter of type 'DbConnection'. - Types of property 'execute' are incompatible. - Type '{ (sql: string, values?: ExecuteValues | undefined): Promise<[T, FieldPacket[]]>; (options: QueryOptions, values?: ExecuteValues | undefined): Promise<...>; }' is not assignable to type '(sql: string, values?: unknown[] | undefined) => Promise>'. - Types of parameters 'values' and 'values' are incompatible. - Type 'unknown[] | undefined' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues[]'. - Type 'unknown' is not assignable to type 'ExecuteValues'. -src/app/api/quality/spc/route.ts(65,7): error TS2345: Argument of type '{ defect_type: {}; count: number; }[]' is not assignable to parameter of type '{ defect_type: string; count: number; }[]'. - Type '{ defect_type: {}; count: number; }' is not assignable to type '{ defect_type: string; count: number; }'. - Types of property 'defect_type' are incompatible. - Type '{}' is not assignable to type 'string'. -src/app/api/quality/spc/route.ts(94,7): error TS2345: Argument of type '{ period: unknown; inspected: number; defective: number; }[]' is not assignable to parameter of type '{ period: string; inspected: number; defective: number; }[]'. - Type '{ period: unknown; inspected: number; defective: number; }' is not assignable to type '{ period: string; inspected: number; defective: number; }'. - Types of property 'period' are incompatible. - Type 'unknown' is not assignable to type 'string'. -src/app/api/reports/delivery-rate/route.ts(47,31): error TS2345: Argument of type 'unknown' is not assignable to parameter of type 'string'. -src/app/api/reports/delivery-rate/route.ts(48,35): error TS2345: Argument of type 'unknown' is not assignable to parameter of type 'string'. -src/app/api/reports/delivery-rate/route.ts(50,9): error TS18046: 'row.total_orders' is of type 'unknown'. -src/app/api/reports/delivery-rate/route.ts(50,44): error TS18046: 'row.completed_orders' is of type 'unknown'. -src/app/api/reports/delivery-rate/route.ts(50,67): error TS18046: 'row.total_orders' is of type 'unknown'. -src/app/api/reports/delivery-rate/route.ts(52,9): error TS18046: 'row.total_orders' is of type 'unknown'. -src/app/api/reports/delivery-rate/route.ts(52,44): error TS2362: The left-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. -src/app/api/reports/delivery-rate/route.ts(52,72): error TS18046: 'row.total_orders' is of type 'unknown'. -src/app/api/reports/delivery-rate/route.ts(59,71): error TS18046: 'r.totalOrders' is of type 'unknown'. -src/app/api/reports/delivery-rate/route.ts(60,75): error TS18046: 'r.completedOrders' is of type 'unknown'. -src/app/api/reports/delivery-rate/route.ts(64,66): error TS18046: 'r.deliveryRate' is of type 'unknown'. -src/app/api/reports/delivery-rate/route.ts(70,66): error TS18046: 'r.onTimeRate' is of type 'unknown'. -src/app/api/reports/delivery-rate/route.ts(102,31): error TS2345: Argument of type 'unknown' is not assignable to parameter of type 'string'. -src/app/api/reports/delivery-rate/route.ts(104,9): error TS18046: 'row.total_orders' is of type 'unknown'. -src/app/api/reports/delivery-rate/route.ts(104,44): error TS18046: 'row.completed_orders' is of type 'unknown'. -src/app/api/reports/delivery-rate/route.ts(104,67): error TS18046: 'row.total_orders' is of type 'unknown'. -src/app/api/reports/delivery-rate/route.ts(106,9): error TS18046: 'row.total_orders' is of type 'unknown'. -src/app/api/reports/delivery-rate/route.ts(106,44): error TS2362: The left-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. -src/app/api/reports/delivery-rate/route.ts(106,72): error TS18046: 'row.total_orders' is of type 'unknown'. -src/app/api/reports/delivery-rate/route.ts(114,71): error TS18046: 'r.totalOrders' is of type 'unknown'. -src/app/api/reports/delivery-rate/route.ts(118,66): error TS18046: 'r.deliveryRate' is of type 'unknown'. -src/app/api/reports/inventory-turnover/route.ts(51,35): error TS2345: Argument of type 'unknown' is not assignable to parameter of type 'string'. -src/app/api/reports/inventory-turnover/route.ts(52,38): error TS2345: Argument of type 'unknown' is not assignable to parameter of type 'string'. -src/app/api/reports/inventory-turnover/route.ts(63,32): error TS2345: Argument of type 'unknown' is not assignable to parameter of type 'string'. -src/app/api/reports/inventory-turnover/route.ts(65,30): error TS2345: Argument of type 'unknown' is not assignable to parameter of type 'string'. -src/app/api/reports/inventory-turnover/route.ts(66,34): error TS2345: Argument of type 'unknown' is not assignable to parameter of type 'string'. -src/app/api/reports/inventory-turnover/route.ts(67,31): error TS2345: Argument of type 'unknown' is not assignable to parameter of type 'string'. -src/app/api/reports/inventory-turnover/route.ts(68,34): error TS2345: Argument of type 'unknown' is not assignable to parameter of type 'string'. -src/app/api/reports/inventory-turnover/route.ts(79,72): error TS18046: 'r.inboundQty' is of type 'unknown'. -src/app/api/reports/inventory-turnover/route.ts(80,73): error TS18046: 'r.outboundQty' is of type 'unknown'. -src/app/api/reports/inventory-turnover/route.ts(84,67): error TS18046: 'r.turnoverRate' is of type 'unknown'. -src/app/api/reports/inventory-turnover/route.ts(116,37): error TS2345: Argument of type 'unknown' is not assignable to parameter of type 'string'. -src/app/api/reports/inventory-turnover/route.ts(117,38): error TS2345: Argument of type 'unknown' is not assignable to parameter of type 'string'. -src/app/api/reports/inventory-turnover/route.ts(125,32): error TS2345: Argument of type 'unknown' is not assignable to parameter of type 'string'. -src/app/api/reports/inventory-turnover/route.ts(126,33): error TS2345: Argument of type 'unknown' is not assignable to parameter of type 'string'. -src/app/api/reports/inventory-turnover/route.ts(127,36): error TS2345: Argument of type 'unknown' is not assignable to parameter of type 'string'. -src/app/api/reports/inventory-turnover/route.ts(128,32): error TS2345: Argument of type 'unknown' is not assignable to parameter of type 'string'. -src/app/api/reports/inventory-turnover/route.ts(139,70): error TS18046: 'r.totalStock' is of type 'unknown'. -src/app/api/reports/inventory-turnover/route.ts(140,73): error TS18046: 'r.outboundQty' is of type 'unknown'. -src/app/api/reports/production-cost/route.ts(46,39): error TS2345: Argument of type 'unknown' is not assignable to parameter of type 'string'. -src/app/api/reports/production-cost/route.ts(47,37): error TS2345: Argument of type 'unknown' is not assignable to parameter of type 'string'. -src/app/api/reports/production-cost/route.ts(55,29): error TS2345: Argument of type 'unknown' is not assignable to parameter of type 'string'. -src/app/api/reports/production-cost/route.ts(56,34): error TS2345: Argument of type 'unknown' is not assignable to parameter of type 'string'. -src/app/api/reports/production-cost/route.ts(59,34): error TS2345: Argument of type 'unknown' is not assignable to parameter of type 'string'. -src/app/api/reports/production-cost/route.ts(60,31): error TS2345: Argument of type 'unknown' is not assignable to parameter of type 'string'. -src/app/api/reports/production-cost/route.ts(61,34): error TS2345: Argument of type 'unknown' is not assignable to parameter of type 'string'. -src/app/api/reports/production-cost/route.ts(71,75): error TS18046: 'r.workOrderCount' is of type 'unknown'. -src/app/api/reports/production-cost/route.ts(72,77): error TS18046: 'r.standardCost' is of type 'unknown'. -src/app/api/reports/production-cost/route.ts(73,75): error TS18046: 'r.actualCost' is of type 'unknown'. -src/app/api/reports/production-cost/route.ts(74,73): error TS18046: 'r.costVariance' is of type 'unknown'. -src/app/api/reports/production-cost/route.ts(78,66): error TS18046: 'r.costVarianceRate' is of type 'unknown'. -src/app/api/reports/production-cost/route.ts(108,39): error TS2345: Argument of type 'unknown' is not assignable to parameter of type 'string'. -src/app/api/reports/production-cost/route.ts(109,37): error TS2345: Argument of type 'unknown' is not assignable to parameter of type 'string'. -src/app/api/reports/production-cost/route.ts(114,9): error TS18046: 'row.total_plan_qty' is of type 'unknown'. -src/app/api/reports/production-cost/route.ts(114,60): error TS2345: Argument of type 'unknown' is not assignable to parameter of type 'string'. -src/app/api/reports/production-cost/route.ts(116,9): error TS18046: 'row.total_completed_qty' is of type 'unknown'. -src/app/api/reports/production-cost/route.ts(116,63): error TS2345: Argument of type 'unknown' is not assignable to parameter of type 'string'. -src/app/api/reports/production-cost/route.ts(123,29): error TS2345: Argument of type 'unknown' is not assignable to parameter of type 'string'. -src/app/api/reports/production-cost/route.ts(124,34): error TS2345: Argument of type 'unknown' is not assignable to parameter of type 'string'. -src/app/api/reports/production-cost/route.ts(139,77): error TS18046: 'r.standardCost' is of type 'unknown'. -src/app/api/reports/production-cost/route.ts(140,75): error TS18046: 'r.actualCost' is of type 'unknown'. -src/app/api/reports/production-cost/route.ts(141,73): error TS18046: 'r.costVariance' is of type 'unknown'. -src/app/api/sales/delivery/[id]/ship/route.ts(127,43): error TS2345: Argument of type 'unknown' is not assignable to parameter of type 'string'. -src/app/api/sales/delivery/[id]/ship/route.ts(128,55): error TS2345: Argument of type 'unknown' is not assignable to parameter of type 'string'. -src/app/api/sales/delivery/[id]/ship/route.ts(140,20): error TS2769: No overload matches this call. - Overload 1 of 2, '(sql: string, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Type 'unknown' is not assignable to type 'ExecuteValues'. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/sales/delivery/[id]/ship/route.ts(152,13): error TS2769: No overload matches this call. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/sales/delivery/partial/route.ts(47,39): error TS2345: Argument of type 'unknown' is not assignable to parameter of type 'string'. -src/app/api/sales/delivery/partial/route.ts(48,42): error TS2345: Argument of type 'unknown' is not assignable to parameter of type 'string'. -src/app/api/sales/delivery/re-ship/route.ts(43,24): error TS2339: Property 'sales_order_id' does not exist on type '{}'. -src/app/api/sales/delivery/re-ship/route.ts(44,24): error TS2339: Property 'customer_id' does not exist on type '{}'. -src/app/api/sales/delivery/re-ship/route.ts(45,24): error TS2339: Property 'customer_name' does not exist on type '{}'. -src/app/api/sales/delivery/re-ship/route.ts(46,24): error TS2339: Property 'warehouse_id' does not exist on type '{}'. -src/app/api/sales/delivery/re-ship/route.ts(68,13): error TS18046: 'item' is of type 'unknown'. -src/app/api/sales/delivery/re-ship/route.ts(69,13): error TS18046: 'item' is of type 'unknown'. -src/app/api/sales/delivery/re-ship/route.ts(70,13): error TS18046: 'item' is of type 'unknown'. -src/app/api/sales/delivery/re-ship/route.ts(71,44): error TS18046: 'item' is of type 'unknown'. -src/app/api/sales/delivery/re-ship/route.ts(72,13): error TS18046: 'item' is of type 'unknown'. -src/app/api/sales/delivery/route.ts(171,26): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/sales/reconciliation/route.ts(115,37): error TS2345: Argument of type 'unknown' is not assignable to parameter of type 'string'. -src/app/api/sales/reconciliation/route.ts(118,71): error TS2345: Argument of type 'unknown' is not assignable to parameter of type 'string'. -src/app/api/sales/reconciliation/route.ts(120,11): error TS2322: Type '({ sourceType: 1; sourceId: unknown; sourceNo: unknown; sourceDate: unknown; amount: number; } | { sourceType: 2; sourceId: unknown; sourceNo: unknown; sourceDate: unknown; amount: number; })[]' is not assignable to type 'ReconciliationLineProps[]'. - Type '{ sourceType: 1; sourceId: unknown; sourceNo: unknown; sourceDate: unknown; amount: number; } | { sourceType: 2; sourceId: unknown; sourceNo: unknown; sourceDate: unknown; amount: number; }' is not assignable to type 'ReconciliationLineProps'. - Type '{ sourceType: 1; sourceId: unknown; sourceNo: unknown; sourceDate: unknown; amount: number; }' is not assignable to type 'ReconciliationLineProps'. - Types of property 'sourceId' are incompatible. - Type 'unknown' is not assignable to type 'number'. -src/app/api/sales/reconciliation/route.ts(126,28): error TS2345: Argument of type 'unknown' is not assignable to parameter of type 'string'. -src/app/api/sales/reconciliation/route.ts(133,28): error TS2345: Argument of type 'unknown' is not assignable to parameter of type 'string'. -src/app/api/sales/return/route.ts(147,24): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/sample/feedback/route.ts(25,17): error TS1196: Catch clause variable type annotation must be 'any' or 'unknown' if specified. -src/app/api/sample/feedback/route.ts(31,3): error TS2345: Argument of type '(request: NextRequest, userInfo: DbRow) => Promise> | NextResponse>>' is not assignable to parameter of type '(request: NextRequest, userInfo: UserInfo, context?: any) => Promise>'. - Types of parameters 'userInfo' and 'userInfo' are incompatible. - Type 'UserInfo' is not assignable to type 'DbRow'. - Index signature for type 'string' is missing in type 'UserInfo'. -src/app/api/sample/feedback/route.ts(46,9): error TS2322: Type 'unknown' is not assignable to type 'number | undefined'. -src/app/api/sample/feedback/route.ts(50,19): error TS1196: Catch clause variable type annotation must be 'any' or 'unknown' if specified. -src/app/api/sample/feedback/route.ts(75,19): error TS1196: Catch clause variable type annotation must be 'any' or 'unknown' if specified. -src/app/api/sample/inventory/route.ts(123,23): error TS2345: Argument of type '{} | null' is not assignable to parameter of type 'SqlValue'. - Type '{}' is not assignable to type 'SqlValue'. -src/app/api/sample/orders/linkage/route.ts(30,24): error TS18046: 'conn.query' is of type 'unknown'. -src/app/api/sample/orders/linkage/route.ts(34,19): error TS2365: Operator '+' cannot be applied to types '{}' and 'number'. -src/app/api/sample/orders/linkage/route.ts(78,11): error TS2571: Object is of type 'unknown'. -src/app/api/sample/orders/linkage/route.ts(82,53): error TS2345: Argument of type 'PoolConnection' is not assignable to parameter of type 'DbRow'. - Index signature for type 'string' is missing in type 'PoolConnection'. -src/app/api/sample/orders/linkage/route.ts(107,28): error TS2352: Conversion of type 'QueryResult' to type 'DbRow' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first. - Type '[RowDataPacket[], ResultSetHeader]' is not comparable to type 'DbRow'. - Index signature for type 'string' is missing in type '[RowDataPacket[], ResultSetHeader]'. -src/app/api/sample/orders/linkage/route.ts(111,9): error TS2769: No overload matches this call. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/sample/orders/linkage/route.ts(129,34): error TS2769: No overload matches this call. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/sample/orders/linkage/route.ts(143,13): error TS2363: The right-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. -src/app/api/sample/orders/linkage/route.ts(146,13): error TS2769: No overload matches this call. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/sample/orders/route.ts(63,5): error TS18046: 'props' is of type 'unknown'. -src/app/api/sample/orders/route.ts(72,5): error TS18046: 'result' is of type 'unknown'. -src/app/api/sample/orders/status/route.ts(12,3): error TS2345: Argument of type '(request: NextRequest, userInfo: DbRow) => Promise> | NextResponse>>' is not assignable to parameter of type '(request: NextRequest, userInfo: UserInfo, context?: any) => Promise>'. - Types of parameters 'userInfo' and 'userInfo' are incompatible. - Type 'UserInfo' is not assignable to type 'DbRow'. - Index signature for type 'string' is missing in type 'UserInfo'. -src/app/api/sample/orders/status/route.ts(28,41): error TS2345: Argument of type 'unknown' is not assignable to parameter of type 'number'. -src/app/api/sample/orders/status/route.ts(31,45): error TS2345: Argument of type 'unknown' is not assignable to parameter of type 'number'. -src/app/api/sample/orders/status/route.ts(34,43): error TS2345: Argument of type 'unknown' is not assignable to parameter of type 'number'. -src/app/api/sample/orders/status/route.ts(37,42): error TS2345: Argument of type 'unknown' is not assignable to parameter of type 'number'. -src/app/api/sample/orders/status/route.ts(43,79): error TS2345: Argument of type 'unknown' is not assignable to parameter of type 'number'. -src/app/api/sample/orders/status/route.ts(45,62): error TS2345: Argument of type 'unknown' is not assignable to parameter of type 'number'. -src/app/api/sample/orders/status/route.ts(49,59): error TS2345: Argument of type 'unknown' is not assignable to parameter of type 'number'. -src/app/api/sample/orders/status/route.ts(57,19): error TS1196: Catch clause variable type annotation must be 'any' or 'unknown' if specified. -src/app/api/screen-plates/history/route.ts(87,42): error TS2352: Conversion of type 'ResultSetHeader' to type 'DbRow' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first. - Index signature for type 'string' is missing in type 'ResultSetHeader'. -src/app/api/screen-plates/route.ts(139,22): error TS2352: Conversion of type 'ResultSetHeader' to type 'DbRow' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first. - Index signature for type 'string' is missing in type 'ResultSetHeader'. -src/app/api/settings/category-linkage/route.ts(109,9): error TS2322: Type 'unknown' is not assignable to type 'string'. -src/app/api/settings/category-linkage/route.ts(110,9): error TS2322: Type 'unknown' is not assignable to type 'string'. -src/app/api/settings/category-linkage/route.ts(142,9): error TS2322: Type 'unknown' is not assignable to type 'string | null'. -src/app/api/settings/category-linkage/route.ts(143,9): error TS2322: Type 'unknown' is not assignable to type 'string | null'. -src/app/api/settings/category-rules/route.ts(110,40): error TS2345: Argument of type '{}' is not assignable to parameter of type 'string'. -src/app/api/settings/category-rules/route.ts(114,11): error TS2322: Type '{}' is not assignable to type 'string'. -src/app/api/settings/category-rules/route.ts(117,11): error TS2322: Type 'unknown' is not assignable to type 'string'. -src/app/api/settings/category-rules/route.ts(130,11): error TS2322: Type 'unknown' is not assignable to type 'string'. -src/app/api/settings/category-rules/route.ts(169,13): error TS2322: Type 'unknown' is not assignable to type 'string'. -src/app/api/settings/category-rules/route.ts(180,13): error TS2322: Type 'unknown' is not assignable to type 'string'. -src/app/api/settings/change-approval/route.ts(166,12): error TS2322: Type 'unknown' is not assignable to type 'SqlValue'. -src/app/api/settings/change-approval/route.ts(166,37): error TS2322: Type 'unknown' is not assignable to type 'SqlValue'. -src/app/api/settings/system/route.ts(870,28): error TS2345: Argument of type 'unknown' is not assignable to parameter of type 'string'. -src/app/api/settings/system/route.ts(871,25): error TS2345: Argument of type 'unknown' is not assignable to parameter of type 'string'. -src/app/api/settings/system/route.ts(884,30): error TS2345: Argument of type 'unknown' is not assignable to parameter of type 'string'. -src/app/api/settings/system/route.ts(885,23): error TS2345: Argument of type 'unknown' is not assignable to parameter of type 'string'. -src/app/api/settings/system/route.ts(887,18): error TS2538: Type 'unknown' cannot be used as an index type. -src/app/api/settings/system/route.ts(888,15): error TS2538: Type 'unknown' cannot be used as an index type. -src/app/api/settings/system/route.ts(890,13): error TS2538: Type 'unknown' cannot be used as an index type. -src/app/api/standard-cards/by-material/[material_id]/route.ts(84,7): error TS2322: Type 'ColorStandardItem[]' is not assignable to type 'SqlValue[]'. - Type 'ColorStandardItem' is not assignable to type 'SqlValue'. -src/app/api/standard-cards/by-material/[material_id]/route.ts(90,7): error TS2322: Type 'ProcessStandardItem[]' is not assignable to type 'SqlValue[]'. - Type 'ProcessStandardItem' is not assignable to type 'SqlValue'. -src/app/api/standard-cards/by-material/[material_id]/route.ts(96,7): error TS2322: Type 'QualityStandardItem[]' is not assignable to type 'SqlValue[]'. - Type 'QualityStandardItem' is not assignable to type 'SqlValue'. -src/app/api/standard-cards/by-material/[material_id]/route.ts(114,7): error TS2322: Type '((ColorStandardItem & { item_type: string; }) | (ProcessStandardItem & { item_type: string; }) | (QualityStandardItem & { ...; }))[]' is not assignable to type 'SqlValue[]'. - Type '(ColorStandardItem & { item_type: string; }) | (ProcessStandardItem & { item_type: string; }) | (QualityStandardItem & { ...; })' is not assignable to type 'SqlValue'. - Type 'ColorStandardItem & { item_type: string; }' is not assignable to type 'SqlValue'. -src/app/api/standard-cards/check-deviation/route.ts(53,11): error TS2353: Object literal may only specify known properties, and 'parameter_name' does not exist in type 'Date | Buffer | readonly SqlValue[]'. -src/app/api/standard-cards/check-deviation/route.ts(83,9): error TS2353: Object literal may only specify known properties, and 'parameter_name' does not exist in type 'Date | Buffer | readonly SqlValue[]'. -src/app/api/standard-cards/route.ts(173,21): error TS2352: Conversion of type 'StandardCard' to type 'DbRow' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first. - Index signature for type 'string' is missing in type 'StandardCard'. -src/app/api/standard-cards/route.ts(330,32): error TS2769: No overload matches this call. - Overload 1 of 2, '(o: {}): string[]', gave the following error. - Argument of type 'unknown' is not assignable to parameter of type '{}'. - Overload 2 of 2, '(o: object): string[]', gave the following error. - Argument of type 'unknown' is not assignable to parameter of type 'object'. -src/app/api/standard-cards/route.ts(331,34): error TS2769: No overload matches this call. - Overload 1 of 2, '(o: ArrayLike | { [s: string]: unknown; }): unknown[]', gave the following error. - Argument of type 'unknown' is not assignable to parameter of type 'ArrayLike | { [s: string]: unknown; }'. - Overload 2 of 2, '(o: {}): any[]', gave the following error. - Argument of type 'unknown' is not assignable to parameter of type '{}'. -src/app/api/standard-cards/route.ts(484,11): error TS18046: 'updateData' is of type 'unknown'. -src/app/api/standard-cards/route.ts(486,11): error TS18046: 'updateData' is of type 'unknown'. -src/app/api/standard-cards/route.ts(488,11): error TS18046: 'updateData' is of type 'unknown'. -src/app/api/standard-cards/route.ts(496,9): error TS18046: 'updateData' is of type 'unknown'. -src/app/api/standard-cards/route.ts(499,9): error TS18046: 'updateData' is of type 'unknown'. -src/app/api/standard-cards/route.ts(505,7): error TS18046: 'updateData' is of type 'unknown'. -src/app/api/standard-cards/route.ts(509,5): error TS18046: 'updateData' is of type 'unknown'. -src/app/api/standard-cards/route.ts(514,9): error TS18046: 'updateData' is of type 'unknown'. -src/app/api/standard-cards/route.ts(515,29): error TS18046: 'updateData' is of type 'unknown'. -src/app/api/standard-cards/route.ts(535,16): error TS18046: 'updateData' is of type 'unknown'. -src/app/api/standard-cards/route.ts(540,21): error TS2769: No overload matches this call. - Overload 1 of 2, '(o: {}): string[]', gave the following error. - Argument of type 'unknown' is not assignable to parameter of type '{}'. - Overload 2 of 2, '(o: object): string[]', gave the following error. - Argument of type 'unknown' is not assignable to parameter of type 'object'. -src/app/api/standard-cards/route.ts(544,38): error TS2769: No overload matches this call. - Overload 1 of 2, '(o: {}): string[]', gave the following error. - Argument of type 'unknown' is not assignable to parameter of type '{}'. - Overload 2 of 2, '(o: object): string[]', gave the following error. - Argument of type 'unknown' is not assignable to parameter of type 'object'. -src/app/api/standard-cards/route.ts(545,34): error TS2769: No overload matches this call. - Overload 1 of 2, '(o: ArrayLike | { [s: string]: unknown; }): unknown[]', gave the following error. - Argument of type 'unknown' is not assignable to parameter of type 'ArrayLike | { [s: string]: unknown; }'. - Overload 2 of 2, '(o: {}): any[]', gave the following error. - Argument of type 'unknown' is not assignable to parameter of type '{}'. -src/app/api/standard-cards/scan/route.ts(33,32): error TS2339: Property 'work_order_no' does not exist on type '{}'. -src/app/api/standard-cards/scan/route.ts(34,32): error TS2339: Property 'work_order_id' does not exist on type '{}'. -src/app/api/standard-cards/scan/route.ts(37,34): error TS2339: Property 'ref_no' does not exist on type '{}'. -src/app/api/standard-cards/scan/route.ts(40,19): error TS2339: Property 'ref_no' does not exist on type '{}'. -src/app/api/standard-cards/scan/route.ts(67,11): error TS2322: Type 'ColorStandardItem[]' is not assignable to type 'SqlValue[]'. - Type 'ColorStandardItem' is not assignable to type 'SqlValue'. -src/app/api/standard-cards/scan/route.ts(73,11): error TS2322: Type 'ProcessStandardItem[]' is not assignable to type 'SqlValue[]'. - Type 'ProcessStandardItem' is not assignable to type 'SqlValue'. -src/app/api/standard-cards/scan/route.ts(79,11): error TS2322: Type 'QualityStandardItem[]' is not assignable to type 'SqlValue[]'. - Type 'QualityStandardItem' is not assignable to type 'SqlValue'. -src/app/api/standard-cards/scan/route.ts(98,11): error TS2322: Type '(ProcessStandardItem | ColorStandardItem | QualityStandardItem)[]' is not assignable to type 'SqlValue[]'. - Type 'ProcessStandardItem | ColorStandardItem | QualityStandardItem' is not assignable to type 'SqlValue'. - Type 'ProcessStandardItem' is not assignable to type 'SqlValue'. -src/app/api/standard-cards/scan/route.ts(104,9): error TS2353: Object literal may only specify known properties, and 'items' does not exist in type 'Date | Buffer | readonly SqlValue[]'. -src/app/api/system/data-scope/route.ts(25,8): error TS2339: Property 'split' does not exist on type '{}'. -src/app/api/system/data-scope/route.ts(28,12): error TS2538: Type 'unknown' cannot be used as an index type. -src/app/api/system/log/export/route.ts(95,31): error TS2339: Property 'replace' does not exist on type '{}'. -src/app/api/system/monitor/route.ts(25,15): error TS2322: Type 'any[][]' is not assignable to type 'DbRow[]'. - Type 'any[]' is not assignable to type 'DbRow'. - Index signature for type 'string' is missing in type 'any[]'. -src/app/api/system/monitor/route.ts(34,32): error TS2339: Property 'count' does not exist on type '{}'. -src/app/api/system/monitor/route.ts(35,42): error TS2339: Property 'count' does not exist on type '{}'. -src/app/api/system/monitor/route.ts(36,48): error TS2339: Property 'count' does not exist on type '{}'. -src/app/api/system/monitor/route.ts(37,36): error TS2339: Property 'count' does not exist on type '{}'. -src/app/api/system/monitor/route.ts(74,15): error TS2322: Type 'any[][]' is not assignable to type 'DbRow[]'. - Type 'any[]' is not assignable to type 'DbRow'. - Index signature for type 'string' is missing in type 'any[]'. -src/app/api/system/monitor/route.ts(82,48): error TS2339: Property 'Value' does not exist on type '{}'. -src/app/api/system/monitor/route.ts(83,44): error TS2339: Property 'Value' does not exist on type '{}'. -src/app/api/system/monitor/route.ts(84,47): error TS2339: Property 'Value' does not exist on type '{}'. -src/app/api/system/monitor/route.ts(85,54): error TS2339: Property 'Value' does not exist on type '{}'. -src/app/api/system/oper-log/route.ts(148,19): error TS2345: Argument of type '(cell: string | number) => string' is not assignable to parameter of type '(value: unknown, index: number, array: unknown[]) => string'. - Types of parameters 'cell' and 'value' are incompatible. - Type 'unknown' is not assignable to type 'string | number'. -src/app/api/system/roles/route.ts(35,12): error TS2339: Property 'effectivePermissions' does not exist on type '{ permissions: any; }'. -src/app/api/system/roles/route.ts(35,74): error TS2339: Property 'id' does not exist on type '{ permissions: any; }'. -src/app/api/system/roles/route.ts(36,12): error TS2339: Property 'parentRole' does not exist on type '{ permissions: any; }'. -src/app/api/system/roles/route.ts(36,30): error TS2339: Property 'parent_id' does not exist on type '{ permissions: any; }'. -src/app/api/system/roles/route.ts(36,71): error TS2339: Property 'parent_id' does not exist on type '{ permissions: any; }'. -src/app/api/system/user/route.ts(82,24): error TS2339: Property 'insertId' does not exist on type 'QueryResult'. - Property 'insertId' does not exist on type 'ResultSetHeader[]'. -src/app/api/system/workflow/route.ts(39,10): error TS2322: Type 'unknown' is not assignable to type 'SqlValue'. -src/app/api/system/workflow/route.ts(184,7): error TS2571: Object is of type 'unknown'. -src/app/api/warehouse/batch-inventory/route.ts(100,9): error TS2353: Object literal may only specify known properties, and 'batch_inventory_id' does not exist in type 'Date | Buffer | readonly SqlValue[]'. -src/app/api/warehouse/batch-inventory/route.ts(115,51): error TS2345: Argument of type 'unknown' is not assignable to parameter of type 'string'. -src/app/api/warehouse/batch-inventory/route.ts(143,35): error TS2339: Property 'insertId' does not exist on type 'QueryResult'. - Property 'insertId' does not exist on type 'ResultSetHeader[]'. -src/app/api/warehouse/batch-inventory/route.ts(188,20): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/warehouse/batch-inventory/route.ts(196,32): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/warehouse/batch-inventory/route.ts(261,36): error TS2339: Property 'insertId' does not exist on type 'QueryResult'. - Property 'insertId' does not exist on type 'ResultSetHeader[]'. -src/app/api/warehouse/batch-inventory/route.ts(288,30): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/warehouse/batch-inventory/route.ts(303,27): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/warehouse/batch-inventory/route.ts(304,27): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/warehouse/batch-inventory/route.ts(315,17): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/warehouse/batch-inventory/route.ts(319,39): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/warehouse/batch-inventory/route.ts(322,17): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/warehouse/batch-inventory/route.ts(322,64): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/warehouse/batch-inventory/route.ts(322,104): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/warehouse/batch-inventory/route.ts(348,19): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/warehouse/batch/trace/route.ts(77,17): error TS18046: 'log' is of type 'unknown'. -src/app/api/warehouse/batch/trace/route.ts(78,25): error TS18046: 'log' is of type 'unknown'. -src/app/api/warehouse/batch/trace/route.ts(80,33): error TS18046: 'log' is of type 'unknown'. -src/app/api/warehouse/batch/trace/route.ts(80,61): error TS18046: 'log' is of type 'unknown'. -src/app/api/warehouse/batch/trace/route.ts(81,35): error TS18046: 'log' is of type 'unknown'. -src/app/api/warehouse/batch/trace/route.ts(82,32): error TS18046: 'log' is of type 'unknown'. -src/app/api/warehouse/batch/trace/route.ts(83,31): error TS18046: 'log' is of type 'unknown'. -src/app/api/warehouse/batch/trace/route.ts(84,24): error TS18046: 'log' is of type 'unknown'. -src/app/api/warehouse/batch/trace/route.ts(85,22): error TS18046: 'log' is of type 'unknown'. -src/app/api/warehouse/batch/trace/route.ts(86,25): error TS18046: 'log' is of type 'unknown'. -src/app/api/warehouse/batch/trace/route.ts(87,24): error TS18046: 'log' is of type 'unknown'. -src/app/api/warehouse/batch/trace/route.ts(88,17): error TS18046: 'log' is of type 'unknown'. -src/app/api/warehouse/batch/trace/route.ts(89,22): error TS18046: 'log' is of type 'unknown'. -src/app/api/warehouse/batch/trace/route.ts(93,13): error TS18046: 'log' is of type 'unknown'. -src/app/api/warehouse/batch/trace/route.ts(93,54): error TS18046: 'log' is of type 'unknown'. -src/app/api/warehouse/batch/trace/route.ts(99,14): error TS18046: 'log' is of type 'unknown'. -src/app/api/warehouse/batch/trace/route.ts(102,13): error TS18046: 'entry' is of type 'unknown'. -src/app/api/warehouse/batch/trace/route.ts(114,20): error TS18046: 'log' is of type 'unknown'. -src/app/api/warehouse/batch/trace/route.ts(114,62): error TS18046: 'log' is of type 'unknown'. -src/app/api/warehouse/batch/trace/route.ts(120,14): error TS18046: 'log' is of type 'unknown'. -src/app/api/warehouse/batch/trace/route.ts(123,13): error TS18046: 'entry' is of type 'unknown'. -src/app/api/warehouse/batch/trace/route.ts(137,20): error TS18046: 'log' is of type 'unknown'. -src/app/api/warehouse/batch/trace/route.ts(137,56): error TS18046: 'log' is of type 'unknown'. -src/app/api/warehouse/batch/trace/route.ts(148,14): error TS18046: 'log' is of type 'unknown'. -src/app/api/warehouse/batch/trace/route.ts(151,13): error TS18046: 'entry' is of type 'unknown'. -src/app/api/warehouse/batch/trace/route.ts(166,11): error TS18046: 'log' is of type 'unknown'. -src/app/api/warehouse/batch/trace/route.ts(167,11): error TS18046: 'log' is of type 'unknown'. -src/app/api/warehouse/batch/trace/route.ts(174,14): error TS18046: 'log' is of type 'unknown'. -src/app/api/warehouse/batch/trace/route.ts(177,13): error TS18046: 'entry' is of type 'unknown'. -src/app/api/warehouse/batch/trace/route.ts(190,20): error TS18046: 'log' is of type 'unknown'. -src/app/api/warehouse/batch/trace/route.ts(190,60): error TS18046: 'log' is of type 'unknown'. -src/app/api/warehouse/batch/trace/route.ts(195,14): error TS18046: 'log' is of type 'unknown'. -src/app/api/warehouse/batch/trace/route.ts(198,13): error TS18046: 'entry' is of type 'unknown'. -src/app/api/warehouse/batch/trace/route.ts(214,23): error TS2345: Argument of type 'unknown' is not assignable to parameter of type '{ log_id: number; operation_type: number; operation_type_label: string; operation_qty: number; before_qty: number; after_qty: number; business_type: string; business_no: string; warehouse_name: string; operator_name: string; remark: string; create_time: string; document?: { ...; } | undefined; }'. -src/app/api/warehouse/batch/trace/route.ts(218,15): error TS2769: No overload matches this call. - Overload 1 of 2, '(predicate: (value: unknown, index: number, array: unknown[]) => value is unknown, thisArg?: any): unknown[]', gave the following error. - Argument of type '(l: DbRow) => boolean' is not assignable to parameter of type '(value: unknown, index: number, array: unknown[]) => value is unknown'. - Types of parameters 'l' and 'value' are incompatible. - Type 'unknown' is not assignable to type 'DbRow'. - Overload 2 of 2, '(predicate: (value: unknown, index: number, array: unknown[]) => unknown, thisArg?: any): unknown[]', gave the following error. - Argument of type '(l: DbRow) => boolean' is not assignable to parameter of type '(value: unknown, index: number, array: unknown[]) => unknown'. - Types of parameters 'l' and 'value' are incompatible. - Type 'unknown' is not assignable to type 'DbRow'. -src/app/api/warehouse/batch/trace/route.ts(219,15): error TS2769: No overload matches this call. - Overload 1 of 3, '(callbackfn: (previousValue: unknown, currentValue: unknown, currentIndex: number, array: unknown[]) => unknown, initialValue: unknown): unknown', gave the following error. - Argument of type '(sum: number, l: DbRow) => number' is not assignable to parameter of type '(previousValue: unknown, currentValue: unknown, currentIndex: number, array: unknown[]) => unknown'. - Types of parameters 'sum' and 'previousValue' are incompatible. - Type 'unknown' is not assignable to type 'number'. - Overload 2 of 3, '(callbackfn: (previousValue: number, currentValue: unknown, currentIndex: number, array: unknown[]) => number, initialValue: number): number', gave the following error. - Argument of type '(sum: number, l: DbRow) => number' is not assignable to parameter of type '(previousValue: number, currentValue: unknown, currentIndex: number, array: unknown[]) => number'. - Types of parameters 'l' and 'currentValue' are incompatible. - Type 'unknown' is not assignable to type 'DbRow'. -src/app/api/warehouse/batch/trace/route.ts(219,59): error TS2345: Argument of type 'unknown' is not assignable to parameter of type 'string'. -src/app/api/warehouse/batch/trace/route.ts(221,15): error TS2769: No overload matches this call. - Overload 1 of 2, '(predicate: (value: unknown, index: number, array: unknown[]) => value is unknown, thisArg?: any): unknown[]', gave the following error. - Argument of type '(l: DbRow) => boolean' is not assignable to parameter of type '(value: unknown, index: number, array: unknown[]) => value is unknown'. - Types of parameters 'l' and 'value' are incompatible. - Type 'unknown' is not assignable to type 'DbRow'. - Overload 2 of 2, '(predicate: (value: unknown, index: number, array: unknown[]) => unknown, thisArg?: any): unknown[]', gave the following error. - Argument of type '(l: DbRow) => boolean' is not assignable to parameter of type '(value: unknown, index: number, array: unknown[]) => unknown'. - Types of parameters 'l' and 'value' are incompatible. - Type 'unknown' is not assignable to type 'DbRow'. -src/app/api/warehouse/batch/trace/route.ts(222,15): error TS2769: No overload matches this call. - Overload 1 of 3, '(callbackfn: (previousValue: unknown, currentValue: unknown, currentIndex: number, array: unknown[]) => unknown, initialValue: unknown): unknown', gave the following error. - Argument of type '(sum: number, l: DbRow) => number' is not assignable to parameter of type '(previousValue: unknown, currentValue: unknown, currentIndex: number, array: unknown[]) => unknown'. - Types of parameters 'sum' and 'previousValue' are incompatible. - Type 'unknown' is not assignable to type 'number'. - Overload 2 of 3, '(callbackfn: (previousValue: number, currentValue: unknown, currentIndex: number, array: unknown[]) => number, initialValue: number): number', gave the following error. - Argument of type '(sum: number, l: DbRow) => number' is not assignable to parameter of type '(previousValue: number, currentValue: unknown, currentIndex: number, array: unknown[]) => number'. - Types of parameters 'l' and 'currentValue' are incompatible. - Type 'unknown' is not assignable to type 'DbRow'. -src/app/api/warehouse/batch/trace/route.ts(222,59): error TS2345: Argument of type 'unknown' is not assignable to parameter of type 'string'. -src/app/api/warehouse/batch/trace/route.ts(226,19): error TS18046: 'batchSnapshot' is of type 'unknown'. -src/app/api/warehouse/batch/trace/route.ts(227,22): error TS18046: 'batchSnapshot' is of type 'unknown'. -src/app/api/warehouse/batch/trace/route.ts(228,24): error TS18046: 'batchSnapshot' is of type 'unknown'. -src/app/api/warehouse/batch/trace/route.ts(229,24): error TS18046: 'batchSnapshot' is of type 'unknown'. -src/app/api/warehouse/batch/trace/route.ts(230,23): error TS18046: 'batchSnapshot' is of type 'unknown'. -src/app/api/warehouse/batch/trace/route.ts(231,19): error TS18046: 'batchSnapshot' is of type 'unknown'. -src/app/api/warehouse/batch/trace/route.ts(232,30): error TS18046: 'batchSnapshot' is of type 'unknown'. -src/app/api/warehouse/batch/trace/route.ts(233,35): error TS18046: 'batchSnapshot' is of type 'unknown'. -src/app/api/warehouse/batch/trace/route.ts(234,32): error TS18046: 'batchSnapshot' is of type 'unknown'. -src/app/api/warehouse/batch/trace/route.ts(235,31): error TS18046: 'batchSnapshot' is of type 'unknown'. -src/app/api/warehouse/batch/trace/route.ts(235,58): error TS18046: 'batchSnapshot' is of type 'unknown'. -src/app/api/warehouse/batch/trace/route.ts(236,23): error TS18046: 'batchSnapshot' is of type 'unknown'. -src/app/api/warehouse/batch/trace/route.ts(237,23): error TS18046: 'batchSnapshot' is of type 'unknown'. -src/app/api/warehouse/batch/trace/route.ts(238,22): error TS18046: 'batchSnapshot' is of type 'unknown'. -src/app/api/warehouse/batch/trace/route.ts(239,17): error TS18046: 'batchSnapshot' is of type 'unknown'. -src/app/api/warehouse/batch/trace/route.ts(240,22): error TS18046: 'batchSnapshot' is of type 'unknown'. -src/app/api/warehouse/batch/trace/route.ts(241,22): error TS18046: 'batchSnapshot' is of type 'unknown'. -src/app/api/warehouse/batch/trace/route.ts(248,21): error TS18046: 'totalIn' is of type 'unknown'. -src/app/api/warehouse/batch/trace/route.ts(248,31): error TS18046: 'totalOut' is of type 'unknown'. -src/app/api/warehouse/fifo-recommend/route.ts(28,29): error TS2339: Property 'includes' does not exist on type '{}'. -src/app/api/warehouse/fifo-recommend/route.ts(32,24): error TS2339: Property 'includes' does not exist on type '{}'. -src/app/api/warehouse/fifo-recommend/route.ts(38,44): error TS2345: Argument of type 'unknown' is not assignable to parameter of type 'string | null'. -src/app/api/warehouse/fifo-recommend/route.ts(112,48): error TS2345: Argument of type 'unknown' is not assignable to parameter of type 'string | null'. -src/app/api/warehouse/fifo-recommend/route.ts(128,16): error TS18046: 'b.compatibility_score' is of type 'unknown'. -src/app/api/warehouse/fifo-recommend/route.ts(128,40): error TS18046: 'a.compatibility_score' is of type 'unknown'. -src/app/api/warehouse/fifo-recommend/route.ts(129,23): error TS2769: No overload matches this call. - Overload 1 of 4, '(value: string | number | Date): Date', gave the following error. - Argument of type 'unknown' is not assignable to parameter of type 'string | number | Date'. - Overload 2 of 4, '(value: string | number): Date', gave the following error. - Argument of type 'unknown' is not assignable to parameter of type 'string | number'. -src/app/api/warehouse/fifo-recommend/route.ts(129,60): error TS2769: No overload matches this call. - Overload 1 of 4, '(value: string | number | Date): Date', gave the following error. - Argument of type 'unknown' is not assignable to parameter of type 'string | number | Date'. - Overload 2 of 4, '(value: string | number): Date', gave the following error. - Argument of type 'unknown' is not assignable to parameter of type 'string | number'. -src/app/api/warehouse/fifo-recommend/route.ts(140,36): error TS2339: Property 'available_qty' does not exist on type '{ expiry_weight: number; compatibility_score: number; override_risk_score: number; is_near_expiry: boolean; is_urgent_expiry: boolean; }'. -src/app/api/warehouse/fifo-recommend/route.ts(142,58): error TS2339: Property 'available_qty' does not exist on type '{ expiry_weight: number; compatibility_score: number; override_risk_score: number; is_near_expiry: boolean; is_urgent_expiry: boolean; }'. -src/app/api/warehouse/fifo-recommend/route.ts(147,38): error TS2339: Property 'available_qty' does not exist on type '{ expiry_weight: number; compatibility_score: number; override_risk_score: number; is_near_expiry: boolean; is_urgent_expiry: boolean; }'. -src/app/api/warehouse/fifo-recommend/route.ts(151,24): error TS2339: Property 'available_qty' does not exist on type '{ expiry_weight: number; compatibility_score: number; override_risk_score: number; is_near_expiry: boolean; is_urgent_expiry: boolean; }'. -src/app/api/warehouse/fifo-recommend/route.ts(157,9): error TS2353: Object literal may only specify known properties, and 'allocated_qty' does not exist in type 'Date | Buffer | readonly SqlValue[]'. -src/app/api/warehouse/fifo-recommend/route.ts(170,52): error TS2345: Argument of type '(r: DbRow) => boolean' is not assignable to parameter of type '(value: SqlValue, index: number, array: SqlValue[]) => unknown'. - Types of parameters 'r' and 'value' are incompatible. - Type 'SqlValue' is not assignable to type 'DbRow'. - Type 'undefined' is not assignable to type 'DbRow'. -src/app/api/warehouse/fifo-recommend/route.ts(182,5): error TS2769: No overload matches this call. - Overload 1 of 3, '(callbackfn: (previousValue: SqlValue, currentValue: SqlValue, currentIndex: number, array: SqlValue[]) => SqlValue, initialValue: SqlValue): SqlValue', gave the following error. - Argument of type '(sum: number, b: DbRow) => number' is not assignable to parameter of type '(previousValue: SqlValue, currentValue: SqlValue, currentIndex: number, array: SqlValue[]) => SqlValue'. - Types of parameters 'sum' and 'previousValue' are incompatible. - Type 'SqlValue' is not assignable to type 'number'. - Type 'undefined' is not assignable to type 'number'. - Overload 2 of 3, '(callbackfn: (previousValue: number, currentValue: SqlValue, currentIndex: number, array: SqlValue[]) => number, initialValue: number): number', gave the following error. - Argument of type '(sum: number, b: DbRow) => number' is not assignable to parameter of type '(previousValue: number, currentValue: SqlValue, currentIndex: number, array: SqlValue[]) => number'. - Types of parameters 'b' and 'currentValue' are incompatible. - Type 'SqlValue' is not assignable to type 'DbRow'. - Type 'undefined' is not assignable to type 'DbRow'. -src/app/api/warehouse/fifo-recommend/route.ts(186,5): error TS2769: No overload matches this call. - Overload 1 of 3, '(callbackfn: (previousValue: SqlValue, currentValue: SqlValue, currentIndex: number, array: SqlValue[]) => SqlValue, initialValue: SqlValue): SqlValue', gave the following error. - Argument of type '(sum: number, b: DbRow) => number' is not assignable to parameter of type '(previousValue: SqlValue, currentValue: SqlValue, currentIndex: number, array: SqlValue[]) => SqlValue'. - Types of parameters 'sum' and 'previousValue' are incompatible. - Type 'SqlValue' is not assignable to type 'number'. - Type 'undefined' is not assignable to type 'number'. - Overload 2 of 3, '(callbackfn: (previousValue: number, currentValue: SqlValue, currentIndex: number, array: SqlValue[]) => number, initialValue: number): number', gave the following error. - Argument of type '(sum: number, b: DbRow) => number' is not assignable to parameter of type '(previousValue: number, currentValue: SqlValue, currentIndex: number, array: SqlValue[]) => number'. - Types of parameters 'b' and 'currentValue' are incompatible. - Type 'SqlValue' is not assignable to type 'DbRow'. - Type 'undefined' is not assignable to type 'DbRow'. -src/app/api/warehouse/fifo-recommend/route.ts(189,19): error TS18049: 'totalRecommendedQty' is possibly 'null' or 'undefined'. -src/app/api/warehouse/fifo-recommend/route.ts(189,19): error TS2365: Operator '>' cannot be applied to types 'string | number | boolean | Date | Buffer | readonly SqlValue[]' and 'number'. -src/app/api/warehouse/fifo-recommend/route.ts(189,45): error TS2362: The left-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. -src/app/api/warehouse/fifo-recommend/route.ts(189,45): error TS18049: 'totalRecommendedCost' is possibly 'null' or 'undefined'. -src/app/api/warehouse/fifo-recommend/route.ts(189,68): error TS2363: The right-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. -src/app/api/warehouse/fifo-recommend/route.ts(189,68): error TS18049: 'totalRecommendedQty' is possibly 'null' or 'undefined'. -src/app/api/warehouse/fifo-recommend/route.ts(219,31): error TS2339: Property 'batch_no' does not exist on type '{ expiry_weight: number; compatibility_score: number; override_risk_score: number; is_near_expiry: boolean; is_urgent_expiry: boolean; }'. -src/app/api/warehouse/fifo-recommend/route.ts(220,43): error TS2339: Property 'available_qty' does not exist on type '{ expiry_weight: number; compatibility_score: number; override_risk_score: number; is_near_expiry: boolean; is_urgent_expiry: boolean; }'. -src/app/api/warehouse/fifo-recommend/route.ts(221,40): error TS2339: Property 'unit_price' does not exist on type '{ expiry_weight: number; compatibility_score: number; override_risk_score: number; is_near_expiry: boolean; is_urgent_expiry: boolean; }'. -src/app/api/warehouse/fifo-recommend/route.ts(222,35): error TS2339: Property 'inbound_date' does not exist on type '{ expiry_weight: number; compatibility_score: number; override_risk_score: number; is_near_expiry: boolean; is_urgent_expiry: boolean; }'. -src/app/api/warehouse/fifo-recommend/route.ts(223,34): error TS2339: Property 'expire_date' does not exist on type '{ expiry_weight: number; compatibility_score: number; override_risk_score: number; is_near_expiry: boolean; is_urgent_expiry: boolean; }'. -src/app/api/warehouse/fifo-recommend/route.ts(236,38): error TS2362: The left-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. -src/app/api/warehouse/fifo-recommend/route.ts(236,38): error TS18049: 'totalRecommendedCost' is possibly 'null' or 'undefined'. -src/app/api/warehouse/inbound/cutting/route.ts(44,17): error TS2339: Property 'is_cut' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(48,17): error TS2339: Property 'is_used' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(52,17): error TS2339: Property 'label_type' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(56,43): error TS2339: Property 'quantity' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(62,19): error TS2339: Property 'id' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(63,24): error TS2339: Property 'label_no' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(64,29): error TS2339: Property 'material_name' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(65,29): error TS2339: Property 'material_code' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(66,30): error TS2339: Property 'specification' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(67,25): error TS2339: Property 'quantity' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(68,21): error TS2339: Property 'unit' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(69,22): error TS2339: Property 'width' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(70,29): error TS2339: Property 'supplier_name' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(71,24): error TS2339: Property 'batch_no' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(72,32): error TS2339: Property 'purchase_order_no' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(73,26): error TS2339: Property 'label_type' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(74,23): error TS2339: Property 'is_used' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(75,22): error TS2339: Property 'is_cut' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(126,7): error TS2345: Argument of type 'SqlValue[]' is not assignable to parameter of type 'DbRow[]'. - Type 'SqlValue' is not assignable to type 'DbRow'. - Type 'undefined' is not assignable to type 'DbRow'. -src/app/api/warehouse/inbound/cutting/route.ts(185,21): error TS2339: Property 'is_cut' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(189,21): error TS2339: Property 'is_used' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(194,21): error TS2339: Property 'label_type' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(198,47): error TS2339: Property 'quantity' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(216,30): error TS2339: Property 'width' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(218,34): error TS2339: Property 'specification' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(222,39): error TS2339: Property 'material_code' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(225,22): error TS2339: Property 'material_code' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(228,39): error TS2339: Property 'width' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(228,71): error TS2339: Property 'specification' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(230,20): error TS2339: Property 'specification' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(230,50): error TS2339: Property 'specification' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(232,20): error TS2339: Property 'specification' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(234,25): error TS2339: Property 'id' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(236,23): error TS2339: Property 'specification' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(236,46): error TS2339: Property 'specification' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(237,38): error TS2339: Property 'width' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(279,25): error TS2339: Property 'id' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(280,25): error TS2339: Property 'label_no' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(291,27): error TS2352: Conversion of type 'QueryResult' to type 'DbRow' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first. - Type '[RowDataPacket[], ResultSetHeader]' is not comparable to type 'DbRow'. - Index signature for type 'string' is missing in type '[RowDataPacket[], ResultSetHeader]'. -src/app/api/warehouse/inbound/cutting/route.ts(294,23): error TS2339: Property 'id' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(298,42): error TS2339: Property 'specification' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(318,51): error TS2339: Property 'quantity' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(319,40): error TS2339: Property 'quantity' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(324,33): error TS2339: Property 'label_no' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(338,27): error TS2339: Property 'purchase_order_no' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(339,27): error TS2339: Property 'supplier_name' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(340,27): error TS2339: Property 'receive_date' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(341,27): error TS2339: Property 'material_code' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(342,27): error TS2339: Property 'material_name' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(344,27): error TS2339: Property 'unit' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(345,27): error TS2339: Property 'batch_no' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(348,27): error TS2339: Property 'length_per_roll' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(350,27): error TS2339: Property 'warehouse_id' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(351,27): error TS2339: Property 'location_id' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(352,27): error TS2339: Property 'id' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(356,31): error TS2352: Conversion of type 'QueryResult' to type 'DbRow' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first. - Type '[RowDataPacket[], ResultSetHeader]' is not comparable to type 'DbRow'. - Index signature for type 'string' is missing in type '[RowDataPacket[], ResultSetHeader]'. -src/app/api/warehouse/inbound/cutting/route.ts(389,51): error TS2339: Property 'quantity' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(395,33): error TS2339: Property 'label_no' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(409,27): error TS2339: Property 'purchase_order_no' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(410,27): error TS2339: Property 'supplier_name' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(411,27): error TS2339: Property 'receive_date' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(412,27): error TS2339: Property 'material_code' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(413,32): error TS2339: Property 'material_name' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(415,27): error TS2339: Property 'unit' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(416,27): error TS2339: Property 'batch_no' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(420,27): error TS2339: Property 'length_per_roll' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(422,27): error TS2339: Property 'warehouse_id' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(423,27): error TS2339: Property 'location_id' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(424,27): error TS2339: Property 'id' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(428,31): error TS2352: Conversion of type 'QueryResult' to type 'DbRow' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first. - Type '[RowDataPacket[], ResultSetHeader]' is not comparable to type 'DbRow'. - Index signature for type 'string' is missing in type '[RowDataPacket[], ResultSetHeader]'. -src/app/api/warehouse/inbound/cutting/route.ts(478,52): error TS2322: Type 'DbRow' is not assignable to type 'SqlValue'. - Type 'DbRow' is missing the following properties from type 'Buffer': slice, subarray, write, toJSON, and 102 more. -src/app/api/warehouse/inbound/cutting/route.ts(478,71): error TS2322: Type 'number | DbRow' is not assignable to type 'SqlValue'. - Type 'DbRow' is not assignable to type 'SqlValue'. - Type 'DbRow' is missing the following properties from type 'Buffer': slice, subarray, write, toJSON, and 102 more. -src/app/api/warehouse/inbound/cutting/route.ts(478,81): error TS2322: Type 'number | DbRow' is not assignable to type 'SqlValue'. - Type 'DbRow' is not assignable to type 'SqlValue'. - Type 'DbRow' is missing the following properties from type 'Buffer': slice, subarray, write, toJSON, and 102 more. -src/app/api/warehouse/inbound/cutting/route.ts(479,45): error TS2345: Argument of type 'DbRow[]' is not assignable to parameter of type 'SqlValue[]'. - Type 'DbRow' is not assignable to type 'SqlValue'. - Type 'DbRow' is missing the following properties from type 'Buffer': slice, subarray, write, toJSON, and 102 more. -src/app/api/warehouse/inbound/route.ts(105,16): error TS2345: Argument of type '(iss: DbRow) => string' is not assignable to parameter of type '(value: $ZodIssue, index: number, array: $ZodIssue[]) => string'. - Types of parameters 'iss' and 'value' are incompatible. - Type '$ZodIssue' is not assignable to type 'DbRow'. - Type '$ZodIssueUnrecognizedKeys' is not assignable to type 'DbRow'. - Index signature for type 'string' is missing in type '$ZodIssueUnrecognizedKeys'. -src/app/api/warehouse/inbound/route.ts(105,35): error TS18046: 'iss.path' is of type 'unknown'. -src/app/api/warehouse/inbound/route.ts(113,26): error TS18046: 'validated' is of type 'unknown'. -src/app/api/warehouse/inbound/route.ts(117,18): error TS18046: 'validated' is of type 'unknown'. -src/app/api/warehouse/inbound/route.ts(120,59): error TS2345: Argument of type 'unknown[]' is not assignable to parameter of type '(number | null | undefined)[]'. - Type 'unknown' is not assignable to type 'number | null | undefined'. -src/app/api/warehouse/inbound/route.ts(139,20): error TS18046: 'validated' is of type 'unknown'. -src/app/api/warehouse/inbound/route.ts(140,21): error TS18046: 'validated' is of type 'unknown'. -src/app/api/warehouse/inbound/route.ts(141,20): error TS18046: 'validated' is of type 'unknown'. -src/app/api/warehouse/inbound/route.ts(142,17): error TS18046: 'validated' is of type 'unknown'. -src/app/api/warehouse/inbound/route.ts(143,15): error TS18046: 'validated' is of type 'unknown'. -src/app/api/warehouse/inbound/route.ts(144,19): error TS18046: 'validated' is of type 'unknown'. -src/app/api/warehouse/inbound/route.ts(145,22): error TS18046: 'validated' is of type 'unknown'. -src/app/api/warehouse/inbound/route.ts(147,14): error TS18046: 'validated' is of type 'unknown'. -src/app/api/warehouse/inbound/route.ts(183,16): error TS2345: Argument of type '(iss: DbRow) => string' is not assignable to parameter of type '(value: $ZodIssue, index: number, array: $ZodIssue[]) => string'. - Types of parameters 'iss' and 'value' are incompatible. - Type '$ZodIssue' is not assignable to type 'DbRow'. - Type '$ZodIssueUnrecognizedKeys' is not assignable to type 'DbRow'. - Index signature for type 'string' is missing in type '$ZodIssueUnrecognizedKeys'. -src/app/api/warehouse/inbound/route.ts(183,35): error TS18046: 'iss.path' is of type 'unknown'. -src/app/api/warehouse/inbound/route.ts(190,13): error TS2339: Property 'id' does not exist on type 'unknown'. -src/app/api/warehouse/inbound/route.ts(190,17): error TS2339: Property 'action' does not exist on type 'unknown'. -src/app/api/warehouse/inbound/route.ts(190,25): error TS2339: Property 'status' does not exist on type 'unknown'. -src/app/api/warehouse/inbound/route.ts(190,33): error TS2339: Property 'remark' does not exist on type 'unknown'. -src/app/api/warehouse/inbound/route.ts(216,30): error TS18046: 'validated' is of type 'unknown'. -src/app/api/warehouse/inbound/route.ts(226,25): error TS18046: 'validated' is of type 'unknown'. -src/app/api/warehouse/inbound/route.ts(227,24): error TS18046: 'validated' is of type 'unknown'. -src/app/api/warehouse/inbound/route.ts(228,24): error TS18046: 'validated' is of type 'unknown'. -src/app/api/warehouse/inbound/route.ts(229,21): error TS18046: 'validated' is of type 'unknown'. -src/app/api/warehouse/inbound/route.ts(230,19): error TS18046: 'validated' is of type 'unknown'. -src/app/api/warehouse/inbound/scan/route.ts(36,8): error TS2322: Type 'unknown' is not assignable to type 'SqlValue'. -src/app/api/warehouse/ink-mixing/route.ts(86,39): error TS2339: Property 'insertId' does not exist on type 'QueryResult'. - Property 'insertId' does not exist on type 'ResultSetHeader[]'. -src/app/api/warehouse/ink-mixing/route.ts(98,27): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/warehouse/ink-mixing/route.ts(102,27): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/warehouse/ink-mixing/route.ts(141,39): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/warehouse/ink-mixing/route.ts(141,52): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/warehouse/ink-mixing/route.ts(177,21): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/warehouse/ink-mixing/route.ts(181,10): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/warehouse/ink-mixing/route.ts(191,21): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/warehouse/ink-mixing/route.ts(195,10): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/warehouse/ink-opening/route.ts(103,31): error TS2339: Property 'insertId' does not exist on type 'QueryResult'. - Property 'insertId' does not exist on type 'ResultSetHeader[]'. -src/app/api/warehouse/ink-opening/route.ts(124,22): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/warehouse/ink-opening/route.ts(128,22): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/warehouse/inventory/export/route.ts(105,17): error TS2538: Type 'unknown' cannot be used as an index type. -src/app/api/warehouse/inventory/export/route.ts(112,26): error TS2339: Property 'replace' does not exist on type '{}'. -src/app/api/warehouse/inventory/route.ts(85,30): error TS2345: Argument of type '{}' is not assignable to parameter of type 'string'. -src/app/api/warehouse/inventory/route.ts(86,27): error TS2345: Argument of type '{}' is not assignable to parameter of type 'string'. -src/app/api/warehouse/inventory/route.ts(87,27): error TS2345: Argument of type '{}' is not assignable to parameter of type 'string'. -src/app/api/warehouse/inventory/route.ts(88,27): error TS2345: Argument of type '{}' is not assignable to parameter of type 'string'. -src/app/api/warehouse/inventory/route.ts(89,26): error TS2345: Argument of type '{}' is not assignable to parameter of type 'string'. -src/app/api/warehouse/inventory/route.ts(90,30): error TS2345: Argument of type '{}' is not assignable to parameter of type 'string'. -src/app/api/warehouse/inventory/route.ts(91,32): error TS2345: Argument of type '{}' is not assignable to parameter of type 'string'. -src/app/api/warehouse/inventory/route.ts(92,28): error TS2345: Argument of type '{}' is not assignable to parameter of type 'string'. -src/app/api/warehouse/inventory/route.ts(93,28): error TS2345: Argument of type '{}' is not assignable to parameter of type 'string'. -src/app/api/warehouse/monthly-report/route.ts(103,30): error TS2345: Argument of type 'unknown' is not assignable to parameter of type 'string'. -src/app/api/warehouse/monthly-report/route.ts(104,30): error TS2345: Argument of type 'unknown' is not assignable to parameter of type 'string'. -src/app/api/warehouse/monthly-report/route.ts(128,28): error TS2345: Argument of type 'unknown' is not assignable to parameter of type 'string'. -src/app/api/warehouse/monthly-report/route.ts(131,31): error TS2345: Argument of type 'unknown' is not assignable to parameter of type 'string'. -src/app/api/warehouse/monthly-report/route.ts(133,30): error TS2345: Argument of type 'unknown' is not assignable to parameter of type 'string'. -src/app/api/warehouse/monthly-report/route.ts(134,32): error TS2345: Argument of type 'unknown' is not assignable to parameter of type 'string'. -src/app/api/warehouse/monthly-report/route.ts(140,7): error TS2571: Object is of type 'unknown'. -src/app/api/warehouse/monthly-report/route.ts(140,31): error TS2571: Object is of type 'unknown'. -src/app/api/warehouse/monthly-report/route.ts(141,7): error TS2571: Object is of type 'unknown'. -src/app/api/warehouse/monthly-report/route.ts(141,31): error TS2571: Object is of type 'unknown'. -src/app/api/warehouse/monthly-report/route.ts(150,12): error TS18046: 'r' is of type 'unknown'. -src/app/api/warehouse/monthly-report/route.ts(151,12): error TS18046: 'r' is of type 'unknown'. -src/app/api/warehouse/outbound/confirm/route.ts(43,35): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/warehouse/outbound/confirm/route.ts(47,24): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/warehouse/outbound/confirm/route.ts(64,49): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/warehouse/outbound/confirm/route.ts(66,33): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/warehouse/outbound/confirm/route.ts(70,26): error TS2488: Type 'QueryResult' must have a '[Symbol.iterator]()' method that returns an iterator. -src/app/api/warehouse/outbound/confirm/route.ts(108,38): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/warehouse/outbound/confirm/route.ts(120,15): error TS2345: Argument of type 'PoolConnection' is not assignable to parameter of type 'DbConnection'. - Types of property 'execute' are incompatible. - Type '{ (sql: string, values?: ExecuteValues | undefined): Promise<[T, FieldPacket[]]>; (options: QueryOptions, values?: ExecuteValues | undefined): Promise<...>; }' is not assignable to type '(sql: string, values?: unknown[] | undefined) => Promise>'. - Types of parameters 'values' and 'values' are incompatible. - Type 'unknown[] | undefined' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues[]'. - Type 'unknown' is not assignable to type 'ExecuteValues'. -src/app/api/warehouse/outbound/confirm/route.ts(132,15): error TS2345: Argument of type 'PoolConnection' is not assignable to parameter of type 'DbConnection'. - Types of property 'execute' are incompatible. - Type '{ (sql: string, values?: ExecuteValues | undefined): Promise<[T, FieldPacket[]]>; (options: QueryOptions, values?: ExecuteValues | undefined): Promise<...>; }' is not assignable to type '(sql: string, values?: unknown[] | undefined) => Promise>'. - Types of parameters 'values' and 'values' are incompatible. - Type 'unknown[] | undefined' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues[]'. - Type 'unknown' is not assignable to type 'ExecuteValues'. -src/app/api/warehouse/outbound/confirm/route.ts(144,35): error TS2345: Argument of type 'DbRow' is not assignable to parameter of type 'SqlValue'. - Type 'DbRow' is missing the following properties from type 'Buffer': slice, subarray, write, toJSON, and 102 more. -src/app/api/warehouse/outbound/confirm/route.ts(145,51): error TS2345: Argument of type '(a: DbRow) => unknown' is not assignable to parameter of type '(value: WidthSlitAlloc, index: number, array: WidthSlitAlloc[]) => unknown'. - Types of parameters 'a' and 'value' are incompatible. - Type 'WidthSlitAlloc' is not assignable to type 'DbRow'. - Index signature for type 'string' is missing in type 'WidthSlitAlloc'. -src/app/api/warehouse/outbound/confirm/route.ts(155,15): error TS2345: Argument of type 'PoolConnection' is not assignable to parameter of type 'DbConnection'. - Types of property 'execute' are incompatible. - Type '{ (sql: string, values?: ExecuteValues | undefined): Promise<[T, FieldPacket[]]>; (options: QueryOptions, values?: ExecuteValues | undefined): Promise<...>; }' is not assignable to type '(sql: string, values?: unknown[] | undefined) => Promise>'. - Types of parameters 'values' and 'values' are incompatible. - Type 'unknown[] | undefined' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues[]'. - Type 'unknown' is not assignable to type 'ExecuteValues'. -src/app/api/warehouse/outbound/confirm/route.ts(175,15): error TS2345: Argument of type 'PoolConnection' is not assignable to parameter of type 'DbConnection'. - Types of property 'execute' are incompatible. - Type '{ (sql: string, values?: ExecuteValues | undefined): Promise<[T, FieldPacket[]]>; (options: QueryOptions, values?: ExecuteValues | undefined): Promise<...>; }' is not assignable to type '(sql: string, values?: unknown[] | undefined) => Promise>'. - Types of parameters 'values' and 'values' are incompatible. - Type 'unknown[] | undefined' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues[]'. - Type 'unknown' is not assignable to type 'ExecuteValues'. -src/app/api/warehouse/outbound/confirm/route.ts(188,35): error TS2345: Argument of type 'DbRow' is not assignable to parameter of type 'SqlValue'. - Type 'DbRow' is missing the following properties from type 'Buffer': slice, subarray, write, toJSON, and 102 more. -src/app/api/warehouse/outbound/confirm/route.ts(193,57): error TS2345: Argument of type '(a: DbRow) => unknown' is not assignable to parameter of type '(value: FIFOAllocationItem, index: number, array: FIFOAllocationItem[]) => unknown'. - Types of parameters 'a' and 'value' are incompatible. - Type 'FIFOAllocationItem' is not assignable to type 'DbRow'. - Index signature for type 'string' is missing in type 'FIFOAllocationItem'. -src/app/api/warehouse/outbound/confirm/route.ts(203,42): error TS2345: Argument of type 'PoolConnection' is not assignable to parameter of type 'DbConnection'. - Types of property 'execute' are incompatible. - Type '{ (sql: string, values?: ExecuteValues | undefined): Promise<[T, FieldPacket[]]>; (options: QueryOptions, values?: ExecuteValues | undefined): Promise<...>; }' is not assignable to type '(sql: string, values?: unknown[] | undefined) => Promise>'. - Types of parameters 'values' and 'values' are incompatible. - Type 'unknown[] | undefined' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues[]'. - Type 'unknown' is not assignable to type 'ExecuteValues'. -src/app/api/warehouse/outbound/confirm/route.ts(217,41): error TS2345: Argument of type 'PoolConnection' is not assignable to parameter of type 'DbConnection'. - Types of property 'execute' are incompatible. - Type '{ (sql: string, values?: ExecuteValues | undefined): Promise<[T, FieldPacket[]]>; (options: QueryOptions, values?: ExecuteValues | undefined): Promise<...>; }' is not assignable to type '(sql: string, values?: unknown[] | undefined) => Promise>'. - Types of parameters 'values' and 'values' are incompatible. - Type 'unknown[] | undefined' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues[]'. - Type 'unknown' is not assignable to type 'ExecuteValues'. -src/app/api/warehouse/outbound/confirm/route.ts(233,29): error TS2339: Property 'affectedRows' does not exist on type 'QueryResult'. - Property 'affectedRows' does not exist on type 'ResultSetHeader[]'. -src/app/api/warehouse/outbound/confirm/route.ts(243,34): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/warehouse/outbound/confirm/route.ts(243,48): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/warehouse/outbound/confirm/route.ts(244,26): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/warehouse/outbound/confirm/route.ts(269,34): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/warehouse/outbound/confirm/route.ts(269,48): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/warehouse/outbound/confirm/route.ts(270,30): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/warehouse/outbound/confirm/route.ts(272,38): error TS2339: Property 'reduce' does not exist on type 'QueryResult'. - Property 'reduce' does not exist on type 'OkPacket'. -src/app/api/warehouse/outbound/confirm/route.ts(295,13): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/warehouse/outbound/confirm/route.ts(295,38): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/warehouse/outbound/confirm/route.ts(295,66): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/warehouse/outbound/confirm/route.ts(300,20): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/warehouse/outbound/confirm/route.ts(358,35): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/warehouse/outbound/confirm/route.ts(362,21): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/warehouse/outbound/confirm/route.ts(399,22): error TS2339: Property 'affectedRows' does not exist on type 'QueryResult'. - Property 'affectedRows' does not exist on type 'ResultSetHeader[]'. -src/app/api/warehouse/outbound/confirm/route.ts(412,40): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/warehouse/outbound/confirm/route.ts(435,42): error TS2345: Argument of type 'PoolConnection' is not assignable to parameter of type 'DbConnection'. - Types of property 'execute' are incompatible. - Type '{ (sql: string, values?: ExecuteValues | undefined): Promise<[T, FieldPacket[]]>; (options: QueryOptions, values?: ExecuteValues | undefined): Promise<...>; }' is not assignable to type '(sql: string, values?: unknown[] | undefined) => Promise>'. - Types of parameters 'values' and 'values' are incompatible. - Type 'unknown[] | undefined' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues[]'. - Type 'unknown' is not assignable to type 'ExecuteValues'. -src/app/api/warehouse/outbound/confirm/route.ts(448,41): error TS2345: Argument of type 'PoolConnection' is not assignable to parameter of type 'DbConnection'. - Types of property 'execute' are incompatible. - Type '{ (sql: string, values?: ExecuteValues | undefined): Promise<[T, FieldPacket[]]>; (options: QueryOptions, values?: ExecuteValues | undefined): Promise<...>; }' is not assignable to type '(sql: string, values?: unknown[] | undefined) => Promise>'. - Types of parameters 'values' and 'values' are incompatible. - Type 'unknown[] | undefined' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues[]'. - Type 'unknown' is not assignable to type 'ExecuteValues'. -src/app/api/warehouse/outbound/fifo/route.ts(87,36): error TS2345: Argument of type 'unknown' is not assignable to parameter of type 'string'. -src/app/api/warehouse/outbound/fifo/route.ts(96,11): error TS2322: Type '(sql: string, values?: SqlValue[] | undefined) => Promise' is not assignable to type '(sql: string, values?: unknown[] | undefined) => Promise>'. - Types of parameters 'values' and 'values' are incompatible. - Type 'unknown[] | undefined' is not assignable to type 'SqlValue[] | undefined'. - Type 'unknown[]' is not assignable to type 'SqlValue[]'. - Type 'unknown' is not assignable to type 'SqlValue'. -src/app/api/warehouse/outbound/fifo/route.ts(155,21): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/warehouse/outbound/fifo/route.ts(183,21): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/warehouse/outbound/fifo/route.ts(187,29): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/warehouse/outbound/fifo/route.ts(219,49): error TS2345: Argument of type 'PoolConnection' is not assignable to parameter of type 'DbConnection'. - Types of property 'execute' are incompatible. - Type '{ (sql: string, values?: ExecuteValues | undefined): Promise<[T, FieldPacket[]]>; (options: QueryOptions, values?: ExecuteValues | undefined): Promise<...>; }' is not assignable to type '(sql: string, values?: unknown[] | undefined) => Promise>'. - Types of parameters 'values' and 'values' are incompatible. - Type 'unknown[] | undefined' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues[]'. - Type 'unknown' is not assignable to type 'ExecuteValues'. -src/app/api/warehouse/outbound/fifo/route.ts(244,26): error TS2339: Property 'affectedRows' does not exist on type 'QueryResult'. - Property 'affectedRows' does not exist on type 'ResultSetHeader[]'. -src/app/api/warehouse/outbound/fifo/route.ts(249,13): error TS2353: Object literal may only specify known properties, and 'material_id' does not exist in type 'Date | Buffer | readonly SqlValue[]'. -src/app/api/warehouse/outbound/fifo/route.ts(283,35): error TS2339: Property 'insertId' does not exist on type 'QueryResult'. - Property 'insertId' does not exist on type 'ResultSetHeader[]'. -src/app/api/warehouse/outbound/fifo/route.ts(293,13): error TS18049: 'obItem' is possibly 'null' or 'undefined'. -src/app/api/warehouse/outbound/fifo/route.ts(293,20): error TS2339: Property 'material_id' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'material_id' does not exist on type 'string'. -src/app/api/warehouse/outbound/fifo/route.ts(294,13): error TS18049: 'obItem' is possibly 'null' or 'undefined'. -src/app/api/warehouse/outbound/fifo/route.ts(294,20): error TS2339: Property 'material_name' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'material_name' does not exist on type 'string'. -src/app/api/warehouse/outbound/fifo/route.ts(295,13): error TS18049: 'obItem' is possibly 'null' or 'undefined'. -src/app/api/warehouse/outbound/fifo/route.ts(295,20): error TS2339: Property 'qty' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'qty' does not exist on type 'string'. -src/app/api/warehouse/outbound/fifo/route.ts(296,13): error TS18049: 'obItem' is possibly 'null' or 'undefined'. -src/app/api/warehouse/outbound/fifo/route.ts(296,20): error TS2339: Property 'unit' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'unit' does not exist on type 'string'. -src/app/api/warehouse/outbound/fifo/route.ts(297,13): error TS18049: 'obItem' is possibly 'null' or 'undefined'. -src/app/api/warehouse/outbound/fifo/route.ts(297,20): error TS2339: Property 'unit_cost' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'unit_cost' does not exist on type 'string'. -src/app/api/warehouse/outbound/fifo/route.ts(298,13): error TS18049: 'obItem' is possibly 'null' or 'undefined'. -src/app/api/warehouse/outbound/fifo/route.ts(298,20): error TS2339: Property 'amount' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'amount' does not exist on type 'string'. -src/app/api/warehouse/outbound/fifo/route.ts(299,13): error TS18049: 'obItem' is possibly 'null' or 'undefined'. -src/app/api/warehouse/outbound/fifo/route.ts(299,20): error TS2339: Property 'batch_no' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'batch_no' does not exist on type 'string'. -src/app/api/warehouse/outbound/fifo/route.ts(300,25): error TS18049: 'obItem' is possibly 'null' or 'undefined'. -src/app/api/warehouse/outbound/fifo/route.ts(300,32): error TS2339: Property 'batch_no' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'batch_no' does not exist on type 'string'. -src/app/api/warehouse/outbound/fifo/route.ts(346,18): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/warehouse/outbound/fifo/route.ts(350,21): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/warehouse/outbound/fifo/route.ts(361,17): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/warehouse/outbound/fifo/route.ts(377,23): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/warehouse/outbound/fifo/route.ts(381,23): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/warehouse/outbound/fifo/route.ts(399,26): error TS2339: Property 'affectedRows' does not exist on type 'QueryResult'. - Property 'affectedRows' does not exist on type 'ResultSetHeader[]'. -src/app/api/warehouse/outbound/fifo/route.ts(407,38): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/warehouse/outbound/fifo/route.ts(407,62): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/warehouse/outbound/fifo/route.ts(410,34): error TS2345: Argument of type 'PoolConnection' is not assignable to parameter of type 'DbConnection'. - Types of property 'execute' are incompatible. - Type '{ (sql: string, values?: ExecuteValues | undefined): Promise<[T, FieldPacket[]]>; (options: QueryOptions, values?: ExecuteValues | undefined): Promise<...>; }' is not assignable to type '(sql: string, values?: unknown[] | undefined) => Promise>'. - Types of parameters 'values' and 'values' are incompatible. - Type 'unknown[] | undefined' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues[]'. - Type 'unknown' is not assignable to type 'ExecuteValues'. -src/app/api/warehouse/outbound/fifo/route.ts(425,11): error TS2353: Object literal may only specify known properties, and 'batch_id' does not exist in type 'Date | Buffer | readonly SqlValue[]'. -src/app/api/warehouse/outbound/fifo/route.ts(433,42): error TS2345: Argument of type 'PoolConnection' is not assignable to parameter of type 'DbConnection'. - Types of property 'execute' are incompatible. - Type '{ (sql: string, values?: ExecuteValues | undefined): Promise<[T, FieldPacket[]]>; (options: QueryOptions, values?: ExecuteValues | undefined): Promise<...>; }' is not assignable to type '(sql: string, values?: unknown[] | undefined) => Promise>'. - Types of parameters 'values' and 'values' are incompatible. - Type 'unknown[] | undefined' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues[]'. - Type 'unknown' is not assignable to type 'ExecuteValues'. -src/app/api/warehouse/outbound/fifo/route.ts(446,41): error TS2345: Argument of type 'PoolConnection' is not assignable to parameter of type 'DbConnection'. - Types of property 'execute' are incompatible. - Type '{ (sql: string, values?: ExecuteValues | undefined): Promise<[T, FieldPacket[]]>; (options: QueryOptions, values?: ExecuteValues | undefined): Promise<...>; }' is not assignable to type '(sql: string, values?: unknown[] | undefined) => Promise>'. - Types of parameters 'values' and 'values' are incompatible. - Type 'unknown[] | undefined' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues[]'. - Type 'unknown' is not assignable to type 'ExecuteValues'. -src/app/api/warehouse/outbound/fifo/route.ts(462,29): error TS2339: Property 'affectedRows' does not exist on type 'QueryResult'. - Property 'affectedRows' does not exist on type 'ResultSetHeader[]'. -src/app/api/warehouse/outbound/route.ts(109,7): error TS2345: Argument of type 'unknown[]' is not assignable to parameter of type 'SqlValue[]'. - Type 'unknown' is not assignable to type 'SqlValue'. -src/app/api/warehouse/outbound/route.ts(169,59): error TS2345: Argument of type 'unknown[]' is not assignable to parameter of type '(number | null | undefined)[]'. - Type 'unknown' is not assignable to type 'number | null | undefined'. -src/app/api/warehouse/outbound/route.ts(192,59): error TS2345: Argument of type 'unknown' is not assignable to parameter of type 'string'. -src/app/api/warehouse/outbound/route.ts(197,31): error TS2345: Argument of type 'unknown' is not assignable to parameter of type 'string'. -src/app/api/warehouse/outbound/route.ts(197,61): error TS2345: Argument of type 'unknown' is not assignable to parameter of type 'string'. -src/app/api/warehouse/outbound/route.ts(224,26): error TS2352: Conversion of type 'QueryResult' to type 'DbRow' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first. - Type '[RowDataPacket[], ResultSetHeader]' is not comparable to type 'DbRow'. - Index signature for type 'string' is missing in type '[RowDataPacket[], ResultSetHeader]'. -src/app/api/warehouse/outbound/route.ts(236,25): error TS2345: Argument of type 'unknown' is not assignable to parameter of type 'string'. -src/app/api/warehouse/outbound/route.ts(236,55): error TS2345: Argument of type 'unknown' is not assignable to parameter of type 'string'. -src/app/api/warehouse/outbound/route.ts(239,45): error TS2345: Argument of type '{}' is not assignable to parameter of type 'string'. -src/app/api/warehouse/production-inbound/route.ts(89,18): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/warehouse/production-inbound/route.ts(92,18): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/warehouse/production-inbound/route.ts(114,35): error TS2339: Property 'insertId' does not exist on type 'QueryResult'. - Property 'insertId' does not exist on type 'ResultSetHeader[]'. -src/app/api/warehouse/production-inbound/route.ts(152,23): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/warehouse/production-inbound/route.ts(156,23): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/warehouse/production-inbound/route.ts(182,35): error TS2339: Property 'reduce' does not exist on type 'QueryResult'. - Property 'reduce' does not exist on type 'OkPacket'. -src/app/api/warehouse/production-inbound/route.ts(197,20): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/warehouse/production-inbound/route.ts(198,22): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/warehouse/production-inbound/route.ts(212,42): error TS2339: Property 'reduce' does not exist on type 'QueryResult'. - Property 'reduce' does not exist on type 'OkPacket'. -src/app/api/warehouse/production-inbound/route.ts(222,24): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/warehouse/route.ts(112,17): error TS2322: Type 'SqlValue[]' is not assignable to type 'DbRow[]'. - Type 'SqlValue' is not assignable to type 'DbRow'. - Type 'undefined' is not assignable to type 'DbRow'. -src/app/api/warehouse/route.ts(119,35): error TS2538: Type 'unknown' cannot be used as an index type. -src/app/api/warehouse/route.ts(121,5): error TS2322: Type '{}' is not assignable to type 'string'. -src/app/api/warehouse/route.ts(126,5): error TS2322: Type '{}' is not assignable to type 'string'. -src/app/api/warehouse/route.ts(127,5): error TS2322: Type 'unknown' is not assignable to type 'number | undefined'. -src/app/api/warehouse/route.ts(180,50): error TS2345: Argument of type '(number | DbRow)[]' is not assignable to parameter of type 'SqlValue[]'. - Type 'number | DbRow' is not assignable to type 'SqlValue'. - Type 'DbRow' is not assignable to type 'SqlValue'. - Type 'DbRow' is missing the following properties from type 'Buffer': slice, subarray, write, toJSON, and 102 more. -src/app/api/warehouse/route.ts(186,46): error TS2345: Argument of type 'DbRow[]' is not assignable to parameter of type 'SqlValue[]'. - Type 'DbRow' is not assignable to type 'SqlValue'. - Type 'DbRow' is missing the following properties from type 'Buffer': slice, subarray, write, toJSON, and 102 more. -src/app/api/warehouse/route.ts(225,36): error TS2571: Object is of type 'unknown'. -src/app/api/warehouse/route.ts(225,37): error TS2352: Conversion of type 'PoolConnection' to type 'DbRow' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first. - Index signature for type 'string' is missing in type 'PoolConnection'. -src/app/api/warehouse/route.ts(296,36): error TS2571: Object is of type 'unknown'. -src/app/api/warehouse/route.ts(296,37): error TS2352: Conversion of type 'PoolConnection' to type 'DbRow' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first. - Index signature for type 'string' is missing in type 'PoolConnection'. -src/app/api/warehouse/sales-outbound/route.ts(82,57): error TS2345: Argument of type 'unknown[]' is not assignable to parameter of type '(number | null | undefined)[]'. - Type 'unknown' is not assignable to type 'number | null | undefined'. -src/app/api/warehouse/sales-outbound/route.ts(113,21): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/warehouse/sales-outbound/route.ts(116,11): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/warehouse/sales-outbound/route.ts(119,11): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/warehouse/sales-outbound/route.ts(134,19): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/warehouse/sales-outbound/route.ts(138,18): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/warehouse/sales-outbound/route.ts(140,68): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/warehouse/sales-outbound/route.ts(159,36): error TS2339: Property 'insertId' does not exist on type 'QueryResult'. - Property 'insertId' does not exist on type 'ResultSetHeader[]'. -src/app/api/warehouse/sales-outbound/route.ts(200,24): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/warehouse/sales-outbound/route.ts(204,24): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/warehouse/sales-outbound/route.ts(221,21): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/warehouse/sales-outbound/route.ts(225,21): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/warehouse/sales-outbound/route.ts(248,43): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/warehouse/sales-outbound/route.ts(248,56): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/warehouse/sales-outbound/route.ts(290,33): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/warehouse/sales-outbound/route.ts(290,46): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/warehouse/sales-outbound/route.ts(338,39): error TS2339: Property 'reduce' does not exist on type 'QueryResult'. - Property 'reduce' does not exist on type 'OkPacket'. -src/app/api/warehouse/sales-outbound/route.ts(349,44): error TS2339: Property 'filter' does not exist on type 'QueryResult'. - Property 'filter' does not exist on type 'OkPacket'. -src/app/api/warehouse/sales-outbound/route.ts(369,45): error TS2339: Property 'every' does not exist on type 'QueryResult'. - Property 'every' does not exist on type 'OkPacket'. -src/app/api/warehouse/sales-outbound/route.ts(372,45): error TS2339: Property 'some' does not exist on type 'QueryResult'. - Property 'some' does not exist on type 'OkPacket'. -src/app/api/warehouse/split-order/route.ts(65,34): error TS2362: The left-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. -src/app/api/warehouse/split-order/route.ts(66,60): error TS2322: Type '{}' is not assignable to type 'number'. -src/app/api/warehouse/split-order/route.ts(87,39): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/warehouse/split-order/route.ts(91,21): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/warehouse/split-order/route.ts(118,49): error TS2339: Property 'slice' does not exist on type '{}'. -src/app/api/warehouse/split-order/route.ts(199,29): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/warehouse/split-order/route.ts(203,21): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/warehouse/split-order/route.ts(214,33): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/warehouse/split-order/route.ts(292,42): error TS2339: Property 'slice' does not exist on type '{}'. -src/app/api/warehouse/split-order/route.ts(332,44): error TS2345: Argument of type 'PoolConnection' is not assignable to parameter of type 'DbConnection'. - Types of property 'execute' are incompatible. - Type '{ (sql: string, values?: ExecuteValues | undefined): Promise<[T, FieldPacket[]]>; (options: QueryOptions, values?: ExecuteValues | undefined): Promise<...>; }' is not assignable to type '(sql: string, values?: unknown[] | undefined) => Promise>'. - Types of parameters 'values' and 'values' are incompatible. - Type 'unknown[] | undefined' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues[]'. - Type 'unknown' is not assignable to type 'ExecuteValues'. -src/app/api/warehouse/split-order/route.ts(389,42): error TS2345: Argument of type 'PoolConnection' is not assignable to parameter of type 'DbConnection'. - Types of property 'execute' are incompatible. - Type '{ (sql: string, values?: ExecuteValues | undefined): Promise<[T, FieldPacket[]]>; (options: QueryOptions, values?: ExecuteValues | undefined): Promise<...>; }' is not assignable to type '(sql: string, values?: unknown[] | undefined) => Promise>'. - Types of parameters 'values' and 'values' are incompatible. - Type 'unknown[] | undefined' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues[]'. - Type 'unknown' is not assignable to type 'ExecuteValues'. -src/app/api/warehouse/split-order/route.ts(405,41): error TS2345: Argument of type 'PoolConnection' is not assignable to parameter of type 'DbConnection'. - Types of property 'execute' are incompatible. - Type '{ (sql: string, values?: ExecuteValues | undefined): Promise<[T, FieldPacket[]]>; (options: QueryOptions, values?: ExecuteValues | undefined): Promise<...>; }' is not assignable to type '(sql: string, values?: unknown[] | undefined) => Promise>'. - Types of parameters 'values' and 'values' are incompatible. - Type 'unknown[] | undefined' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues[]'. - Type 'unknown' is not assignable to type 'ExecuteValues'. -src/app/api/warehouse/split-order/route.ts(407,34): error TS2345: Argument of type 'PoolConnection' is not assignable to parameter of type 'DbConnection'. - Types of property 'execute' are incompatible. - Type '{ (sql: string, values?: ExecuteValues | undefined): Promise<[T, FieldPacket[]]>; (options: QueryOptions, values?: ExecuteValues | undefined): Promise<...>; }' is not assignable to type '(sql: string, values?: unknown[] | undefined) => Promise>'. - Types of parameters 'values' and 'values' are incompatible. - Type 'unknown[] | undefined' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues[]'. - Type 'unknown' is not assignable to type 'ExecuteValues'. -src/app/api/warehouse/split-order/route.ts(423,36): error TS2345: Argument of type 'PoolConnection' is not assignable to parameter of type 'DbConnection'. - Types of property 'execute' are incompatible. - Type '{ (sql: string, values?: ExecuteValues | undefined): Promise<[T, FieldPacket[]]>; (options: QueryOptions, values?: ExecuteValues | undefined): Promise<...>; }' is not assignable to type '(sql: string, values?: unknown[] | undefined) => Promise>'. - Types of parameters 'values' and 'values' are incompatible. - Type 'unknown[] | undefined' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues[]'. - Type 'unknown' is not assignable to type 'ExecuteValues'. -src/app/api/warehouse/split-order/route.ts(440,36): error TS2345: Argument of type 'PoolConnection' is not assignable to parameter of type 'DbConnection'. - Types of property 'execute' are incompatible. - Type '{ (sql: string, values?: ExecuteValues | undefined): Promise<[T, FieldPacket[]]>; (options: QueryOptions, values?: ExecuteValues | undefined): Promise<...>; }' is not assignable to type '(sql: string, values?: unknown[] | undefined) => Promise>'. - Types of parameters 'values' and 'values' are incompatible. - Type 'unknown[] | undefined' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues[]'. - Type 'unknown' is not assignable to type 'ExecuteValues'. -src/app/api/warehouse/stocktaking/[id]/items/route.ts(38,41): error TS2538: Type 'unknown' cannot be used as an index type. -src/app/api/warehouse/stocktaking/[id]/items/route.ts(39,33): error TS2538: Type 'unknown' cannot be used as an index type. -src/app/api/warehouse/stocktaking/route.ts(65,27): error TS2538: Type 'unknown' cannot be used as an index type. -src/app/api/warehouse/stocktaking/route.ts(66,31): error TS2538: Type 'unknown' cannot be used as an index type. -src/app/api/warehouse/stocktaking/route.ts(225,44): error TS2339: Property 'quantity' does not exist on type '{}'. -src/app/api/warehouse/transfer/[id]/inbound/route.ts(85,22): error TS2769: No overload matches this call. - Overload 1 of 2, '(sql: string, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Type 'unknown' is not assignable to type 'ExecuteValues'. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/warehouse/transfer/[id]/inbound/route.ts(112,42): error TS2345: Argument of type 'PoolConnection' is not assignable to parameter of type 'DbConnection'. - Types of property 'execute' are incompatible. - Type '{ (sql: string, values?: ExecuteValues | undefined): Promise<[T, FieldPacket[]]>; (options: QueryOptions, values?: ExecuteValues | undefined): Promise<...>; }' is not assignable to type '(sql: string, values?: unknown[] | undefined) => Promise>'. - Types of parameters 'values' and 'values' are incompatible. - Type 'unknown[] | undefined' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues[]'. - Type 'unknown' is not assignable to type 'ExecuteValues'. -src/app/api/warehouse/transfer/[id]/inbound/route.ts(133,41): error TS2345: Argument of type 'PoolConnection' is not assignable to parameter of type 'DbConnection'. - Types of property 'execute' are incompatible. - Type '{ (sql: string, values?: ExecuteValues | undefined): Promise<[T, FieldPacket[]]>; (options: QueryOptions, values?: ExecuteValues | undefined): Promise<...>; }' is not assignable to type '(sql: string, values?: unknown[] | undefined) => Promise>'. - Types of parameters 'values' and 'values' are incompatible. - Type 'unknown[] | undefined' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues[]'. - Type 'unknown' is not assignable to type 'ExecuteValues'. -src/app/api/warehouse/transfer/[id]/outbound/route.ts(78,22): error TS2769: No overload matches this call. - Overload 1 of 2, '(sql: string, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Type 'unknown' is not assignable to type 'ExecuteValues'. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/warehouse/transfer/[id]/outbound/route.ts(88,42): error TS2345: Argument of type 'PoolConnection' is not assignable to parameter of type 'DbConnection'. - Types of property 'execute' are incompatible. - Type '{ (sql: string, values?: ExecuteValues | undefined): Promise<[T, FieldPacket[]]>; (options: QueryOptions, values?: ExecuteValues | undefined): Promise<...>; }' is not assignable to type '(sql: string, values?: unknown[] | undefined) => Promise>'. - Types of parameters 'values' and 'values' are incompatible. - Type 'unknown[] | undefined' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues[]'. - Type 'unknown' is not assignable to type 'ExecuteValues'. -src/app/api/warehouse/transfer/[id]/outbound/route.ts(109,41): error TS2345: Argument of type 'PoolConnection' is not assignable to parameter of type 'DbConnection'. - Types of property 'execute' are incompatible. - Type '{ (sql: string, values?: ExecuteValues | undefined): Promise<[T, FieldPacket[]]>; (options: QueryOptions, values?: ExecuteValues | undefined): Promise<...>; }' is not assignable to type '(sql: string, values?: unknown[] | undefined) => Promise>'. - Types of parameters 'values' and 'values' are incompatible. - Type 'unknown[] | undefined' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues[]'. - Type 'unknown' is not assignable to type 'ExecuteValues'. -src/app/api/warehouse/transfer/route.ts(62,27): error TS2538: Type 'unknown' cannot be used as an index type. -src/app/api/warehouse/transfer/route.ts(63,31): error TS2538: Type 'unknown' cannot be used as an index type. -src/app/api/warehouse/transfer/route.ts(117,59): error TS2345: Argument of type 'unknown[]' is not assignable to parameter of type '(number | null | undefined)[]'. - Type 'unknown' is not assignable to type 'number | null | undefined'. -src/app/api/workflow/process/route.ts(91,6): error TS2322: Type 'unknown' is not assignable to type 'SqlValue'. -src/app/api/workflow/process/route.ts(96,5): error TS2322: Type 'unknown' is not assignable to type 'SqlValue'. -src/app/api/workflow/process/route.ts(100,5): error TS18046: 'workflow' is of type 'unknown'. -src/app/api/workflow/process/route.ts(102,11): error TS18046: 'workflow' is of type 'unknown'. -src/app/api/workflow/process/route.ts(123,9): error TS18046: 'workflow' is of type 'unknown'. -src/app/api/workflow/process/route.ts(125,19): error TS18046: 'workflow' is of type 'unknown'. -src/app/api/workflow/process/route.ts(126,21): error TS18046: 'workflow' is of type 'unknown'. -src/app/api/workorders/route.ts(50,10): error TS2322: Type 'unknown' is not assignable to type 'SqlValue'. -src/app/api/workorders/route.ts(59,10): error TS2322: Type 'unknown' is not assignable to type 'SqlValue'. -src/app/api/workorders/route.ts(142,11): error TS2571: Object is of type 'unknown'. -src/app/api/workorders/route.ts(149,39): error TS2365: Operator '+' cannot be applied to types 'number' and '{}'. -src/app/api/workorders/route.ts(173,28): error TS2352: Conversion of type 'QueryResult' to type 'DbRow' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first. - Type '[RowDataPacket[], ResultSetHeader]' is not comparable to type 'DbRow'. - Index signature for type 'string' is missing in type '[RowDataPacket[], ResultSetHeader]'. -src/app/api/workorders/route.ts(203,13): error TS18046: 'bomLine.quantity' is of type 'unknown'. -src/app/api/workorders/route.ts(203,53): error TS2362: The left-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. -src/app/api/workorders/route.ts(205,13): error TS2769: No overload matches this call. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/workorders/route.ts(339,27): error TS2345: Argument of type 'unknown' is not assignable to parameter of type 'SqlValue'. -src/app/api/workorders/route.ts(340,26): error TS2769: No overload matches this call. - Overload 1 of 2, '(sql: string, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'SqlValue[]' is not assignable to parameter of type 'ExecuteValues | undefined'. - Type 'SqlValue[]' is not assignable to type 'ExecuteValues[]'. - Type 'SqlValue' is not assignable to type 'ExecuteValues'. - Type 'undefined' is not assignable to type 'ExecuteValues'. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/workorders/route.ts(348,11): error TS2769: No overload matches this call. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/workorders/route.ts(359,28): error TS2769: No overload matches this call. - Overload 1 of 2, '(sql: string, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Type '{}' is not assignable to type 'ExecuteValues'. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/workorders/route.ts(367,50): error TS2769: No overload matches this call. - Overload 1 of 2, '(sql: string, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Type '{}' is not assignable to type 'ExecuteValues'. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/workorders/route.ts(374,28): error TS2769: No overload matches this call. - Overload 1 of 2, '(sql: string, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Type '{}' is not assignable to type 'ExecuteValues'. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/workorders/route.ts(425,46): error TS2769: No overload matches this call. - Overload 1 of 2, '(sql: string, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Type 'unknown' is not assignable to type 'ExecuteValues'. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/workorders/route.ts(434,24): error TS2769: No overload matches this call. - Overload 1 of 2, '(sql: string, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Type 'unknown' is not assignable to type 'ExecuteValues'. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/workorders/route.ts(440,50): error TS2769: No overload matches this call. - Overload 1 of 2, '(sql: string, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Type '{}' is not assignable to type 'ExecuteValues'. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/workorders/route.ts(447,28): error TS2769: No overload matches this call. - Overload 1 of 2, '(sql: string, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Type '{}' is not assignable to type 'ExecuteValues'. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/application/handlers/DeliveryShippedHandler.ts(24,25): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/application/handlers/DeliveryShippedHandler.ts(25,41): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/application/handlers/DeliveryShippedHandler.ts(33,29): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/application/handlers/DeliveryShippedHandler.ts(42,27): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/application/handlers/DeliveryShippedHandler.ts(43,46): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/application/handlers/DeliveryShippedHandler.ts(44,37): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/application/handlers/DeliveryShippedHandler.ts(48,16): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/application/handlers/DeliveryShippedHandler.ts(53,46): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/application/handlers/DeliveryShippedHandler.ts(59,41): error TS2345: Argument of type 'PoolConnection' is not assignable to parameter of type 'DbConnection'. - Types of property 'execute' are incompatible. - Type '{ (sql: string, values?: ExecuteValues | undefined): Promise<[T, FieldPacket[]]>; (options: QueryOptions, values?: ExecuteValues | undefined): Promise<...>; }' is not assignable to type '(sql: string, values?: unknown[] | undefined) => Promise>'. - Types of parameters 'values' and 'values' are incompatible. - Type 'unknown[] | undefined' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues[]'. - Type 'unknown' is not assignable to type 'ExecuteValues'. -src/application/handlers/DeliveryShippedHandler.ts(62,42): error TS2345: Argument of type 'PoolConnection' is not assignable to parameter of type 'DbConnection'. - Types of property 'execute' are incompatible. - Type '{ (sql: string, values?: ExecuteValues | undefined): Promise<[T, FieldPacket[]]>; (options: QueryOptions, values?: ExecuteValues | undefined): Promise<...>; }' is not assignable to type '(sql: string, values?: unknown[] | undefined) => Promise>'. - Types of parameters 'values' and 'values' are incompatible. - Type 'unknown[] | undefined' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues[]'. - Type 'unknown' is not assignable to type 'ExecuteValues'. -src/application/handlers/FinishOrderInventoryHandler.ts(97,39): error TS2345: Argument of type 'PoolConnection' is not assignable to parameter of type 'DbConnection'. - Types of property 'execute' are incompatible. - Type '{ (sql: string, values?: ExecuteValues | undefined): Promise<[T, FieldPacket[]]>; (options: QueryOptions, values?: ExecuteValues | undefined): Promise<...>; }' is not assignable to type '(sql: string, values?: unknown[] | undefined) => Promise>'. - Types of parameters 'values' and 'values' are incompatible. - Type 'unknown[] | undefined' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues[]'. - Type 'unknown' is not assignable to type 'ExecuteValues'. -src/application/handlers/InventoryRollbackHandler.ts(120,42): error TS2345: Argument of type 'PoolConnection' is not assignable to parameter of type 'DbConnection'. - Types of property 'execute' are incompatible. - Type '{ (sql: string, values?: ExecuteValues | undefined): Promise<[T, FieldPacket[]]>; (options: QueryOptions, values?: ExecuteValues | undefined): Promise<...>; }' is not assignable to type '(sql: string, values?: unknown[] | undefined) => Promise>'. - Types of parameters 'values' and 'values' are incompatible. - Type 'unknown[] | undefined' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues[]'. - Type 'unknown' is not assignable to type 'ExecuteValues'. -src/application/handlers/InventoryRollbackHandler.ts(133,41): error TS2345: Argument of type 'PoolConnection' is not assignable to parameter of type 'DbConnection'. - Types of property 'execute' are incompatible. - Type '{ (sql: string, values?: ExecuteValues | undefined): Promise<[T, FieldPacket[]]>; (options: QueryOptions, values?: ExecuteValues | undefined): Promise<...>; }' is not assignable to type '(sql: string, values?: unknown[] | undefined) => Promise>'. - Types of parameters 'values' and 'values' are incompatible. - Type 'unknown[] | undefined' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues[]'. - Type 'unknown' is not assignable to type 'ExecuteValues'. -src/application/handlers/InventorySyncHandler.ts(93,41): error TS2345: Argument of type 'PoolConnection' is not assignable to parameter of type 'DbConnection'. - Types of property 'execute' are incompatible. - Type '{ (sql: string, values?: ExecuteValues | undefined): Promise<[T, FieldPacket[]]>; (options: QueryOptions, values?: ExecuteValues | undefined): Promise<...>; }' is not assignable to type '(sql: string, values?: unknown[] | undefined) => Promise>'. - Types of parameters 'values' and 'values' are incompatible. - Type 'unknown[] | undefined' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues[]'. - Type 'unknown' is not assignable to type 'ExecuteValues'. -src/application/handlers/InventorySyncHandler.ts(96,42): error TS2345: Argument of type 'PoolConnection' is not assignable to parameter of type 'DbConnection'. - Types of property 'execute' are incompatible. - Type '{ (sql: string, values?: ExecuteValues | undefined): Promise<[T, FieldPacket[]]>; (options: QueryOptions, values?: ExecuteValues | undefined): Promise<...>; }' is not assignable to type '(sql: string, values?: unknown[] | undefined) => Promise>'. - Types of parameters 'values' and 'values' are incompatible. - Type 'unknown[] | undefined' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues[]'. - Type 'unknown' is not assignable to type 'ExecuteValues'. -src/application/handlers/MaterialReturnInventoryHandler.ts(21,25): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/application/handlers/MaterialReturnInventoryHandler.ts(22,26): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/application/handlers/MaterialReturnInventoryHandler.ts(56,29): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/application/handlers/MaterialReturnInventoryHandler.ts(59,46): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/application/handlers/MaterialReturnInventoryHandler.ts(77,43): error TS2345: Argument of type 'PoolConnection' is not assignable to parameter of type 'DbConnection'. - Types of property 'execute' are incompatible. - Type '{ (sql: string, values?: ExecuteValues | undefined): Promise<[T, FieldPacket[]]>; (options: QueryOptions, values?: ExecuteValues | undefined): Promise<...>; }' is not assignable to type '(sql: string, values?: unknown[] | undefined) => Promise>'. - Types of parameters 'values' and 'values' are incompatible. - Type 'unknown[] | undefined' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues[]'. - Type 'unknown' is not assignable to type 'ExecuteValues'. -src/application/handlers/MaterialReturnInventoryHandler.ts(81,42): error TS2345: Argument of type 'PoolConnection' is not assignable to parameter of type 'DbConnection'. - Types of property 'execute' are incompatible. - Type '{ (sql: string, values?: ExecuteValues | undefined): Promise<[T, FieldPacket[]]>; (options: QueryOptions, values?: ExecuteValues | undefined): Promise<...>; }' is not assignable to type '(sql: string, values?: unknown[] | undefined) => Promise>'. - Types of parameters 'values' and 'values' are incompatible. - Type 'unknown[] | undefined' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues[]'. - Type 'unknown' is not assignable to type 'ExecuteValues'. -src/application/handlers/OutboundInventoryHandler.ts(20,25): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/application/handlers/OutboundInventoryHandler.ts(21,41): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/application/handlers/OutboundInventoryHandler.ts(29,44): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/application/handlers/OutboundInventoryHandler.ts(38,27): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/application/handlers/OutboundInventoryHandler.ts(39,46): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/application/handlers/OutboundInventoryHandler.ts(40,37): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/application/handlers/OutboundInventoryHandler.ts(44,16): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/application/handlers/OutboundInventoryHandler.ts(49,46): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/application/handlers/OutboundInventoryHandler.ts(58,25): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/application/handlers/OutboundInventoryHandler.ts(59,26): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/application/handlers/PickOrderInventoryHandler.ts(64,41): error TS2345: Argument of type 'PoolConnection' is not assignable to parameter of type 'DbConnection'. - Types of property 'execute' are incompatible. - Type '{ (sql: string, values?: ExecuteValues | undefined): Promise<[T, FieldPacket[]]>; (options: QueryOptions, values?: ExecuteValues | undefined): Promise<...>; }' is not assignable to type '(sql: string, values?: unknown[] | undefined) => Promise>'. - Types of parameters 'values' and 'values' are incompatible. - Type 'unknown[] | undefined' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues[]'. - Type 'unknown' is not assignable to type 'ExecuteValues'. -src/application/handlers/PickOrderInventoryHandler.ts(67,42): error TS2345: Argument of type 'PoolConnection' is not assignable to parameter of type 'DbConnection'. - Types of property 'execute' are incompatible. - Type '{ (sql: string, values?: ExecuteValues | undefined): Promise<[T, FieldPacket[]]>; (options: QueryOptions, values?: ExecuteValues | undefined): Promise<...>; }' is not assignable to type '(sql: string, values?: unknown[] | undefined) => Promise>'. - Types of parameters 'values' and 'values' are incompatible. - Type 'unknown[] | undefined' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues[]'. - Type 'unknown' is not assignable to type 'ExecuteValues'. -src/application/handlers/PurchasePayableHandler.ts(64,20): error TS2769: No overload matches this call. - Overload 1 of 2, '(sql: string, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Type 'unknown' is not assignable to type 'ExecuteValues'. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/application/handlers/PurchaseReceivedHandler.ts(17,25): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/application/handlers/PurchaseReceivedHandler.ts(20,29): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/application/handlers/PurchaseReceivedHandler.ts(35,27): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/application/handlers/PurchaseReceivedHandler.ts(38,44): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/application/handlers/PurchaseReconciliationWrittenOffHandler.ts(29,15): error TS2322: Type '[QueryResult, FieldPacket[]]' is not assignable to type 'DbRow[]'. - Type 'QueryResult | FieldPacket[]' is not assignable to type 'DbRow'. - Type 'OkPacket' is not assignable to type 'DbRow'. - Index signature for type 'string' is missing in type 'OkPacket'. -src/application/handlers/PurchaseReconciliationWrittenOffHandler.ts(46,36): error TS18046: 'payable' is of type 'unknown'. -src/application/handlers/PurchaseReconciliationWrittenOffHandler.ts(47,39): error TS18046: 'payable' is of type 'unknown'. -src/application/handlers/PurchaseReconciliationWrittenOffHandler.ts(69,32): error TS18046: 'payable' is of type 'unknown'. -src/application/handlers/PurchaseReconciliationWrittenOffHandler.ts(85,22): error TS18046: 'payable' is of type 'unknown'. -src/application/handlers/ReconciliationWriteOffHandler.ts(47,15): error TS2304: Cannot find name 'DbResult'. -src/application/handlers/ReturnOrderInventoryHandler.ts(67,41): error TS2345: Argument of type 'PoolConnection' is not assignable to parameter of type 'DbConnection'. - Types of property 'execute' are incompatible. - Type '{ (sql: string, values?: ExecuteValues | undefined): Promise<[T, FieldPacket[]]>; (options: QueryOptions, values?: ExecuteValues | undefined): Promise<...>; }' is not assignable to type '(sql: string, values?: unknown[] | undefined) => Promise>'. - Types of parameters 'values' and 'values' are incompatible. - Type 'unknown[] | undefined' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues[]'. - Type 'unknown' is not assignable to type 'ExecuteValues'. -src/application/handlers/ReturnOrderInventoryHandler.ts(70,42): error TS2345: Argument of type 'PoolConnection' is not assignable to parameter of type 'DbConnection'. - Types of property 'execute' are incompatible. - Type '{ (sql: string, values?: ExecuteValues | undefined): Promise<[T, FieldPacket[]]>; (options: QueryOptions, values?: ExecuteValues | undefined): Promise<...>; }' is not assignable to type '(sql: string, values?: unknown[] | undefined) => Promise>'. - Types of parameters 'values' and 'values' are incompatible. - Type 'unknown[] | undefined' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues[]'. - Type 'unknown' is not assignable to type 'ExecuteValues'. -src/application/handlers/SalesReceivableHandler.ts(26,15): error TS2304: Cannot find name 'DbResult'. -src/application/handlers/SalesShippedHandler.ts(18,25): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/application/handlers/SalesShippedHandler.ts(19,41): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/application/handlers/SalesShippedHandler.ts(27,29): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/application/handlers/SalesShippedHandler.ts(36,27): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/application/handlers/SalesShippedHandler.ts(37,46): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/application/handlers/SalesShippedHandler.ts(38,37): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/application/handlers/SalesShippedHandler.ts(42,16): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/application/handlers/SalesShippedHandler.ts(47,46): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/application/handlers/SalesShippedHandler.ts(53,41): error TS2345: Argument of type 'PoolConnection' is not assignable to parameter of type 'DbConnection'. - Types of property 'execute' are incompatible. - Type '{ (sql: string, values?: ExecuteValues | undefined): Promise<[T, FieldPacket[]]>; (options: QueryOptions, values?: ExecuteValues | undefined): Promise<...>; }' is not assignable to type '(sql: string, values?: unknown[] | undefined) => Promise>'. - Types of parameters 'values' and 'values' are incompatible. - Type 'unknown[] | undefined' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues[]'. - Type 'unknown' is not assignable to type 'ExecuteValues'. -src/application/handlers/SalesShippedHandler.ts(56,42): error TS2345: Argument of type 'PoolConnection' is not assignable to parameter of type 'DbConnection'. - Types of property 'execute' are incompatible. - Type '{ (sql: string, values?: ExecuteValues | undefined): Promise<[T, FieldPacket[]]>; (options: QueryOptions, values?: ExecuteValues | undefined): Promise<...>; }' is not assignable to type '(sql: string, values?: unknown[] | undefined) => Promise>'. - Types of parameters 'values' and 'values' are incompatible. - Type 'unknown[] | undefined' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues[]'. - Type 'unknown' is not assignable to type 'ExecuteValues'. -src/application/handlers/SalesToWorkOrderHandler.ts(167,5): error TS2322: Type '{ materialId: unknown; materialCode: unknown; materialName: unknown; requiredQty: number; unit: {}; }[]' is not assignable to type 'MaterialRequirementItem[]'. - Type '{ materialId: unknown; materialCode: unknown; materialName: unknown; requiredQty: number; unit: {}; }' is not assignable to type 'MaterialRequirementItem'. - Types of property 'materialId' are incompatible. - Type 'unknown' is not assignable to type 'number'. -src/application/handlers/SalesToWorkOrderHandler.ts(171,20): error TS18046: 'row.quantity' is of type 'unknown'. -src/application/handlers/WorkOrderCompletedHandler.ts(75,39): error TS2345: Argument of type 'PoolConnection' is not assignable to parameter of type 'DbConnection'. - Types of property 'execute' are incompatible. - Type '{ (sql: string, values?: ExecuteValues | undefined): Promise<[T, FieldPacket[]]>; (options: QueryOptions, values?: ExecuteValues | undefined): Promise<...>; }' is not assignable to type '(sql: string, values?: unknown[] | undefined) => Promise>'. - Types of parameters 'values' and 'values' are incompatible. - Type 'unknown[] | undefined' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues[]'. - Type 'unknown' is not assignable to type 'ExecuteValues'. -src/application/handlers/WorkOrderCompletedHandler.ts(78,40): error TS2345: Argument of type 'PoolConnection' is not assignable to parameter of type 'DbConnection'. - Types of property 'execute' are incompatible. - Type '{ (sql: string, values?: ExecuteValues | undefined): Promise<[T, FieldPacket[]]>; (options: QueryOptions, values?: ExecuteValues | undefined): Promise<...>; }' is not assignable to type '(sql: string, values?: unknown[] | undefined) => Promise>'. - Types of parameters 'values' and 'values' are incompatible. - Type 'unknown[] | undefined' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues[]'. - Type 'unknown' is not assignable to type 'ExecuteValues'. -src/application/handlers/WorkOrderMaterialIssuedHandler.ts(110,45): error TS2345: Argument of type 'PoolConnection' is not assignable to parameter of type 'DbConnection'. - Types of property 'execute' are incompatible. - Type '{ (sql: string, values?: ExecuteValues | undefined): Promise<[T, FieldPacket[]]>; (options: QueryOptions, values?: ExecuteValues | undefined): Promise<...>; }' is not assignable to type '(sql: string, values?: unknown[] | undefined) => Promise>'. - Types of parameters 'values' and 'values' are incompatible. - Type 'unknown[] | undefined' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues[]'. - Type 'unknown' is not assignable to type 'ExecuteValues'. -src/application/handlers/WorkOrderMaterialIssuedHandler.ts(115,44): error TS2345: Argument of type 'PoolConnection' is not assignable to parameter of type 'DbConnection'. - Types of property 'execute' are incompatible. - Type '{ (sql: string, values?: ExecuteValues | undefined): Promise<[T, FieldPacket[]]>; (options: QueryOptions, values?: ExecuteValues | undefined): Promise<...>; }' is not assignable to type '(sql: string, values?: unknown[] | undefined) => Promise>'. - Types of parameters 'values' and 'values' are incompatible. - Type 'unknown[] | undefined' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues[]'. - Type 'unknown' is not assignable to type 'ExecuteValues'. -src/application/services/InventoryValidationService.ts(168,32): error TS2345: Argument of type 'PoolConnection' is not assignable to parameter of type 'DbConnection'. - Types of property 'execute' are incompatible. - Type '{ (sql: string, values?: ExecuteValues | undefined): Promise<[T, FieldPacket[]]>; (options: QueryOptions, values?: ExecuteValues | undefined): Promise<...>; }' is not assignable to type '(sql: string, values?: unknown[] | undefined) => Promise>'. - Types of parameters 'values' and 'values' are incompatible. - Type 'unknown[] | undefined' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues[]'. - Type 'unknown' is not assignable to type 'ExecuteValues'. -src/application/services/InventoryValidationService.ts(223,32): error TS2345: Argument of type 'PoolConnection' is not assignable to parameter of type 'DbConnection'. - Types of property 'execute' are incompatible. - Type '{ (sql: string, values?: ExecuteValues | undefined): Promise<[T, FieldPacket[]]>; (options: QueryOptions, values?: ExecuteValues | undefined): Promise<...>; }' is not assignable to type '(sql: string, values?: unknown[] | undefined) => Promise>'. - Types of parameters 'values' and 'values' are incompatible. - Type 'unknown[] | undefined' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues[]'. - Type 'unknown' is not assignable to type 'ExecuteValues'. -src/application/services/SampleProcessCardService.ts(370,13): error TS2304: Cannot find name 'DbResult'. -src/components/layout/header.tsx(170,48): error TS2304: Cannot find name 'DbRow'. -src/components/SystemConfigInitializer.tsx(39,38): error TS2345: Argument of type 'unknown' is not assignable to parameter of type 'string'. -src/infrastructure/repositories/DrizzleInboundOrderRepository.ts(327,5): error TS2322: Type '{ id: number; orderNo: unknown; }' is not assignable to type '{ id: number; orderNo: string; }'. - Types of property 'orderNo' are incompatible. - Type 'unknown' is not assignable to type 'string'. -src/lib/__tests__/fifo-allocation.test.ts(29,41): error TS2345: Argument of type '{ query: Mock; execute: Mock; }' is not assignable to parameter of type 'DbConnection'. - Type '{ query: Mock; execute: Mock; }' is missing the following properties from type 'DbConnection': beginTransaction, commit, rollback, release -src/lib/__tests__/fifo-allocation.test.ts(87,41): error TS2345: Argument of type '{ query: Mock; execute: Mock; }' is not assignable to parameter of type 'DbConnection'. - Type '{ query: Mock; execute: Mock; }' is missing the following properties from type 'DbConnection': beginTransaction, commit, rollback, release -src/lib/__tests__/fifo-allocation.test.ts(104,41): error TS2345: Argument of type '{ query: Mock; execute: Mock; }' is not assignable to parameter of type 'DbConnection'. - Type '{ query: Mock; execute: Mock; }' is missing the following properties from type 'DbConnection': beginTransaction, commit, rollback, release -src/lib/__tests__/fifo-allocation.test.ts(135,41): error TS2345: Argument of type '{ query: Mock; execute: Mock; }' is not assignable to parameter of type 'DbConnection'. - Type '{ query: Mock; execute: Mock; }' is missing the following properties from type 'DbConnection': beginTransaction, commit, rollback, release -src/lib/__tests__/fifo-allocation.test.ts(164,41): error TS2345: Argument of type '{ query: Mock; execute: Mock; }' is not assignable to parameter of type 'DbConnection'. - Type '{ query: Mock; execute: Mock; }' is missing the following properties from type 'DbConnection': beginTransaction, commit, rollback, release -src/lib/__tests__/fifo-allocation.test.ts(194,26): error TS2345: Argument of type '{ query: Mock; execute: Mock; }' is not assignable to parameter of type 'DbConnection'. - Type '{ query: Mock; execute: Mock; }' is missing the following properties from type 'DbConnection': beginTransaction, commit, rollback, release -src/lib/__tests__/fifo-allocation.test.ts(236,39): error TS2345: Argument of type '{ query: Mock; execute: Mock; }' is not assignable to parameter of type 'DbConnection'. - Type '{ query: Mock; execute: Mock; }' is missing the following properties from type 'DbConnection': beginTransaction, commit, rollback, release -src/lib/__tests__/fifo-allocation.test.ts(289,43): error TS2345: Argument of type '{ query: Mock; execute: Mock; }' is not assignable to parameter of type 'DbConnection'. - Type '{ query: Mock; execute: Mock; }' is missing the following properties from type 'DbConnection': beginTransaction, commit, rollback, release -src/lib/fifo-allocation.ts(174,5): error TS2322: Type 'unknown' is not assignable to type 'string'. -src/lib/fifo-allocation.ts(175,5): error TS2322: Type 'unknown' is not assignable to type 'string'. -src/lib/fifo-allocation.ts(205,45): error TS2345: Argument of type 'unknown' is not assignable to parameter of type 'Value'. -src/lib/fifo-allocation.ts(213,7): error TS2322: Type 'unknown' is not assignable to type 'number'. -src/lib/fifo-allocation.ts(214,7): error TS2322: Type 'unknown' is not assignable to type 'string'. -src/lib/fifo-allocation.ts(215,7): error TS2322: Type 'unknown' is not assignable to type 'number'. -src/lib/fifo-allocation.ts(216,7): error TS2322: Type 'unknown' is not assignable to type 'string'. -src/lib/fifo-allocation.ts(217,7): error TS2322: Type 'unknown' is not assignable to type 'string'. -src/lib/fifo-allocation.ts(220,30): error TS2345: Argument of type '{}' is not assignable to parameter of type 'Value'. -src/lib/fifo-allocation.ts(221,7): error TS2322: Type 'unknown' is not assignable to type 'string'. -src/lib/fifo-allocation.ts(222,7): error TS2322: Type 'unknown' is not assignable to type 'string | undefined'. -src/lib/fifo-allocation.ts(223,7): error TS2322: Type 'unknown' is not assignable to type 'string | undefined'. -src/lib/fifo-allocation.ts(224,7): error TS2322: Type 'unknown' is not assignable to type 'number | undefined'. -src/lib/fifo-allocation.ts(319,9): error TS2322: Type 'unknown[]' is not assignable to type 'DbResult'. - Target requires 2 element(s) but source may have fewer. -src/lib/fifo-allocation.ts(332,9): error TS2322: Type 'unknown[]' is not assignable to type 'DbResult'. - Target requires 2 element(s) but source may have fewer. -src/lib/fifo-allocation.ts(621,11): error TS2345: Argument of type 'PoolConnection' is not assignable to parameter of type 'DbConnection'. - Types of property 'execute' are incompatible. - Type '{ (sql: string, values?: ExecuteValues | undefined): Promise<[T, FieldPacket[]]>; (options: QueryOptions, values?: ExecuteValues | undefined): Promise<...>; }' is not assignable to type '(sql: string, values?: unknown[] | undefined) => Promise>'. - Types of parameters 'values' and 'values' are incompatible. - Type 'unknown[] | undefined' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues[]'. - Type 'unknown' is not assignable to type 'ExecuteValues'. -src/lib/fifo-allocation.ts(640,71): error TS2345: Argument of type 'PoolConnection' is not assignable to parameter of type 'DbConnection'. - Types of property 'execute' are incompatible. - Type '{ (sql: string, values?: ExecuteValues | undefined): Promise<[T, FieldPacket[]]>; (options: QueryOptions, values?: ExecuteValues | undefined): Promise<...>; }' is not assignable to type '(sql: string, values?: unknown[] | undefined) => Promise>'. - Types of parameters 'values' and 'values' are incompatible. - Type 'unknown[] | undefined' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues[]'. - Type 'unknown' is not assignable to type 'ExecuteValues'. -src/lib/fifo-config.ts(15,43): error TS2345: Argument of type '{}' is not assignable to parameter of type '"off" | "hint" | "force"'. -src/lib/fifo-config.ts(38,10): error TS2365: Operator '>' cannot be applied to types '{}' and 'number'. -src/lib/global-config.ts(129,3): error TS2322: Type 'unknown' is not assignable to type 'string'. -src/lib/global-config.ts(133,3): error TS2322: Type 'unknown' is not assignable to type 'string'. -src/lib/global-config.ts(137,3): error TS2322: Type 'unknown' is not assignable to type 'string'. -src/lib/global-config.ts(141,3): error TS2322: Type 'unknown' is not assignable to type 'string'. -src/lib/global-config.ts(145,3): error TS2322: Type '{}' is not assignable to type 'string'. -src/lib/global-config.ts(149,3): error TS2322: Type 'unknown' is not assignable to type 'string'. -src/lib/global-config.ts(153,3): error TS2322: Type 'unknown' is not assignable to type 'string'. -src/lib/global-config.ts(157,3): error TS2322: Type 'unknown' is not assignable to type 'string'. -src/lib/global-config.ts(161,3): error TS2322: Type 'unknown' is not assignable to type 'string'. -src/lib/global-config.ts(165,3): error TS2322: Type '{}' is not assignable to type 'string'. -src/lib/global-config.ts(249,3): error TS2322: Type 'unknown' is not assignable to type 'string'. -src/lib/global-config.ts(253,3): error TS2322: Type 'unknown' is not assignable to type 'string'. -src/lib/global-config.ts(257,3): error TS2322: Type 'unknown' is not assignable to type 'string'. -src/lib/global-config.ts(261,3): error TS2322: Type 'unknown' is not assignable to type 'string'. -src/lib/global-config.ts(265,3): error TS2322: Type 'unknown' is not assignable to type 'string'. -src/lib/global-config.ts(269,3): error TS2322: Type '{}' is not assignable to type 'string'. -src/lib/global-config.ts(305,3): error TS2322: Type '{}' is not assignable to type 'string'. -src/lib/global-export-service.ts(134,20): error TS2345: Argument of type '{}[]' is not assignable to parameter of type '(string | number)[]'. - Type '{}' is not assignable to type 'string | number'. -src/lib/inventory-sync.ts(298,32): error TS2345: Argument of type 'PoolConnection' is not assignable to parameter of type 'DbConnection'. - Types of property 'execute' are incompatible. - Type '{ (sql: string, values?: ExecuteValues | undefined): Promise<[T, FieldPacket[]]>; (options: QueryOptions, values?: ExecuteValues | undefined): Promise<...>; }' is not assignable to type '(sql: string, values?: unknown[] | undefined) => Promise>'. - Types of parameters 'values' and 'values' are incompatible. - Type 'unknown[] | undefined' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues[]'. - Type 'unknown' is not assignable to type 'ExecuteValues'. -src/lib/inventory-sync.ts(464,32): error TS2345: Argument of type 'PoolConnection' is not assignable to parameter of type 'DbConnection'. - Types of property 'execute' are incompatible. - Type '{ (sql: string, values?: ExecuteValues | undefined): Promise<[T, FieldPacket[]]>; (options: QueryOptions, values?: ExecuteValues | undefined): Promise<...>; }' is not assignable to type '(sql: string, values?: unknown[] | undefined) => Promise>'. - Types of parameters 'values' and 'values' are incompatible. - Type 'unknown[] | undefined' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues[]'. - Type 'unknown' is not assignable to type 'ExecuteValues'. -src/lib/inventory-sync.ts(587,32): error TS2345: Argument of type 'PoolConnection' is not assignable to parameter of type 'DbConnection'. - Types of property 'execute' are incompatible. - Type '{ (sql: string, values?: ExecuteValues | undefined): Promise<[T, FieldPacket[]]>; (options: QueryOptions, values?: ExecuteValues | undefined): Promise<...>; }' is not assignable to type '(sql: string, values?: unknown[] | undefined) => Promise>'. - Types of parameters 'values' and 'values' are incompatible. - Type 'unknown[] | undefined' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues[]'. - Type 'unknown' is not assignable to type 'ExecuteValues'. -src/lib/material-requisition.ts(88,43): error TS2345: Argument of type '{}' is not assignable to parameter of type 'string'. -src/lib/material-requisition.ts(192,41): error TS2345: Argument of type '{}' is not assignable to parameter of type 'string'. -src/lib/material-requisition.ts(267,41): error TS2345: Argument of type '{}' is not assignable to parameter of type 'string'. -src/lib/production-scheduling-enhanced.ts(186,3): error TS2322: Type 'DbRow[]' is not assignable to type 'Equipment[]'. - Type 'DbRow' is missing the following properties from type 'Equipment': id, equipment_code, equipment_name, equipment_type, and 5 more. -src/lib/production-scheduling-enhanced.ts(205,3): error TS2322: Type 'DbRow[]' is not assignable to type 'ColorSequence[]'. - Type 'DbRow' is missing the following properties from type 'ColorSequence': seq_no, color_name, screen_plate_id, ink_formula_id, and 2 more. -src/lib/production-scheduling-enhanced.ts(500,7): error TS2322: Type 'unknown' is not assignable to type 'number'. -src/lib/production-scheduling-enhanced.ts(501,7): error TS2322: Type 'unknown' is not assignable to type 'string'. -src/lib/production-scheduling-enhanced.ts(502,7): error TS2322: Type 'unknown' is not assignable to type 'number'. -src/lib/production-scheduling-enhanced.ts(503,7): error TS2322: Type 'unknown' is not assignable to type 'string'. -src/lib/production-scheduling-enhanced.ts(518,7): error TS2322: Type '{}' is not assignable to type 'string'. -src/lib/production-scheduling-enhanced.ts(519,7): error TS2322: Type 'unknown' is not assignable to type 'string'. -src/lib/production-scheduling-enhanced.ts(600,9): error TS2769: No overload matches this call. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/lib/production-scheduling.ts(231,6): error TS2322: Type '{}' is not assignable to type 'SqlValue'. -src/lib/production-scheduling.ts(254,8): error TS2322: Type 'unknown' is not assignable to type 'SqlValue'. -src/lib/production-scheduling.ts(262,9): error TS2322: Type 'unknown' is not assignable to type 'string'. -src/lib/production-scheduling.ts(390,7): error TS2353: Object literal may only specify known properties, and 'totalWorkHours' does not exist in type 'CapacityLoad'. -src/lib/production-scheduling.ts(397,30): error TS2769: No overload matches this call. - Overload 1 of 4, '(value: string | number | Date): Date', gave the following error. - Argument of type 'unknown' is not assignable to parameter of type 'string | number | Date'. - Overload 2 of 4, '(value: string | number): Date', gave the following error. - Argument of type 'unknown' is not assignable to parameter of type 'string | number'. -src/lib/production-scheduling.ts(398,28): error TS2769: No overload matches this call. - Overload 1 of 4, '(value: string | number | Date): Date', gave the following error. - Argument of type 'unknown' is not assignable to parameter of type 'string | number | Date'. - Overload 2 of 4, '(value: string | number): Date', gave the following error. - Argument of type 'unknown' is not assignable to parameter of type 'string | number'. -src/lib/production-scheduling.ts(413,14): error TS2339: Property 'totalWorkHours' does not exist on type 'CapacityLoad'. -src/lib/production-scheduling.ts(415,11): error TS2322: Type 'unknown' is not assignable to type 'string'. -src/lib/production-scheduling.ts(416,11): error TS2322: Type 'unknown' is not assignable to type 'string'. -src/lib/production-scheduling.ts(418,11): error TS2322: Type 'unknown' is not assignable to type 'string'. -src/lib/production-scheduling.ts(429,26): error TS2339: Property 'totalWorkHours' does not exist on type 'CapacityLoad'. -src/lib/seeds/full-seed-steps.ts(752,23): error TS2345: Argument of type 'unknown' is not assignable to parameter of type 'number'. -src/lib/seeds/full-seed-steps.ts(1528,31): error TS2363: The right-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. -src/lib/seeds/full-seed-steps.ts(1536,17): error TS2345: Argument of type 'unknown' is not assignable to parameter of type 'number'. -src/lib/seeds/full-seed-steps.ts(1553,22): error TS2363: The right-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. -src/lib/seeds/full-seed-steps.ts(1714,20): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type '{}'. - Property '0' does not exist on type '{}'. -src/lib/warehouse-core.ts(316,32): error TS2345: Argument of type 'PoolConnection' is not assignable to parameter of type 'DbConnection'. - Types of property 'execute' are incompatible. - Type '{ (sql: string, values?: ExecuteValues | undefined): Promise<[T, FieldPacket[]]>; (options: QueryOptions, values?: ExecuteValues | undefined): Promise<...>; }' is not assignable to type '(sql: string, values?: unknown[] | undefined) => Promise>'. - Types of parameters 'values' and 'values' are incompatible. - Type 'unknown[] | undefined' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues | undefined'. - Type 'unknown[]' is not assignable to type 'ExecuteValues[]'. - Type 'unknown' is not assignable to type 'ExecuteValues'. -tests/unit/domain/warehouse/aggregates/inbound-order-gaps.test.ts(99,14): error TS2571: Object is of type 'unknown'. diff --git a/_tsc_finance.txt b/_tsc_finance.txt deleted file mode 100644 index e69de29b..00000000 diff --git a/_tsc_out.txt b/_tsc_out.txt deleted file mode 100644 index 6270c039..00000000 --- a/_tsc_out.txt +++ /dev/null @@ -1,3649 +0,0 @@ -src/app/api/auth/menus/route.ts(74,34): error TS2345: Argument of type 'DbRow[]' is not assignable to parameter of type 'MenuRow[]'. - Type 'DbRow' is missing the following properties from type 'MenuRow': id, menu_name, menu_code, menu_type, and 7 more. -src/app/api/auth/menus/route.ts(76,42): error TS2345: Argument of type 'DbRow[]' is not assignable to parameter of type 'MenuRow[]'. - Type 'DbRow' is missing the following properties from type 'MenuRow': id, menu_name, menu_code, menu_type, and 7 more. -src/app/api/auth/register/route.ts(186,32): error TS2339: Property 'insertId' does not exist on type 'QueryResult'. - Property 'insertId' does not exist on type 'ResultSetHeader[]'. -src/app/api/auth/register/route.ts(200,25): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/auth/register/route.ts(203,13): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/biz/contract-review/route.ts(53,24): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/biz/contract-review/route.ts(53,38): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/biz/contract-review/route.ts(70,33): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/biz/contract-review/route.ts(70,46): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/biz/contract-review/route.ts(85,31): error TS2339: Property 'insertId' does not exist on type 'QueryResult'. - Property 'insertId' does not exist on type 'ResultSetHeader[]'. -src/app/api/biz/contract-review/route.ts(110,20): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/biz/contract-review/route.ts(114,20): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/biz/contract-review/route.ts(133,23): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/biz/contract-review/route.ts(134,17): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/customers/route.ts(97,27): error TS2322: Type 'SqlValue[]' is not assignable to type 'DbRow[]'. - Type 'SqlValue' is not assignable to type 'DbRow'. - Type 'undefined' is not assignable to type 'DbRow'. -src/app/api/customers/route.ts(141,64): error TS2345: Argument of type 'DbRow[]' is not assignable to parameter of type 'number | SqlValue[]'. - Type 'DbRow[]' is not assignable to type 'SqlValue[]'. - Type 'DbRow' is not assignable to type 'SqlValue'. - Type 'DbRow' is missing the following properties from type 'Buffer': slice, subarray, write, toJSON, and 102 more. -src/app/api/dashboard/ceo/route.ts(26,51): error TS18046: 'overview' is of type 'unknown'. -src/app/api/dashboard/ceo/route.ts(37,51): error TS18046: 'overview' is of type 'unknown'. -src/app/api/dashboard/ceo/route.ts(49,9): error TS18046: 'overview' is of type 'unknown'. -src/app/api/dashboard/ceo/route.ts(61,9): error TS18046: 'overview' is of type 'unknown'. -src/app/api/dashboard/ceo/route.ts(76,9): error TS18046: 'overview' is of type 'unknown'. -src/app/api/dashboard/ceo/route.ts(99,9): error TS18046: 'production' is of type 'unknown'. -src/app/api/dashboard/ceo/route.ts(100,9): error TS18046: 'production' is of type 'unknown'. -src/app/api/dashboard/ceo/route.ts(114,7): error TS18046: 'production' is of type 'unknown'. -src/app/api/dashboard/ceo/route.ts(134,7): error TS18046: 'production' is of type 'unknown'. -src/app/api/dashboard/ceo/route.ts(148,23): error TS18046: 'production' is of type 'unknown'. -src/app/api/dashboard/ceo/route.ts(151,7): error TS18046: 'production' is of type 'unknown'. -src/app/api/dashboard/ceo/route.ts(152,9): error TS18046: 'production' is of type 'unknown'. -src/app/api/dashboard/ceo/route.ts(153,35): error TS18046: 'production' is of type 'unknown'. -src/app/api/dashboard/ceo/route.ts(167,9): error TS18046: 'production' is of type 'unknown'. -src/app/api/dashboard/ceo/route.ts(186,9): error TS18046: 'quality' is of type 'unknown'. -src/app/api/dashboard/ceo/route.ts(202,9): error TS18046: 'quality' is of type 'unknown'. -src/app/api/dashboard/ceo/route.ts(203,9): error TS18046: 'quality' is of type 'unknown'. -src/app/api/dashboard/ceo/route.ts(204,9): error TS18046: 'quality' is of type 'unknown'. -src/app/api/dashboard/ceo/route.ts(205,9): error TS18046: 'quality' is of type 'unknown'. -src/app/api/dashboard/ceo/route.ts(206,11): error TS18046: 'quality' is of type 'unknown'. -src/app/api/dashboard/ceo/route.ts(207,27): error TS18046: 'quality' is of type 'unknown'. -src/app/api/dashboard/ceo/route.ts(207,55): error TS18046: 'quality' is of type 'unknown'. -src/app/api/dashboard/ceo/route.ts(232,9): error TS18046: 'finance' is of type 'unknown'. -src/app/api/dashboard/ceo/route.ts(234,9): error TS18046: 'finance' is of type 'unknown'. -src/app/api/dashboard/ceo/route.ts(249,9): error TS18046: 'finance' is of type 'unknown'. -src/app/api/dashboard/ceo/route.ts(251,9): error TS18046: 'finance' is of type 'unknown'. -src/app/api/dashboard/ceo/route.ts(268,9): error TS18046: 'inventory' is of type 'unknown'. -src/app/api/dashboard/ceo/route.ts(269,9): error TS18046: 'inventory' is of type 'unknown'. -src/app/api/dashboard/ceo/route.ts(288,51): error TS18046: 'inventory' is of type 'unknown'. -src/app/api/dashboard/ceo/route.ts(300,9): error TS18046: 'inventory' is of type 'unknown'. -src/app/api/dashboard/ceo/route.ts(343,7): error TS2322: Type '{ name: string | number | true | Date | Buffer; total: number; completed: number; }[]' is not assignable to type 'SqlValue[]'. - Type '{ name: string | number | true | Date | Buffer; total: number; completed: number; }' is not assignable to type 'SqlValue'. -src/app/api/dashboard/ceo/route.ts(363,7): error TS2322: Type '{ name: string | number | true | Date | Buffer; qty: number; }[]' is not assignable to type 'SqlValue[]'. - Type '{ name: string | number | true | Date | Buffer; qty: number; }' is not assignable to type 'SqlValue'. -src/app/api/dashboard/ceo/route.ts(382,7): error TS2322: Type '{ name: string | number | true | Date | Buffer; qty: number; }[]' is not assignable to type 'SqlValue[]'. - Type '{ name: string | number | true | Date | Buffer; qty: number; }' is not assignable to type 'SqlValue'. -src/app/api/dashboard/ceo/route.ts(401,7): error TS2322: Type '{ year: number; total: number; }[]' is not assignable to type 'SqlValue[]'. - Type '{ year: number; total: number; }' is not assignable to type 'SqlValue'. -src/app/api/dashboard/ceo/route.ts(429,9): error TS18046: 'shiftData' is of type 'unknown'. -src/app/api/dashboard/ceo/route.ts(430,9): error TS18046: 'shiftData' is of type 'unknown'. -src/app/api/dashboard/ceo/route.ts(431,9): error TS18046: 'shiftData' is of type 'unknown'. -src/app/api/dashboard/ceo/route.ts(432,11): error TS18046: 'shiftData' is of type 'unknown'. -src/app/api/dashboard/ceo/route.ts(433,27): error TS18046: 'shiftData' is of type 'unknown'. -src/app/api/dashboard/ceo/route.ts(433,55): error TS18046: 'shiftData' is of type 'unknown'. -src/app/api/dashboard/ceo/route.ts(437,9): error TS18046: 'shiftData' is of type 'unknown'. -src/app/api/dashboard/ceo/route.ts(438,9): error TS18046: 'shiftData' is of type 'unknown'. -src/app/api/dashboard/ceo/route.ts(439,9): error TS18046: 'shiftData' is of type 'unknown'. -src/app/api/dashboard/ceo/route.ts(440,11): error TS18046: 'shiftData' is of type 'unknown'. -src/app/api/dashboard/ceo/route.ts(441,27): error TS18046: 'shiftData' is of type 'unknown'. -src/app/api/dashboard/ceo/route.ts(441,58): error TS18046: 'shiftData' is of type 'unknown'. -src/app/api/dashboard/ceo/route.ts(445,9): error TS18046: 'shiftData' is of type 'unknown'. -src/app/api/dashboard/ceo/route.ts(446,9): error TS18046: 'shiftData' is of type 'unknown'. -src/app/api/dashboard/ceo/route.ts(447,9): error TS18046: 'shiftData' is of type 'unknown'. -src/app/api/dashboard/ceo/route.ts(448,11): error TS18046: 'shiftData' is of type 'unknown'. -src/app/api/dashboard/ceo/route.ts(449,27): error TS18046: 'shiftData' is of type 'unknown'. -src/app/api/dashboard/ceo/route.ts(449,57): error TS18046: 'shiftData' is of type 'unknown'. -src/app/api/dashboard/finance/route.ts(32,9): error TS18046: 'overview' is of type 'unknown'. -src/app/api/dashboard/finance/route.ts(34,9): error TS18046: 'overview' is of type 'unknown'. -src/app/api/dashboard/finance/route.ts(35,7): error TS18046: 'overview' is of type 'unknown'. -src/app/api/dashboard/finance/route.ts(35,28): error TS18046: 'overview' is of type 'unknown'. -src/app/api/dashboard/finance/route.ts(35,55): error TS18046: 'overview' is of type 'unknown'. -src/app/api/dashboard/finance/route.ts(50,9): error TS18046: 'overview' is of type 'unknown'. -src/app/api/dashboard/finance/route.ts(52,9): error TS18046: 'overview' is of type 'unknown'. -src/app/api/dashboard/finance/route.ts(119,48): error TS2769: No overload matches this call. - Overload 1 of 4, '(value: string | number | Date): Date', gave the following error. - Argument of type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to parameter of type 'string | number | Date'. - Type 'undefined' is not assignable to type 'string | number | Date'. - Overload 2 of 4, '(value: string | number): Date', gave the following error. - Argument of type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to parameter of type 'string | number'. - Type 'undefined' is not assignable to type 'string | number'. -src/app/api/dashboard/finance/route.ts(119,77): error TS2769: No overload matches this call. - Overload 1 of 4, '(value: string | number | Date): Date', gave the following error. - Argument of type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to parameter of type 'string | number | Date'. - Type 'undefined' is not assignable to type 'string | number | Date'. - Overload 2 of 4, '(value: string | number): Date', gave the following error. - Argument of type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to parameter of type 'string | number'. - Type 'undefined' is not assignable to type 'string | number'. -src/app/api/dashboard/kpi/route.ts(54,7): error TS18046: 'otd' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(55,7): error TS18046: 'otd' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(56,7): error TS18046: 'otd' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(56,67): error TS18046: 'otd' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(57,7): error TS18046: 'otd' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(58,9): error TS18046: 'otd' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(58,43): error TS18046: 'otd' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(58,62): error TS18046: 'otd' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(88,5): error TS18046: 'inventoryTurnover' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(89,5): error TS18046: 'inventoryTurnover' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(90,5): error TS18046: 'inventoryTurnover' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(91,7): error TS18046: 'inventoryTurnover' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(92,23): error TS18046: 'inventoryTurnover' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(92,55): error TS18046: 'inventoryTurnover' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(95,5): error TS18046: 'inventoryTurnover' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(96,7): error TS18046: 'inventoryTurnover' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(96,53): error TS18046: 'inventoryTurnover' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(128,7): error TS18046: 'oee' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(139,7): error TS18046: 'oee' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(143,7): error TS18046: 'oee' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(147,7): error TS18046: 'oee' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(151,7): error TS18046: 'oee' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(152,22): error TS18046: 'oee' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(152,41): error TS18046: 'oee' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(152,59): error TS18046: 'oee' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(188,5): error TS18046: 'qualityRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(189,5): error TS18046: 'qualityRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(190,5): error TS18046: 'qualityRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(191,7): error TS18046: 'qualityRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(192,23): error TS18046: 'qualityRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(192,52): error TS18046: 'qualityRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(211,5): error TS18046: 'qualityRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(212,5): error TS18046: 'qualityRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(213,5): error TS18046: 'qualityRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(214,7): error TS18046: 'qualityRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(215,23): error TS18046: 'qualityRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(215,51): error TS18046: 'qualityRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(234,5): error TS18046: 'qualityRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(235,5): error TS18046: 'qualityRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(236,5): error TS18046: 'qualityRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(237,7): error TS18046: 'qualityRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(238,23): error TS18046: 'qualityRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(238,49): error TS18046: 'qualityRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(247,5): error TS18046: 'qualityRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(247,33): error TS18046: 'qualityRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(247,60): error TS18046: 'qualityRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(249,5): error TS18046: 'qualityRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(249,34): error TS18046: 'qualityRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(249,62): error TS18046: 'qualityRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(250,3): error TS18046: 'qualityRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(274,7): error TS2322: Type '{ id: string | number | boolean | Date | Buffer | null | undefined; name: string | number | boolean | Date | Buffer | null | undefined; qualityScore: number; deliveryScore: number; priceScore: number; overallScore: number; }[]' is not assignable to type 'SqlValue[]'. - Type '{ id: string | number | boolean | Date | Buffer | null | undefined; name: string | number | boolean | Date | Buffer | null | undefined; qualityScore: number; deliveryScore: number; priceScore: number; overallScore: number; }' is not assignable to type 'SqlValue'. -src/app/api/dashboard/kpi/route.ts(317,7): error TS2322: Type '{ id: string | number | boolean | Date | Buffer | null | undefined; name: string | number | boolean | Date | Buffer | null | undefined; creditLimit: number; creditUsed: number; usageRate: number; }[]' is not assignable to type 'SqlValue[]'. - Type '{ id: string | number | boolean | Date | Buffer | null | undefined; name: string | number | boolean | Date | Buffer | null | undefined; creditLimit: number; creditUsed: number; usageRate: number; }' is not assignable to type 'SqlValue'. -src/app/api/dashboard/kpi/route.ts(364,5): error TS18046: 'fifoCompliance' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(365,5): error TS18046: 'fifoCompliance' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(366,5): error TS18046: 'fifoCompliance' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(367,5): error TS18046: 'fifoCompliance' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(367,35): error TS18046: 'fifoCompliance' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(367,66): error TS18046: 'fifoCompliance' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(368,5): error TS18046: 'fifoCompliance' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(369,7): error TS18046: 'fifoCompliance' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(370,23): error TS18046: 'fifoCompliance' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(370,53): error TS18046: 'fifoCompliance' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(400,5): error TS18046: 'departmentEfficiency' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(401,5): error TS18046: 'departmentEfficiency' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(421,5): error TS18046: 'departmentEfficiency' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(422,5): error TS18046: 'departmentEfficiency' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(454,5): error TS18046: 'inkConsumptionRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(455,5): error TS18046: 'inkConsumptionRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(456,9): error TS18046: 'inkConsumptionRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(457,7): error TS18046: 'inkConsumptionRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(458,21): error TS18046: 'inkConsumptionRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(458,54): error TS18046: 'inkConsumptionRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(460,7): error TS18046: 'inkConsumptionRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(462,16): error TS18046: 'inkConsumptionRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(462,54): error TS18046: 'inkConsumptionRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(489,5): error TS18046: 'inkConsumptionRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(521,5): error TS18046: 'paperUtilizationRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(522,5): error TS18046: 'paperUtilizationRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(523,5): error TS18046: 'paperUtilizationRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(524,52): error TS18046: 'paperUtilizationRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(526,9): error TS18046: 'paperUtilizationRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(527,7): error TS18046: 'paperUtilizationRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(528,21): error TS18046: 'paperUtilizationRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(528,54): error TS18046: 'paperUtilizationRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(554,5): error TS18046: 'paperUtilizationRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(587,5): error TS18046: 'surplusInkReuseRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(588,5): error TS18046: 'surplusInkReuseRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(589,5): error TS18046: 'surplusInkReuseRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(590,5): error TS18046: 'surplusInkReuseRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(592,9): error TS18046: 'surplusInkReuseRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(593,7): error TS18046: 'surplusInkReuseRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(594,21): error TS18046: 'surplusInkReuseRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(594,55): error TS18046: 'surplusInkReuseRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(639,7): error TS18046: 'setupTime' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(645,7): error TS18046: 'setupTime' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(645,31): error TS18046: 'setupTime' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(646,34): error TS2365: Operator '+' cannot be applied to types 'number' and 'string | number | boolean | Date | Buffer'. -src/app/api/dashboard/kpi/route.ts(646,38): error TS18049: 'r.setupCount' is possibly 'null' or 'undefined'. -src/app/api/dashboard/kpi/route.ts(649,7): error TS18046: 'setupTime' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(649,32): error TS18046: 'setupTime' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(650,38): error TS2362: The left-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. -src/app/api/dashboard/kpi/route.ts(650,38): error TS18049: 'r.avgSetupMinutes' is possibly 'null' or 'undefined'. -src/app/api/dashboard/kpi/route.ts(650,58): error TS2363: The right-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. -src/app/api/dashboard/kpi/route.ts(650,58): error TS18049: 'r.setupCount' is possibly 'null' or 'undefined'. -src/app/api/dashboard/kpi/route.ts(653,7): error TS18046: 'setupTime' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(654,9): error TS18046: 'setupTime' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(655,25): error TS18046: 'setupTime' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(655,50): error TS18046: 'setupTime' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(682,5): error TS18046: 'ftq' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(683,5): error TS18046: 'ftq' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(684,5): error TS18046: 'ftq' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(684,28): error TS18046: 'ftq' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(684,50): error TS18046: 'ftq' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(685,5): error TS18046: 'ftq' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(686,7): error TS18046: 'ftq' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(687,23): error TS18046: 'ftq' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(687,46): error TS18046: 'ftq' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(719,5): error TS18046: 'staleInventoryRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(720,5): error TS18046: 'staleInventoryRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(721,5): error TS18046: 'staleInventoryRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(722,5): error TS18046: 'staleInventoryRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(723,5): error TS18046: 'staleInventoryRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(724,7): error TS18046: 'staleInventoryRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(724,41): error TS18046: 'staleInventoryRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(725,5): error TS18046: 'staleInventoryRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(726,7): error TS18046: 'staleInventoryRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(728,14): error TS18046: 'staleInventoryRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(728,46): error TS18046: 'staleInventoryRate' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(763,5): error TS18046: 'oeeLossAnalysis' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(764,5): error TS18046: 'oeeLossAnalysis' is of type 'unknown'. -src/app/api/dashboard/kpi/route.ts(765,5): error TS18046: 'oeeLossAnalysis' is of type 'unknown'. -src/app/api/dashboard/production/route.ts(124,9): error TS18046: 'dieStats' is of type 'unknown'. -src/app/api/dashboard/production/route.ts(125,9): error TS18046: 'dieStats' is of type 'unknown'. -src/app/api/dashboard/production/route.ts(126,9): error TS18046: 'dieStats' is of type 'unknown'. -src/app/api/dashboard/production/route.ts(127,9): error TS18046: 'dieStats' is of type 'unknown'. -src/app/api/dashboard/production/route.ts(128,9): error TS18046: 'dieStats' is of type 'unknown'. -src/app/api/dashboard/production/route.ts(128,27): error TS18046: 'dieStats' is of type 'unknown'. -src/app/api/dashboard/production/route.ts(128,44): error TS18046: 'dieStats' is of type 'unknown'. -src/app/api/dashboard/production/route.ts(128,63): error TS18046: 'dieStats' is of type 'unknown'. -src/app/api/dashboard/production/route.ts(128,81): error TS18046: 'dieStats' is of type 'unknown'. -src/app/api/dashboard/production/route.ts(136,43): error TS2339: Property 'total' does not exist on type '{}'. -src/app/api/dashboard/production/route.ts(137,45): error TS2339: Property 'running' does not exist on type '{}'. -src/app/api/dashboard/production/route.ts(145,43): error TS2339: Property 'total_orders' does not exist on type '{}'. -src/app/api/dashboard/production/route.ts(146,44): error TS2339: Property 'active_orders' does not exist on type '{}'. -src/app/api/dashboard/production/route.ts(147,46): error TS2339: Property 'completed_today' does not exist on type '{}'. -src/app/api/dashboard/production/route.ts(152,44): error TS2345: Argument of type '(e: DbRow) => { id: string | number | boolean | Date | Buffer | null | undefined; name: string | number | boolean | Date | Buffer | null | undefined; ... 5 more ...; runtime: number; }' is not assignable to parameter of type '(value: SqlValue, index: number, array: SqlValue[]) => { id: string | number | boolean | Date | Buffer | null | undefined; ... 6 more ...; runtime: number; }'. - Types of parameters 'e' and 'value' are incompatible. - Type 'SqlValue' is not assignable to type 'DbRow'. - Type 'undefined' is not assignable to type 'DbRow'. -src/app/api/dashboard/production/route.ts(169,40): error TS2345: Argument of type '(o: DbRow) => { id: string | number | boolean | Date | Buffer | null | undefined; orderNo: string | number | boolean | Date | Buffer | null | undefined; ... 4 more ...; updateTime: string | ... 5 more ... | undefined; }' is not assignable to parameter of type '(value: SqlValue, index: number, array: SqlValue[]) => { id: string | number | boolean | Date | Buffer | null | undefined; ... 5 more ...; updateTime: string | ... 5 more ... | undefined; }'. - Types of parameters 'o' and 'value' are incompatible. - Type 'SqlValue' is not assignable to type 'DbRow'. - Type 'undefined' is not assignable to type 'DbRow'. -src/app/api/dashboard/production/route.ts(179,41): error TS2339: Property 'total_opened' does not exist on type '{}'. -src/app/api/dashboard/production/route.ts(180,35): error TS2339: Property 'in_use' does not exist on type '{}'. -src/app/api/dashboard/production/route.ts(181,37): error TS2339: Property 'expired' does not exist on type '{}'. -src/app/api/dashboard/production/route.ts(182,42): error TS2339: Property 'expiring_soon' does not exist on type '{}'. -src/app/api/dashboard/production/route.ts(185,35): error TS2339: Property 'total' does not exist on type '{}'. -src/app/api/dashboard/production/route.ts(186,36): error TS2339: Property 'normal' does not exist on type '{}'. -src/app/api/dashboard/production/route.ts(187,37): error TS2339: Property 'warning' does not exist on type '{}'. -src/app/api/dashboard/production/route.ts(188,36): error TS2339: Property 'locked' does not exist on type '{}'. -src/app/api/dashboard/production/route.ts(189,38): error TS2339: Property 'scrapped' does not exist on type '{}'. -src/app/api/dashboard/quality/route.ts(26,9): error TS18046: 'overview' is of type 'unknown'. -src/app/api/dashboard/quality/route.ts(41,9): error TS18046: 'overview' is of type 'unknown'. -src/app/api/dashboard/quality/route.ts(42,9): error TS18046: 'overview' is of type 'unknown'. -src/app/api/dashboard/quality/route.ts(43,9): error TS18046: 'overview' is of type 'unknown'. -src/app/api/dashboard/quality/route.ts(44,11): error TS18046: 'overview' is of type 'unknown'. -src/app/api/dashboard/quality/route.ts(45,27): error TS18046: 'overview' is of type 'unknown'. -src/app/api/dashboard/quality/route.ts(45,56): error TS18046: 'overview' is of type 'unknown'. -src/app/api/dashboard/quality/route.ts(47,9): error TS18046: 'overview' is of type 'unknown'. -src/app/api/dashboard/quality/route.ts(48,11): error TS18046: 'overview' is of type 'unknown'. -src/app/api/dashboard/quality/route.ts(49,27): error TS18046: 'overview' is of type 'unknown'. -src/app/api/dashboard/quality/route.ts(49,56): error TS18046: 'overview' is of type 'unknown'. -src/app/api/dashboard/route.ts(102,11): error TS2353: Object literal may only specify known properties, and 'type' does not exist in type 'Date | Buffer | readonly SqlValue[]'. -src/app/api/dashboard/route.ts(115,11): error TS2353: Object literal may only specify known properties, and 'type' does not exist in type 'Date | Buffer | readonly SqlValue[]'. -src/app/api/dashboard/route.ts(123,11): error TS2353: Object literal may only specify known properties, and 'type' does not exist in type 'Date | Buffer | readonly SqlValue[]'. -src/app/api/dashboard/route.ts(163,40): error TS2345: Argument of type '(o: DbRow) => { id: string | number | boolean | Date | Buffer | null | undefined; orderNo: string | number | boolean | Date | Buffer | null | undefined; ... 4 more ...; date: string | ... 5 more ... | undefined; }' is not assignable to parameter of type '(value: SqlValue, index: number, array: SqlValue[]) => { id: string | number | boolean | Date | Buffer | null | undefined; ... 5 more ...; date: string | ... 5 more ... | undefined; }'. - Types of parameters 'o' and 'value' are incompatible. - Type 'SqlValue' is not assignable to type 'DbRow'. - Type 'undefined' is not assignable to type 'DbRow'. -src/app/api/dashboard/sales/route.ts(28,9): error TS18046: 'overview' is of type 'unknown'. -src/app/api/dashboard/sales/route.ts(29,9): error TS18046: 'overview' is of type 'unknown'. -src/app/api/dashboard/sales/route.ts(30,9): error TS18046: 'overview' is of type 'unknown'. -src/app/api/dashboard/sales/route.ts(31,9): error TS18046: 'overview' is of type 'unknown'. -src/app/api/dashboard/sales/route.ts(45,9): error TS18046: 'overview' is of type 'unknown'. -src/app/api/dashboard/warehouse/route.ts(25,9): error TS18046: 'overview' is of type 'unknown'. -src/app/api/dashboard/warehouse/route.ts(26,9): error TS18046: 'overview' is of type 'unknown'. -src/app/api/dashboard/warehouse/route.ts(27,9): error TS18046: 'overview' is of type 'unknown'. -src/app/api/dashboard/warehouse/route.ts(43,9): error TS18046: 'overview' is of type 'unknown'. -src/app/api/dashboard/warehouse/route.ts(45,9): error TS18046: 'overview' is of type 'unknown'. -src/app/api/dcprint/formula/version/[id]/activate/route.ts(9,3): error TS2345: Argument of type '(request: NextRequest, userInfo: DbRow, { params }: { params: Promise<{ id: string; }>; }) => Promise>>' is not assignable to parameter of type '(request: NextRequest, userInfo: UserInfo, context?: any) => Promise>'. - Types of parameters 'userInfo' and 'userInfo' are incompatible. - Type 'UserInfo' is not assignable to type 'DbRow'. - Index signature for type 'string' is missing in type 'UserInfo'. -src/app/api/dcprint/formula/version/[id]/activate/route.ts(17,41): error TS2345: Argument of type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to parameter of type 'number'. - Type 'undefined' is not assignable to type 'number'. -src/app/api/dcprint/formula/version/[id]/cancel/route.ts(9,3): error TS2345: Argument of type '(request: NextRequest, userInfo: DbRow, { params }: { params: Promise<{ id: string; }>; }) => Promise>>' is not assignable to parameter of type '(request: NextRequest, userInfo: UserInfo, context?: any) => Promise>'. - Types of parameters 'userInfo' and 'userInfo' are incompatible. - Type 'UserInfo' is not assignable to type 'DbRow'. - Index signature for type 'string' is missing in type 'UserInfo'. -src/app/api/dcprint/formula/version/[id]/cancel/route.ts(19,39): error TS2345: Argument of type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to parameter of type 'number'. - Type 'undefined' is not assignable to type 'number'. -src/app/api/dcprint/formula/version/[id]/duplicate/route.ts(9,3): error TS2345: Argument of type '(request: NextRequest, userInfo: DbRow, { params }: { params: Promise<{ id: string; }>; }) => Promise> | NextResponse>>' is not assignable to parameter of type '(request: NextRequest, userInfo: UserInfo, context?: any) => Promise>'. - Types of parameters 'userInfo' and 'userInfo' are incompatible. - Type 'UserInfo' is not assignable to type 'DbRow'. - Index signature for type 'string' is missing in type 'UserInfo'. -src/app/api/dcprint/formula/version/[id]/duplicate/route.ts(18,68): error TS2345: Argument of type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to parameter of type 'number'. - Type 'undefined' is not assignable to type 'number'. -src/app/api/dcprint/ink-consumption/route.ts(167,7): error TS18046: 'summary' is of type 'unknown'. -src/app/api/dcprint/ink-consumption/route.ts(168,7): error TS18046: 'summary' is of type 'unknown'. -src/app/api/dcprint/ink-consumption/route.ts(169,7): error TS18046: 'summary' is of type 'unknown'. -src/app/api/dcprint/ink-consumption/route.ts(170,7): error TS18046: 'summary' is of type 'unknown'. -src/app/api/dcprint/ink-consumption/route.ts(171,7): error TS18046: 'summary' is of type 'unknown'. -src/app/api/dcprint/ink-consumption/route.ts(172,9): error TS18046: 'summary' is of type 'unknown'. -src/app/api/dcprint/ink-consumption/route.ts(174,17): error TS18046: 'summary' is of type 'unknown'. -src/app/api/dcprint/ink-consumption/route.ts(174,42): error TS18046: 'summary' is of type 'unknown'. -src/app/api/dcprint/ink-consumption/route.ts(174,68): error TS18046: 'summary' is of type 'unknown'. -src/app/api/dcprint/ink-consumption/route.ts(177,7): error TS18046: 'summary' is of type 'unknown'. -src/app/api/dcprint/ink-consumption/route.ts(179,25): error TS18046: 'summary' is of type 'unknown'. -src/app/api/dcprint/ink-dispatch/route.ts(118,39): error TS2339: Property 'insertId' does not exist on type 'QueryResult'. - Property 'insertId' does not exist on type 'ResultSetHeader[]'. -src/app/api/dcprint/ink-dispatch/route.ts(131,25): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/dcprint/ink-dispatch/route.ts(132,27): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/dcprint/ink-dispatch/route.ts(200,34): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/dcprint/ink-dispatch/route.ts(200,47): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/dcprint/ink-dispatch/route.ts(248,26): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/dcprint/ink-dispatch/route.ts(248,40): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/dcprint/ink-dispatch/route.ts(249,46): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/dcprint/ink-dispatch/route.ts(252,42): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/dcprint/ink-formula/route.ts(118,38): error TS2339: Property 'insertId' does not exist on type 'QueryResult'. - Property 'insertId' does not exist on type 'ResultSetHeader[]'. -src/app/api/dcprint/ink-init/route.ts(20,7): error TS2345: Argument of type '{ table: string; status: string; message?: undefined; } | { table: string; status: string; message: string; }' is not assignable to parameter of type 'SqlValue'. - Type '{ table: string; status: string; message?: undefined; }' is not assignable to type 'SqlValue'. -src/app/api/dcprint/ink-init/route.ts(51,7): error TS2345: Argument of type '{ table: string; status: string; message?: undefined; } | { table: string; status: string; message: string; }' is not assignable to parameter of type 'SqlValue'. - Type '{ table: string; status: string; message?: undefined; }' is not assignable to type 'SqlValue'. -src/app/api/dcprint/ink-init/route.ts(77,7): error TS2345: Argument of type '{ table: string; status: string; message?: undefined; } | { table: string; status: string; message: string; }' is not assignable to parameter of type 'SqlValue'. - Type '{ table: string; status: string; message?: undefined; }' is not assignable to type 'SqlValue'. -src/app/api/dcprint/ink-init/route.ts(97,7): error TS2345: Argument of type '{ table: string; status: string; message?: undefined; } | { table: string; status: string; message: string; }' is not assignable to parameter of type 'SqlValue'. - Type '{ table: string; status: string; message?: undefined; }' is not assignable to type 'SqlValue'. -src/app/api/dcprint/ink-init/route.ts(136,7): error TS2345: Argument of type '{ table: string; status: string; message?: undefined; } | { table: string; status: string; message: string; }' is not assignable to parameter of type 'SqlValue'. - Type '{ table: string; status: string; message?: undefined; }' is not assignable to type 'SqlValue'. -src/app/api/dcprint/ink-init/route.ts(166,7): error TS2345: Argument of type '{ table: string; status: string; message?: undefined; } | { table: string; status: string; message: string; }' is not assignable to parameter of type 'SqlValue'. - Type '{ table: string; status: string; message?: undefined; }' is not assignable to type 'SqlValue'. -src/app/api/dcprint/ink-init/route.ts(205,13): error TS2488: Type 'ResultSetHeader' must have a '[Symbol.iterator]()' method that returns an iterator. -src/app/api/dcprint/ink-init/route.ts(216,11): error TS2353: Object literal may only specify known properties, and 'table' does not exist in type 'Date | Buffer | readonly SqlValue[]'. -src/app/api/dcprint/ink-init/route.ts(223,9): error TS2353: Object literal may only specify known properties, and 'table' does not exist in type 'Date | Buffer | readonly SqlValue[]'. -src/app/api/dcprint/ink-init/route.ts(231,13): error TS2488: Type 'ResultSetHeader' must have a '[Symbol.iterator]()' method that returns an iterator. -src/app/api/dcprint/ink-init/route.ts(239,11): error TS2353: Object literal may only specify known properties, and 'table' does not exist in type 'Date | Buffer | readonly SqlValue[]'. -src/app/api/dcprint/ink-init/route.ts(246,9): error TS2353: Object literal may only specify known properties, and 'table' does not exist in type 'Date | Buffer | readonly SqlValue[]'. -src/app/api/dcprint/ink-init/route.ts(254,13): error TS2488: Type 'ResultSetHeader' must have a '[Symbol.iterator]()' method that returns an iterator. -src/app/api/dcprint/ink-init/route.ts(259,24): error TS2353: Object literal may only specify known properties, and 'table' does not exist in type 'Date | Buffer | readonly SqlValue[]'. -src/app/api/dcprint/ink-init/route.ts(263,9): error TS2353: Object literal may only specify known properties, and 'table' does not exist in type 'Date | Buffer | readonly SqlValue[]'. -src/app/api/dcprint/ink-opening/route.ts(76,29): error TS2345: Argument of type 'string | number | true | Date | Buffer' is not assignable to parameter of type 'string'. - Type 'number' is not assignable to type 'string'. -src/app/api/dcprint/ink-opening/route.ts(77,29): error TS2345: Argument of type 'string | number | true | Date | Buffer' is not assignable to parameter of type 'string'. - Type 'number' is not assignable to type 'string'. -src/app/api/dcprint/ink-opening/route.ts(78,31): error TS2345: Argument of type 'string | number | true | Date | Buffer' is not assignable to parameter of type 'string'. - Type 'number' is not assignable to type 'string'. -src/app/api/dcprint/ink-opening/route.ts(79,32): error TS2345: Argument of type 'string | number | true | Date | Buffer' is not assignable to parameter of type 'string'. - Type 'number' is not assignable to type 'string'. -src/app/api/dcprint/ink-opening/route.ts(80,37): error TS2345: Argument of type 'string | number | true | Date | Buffer' is not assignable to parameter of type 'string'. - Type 'number' is not assignable to type 'string'. -src/app/api/dcprint/ink-opening/route.ts(169,23): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/dcprint/ink-opening/route.ts(169,37): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/dcprint/ink-opening/route.ts(170,40): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/dcprint/ink-opening/route.ts(199,37): error TS2339: Property 'insertId' does not exist on type 'QueryResult'. - Property 'insertId' does not exist on type 'ResultSetHeader[]'. -src/app/api/dcprint/ink-query/route.ts(37,5): error TS18046: 'result' is of type 'unknown'. -src/app/api/dcprint/ink-query/route.ts(41,5): error TS18046: 'result' is of type 'unknown'. -src/app/api/dcprint/ink-query/route.ts(45,5): error TS18046: 'result' is of type 'unknown'. -src/app/api/dcprint/ink-query/route.ts(49,5): error TS18046: 'result' is of type 'unknown'. -src/app/api/dcprint/ink-query/route.ts(65,5): error TS18046: 'trace' is of type 'unknown'. -src/app/api/dcprint/ink-query/route.ts(88,9): error TS18046: 'trace' is of type 'unknown'. -src/app/api/dcprint/ink-query/route.ts(113,5): error TS18046: 'trace' is of type 'unknown'. -src/app/api/dcprint/ink-query/route.ts(130,32): error TS18046: 'trace' is of type 'unknown'. -src/app/api/dcprint/ink-query/route.ts(132,5): error TS18046: 'trace' is of type 'unknown'. -src/app/api/dcprint/ink-query/route.ts(140,5): error TS18046: 'trace' is of type 'unknown'. -src/app/api/dcprint/ink-query/route.ts(162,7): error TS18046: 'guide' is of type 'unknown'. -src/app/api/dcprint/ink-query/route.ts(177,11): error TS18046: 'guide' is of type 'unknown'. -src/app/api/dcprint/ink-query/route.ts(192,11): error TS18046: 'guide' is of type 'unknown'. -src/app/api/dcprint/ink-query/route.ts(224,9): error TS18046: 'trace' is of type 'unknown'. -src/app/api/dcprint/ink-query/route.ts(238,5): error TS18046: 'trace' is of type 'unknown'. -src/app/api/dcprint/ink-query/route.ts(258,13): error TS18046: 'trace' is of type 'unknown'. -src/app/api/dcprint/ink-query/route.ts(293,5): error TS18046: 'info' is of type 'unknown'. -src/app/api/dcprint/ink-query/route.ts(303,5): error TS18046: 'info' is of type 'unknown'. -src/app/api/dcprint/ink-query/route.ts(327,5): error TS18046: 'info' is of type 'unknown'. -src/app/api/dcprint/ink-surplus/route.ts(157,34): error TS18049: 'b' is possibly 'null' or 'undefined'. -src/app/api/dcprint/ink-surplus/route.ts(157,36): error TS2339: Property 'match_score' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'match_score' does not exist on type 'string'. -src/app/api/dcprint/ink-surplus/route.ts(157,50): error TS18049: 'a' is possibly 'null' or 'undefined'. -src/app/api/dcprint/ink-surplus/route.ts(157,52): error TS2339: Property 'match_score' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'match_score' does not exist on type 'string'. -src/app/api/dcprint/ink-surplus/route.ts(260,21): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/dcprint/ink-surplus/route.ts(264,21): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/dcprint/ink-surplus/route.ts(301,33): error TS2339: Property 'insertId' does not exist on type 'QueryResult'. - Property 'insertId' does not exist on type 'ResultSetHeader[]'. -src/app/api/dcprint/ink-usage/route.ts(102,20): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/dcprint/ink-usage/route.ts(103,27): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/dcprint/ink-usage/route.ts(116,21): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/dcprint/ink-usage/route.ts(120,21): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/dcprint/ink-usage/route.ts(160,26): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/dcprint/ink-usage/route.ts(161,28): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/dcprint/ink-usage/route.ts(167,27): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/dcprint/ink-usage/route.ts(173,31): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/dcprint/ink-usage/route.ts(173,45): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/dcprint/ink-usage/route.ts(173,77): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/dcprint/ink-usage/route.ts(175,53): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/dcprint/ink-usage/route.ts(175,94): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/dcprint/ink-usage/route.ts(225,33): error TS2339: Property 'insertId' does not exist on type 'QueryResult'. - Property 'insertId' does not exist on type 'ResultSetHeader[]'. -src/app/api/dcprint/labels/route.ts(148,5): error TS2345: Argument of type 'SqlValue[]' is not assignable to parameter of type 'DbRow[]'. - Type 'SqlValue' is not assignable to type 'DbRow'. - Type 'undefined' is not assignable to type 'DbRow'. -src/app/api/dcprint/labels/route.ts(334,46): error TS2322: Type 'DbRow' is not assignable to type 'SqlValue'. - Type 'DbRow' is missing the following properties from type 'Buffer': slice, subarray, write, toJSON, and 102 more. -src/app/api/dcprint/labels/route.ts(334,57): error TS2322: Type 'number | DbRow' is not assignable to type 'SqlValue'. - Type 'DbRow' is not assignable to type 'SqlValue'. - Type 'DbRow' is missing the following properties from type 'Buffer': slice, subarray, write, toJSON, and 102 more. -src/app/api/dcprint/labels/route.ts(334,67): error TS2322: Type 'number | DbRow' is not assignable to type 'SqlValue'. - Type 'DbRow' is not assignable to type 'SqlValue'. - Type 'DbRow' is missing the following properties from type 'Buffer': slice, subarray, write, toJSON, and 102 more. -src/app/api/dcprint/labels/route.ts(335,45): error TS2345: Argument of type 'DbRow[]' is not assignable to parameter of type 'SqlValue[]'. - Type 'DbRow' is not assignable to type 'SqlValue'. - Type 'DbRow' is missing the following properties from type 'Buffer': slice, subarray, write, toJSON, and 102 more. -src/app/api/dcprint/process-cards/route.ts(92,5): error TS2345: Argument of type 'SqlValue[]' is not assignable to parameter of type 'DbRow[]'. - Type 'SqlValue' is not assignable to type 'DbRow'. - Type 'undefined' is not assignable to type 'DbRow'. -src/app/api/dcprint/process-cards/route.ts(147,19): error TS2339: Property 'is_main_material' does not exist on type '{}'. -src/app/api/dcprint/process-cards/route.ts(160,23): error TS2339: Property 'material_code' does not exist on type '{}'. -src/app/api/dcprint/process-cards/route.ts(161,23): error TS2339: Property 'batch_no' does not exist on type '{}'. -src/app/api/dcprint/process-cards/route.ts(190,23): error TS2352: Conversion of type '[QueryResult, FieldPacket[]]' to type 'DbRow' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first. - Index signature for type 'string' is missing in type '[QueryResult, FieldPacket[]]'. -src/app/api/dcprint/process-cards/route.ts(258,12): error TS2339: Property 'lock_status' does not exist on type '{}'. -src/app/api/dcprint/process-cards/route.ts(280,12): error TS2339: Property 'id' does not exist on type '{}'. -src/app/api/dcprint/process-cards/route.ts(281,12): error TS2339: Property 'card_no' does not exist on type '{}'. -src/app/api/dcprint/process-cards/route.ts(284,13): error TS2339: Property 'material_code' does not exist on type '{}'. -src/app/api/dcprint/process-cards/route.ts(285,13): error TS2339: Property 'material_name' does not exist on type '{}'. -src/app/api/dcprint/process-cards/route.ts(286,13): error TS2339: Property 'specification' does not exist on type '{}'. -src/app/api/dcprint/process-cards/route.ts(287,13): error TS2339: Property 'batch_no' does not exist on type '{}'. -src/app/api/dcprint/process-cards/route.ts(288,13): error TS2339: Property 'quantity' does not exist on type '{}'. -src/app/api/dcprint/process-cards/route.ts(289,13): error TS2339: Property 'unit' does not exist on type '{}'. -src/app/api/dcprint/process-cards/route.ts(383,52): error TS2322: Type 'DbRow' is not assignable to type 'SqlValue'. - Type 'DbRow' is missing the following properties from type 'Buffer': slice, subarray, write, toJSON, and 102 more. -src/app/api/dcprint/process-cards/route.ts(383,71): error TS2322: Type 'number | DbRow' is not assignable to type 'SqlValue'. - Type 'DbRow' is not assignable to type 'SqlValue'. - Type 'DbRow' is missing the following properties from type 'Buffer': slice, subarray, write, toJSON, and 102 more. -src/app/api/dcprint/process-cards/route.ts(383,81): error TS2322: Type 'number | DbRow' is not assignable to type 'SqlValue'. - Type 'DbRow' is not assignable to type 'SqlValue'. - Type 'DbRow' is missing the following properties from type 'Buffer': slice, subarray, write, toJSON, and 102 more. -src/app/api/dcprint/process-cards/route.ts(384,45): error TS2345: Argument of type 'DbRow[]' is not assignable to parameter of type 'SqlValue[]'. - Type 'DbRow' is not assignable to type 'SqlValue'. - Type 'DbRow' is missing the following properties from type 'Buffer': slice, subarray, write, toJSON, and 102 more. -src/app/api/dcprint/sample-card/[id]/cancel/route.ts(10,3): error TS2345: Argument of type '(_request: NextRequest, userInfo: DbRow, { params }: { params: Promise<{ id: string; }>; }) => Promise> | NextResponse>>' is not assignable to parameter of type '(request: NextRequest, userInfo: UserInfo, context?: any) => Promise>'. - Types of parameters 'userInfo' and 'userInfo' are incompatible. - Type 'UserInfo' is not assignable to type 'DbRow'. - Index signature for type 'string' is missing in type 'UserInfo'. -src/app/api/dcprint/sample-card/[id]/cancel/route.ts(17,44): error TS2345: Argument of type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to parameter of type 'number'. - Type 'undefined' is not assignable to type 'number'. -src/app/api/dcprint/sample-card/[id]/confirm/route.ts(10,3): error TS2345: Argument of type '(_request: NextRequest, userInfo: DbRow, { params }: { params: Promise<{ id: string; }>; }) => Promise> | NextResponse>>' is not assignable to parameter of type '(request: NextRequest, userInfo: UserInfo, context?: any) => Promise>'. - Types of parameters 'userInfo' and 'userInfo' are incompatible. - Type 'UserInfo' is not assignable to type 'DbRow'. - Index signature for type 'string' is missing in type 'UserInfo'. -src/app/api/dcprint/sample-card/[id]/confirm/route.ts(17,45): error TS2345: Argument of type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to parameter of type 'number'. - Type 'undefined' is not assignable to type 'number'. -src/app/api/dcprint/sample-card/[id]/convert-work-order/route.ts(10,3): error TS2345: Argument of type '(request: NextRequest, userInfo: DbRow, { params }: { params: Promise<{ id: string; }>; }) => Promise> | NextResponse>>' is not assignable to parameter of type '(request: NextRequest, userInfo: UserInfo, context?: any) => Promise>'. - Types of parameters 'userInfo' and 'userInfo' are incompatible. - Type 'UserInfo' is not assignable to type 'DbRow'. - Index signature for type 'string' is missing in type 'UserInfo'. -src/app/api/dcprint/sample-card/[id]/convert-work-order/route.ts(26,9): error TS2345: Argument of type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to parameter of type 'number'. - Type 'undefined' is not assignable to type 'number'. -src/app/api/dcprint/sample-card/[id]/cost-variance/route.ts(10,3): error TS2345: Argument of type '(_request: NextRequest, _userInfo: DbRow, { params }: { params: Promise<{ id: string; }>; }) => Promise> | NextResponse Promise>'. - Types of parameters '_userInfo' and 'userInfo' are incompatible. - Type 'UserInfo' is not assignable to type 'DbRow'. - Index signature for type 'string' is missing in type 'UserInfo'. -src/app/api/dcprint/sample-card/[id]/duplicate/route.ts(10,3): error TS2345: Argument of type '(_request: NextRequest, userInfo: DbRow, { params }: { params: Promise<{ id: string; }>; }) => Promise> | NextResponse>>' is not assignable to parameter of type '(request: NextRequest, userInfo: UserInfo, context?: any) => Promise>'. - Types of parameters 'userInfo' and 'userInfo' are incompatible. - Type 'UserInfo' is not assignable to type 'DbRow'. - Index signature for type 'string' is missing in type 'UserInfo'. -src/app/api/dcprint/sample-card/[id]/duplicate/route.ts(17,68): error TS2345: Argument of type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to parameter of type 'number'. - Type 'undefined' is not assignable to type 'number'. -src/app/api/dcprint/sample-card/[id]/generate-quote/route.ts(10,3): error TS2345: Argument of type '(request: NextRequest, userInfo: DbRow, { params }: { params: Promise<{ id: string; }>; }) => Promise> | NextResponse>>' is not assignable to parameter of type '(request: NextRequest, userInfo: UserInfo, context?: any) => Promise>'. - Types of parameters 'userInfo' and 'userInfo' are incompatible. - Type 'UserInfo' is not assignable to type 'DbRow'. - Index signature for type 'string' is missing in type 'UserInfo'. -src/app/api/dcprint/sample-card/[id]/generate-quote/route.ts(26,9): error TS2345: Argument of type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to parameter of type 'number'. - Type 'undefined' is not assignable to type 'number'. -src/app/api/dcprint/sample-card/[id]/route.ts(11,3): error TS2345: Argument of type '(_request: NextRequest, _userInfo: DbRow, { params }: { params: Promise<{ id: string; }>; }) => Promise> | NextResponse>>' is not assignable to parameter of type '(request: NextRequest, userInfo: UserInfo, context?: any) => Promise>'. - Types of parameters '_userInfo' and 'userInfo' are incompatible. - Type 'UserInfo' is not assignable to type 'DbRow'. - Index signature for type 'string' is missing in type 'UserInfo'. -src/app/api/dcprint/sample-card/[id]/route.ts(25,3): error TS2345: Argument of type '(request: NextRequest, userInfo: DbRow, { params }: { params: Promise<{ id: string; }>; }) => Promise> | NextResponse>>' is not assignable to parameter of type '(request: NextRequest, userInfo: UserInfo, context?: any) => Promise>'. - Types of parameters 'userInfo' and 'userInfo' are incompatible. - Type 'UserInfo' is not assignable to type 'DbRow'. - Index signature for type 'string' is missing in type 'UserInfo'. -src/app/api/dcprint/sample-card/[id]/route.ts(41,57): error TS2345: Argument of type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to parameter of type 'number'. - Type 'undefined' is not assignable to type 'number'. -src/app/api/dcprint/sample-card/[id]/route.ts(51,3): error TS2345: Argument of type '(_request: NextRequest, _userInfo: DbRow, { params }: { params: Promise<{ id: string; }>; }) => Promise> | NextResponse>>' is not assignable to parameter of type '(request: NextRequest, userInfo: UserInfo, context?: any) => Promise>'. - Types of parameters '_userInfo' and 'userInfo' are incompatible. - Type 'UserInfo' is not assignable to type 'DbRow'. - Index signature for type 'string' is missing in type 'UserInfo'. -src/app/api/dcprint/sample-card/[id]/save-as-template/route.ts(11,3): error TS2345: Argument of type '(request: NextRequest, userInfo: DbRow, { params }: { params: Promise<{ id: string; }>; }) => Promise> | NextResponse>>' is not assignable to parameter of type '(request: NextRequest, userInfo: UserInfo, context?: any) => Promise>'. - Types of parameters 'userInfo' and 'userInfo' are incompatible. - Type 'UserInfo' is not assignable to type 'DbRow'. - Index signature for type 'string' is missing in type 'UserInfo'. -src/app/api/dcprint/sample-card/[id]/save-as-template/route.ts(26,9): error TS2345: Argument of type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to parameter of type 'number'. - Type 'undefined' is not assignable to type 'number'. -src/app/api/dcprint/sample-card/[id]/submit/route.ts(10,3): error TS2345: Argument of type '(_request: NextRequest, userInfo: DbRow, { params }: { params: Promise<{ id: string; }>; }) => Promise> | NextResponse>>' is not assignable to parameter of type '(request: NextRequest, userInfo: UserInfo, context?: any) => Promise>'. - Types of parameters 'userInfo' and 'userInfo' are incompatible. - Type 'UserInfo' is not assignable to type 'DbRow'. - Index signature for type 'string' is missing in type 'UserInfo'. -src/app/api/dcprint/sample-card/[id]/submit/route.ts(17,59): error TS2345: Argument of type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to parameter of type 'number'. - Type 'undefined' is not assignable to type 'number'. -src/app/api/dcprint/sample-card/template/[id]/route.ts(21,3): error TS2345: Argument of type '(request: NextRequest, userInfo: DbRow, { params }: { params: Promise<{ id: string; }>; }) => Promise>>' is not assignable to parameter of type '(request: NextRequest, userInfo: UserInfo, context?: any) => Promise>'. - Types of parameters 'userInfo' and 'userInfo' are incompatible. - Type 'UserInfo' is not assignable to type 'DbRow'. - Index signature for type 'string' is missing in type 'UserInfo'. -src/app/api/dcprint/sample-card/template/[id]/route.ts(28,52): error TS2345: Argument of type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to parameter of type 'number'. - Type 'undefined' is not assignable to type 'number'. -src/app/api/dcprint/sample-card/template/route.ts(23,3): error TS2345: Argument of type '(request: NextRequest, userInfo: DbRow) => Promise> | NextResponse>>' is not assignable to parameter of type '(request: NextRequest, userInfo: UserInfo, context?: any) => Promise>'. - Types of parameters 'userInfo' and 'userInfo' are incompatible. - Type 'UserInfo' is not assignable to type 'DbRow'. - Index signature for type 'string' is missing in type 'UserInfo'. -src/app/api/dcprint/sample-card/template/route.ts(28,51): error TS2345: Argument of type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to parameter of type 'number'. - Type 'undefined' is not assignable to type 'number'. -src/app/api/dcprint/scan/route.ts(25,21): error TS18046: 'qrData' is of type 'unknown'. -src/app/api/dcprint/scan/route.ts(26,18): error TS18046: 'qrData' is of type 'unknown'. -src/app/api/dcprint/scan/route.ts(123,13): error TS2339: Property 'isMainMaterial' does not exist on type '{}'. -src/app/api/dcprint/scan/route.ts(141,14): error TS2339: Property 'id' does not exist on type '{}'. -src/app/api/dcprint/scan/route.ts(143,11): error TS2339: Property 'cuttingRecords' does not exist on type '{}'. -src/app/api/dcprint/scan/route.ts(147,13): error TS2339: Property 'isCut' does not exist on type '{}'. -src/app/api/dcprint/scan/route.ts(159,14): error TS2339: Property 'id' does not exist on type '{}'. -src/app/api/dcprint/scan/route.ts(161,11): error TS2339: Property 'childLabels' does not exist on type '{}'. -src/app/api/dcprint/scan/route.ts(205,16): error TS2339: Property 'id' does not exist on type '{}'. -src/app/api/dcprint/scan/route.ts(208,13): error TS2339: Property 'processCards' does not exist on type '{}'. -src/app/api/dcprint/scan/route.ts(263,11): error TS2339: Property 'id' does not exist on type '{}'. -src/app/api/dcprint/scan/route.ts(266,8): error TS2339: Property 'materials' does not exist on type '{}'. -src/app/api/dcprint/scan/route.ts(267,8): error TS2339: Property 'mainMaterials' does not exist on type '{}'. -src/app/api/dcprint/scan/route.ts(267,42): error TS2769: No overload matches this call. - Overload 1 of 2, '(predicate: (value: unknown[], index: number, array: unknown[][]) => value is unknown[], thisArg?: any): unknown[][]', gave the following error. - Argument of type '(m: DbRow) => boolean' is not assignable to parameter of type '(value: unknown[], index: number, array: unknown[][]) => value is unknown[]'. - Types of parameters 'm' and 'value' are incompatible. - Type 'unknown[]' is not assignable to type 'DbRow'. - Index signature for type 'string' is missing in type 'unknown[]'. - Overload 2 of 2, '(predicate: (value: unknown[], index: number, array: unknown[][]) => unknown, thisArg?: any): unknown[][]', gave the following error. - Argument of type '(m: DbRow) => boolean' is not assignable to parameter of type '(value: unknown[], index: number, array: unknown[][]) => unknown'. - Types of parameters 'm' and 'value' are incompatible. - Type 'unknown[]' is not assignable to type 'DbRow'. - Index signature for type 'string' is missing in type 'unknown[]'. -src/app/api/dcprint/scan/route.ts(268,8): error TS2339: Property 'auxiliaryMaterials' does not exist on type '{}'. -src/app/api/dcprint/scan/route.ts(268,47): error TS2769: No overload matches this call. - Overload 1 of 2, '(predicate: (value: unknown[], index: number, array: unknown[][]) => value is unknown[], thisArg?: any): unknown[][]', gave the following error. - Argument of type '(m: DbRow) => boolean' is not assignable to parameter of type '(value: unknown[], index: number, array: unknown[][]) => value is unknown[]'. - Types of parameters 'm' and 'value' are incompatible. - Type 'unknown[]' is not assignable to type 'DbRow'. - Index signature for type 'string' is missing in type 'unknown[]'. - Overload 2 of 2, '(predicate: (value: unknown[], index: number, array: unknown[][]) => unknown, thisArg?: any): unknown[][]', gave the following error. - Argument of type '(m: DbRow) => boolean' is not assignable to parameter of type '(value: unknown[], index: number, array: unknown[][]) => unknown'. - Types of parameters 'm' and 'value' are incompatible. - Type 'unknown[]' is not assignable to type 'DbRow'. - Index signature for type 'string' is missing in type 'unknown[]'. -src/app/api/dcprint/tool/[id]/activate/route.ts(11,3): error TS2345: Argument of type '(request: NextRequest, _userInfo: DbRow, { params }: { params: Promise<{ id: string; }>; }) => Promise> | NextResponse>>' is not assignable to parameter of type '(request: NextRequest, userInfo: UserInfo, context?: any) => Promise>'. - Types of parameters '_userInfo' and 'userInfo' are incompatible. - Type 'UserInfo' is not assignable to type 'DbRow'. - Index signature for type 'string' is missing in type 'UserInfo'. -src/app/api/dcprint/tool/[id]/maintenance/route.ts(11,3): error TS2345: Argument of type '(request: NextRequest, _userInfo: DbRow, { params }: { params: Promise<{ id: string; }>; }) => Promise>>' is not assignable to parameter of type '(request: NextRequest, userInfo: UserInfo, context?: any) => Promise>'. - Types of parameters '_userInfo' and 'userInfo' are incompatible. - Type 'UserInfo' is not assignable to type 'DbRow'. - Index signature for type 'string' is missing in type 'UserInfo'. -src/app/api/dcprint/tool/[id]/maintenance/route.ts(24,3): error TS2345: Argument of type '(request: NextRequest, userInfo: DbRow, { params }: { params: Promise<{ id: string; }>; }) => Promise> | NextResponse> | NextResponse<...>>' is not assignable to parameter of type '(request: NextRequest, userInfo: UserInfo, context?: any) => Promise>'. - Types of parameters 'userInfo' and 'userInfo' are incompatible. - Type 'UserInfo' is not assignable to type 'DbRow'. - Index signature for type 'string' is missing in type 'UserInfo'. -src/app/api/dcprint/tool/[id]/maintenance/route.ts(45,9): error TS2322: Type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to type 'number | undefined'. - Type 'null' is not assignable to type 'number | undefined'. -src/app/api/dcprint/tool/[id]/maintenance/route.ts(46,9): error TS2322: Type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to type 'string | undefined'. - Type 'null' is not assignable to type 'string | undefined'. -src/app/api/dcprint/tool/[id]/route.ts(11,3): error TS2345: Argument of type '(request: NextRequest, _userInfo: DbRow, { params }: { params: Promise<{ id: string; }>; }) => Promise> | NextResponse>>' is not assignable to parameter of type '(request: NextRequest, userInfo: UserInfo, context?: any) => Promise>'. - Types of parameters '_userInfo' and 'userInfo' are incompatible. - Type 'UserInfo' is not assignable to type 'DbRow'. - Index signature for type 'string' is missing in type 'UserInfo'. -src/app/api/dcprint/tool/[id]/route.ts(25,3): error TS2345: Argument of type '(request: NextRequest, _userInfo: DbRow, { params }: { params: Promise<{ id: string; }>; }) => Promise>>' is not assignable to parameter of type '(request: NextRequest, userInfo: UserInfo, context?: any) => Promise>'. - Types of parameters '_userInfo' and 'userInfo' are incompatible. - Type 'UserInfo' is not assignable to type 'DbRow'. - Index signature for type 'string' is missing in type 'UserInfo'. -src/app/api/dcprint/tool/[id]/route.ts(39,3): error TS2345: Argument of type '(request: NextRequest, _userInfo: DbRow, { params }: { params: Promise<{ id: string; }>; }) => Promise> | NextResponse>>' is not assignable to parameter of type '(request: NextRequest, userInfo: UserInfo, context?: any) => Promise>'. - Types of parameters '_userInfo' and 'userInfo' are incompatible. - Type 'UserInfo' is not assignable to type 'DbRow'. - Index signature for type 'string' is missing in type 'UserInfo'. -src/app/api/dcprint/tool/[id]/scrap/route.ts(11,3): error TS2345: Argument of type '(request: NextRequest, userInfo: DbRow, { params }: { params: Promise<{ id: string; }>; }) => Promise> | NextResponse>>' is not assignable to parameter of type '(request: NextRequest, userInfo: UserInfo, context?: any) => Promise>'. - Types of parameters 'userInfo' and 'userInfo' are incompatible. - Type 'UserInfo' is not assignable to type 'DbRow'. - Index signature for type 'string' is missing in type 'UserInfo'. -src/app/api/dcprint/tool/[id]/scrap/route.ts(22,9): error TS2322: Type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to type 'number'. - Type 'undefined' is not assignable to type 'number'. -src/app/api/dcprint/tool/[id]/usage/route.ts(11,3): error TS2345: Argument of type '(request: NextRequest, _userInfo: DbRow, { params }: { params: Promise<{ id: string; }>; }) => Promise>>' is not assignable to parameter of type '(request: NextRequest, userInfo: UserInfo, context?: any) => Promise>'. - Types of parameters '_userInfo' and 'userInfo' are incompatible. - Type 'UserInfo' is not assignable to type 'DbRow'. - Index signature for type 'string' is missing in type 'UserInfo'. -src/app/api/dcprint/tool/[id]/usage/route.ts(24,3): error TS2345: Argument of type '(request: NextRequest, userInfo: DbRow, { params }: { params: Promise<{ id: string; }>; }) => Promise> | NextResponse>>' is not assignable to parameter of type '(request: NextRequest, userInfo: UserInfo, context?: any) => Promise>'. - Types of parameters 'userInfo' and 'userInfo' are incompatible. - Type 'UserInfo' is not assignable to type 'DbRow'. - Index signature for type 'string' is missing in type 'UserInfo'. -src/app/api/dcprint/tool/[id]/usage/route.ts(39,9): error TS2322: Type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to type 'number | undefined'. - Type 'null' is not assignable to type 'number | undefined'. -src/app/api/dcprint/tool/[id]/usage/route.ts(40,9): error TS2322: Type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to type 'string | undefined'. - Type 'null' is not assignable to type 'string | undefined'. -src/app/api/dcprint/trace/route.ts(106,5): error TS2345: Argument of type 'SqlValue[]' is not assignable to parameter of type 'DbRow[]'. - Type 'SqlValue' is not assignable to type 'DbRow'. - Type 'undefined' is not assignable to type 'DbRow'. -src/app/api/dcprint/trace/route.ts(113,29): error TS2345: Argument of type '(item: DbRow) => { id: string | number | boolean | Date | Buffer | null | undefined; traceNo: string | number | boolean | Date | Buffer | null | undefined; ... 10 more ...; remark: string | ... 5 more ... | undefined; }' is not assignable to parameter of type '(value: unknown[], index: number, array: unknown[][]) => { id: string | number | boolean | Date | Buffer | null | undefined; traceNo: string | number | ... 4 more ... | undefined; ... 10 more ...; remark: string | ... 5 more ... | undefined; }'. - Types of parameters 'item' and 'value' are incompatible. - Type 'unknown[]' is not assignable to type 'DbRow'. - Index signature for type 'string' is missing in type 'unknown[]'. -src/app/api/dcprint/trace/route.ts(186,13): error TS2339: Property 'id' does not exist on type '{}'. -src/app/api/dcprint/trace/route.ts(199,14): error TS2339: Property 'id' does not exist on type '{}'. -src/app/api/dcprint/trace/route.ts(200,14): error TS2339: Property 'card_no' does not exist on type '{}'. -src/app/api/dcprint/trace/route.ts(201,14): error TS2339: Property 'work_order_no' does not exist on type '{}'. -src/app/api/dcprint/trace/route.ts(202,14): error TS2339: Property 'product_code' does not exist on type '{}'. -src/app/api/dcprint/trace/route.ts(203,14): error TS2339: Property 'main_label_id' does not exist on type '{}'. -src/app/api/dcprint/trace/route.ts(243,24): error TS2339: Property 'card_no' does not exist on type '{}'. -src/app/api/dcprint/trace/route.ts(244,29): error TS2339: Property 'work_order_no' does not exist on type '{}'. -src/app/api/dcprint/trace/route.ts(245,29): error TS2339: Property 'product_code' does not exist on type '{}'. -src/app/api/dcprint/trace/route.ts(246,29): error TS2339: Property 'product_name' does not exist on type '{}'. -src/app/api/dcprint/trace/route.ts(249,25): error TS2339: Property 'main_label_no' does not exist on type '{}'. -src/app/api/dcprint/trace/route.ts(250,30): error TS2339: Property 'main_material_code' does not exist on type '{}'. -src/app/api/dcprint/trace/route.ts(251,30): error TS2339: Property 'main_material_name' does not exist on type '{}'. -src/app/api/dcprint/trace/route.ts(252,31): error TS2339: Property 'main_specification' does not exist on type '{}'. -src/app/api/dcprint/trace/route.ts(253,25): error TS2339: Property 'main_batch_no' does not exist on type '{}'. -src/app/api/dcprint/trace/route.ts(254,30): error TS2339: Property 'main_supplier_name' does not exist on type '{}'. -src/app/api/dcprint/trace/route.ts(255,29): error TS2339: Property 'main_receive_date' does not exist on type '{}'. -src/app/api/dcprint/trace/route.ts(256,33): error TS2339: Property 'main_purchase_order_no' does not exist on type '{}'. -src/app/api/dcprint/trace/route.ts(317,12): error TS2339: Property 'id' does not exist on type '{}'. -src/app/api/dcprint/trace/route.ts(339,50): error TS2322: Type 'DbRow' is not assignable to type 'SqlValue'. - Type 'DbRow' is missing the following properties from type 'Buffer': slice, subarray, write, toJSON, and 102 more. -src/app/api/dcprint/trace/route.ts(339,61): error TS2322: Type 'number | DbRow' is not assignable to type 'SqlValue'. - Type 'DbRow' is not assignable to type 'SqlValue'. - Type 'DbRow' is missing the following properties from type 'Buffer': slice, subarray, write, toJSON, and 102 more. -src/app/api/dcprint/trace/route.ts(339,71): error TS2322: Type 'number | DbRow' is not assignable to type 'SqlValue'. - Type 'DbRow' is not assignable to type 'SqlValue'. - Type 'DbRow' is missing the following properties from type 'Buffer': slice, subarray, write, toJSON, and 102 more. -src/app/api/dcprint/trace/route.ts(340,43): error TS2345: Argument of type 'DbRow[]' is not assignable to parameter of type 'SqlValue[]'. - Type 'DbRow' is not assignable to type 'SqlValue'. - Type 'DbRow' is missing the following properties from type 'Buffer': slice, subarray, write, toJSON, and 102 more. -src/app/api/delivery/vehicles/route.ts(65,27): error TS2322: Type 'SqlValue[]' is not assignable to type 'DbRow[]'. - Type 'SqlValue' is not assignable to type 'DbRow'. - Type 'undefined' is not assignable to type 'DbRow'. -src/app/api/delivery/vehicles/route.ts(98,63): error TS2345: Argument of type 'DbRow[]' is not assignable to parameter of type 'number | SqlValue[]'. - Type 'DbRow[]' is not assignable to type 'SqlValue[]'. - Type 'DbRow' is not assignable to type 'SqlValue'. - Type 'DbRow' is missing the following properties from type 'Buffer': slice, subarray, write, toJSON, and 102 more. -src/app/api/engineering/sample-to-mass/route.ts(137,20): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/engineering/sample-to-mass/route.ts(137,34): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/engineering/sample-to-mass/route.ts(147,50): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/engineering/sample-to-mass/route.ts(149,20): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/engineering/sample-to-mass/route.ts(149,34): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/engineering/sample-to-mass/route.ts(177,13): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/engineering/sample-to-mass/route.ts(180,22): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/engineering/sample-to-mass/route.ts(216,33): error TS2339: Property 'insertId' does not exist on type 'QueryResult'. - Property 'insertId' does not exist on type 'ResultSetHeader[]'. -src/app/api/engineering/sample-to-mass/route.ts(240,24): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/engineering/sample-to-mass/route.ts(244,24): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/engineering/sample-to-mass/route.ts(302,28): error TS2352: Conversion of type 'UserInfo' to type 'DbRow' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first. - Index signature for type 'string' is missing in type 'UserInfo'. -src/app/api/engineering/sample-to-mass/route.ts(302,61): error TS2352: Conversion of type 'UserInfo' to type 'DbRow' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first. - Index signature for type 'string' is missing in type 'UserInfo'. -src/app/api/finance/aging/route.ts(75,5): error TS2571: Object is of type 'unknown'. -src/app/api/finance/aging/route.ts(76,5): error TS2571: Object is of type 'unknown'. -src/app/api/finance/aging/route.ts(78,26): error TS2571: Object is of type 'unknown'. -src/app/api/finance/aging/route.ts(95,5): error TS2365: Operator '+=' cannot be applied to types 'number' and 'string | number | boolean | Date | Buffer'. -src/app/api/finance/aging/route.ts(95,34): error TS18049: 'summary.total_amount' is possibly 'null' or 'undefined'. -src/app/api/finance/aging/route.ts(96,5): error TS2365: Operator '+=' cannot be applied to types 'number' and 'string | number | boolean | Date | Buffer'. -src/app/api/finance/aging/route.ts(96,38): error TS18049: 'summary.remaining_amount' is possibly 'null' or 'undefined'. -src/app/api/finance/aging/route.ts(97,25): error TS18049: 'summary.buckets' is possibly 'null' or 'undefined'. -src/app/api/finance/aging/route.ts(97,41): error TS2339: Property 'length' does not exist on type 'string | number | boolean | Date | Buffer'. - Property 'length' does not exist on type 'number'. -src/app/api/finance/aging/route.ts(98,41): error TS18049: 'summary.buckets' is possibly 'null' or 'undefined'. -src/app/api/finance/aging/route.ts(98,41): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'string | number | boolean | Date | Buffer'. - No index signature with a parameter of type 'number' was found on type 'string | number | boolean | Date | Buffer'. -src/app/api/finance/aging/route.ts(99,40): error TS18049: 'summary.buckets' is possibly 'null' or 'undefined'. -src/app/api/finance/aging/route.ts(99,40): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'string | number | boolean | Date | Buffer'. - No index signature with a parameter of type 'number' was found on type 'string | number | boolean | Date | Buffer'. -src/app/api/finance/cost-variance/route.ts(186,28): error TS2345: Argument of type 'unknown' is not assignable to parameter of type 'SqlValue'. -src/app/api/finance/invoice/route.ts(141,32): error TS2339: Property 'insertId' does not exist on type 'QueryResult'. - Property 'insertId' does not exist on type 'ResultSetHeader[]'. -src/app/api/finance/stats/route.ts(82,34): error TS2345: Argument of type 'string | number | true | Date | Buffer' is not assignable to parameter of type 'string'. - Type 'number' is not assignable to type 'string'. -src/app/api/finance/stats/route.ts(83,36): error TS2345: Argument of type 'string | number | true | Date | Buffer' is not assignable to parameter of type 'string'. - Type 'number' is not assignable to type 'string'. -src/app/api/finance/stats/route.ts(84,35): error TS2345: Argument of type 'string | number | true | Date | Buffer' is not assignable to parameter of type 'string'. - Type 'number' is not assignable to type 'string'. -src/app/api/finance/stats/route.ts(85,36): error TS2345: Argument of type 'string | number | true | Date | Buffer' is not assignable to parameter of type 'string'. - Type 'number' is not assignable to type 'string'. -src/app/api/finance/stats/route.ts(86,37): error TS2345: Argument of type 'string | number | true | Date | Buffer' is not assignable to parameter of type 'string'. - Type 'number' is not assignable to type 'string'. -src/app/api/finance/stats/route.ts(87,37): error TS2345: Argument of type 'string | number | true | Date | Buffer' is not assignable to parameter of type 'string'. - Type 'number' is not assignable to type 'string'. -src/app/api/finance/stats/route.ts(91,34): error TS2345: Argument of type 'string | number | true | Date | Buffer' is not assignable to parameter of type 'string'. - Type 'number' is not assignable to type 'string'. -src/app/api/finance/stats/route.ts(92,32): error TS2345: Argument of type 'string | number | true | Date | Buffer' is not assignable to parameter of type 'string'. - Type 'number' is not assignable to type 'string'. -src/app/api/finance/stats/route.ts(93,35): error TS2345: Argument of type 'string | number | true | Date | Buffer' is not assignable to parameter of type 'string'. - Type 'number' is not assignable to type 'string'. -src/app/api/finance/stats/route.ts(94,36): error TS2345: Argument of type 'string | number | true | Date | Buffer' is not assignable to parameter of type 'string'. - Type 'number' is not assignable to type 'string'. -src/app/api/finance/stats/route.ts(95,37): error TS2345: Argument of type 'string | number | true | Date | Buffer' is not assignable to parameter of type 'string'. - Type 'number' is not assignable to type 'string'. -src/app/api/finance/stats/route.ts(96,37): error TS2345: Argument of type 'string | number | true | Date | Buffer' is not assignable to parameter of type 'string'. - Type 'number' is not assignable to type 'string'. -src/app/api/init/business-seed/route.ts(215,23): error TS2488: Type 'QueryResult' must have a '[Symbol.iterator]()' method that returns an iterator. -src/app/api/init/business-seed/route.ts(243,23): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/business-seed/route.ts(745,23): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/business-seed/route.ts(970,11): error TS2322: Type 'QueryResult' is not assignable to type '{ id: number; material_name: string; }[]'. - Type 'OkPacket' is missing the following properties from type '{ id: number; material_name: string; }[]': length, pop, push, concat, and 35 more. -src/app/api/init/business-seed/route.ts(979,21): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/core-flow-seed/route.ts(88,11): error TS2322: Type 'QueryResult' is not assignable to type 'DbRow[]'. - Type 'OkPacket' is missing the following properties from type 'DbRow[]': length, pop, push, concat, and 35 more. -src/app/api/init/core-flow-seed/route.ts(92,11): error TS2322: Type 'QueryResult' is not assignable to type 'DbRow[]'. - Type 'OkPacket' is missing the following properties from type 'DbRow[]': length, pop, push, concat, and 35 more. -src/app/api/init/core-flow-seed/route.ts(96,32): error TS2339: Property 'find' does not exist on type 'QueryResult'. - Property 'find' does not exist on type 'OkPacket'. -src/app/api/init/core-flow-seed/route.ts(96,78): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/core-flow-seed/route.ts(97,29): error TS2339: Property 'find' does not exist on type 'QueryResult'. - Property 'find' does not exist on type 'OkPacket'. -src/app/api/init/core-flow-seed/route.ts(97,77): error TS7053: Element implicitly has an 'any' type because expression of type '1' can't be used to index type 'QueryResult'. - Property '1' does not exist on type 'QueryResult'. -src/app/api/init/core-flow-seed/route.ts(98,31): error TS2339: Property 'find' does not exist on type 'QueryResult'. - Property 'find' does not exist on type 'OkPacket'. -src/app/api/init/core-flow-seed/route.ts(98,81): error TS7053: Element implicitly has an 'any' type because expression of type '2' can't be used to index type 'QueryResult'. - Property '2' does not exist on type 'QueryResult'. -src/app/api/init/core-flow-seed/route.ts(99,29): error TS2339: Property 'find' does not exist on type 'QueryResult'. - Property 'find' does not exist on type 'OkPacket'. -src/app/api/init/core-flow-seed/route.ts(99,77): error TS7053: Element implicitly has an 'any' type because expression of type '3' can't be used to index type 'QueryResult'. - Property '3' does not exist on type 'QueryResult'. -src/app/api/init/core-flow-seed/route.ts(155,27): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/core-flow-seed/route.ts(241,22): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/core-flow-seed/route.ts(243,13): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/core-flow-seed/route.ts(255,39): error TS2769: No overload matches this call. - Overload 1 of 2, '(predicate: (value: unknown, index: number, array: unknown[]) => value is unknown, thisArg?: any): unknown[]', gave the following error. - Argument of type '(m: DbRow) => boolean' is not assignable to parameter of type '(value: unknown, index: number, array: unknown[]) => value is unknown'. - Types of parameters 'm' and 'value' are incompatible. - Type 'unknown' is not assignable to type 'DbRow'. - Overload 2 of 2, '(predicate: (value: unknown, index: number, array: unknown[]) => unknown, thisArg?: any): unknown[]', gave the following error. - Argument of type '(m: DbRow) => boolean' is not assignable to parameter of type '(value: unknown, index: number, array: unknown[]) => unknown'. - Types of parameters 'm' and 'value' are incompatible. - Type 'unknown' is not assignable to type 'DbRow'. -src/app/api/init/core-flow-seed/route.ts(256,38): error TS2769: No overload matches this call. - Overload 1 of 2, '(predicate: (value: unknown, index: number, array: unknown[]) => value is unknown, thisArg?: any): unknown[]', gave the following error. - Argument of type '(m: DbRow) => boolean' is not assignable to parameter of type '(value: unknown, index: number, array: unknown[]) => value is unknown'. - Types of parameters 'm' and 'value' are incompatible. - Type 'unknown' is not assignable to type 'DbRow'. - Overload 2 of 2, '(predicate: (value: unknown, index: number, array: unknown[]) => unknown, thisArg?: any): unknown[]', gave the following error. - Argument of type '(m: DbRow) => boolean' is not assignable to parameter of type '(value: unknown, index: number, array: unknown[]) => unknown'. - Types of parameters 'm' and 'value' are incompatible. - Type 'unknown' is not assignable to type 'DbRow'. -src/app/api/init/core-flow-seed/route.ts(257,39): error TS2769: No overload matches this call. - Overload 1 of 2, '(predicate: (value: unknown, index: number, array: unknown[]) => value is unknown, thisArg?: any): unknown[]', gave the following error. - Argument of type '(m: DbRow) => boolean' is not assignable to parameter of type '(value: unknown, index: number, array: unknown[]) => value is unknown'. - Types of parameters 'm' and 'value' are incompatible. - Type 'unknown' is not assignable to type 'DbRow'. - Overload 2 of 2, '(predicate: (value: unknown, index: number, array: unknown[]) => unknown, thisArg?: any): unknown[]', gave the following error. - Argument of type '(m: DbRow) => boolean' is not assignable to parameter of type '(value: unknown, index: number, array: unknown[]) => unknown'. - Types of parameters 'm' and 'value' are incompatible. - Type 'unknown' is not assignable to type 'DbRow'. -src/app/api/init/core-flow-seed/route.ts(258,42): error TS2769: No overload matches this call. - Overload 1 of 2, '(predicate: (value: unknown, index: number, array: unknown[]) => value is unknown, thisArg?: any): unknown[]', gave the following error. - Argument of type '(m: DbRow) => boolean' is not assignable to parameter of type '(value: unknown, index: number, array: unknown[]) => value is unknown'. - Types of parameters 'm' and 'value' are incompatible. - Type 'unknown' is not assignable to type 'DbRow'. - Overload 2 of 2, '(predicate: (value: unknown, index: number, array: unknown[]) => unknown, thisArg?: any): unknown[]', gave the following error. - Argument of type '(m: DbRow) => boolean' is not assignable to parameter of type '(value: unknown, index: number, array: unknown[]) => unknown'. - Types of parameters 'm' and 'value' are incompatible. - Type 'unknown' is not assignable to type 'DbRow'. -src/app/api/init/core-flow-seed/route.ts(301,19): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/core-flow-seed/route.ts(333,19): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/core-flow-seed/route.ts(385,34): error TS2339: Property 'sale_price' does not exist on type '{}'. -src/app/api/init/core-flow-seed/route.ts(414,23): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/core-flow-seed/route.ts(421,20): error TS2339: Property 'id' does not exist on type '{}'. -src/app/api/init/core-flow-seed/route.ts(423,20): error TS2339: Property 'unit' does not exist on type '{}'. -src/app/api/init/core-flow-seed/route.ts(435,9): error TS2353: Object literal may only specify known properties, and 'id' does not exist in type 'Date | Buffer | readonly SqlValue[]'. -src/app/api/init/core-flow-seed/route.ts(453,43): error TS18049: 'od' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(453,46): error TS2339: Property 'orderDate' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'orderDate' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(474,11): error TS18049: 'od' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(474,14): error TS2339: Property 'id' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'id' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(475,11): error TS18049: 'od' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(475,14): error TS2339: Property 'orderNo' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'orderNo' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(476,11): error TS18049: 'od' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(476,14): error TS2339: Property 'customerName' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'customerName' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(477,11): error TS18049: 'od' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(477,14): error TS2339: Property 'productName' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'productName' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(478,11): error TS18049: 'od' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(478,14): error TS2339: Property 'qty' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'qty' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(490,18): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/core-flow-seed/route.ts(492,9): error TS2353: Object literal may only specify known properties, and 'id' does not exist in type 'Date | Buffer | readonly SqlValue[]'. -src/app/api/init/core-flow-seed/route.ts(492,13): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/core-flow-seed/route.ts(494,18): error TS18049: 'od' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(494,21): error TS2339: Property 'id' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'id' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(495,18): error TS18049: 'od' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(495,21): error TS2339: Property 'orderNo' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'orderNo' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(496,23): error TS18049: 'od' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(496,26): error TS2339: Property 'customerName' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'customerName' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(497,22): error TS18049: 'od' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(497,25): error TS2339: Property 'productName' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'productName' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(498,14): error TS18049: 'od' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(498,17): error TS2339: Property 'qty' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'qty' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(514,45): error TS18049: 'wo' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(514,48): error TS2339: Property 'planStart' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'planStart' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(518,59): error TS2339: Property 'purchase_price' does not exist on type '{}'. -src/app/api/init/core-flow-seed/route.ts(538,21): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/core-flow-seed/route.ts(546,20): error TS2339: Property 'id' does not exist on type '{}'. -src/app/api/init/core-flow-seed/route.ts(547,20): error TS2339: Property 'material_name' does not exist on type '{}'. -src/app/api/init/core-flow-seed/route.ts(548,20): error TS2339: Property 'specification' does not exist on type '{}'. -src/app/api/init/core-flow-seed/route.ts(551,20): error TS2339: Property 'unit' does not exist on type '{}'. -src/app/api/init/core-flow-seed/route.ts(552,20): error TS2339: Property 'purchase_price' does not exist on type '{}'. -src/app/api/init/core-flow-seed/route.ts(567,20): error TS2339: Property 'material_code' does not exist on type '{}'. -src/app/api/init/core-flow-seed/route.ts(568,20): error TS2339: Property 'material_name' does not exist on type '{}'. -src/app/api/init/core-flow-seed/route.ts(569,20): error TS2339: Property 'specification' does not exist on type '{}'. -src/app/api/init/core-flow-seed/route.ts(570,20): error TS2339: Property 'unit' does not exist on type '{}'. -src/app/api/init/core-flow-seed/route.ts(585,21): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/core-flow-seed/route.ts(587,9): error TS2353: Object literal may only specify known properties, and 'id' does not exist in type 'Date | Buffer | readonly SqlValue[]'. -src/app/api/init/core-flow-seed/route.ts(587,13): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/core-flow-seed/route.ts(589,32): error TS2339: Property 'material_code' does not exist on type '{}'. -src/app/api/init/core-flow-seed/route.ts(590,32): error TS2339: Property 'material_name' does not exist on type '{}'. -src/app/api/init/core-flow-seed/route.ts(609,21): error TS2339: Property 'material_code' does not exist on type '{}'. -src/app/api/init/core-flow-seed/route.ts(610,21): error TS2339: Property 'material_name' does not exist on type '{}'. -src/app/api/init/core-flow-seed/route.ts(611,21): error TS2339: Property 'specification' does not exist on type '{}'. -src/app/api/init/core-flow-seed/route.ts(612,21): error TS2339: Property 'unit' does not exist on type '{}'. -src/app/api/init/core-flow-seed/route.ts(624,23): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/core-flow-seed/route.ts(626,11): error TS2353: Object literal may only specify known properties, and 'id' does not exist in type 'Date | Buffer | readonly SqlValue[]'. -src/app/api/init/core-flow-seed/route.ts(626,15): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/core-flow-seed/route.ts(628,33): error TS2339: Property 'material_code' does not exist on type '{}'. -src/app/api/init/core-flow-seed/route.ts(629,33): error TS2339: Property 'material_name' does not exist on type '{}'. -src/app/api/init/core-flow-seed/route.ts(647,12): error TS18049: 'srcLabel' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(647,21): error TS2339: Property 'width' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'width' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(649,21): error TS18049: 'srcLabel' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(649,30): error TS2339: Property 'width' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'width' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(664,11): error TS18049: 'srcLabel' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(664,20): error TS2339: Property 'id' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'id' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(665,11): error TS18049: 'srcLabel' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(665,20): error TS2339: Property 'labelNo' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'labelNo' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(667,11): error TS18049: 'srcLabel' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(667,20): error TS2339: Property 'width' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'width' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(668,11): error TS18049: 'srcLabel' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(668,20): error TS2339: Property 'width' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'width' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(677,21): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/core-flow-seed/route.ts(680,31): error TS18049: 'srcLabel' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(680,40): error TS2339: Property 'labelNo' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'labelNo' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(685,13): error TS18049: 'srcLabel' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(685,22): error TS2339: Property 'labelNo' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'labelNo' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(686,13): error TS18049: 'srcLabel' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(686,22): error TS2339: Property 'supplierName' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'supplierName' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(687,13): error TS18049: 'srcLabel' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(687,22): error TS2339: Property 'receiveDate' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'receiveDate' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(688,13): error TS18049: 'srcLabel' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(688,22): error TS2339: Property 'materialCode' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'materialCode' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(689,13): error TS18049: 'srcLabel' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(689,22): error TS2339: Property 'materialName' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'materialName' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(692,13): error TS18049: 'srcLabel' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(692,22): error TS2339: Property 'batchNo' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'batchNo' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(693,25): error TS18049: 'srcLabel' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(693,34): error TS2339: Property 'qty' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'qty' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(693,56): error TS18049: 'srcLabel' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(693,65): error TS2339: Property 'width' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'width' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(695,13): error TS18049: 'srcLabel' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(695,22): error TS2339: Property 'warehouseId' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'warehouseId' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(699,13): error TS18049: 'srcLabel' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(699,22): error TS2339: Property 'id' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'id' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(706,30): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/core-flow-seed/route.ts(708,11): error TS2353: Object literal may only specify known properties, and 'id' does not exist in type 'Date | Buffer | readonly SqlValue[]'. -src/app/api/init/core-flow-seed/route.ts(708,15): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/core-flow-seed/route.ts(710,25): error TS18049: 'srcLabel' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(710,34): error TS2339: Property 'materialCode' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'materialCode' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(711,25): error TS18049: 'srcLabel' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(711,34): error TS2339: Property 'materialName' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'materialName' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(712,20): error TS18049: 'srcLabel' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(712,29): error TS2339: Property 'batchNo' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'batchNo' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(713,28): error TS18049: 'srcLabel' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(713,37): error TS2339: Property 'qty' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'qty' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(713,59): error TS18049: 'srcLabel' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(713,68): error TS2339: Property 'width' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'width' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(715,24): error TS18049: 'srcLabel' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(715,33): error TS2339: Property 'warehouseId' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'warehouseId' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(716,25): error TS18049: 'srcLabel' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(716,34): error TS2339: Property 'supplierName' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'supplierName' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(717,24): error TS18049: 'srcLabel' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(717,33): error TS2339: Property 'receiveDate' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'receiveDate' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(722,19): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/core-flow-seed/route.ts(735,67): error TS18049: 'wo' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(735,70): error TS2339: Property 'status' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'status' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(736,26): error TS18049: 'wo' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(736,29): error TS2339: Property 'status' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'status' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(742,11): error TS18049: 'wo' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(742,14): error TS2339: Property 'id' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'id' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(743,11): error TS18049: 'wo' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(743,14): error TS2339: Property 'woNo' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'woNo' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(744,11): error TS18049: 'wo' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(744,14): error TS2339: Property 'productName' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'productName' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(745,11): error TS18049: 'wo' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(745,14): error TS2339: Property 'productName' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'productName' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(746,48): error TS2339: Property 'specification' does not exist on type '{}'. -src/app/api/init/core-flow-seed/route.ts(747,11): error TS18049: 'wo' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(747,14): error TS2339: Property 'planStart' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'planStart' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(748,11): error TS18049: 'wo' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(748,14): error TS2339: Property 'qty' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'qty' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(749,11): error TS18049: 'mainLabel' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(749,21): error TS2339: Property 'id' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'id' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(750,11): error TS18049: 'mainLabel' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(750,21): error TS2339: Property 'labelNo' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'labelNo' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(758,22): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/core-flow-seed/route.ts(761,9): error TS2353: Object literal may only specify known properties, and 'id' does not exist in type 'Date | Buffer | readonly SqlValue[]'. -src/app/api/init/core-flow-seed/route.ts(763,15): error TS18049: 'wo' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(763,18): error TS2339: Property 'id' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'id' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(764,15): error TS18049: 'wo' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(764,18): error TS2339: Property 'woNo' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'woNo' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(765,22): error TS18049: 'wo' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(765,25): error TS2339: Property 'productName' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'productName' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(766,18): error TS18049: 'wo' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(766,21): error TS2339: Property 'qty' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'qty' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(767,22): error TS18049: 'mainLabel' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(767,32): error TS2339: Property 'id' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'id' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(768,22): error TS18049: 'mainLabel' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(768,32): error TS2339: Property 'labelNo' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'labelNo' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(778,11): error TS18049: 'mainLabel' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(778,21): error TS2339: Property 'id' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'id' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(779,11): error TS18049: 'mainLabel' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(779,21): error TS2339: Property 'labelNo' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'labelNo' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(781,11): error TS18049: 'mainLabel' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(781,21): error TS2339: Property 'materialCode' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'materialCode' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(782,11): error TS18049: 'mainLabel' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(782,21): error TS2339: Property 'materialName' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'materialName' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(783,48): error TS2339: Property 'specification' does not exist on type '{}'. -src/app/api/init/core-flow-seed/route.ts(784,11): error TS18049: 'mainLabel' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(784,21): error TS2339: Property 'batchNo' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'batchNo' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(785,11): error TS18049: 'mainLabel' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(785,21): error TS2339: Property 'qty' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'qty' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(792,11): error TS2769: No overload matches this call. - Overload 1 of 2, '(predicate: (value: SqlValue, index: number, obj: SqlValue[]) => value is SqlValue, thisArg?: any): SqlValue', gave the following error. - Argument of type '(l: DbRow) => any' is not assignable to parameter of type '(value: SqlValue, index: number, obj: SqlValue[]) => value is SqlValue'. - Types of parameters 'l' and 'value' are incompatible. - Type 'SqlValue' is not assignable to type 'DbRow'. - Type 'undefined' is not assignable to type 'DbRow'. - Overload 2 of 2, '(predicate: (value: SqlValue, index: number, obj: SqlValue[]) => unknown, thisArg?: any): SqlValue', gave the following error. - Argument of type '(l: DbRow) => any' is not assignable to parameter of type '(value: SqlValue, index: number, obj: SqlValue[]) => unknown'. - Types of parameters 'l' and 'value' are incompatible. - Type 'SqlValue' is not assignable to type 'DbRow'. - Type 'undefined' is not assignable to type 'DbRow'. -src/app/api/init/core-flow-seed/route.ts(792,41): error TS2339: Property 'startsWith' does not exist on type 'string | number | boolean | Date | Buffer'. - Property 'startsWith' does not exist on type 'number'. -src/app/api/init/core-flow-seed/route.ts(792,66): error TS18049: 'l.id' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(792,73): error TS18049: 'mainLabel' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(792,83): error TS2339: Property 'id' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'id' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(800,24): error TS2339: Property 'id' does not exist on type 'string | number | true | Date | Buffer | readonly SqlValue[]'. - Property 'id' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(801,24): error TS2339: Property 'labelNo' does not exist on type 'string | number | true | Date | Buffer | readonly SqlValue[]'. - Property 'labelNo' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(803,24): error TS2339: Property 'materialCode' does not exist on type 'string | number | true | Date | Buffer | readonly SqlValue[]'. - Property 'materialCode' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(804,24): error TS2339: Property 'materialName' does not exist on type 'string | number | true | Date | Buffer | readonly SqlValue[]'. - Property 'materialName' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(805,50): error TS2339: Property 'specification' does not exist on type '{}'. -src/app/api/init/core-flow-seed/route.ts(806,24): error TS2339: Property 'batchNo' does not exist on type 'string | number | true | Date | Buffer | readonly SqlValue[]'. - Property 'batchNo' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(807,24): error TS2339: Property 'qty' does not exist on type 'string | number | true | Date | Buffer | readonly SqlValue[]'. - Property 'qty' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(824,11): error TS18049: 'label' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(824,17): error TS2339: Property 'labelNo' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'labelNo' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(825,11): error TS18049: 'label' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(825,17): error TS2339: Property 'labelNo' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'labelNo' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(826,11): error TS18049: 'card' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(826,16): error TS2339: Property 'cardNo' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'cardNo' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(843,35): error TS18049: 'mainLabel' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(843,45): error TS2339: Property 'qty' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'qty' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(849,11): error TS18049: 'wo' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(849,14): error TS2339: Property 'id' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'id' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(850,11): error TS18049: 'wo' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(850,14): error TS2339: Property 'woNo' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'woNo' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(861,20): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/core-flow-seed/route.ts(867,48): error TS2339: Property 'id' does not exist on type '{}'. -src/app/api/init/core-flow-seed/route.ts(868,11): error TS18049: 'mainLabel' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(868,21): error TS2339: Property 'materialCode' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'materialCode' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(869,11): error TS18049: 'mainLabel' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(869,21): error TS2339: Property 'materialName' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'materialName' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(870,11): error TS18049: 'mainLabel' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(870,21): error TS2339: Property 'qty' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'qty' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(873,11): error TS18049: 'mainLabel' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(873,21): error TS2339: Property 'batchNo' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'batchNo' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(878,41): error TS2769: No overload matches this call. - Overload 1 of 2, '(predicate: (value: SqlValue, index: number, obj: SqlValue[]) => value is SqlValue, thisArg?: any): SqlValue', gave the following error. - Argument of type '(l: DbRow) => any' is not assignable to parameter of type '(value: SqlValue, index: number, obj: SqlValue[]) => value is SqlValue'. - Types of parameters 'l' and 'value' are incompatible. - Type 'SqlValue' is not assignable to type 'DbRow'. - Type 'undefined' is not assignable to type 'DbRow'. - Overload 2 of 2, '(predicate: (value: SqlValue, index: number, obj: SqlValue[]) => unknown, thisArg?: any): SqlValue', gave the following error. - Argument of type '(l: DbRow) => any' is not assignable to parameter of type '(value: SqlValue, index: number, obj: SqlValue[]) => unknown'. - Types of parameters 'l' and 'value' are incompatible. - Type 'SqlValue' is not assignable to type 'DbRow'. - Type 'undefined' is not assignable to type 'DbRow'. -src/app/api/init/core-flow-seed/route.ts(878,71): error TS2339: Property 'startsWith' does not exist on type 'string | number | boolean | Date | Buffer'. - Property 'startsWith' does not exist on type 'number'. -src/app/api/init/core-flow-seed/route.ts(884,50): error TS2339: Property 'id' does not exist on type '{}'. -src/app/api/init/core-flow-seed/route.ts(885,24): error TS2339: Property 'materialCode' does not exist on type 'string | number | true | Date | Buffer | readonly SqlValue[]'. - Property 'materialCode' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(886,24): error TS2339: Property 'materialName' does not exist on type 'string | number | true | Date | Buffer | readonly SqlValue[]'. - Property 'materialName' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(887,24): error TS2339: Property 'qty' does not exist on type 'string | number | true | Date | Buffer | readonly SqlValue[]'. - Property 'qty' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(888,35): error TS2339: Property 'qty' does not exist on type 'string | number | true | Date | Buffer | readonly SqlValue[]'. - Property 'qty' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(890,24): error TS2339: Property 'batchNo' does not exist on type 'string | number | true | Date | Buffer | readonly SqlValue[]'. - Property 'batchNo' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(903,34): error TS18049: 'wo' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(903,37): error TS2339: Property 'qty' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'qty' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(913,11): error TS18049: 'wo' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(913,14): error TS2339: Property 'id' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'id' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(914,11): error TS18049: 'wo' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(914,14): error TS2339: Property 'woNo' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'woNo' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(934,22): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/core-flow-seed/route.ts(943,34): error TS18049: 'wo' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(943,37): error TS2339: Property 'qty' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'qty' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(950,11): error TS18049: 'wo' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(950,14): error TS2339: Property 'id' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'id' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(951,11): error TS18049: 'wo' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(951,14): error TS2339: Property 'woNo' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'woNo' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(962,20): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/core-flow-seed/route.ts(969,20): error TS2339: Property 'id' does not exist on type '{}'. -src/app/api/init/core-flow-seed/route.ts(970,20): error TS2339: Property 'material_code' does not exist on type '{}'. -src/app/api/init/core-flow-seed/route.ts(971,20): error TS2339: Property 'material_name' does not exist on type '{}'. -src/app/api/init/core-flow-seed/route.ts(984,11): error TS18049: 'wo' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(984,14): error TS2339: Property 'id' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'id' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(985,11): error TS18049: 'wo' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(985,14): error TS2339: Property 'woNo' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'woNo' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(986,20): error TS2339: Property 'id' does not exist on type '{}'. -src/app/api/init/core-flow-seed/route.ts(987,20): error TS2339: Property 'material_code' does not exist on type '{}'. -src/app/api/init/core-flow-seed/route.ts(988,20): error TS2339: Property 'material_name' does not exist on type '{}'. -src/app/api/init/core-flow-seed/route.ts(1013,11): error TS18049: 'card' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(1013,16): error TS2339: Property 'id' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'id' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(1014,11): error TS18049: 'card' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(1014,16): error TS2339: Property 'cardNo' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'cardNo' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(1015,11): error TS18049: 'wo' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(1015,14): error TS2339: Property 'woNo' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'woNo' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(1016,11): error TS18049: 'wo' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(1016,14): error TS2339: Property 'productName' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'productName' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(1017,11): error TS18049: 'mainLabel' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(1017,21): error TS2339: Property 'id' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'id' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(1025,20): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/core-flow-seed/route.ts(1031,11): error TS18049: 'mainLabel' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(1031,21): error TS2339: Property 'id' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'id' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(1032,11): error TS18049: 'mainLabel' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(1032,21): error TS2339: Property 'labelNo' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'labelNo' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(1033,11): error TS18049: 'mainLabel' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(1033,21): error TS2339: Property 'materialCode' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'materialCode' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(1034,11): error TS18049: 'mainLabel' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(1034,21): error TS2339: Property 'materialName' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'materialName' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(1035,48): error TS2339: Property 'specification' does not exist on type '{}'. -src/app/api/init/core-flow-seed/route.ts(1036,11): error TS18049: 'mainLabel' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(1036,21): error TS2339: Property 'batchNo' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'batchNo' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(1037,11): error TS18049: 'mainLabel' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(1037,21): error TS2339: Property 'supplierName' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'supplierName' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(1038,11): error TS18049: 'mainLabel' is possibly 'null' or 'undefined'. -src/app/api/init/core-flow-seed/route.ts(1038,21): error TS2339: Property 'receiveDate' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'receiveDate' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(1043,39): error TS2769: No overload matches this call. - Overload 1 of 2, '(predicate: (value: SqlValue, index: number, obj: SqlValue[]) => value is SqlValue, thisArg?: any): SqlValue', gave the following error. - Argument of type '(l: DbRow) => any' is not assignable to parameter of type '(value: SqlValue, index: number, obj: SqlValue[]) => value is SqlValue'. - Types of parameters 'l' and 'value' are incompatible. - Type 'SqlValue' is not assignable to type 'DbRow'. - Type 'undefined' is not assignable to type 'DbRow'. - Overload 2 of 2, '(predicate: (value: SqlValue, index: number, obj: SqlValue[]) => unknown, thisArg?: any): SqlValue', gave the following error. - Argument of type '(l: DbRow) => any' is not assignable to parameter of type '(value: SqlValue, index: number, obj: SqlValue[]) => unknown'. - Types of parameters 'l' and 'value' are incompatible. - Type 'SqlValue' is not assignable to type 'DbRow'. - Type 'undefined' is not assignable to type 'DbRow'. -src/app/api/init/core-flow-seed/route.ts(1043,69): error TS2339: Property 'startsWith' does not exist on type 'string | number | boolean | Date | Buffer'. - Property 'startsWith' does not exist on type 'number'. -src/app/api/init/core-flow-seed/route.ts(1049,22): error TS2339: Property 'id' does not exist on type 'string | number | true | Date | Buffer | readonly SqlValue[]'. - Property 'id' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(1050,22): error TS2339: Property 'labelNo' does not exist on type 'string | number | true | Date | Buffer | readonly SqlValue[]'. - Property 'labelNo' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(1051,22): error TS2339: Property 'materialCode' does not exist on type 'string | number | true | Date | Buffer | readonly SqlValue[]'. - Property 'materialCode' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(1052,22): error TS2339: Property 'materialName' does not exist on type 'string | number | true | Date | Buffer | readonly SqlValue[]'. - Property 'materialName' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(1053,48): error TS2339: Property 'specification' does not exist on type '{}'. -src/app/api/init/core-flow-seed/route.ts(1054,22): error TS2339: Property 'batchNo' does not exist on type 'string | number | true | Date | Buffer | readonly SqlValue[]'. - Property 'batchNo' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(1055,22): error TS2339: Property 'supplierName' does not exist on type 'string | number | true | Date | Buffer | readonly SqlValue[]'. - Property 'supplierName' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(1056,22): error TS2339: Property 'receiveDate' does not exist on type 'string | number | true | Date | Buffer | readonly SqlValue[]'. - Property 'receiveDate' does not exist on type 'string'. -src/app/api/init/core-flow-seed/route.ts(1073,11): error TS18046: 'mat' is of type 'unknown'. -src/app/api/init/core-flow-seed/route.ts(1074,11): error TS18046: 'mat' is of type 'unknown'. -src/app/api/init/core-flow-seed/route.ts(1080,11): error TS18046: 'mat' is of type 'unknown'. -src/app/api/init/core-flow-seed/route.ts(1081,11): error TS18046: 'mat' is of type 'unknown'. -src/app/api/init/core-flow-seed/route.ts(1082,28): error TS18046: 'mat' is of type 'unknown'. -src/app/api/init/department/route.ts(94,48): error TS2345: Argument of type '(d: DbRow) => [string | number | boolean | Date | Buffer | null | undefined, string | number | boolean | Date | Buffer | null | undefined]' is not assignable to parameter of type '(value: unknown, index: number, array: unknown[]) => [string | number | boolean | Date | Buffer | null | undefined, string | number | boolean | Date | Buffer<...> | null | undefined]'. - Types of parameters 'd' and 'value' are incompatible. - Type 'unknown' is not assignable to type 'DbRow'. -src/app/api/init/department/route.ts(113,7): error TS2322: Type '{ children: DbRow[]; id: number; dept_code: string; dept_name: string; parent_id: number; sort_order: number; status: number; create_time?: string | undefined; update_time?: string | undefined; }[]' is not assignable to type 'DbRow[]'. - Type '{ children: DbRow[]; id: number; dept_code: string; dept_name: string; parent_id: number; sort_order: number; status: number; create_time?: string; update_time?: string; }' is not assignable to type 'DbRow'. - Property 'children' is incompatible with index signature. - Type 'DbRow[]' is not assignable to type 'string | number | boolean | Date | Buffer | null | undefined'. - Type 'DbRow[]' is missing the following properties from type 'Buffer': subarray, write, toJSON, equals, and 73 more. -src/app/api/init/department/route.ts(146,7): error TS2322: Type '{ children: DbRow[]; id: number; dept_code: string; dept_name: string; parent_id: number; sort_order: number; status: number; create_time?: string | undefined; update_time?: string | undefined; }[]' is not assignable to type 'DbRow[]'. - Type '{ children: DbRow[]; id: number; dept_code: string; dept_name: string; parent_id: number; sort_order: number; status: number; create_time?: string; update_time?: string; }' is not assignable to type 'DbRow'. - Property 'children' is incompatible with index signature. - Type 'DbRow[]' is not assignable to type 'string | number | boolean | Date | Buffer | null | undefined'. - Type 'DbRow[]' is missing the following properties from type 'Buffer': subarray, write, toJSON, equals, and 73 more. -src/app/api/init/full-business-seed/route.ts(95,11): error TS2322: Type 'QueryResult' is not assignable to type 'DbRow[]'. - Type 'OkPacket' is missing the following properties from type 'DbRow[]': length, pop, push, concat, and 35 more. -src/app/api/init/full-business-seed/route.ts(100,11): error TS2322: Type 'QueryResult' is not assignable to type 'DbRow[]'. - Type 'OkPacket' is missing the following properties from type 'DbRow[]': length, pop, push, concat, and 35 more. -src/app/api/init/full-business-seed/route.ts(105,11): error TS2322: Type 'QueryResult' is not assignable to type 'DbRow[]'. - Type 'OkPacket' is missing the following properties from type 'DbRow[]': length, pop, push, concat, and 35 more. -src/app/api/init/full-business-seed/route.ts(110,11): error TS2322: Type 'QueryResult' is not assignable to type 'DbRow[]'. - Type 'OkPacket' is missing the following properties from type 'DbRow[]': length, pop, push, concat, and 35 more. -src/app/api/init/full-business-seed/route.ts(115,11): error TS2322: Type 'QueryResult' is not assignable to type 'DbRow[]'. - Type 'OkPacket' is missing the following properties from type 'DbRow[]': length, pop, push, concat, and 35 more. -src/app/api/init/full-business-seed/route.ts(120,11): error TS2322: Type 'QueryResult' is not assignable to type 'DbRow[]'. - Type 'OkPacket' is missing the following properties from type 'DbRow[]': length, pop, push, concat, and 35 more. -src/app/api/init/full-business-seed/route.ts(125,9): error TS2322: Type 'QueryResult' is not assignable to type 'DbRow[]'. - Type 'OkPacket' is missing the following properties from type 'DbRow[]': length, pop, push, concat, and 35 more. -src/app/api/init/full-business-seed/route.ts(167,7): error TS2322: Type 'QueryResult' is not assignable to type 'DbRow[]'. - Type 'OkPacket' is missing the following properties from type 'DbRow[]': length, pop, push, concat, and 35 more. -src/app/api/init/full-business-seed/route.ts(173,11): error TS2322: Type 'QueryResult' is not assignable to type 'DbRow[]'. - Type 'OkPacket' is missing the following properties from type 'DbRow[]': length, pop, push, concat, and 35 more. -src/app/api/init/full-business-seed/route.ts(279,9): error TS2769: No overload matches this call. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/init/full-business-seed/route.ts(300,23): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/full-business-seed/route.ts(325,9): error TS2769: No overload matches this call. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/init/full-business-seed/route.ts(369,9): error TS2769: No overload matches this call. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/init/full-business-seed/route.ts(398,9): error TS2769: No overload matches this call. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/init/full-business-seed/route.ts(428,9): error TS2769: No overload matches this call. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/init/full-business-seed/route.ts(446,24): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/full-business-seed/route.ts(464,9): error TS2769: No overload matches this call. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/init/full-business-seed/route.ts(573,23): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/full-business-seed/route.ts(579,26): error TS2339: Property 'includes' does not exist on type 'string | number | boolean | Date | Buffer'. - Property 'includes' does not exist on type 'number'. -src/app/api/init/full-business-seed/route.ts(580,26): error TS2339: Property 'includes' does not exist on type 'string | number | boolean | Date | Buffer'. - Property 'includes' does not exist on type 'number'. -src/app/api/init/full-business-seed/route.ts(581,26): error TS2339: Property 'includes' does not exist on type 'string | number | boolean | Date | Buffer'. - Property 'includes' does not exist on type 'number'. -src/app/api/init/full-business-seed/route.ts(582,26): error TS2339: Property 'includes' does not exist on type 'string | number | boolean | Date | Buffer'. - Property 'includes' does not exist on type 'number'. -src/app/api/init/full-business-seed/route.ts(597,9): error TS2769: No overload matches this call. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/init/full-business-seed/route.ts(630,9): error TS2769: No overload matches this call. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/init/full-business-seed/route.ts(662,9): error TS2769: No overload matches this call. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/init/full-business-seed/route.ts(697,9): error TS2769: No overload matches this call. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/init/full-business-seed/route.ts(726,18): error TS2769: No overload matches this call. - Overload 1 of 2, '(sql: string, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to type 'ExecuteValues'. - Type 'undefined' is not assignable to type 'ExecuteValues'. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/init/full-business-seed/route.ts(746,19): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/full-business-seed/route.ts(755,9): error TS2769: No overload matches this call. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/init/full-business-seed/route.ts(769,27): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/full-business-seed/route.ts(793,48): error TS2363: The right-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. -src/app/api/init/full-business-seed/route.ts(795,9): error TS2769: No overload matches this call. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/init/full-business-seed/route.ts(825,36): error TS2769: No overload matches this call. - Overload 1 of 2, '(sql: string, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to type 'ExecuteValues'. - Type 'undefined' is not assignable to type 'ExecuteValues'. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/init/full-business-seed/route.ts(829,24): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/full-business-seed/route.ts(861,24): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/full-business-seed/route.ts(870,44): error TS2363: The right-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. -src/app/api/init/full-business-seed/route.ts(872,9): error TS2769: No overload matches this call. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/init/full-business-seed/route.ts(904,36): error TS2769: No overload matches this call. - Overload 1 of 2, '(sql: string, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to type 'ExecuteValues'. - Type 'undefined' is not assignable to type 'ExecuteValues'. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/init/full-business-seed/route.ts(908,24): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/full-business-seed/route.ts(931,22): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/full-business-seed/route.ts(940,44): error TS2363: The right-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. -src/app/api/init/full-business-seed/route.ts(942,9): error TS2769: No overload matches this call. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/init/full-business-seed/route.ts(973,9): error TS2769: No overload matches this call. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/init/full-business-seed/route.ts(991,30): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/full-business-seed/route.ts(1002,18): error TS2769: No overload matches this call. - Overload 1 of 2, '(sql: string, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to type 'ExecuteValues'. - Type 'undefined' is not assignable to type 'ExecuteValues'. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/init/full-business-seed/route.ts(1015,9): error TS2769: No overload matches this call. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/init/full-business-seed/route.ts(1030,30): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/full-business-seed/route.ts(1038,9): error TS2769: No overload matches this call. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/init/full-business-seed/route.ts(1059,9): error TS2769: No overload matches this call. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/init/full-business-seed/route.ts(1096,9): error TS2769: No overload matches this call. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/init/full-business-seed/route.ts(1131,9): error TS2769: No overload matches this call. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/init/full-business-seed/route.ts(1162,9): error TS2769: No overload matches this call. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/init/full-business-seed/route.ts(1186,9): error TS2769: No overload matches this call. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/init/full-business-seed/route.ts(1201,24): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/full-business-seed/route.ts(1209,9): error TS2769: No overload matches this call. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/init/full-business-seed/route.ts(1228,9): error TS2769: No overload matches this call. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/init/full-business-seed/route.ts(1242,27): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/full-business-seed/route.ts(1253,9): error TS2769: No overload matches this call. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/init/full-business-seed/route.ts(1274,9): error TS2769: No overload matches this call. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/init/full-business-seed/route.ts(1288,22): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/full-business-seed/route.ts(1299,9): error TS2769: No overload matches this call. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/init/full-business-seed/route.ts(1318,36): error TS2769: No overload matches this call. - Overload 1 of 2, '(sql: string, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to type 'ExecuteValues'. - Type 'undefined' is not assignable to type 'ExecuteValues'. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/init/full-business-seed/route.ts(1322,24): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/full-business-seed/route.ts(1341,24): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/full-business-seed/route.ts(1349,9): error TS2769: No overload matches this call. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/init/full-business-seed/route.ts(1377,9): error TS2769: No overload matches this call. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/init/full-business-seed/route.ts(1399,18): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/full-business-seed/route.ts(1409,44): error TS2363: The right-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. -src/app/api/init/full-business-seed/route.ts(1415,9): error TS2769: No overload matches this call. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/init/full-business-seed/route.ts(1448,9): error TS2769: No overload matches this call. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/init/full-business-seed/route.ts(1465,23): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/full-business-seed/route.ts(1475,44): error TS2363: The right-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. -src/app/api/init/full-business-seed/route.ts(1478,9): error TS2769: No overload matches this call. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/init/full-business-seed/route.ts(1505,9): error TS2769: No overload matches this call. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/init/full-business-seed/route.ts(1524,26): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/full-business-seed/route.ts(1546,9): error TS2769: No overload matches this call. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/init/full-business-seed/route.ts(1573,9): error TS2769: No overload matches this call. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/init/full-business-seed/route.ts(1610,9): error TS2769: No overload matches this call. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/init/full-business-seed/route.ts(1651,9): error TS2769: No overload matches this call. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/init/full-business-seed/route.ts(1691,9): error TS2769: No overload matches this call. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/init/full-business-seed/route.ts(1723,9): error TS2769: No overload matches this call. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/init/full-business-seed/route.ts(1763,9): error TS2769: No overload matches this call. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/init/full-business-seed/route.ts(1788,9): error TS2769: No overload matches this call. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/init/full-business-seed/route.ts(1839,18): error TS2769: No overload matches this call. - Overload 1 of 2, '(sql: string, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to type 'ExecuteValues'. - Type 'undefined' is not assignable to type 'ExecuteValues'. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/init/full-business-seed/route.ts(1855,24): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/full-business-seed/route.ts(1864,9): error TS2769: No overload matches this call. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/init/menus/route.ts(34,34): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/menus/route.ts(35,32): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/menus/route.ts(112,34): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/menus/route.ts(112,65): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/menus/route.ts(113,26): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/menus/route.ts(114,24): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/menus/route.ts(131,41): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/menus/route.ts(131,74): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/menus/route.ts(136,15): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/menus/route.ts(162,24): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/menus/route.ts(163,78): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/menus/route.ts(164,68): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/menus/route.ts(181,67): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/menus/route.ts(188,30): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/menus/route.ts(189,23): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/menus/route.ts(195,36): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/menus/route.ts(198,15): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/menus/route.ts(1401,37): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/menus/route.ts(1403,26): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/menus/route.ts(1409,34): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/menus/route.ts(1474,26): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/menus/route.ts(1475,29): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/menus/route.ts(1483,37): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/migrate/route.ts(92,32): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/migrate/route.ts(162,26): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/migrate/route.ts(188,32): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/quality-final-seed/route.ts(743,13): error TS2322: Type 'QueryResult' is not assignable to type 'DbRow[]'. - Type 'OkPacket' is missing the following properties from type 'DbRow[]': length, pop, push, concat, and 35 more. -src/app/api/init/quality-final-seed/route.ts(765,20): error TS2769: No overload matches this call. - Overload 1 of 2, '(sql: string, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to type 'ExecuteValues'. - Type 'undefined' is not assignable to type 'ExecuteValues'. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/init/seed-data/route.ts(12,14): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(59,16): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(60,17): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(61,18): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(62,17): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(69,16): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(74,16): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(75,18): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(93,19): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(93,65): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(116,64): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(118,20): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(118,64): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(119,24): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(119,70): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(139,19): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(139,65): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(168,19): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(168,65): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(187,70): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(189,19): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(189,63): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(210,72): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(212,19): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(212,63): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(235,19): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(235,65): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(258,19): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(258,65): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(296,19): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(296,65): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(375,53): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(376,23): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(393,20): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(393,48): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(410,19): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(410,67): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(411,21): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(411,67): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(438,19): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(438,65): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(439,19): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(439,67): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(458,19): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(458,67): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(459,24): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(459,72): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(472,22): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(473,19): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(473,65): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(493,19): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(493,67): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(494,19): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(494,65): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(509,22): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(510,19): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(510,65): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(530,19): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(530,67): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(536,23): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(537,19): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(537,65): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(559,19): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(559,67): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(565,22): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(566,19): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(566,65): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(567,21): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(567,67): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(590,24): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(590,72): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(591,22): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(591,64): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(604,22): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(605,19): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(605,65): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(627,24): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(629,22): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(629,58): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(630,21): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(630,67): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(631,21): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(631,67): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(649,45): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(654,19): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(654,63): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(655,19): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(655,65): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(703,19): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(703,63): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(737,19): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(737,63): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(766,19): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(766,63): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(790,19): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(790,65): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(791,19): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(791,67): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(792,21): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(792,67): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(819,19): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(819,65): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(857,19): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(857,67): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(858,24): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(858,72): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(872,19): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(872,65): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(876,13): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(893,19): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(893,67): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(894,24): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(894,72): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(907,19): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(907,65): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(911,13): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(929,11): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(931,21): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(931,67): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(932,26): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(932,74): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(959,11): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(966,26): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(966,54): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(967,57): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(968,23): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(968,63): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(969,24): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(969,56): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(973,17): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(974,17): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(987,71): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(990,43): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(997,19): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(997,65): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(998,19): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(998,67): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(999,21): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(999,67): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(1027,24): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(1027,72): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(1028,19): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(1028,65): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(1029,20): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(1029,64): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(1041,19): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(1041,65): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(1056,19): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(1056,65): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(1062,13): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(1080,19): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(1080,65): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(1097,13): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(1111,12): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(1120,19): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(1120,65): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(1155,19): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(1155,65): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(1156,19): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(1156,65): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(1183,14): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(1193,24): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(1193,72): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(1194,19): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(1194,65): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(1232,24): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(1232,72): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(1233,19): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(1233,65): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(1263,19): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(1263,65): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(1268,32): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/seed-data/route.ts(1269,17): error TS7053: Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'QueryResult'. - No index signature with a parameter of type 'number' was found on type 'QueryResult'. -src/app/api/init/seed-data/route.ts(1269,65): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/init/settings-seed/route.ts(375,22): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/init/settings-seed/route.ts(1352,23): error TS2339: Property 'filter' does not exist on type 'QueryResult'. - Property 'filter' does not exist on type 'OkPacket'. -src/app/api/init/settings-seed/route.ts(1375,29): error TS2339: Property 'map' does not exist on type 'QueryResult'. - Property 'map' does not exist on type 'OkPacket'. -src/app/api/init/supplement-tables/route.ts(1051,20): error TS2339: Property 'cnt' does not exist on type 'QueryResult'. - Property 'cnt' does not exist on type 'OkPacket'. -src/app/api/init/supplement-tables/route.ts(1100,25): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/ink-usages/route.ts(131,35): error TS2352: Conversion of type 'ResultSetHeader' to type 'DbRow' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first. - Index signature for type 'string' is missing in type 'ResultSetHeader'. -src/app/api/inventory/route.ts(122,20): error TS2345: Argument of type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to parameter of type 'string'. - Type 'undefined' is not assignable to type 'string'. -src/app/api/inventory/route.ts(123,40): error TS2345: Argument of type 'string | number | true | Date | Buffer' is not assignable to parameter of type 'string'. - Type 'number' is not assignable to type 'string'. -src/app/api/inventory/route.ts(183,22): error TS2339: Property 'material_name' does not exist on type '{}'. -src/app/api/inventory/route.ts(185,23): error TS2339: Property 'warehouse_name' does not exist on type '{}'. -src/app/api/inventory/route.ts(188,22): error TS2339: Property 'unit' does not exist on type '{}'. -src/app/api/inventory/route.ts(189,22): error TS2339: Property 'purchase_price' does not exist on type '{}'. -src/app/api/inventory/route.ts(194,28): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/inventory/route.ts(204,31): error TS2339: Property 'purchase_price' does not exist on type '{}'. -src/app/api/inventory/route.ts(205,45): error TS2339: Property 'purchase_price' does not exist on type '{}'. -src/app/api/inventory/route.ts(223,22): error TS2339: Property 'unit' does not exist on type '{}'. -src/app/api/inventory/route.ts(292,41): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/inventory/route.ts(296,27): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/inventory/route.ts(316,51): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/inventory/route.ts(316,65): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/inventory/route.ts(326,30): error TS2339: Property 'affectedRows' does not exist on type 'QueryResult'. - Property 'affectedRows' does not exist on type 'ResultSetHeader[]'. -src/app/api/inventory/route.ts(450,32): error TS2339: Property 'material_name' does not exist on type '{}'. -src/app/api/inventory/route.ts(463,42): error TS2339: Property 'warehouse_code' does not exist on type '{}'. -src/app/api/inventory/route.ts(480,28): error TS2339: Property 'unit' does not exist on type '{}'. -src/app/api/inventory/route.ts(569,39): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/inventory/route.ts(573,25): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/inventory/route.ts(580,47): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/inventory/route.ts(584,35): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/inventory/route.ts(593,28): error TS2339: Property 'affectedRows' does not exist on type 'QueryResult'. - Property 'affectedRows' does not exist on type 'ResultSetHeader[]'. -src/app/api/linkage/validate/route.ts(120,19): error TS2362: The left-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. -src/app/api/linkage/validate/route.ts(120,35): error TS2362: The left-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. -src/app/api/linkage/validate/route.ts(127,14): error TS2365: Operator '>' cannot be applied to types 'string | number | boolean | Date | Buffer' and 'number'. -src/app/api/linkage/validate/route.ts(131,14): error TS2365: Operator '>' cannot be applied to types 'string | number | boolean | Date | Buffer' and 'number'. -src/app/api/linkage/validate/route.ts(136,7): error TS18049: 'newStatus' is possibly 'null' or 'undefined'. -src/app/api/linkage/validate/route.ts(136,19): error TS18049: 'oldStatus' is possibly 'null' or 'undefined'. -src/app/api/linkage/validate/route.ts(142,47): error TS2345: Argument of type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to parameter of type 'string'. - Type 'undefined' is not assignable to type 'string'. -src/app/api/linkage/validate/route.ts(183,11): error TS18049: 'orderLine.status' is possibly 'null' or 'undefined'. -src/app/api/linkage/validate/route.ts(183,11): error TS2365: Operator '<' cannot be applied to types 'string | number | boolean | Date | Buffer' and 'number'. -src/app/api/linkage/validate/route.ts(187,11): error TS18049: 'orderLine.status' is possibly 'null' or 'undefined'. -src/app/api/linkage/validate/route.ts(187,11): error TS2365: Operator '>=' cannot be applied to types 'string | number | boolean | Date | Buffer' and 'number'. -src/app/api/linkage/validate/route.ts(197,22): error TS2362: The left-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. -src/app/api/linkage/validate/route.ts(197,22): error TS18049: 'orderLine.req_qty' is possibly 'null' or 'undefined'. -src/app/api/linkage/validate/route.ts(197,47): error TS2362: The left-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. -src/app/api/linkage/validate/route.ts(198,37): error TS2363: The right-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. -src/app/api/linkage/validate/route.ts(198,37): error TS18049: 'orderLine.ordered_qty' is possibly 'null' or 'undefined'. -src/app/api/linkage/validate/route.ts(269,13): error TS2769: No overload matches this call. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/linkage/validate/route.ts(276,29): error TS2345: Argument of type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to parameter of type 'number'. - Type 'undefined' is not assignable to type 'number'. -src/app/api/linkage/validate/route.ts(309,13): error TS2769: No overload matches this call. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/linkage/validate/route.ts(319,13): error TS2769: No overload matches this call. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/linkage/validate/route.ts(326,29): error TS2345: Argument of type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to parameter of type 'number'. - Type 'undefined' is not assignable to type 'number'. -src/app/api/linkage/validate/route.ts(364,21): error TS18049: 'orderLine.available_to_receive' is possibly 'null' or 'undefined'. -src/app/api/linkage/validate/route.ts(406,51): error TS2345: Argument of type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to parameter of type 'number'. - Type 'undefined' is not assignable to type 'number'. -src/app/api/linkage/validate/route.ts(467,25): error TS2362: The left-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. -src/app/api/linkage/validate/route.ts(467,40): error TS2363: The right-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. -src/app/api/linkage/validate/route.ts(468,26): error TS2362: The left-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. -src/app/api/linkage/validate/route.ts(468,42): error TS2363: The right-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. -src/app/api/linkage/validate/route.ts(469,29): error TS2365: Operator '>' cannot be applied to types 'string | number | boolean | Date | Buffer' and 'number'. -src/app/api/linkage/validate/route.ts(469,46): error TS2362: The left-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. -src/app/api/linkage/validate/route.ts(469,62): error TS2363: The right-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. -src/app/api/material-labels/route.ts(176,33): error TS2352: Conversion of type 'ResultSetHeader' to type 'DbRow' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first. - Index signature for type 'string' is missing in type 'ResultSetHeader'. -src/app/api/material-labels/route.ts(205,23): error TS18049: 'parent.width' is possibly 'null' or 'undefined'. -src/app/api/material-labels/route.ts(213,30): error TS2363: The right-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. -src/app/api/material-labels/route.ts(213,30): error TS18049: 'parent.width' is possibly 'null' or 'undefined'. -src/app/api/material-labels/route.ts(214,36): error TS2362: The left-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. -src/app/api/material-labels/route.ts(214,36): error TS18049: 'parent.quantity' is possibly 'null' or 'undefined'. -src/app/api/material-labels/route.ts(235,30): error TS2362: The left-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. -src/app/api/material-labels/route.ts(254,7): error TS2353: Object literal may only specify known properties, and 'id' does not exist in type 'Date | Buffer | readonly SqlValue[]'. -src/app/api/material-labels/route.ts(254,12): error TS2352: Conversion of type 'ResultSetHeader' to type 'DbRow' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first. - Index signature for type 'string' is missing in type 'ResultSetHeader'. -src/app/api/material-labels/route.ts(274,23): error TS2362: The left-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. -src/app/api/material-labels/route.ts(274,23): error TS18049: 'parent.width' is possibly 'null' or 'undefined'. -src/app/api/material-requisitions/route.ts(66,35): error TS2538: Type 'Buffer' cannot be used as an index type. -src/app/api/material-requisitions/route.ts(66,35): error TS2538: Type 'Date' cannot be used as an index type. -src/app/api/material-requisitions/route.ts(66,35): error TS2538: Type 'false' cannot be used as an index type. -src/app/api/material-requisitions/route.ts(66,35): error TS2538: Type 'null' cannot be used as an index type. -src/app/api/material-requisitions/route.ts(66,35): error TS2538: Type 'true' cannot be used as an index type. -src/app/api/material-requisitions/route.ts(66,35): error TS2538: Type 'undefined' cannot be used as an index type. -src/app/api/material-requisitions/route.ts(67,33): error TS2538: Type 'Buffer' cannot be used as an index type. -src/app/api/material-requisitions/route.ts(67,33): error TS2538: Type 'Date' cannot be used as an index type. -src/app/api/material-requisitions/route.ts(67,33): error TS2538: Type 'false' cannot be used as an index type. -src/app/api/material-requisitions/route.ts(67,33): error TS2538: Type 'null' cannot be used as an index type. -src/app/api/material-requisitions/route.ts(67,33): error TS2538: Type 'true' cannot be used as an index type. -src/app/api/material-requisitions/route.ts(67,33): error TS2538: Type 'undefined' cannot be used as an index type. -src/app/api/material-requisitions/route.ts(114,57): error TS2345: Argument of type '(string | number | boolean | Date | Buffer | null | undefined)[]' is not assignable to parameter of type '(number | null | undefined)[]'. - Type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to type 'number | null | undefined'. - Type 'string' is not assignable to type 'number'. -src/app/api/material-returns/route.ts(57,33): error TS2538: Type 'Buffer' cannot be used as an index type. -src/app/api/material-returns/route.ts(57,33): error TS2538: Type 'Date' cannot be used as an index type. -src/app/api/material-returns/route.ts(57,33): error TS2538: Type 'false' cannot be used as an index type. -src/app/api/material-returns/route.ts(57,33): error TS2538: Type 'null' cannot be used as an index type. -src/app/api/material-returns/route.ts(57,33): error TS2538: Type 'true' cannot be used as an index type. -src/app/api/material-returns/route.ts(57,33): error TS2538: Type 'undefined' cannot be used as an index type. -src/app/api/menu/route.ts(10,3): error TS2322: Type '{ children: DbRow[]; }[]' is not assignable to type 'DbRow[]'. - Type '{ children: DbRow[]; }' is not assignable to type 'DbRow'. - Property 'children' is incompatible with index signature. - Type 'DbRow[]' is not assignable to type 'string | number | boolean | Date | Buffer | null | undefined'. - Type 'DbRow[]' is missing the following properties from type 'Buffer': subarray, write, toJSON, equals, and 73 more. -src/app/api/menu/route.ts(12,21): error TS2362: The left-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. -src/app/api/menu/route.ts(12,21): error TS18049: 'a.sort_order' is possibly 'null' or 'undefined'. -src/app/api/menu/route.ts(12,36): error TS2363: The right-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. -src/app/api/menu/route.ts(12,36): error TS18049: 'b.sort_order' is possibly 'null' or 'undefined'. -src/app/api/menu/route.ts(15,38): error TS2345: Argument of type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to parameter of type 'number | undefined'. - Type 'null' is not assignable to type 'number | undefined'. -src/app/api/menu/sort-order/route.ts(60,24): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/migrations/six-critical-fixes/route.ts(152,13): error TS2365: Operator '>' cannot be applied to types 'string | number | boolean | Date | Buffer' and 'number'. -src/app/api/migrations/six-critical-fixes/route.ts(173,13): error TS2365: Operator '>' cannot be applied to types 'string | number | boolean | Date | Buffer' and 'number'. -src/app/api/migrations/six-critical-fixes/route.ts(192,13): error TS2365: Operator '>' cannot be applied to types 'string | number | boolean | Date | Buffer' and 'number'. -src/app/api/migrations/six-critical-fixes/route.ts(210,13): error TS2365: Operator '>' cannot be applied to types 'string | number | boolean | Date | Buffer' and 'number'. -src/app/api/migrations/six-critical-fixes/route.ts(318,13): error TS2365: Operator '>' cannot be applied to types 'string | number | boolean | Date | Buffer' and 'number'. -src/app/api/migrations/six-critical-fixes/route.ts(335,13): error TS2365: Operator '>' cannot be applied to types 'string | number | boolean | Date | Buffer' and 'number'. -src/app/api/migrations/six-critical-fixes/route.ts(354,13): error TS2365: Operator '>' cannot be applied to types 'string | number | boolean | Date | Buffer' and 'number'. -src/app/api/migrations/six-critical-fixes/route.ts(371,13): error TS2365: Operator '>' cannot be applied to types 'string | number | boolean | Date | Buffer' and 'number'. -src/app/api/migrations/six-critical-fixes/route.ts(390,13): error TS2365: Operator '>' cannot be applied to types 'string | number | boolean | Date | Buffer' and 'number'. -src/app/api/migrations/six-critical-fixes/route.ts(461,13): error TS2365: Operator '>' cannot be applied to types 'string | number | boolean | Date | Buffer' and 'number'. -src/app/api/migrations/six-critical-fixes/route.ts(478,13): error TS2365: Operator '>' cannot be applied to types 'string | number | boolean | Date | Buffer' and 'number'. -src/app/api/migrations/six-critical-fixes/route.ts(495,13): error TS2365: Operator '>' cannot be applied to types 'string | number | boolean | Date | Buffer' and 'number'. -src/app/api/migrations/six-critical-fixes/route.ts(556,15): error TS2365: Operator '>' cannot be applied to types 'string | number | boolean | Date | Buffer' and 'number'. -src/app/api/orders/bom/materials/route.ts(121,35): error TS2352: Conversion of type 'any[]' to type 'DbRow' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first. - Index signature for type 'string' is missing in type 'any[]'. -src/app/api/orders/bom/route.ts(182,22): error TS2352: Conversion of type 'QueryResult' to type 'DbRow' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first. - Type '[RowDataPacket[], ResultSetHeader]' is not comparable to type 'DbRow'. - Index signature for type 'string' is missing in type '[RowDataPacket[], ResultSetHeader]'. -src/app/api/orders/bom/route.ts(273,11): error TS18049: 'bom.status' is possibly 'null' or 'undefined'. -src/app/api/orders/bom/route.ts(273,11): error TS2365: Operator '>=' cannot be applied to types 'string | number | boolean | Date | Buffer' and 'number'. -src/app/api/orders/bom/route.ts(295,26): error TS2769: No overload matches this call. - Overload 1 of 2, '(sql: string, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'SqlValue[]' is not assignable to parameter of type 'ExecuteValues | undefined'. - Type 'SqlValue[]' is not assignable to type 'ExecuteValues[]'. - Type 'SqlValue' is not assignable to type 'ExecuteValues'. - Type 'undefined' is not assignable to type 'ExecuteValues'. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/orders/bom/route.ts(369,9): error TS18049: 'bomData.status' is possibly 'null' or 'undefined'. -src/app/api/orders/bom/route.ts(369,9): error TS2365: Operator '>=' cannot be applied to types 'string | number | boolean | Date | Buffer' and 'number'. -src/app/api/orders/bom/route.ts(379,24): error TS2769: No overload matches this call. - Overload 1 of 2, '(sql: string, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to type 'ExecuteValues'. - Type 'undefined' is not assignable to type 'ExecuteValues'. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/orders/export/route.ts(58,32): error TS2339: Property 'replace' does not exist on type 'string | number | true | Date | Buffer'. - Property 'replace' does not exist on type 'number'. -src/app/api/orders/route.ts(50,34): error TS2345: Argument of type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to parameter of type 'string'. - Type 'undefined' is not assignable to type 'string'. -src/app/api/orders/route.ts(51,59): error TS2345: Argument of type 'string | number | true | Date | Buffer' is not assignable to parameter of type 'string'. - Type 'number' is not assignable to type 'string'. -src/app/api/orders/route.ts(56,32): error TS2345: Argument of type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to parameter of type 'string'. - Type 'undefined' is not assignable to type 'string'. -src/app/api/orders/route.ts(58,34): error TS2345: Argument of type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to parameter of type 'string'. - Type 'undefined' is not assignable to type 'string'. -src/app/api/orders/route.ts(59,35): error TS2345: Argument of type 'string | number | true | Date | Buffer' is not assignable to parameter of type 'string'. - Type 'number' is not assignable to type 'string'. -src/app/api/orders/route.ts(109,36): error TS2345: Argument of type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to parameter of type 'string'. - Type 'undefined' is not assignable to type 'string'. -src/app/api/orders/route.ts(110,61): error TS2345: Argument of type 'string | number | true | Date | Buffer' is not assignable to parameter of type 'string'. - Type 'number' is not assignable to type 'string'. -src/app/api/orders/route.ts(115,34): error TS2345: Argument of type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to parameter of type 'string'. - Type 'undefined' is not assignable to type 'string'. -src/app/api/orders/route.ts(117,36): error TS2345: Argument of type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to parameter of type 'string'. - Type 'undefined' is not assignable to type 'string'. -src/app/api/orders/route.ts(118,37): error TS2345: Argument of type 'string | number | true | Date | Buffer' is not assignable to parameter of type 'string'. - Type 'number' is not assignable to type 'string'. -src/app/api/orders/route.ts(176,43): error TS2362: The left-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. -src/app/api/orders/route.ts(176,43): error TS18049: 'item.quantity' is possibly 'null' or 'undefined'. -src/app/api/orders/route.ts(176,59): error TS2363: The right-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. -src/app/api/orders/route.ts(176,59): error TS18049: 'item.unit_price' is possibly 'null' or 'undefined'. -src/app/api/orders/route.ts(188,22): error TS2352: Conversion of type 'any[]' to type 'DbRow' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first. - Index signature for type 'string' is missing in type 'any[]'. -src/app/api/orders/sales/route.ts(61,30): error TS2345: Argument of type 'string | number | true | Date | Buffer' is not assignable to parameter of type 'string'. - Type 'number' is not assignable to type 'string'. -src/app/api/orders/sales/route.ts(62,32): error TS2345: Argument of type 'string | number | true | Date | Buffer' is not assignable to parameter of type 'string'. - Type 'number' is not assignable to type 'string'. -src/app/api/orders/sales/route.ts(64,31): error TS2345: Argument of type 'string | number | true | Date | Buffer' is not assignable to parameter of type 'string'. - Type 'number' is not assignable to type 'string'. -src/app/api/orders/sales/route.ts(66,35): error TS2345: Argument of type 'string | number | true | Date | Buffer' is not assignable to parameter of type 'string'. - Type 'number' is not assignable to type 'string'. -src/app/api/orders/sales/route.ts(67,33): error TS2345: Argument of type 'string | number | true | Date | Buffer' is not assignable to parameter of type 'string'. - Type 'number' is not assignable to type 'string'. -src/app/api/orders/sales/route.ts(68,34): error TS2345: Argument of type 'string | number | true | Date | Buffer' is not assignable to parameter of type 'string'. - Type 'number' is not assignable to type 'string'. -src/app/api/orders/sales/route.ts(86,70): error TS2538: Type 'Buffer' cannot be used as an index type. -src/app/api/orders/sales/route.ts(86,70): error TS2538: Type 'Date' cannot be used as an index type. -src/app/api/orders/sales/route.ts(86,70): error TS2538: Type 'false' cannot be used as an index type. -src/app/api/orders/sales/route.ts(86,70): error TS2538: Type 'null' cannot be used as an index type. -src/app/api/orders/sales/route.ts(86,70): error TS2538: Type 'true' cannot be used as an index type. -src/app/api/orders/sales/route.ts(86,70): error TS2538: Type 'undefined' cannot be used as an index type. -src/app/api/orders/sales/route.ts(118,59): error TS2345: Argument of type '(string | number | boolean | Date | Buffer | null | undefined)[]' is not assignable to parameter of type '(number | null | undefined)[]'. - Type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to type 'number | null | undefined'. - Type 'string' is not assignable to type 'number'. -src/app/api/organization/employee/route.ts(86,27): error TS2322: Type 'SqlValue[]' is not assignable to type 'DbRow[]'. - Type 'SqlValue' is not assignable to type 'DbRow'. - Type 'undefined' is not assignable to type 'DbRow'. -src/app/api/organization/employee/route.ts(122,64): error TS2345: Argument of type 'DbRow[]' is not assignable to parameter of type 'number | SqlValue[]'. - Type 'DbRow[]' is not assignable to type 'SqlValue[]'. - Type 'DbRow' is not assignable to type 'SqlValue'. - Type 'DbRow' is missing the following properties from type 'Buffer': slice, subarray, write, toJSON, and 102 more. -src/app/api/outsource/issue/route.ts(126,23): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/outsource/issue/route.ts(127,23): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/outsource/issue/route.ts(195,41): error TS2339: Property 'reduce' does not exist on type 'QueryResult'. - Property 'reduce' does not exist on type 'OkPacket'. -src/app/api/outsource/receive/route.ts(106,25): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/outsource/receive/route.ts(107,25): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/outsource/settlement/route.ts(114,28): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/outsource/settlement/route.ts(116,28): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/prepress/die-maintenance/route.ts(92,19): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/prepress/die-maintenance/route.ts(192,21): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/prepress/die-maintenance/route.ts(268,22): error TS2769: No overload matches this call. - Overload 1 of 2, '(sql: string, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'SqlValue[]' is not assignable to parameter of type 'ExecuteValues | undefined'. - Type 'SqlValue[]' is not assignable to type 'ExecuteValues[]'. - Type 'SqlValue' is not assignable to type 'ExecuteValues'. - Type 'undefined' is not assignable to type 'ExecuteValues'. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/prepress/die-migrate/route.ts(32,7): error TS2345: Argument of type '{ table: string; status: string; reason?: undefined; message?: undefined; } | { table: string; status: string; reason: string; message?: undefined; } | { table: string; status: string; message: string; reason?: undefined; }' is not assignable to parameter of type 'SqlValue'. - Type '{ table: string; status: string; reason?: undefined; message?: undefined; }' is not assignable to type 'SqlValue'. -src/app/api/prepress/die-migrate/route.ts(39,7): error TS2345: Argument of type '{ table: string; status: string; reason?: undefined; message?: undefined; } | { table: string; status: string; reason: string; message?: undefined; } | { table: string; status: string; message: string; reason?: undefined; }' is not assignable to parameter of type 'SqlValue'. - Type '{ table: string; status: string; reason?: undefined; message?: undefined; }' is not assignable to type 'SqlValue'. -src/app/api/prepress/die-migrate/route.ts(46,7): error TS2345: Argument of type '{ table: string; status: string; reason?: undefined; message?: undefined; } | { table: string; status: string; reason: string; message?: undefined; } | { table: string; status: string; message: string; reason?: undefined; }' is not assignable to parameter of type 'SqlValue'. - Type '{ table: string; status: string; reason?: undefined; message?: undefined; }' is not assignable to type 'SqlValue'. -src/app/api/prepress/die-migrate/route.ts(53,7): error TS2345: Argument of type '{ table: string; status: string; reason?: undefined; message?: undefined; } | { table: string; status: string; reason: string; message?: undefined; } | { table: string; status: string; message: string; reason?: undefined; }' is not assignable to parameter of type 'SqlValue'. - Type '{ table: string; status: string; reason?: undefined; message?: undefined; }' is not assignable to type 'SqlValue'. -src/app/api/prepress/die-migrate/route.ts(60,7): error TS2345: Argument of type '{ table: string; status: string; reason?: undefined; message?: undefined; } | { table: string; status: string; reason: string; message?: undefined; } | { table: string; status: string; message: string; reason?: undefined; }' is not assignable to parameter of type 'SqlValue'. - Type '{ table: string; status: string; reason?: undefined; message?: undefined; }' is not assignable to type 'SqlValue'. -src/app/api/prepress/die-migrate/route.ts(67,7): error TS2345: Argument of type '{ table: string; status: string; reason?: undefined; message?: undefined; } | { table: string; status: string; reason: string; message?: undefined; } | { table: string; status: string; message: string; reason?: undefined; }' is not assignable to parameter of type 'SqlValue'. - Type '{ table: string; status: string; reason?: undefined; message?: undefined; }' is not assignable to type 'SqlValue'. -src/app/api/prepress/die-migrate/route.ts(74,7): error TS2345: Argument of type '{ table: string; status: string; reason?: undefined; message?: undefined; } | { table: string; status: string; reason: string; message?: undefined; } | { table: string; status: string; message: string; reason?: undefined; }' is not assignable to parameter of type 'SqlValue'. - Type '{ table: string; status: string; reason?: undefined; message?: undefined; }' is not assignable to type 'SqlValue'. -src/app/api/prepress/die-migrate/route.ts(81,7): error TS2345: Argument of type '{ table: string; status: string; reason?: undefined; message?: undefined; } | { table: string; status: string; reason: string; message?: undefined; } | { table: string; status: string; message: string; reason?: undefined; }' is not assignable to parameter of type 'SqlValue'. - Type '{ table: string; status: string; reason?: undefined; message?: undefined; }' is not assignable to type 'SqlValue'. -src/app/api/prepress/die-migrate/route.ts(88,7): error TS2345: Argument of type '{ table: string; status: string; reason?: undefined; message?: undefined; } | { table: string; status: string; reason: string; message?: undefined; } | { table: string; status: string; message: string; reason?: undefined; }' is not assignable to parameter of type 'SqlValue'. - Type '{ table: string; status: string; reason?: undefined; message?: undefined; }' is not assignable to type 'SqlValue'. -src/app/api/prepress/die-migrate/route.ts(95,7): error TS2345: Argument of type '{ table: string; status: string; reason?: undefined; message?: undefined; } | { table: string; status: string; reason: string; message?: undefined; } | { table: string; status: string; message: string; reason?: undefined; }' is not assignable to parameter of type 'SqlValue'. - Type '{ table: string; status: string; reason?: undefined; message?: undefined; }' is not assignable to type 'SqlValue'. -src/app/api/prepress/die-migrate/route.ts(102,7): error TS2345: Argument of type '{ table: string; status: string; reason?: undefined; message?: undefined; } | { table: string; status: string; reason: string; message?: undefined; } | { table: string; status: string; message: string; reason?: undefined; }' is not assignable to parameter of type 'SqlValue'. - Type '{ table: string; status: string; reason?: undefined; message?: undefined; }' is not assignable to type 'SqlValue'. -src/app/api/prepress/die-migrate/route.ts(109,7): error TS2345: Argument of type '{ table: string; status: string; reason?: undefined; message?: undefined; } | { table: string; status: string; reason: string; message?: undefined; } | { table: string; status: string; message: string; reason?: undefined; }' is not assignable to parameter of type 'SqlValue'. - Type '{ table: string; status: string; reason?: undefined; message?: undefined; }' is not assignable to type 'SqlValue'. -src/app/api/prepress/die-migrate/route.ts(116,7): error TS2345: Argument of type '{ table: string; status: string; reason?: undefined; message?: undefined; } | { table: string; status: string; reason: string; message?: undefined; } | { table: string; status: string; message: string; reason?: undefined; }' is not assignable to parameter of type 'SqlValue'. - Type '{ table: string; status: string; reason?: undefined; message?: undefined; }' is not assignable to type 'SqlValue'. -src/app/api/prepress/die-migrate/route.ts(123,7): error TS2345: Argument of type '{ table: string; status: string; reason?: undefined; message?: undefined; } | { table: string; status: string; reason: string; message?: undefined; } | { table: string; status: string; message: string; reason?: undefined; }' is not assignable to parameter of type 'SqlValue'. - Type '{ table: string; status: string; reason?: undefined; message?: undefined; }' is not assignable to type 'SqlValue'. -src/app/api/prepress/die-migrate/route.ts(130,7): error TS2345: Argument of type '{ table: string; status: string; message?: undefined; } | { table: string; status: string; message: string; }' is not assignable to parameter of type 'SqlValue'. - Type '{ table: string; status: string; message?: undefined; }' is not assignable to type 'SqlValue'. -src/app/api/prepress/die-migrate/route.ts(158,7): error TS2345: Argument of type '{ table: string; status: string; message?: undefined; } | { table: string; status: string; message: string; }' is not assignable to parameter of type 'SqlValue'. - Type '{ table: string; status: string; message?: undefined; }' is not assignable to type 'SqlValue'. -src/app/api/prepress/die-migrate/route.ts(191,9): error TS2353: Object literal may only specify known properties, and 'action' does not exist in type 'Date | Buffer | readonly SqlValue[]'. -src/app/api/prepress/die-migrate/route.ts(196,22): error TS2353: Object literal may only specify known properties, and 'action' does not exist in type 'Date | Buffer | readonly SqlValue[]'. -src/app/api/prepress/die-migrate/route.ts(210,9): error TS2353: Object literal may only specify known properties, and 'action' does not exist in type 'Date | Buffer | readonly SqlValue[]'. -src/app/api/prepress/die-migrate/route.ts(215,22): error TS2353: Object literal may only specify known properties, and 'action' does not exist in type 'Date | Buffer | readonly SqlValue[]'. -src/app/api/prepress/die-migrate/route.ts(226,22): error TS2353: Object literal may only specify known properties, and 'action' does not exist in type 'Date | Buffer | readonly SqlValue[]'. -src/app/api/prepress/die-migrate/route.ts(229,9): error TS2353: Object literal may only specify known properties, and 'action' does not exist in type 'Date | Buffer | readonly SqlValue[]'. -src/app/api/prepress/die-usage/route.ts(113,19): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/production/material-issue/route.ts(85,59): error TS2345: Argument of type '(string | number | boolean | Date | Buffer | null | undefined)[]' is not assignable to parameter of type '(number | null | undefined)[]'. - Type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to type 'number | null | undefined'. - Type 'string' is not assignable to type 'number'. -src/app/api/production/material-issue/route.ts(116,20): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/production/material-issue/route.ts(119,13): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/production/material-issue/route.ts(122,13): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/production/material-issue/route.ts(144,23): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/production/material-issue/route.ts(145,35): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/production/material-issue/route.ts(146,37): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/production/material-issue/route.ts(151,36): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/production/material-issue/route.ts(164,42): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/production/material-issue/route.ts(194,21): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/production/material-issue/route.ts(198,21): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/production/material-issue/route.ts(219,35): error TS2339: Property 'insertId' does not exist on type 'QueryResult'. - Property 'insertId' does not exist on type 'ResultSetHeader[]'. -src/app/api/production/material-issue/route.ts(264,23): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/production/material-issue/route.ts(268,23): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/production/material-issue/route.ts(286,23): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/production/material-issue/route.ts(289,22): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/production/material-issue/route.ts(291,52): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/production/material-issue/route.ts(307,29): error TS2339: Property 'map' does not exist on type 'QueryResult'. - Property 'map' does not exist on type 'OkPacket'. -src/app/api/production/material-return/route.ts(131,13): error TS2322: Type '{ materialId: string | number | boolean | Date | Buffer | null | undefined; materialCode: string | number | true | Date | Buffer<...> | null; materialName: string | ... 4 more ... | null; quantity: number; unit: string | ... 4 more ... | null; batchNo: string | ... 4 more ... | null; }[]' is not assignable to type '{ materialId: number; materialCode: string | null; materialName: string | null; quantity: number; unit: string | null; batchNo: string | null; unitPrice?: number | undefined; }[]'. - Type '{ materialId: string | number | boolean | Date | Buffer | null | undefined; materialCode: string | number | true | Date | Buffer | null; materialName: string | ... 4 more ... | null; quantity: number; unit: string | ... 4 more ... | null; batchNo: string | ... 4 more ... | null; }' is not assignable to type '{ materialId: number; materialCode: string | null; materialName: string | null; quantity: number; unit: string | null; batchNo: string | null; unitPrice?: number | undefined; }'. - Types of property 'materialId' are incompatible. - Type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to type 'number'. - Type 'undefined' is not assignable to type 'number'. -src/app/api/production/orders/route.ts(43,26): error TS2345: Argument of type 'string | number | true | Date | Buffer' is not assignable to parameter of type 'string'. - Type 'number' is not assignable to type 'string'. -src/app/api/production/process-route/route.ts(40,5): error TS2322: Type 'any[]' is not assignable to type 'string | number | boolean | Date | Buffer | null | undefined'. - Type 'any[]' is missing the following properties from type 'Buffer': subarray, write, toJSON, equals, and 73 more. -src/app/api/production/process-route/route.ts(70,23): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/production/schedule/capacity/route.ts(100,27): error TS2362: The left-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. -src/app/api/production/schedule/capacity/route.ts(100,27): error TS18049: 'row.capacity_per_hour' is possibly 'null' or 'undefined'. -src/app/api/production/schedule/capacity/route.ts(101,61): error TS2362: The left-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. -src/app/api/production/schedule/capacity/route.ts(101,61): error TS18049: 'row.used_hours' is possibly 'null' or 'undefined'. -src/app/api/production/schedule/capacity/route.ts(121,73): error TS2365: Operator '+' cannot be applied to types 'number' and 'string | number | boolean | Date | Buffer'. -src/app/api/production/schedule/capacity/route.ts(121,79): error TS18049: 'r.equipment_count' is possibly 'null' or 'undefined'. -src/app/api/production/schedule/capacity/route.ts(126,64): error TS2365: Operator '+' cannot be applied to types 'number' and 'string | number | boolean | Date | Buffer'. -src/app/api/production/schedule/capacity/route.ts(126,70): error TS18049: 'c.utilizationRate' is possibly 'null' or 'undefined'. -src/app/api/production/trace/route.ts(19,5): error TS2322: Type 'unknown[]' is not assignable to type 'SqlValue[]'. - Type 'unknown' is not assignable to type 'SqlValue'. -src/app/api/production/trace/route.ts(26,7): error TS2322: Type 'unknown[]' is not assignable to type 'SqlValue[]'. - Type 'unknown' is not assignable to type 'SqlValue'. -src/app/api/production/trace/route.ts(34,7): error TS2322: Type 'unknown[]' is not assignable to type 'SqlValue[]'. - Type 'unknown' is not assignable to type 'SqlValue'. -src/app/api/production/trace/route.ts(76,20): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/production/trace/route.ts(101,13): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/production/trace/route.ts(104,22): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/production/trace/route.ts(130,33): error TS2339: Property 'insertId' does not exist on type 'QueryResult'. - Property 'insertId' does not exist on type 'ResultSetHeader[]'. -src/app/api/production/trace/route.ts(153,9): error TS2353: Object literal may only specify known properties, and 'level' does not exist in type 'Date | Buffer | readonly SqlValue[]'. -src/app/api/production/trace/route.ts(186,31): error TS18049: 'a' is possibly 'null' or 'undefined'. -src/app/api/production/trace/route.ts(186,33): error TS2339: Property 'level' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'level' does not exist on type 'string'. -src/app/api/production/trace/route.ts(186,41): error TS18049: 'b' is possibly 'null' or 'undefined'. -src/app/api/production/trace/route.ts(186,43): error TS2339: Property 'level' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'level' does not exist on type 'string'. -src/app/api/production/work-report/route.ts(103,24): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/production/work-report/route.ts(110,21): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/products/categories/route.ts(89,23): error TS2352: Conversion of type 'any[]' to type 'DbRow' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first. - Index signature for type 'string' is missing in type 'any[]'. -src/app/api/products/route.ts(152,23): error TS2352: Conversion of type 'any[]' to type 'DbRow' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first. - Index signature for type 'string' is missing in type 'any[]'. -src/app/api/products/route.ts(201,27): error TS2345: Argument of type '{} | null' is not assignable to parameter of type 'SqlValue'. - Type '{}' is not assignable to type 'SqlValue'. -src/app/api/purchase/orders/route.ts(130,59): error TS2345: Argument of type '(string | number | boolean | Date | Buffer | null | undefined)[]' is not assignable to parameter of type '(number | null | undefined)[]'. - Type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to type 'number | null | undefined'. - Type 'string' is not assignable to type 'number'. -src/app/api/purchase/reconciliation/route.ts(92,32): error TS2362: The left-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. -src/app/api/purchase/reconciliation/route.ts(162,11): error TS2322: Type '({ sourceType: 1; sourceId: string | number | boolean | Date | Buffer | null | undefined; sourceNo: string | number | boolean | Date | Buffer<...> | null | undefined; sourceDate: string; amount: number; } | { ...; })[]' is not assignable to type 'PurchaseReconciliationLineProps[]'. - Type '{ sourceType: 1; sourceId: string | number | boolean | Date | Buffer | null | undefined; sourceNo: string | number | boolean | Date | Buffer<...> | null | undefined; sourceDate: string; amount: number; } | { ...; }' is not assignable to type 'PurchaseReconciliationLineProps'. - Type '{ sourceType: 1; sourceId: string | number | boolean | Date | Buffer | null | undefined; sourceNo: string | number | boolean | Date | Buffer | null | undefined; sourceDate: string; amount: number; }' is not assignable to type 'PurchaseReconciliationLineProps'. - Types of property 'sourceId' are incompatible. - Type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to type 'number'. - Type 'undefined' is not assignable to type 'number'. -src/app/api/purchase/request/route.ts(130,25): error TS2352: Conversion of type 'PurchaseRequest[]' to type 'DbRow[]' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first. - Type 'PurchaseRequest' is not comparable to type 'DbRow'. - Index signature for type 'string' is missing in type 'PurchaseRequest'. -src/app/api/purchase/request/route.ts(137,24): error TS2352: Conversion of type 'RequestItem[]' to type 'DbRow[]' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first. - Type 'RequestItem' is not comparable to type 'DbRow'. - Index signature for type 'string' is missing in type 'RequestItem'. -src/app/api/purchase/request/route.ts(143,23): error TS2352: Conversion of type 'PurchaseRequest[]' to type 'DbRow[]' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first. - Type 'PurchaseRequest' is not comparable to type 'DbRow'. - Index signature for type 'string' is missing in type 'PurchaseRequest'. -src/app/api/purchase/request/route.ts(233,26): error TS2352: Conversion of type 'QueryResult' to type 'DbRow' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first. - Type '[RowDataPacket[], ResultSetHeader]' is not comparable to type 'DbRow'. - Index signature for type 'string' is missing in type '[RowDataPacket[], ResultSetHeader]'. -src/app/api/purchase/suppliers/route.ts(130,35): error TS2352: Conversion of type 'any[]' to type 'DbRow' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first. - Index signature for type 'string' is missing in type 'any[]'. -src/app/api/purchase/suppliers/route.ts(174,27): error TS2345: Argument of type '{} | null' is not assignable to parameter of type 'SqlValue'. - Type '{}' is not assignable to type 'SqlValue'. -src/app/api/qrcode/route.ts(130,35): error TS2352: Conversion of type 'QueryResult' to type 'DbRow' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first. - Type '[RowDataPacket[], ResultSetHeader]' is not comparable to type 'DbRow'. - Index signature for type 'string' is missing in type '[RowDataPacket[], ResultSetHeader]'. -src/app/api/qrcode/route.ts(151,25): error TS2339: Property 'affectedRows' does not exist on type 'QueryResult'. - Property 'affectedRows' does not exist on type 'ResultSetHeader[]'. -src/app/api/qrcode/route.ts(247,18): error TS2769: No overload matches this call. - Overload 1 of 2, '(sql: string, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'SqlValue[]' is not assignable to parameter of type 'ExecuteValues | undefined'. - Type 'SqlValue[]' is not assignable to type 'ExecuteValues[]'. - Type 'SqlValue' is not assignable to type 'ExecuteValues'. - Type 'undefined' is not assignable to type 'ExecuteValues'. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/qrcode/trace/route.ts(51,46): error TS2339: Property 'qr_code' does not exist on type '{}'. -src/app/api/qrcode/trace/route.ts(58,13): error TS2339: Property 'qr_code' does not exist on type '{}'. -src/app/api/qrcode/trace/route.ts(63,13): error TS2339: Property 'ref_no' does not exist on type '{}'. -src/app/api/qrcode/trace/route.ts(63,28): error TS2339: Property 'batch_no' does not exist on type '{}'. -src/app/api/qrcode/trace/route.ts(63,51): error TS2339: Property 'material_code' does not exist on type '{}'. -src/app/api/qrcode/trace/route.ts(63,79): error TS2339: Property 'qr_code' does not exist on type '{}'. -src/app/api/qrcode/trace/route.ts(67,14): error TS2339: Property 'batch_no' does not exist on type '{}'. -src/app/api/qrcode/trace/route.ts(73,15): error TS2339: Property 'batch_no' does not exist on type '{}'. -src/app/api/qrcode/trace/route.ts(78,14): error TS2339: Property 'material_id' does not exist on type '{}'. -src/app/api/qrcode/trace/route.ts(81,15): error TS2339: Property 'material_id' does not exist on type '{}'. -src/app/api/qrcode/trace/route.ts(86,14): error TS2339: Property 'batch_no' does not exist on type '{}'. -src/app/api/qrcode/trace/route.ts(92,15): error TS2339: Property 'batch_no' does not exist on type '{}'. -src/app/api/qrcode/trace/route.ts(97,14): error TS2339: Property 'material_id' does not exist on type '{}'. -src/app/api/qrcode/trace/route.ts(97,36): error TS2339: Property 'qr_type' does not exist on type '{}'. -src/app/api/qrcode/trace/route.ts(103,15): error TS2339: Property 'material_id' does not exist on type '{}'. -src/app/api/qrcode/trace/route.ts(108,14): error TS2339: Property 'qr_type' does not exist on type '{}'. -src/app/api/qrcode/trace/route.ts(108,47): error TS2339: Property 'work_order_no' does not exist on type '{}'. -src/app/api/qrcode/trace/route.ts(114,15): error TS2339: Property 'work_order_no' does not exist on type '{}'. -src/app/api/qrcode/trace/route.ts(119,14): error TS2339: Property 'qr_type' does not exist on type '{}'. -src/app/api/qrcode/trace/route.ts(119,46): error TS2339: Property 'ref_no' does not exist on type '{}'. -src/app/api/qrcode/trace/route.ts(125,15): error TS2339: Property 'ref_no' does not exist on type '{}'. -src/app/api/qrcode/trace/route.ts(130,14): error TS2339: Property 'ref_no' does not exist on type '{}'. -src/app/api/qrcode/trace/route.ts(130,31): error TS2339: Property 'ref_no' does not exist on type '{}'. -src/app/api/qrcode/trace/route.ts(132,14): error TS2339: Property 'ref_no' does not exist on type '{}'. -src/app/api/qrcode/trace/route.ts(135,14): error TS2339: Property 'ref_no' does not exist on type '{}'. -src/app/api/qrcode/trace/route.ts(135,31): error TS2339: Property 'ref_no' does not exist on type '{}'. -src/app/api/qrcode/trace/route.ts(137,14): error TS2339: Property 'ref_no' does not exist on type '{}'. -src/app/api/qrcode/trace/route.ts(140,14): error TS2339: Property 'work_order_no' does not exist on type '{}'. -src/app/api/qrcode/trace/route.ts(143,15): error TS2339: Property 'work_order_no' does not exist on type '{}'. -src/app/api/qrcode/trace/route.ts(148,14): error TS2339: Property 'ref_no' does not exist on type '{}'. -src/app/api/qrcode/trace/route.ts(151,15): error TS2339: Property 'batch_no' does not exist on type '{}'. -src/app/api/qrcode/trace/route.ts(151,38): error TS2339: Property 'material_code' does not exist on type '{}'. -src/app/api/qrcode/trace/route.ts(158,34): error TS2345: Argument of type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to parameter of type 'string'. - Type 'undefined' is not assignable to type 'string'. -src/app/api/qrcode/trace/route.ts(163,43): error TS2769: No overload matches this call. - Overload 1 of 4, '(value: string | number | Date): Date', gave the following error. - Argument of type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to parameter of type 'string | number | Date'. - Type 'undefined' is not assignable to type 'string | number | Date'. - Overload 2 of 4, '(value: string | number): Date', gave the following error. - Argument of type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to parameter of type 'string | number'. - Type 'undefined' is not assignable to type 'string | number'. -src/app/api/qrcode/trace/route.ts(163,72): error TS2769: No overload matches this call. - Overload 1 of 4, '(value: string | number | Date): Date', gave the following error. - Argument of type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to parameter of type 'string | number | Date'. - Type 'undefined' is not assignable to type 'string | number | Date'. - Overload 2 of 4, '(value: string | number): Date', gave the following error. - Argument of type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to parameter of type 'string | number'. - Type 'undefined' is not assignable to type 'string | number'. -src/app/api/qrcode/trace/route.ts(165,14): error TS2339: Property 'create_time' does not exist on type '{}'. -src/app/api/qrcode/trace/route.ts(167,20): error TS2339: Property 'create_time' does not exist on type '{}'. -src/app/api/qrcode/trace/route.ts(168,39): error TS2339: Property 'qr_type' does not exist on type '{}'. -src/app/api/qrcode/trace/route.ts(171,30): error TS2339: Property 'qr_code' does not exist on type '{}'. -src/app/api/qrcode/trace/route.ts(177,23): error TS2339: Property 'create_time' does not exist on type '{}'. -src/app/api/qrcode/trace/route.ts(177,48): error TS2339: Property 'created_at' does not exist on type '{}'. -src/app/api/qrcode/trace/route.ts(181,29): error TS2339: Property 'batch_no' does not exist on type '{}'. -src/app/api/qrcode/trace/route.ts(181,58): error TS2339: Property 'quantity' does not exist on type '{}'. -src/app/api/qrcode/trace/route.ts(181,84): error TS2339: Property 'available_qty' does not exist on type '{}'. -src/app/api/qrcode/trace/route.ts(185,34): error TS2339: Property 'length' does not exist on type '{}'. -src/app/api/qrcode/trace/route.ts(186,17): error TS2339: Property 'forEach' does not exist on type '{}'. -src/app/api/qrcode/trace/route.ts(197,50): error TS2769: No overload matches this call. - Overload 1 of 4, '(value: string | number | Date): Date', gave the following error. - Argument of type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to parameter of type 'string | number | Date'. - Type 'undefined' is not assignable to type 'string | number | Date'. - Overload 2 of 4, '(value: string | number): Date', gave the following error. - Argument of type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to parameter of type 'string | number'. - Type 'undefined' is not assignable to type 'string | number'. -src/app/api/qrcode/trace/route.ts(197,79): error TS2769: No overload matches this call. - Overload 1 of 4, '(value: string | number | Date): Date', gave the following error. - Argument of type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to parameter of type 'string | number | Date'. - Type 'undefined' is not assignable to type 'string | number | Date'. - Overload 2 of 4, '(value: string | number): Date', gave the following error. - Argument of type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to parameter of type 'string | number'. - Type 'undefined' is not assignable to type 'string | number'. -src/app/api/qrcode/trace/route.ts(213,57): error TS2339: Property 'qr_code' does not exist on type '{}'. -src/app/api/quality/final/route.ts(17,64): error TS2345: Argument of type 'DbRow[]' is not assignable to parameter of type 'SqlValue[]'. - Type 'DbRow' is not assignable to type 'SqlValue'. - Type 'DbRow' is missing the following properties from type 'Buffer': slice, subarray, write, toJSON, and 102 more. -src/app/api/quality/final/route.ts(22,42): error TS2345: Argument of type '(number | DbRow)[]' is not assignable to parameter of type 'SqlValue[]'. - Type 'number | DbRow' is not assignable to type 'SqlValue'. - Type 'DbRow' is not assignable to type 'SqlValue'. - Type 'DbRow' is missing the following properties from type 'Buffer': slice, subarray, write, toJSON, and 102 more. -src/app/api/quality/final/route.ts(93,59): error TS2345: Argument of type 'SqlValue[]' is not assignable to parameter of type 'DbRow[]'. - Type 'SqlValue' is not assignable to type 'DbRow'. - Type 'undefined' is not assignable to type 'DbRow'. -src/app/api/quality/incoming/route.ts(164,49): error TS2339: Property 'slice' does not exist on type 'string | number | true | Date | Buffer'. - Property 'slice' does not exist on type 'number'. -src/app/api/quality/incoming/route.ts(192,29): error TS2352: Conversion of type 'unknown[]' to type 'DbRow' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first. - Index signature for type 'string' is missing in type 'unknown[]'. -src/app/api/quality/process/route.ts(18,64): error TS2345: Argument of type 'DbRow[]' is not assignable to parameter of type 'SqlValue[]'. - Type 'DbRow' is not assignable to type 'SqlValue'. - Type 'DbRow' is missing the following properties from type 'Buffer': slice, subarray, write, toJSON, and 102 more. -src/app/api/quality/process/route.ts(23,42): error TS2345: Argument of type '(number | DbRow)[]' is not assignable to parameter of type 'SqlValue[]'. - Type 'number | DbRow' is not assignable to type 'SqlValue'. - Type 'DbRow' is not assignable to type 'SqlValue'. - Type 'DbRow' is missing the following properties from type 'Buffer': slice, subarray, write, toJSON, and 102 more. -src/app/api/quality/process/route.ts(111,59): error TS2345: Argument of type 'SqlValue[]' is not assignable to parameter of type 'DbRow[]'. - Type 'SqlValue' is not assignable to type 'DbRow'. - Type 'undefined' is not assignable to type 'DbRow'. -src/app/api/quality/sgs/expiry-warning/route.ts(35,29): error TS2345: Argument of type '(r: DbRow) => string | number | boolean | Date | Buffer | null | undefined' is not assignable to parameter of type '(value: SqlValue, index: number, array: SqlValue[]) => string | number | boolean | Date | Buffer | null | undefined'. - Types of parameters 'r' and 'value' are incompatible. - Type 'SqlValue' is not assignable to type 'DbRow'. - Type 'undefined' is not assignable to type 'DbRow'. -src/app/api/quality/spc/route.ts(65,7): error TS2345: Argument of type '{ defect_type: string | number | true | Date | Buffer; count: number; }[]' is not assignable to parameter of type '{ defect_type: string; count: number; }[]'. - Type '{ defect_type: string | number | true | Date | Buffer; count: number; }' is not assignable to type '{ defect_type: string; count: number; }'. - Types of property 'defect_type' are incompatible. - Type 'string | number | true | Date | Buffer' is not assignable to type 'string'. - Type 'number' is not assignable to type 'string'. -src/app/api/quality/spc/route.ts(94,7): error TS2345: Argument of type '{ period: string | number | boolean | Date | Buffer | null | undefined; inspected: number; defective: number; }[]' is not assignable to parameter of type '{ period: string; inspected: number; defective: number; }[]'. - Type '{ period: string | number | boolean | Date | Buffer | null | undefined; inspected: number; defective: number; }' is not assignable to type '{ period: string; inspected: number; defective: number; }'. - Types of property 'period' are incompatible. - Type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to type 'string'. - Type 'undefined' is not assignable to type 'string'. -src/app/api/reports/delivery-rate/route.ts(47,31): error TS2345: Argument of type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to parameter of type 'string'. - Type 'undefined' is not assignable to type 'string'. -src/app/api/reports/delivery-rate/route.ts(48,35): error TS2345: Argument of type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to parameter of type 'string'. - Type 'undefined' is not assignable to type 'string'. -src/app/api/reports/delivery-rate/route.ts(50,9): error TS18049: 'row.total_orders' is possibly 'null' or 'undefined'. -src/app/api/reports/delivery-rate/route.ts(50,9): error TS2365: Operator '>' cannot be applied to types 'string | number | boolean | Date | Buffer' and 'number'. -src/app/api/reports/delivery-rate/route.ts(50,44): error TS2362: The left-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. -src/app/api/reports/delivery-rate/route.ts(50,44): error TS18049: 'row.completed_orders' is possibly 'null' or 'undefined'. -src/app/api/reports/delivery-rate/route.ts(50,67): error TS2363: The right-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. -src/app/api/reports/delivery-rate/route.ts(50,67): error TS18049: 'row.total_orders' is possibly 'null' or 'undefined'. -src/app/api/reports/delivery-rate/route.ts(52,9): error TS18049: 'row.total_orders' is possibly 'null' or 'undefined'. -src/app/api/reports/delivery-rate/route.ts(52,9): error TS2365: Operator '>' cannot be applied to types 'string | number | boolean | Date | Buffer' and 'number'. -src/app/api/reports/delivery-rate/route.ts(52,44): error TS2362: The left-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. -src/app/api/reports/delivery-rate/route.ts(52,72): error TS2363: The right-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. -src/app/api/reports/delivery-rate/route.ts(52,72): error TS18049: 'row.total_orders' is possibly 'null' or 'undefined'. -src/app/api/reports/delivery-rate/route.ts(59,65): error TS2365: Operator '+' cannot be applied to types 'number' and 'string | number | boolean | Date | Buffer'. -src/app/api/reports/delivery-rate/route.ts(59,71): error TS18049: 'r.totalOrders' is possibly 'null' or 'undefined'. -src/app/api/reports/delivery-rate/route.ts(60,69): error TS2365: Operator '+' cannot be applied to types 'number' and 'string | number | boolean | Date | Buffer'. -src/app/api/reports/delivery-rate/route.ts(60,75): error TS18049: 'r.completedOrders' is possibly 'null' or 'undefined'. -src/app/api/reports/delivery-rate/route.ts(64,60): error TS2365: Operator '+' cannot be applied to types 'number' and 'string | number | boolean | Date | Buffer'. -src/app/api/reports/delivery-rate/route.ts(64,66): error TS18049: 'r.deliveryRate' is possibly 'null' or 'undefined'. -src/app/api/reports/delivery-rate/route.ts(70,60): error TS2365: Operator '+' cannot be applied to types 'number' and 'string | number | boolean | Date | Buffer'. -src/app/api/reports/delivery-rate/route.ts(70,66): error TS18049: 'r.onTimeRate' is possibly 'null' or 'undefined'. -src/app/api/reports/delivery-rate/route.ts(102,31): error TS2345: Argument of type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to parameter of type 'string'. - Type 'undefined' is not assignable to type 'string'. -src/app/api/reports/delivery-rate/route.ts(104,9): error TS18049: 'row.total_orders' is possibly 'null' or 'undefined'. -src/app/api/reports/delivery-rate/route.ts(104,9): error TS2365: Operator '>' cannot be applied to types 'string | number | boolean | Date | Buffer' and 'number'. -src/app/api/reports/delivery-rate/route.ts(104,44): error TS2362: The left-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. -src/app/api/reports/delivery-rate/route.ts(104,44): error TS18049: 'row.completed_orders' is possibly 'null' or 'undefined'. -src/app/api/reports/delivery-rate/route.ts(104,67): error TS2363: The right-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. -src/app/api/reports/delivery-rate/route.ts(104,67): error TS18049: 'row.total_orders' is possibly 'null' or 'undefined'. -src/app/api/reports/delivery-rate/route.ts(106,9): error TS18049: 'row.total_orders' is possibly 'null' or 'undefined'. -src/app/api/reports/delivery-rate/route.ts(106,9): error TS2365: Operator '>' cannot be applied to types 'string | number | boolean | Date | Buffer' and 'number'. -src/app/api/reports/delivery-rate/route.ts(106,44): error TS2362: The left-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. -src/app/api/reports/delivery-rate/route.ts(106,72): error TS2363: The right-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. -src/app/api/reports/delivery-rate/route.ts(106,72): error TS18049: 'row.total_orders' is possibly 'null' or 'undefined'. -src/app/api/reports/delivery-rate/route.ts(114,65): error TS2365: Operator '+' cannot be applied to types 'number' and 'string | number | boolean | Date | Buffer'. -src/app/api/reports/delivery-rate/route.ts(114,71): error TS18049: 'r.totalOrders' is possibly 'null' or 'undefined'. -src/app/api/reports/delivery-rate/route.ts(118,60): error TS2365: Operator '+' cannot be applied to types 'number' and 'string | number | boolean | Date | Buffer'. -src/app/api/reports/delivery-rate/route.ts(118,66): error TS18049: 'r.deliveryRate' is possibly 'null' or 'undefined'. -src/app/api/reports/inventory-turnover/route.ts(51,35): error TS2345: Argument of type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to parameter of type 'string'. - Type 'undefined' is not assignable to type 'string'. -src/app/api/reports/inventory-turnover/route.ts(52,38): error TS2345: Argument of type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to parameter of type 'string'. - Type 'undefined' is not assignable to type 'string'. -src/app/api/reports/inventory-turnover/route.ts(63,32): error TS2345: Argument of type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to parameter of type 'string'. - Type 'undefined' is not assignable to type 'string'. -src/app/api/reports/inventory-turnover/route.ts(65,30): error TS2345: Argument of type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to parameter of type 'string'. - Type 'undefined' is not assignable to type 'string'. -src/app/api/reports/inventory-turnover/route.ts(66,34): error TS2345: Argument of type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to parameter of type 'string'. - Type 'undefined' is not assignable to type 'string'. -src/app/api/reports/inventory-turnover/route.ts(67,31): error TS2345: Argument of type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to parameter of type 'string'. - Type 'undefined' is not assignable to type 'string'. -src/app/api/reports/inventory-turnover/route.ts(68,34): error TS2345: Argument of type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to parameter of type 'string'. - Type 'undefined' is not assignable to type 'string'. -src/app/api/reports/inventory-turnover/route.ts(79,66): error TS2365: Operator '+' cannot be applied to types 'number' and 'string | number | boolean | Date | Buffer'. -src/app/api/reports/inventory-turnover/route.ts(79,72): error TS18049: 'r.inboundQty' is possibly 'null' or 'undefined'. -src/app/api/reports/inventory-turnover/route.ts(80,67): error TS2365: Operator '+' cannot be applied to types 'number' and 'string | number | boolean | Date | Buffer'. -src/app/api/reports/inventory-turnover/route.ts(80,73): error TS18049: 'r.outboundQty' is possibly 'null' or 'undefined'. -src/app/api/reports/inventory-turnover/route.ts(84,61): error TS2365: Operator '+' cannot be applied to types 'number' and 'string | number | boolean | Date | Buffer'. -src/app/api/reports/inventory-turnover/route.ts(84,67): error TS18049: 'r.turnoverRate' is possibly 'null' or 'undefined'. -src/app/api/reports/inventory-turnover/route.ts(116,37): error TS2345: Argument of type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to parameter of type 'string'. - Type 'undefined' is not assignable to type 'string'. -src/app/api/reports/inventory-turnover/route.ts(117,38): error TS2345: Argument of type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to parameter of type 'string'. - Type 'undefined' is not assignable to type 'string'. -src/app/api/reports/inventory-turnover/route.ts(125,32): error TS2345: Argument of type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to parameter of type 'string'. - Type 'undefined' is not assignable to type 'string'. -src/app/api/reports/inventory-turnover/route.ts(126,33): error TS2345: Argument of type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to parameter of type 'string'. - Type 'undefined' is not assignable to type 'string'. -src/app/api/reports/inventory-turnover/route.ts(127,36): error TS2345: Argument of type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to parameter of type 'string'. - Type 'undefined' is not assignable to type 'string'. -src/app/api/reports/inventory-turnover/route.ts(128,32): error TS2345: Argument of type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to parameter of type 'string'. - Type 'undefined' is not assignable to type 'string'. -src/app/api/reports/inventory-turnover/route.ts(139,64): error TS2365: Operator '+' cannot be applied to types 'number' and 'string | number | boolean | Date | Buffer'. -src/app/api/reports/inventory-turnover/route.ts(139,70): error TS18049: 'r.totalStock' is possibly 'null' or 'undefined'. -src/app/api/reports/inventory-turnover/route.ts(140,67): error TS2365: Operator '+' cannot be applied to types 'number' and 'string | number | boolean | Date | Buffer'. -src/app/api/reports/inventory-turnover/route.ts(140,73): error TS18049: 'r.outboundQty' is possibly 'null' or 'undefined'. -src/app/api/reports/production-cost/route.ts(46,39): error TS2345: Argument of type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to parameter of type 'string'. - Type 'undefined' is not assignable to type 'string'. -src/app/api/reports/production-cost/route.ts(47,37): error TS2345: Argument of type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to parameter of type 'string'. - Type 'undefined' is not assignable to type 'string'. -src/app/api/reports/production-cost/route.ts(55,29): error TS2345: Argument of type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to parameter of type 'string'. - Type 'undefined' is not assignable to type 'string'. -src/app/api/reports/production-cost/route.ts(56,34): error TS2345: Argument of type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to parameter of type 'string'. - Type 'undefined' is not assignable to type 'string'. -src/app/api/reports/production-cost/route.ts(59,34): error TS2345: Argument of type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to parameter of type 'string'. - Type 'undefined' is not assignable to type 'string'. -src/app/api/reports/production-cost/route.ts(60,31): error TS2345: Argument of type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to parameter of type 'string'. - Type 'undefined' is not assignable to type 'string'. -src/app/api/reports/production-cost/route.ts(61,34): error TS2345: Argument of type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to parameter of type 'string'. - Type 'undefined' is not assignable to type 'string'. -src/app/api/reports/production-cost/route.ts(71,69): error TS2365: Operator '+' cannot be applied to types 'number' and 'string | number | boolean | Date | Buffer'. -src/app/api/reports/production-cost/route.ts(71,75): error TS18049: 'r.workOrderCount' is possibly 'null' or 'undefined'. -src/app/api/reports/production-cost/route.ts(72,71): error TS2365: Operator '+' cannot be applied to types 'number' and 'string | number | boolean | Date | Buffer'. -src/app/api/reports/production-cost/route.ts(72,77): error TS18049: 'r.standardCost' is possibly 'null' or 'undefined'. -src/app/api/reports/production-cost/route.ts(73,69): error TS2365: Operator '+' cannot be applied to types 'number' and 'string | number | boolean | Date | Buffer'. -src/app/api/reports/production-cost/route.ts(73,75): error TS18049: 'r.actualCost' is possibly 'null' or 'undefined'. -src/app/api/reports/production-cost/route.ts(74,67): error TS2365: Operator '+' cannot be applied to types 'number' and 'string | number | boolean | Date | Buffer'. -src/app/api/reports/production-cost/route.ts(74,73): error TS18049: 'r.costVariance' is possibly 'null' or 'undefined'. -src/app/api/reports/production-cost/route.ts(78,60): error TS2365: Operator '+' cannot be applied to types 'number' and 'string | number | boolean | Date | Buffer'. -src/app/api/reports/production-cost/route.ts(78,66): error TS18049: 'r.costVarianceRate' is possibly 'null' or 'undefined'. -src/app/api/reports/production-cost/route.ts(108,39): error TS2345: Argument of type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to parameter of type 'string'. - Type 'undefined' is not assignable to type 'string'. -src/app/api/reports/production-cost/route.ts(109,37): error TS2345: Argument of type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to parameter of type 'string'. - Type 'undefined' is not assignable to type 'string'. -src/app/api/reports/production-cost/route.ts(114,9): error TS18049: 'row.total_plan_qty' is possibly 'null' or 'undefined'. -src/app/api/reports/production-cost/route.ts(114,9): error TS2365: Operator '>' cannot be applied to types 'string | number | boolean | Date | Buffer' and 'number'. -src/app/api/reports/production-cost/route.ts(114,60): error TS2345: Argument of type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to parameter of type 'string'. - Type 'undefined' is not assignable to type 'string'. -src/app/api/reports/production-cost/route.ts(116,9): error TS18049: 'row.total_completed_qty' is possibly 'null' or 'undefined'. -src/app/api/reports/production-cost/route.ts(116,9): error TS2365: Operator '>' cannot be applied to types 'string | number | boolean | Date | Buffer' and 'number'. -src/app/api/reports/production-cost/route.ts(116,63): error TS2345: Argument of type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to parameter of type 'string'. - Type 'undefined' is not assignable to type 'string'. -src/app/api/reports/production-cost/route.ts(123,29): error TS2345: Argument of type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to parameter of type 'string'. - Type 'undefined' is not assignable to type 'string'. -src/app/api/reports/production-cost/route.ts(124,34): error TS2345: Argument of type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to parameter of type 'string'. - Type 'undefined' is not assignable to type 'string'. -src/app/api/reports/production-cost/route.ts(139,71): error TS2365: Operator '+' cannot be applied to types 'number' and 'string | number | boolean | Date | Buffer'. -src/app/api/reports/production-cost/route.ts(139,77): error TS18049: 'r.standardCost' is possibly 'null' or 'undefined'. -src/app/api/reports/production-cost/route.ts(140,69): error TS2365: Operator '+' cannot be applied to types 'number' and 'string | number | boolean | Date | Buffer'. -src/app/api/reports/production-cost/route.ts(140,75): error TS18049: 'r.actualCost' is possibly 'null' or 'undefined'. -src/app/api/reports/production-cost/route.ts(141,67): error TS2365: Operator '+' cannot be applied to types 'number' and 'string | number | boolean | Date | Buffer'. -src/app/api/reports/production-cost/route.ts(141,73): error TS18049: 'r.costVariance' is possibly 'null' or 'undefined'. -src/app/api/sales/delivery/[id]/ship/route.ts(127,43): error TS2345: Argument of type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to parameter of type 'string'. - Type 'undefined' is not assignable to type 'string'. -src/app/api/sales/delivery/[id]/ship/route.ts(128,55): error TS2345: Argument of type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to parameter of type 'string'. - Type 'undefined' is not assignable to type 'string'. -src/app/api/sales/delivery/[id]/ship/route.ts(140,20): error TS2769: No overload matches this call. - Overload 1 of 2, '(sql: string, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to type 'ExecuteValues'. - Type 'undefined' is not assignable to type 'ExecuteValues'. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/sales/delivery/[id]/ship/route.ts(152,13): error TS2769: No overload matches this call. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/sales/delivery/partial/route.ts(47,39): error TS2345: Argument of type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to parameter of type 'string'. - Type 'undefined' is not assignable to type 'string'. -src/app/api/sales/delivery/partial/route.ts(48,42): error TS2345: Argument of type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to parameter of type 'string'. - Type 'undefined' is not assignable to type 'string'. -src/app/api/sales/delivery/re-ship/route.ts(43,24): error TS2339: Property 'sales_order_id' does not exist on type '{}'. -src/app/api/sales/delivery/re-ship/route.ts(44,24): error TS2339: Property 'customer_id' does not exist on type '{}'. -src/app/api/sales/delivery/re-ship/route.ts(45,24): error TS2339: Property 'customer_name' does not exist on type '{}'. -src/app/api/sales/delivery/re-ship/route.ts(46,24): error TS2339: Property 'warehouse_id' does not exist on type '{}'. -src/app/api/sales/delivery/re-ship/route.ts(68,13): error TS18046: 'item' is of type 'unknown'. -src/app/api/sales/delivery/re-ship/route.ts(69,13): error TS18046: 'item' is of type 'unknown'. -src/app/api/sales/delivery/re-ship/route.ts(70,13): error TS18046: 'item' is of type 'unknown'. -src/app/api/sales/delivery/re-ship/route.ts(71,44): error TS18046: 'item' is of type 'unknown'. -src/app/api/sales/delivery/re-ship/route.ts(72,13): error TS18046: 'item' is of type 'unknown'. -src/app/api/sales/delivery/route.ts(171,26): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/sales/reconciliation/route.ts(115,37): error TS2345: Argument of type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to parameter of type 'string'. - Type 'undefined' is not assignable to type 'string'. -src/app/api/sales/reconciliation/route.ts(118,71): error TS2345: Argument of type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to parameter of type 'string'. - Type 'undefined' is not assignable to type 'string'. -src/app/api/sales/reconciliation/route.ts(120,11): error TS2322: Type '({ sourceType: 1; sourceId: string | number | boolean | Date | Buffer | null | undefined; sourceNo: string | number | boolean | Date | Buffer<...> | null | undefined; sourceDate: string | ... 5 more ... | undefined; amount: number; } | { ...; })[]' is not assignable to type 'ReconciliationLineProps[]'. - Type '{ sourceType: 1; sourceId: string | number | boolean | Date | Buffer | null | undefined; sourceNo: string | number | boolean | Date | Buffer<...> | null | undefined; sourceDate: string | ... 5 more ... | undefined; amount: number; } | { ...; }' is not assignable to type 'ReconciliationLineProps'. - Type '{ sourceType: 1; sourceId: string | number | boolean | Date | Buffer | null | undefined; sourceNo: string | number | boolean | Date | Buffer | null | undefined; sourceDate: string | ... 5 more ... | undefined; amount: number; }' is not assignable to type 'ReconciliationLineProps'. - Types of property 'sourceId' are incompatible. - Type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to type 'number'. - Type 'undefined' is not assignable to type 'number'. -src/app/api/sales/reconciliation/route.ts(126,28): error TS2345: Argument of type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to parameter of type 'string'. - Type 'undefined' is not assignable to type 'string'. -src/app/api/sales/reconciliation/route.ts(133,28): error TS2345: Argument of type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to parameter of type 'string'. - Type 'undefined' is not assignable to type 'string'. -src/app/api/sales/return/route.ts(150,24): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/sample/feedback/route.ts(29,17): error TS1196: Catch clause variable type annotation must be 'any' or 'unknown' if specified. -src/app/api/sample/feedback/route.ts(35,3): error TS2345: Argument of type '(request: NextRequest, userInfo: DbRow) => Promise> | NextResponse>>' is not assignable to parameter of type '(request: NextRequest, userInfo: UserInfo, context?: any) => Promise>'. - Types of parameters 'userInfo' and 'userInfo' are incompatible. - Type 'UserInfo' is not assignable to type 'DbRow'. - Index signature for type 'string' is missing in type 'UserInfo'. -src/app/api/sample/feedback/route.ts(50,9): error TS2322: Type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to type 'number | undefined'. - Type 'null' is not assignable to type 'number | undefined'. -src/app/api/sample/feedback/route.ts(54,19): error TS1196: Catch clause variable type annotation must be 'any' or 'unknown' if specified. -src/app/api/sample/feedback/route.ts(79,19): error TS1196: Catch clause variable type annotation must be 'any' or 'unknown' if specified. -src/app/api/sample/inventory/route.ts(123,23): error TS2345: Argument of type '{} | null' is not assignable to parameter of type 'SqlValue'. - Type '{}' is not assignable to type 'SqlValue'. -src/app/api/sample/orders/linkage/route.ts(30,24): error TS2723: Cannot invoke an object which is possibly 'null' or 'undefined'. -src/app/api/sample/orders/linkage/route.ts(30,24): error TS18049: 'conn.query' is possibly 'null' or 'undefined'. -src/app/api/sample/orders/linkage/route.ts(30,29): error TS2349: This expression is not callable. - No constituent of type 'string | number | boolean | Date | Buffer' is callable. -src/app/api/sample/orders/linkage/route.ts(34,19): error TS2365: Operator '+' cannot be applied to types 'string | number | boolean | Date | Buffer' and 'number'. -src/app/api/sample/orders/linkage/route.ts(78,11): error TS2533: Object is possibly 'null' or 'undefined'. -src/app/api/sample/orders/linkage/route.ts(78,11): error TS2365: Operator '>' cannot be applied to types 'string | number | boolean | Date | Buffer' and 'number'. -src/app/api/sample/orders/linkage/route.ts(82,53): error TS2345: Argument of type 'PoolConnection' is not assignable to parameter of type 'DbRow'. - Index signature for type 'string' is missing in type 'PoolConnection'. -src/app/api/sample/orders/linkage/route.ts(107,28): error TS2352: Conversion of type 'QueryResult' to type 'DbRow' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first. - Type '[RowDataPacket[], ResultSetHeader]' is not comparable to type 'DbRow'. - Index signature for type 'string' is missing in type '[RowDataPacket[], ResultSetHeader]'. -src/app/api/sample/orders/linkage/route.ts(110,24): error TS2769: No overload matches this call. - Overload 1 of 2, '(sql: string, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to type 'ExecuteValues'. - Type 'undefined' is not assignable to type 'ExecuteValues'. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/sample/orders/linkage/route.ts(129,34): error TS2769: No overload matches this call. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/sample/orders/linkage/route.ts(143,13): error TS2363: The right-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. -src/app/api/sample/orders/linkage/route.ts(146,13): error TS2769: No overload matches this call. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/sample/orders/route.ts(63,5): error TS18046: 'props' is of type 'unknown'. -src/app/api/sample/orders/route.ts(72,5): error TS18046: 'result' is of type 'unknown'. -src/app/api/sample/orders/status/route.ts(12,3): error TS2345: Argument of type '(request: NextRequest, userInfo: DbRow) => Promise> | NextResponse>>' is not assignable to parameter of type '(request: NextRequest, userInfo: UserInfo, context?: any) => Promise>'. - Types of parameters 'userInfo' and 'userInfo' are incompatible. - Type 'UserInfo' is not assignable to type 'DbRow'. - Index signature for type 'string' is missing in type 'UserInfo'. -src/app/api/sample/orders/status/route.ts(28,41): error TS2345: Argument of type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to parameter of type 'number'. - Type 'undefined' is not assignable to type 'number'. -src/app/api/sample/orders/status/route.ts(31,45): error TS2345: Argument of type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to parameter of type 'number'. - Type 'undefined' is not assignable to type 'number'. -src/app/api/sample/orders/status/route.ts(34,43): error TS2345: Argument of type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to parameter of type 'number'. - Type 'undefined' is not assignable to type 'number'. -src/app/api/sample/orders/status/route.ts(37,42): error TS2345: Argument of type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to parameter of type 'number'. - Type 'undefined' is not assignable to type 'number'. -src/app/api/sample/orders/status/route.ts(43,79): error TS2345: Argument of type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to parameter of type 'number'. - Type 'undefined' is not assignable to type 'number'. -src/app/api/sample/orders/status/route.ts(45,62): error TS2345: Argument of type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to parameter of type 'number'. - Type 'undefined' is not assignable to type 'number'. -src/app/api/sample/orders/status/route.ts(49,59): error TS2345: Argument of type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to parameter of type 'number'. - Type 'undefined' is not assignable to type 'number'. -src/app/api/sample/orders/status/route.ts(57,19): error TS1196: Catch clause variable type annotation must be 'any' or 'unknown' if specified. -src/app/api/screen-plates/history/route.ts(87,42): error TS2352: Conversion of type 'ResultSetHeader' to type 'DbRow' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first. - Index signature for type 'string' is missing in type 'ResultSetHeader'. -src/app/api/screen-plates/route.ts(140,22): error TS2352: Conversion of type 'ResultSetHeader' to type 'DbRow' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first. - Index signature for type 'string' is missing in type 'ResultSetHeader'. -src/app/api/settings/category-linkage/route.ts(109,9): error TS2322: Type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to type 'string'. - Type 'undefined' is not assignable to type 'string'. -src/app/api/settings/category-linkage/route.ts(110,9): error TS2322: Type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to type 'string'. - Type 'undefined' is not assignable to type 'string'. -src/app/api/settings/category-linkage/route.ts(142,9): error TS2322: Type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to type 'string | null'. - Type 'undefined' is not assignable to type 'string | null'. -src/app/api/settings/category-linkage/route.ts(143,9): error TS2322: Type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to type 'string | null'. - Type 'undefined' is not assignable to type 'string | null'. -src/app/api/settings/category-rules/route.ts(107,40): error TS2345: Argument of type 'string | number | boolean | Date | Buffer' is not assignable to parameter of type 'string'. - Type 'number' is not assignable to type 'string'. -src/app/api/settings/category-rules/route.ts(111,11): error TS2322: Type 'string | number | boolean | Date | Buffer' is not assignable to type 'string'. - Type 'number' is not assignable to type 'string'. -src/app/api/settings/category-rules/route.ts(114,11): error TS2322: Type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to type 'string'. - Type 'undefined' is not assignable to type 'string'. -src/app/api/settings/category-rules/route.ts(127,11): error TS2322: Type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to type 'string'. - Type 'undefined' is not assignable to type 'string'. -src/app/api/settings/category-rules/route.ts(166,13): error TS2322: Type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to type 'string'. - Type 'undefined' is not assignable to type 'string'. -src/app/api/settings/category-rules/route.ts(177,13): error TS2322: Type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to type 'string'. - Type 'undefined' is not assignable to type 'string'. -src/app/api/settings/system/route.ts(870,28): error TS2345: Argument of type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to parameter of type 'string'. - Type 'undefined' is not assignable to type 'string'. -src/app/api/settings/system/route.ts(871,25): error TS2345: Argument of type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to parameter of type 'string'. - Type 'undefined' is not assignable to type 'string'. -src/app/api/settings/system/route.ts(884,30): error TS2345: Argument of type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to parameter of type 'string'. - Type 'undefined' is not assignable to type 'string'. -src/app/api/settings/system/route.ts(885,23): error TS2345: Argument of type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to parameter of type 'string'. - Type 'undefined' is not assignable to type 'string'. -src/app/api/settings/system/route.ts(887,18): error TS2538: Type 'Buffer' cannot be used as an index type. -src/app/api/settings/system/route.ts(887,18): error TS2538: Type 'Date' cannot be used as an index type. -src/app/api/settings/system/route.ts(887,18): error TS2538: Type 'false' cannot be used as an index type. -src/app/api/settings/system/route.ts(887,18): error TS2538: Type 'null' cannot be used as an index type. -src/app/api/settings/system/route.ts(887,18): error TS2538: Type 'true' cannot be used as an index type. -src/app/api/settings/system/route.ts(887,18): error TS2538: Type 'undefined' cannot be used as an index type. -src/app/api/settings/system/route.ts(888,15): error TS2538: Type 'Buffer' cannot be used as an index type. -src/app/api/settings/system/route.ts(888,15): error TS2538: Type 'Date' cannot be used as an index type. -src/app/api/settings/system/route.ts(888,15): error TS2538: Type 'false' cannot be used as an index type. -src/app/api/settings/system/route.ts(888,15): error TS2538: Type 'null' cannot be used as an index type. -src/app/api/settings/system/route.ts(888,15): error TS2538: Type 'true' cannot be used as an index type. -src/app/api/settings/system/route.ts(888,15): error TS2538: Type 'undefined' cannot be used as an index type. -src/app/api/settings/system/route.ts(890,13): error TS2538: Type 'Buffer' cannot be used as an index type. -src/app/api/settings/system/route.ts(890,13): error TS2538: Type 'Date' cannot be used as an index type. -src/app/api/settings/system/route.ts(890,13): error TS2538: Type 'false' cannot be used as an index type. -src/app/api/settings/system/route.ts(890,13): error TS2538: Type 'null' cannot be used as an index type. -src/app/api/settings/system/route.ts(890,13): error TS2538: Type 'true' cannot be used as an index type. -src/app/api/settings/system/route.ts(890,13): error TS2538: Type 'undefined' cannot be used as an index type. -src/app/api/standard-cards/by-material/[material_id]/route.ts(84,7): error TS2322: Type 'ColorStandardItem[]' is not assignable to type 'SqlValue[]'. - Type 'ColorStandardItem' is not assignable to type 'SqlValue'. -src/app/api/standard-cards/by-material/[material_id]/route.ts(90,7): error TS2322: Type 'ProcessStandardItem[]' is not assignable to type 'SqlValue[]'. - Type 'ProcessStandardItem' is not assignable to type 'SqlValue'. -src/app/api/standard-cards/by-material/[material_id]/route.ts(96,7): error TS2322: Type 'QualityStandardItem[]' is not assignable to type 'SqlValue[]'. - Type 'QualityStandardItem' is not assignable to type 'SqlValue'. -src/app/api/standard-cards/by-material/[material_id]/route.ts(114,7): error TS2322: Type '((ColorStandardItem & { item_type: string; }) | (ProcessStandardItem & { item_type: string; }) | (QualityStandardItem & { ...; }))[]' is not assignable to type 'SqlValue[]'. - Type '(ColorStandardItem & { item_type: string; }) | (ProcessStandardItem & { item_type: string; }) | (QualityStandardItem & { ...; })' is not assignable to type 'SqlValue'. - Type 'ColorStandardItem & { item_type: string; }' is not assignable to type 'SqlValue'. -src/app/api/standard-cards/check-deviation/route.ts(53,11): error TS2353: Object literal may only specify known properties, and 'parameter_name' does not exist in type 'Date | Buffer | readonly SqlValue[]'. -src/app/api/standard-cards/check-deviation/route.ts(83,9): error TS2353: Object literal may only specify known properties, and 'parameter_name' does not exist in type 'Date | Buffer | readonly SqlValue[]'. -src/app/api/standard-cards/route.ts(173,21): error TS2352: Conversion of type 'StandardCard' to type 'DbRow' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first. - Index signature for type 'string' is missing in type 'StandardCard'. -src/app/api/standard-cards/route.ts(330,32): error TS2769: No overload matches this call. - Overload 1 of 2, '(o: {}): string[]', gave the following error. - Argument of type 'unknown' is not assignable to parameter of type '{}'. - Overload 2 of 2, '(o: object): string[]', gave the following error. - Argument of type 'unknown' is not assignable to parameter of type 'object'. -src/app/api/standard-cards/route.ts(331,34): error TS2769: No overload matches this call. - Overload 1 of 2, '(o: ArrayLike | { [s: string]: unknown; }): unknown[]', gave the following error. - Argument of type 'unknown' is not assignable to parameter of type 'ArrayLike | { [s: string]: unknown; }'. - Overload 2 of 2, '(o: {}): any[]', gave the following error. - Argument of type 'unknown' is not assignable to parameter of type '{}'. -src/app/api/standard-cards/route.ts(484,11): error TS18046: 'updateData' is of type 'unknown'. -src/app/api/standard-cards/route.ts(486,11): error TS18046: 'updateData' is of type 'unknown'. -src/app/api/standard-cards/route.ts(488,11): error TS18046: 'updateData' is of type 'unknown'. -src/app/api/standard-cards/route.ts(496,9): error TS18046: 'updateData' is of type 'unknown'. -src/app/api/standard-cards/route.ts(499,9): error TS18046: 'updateData' is of type 'unknown'. -src/app/api/standard-cards/route.ts(505,7): error TS18046: 'updateData' is of type 'unknown'. -src/app/api/standard-cards/route.ts(509,5): error TS18046: 'updateData' is of type 'unknown'. -src/app/api/standard-cards/route.ts(514,9): error TS18046: 'updateData' is of type 'unknown'. -src/app/api/standard-cards/route.ts(515,29): error TS18046: 'updateData' is of type 'unknown'. -src/app/api/standard-cards/route.ts(535,16): error TS18046: 'updateData' is of type 'unknown'. -src/app/api/standard-cards/route.ts(540,21): error TS2769: No overload matches this call. - Overload 1 of 2, '(o: {}): string[]', gave the following error. - Argument of type 'unknown' is not assignable to parameter of type '{}'. - Overload 2 of 2, '(o: object): string[]', gave the following error. - Argument of type 'unknown' is not assignable to parameter of type 'object'. -src/app/api/standard-cards/route.ts(544,38): error TS2769: No overload matches this call. - Overload 1 of 2, '(o: {}): string[]', gave the following error. - Argument of type 'unknown' is not assignable to parameter of type '{}'. - Overload 2 of 2, '(o: object): string[]', gave the following error. - Argument of type 'unknown' is not assignable to parameter of type 'object'. -src/app/api/standard-cards/route.ts(545,34): error TS2769: No overload matches this call. - Overload 1 of 2, '(o: ArrayLike | { [s: string]: unknown; }): unknown[]', gave the following error. - Argument of type 'unknown' is not assignable to parameter of type 'ArrayLike | { [s: string]: unknown; }'. - Overload 2 of 2, '(o: {}): any[]', gave the following error. - Argument of type 'unknown' is not assignable to parameter of type '{}'. -src/app/api/standard-cards/scan/route.ts(33,32): error TS2339: Property 'work_order_no' does not exist on type '{}'. -src/app/api/standard-cards/scan/route.ts(34,32): error TS2339: Property 'work_order_id' does not exist on type '{}'. -src/app/api/standard-cards/scan/route.ts(37,34): error TS2339: Property 'ref_no' does not exist on type '{}'. -src/app/api/standard-cards/scan/route.ts(40,19): error TS2339: Property 'ref_no' does not exist on type '{}'. -src/app/api/standard-cards/scan/route.ts(67,11): error TS2322: Type 'ColorStandardItem[]' is not assignable to type 'SqlValue[]'. - Type 'ColorStandardItem' is not assignable to type 'SqlValue'. -src/app/api/standard-cards/scan/route.ts(73,11): error TS2322: Type 'ProcessStandardItem[]' is not assignable to type 'SqlValue[]'. - Type 'ProcessStandardItem' is not assignable to type 'SqlValue'. -src/app/api/standard-cards/scan/route.ts(79,11): error TS2322: Type 'QualityStandardItem[]' is not assignable to type 'SqlValue[]'. - Type 'QualityStandardItem' is not assignable to type 'SqlValue'. -src/app/api/standard-cards/scan/route.ts(98,11): error TS2322: Type '(ProcessStandardItem | ColorStandardItem | QualityStandardItem)[]' is not assignable to type 'SqlValue[]'. - Type 'ProcessStandardItem | ColorStandardItem | QualityStandardItem' is not assignable to type 'SqlValue'. - Type 'ProcessStandardItem' is not assignable to type 'SqlValue'. -src/app/api/standard-cards/scan/route.ts(104,9): error TS2353: Object literal may only specify known properties, and 'items' does not exist in type 'Date | Buffer | readonly SqlValue[]'. -src/app/api/system/data-scope/route.ts(25,8): error TS2339: Property 'split' does not exist on type 'string | number | true | Date | Buffer'. - Property 'split' does not exist on type 'number'. -src/app/api/system/data-scope/route.ts(28,12): error TS2538: Type 'Buffer' cannot be used as an index type. -src/app/api/system/data-scope/route.ts(28,12): error TS2538: Type 'Date' cannot be used as an index type. -src/app/api/system/data-scope/route.ts(28,12): error TS2538: Type 'false' cannot be used as an index type. -src/app/api/system/data-scope/route.ts(28,12): error TS2538: Type 'null' cannot be used as an index type. -src/app/api/system/data-scope/route.ts(28,12): error TS2538: Type 'true' cannot be used as an index type. -src/app/api/system/data-scope/route.ts(28,12): error TS2538: Type 'undefined' cannot be used as an index type. -src/app/api/system/log/export/route.ts(95,31): error TS2339: Property 'replace' does not exist on type 'string | number | true | Date | Buffer'. - Property 'replace' does not exist on type 'number'. -src/app/api/system/monitor/route.ts(25,15): error TS2322: Type 'any[][]' is not assignable to type 'DbRow[]'. - Type 'any[]' is not assignable to type 'DbRow'. - Index signature for type 'string' is missing in type 'any[]'. -src/app/api/system/monitor/route.ts(34,32): error TS2339: Property 'count' does not exist on type 'string | number | boolean | Date | Buffer'. - Property 'count' does not exist on type 'string'. -src/app/api/system/monitor/route.ts(35,42): error TS2339: Property 'count' does not exist on type 'string | number | boolean | Date | Buffer'. - Property 'count' does not exist on type 'string'. -src/app/api/system/monitor/route.ts(36,48): error TS2339: Property 'count' does not exist on type 'string | number | boolean | Date | Buffer'. - Property 'count' does not exist on type 'string'. -src/app/api/system/monitor/route.ts(37,36): error TS2339: Property 'count' does not exist on type 'string | number | boolean | Date | Buffer'. - Property 'count' does not exist on type 'string'. -src/app/api/system/monitor/route.ts(74,15): error TS2322: Type 'any[][]' is not assignable to type 'DbRow[]'. - Type 'any[]' is not assignable to type 'DbRow'. - Index signature for type 'string' is missing in type 'any[]'. -src/app/api/system/monitor/route.ts(82,48): error TS2551: Property 'Value' does not exist on type 'string | number | boolean | Date | Buffer'. Did you mean 'valueOf'? - Property 'Value' does not exist on type 'string'. -src/app/api/system/monitor/route.ts(83,44): error TS2551: Property 'Value' does not exist on type 'string | number | boolean | Date | Buffer'. Did you mean 'valueOf'? - Property 'Value' does not exist on type 'string'. -src/app/api/system/monitor/route.ts(84,47): error TS2551: Property 'Value' does not exist on type 'string | number | boolean | Date | Buffer'. Did you mean 'valueOf'? - Property 'Value' does not exist on type 'string'. -src/app/api/system/monitor/route.ts(85,54): error TS2551: Property 'Value' does not exist on type 'string | number | boolean | Date | Buffer'. Did you mean 'valueOf'? - Property 'Value' does not exist on type 'string'. -src/app/api/system/oper-log/route.ts(148,19): error TS2345: Argument of type '(cell: string | number) => string' is not assignable to parameter of type '(value: string | number | boolean | Date | Buffer | null | undefined, index: number, array: (string | number | boolean | Date | Buffer<...> | null | undefined)[]) => string'. - Types of parameters 'cell' and 'value' are incompatible. - Type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to type 'string | number'. - Type 'undefined' is not assignable to type 'string | number'. -src/app/api/system/roles/route.ts(37,12): error TS2339: Property 'effectivePermissions' does not exist on type '{ permissions: any; }'. -src/app/api/system/roles/route.ts(37,74): error TS2339: Property 'id' does not exist on type '{ permissions: any; }'. -src/app/api/system/roles/route.ts(38,12): error TS2339: Property 'parentRole' does not exist on type '{ permissions: any; }'. -src/app/api/system/roles/route.ts(38,30): error TS2339: Property 'parent_id' does not exist on type '{ permissions: any; }'. -src/app/api/system/roles/route.ts(38,71): error TS2339: Property 'parent_id' does not exist on type '{ permissions: any; }'. -src/app/api/system/user/route.ts(83,24): error TS2339: Property 'insertId' does not exist on type 'QueryResult'. - Property 'insertId' does not exist on type 'ResultSetHeader[]'. -src/app/api/system/workflow/route.ts(184,7): error TS2533: Object is possibly 'null' or 'undefined'. -src/app/api/system/workflow/route.ts(184,7): error TS2365: Operator '>' cannot be applied to types 'string | number | boolean | Date | Buffer' and 'number'. -src/app/api/warehouse/batch-inventory/route.ts(100,9): error TS2353: Object literal may only specify known properties, and 'batch_inventory_id' does not exist in type 'Date | Buffer | readonly SqlValue[]'. -src/app/api/warehouse/batch-inventory/route.ts(115,51): error TS2345: Argument of type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to parameter of type 'string'. - Type 'undefined' is not assignable to type 'string'. -src/app/api/warehouse/batch-inventory/route.ts(143,35): error TS2339: Property 'insertId' does not exist on type 'QueryResult'. - Property 'insertId' does not exist on type 'ResultSetHeader[]'. -src/app/api/warehouse/batch-inventory/route.ts(188,20): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/warehouse/batch-inventory/route.ts(196,32): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/warehouse/batch-inventory/route.ts(261,36): error TS2339: Property 'insertId' does not exist on type 'QueryResult'. - Property 'insertId' does not exist on type 'ResultSetHeader[]'. -src/app/api/warehouse/batch-inventory/route.ts(288,30): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/warehouse/batch-inventory/route.ts(303,27): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/warehouse/batch-inventory/route.ts(304,27): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/warehouse/batch-inventory/route.ts(315,17): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/warehouse/batch-inventory/route.ts(319,39): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/warehouse/batch-inventory/route.ts(322,17): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/warehouse/batch-inventory/route.ts(322,64): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/warehouse/batch-inventory/route.ts(322,104): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/warehouse/batch-inventory/route.ts(348,19): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/warehouse/batch/trace/route.ts(77,17): error TS18046: 'log' is of type 'unknown'. -src/app/api/warehouse/batch/trace/route.ts(78,25): error TS18046: 'log' is of type 'unknown'. -src/app/api/warehouse/batch/trace/route.ts(80,33): error TS18046: 'log' is of type 'unknown'. -src/app/api/warehouse/batch/trace/route.ts(80,61): error TS18046: 'log' is of type 'unknown'. -src/app/api/warehouse/batch/trace/route.ts(81,35): error TS18046: 'log' is of type 'unknown'. -src/app/api/warehouse/batch/trace/route.ts(82,32): error TS18046: 'log' is of type 'unknown'. -src/app/api/warehouse/batch/trace/route.ts(83,31): error TS18046: 'log' is of type 'unknown'. -src/app/api/warehouse/batch/trace/route.ts(84,24): error TS18046: 'log' is of type 'unknown'. -src/app/api/warehouse/batch/trace/route.ts(85,22): error TS18046: 'log' is of type 'unknown'. -src/app/api/warehouse/batch/trace/route.ts(86,25): error TS18046: 'log' is of type 'unknown'. -src/app/api/warehouse/batch/trace/route.ts(87,24): error TS18046: 'log' is of type 'unknown'. -src/app/api/warehouse/batch/trace/route.ts(88,17): error TS18046: 'log' is of type 'unknown'. -src/app/api/warehouse/batch/trace/route.ts(89,22): error TS18046: 'log' is of type 'unknown'. -src/app/api/warehouse/batch/trace/route.ts(93,13): error TS18046: 'log' is of type 'unknown'. -src/app/api/warehouse/batch/trace/route.ts(93,54): error TS18046: 'log' is of type 'unknown'. -src/app/api/warehouse/batch/trace/route.ts(99,14): error TS18046: 'log' is of type 'unknown'. -src/app/api/warehouse/batch/trace/route.ts(102,13): error TS18046: 'entry' is of type 'unknown'. -src/app/api/warehouse/batch/trace/route.ts(114,20): error TS18046: 'log' is of type 'unknown'. -src/app/api/warehouse/batch/trace/route.ts(114,62): error TS18046: 'log' is of type 'unknown'. -src/app/api/warehouse/batch/trace/route.ts(120,14): error TS18046: 'log' is of type 'unknown'. -src/app/api/warehouse/batch/trace/route.ts(123,13): error TS18046: 'entry' is of type 'unknown'. -src/app/api/warehouse/batch/trace/route.ts(137,20): error TS18046: 'log' is of type 'unknown'. -src/app/api/warehouse/batch/trace/route.ts(137,56): error TS18046: 'log' is of type 'unknown'. -src/app/api/warehouse/batch/trace/route.ts(148,14): error TS18046: 'log' is of type 'unknown'. -src/app/api/warehouse/batch/trace/route.ts(151,13): error TS18046: 'entry' is of type 'unknown'. -src/app/api/warehouse/batch/trace/route.ts(166,11): error TS18046: 'log' is of type 'unknown'. -src/app/api/warehouse/batch/trace/route.ts(167,11): error TS18046: 'log' is of type 'unknown'. -src/app/api/warehouse/batch/trace/route.ts(174,14): error TS18046: 'log' is of type 'unknown'. -src/app/api/warehouse/batch/trace/route.ts(177,13): error TS18046: 'entry' is of type 'unknown'. -src/app/api/warehouse/batch/trace/route.ts(190,20): error TS18046: 'log' is of type 'unknown'. -src/app/api/warehouse/batch/trace/route.ts(190,60): error TS18046: 'log' is of type 'unknown'. -src/app/api/warehouse/batch/trace/route.ts(195,14): error TS18046: 'log' is of type 'unknown'. -src/app/api/warehouse/batch/trace/route.ts(198,13): error TS18046: 'entry' is of type 'unknown'. -src/app/api/warehouse/batch/trace/route.ts(214,23): error TS2345: Argument of type 'unknown' is not assignable to parameter of type '{ log_id: number; operation_type: number; operation_type_label: string; operation_qty: number; before_qty: number; after_qty: number; business_type: string; business_no: string; warehouse_name: string; operator_name: string; remark: string; create_time: string; document?: { ...; } | undefined; }'. -src/app/api/warehouse/batch/trace/route.ts(218,15): error TS2769: No overload matches this call. - Overload 1 of 2, '(predicate: (value: unknown, index: number, array: unknown[]) => value is unknown, thisArg?: any): unknown[]', gave the following error. - Argument of type '(l: DbRow) => boolean' is not assignable to parameter of type '(value: unknown, index: number, array: unknown[]) => value is unknown'. - Types of parameters 'l' and 'value' are incompatible. - Type 'unknown' is not assignable to type 'DbRow'. - Overload 2 of 2, '(predicate: (value: unknown, index: number, array: unknown[]) => unknown, thisArg?: any): unknown[]', gave the following error. - Argument of type '(l: DbRow) => boolean' is not assignable to parameter of type '(value: unknown, index: number, array: unknown[]) => unknown'. - Types of parameters 'l' and 'value' are incompatible. - Type 'unknown' is not assignable to type 'DbRow'. -src/app/api/warehouse/batch/trace/route.ts(219,15): error TS2769: No overload matches this call. - Overload 1 of 3, '(callbackfn: (previousValue: unknown, currentValue: unknown, currentIndex: number, array: unknown[]) => unknown, initialValue: unknown): unknown', gave the following error. - Argument of type '(sum: number, l: DbRow) => number' is not assignable to parameter of type '(previousValue: unknown, currentValue: unknown, currentIndex: number, array: unknown[]) => unknown'. - Types of parameters 'sum' and 'previousValue' are incompatible. - Type 'unknown' is not assignable to type 'number'. - Overload 2 of 3, '(callbackfn: (previousValue: number, currentValue: unknown, currentIndex: number, array: unknown[]) => number, initialValue: number): number', gave the following error. - Argument of type '(sum: number, l: DbRow) => number' is not assignable to parameter of type '(previousValue: number, currentValue: unknown, currentIndex: number, array: unknown[]) => number'. - Types of parameters 'l' and 'currentValue' are incompatible. - Type 'unknown' is not assignable to type 'DbRow'. -src/app/api/warehouse/batch/trace/route.ts(219,59): error TS2345: Argument of type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to parameter of type 'string'. - Type 'undefined' is not assignable to type 'string'. -src/app/api/warehouse/batch/trace/route.ts(221,15): error TS2769: No overload matches this call. - Overload 1 of 2, '(predicate: (value: unknown, index: number, array: unknown[]) => value is unknown, thisArg?: any): unknown[]', gave the following error. - Argument of type '(l: DbRow) => boolean' is not assignable to parameter of type '(value: unknown, index: number, array: unknown[]) => value is unknown'. - Types of parameters 'l' and 'value' are incompatible. - Type 'unknown' is not assignable to type 'DbRow'. - Overload 2 of 2, '(predicate: (value: unknown, index: number, array: unknown[]) => unknown, thisArg?: any): unknown[]', gave the following error. - Argument of type '(l: DbRow) => boolean' is not assignable to parameter of type '(value: unknown, index: number, array: unknown[]) => unknown'. - Types of parameters 'l' and 'value' are incompatible. - Type 'unknown' is not assignable to type 'DbRow'. -src/app/api/warehouse/batch/trace/route.ts(222,15): error TS2769: No overload matches this call. - Overload 1 of 3, '(callbackfn: (previousValue: unknown, currentValue: unknown, currentIndex: number, array: unknown[]) => unknown, initialValue: unknown): unknown', gave the following error. - Argument of type '(sum: number, l: DbRow) => number' is not assignable to parameter of type '(previousValue: unknown, currentValue: unknown, currentIndex: number, array: unknown[]) => unknown'. - Types of parameters 'sum' and 'previousValue' are incompatible. - Type 'unknown' is not assignable to type 'number'. - Overload 2 of 3, '(callbackfn: (previousValue: number, currentValue: unknown, currentIndex: number, array: unknown[]) => number, initialValue: number): number', gave the following error. - Argument of type '(sum: number, l: DbRow) => number' is not assignable to parameter of type '(previousValue: number, currentValue: unknown, currentIndex: number, array: unknown[]) => number'. - Types of parameters 'l' and 'currentValue' are incompatible. - Type 'unknown' is not assignable to type 'DbRow'. -src/app/api/warehouse/batch/trace/route.ts(222,59): error TS2345: Argument of type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to parameter of type 'string'. - Type 'undefined' is not assignable to type 'string'. -src/app/api/warehouse/batch/trace/route.ts(226,19): error TS18046: 'batchSnapshot' is of type 'unknown'. -src/app/api/warehouse/batch/trace/route.ts(227,22): error TS18046: 'batchSnapshot' is of type 'unknown'. -src/app/api/warehouse/batch/trace/route.ts(228,24): error TS18046: 'batchSnapshot' is of type 'unknown'. -src/app/api/warehouse/batch/trace/route.ts(229,24): error TS18046: 'batchSnapshot' is of type 'unknown'. -src/app/api/warehouse/batch/trace/route.ts(230,23): error TS18046: 'batchSnapshot' is of type 'unknown'. -src/app/api/warehouse/batch/trace/route.ts(231,19): error TS18046: 'batchSnapshot' is of type 'unknown'. -src/app/api/warehouse/batch/trace/route.ts(232,30): error TS18046: 'batchSnapshot' is of type 'unknown'. -src/app/api/warehouse/batch/trace/route.ts(233,35): error TS18046: 'batchSnapshot' is of type 'unknown'. -src/app/api/warehouse/batch/trace/route.ts(234,32): error TS18046: 'batchSnapshot' is of type 'unknown'. -src/app/api/warehouse/batch/trace/route.ts(235,31): error TS18046: 'batchSnapshot' is of type 'unknown'. -src/app/api/warehouse/batch/trace/route.ts(235,58): error TS18046: 'batchSnapshot' is of type 'unknown'. -src/app/api/warehouse/batch/trace/route.ts(236,23): error TS18046: 'batchSnapshot' is of type 'unknown'. -src/app/api/warehouse/batch/trace/route.ts(237,23): error TS18046: 'batchSnapshot' is of type 'unknown'. -src/app/api/warehouse/batch/trace/route.ts(238,22): error TS18046: 'batchSnapshot' is of type 'unknown'. -src/app/api/warehouse/batch/trace/route.ts(239,17): error TS18046: 'batchSnapshot' is of type 'unknown'. -src/app/api/warehouse/batch/trace/route.ts(240,22): error TS18046: 'batchSnapshot' is of type 'unknown'. -src/app/api/warehouse/batch/trace/route.ts(241,22): error TS18046: 'batchSnapshot' is of type 'unknown'. -src/app/api/warehouse/batch/trace/route.ts(248,21): error TS18046: 'totalIn' is of type 'unknown'. -src/app/api/warehouse/batch/trace/route.ts(248,31): error TS18046: 'totalOut' is of type 'unknown'. -src/app/api/warehouse/fifo-recommend/route.ts(28,29): error TS2339: Property 'includes' does not exist on type 'string | number | true | Date | Buffer'. - Property 'includes' does not exist on type 'number'. -src/app/api/warehouse/fifo-recommend/route.ts(32,24): error TS2339: Property 'includes' does not exist on type 'string | number | true | Date | Buffer'. - Property 'includes' does not exist on type 'number'. -src/app/api/warehouse/fifo-recommend/route.ts(38,44): error TS2345: Argument of type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to parameter of type 'string | null'. - Type 'undefined' is not assignable to type 'string | null'. -src/app/api/warehouse/fifo-recommend/route.ts(112,48): error TS2345: Argument of type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to parameter of type 'string | null'. - Type 'undefined' is not assignable to type 'string | null'. -src/app/api/warehouse/fifo-recommend/route.ts(128,16): error TS2362: The left-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. -src/app/api/warehouse/fifo-recommend/route.ts(128,16): error TS18049: 'b.compatibility_score' is possibly 'null' or 'undefined'. -src/app/api/warehouse/fifo-recommend/route.ts(128,40): error TS2363: The right-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. -src/app/api/warehouse/fifo-recommend/route.ts(128,40): error TS18049: 'a.compatibility_score' is possibly 'null' or 'undefined'. -src/app/api/warehouse/fifo-recommend/route.ts(129,23): error TS2769: No overload matches this call. - Overload 1 of 4, '(value: string | number | Date): Date', gave the following error. - Argument of type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to parameter of type 'string | number | Date'. - Type 'undefined' is not assignable to type 'string | number | Date'. - Overload 2 of 4, '(value: string | number): Date', gave the following error. - Argument of type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to parameter of type 'string | number'. - Type 'undefined' is not assignable to type 'string | number'. -src/app/api/warehouse/fifo-recommend/route.ts(129,60): error TS2769: No overload matches this call. - Overload 1 of 4, '(value: string | number | Date): Date', gave the following error. - Argument of type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to parameter of type 'string | number | Date'. - Type 'undefined' is not assignable to type 'string | number | Date'. - Overload 2 of 4, '(value: string | number): Date', gave the following error. - Argument of type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to parameter of type 'string | number'. - Type 'undefined' is not assignable to type 'string | number'. -src/app/api/warehouse/fifo-recommend/route.ts(140,36): error TS2339: Property 'available_qty' does not exist on type '{ expiry_weight: number; compatibility_score: number; override_risk_score: number; is_near_expiry: boolean; is_urgent_expiry: boolean; }'. -src/app/api/warehouse/fifo-recommend/route.ts(142,58): error TS2339: Property 'available_qty' does not exist on type '{ expiry_weight: number; compatibility_score: number; override_risk_score: number; is_near_expiry: boolean; is_urgent_expiry: boolean; }'. -src/app/api/warehouse/fifo-recommend/route.ts(147,38): error TS2339: Property 'available_qty' does not exist on type '{ expiry_weight: number; compatibility_score: number; override_risk_score: number; is_near_expiry: boolean; is_urgent_expiry: boolean; }'. -src/app/api/warehouse/fifo-recommend/route.ts(151,24): error TS2339: Property 'available_qty' does not exist on type '{ expiry_weight: number; compatibility_score: number; override_risk_score: number; is_near_expiry: boolean; is_urgent_expiry: boolean; }'. -src/app/api/warehouse/fifo-recommend/route.ts(157,9): error TS2353: Object literal may only specify known properties, and 'allocated_qty' does not exist in type 'Date | Buffer | readonly SqlValue[]'. -src/app/api/warehouse/fifo-recommend/route.ts(170,52): error TS2345: Argument of type '(r: DbRow) => boolean' is not assignable to parameter of type '(value: SqlValue, index: number, array: SqlValue[]) => unknown'. - Types of parameters 'r' and 'value' are incompatible. - Type 'SqlValue' is not assignable to type 'DbRow'. - Type 'undefined' is not assignable to type 'DbRow'. -src/app/api/warehouse/fifo-recommend/route.ts(182,5): error TS2769: No overload matches this call. - Overload 1 of 3, '(callbackfn: (previousValue: SqlValue, currentValue: SqlValue, currentIndex: number, array: SqlValue[]) => SqlValue, initialValue: SqlValue): SqlValue', gave the following error. - Argument of type '(sum: number, b: DbRow) => number' is not assignable to parameter of type '(previousValue: SqlValue, currentValue: SqlValue, currentIndex: number, array: SqlValue[]) => SqlValue'. - Types of parameters 'sum' and 'previousValue' are incompatible. - Type 'SqlValue' is not assignable to type 'number'. - Type 'undefined' is not assignable to type 'number'. - Overload 2 of 3, '(callbackfn: (previousValue: number, currentValue: SqlValue, currentIndex: number, array: SqlValue[]) => number, initialValue: number): number', gave the following error. - Argument of type '(sum: number, b: DbRow) => number' is not assignable to parameter of type '(previousValue: number, currentValue: SqlValue, currentIndex: number, array: SqlValue[]) => number'. - Types of parameters 'b' and 'currentValue' are incompatible. - Type 'SqlValue' is not assignable to type 'DbRow'. - Type 'undefined' is not assignable to type 'DbRow'. -src/app/api/warehouse/fifo-recommend/route.ts(186,5): error TS2769: No overload matches this call. - Overload 1 of 3, '(callbackfn: (previousValue: SqlValue, currentValue: SqlValue, currentIndex: number, array: SqlValue[]) => SqlValue, initialValue: SqlValue): SqlValue', gave the following error. - Argument of type '(sum: number, b: DbRow) => number' is not assignable to parameter of type '(previousValue: SqlValue, currentValue: SqlValue, currentIndex: number, array: SqlValue[]) => SqlValue'. - Types of parameters 'sum' and 'previousValue' are incompatible. - Type 'SqlValue' is not assignable to type 'number'. - Type 'undefined' is not assignable to type 'number'. - Overload 2 of 3, '(callbackfn: (previousValue: number, currentValue: SqlValue, currentIndex: number, array: SqlValue[]) => number, initialValue: number): number', gave the following error. - Argument of type '(sum: number, b: DbRow) => number' is not assignable to parameter of type '(previousValue: number, currentValue: SqlValue, currentIndex: number, array: SqlValue[]) => number'. - Types of parameters 'b' and 'currentValue' are incompatible. - Type 'SqlValue' is not assignable to type 'DbRow'. - Type 'undefined' is not assignable to type 'DbRow'. -src/app/api/warehouse/fifo-recommend/route.ts(189,19): error TS18049: 'totalRecommendedQty' is possibly 'null' or 'undefined'. -src/app/api/warehouse/fifo-recommend/route.ts(189,19): error TS2365: Operator '>' cannot be applied to types 'string | number | boolean | Date | Buffer | readonly SqlValue[]' and 'number'. -src/app/api/warehouse/fifo-recommend/route.ts(189,45): error TS2362: The left-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. -src/app/api/warehouse/fifo-recommend/route.ts(189,45): error TS18049: 'totalRecommendedCost' is possibly 'null' or 'undefined'. -src/app/api/warehouse/fifo-recommend/route.ts(189,68): error TS2363: The right-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. -src/app/api/warehouse/fifo-recommend/route.ts(189,68): error TS18049: 'totalRecommendedQty' is possibly 'null' or 'undefined'. -src/app/api/warehouse/fifo-recommend/route.ts(219,31): error TS2339: Property 'batch_no' does not exist on type '{ expiry_weight: number; compatibility_score: number; override_risk_score: number; is_near_expiry: boolean; is_urgent_expiry: boolean; }'. -src/app/api/warehouse/fifo-recommend/route.ts(220,43): error TS2339: Property 'available_qty' does not exist on type '{ expiry_weight: number; compatibility_score: number; override_risk_score: number; is_near_expiry: boolean; is_urgent_expiry: boolean; }'. -src/app/api/warehouse/fifo-recommend/route.ts(221,40): error TS2339: Property 'unit_price' does not exist on type '{ expiry_weight: number; compatibility_score: number; override_risk_score: number; is_near_expiry: boolean; is_urgent_expiry: boolean; }'. -src/app/api/warehouse/fifo-recommend/route.ts(222,35): error TS2339: Property 'inbound_date' does not exist on type '{ expiry_weight: number; compatibility_score: number; override_risk_score: number; is_near_expiry: boolean; is_urgent_expiry: boolean; }'. -src/app/api/warehouse/fifo-recommend/route.ts(223,34): error TS2339: Property 'expire_date' does not exist on type '{ expiry_weight: number; compatibility_score: number; override_risk_score: number; is_near_expiry: boolean; is_urgent_expiry: boolean; }'. -src/app/api/warehouse/fifo-recommend/route.ts(236,38): error TS2362: The left-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. -src/app/api/warehouse/fifo-recommend/route.ts(236,38): error TS18049: 'totalRecommendedCost' is possibly 'null' or 'undefined'. -src/app/api/warehouse/inbound/cutting/route.ts(44,17): error TS2339: Property 'is_cut' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(48,17): error TS2339: Property 'is_used' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(52,17): error TS2339: Property 'label_type' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(56,43): error TS2339: Property 'quantity' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(62,19): error TS2339: Property 'id' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(63,24): error TS2339: Property 'label_no' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(64,29): error TS2339: Property 'material_name' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(65,29): error TS2339: Property 'material_code' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(66,30): error TS2339: Property 'specification' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(67,25): error TS2339: Property 'quantity' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(68,21): error TS2339: Property 'unit' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(69,22): error TS2339: Property 'width' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(70,29): error TS2339: Property 'supplier_name' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(71,24): error TS2339: Property 'batch_no' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(72,32): error TS2339: Property 'purchase_order_no' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(73,26): error TS2339: Property 'label_type' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(74,23): error TS2339: Property 'is_used' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(75,22): error TS2339: Property 'is_cut' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(126,7): error TS2345: Argument of type 'SqlValue[]' is not assignable to parameter of type 'DbRow[]'. - Type 'SqlValue' is not assignable to type 'DbRow'. - Type 'undefined' is not assignable to type 'DbRow'. -src/app/api/warehouse/inbound/cutting/route.ts(185,21): error TS2339: Property 'is_cut' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(189,21): error TS2339: Property 'is_used' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(194,21): error TS2339: Property 'label_type' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(198,47): error TS2339: Property 'quantity' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(216,30): error TS2339: Property 'width' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(218,34): error TS2339: Property 'specification' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(222,39): error TS2339: Property 'material_code' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(225,22): error TS2339: Property 'material_code' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(229,29): error TS2339: Property 'width' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(230,33): error TS2339: Property 'specification' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(233,20): error TS2339: Property 'specification' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(233,50): error TS2339: Property 'specification' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(236,21): error TS2339: Property 'specification' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(236,67): error TS2339: Property 'id' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(238,23): error TS2339: Property 'specification' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(238,46): error TS2339: Property 'specification' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(239,38): error TS2339: Property 'width' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(281,23): error TS2339: Property 'id' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(282,23): error TS2339: Property 'label_no' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(293,25): error TS2352: Conversion of type 'QueryResult' to type 'DbRow' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first. - Type '[RowDataPacket[], ResultSetHeader]' is not comparable to type 'DbRow'. - Index signature for type 'string' is missing in type '[RowDataPacket[], ResultSetHeader]'. -src/app/api/warehouse/inbound/cutting/route.ts(296,21): error TS2339: Property 'id' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(300,40): error TS2339: Property 'specification' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(320,49): error TS2339: Property 'quantity' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(321,38): error TS2339: Property 'quantity' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(326,31): error TS2339: Property 'label_no' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(340,25): error TS2339: Property 'purchase_order_no' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(341,25): error TS2339: Property 'supplier_name' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(342,25): error TS2339: Property 'receive_date' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(343,25): error TS2339: Property 'material_code' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(344,25): error TS2339: Property 'material_name' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(346,25): error TS2339: Property 'unit' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(347,25): error TS2339: Property 'batch_no' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(350,25): error TS2339: Property 'length_per_roll' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(352,25): error TS2339: Property 'warehouse_id' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(353,25): error TS2339: Property 'location_id' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(354,25): error TS2339: Property 'id' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(358,29): error TS2352: Conversion of type 'QueryResult' to type 'DbRow' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first. - Type '[RowDataPacket[], ResultSetHeader]' is not comparable to type 'DbRow'. - Index signature for type 'string' is missing in type '[RowDataPacket[], ResultSetHeader]'. -src/app/api/warehouse/inbound/cutting/route.ts(391,49): error TS2339: Property 'quantity' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(397,31): error TS2339: Property 'label_no' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(411,25): error TS2339: Property 'purchase_order_no' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(412,25): error TS2339: Property 'supplier_name' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(413,25): error TS2339: Property 'receive_date' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(414,25): error TS2339: Property 'material_code' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(415,30): error TS2339: Property 'material_name' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(417,25): error TS2339: Property 'unit' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(418,25): error TS2339: Property 'batch_no' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(422,25): error TS2339: Property 'length_per_roll' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(424,25): error TS2339: Property 'warehouse_id' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(425,25): error TS2339: Property 'location_id' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(426,25): error TS2339: Property 'id' does not exist on type '{}'. -src/app/api/warehouse/inbound/cutting/route.ts(430,29): error TS2352: Conversion of type 'QueryResult' to type 'DbRow' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first. - Type '[RowDataPacket[], ResultSetHeader]' is not comparable to type 'DbRow'. - Index signature for type 'string' is missing in type '[RowDataPacket[], ResultSetHeader]'. -src/app/api/warehouse/inbound/cutting/route.ts(484,52): error TS2322: Type 'DbRow' is not assignable to type 'SqlValue'. - Type 'DbRow' is missing the following properties from type 'Buffer': slice, subarray, write, toJSON, and 102 more. -src/app/api/warehouse/inbound/cutting/route.ts(484,71): error TS2322: Type 'number | DbRow' is not assignable to type 'SqlValue'. - Type 'DbRow' is not assignable to type 'SqlValue'. - Type 'DbRow' is missing the following properties from type 'Buffer': slice, subarray, write, toJSON, and 102 more. -src/app/api/warehouse/inbound/cutting/route.ts(484,81): error TS2322: Type 'number | DbRow' is not assignable to type 'SqlValue'. - Type 'DbRow' is not assignable to type 'SqlValue'. - Type 'DbRow' is missing the following properties from type 'Buffer': slice, subarray, write, toJSON, and 102 more. -src/app/api/warehouse/inbound/cutting/route.ts(485,45): error TS2345: Argument of type 'DbRow[]' is not assignable to parameter of type 'SqlValue[]'. - Type 'DbRow' is not assignable to type 'SqlValue'. - Type 'DbRow' is missing the following properties from type 'Buffer': slice, subarray, write, toJSON, and 102 more. -src/app/api/warehouse/inbound/route.ts(105,16): error TS2345: Argument of type '(iss: DbRow) => string' is not assignable to parameter of type '(value: $ZodIssue, index: number, array: $ZodIssue[]) => string'. - Types of parameters 'iss' and 'value' are incompatible. - Type '$ZodIssue' is not assignable to type 'DbRow'. - Type '$ZodIssueInvalidType' is not assignable to type 'DbRow'. - Index signature for type 'string' is missing in type '$ZodIssueInvalidType'. -src/app/api/warehouse/inbound/route.ts(105,35): error TS18049: 'iss.path' is possibly 'null' or 'undefined'. -src/app/api/warehouse/inbound/route.ts(105,44): error TS2339: Property 'join' does not exist on type 'string | number | boolean | Date | Buffer'. - Property 'join' does not exist on type 'string'. -src/app/api/warehouse/inbound/route.ts(113,26): error TS18046: 'validated' is of type 'unknown'. -src/app/api/warehouse/inbound/route.ts(117,18): error TS18046: 'validated' is of type 'unknown'. -src/app/api/warehouse/inbound/route.ts(120,59): error TS2345: Argument of type '(string | number | boolean | Date | Buffer | null | undefined)[]' is not assignable to parameter of type '(number | null | undefined)[]'. - Type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to type 'number | null | undefined'. - Type 'string' is not assignable to type 'number'. -src/app/api/warehouse/inbound/route.ts(139,20): error TS18046: 'validated' is of type 'unknown'. -src/app/api/warehouse/inbound/route.ts(140,21): error TS18046: 'validated' is of type 'unknown'. -src/app/api/warehouse/inbound/route.ts(141,20): error TS18046: 'validated' is of type 'unknown'. -src/app/api/warehouse/inbound/route.ts(142,17): error TS18046: 'validated' is of type 'unknown'. -src/app/api/warehouse/inbound/route.ts(143,15): error TS18046: 'validated' is of type 'unknown'. -src/app/api/warehouse/inbound/route.ts(144,19): error TS18046: 'validated' is of type 'unknown'. -src/app/api/warehouse/inbound/route.ts(145,22): error TS18046: 'validated' is of type 'unknown'. -src/app/api/warehouse/inbound/route.ts(147,14): error TS18046: 'validated' is of type 'unknown'. -src/app/api/warehouse/inbound/route.ts(179,16): error TS2345: Argument of type '(iss: DbRow) => string' is not assignable to parameter of type '(value: $ZodIssue, index: number, array: $ZodIssue[]) => string'. - Types of parameters 'iss' and 'value' are incompatible. - Type '$ZodIssue' is not assignable to type 'DbRow'. - Type '$ZodIssueInvalidType' is not assignable to type 'DbRow'. - Index signature for type 'string' is missing in type '$ZodIssueInvalidType'. -src/app/api/warehouse/inbound/route.ts(179,35): error TS18049: 'iss.path' is possibly 'null' or 'undefined'. -src/app/api/warehouse/inbound/route.ts(179,44): error TS2339: Property 'join' does not exist on type 'string | number | boolean | Date | Buffer'. - Property 'join' does not exist on type 'string'. -src/app/api/warehouse/inbound/route.ts(186,13): error TS2339: Property 'id' does not exist on type 'unknown'. -src/app/api/warehouse/inbound/route.ts(186,17): error TS2339: Property 'action' does not exist on type 'unknown'. -src/app/api/warehouse/inbound/route.ts(186,25): error TS2339: Property 'status' does not exist on type 'unknown'. -src/app/api/warehouse/inbound/route.ts(186,33): error TS2339: Property 'remark' does not exist on type 'unknown'. -src/app/api/warehouse/inbound/route.ts(212,30): error TS18046: 'validated' is of type 'unknown'. -src/app/api/warehouse/inbound/route.ts(222,25): error TS18046: 'validated' is of type 'unknown'. -src/app/api/warehouse/inbound/route.ts(223,24): error TS18046: 'validated' is of type 'unknown'. -src/app/api/warehouse/inbound/route.ts(224,24): error TS18046: 'validated' is of type 'unknown'. -src/app/api/warehouse/inbound/route.ts(225,21): error TS18046: 'validated' is of type 'unknown'. -src/app/api/warehouse/inbound/route.ts(226,19): error TS18046: 'validated' is of type 'unknown'. -src/app/api/warehouse/ink-mixing/route.ts(87,39): error TS2339: Property 'insertId' does not exist on type 'QueryResult'. - Property 'insertId' does not exist on type 'ResultSetHeader[]'. -src/app/api/warehouse/ink-mixing/route.ts(99,27): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/warehouse/ink-mixing/route.ts(103,27): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/warehouse/ink-mixing/route.ts(142,39): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/warehouse/ink-mixing/route.ts(142,52): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/warehouse/ink-mixing/route.ts(178,21): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/warehouse/ink-mixing/route.ts(182,10): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/warehouse/ink-mixing/route.ts(192,21): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/warehouse/ink-mixing/route.ts(196,10): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/warehouse/ink-opening/route.ts(103,31): error TS2339: Property 'insertId' does not exist on type 'QueryResult'. - Property 'insertId' does not exist on type 'ResultSetHeader[]'. -src/app/api/warehouse/ink-opening/route.ts(124,22): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/warehouse/ink-opening/route.ts(128,22): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/warehouse/inventory/export/route.ts(105,17): error TS2538: Type 'Buffer' cannot be used as an index type. -src/app/api/warehouse/inventory/export/route.ts(105,17): error TS2538: Type 'Date' cannot be used as an index type. -src/app/api/warehouse/inventory/export/route.ts(105,17): error TS2538: Type 'false' cannot be used as an index type. -src/app/api/warehouse/inventory/export/route.ts(105,17): error TS2538: Type 'null' cannot be used as an index type. -src/app/api/warehouse/inventory/export/route.ts(105,17): error TS2538: Type 'true' cannot be used as an index type. -src/app/api/warehouse/inventory/export/route.ts(105,17): error TS2538: Type 'undefined' cannot be used as an index type. -src/app/api/warehouse/inventory/export/route.ts(112,26): error TS2339: Property 'replace' does not exist on type 'string | number | true | Date | Buffer'. - Property 'replace' does not exist on type 'number'. -src/app/api/warehouse/inventory/route.ts(85,30): error TS2345: Argument of type 'string | number | true | Date | Buffer' is not assignable to parameter of type 'string'. - Type 'number' is not assignable to type 'string'. -src/app/api/warehouse/inventory/route.ts(86,27): error TS2345: Argument of type 'string | number | true | Date | Buffer' is not assignable to parameter of type 'string'. - Type 'number' is not assignable to type 'string'. -src/app/api/warehouse/inventory/route.ts(87,27): error TS2345: Argument of type 'string | number | true | Date | Buffer' is not assignable to parameter of type 'string'. - Type 'number' is not assignable to type 'string'. -src/app/api/warehouse/inventory/route.ts(88,27): error TS2345: Argument of type 'string | number | true | Date | Buffer' is not assignable to parameter of type 'string'. - Type 'number' is not assignable to type 'string'. -src/app/api/warehouse/inventory/route.ts(89,26): error TS2345: Argument of type 'string | number | true | Date | Buffer' is not assignable to parameter of type 'string'. - Type 'number' is not assignable to type 'string'. -src/app/api/warehouse/inventory/route.ts(90,30): error TS2345: Argument of type 'string | number | true | Date | Buffer' is not assignable to parameter of type 'string'. - Type 'number' is not assignable to type 'string'. -src/app/api/warehouse/inventory/route.ts(91,32): error TS2345: Argument of type 'string | number | true | Date | Buffer' is not assignable to parameter of type 'string'. - Type 'number' is not assignable to type 'string'. -src/app/api/warehouse/inventory/route.ts(92,28): error TS2345: Argument of type 'string | number | true | Date | Buffer' is not assignable to parameter of type 'string'. - Type 'number' is not assignable to type 'string'. -src/app/api/warehouse/inventory/route.ts(93,28): error TS2345: Argument of type 'string | number | true | Date | Buffer' is not assignable to parameter of type 'string'. - Type 'number' is not assignable to type 'string'. -src/app/api/warehouse/monthly-report/route.ts(103,30): error TS2345: Argument of type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to parameter of type 'string'. - Type 'undefined' is not assignable to type 'string'. -src/app/api/warehouse/monthly-report/route.ts(104,30): error TS2345: Argument of type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to parameter of type 'string'. - Type 'undefined' is not assignable to type 'string'. -src/app/api/warehouse/monthly-report/route.ts(128,28): error TS2345: Argument of type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to parameter of type 'string'. - Type 'undefined' is not assignable to type 'string'. -src/app/api/warehouse/monthly-report/route.ts(131,31): error TS2345: Argument of type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to parameter of type 'string'. - Type 'undefined' is not assignable to type 'string'. -src/app/api/warehouse/monthly-report/route.ts(133,30): error TS2345: Argument of type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to parameter of type 'string'. - Type 'undefined' is not assignable to type 'string'. -src/app/api/warehouse/monthly-report/route.ts(134,32): error TS2345: Argument of type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to parameter of type 'string'. - Type 'undefined' is not assignable to type 'string'. -src/app/api/warehouse/monthly-report/route.ts(140,7): error TS2571: Object is of type 'unknown'. -src/app/api/warehouse/monthly-report/route.ts(140,31): error TS2571: Object is of type 'unknown'. -src/app/api/warehouse/monthly-report/route.ts(141,7): error TS2571: Object is of type 'unknown'. -src/app/api/warehouse/monthly-report/route.ts(141,31): error TS2571: Object is of type 'unknown'. -src/app/api/warehouse/monthly-report/route.ts(150,12): error TS18046: 'r' is of type 'unknown'. -src/app/api/warehouse/monthly-report/route.ts(151,12): error TS18046: 'r' is of type 'unknown'. -src/app/api/warehouse/outbound/confirm/route.ts(46,35): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/warehouse/outbound/confirm/route.ts(50,24): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/warehouse/outbound/confirm/route.ts(67,49): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/warehouse/outbound/confirm/route.ts(69,33): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/warehouse/outbound/confirm/route.ts(111,38): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/warehouse/outbound/confirm/route.ts(148,51): error TS2345: Argument of type '(a: DbRow) => string | number | boolean | Date | Buffer | null | undefined' is not assignable to parameter of type '(value: WidthSlitAlloc, index: number, array: WidthSlitAlloc[]) => string | number | boolean | Date | Buffer | null | undefined'. - Types of parameters 'a' and 'value' are incompatible. - Type 'WidthSlitAlloc' is not assignable to type 'DbRow'. - Index signature for type 'string' is missing in type 'WidthSlitAlloc'. -src/app/api/warehouse/outbound/confirm/route.ts(196,57): error TS2345: Argument of type '(a: DbRow) => string | number | boolean | Date | Buffer | null | undefined' is not assignable to parameter of type '(value: FIFOAllocationItem, index: number, array: FIFOAllocationItem[]) => string | number | boolean | Date | Buffer | null | undefined'. - Types of parameters 'a' and 'value' are incompatible. - Type 'FIFOAllocationItem' is not assignable to type 'DbRow'. - Index signature for type 'string' is missing in type 'FIFOAllocationItem'. -src/app/api/warehouse/outbound/confirm/route.ts(236,29): error TS2339: Property 'affectedRows' does not exist on type 'QueryResult'. - Property 'affectedRows' does not exist on type 'ResultSetHeader[]'. -src/app/api/warehouse/outbound/confirm/route.ts(246,34): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/warehouse/outbound/confirm/route.ts(246,48): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/warehouse/outbound/confirm/route.ts(247,26): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/warehouse/outbound/confirm/route.ts(272,34): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/warehouse/outbound/confirm/route.ts(272,48): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/warehouse/outbound/confirm/route.ts(273,30): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/warehouse/outbound/confirm/route.ts(275,38): error TS2339: Property 'reduce' does not exist on type 'QueryResult'. - Property 'reduce' does not exist on type 'OkPacket'. -src/app/api/warehouse/outbound/confirm/route.ts(298,13): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/warehouse/outbound/confirm/route.ts(298,38): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/warehouse/outbound/confirm/route.ts(298,66): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/warehouse/outbound/confirm/route.ts(303,20): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/warehouse/outbound/confirm/route.ts(361,35): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/warehouse/outbound/confirm/route.ts(365,21): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/warehouse/outbound/confirm/route.ts(402,22): error TS2339: Property 'affectedRows' does not exist on type 'QueryResult'. - Property 'affectedRows' does not exist on type 'ResultSetHeader[]'. -src/app/api/warehouse/outbound/confirm/route.ts(415,40): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/warehouse/outbound/fifo/route.ts(86,36): error TS2345: Argument of type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to parameter of type 'string'. - Type 'undefined' is not assignable to type 'string'. -src/app/api/warehouse/outbound/fifo/route.ts(154,21): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/warehouse/outbound/fifo/route.ts(182,21): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/warehouse/outbound/fifo/route.ts(186,29): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/warehouse/outbound/fifo/route.ts(243,26): error TS2339: Property 'affectedRows' does not exist on type 'QueryResult'. - Property 'affectedRows' does not exist on type 'ResultSetHeader[]'. -src/app/api/warehouse/outbound/fifo/route.ts(248,13): error TS2353: Object literal may only specify known properties, and 'material_id' does not exist in type 'Date | Buffer | readonly SqlValue[]'. -src/app/api/warehouse/outbound/fifo/route.ts(282,35): error TS2339: Property 'insertId' does not exist on type 'QueryResult'. - Property 'insertId' does not exist on type 'ResultSetHeader[]'. -src/app/api/warehouse/outbound/fifo/route.ts(292,13): error TS18049: 'obItem' is possibly 'null' or 'undefined'. -src/app/api/warehouse/outbound/fifo/route.ts(292,20): error TS2339: Property 'material_id' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'material_id' does not exist on type 'string'. -src/app/api/warehouse/outbound/fifo/route.ts(293,13): error TS18049: 'obItem' is possibly 'null' or 'undefined'. -src/app/api/warehouse/outbound/fifo/route.ts(293,20): error TS2339: Property 'material_name' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'material_name' does not exist on type 'string'. -src/app/api/warehouse/outbound/fifo/route.ts(294,13): error TS18049: 'obItem' is possibly 'null' or 'undefined'. -src/app/api/warehouse/outbound/fifo/route.ts(294,20): error TS2339: Property 'qty' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'qty' does not exist on type 'string'. -src/app/api/warehouse/outbound/fifo/route.ts(295,13): error TS18049: 'obItem' is possibly 'null' or 'undefined'. -src/app/api/warehouse/outbound/fifo/route.ts(295,20): error TS2339: Property 'unit' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'unit' does not exist on type 'string'. -src/app/api/warehouse/outbound/fifo/route.ts(296,13): error TS18049: 'obItem' is possibly 'null' or 'undefined'. -src/app/api/warehouse/outbound/fifo/route.ts(296,20): error TS2339: Property 'unit_cost' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'unit_cost' does not exist on type 'string'. -src/app/api/warehouse/outbound/fifo/route.ts(297,13): error TS18049: 'obItem' is possibly 'null' or 'undefined'. -src/app/api/warehouse/outbound/fifo/route.ts(297,20): error TS2339: Property 'amount' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'amount' does not exist on type 'string'. -src/app/api/warehouse/outbound/fifo/route.ts(298,13): error TS18049: 'obItem' is possibly 'null' or 'undefined'. -src/app/api/warehouse/outbound/fifo/route.ts(298,20): error TS2339: Property 'batch_no' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'batch_no' does not exist on type 'string'. -src/app/api/warehouse/outbound/fifo/route.ts(299,25): error TS18049: 'obItem' is possibly 'null' or 'undefined'. -src/app/api/warehouse/outbound/fifo/route.ts(299,32): error TS2339: Property 'batch_no' does not exist on type 'string | number | boolean | Date | Buffer | readonly SqlValue[]'. - Property 'batch_no' does not exist on type 'string'. -src/app/api/warehouse/outbound/fifo/route.ts(345,18): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/warehouse/outbound/fifo/route.ts(349,21): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/warehouse/outbound/fifo/route.ts(360,17): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/warehouse/outbound/fifo/route.ts(376,23): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/warehouse/outbound/fifo/route.ts(380,23): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/warehouse/outbound/fifo/route.ts(398,26): error TS2339: Property 'affectedRows' does not exist on type 'QueryResult'. - Property 'affectedRows' does not exist on type 'ResultSetHeader[]'. -src/app/api/warehouse/outbound/fifo/route.ts(406,38): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/warehouse/outbound/fifo/route.ts(406,62): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/warehouse/outbound/fifo/route.ts(429,11): error TS2353: Object literal may only specify known properties, and 'batch_id' does not exist in type 'Date | Buffer | readonly SqlValue[]'. -src/app/api/warehouse/outbound/fifo/route.ts(466,29): error TS2339: Property 'affectedRows' does not exist on type 'QueryResult'. - Property 'affectedRows' does not exist on type 'ResultSetHeader[]'. -src/app/api/warehouse/outbound/route.ts(171,59): error TS2345: Argument of type '(string | number | boolean | Date | Buffer | null | undefined)[]' is not assignable to parameter of type '(number | null | undefined)[]'. - Type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to type 'number | null | undefined'. - Type 'string' is not assignable to type 'number'. -src/app/api/warehouse/outbound/route.ts(194,57): error TS2345: Argument of type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to parameter of type 'string'. - Type 'undefined' is not assignable to type 'string'. -src/app/api/warehouse/outbound/route.ts(199,29): error TS2345: Argument of type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to parameter of type 'string'. - Type 'undefined' is not assignable to type 'string'. -src/app/api/warehouse/outbound/route.ts(199,59): error TS2345: Argument of type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to parameter of type 'string'. - Type 'undefined' is not assignable to type 'string'. -src/app/api/warehouse/outbound/route.ts(226,24): error TS2352: Conversion of type 'QueryResult' to type 'DbRow' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first. - Type '[RowDataPacket[], ResultSetHeader]' is not comparable to type 'DbRow'. - Index signature for type 'string' is missing in type '[RowDataPacket[], ResultSetHeader]'. -src/app/api/warehouse/outbound/route.ts(238,23): error TS2345: Argument of type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to parameter of type 'string'. - Type 'undefined' is not assignable to type 'string'. -src/app/api/warehouse/outbound/route.ts(238,53): error TS2345: Argument of type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to parameter of type 'string'. - Type 'undefined' is not assignable to type 'string'. -src/app/api/warehouse/outbound/route.ts(241,43): error TS2345: Argument of type 'string | number | boolean | Date | Buffer' is not assignable to parameter of type 'string'. - Type 'number' is not assignable to type 'string'. -src/app/api/warehouse/production-inbound/route.ts(90,18): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/warehouse/production-inbound/route.ts(93,18): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/warehouse/production-inbound/route.ts(115,35): error TS2339: Property 'insertId' does not exist on type 'QueryResult'. - Property 'insertId' does not exist on type 'ResultSetHeader[]'. -src/app/api/warehouse/production-inbound/route.ts(153,23): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/warehouse/production-inbound/route.ts(157,23): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/warehouse/production-inbound/route.ts(183,35): error TS2339: Property 'reduce' does not exist on type 'QueryResult'. - Property 'reduce' does not exist on type 'OkPacket'. -src/app/api/warehouse/production-inbound/route.ts(198,20): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/warehouse/production-inbound/route.ts(199,22): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/warehouse/production-inbound/route.ts(213,42): error TS2339: Property 'reduce' does not exist on type 'QueryResult'. - Property 'reduce' does not exist on type 'OkPacket'. -src/app/api/warehouse/production-inbound/route.ts(223,24): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/warehouse/route.ts(112,17): error TS2322: Type 'SqlValue[]' is not assignable to type 'DbRow[]'. - Type 'SqlValue' is not assignable to type 'DbRow'. - Type 'undefined' is not assignable to type 'DbRow'. -src/app/api/warehouse/route.ts(119,35): error TS2538: Type 'Buffer' cannot be used as an index type. -src/app/api/warehouse/route.ts(119,35): error TS2538: Type 'Date' cannot be used as an index type. -src/app/api/warehouse/route.ts(119,35): error TS2538: Type 'false' cannot be used as an index type. -src/app/api/warehouse/route.ts(119,35): error TS2538: Type 'null' cannot be used as an index type. -src/app/api/warehouse/route.ts(119,35): error TS2538: Type 'true' cannot be used as an index type. -src/app/api/warehouse/route.ts(119,35): error TS2538: Type 'undefined' cannot be used as an index type. -src/app/api/warehouse/route.ts(121,5): error TS2322: Type 'string | number | true | Date | Buffer' is not assignable to type 'string | undefined'. - Type 'number' is not assignable to type 'string'. -src/app/api/warehouse/route.ts(125,5): error TS2322: Type 'string | number | true | Date | Buffer' is not assignable to type 'string | undefined'. - Type 'number' is not assignable to type 'string'. -src/app/api/warehouse/route.ts(126,5): error TS2322: Type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to type 'number | undefined'. - Type 'null' is not assignable to type 'number | undefined'. -src/app/api/warehouse/route.ts(179,50): error TS2345: Argument of type '(number | DbRow)[]' is not assignable to parameter of type 'SqlValue[]'. - Type 'number | DbRow' is not assignable to type 'SqlValue'. - Type 'DbRow' is not assignable to type 'SqlValue'. - Type 'DbRow' is missing the following properties from type 'Buffer': slice, subarray, write, toJSON, and 102 more. -src/app/api/warehouse/route.ts(185,46): error TS2345: Argument of type 'DbRow[]' is not assignable to parameter of type 'SqlValue[]'. - Type 'DbRow' is not assignable to type 'SqlValue'. - Type 'DbRow' is missing the following properties from type 'Buffer': slice, subarray, write, toJSON, and 102 more. -src/app/api/warehouse/route.ts(224,36): error TS2533: Object is possibly 'null' or 'undefined'. -src/app/api/warehouse/route.ts(224,36): error TS2723: Cannot invoke an object which is possibly 'null' or 'undefined'. -src/app/api/warehouse/route.ts(224,37): error TS2352: Conversion of type 'PoolConnection' to type 'DbRow' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first. - Index signature for type 'string' is missing in type 'PoolConnection'. -src/app/api/warehouse/route.ts(224,58): error TS2349: This expression is not callable. - No constituent of type 'string | number | boolean | Date | Buffer' is callable. -src/app/api/warehouse/route.ts(295,36): error TS2533: Object is possibly 'null' or 'undefined'. -src/app/api/warehouse/route.ts(295,36): error TS2723: Cannot invoke an object which is possibly 'null' or 'undefined'. -src/app/api/warehouse/route.ts(295,37): error TS2352: Conversion of type 'PoolConnection' to type 'DbRow' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first. - Index signature for type 'string' is missing in type 'PoolConnection'. -src/app/api/warehouse/route.ts(295,58): error TS2349: This expression is not callable. - No constituent of type 'string | number | boolean | Date | Buffer' is callable. -src/app/api/warehouse/sales-outbound/route.ts(85,57): error TS2345: Argument of type '(string | number | boolean | Date | Buffer | null | undefined)[]' is not assignable to parameter of type '(number | null | undefined)[]'. - Type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to type 'number | null | undefined'. - Type 'string' is not assignable to type 'number'. -src/app/api/warehouse/sales-outbound/route.ts(116,21): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/warehouse/sales-outbound/route.ts(119,11): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/warehouse/sales-outbound/route.ts(122,11): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/warehouse/sales-outbound/route.ts(137,19): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/warehouse/sales-outbound/route.ts(141,18): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/warehouse/sales-outbound/route.ts(143,68): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/warehouse/sales-outbound/route.ts(162,36): error TS2339: Property 'insertId' does not exist on type 'QueryResult'. - Property 'insertId' does not exist on type 'ResultSetHeader[]'. -src/app/api/warehouse/sales-outbound/route.ts(203,24): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/warehouse/sales-outbound/route.ts(207,24): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/warehouse/sales-outbound/route.ts(224,21): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/warehouse/sales-outbound/route.ts(228,21): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/warehouse/sales-outbound/route.ts(251,43): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/warehouse/sales-outbound/route.ts(251,56): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/warehouse/sales-outbound/route.ts(293,33): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/warehouse/sales-outbound/route.ts(293,46): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/warehouse/sales-outbound/route.ts(341,39): error TS2339: Property 'reduce' does not exist on type 'QueryResult'. - Property 'reduce' does not exist on type 'OkPacket'. -src/app/api/warehouse/sales-outbound/route.ts(352,44): error TS2339: Property 'filter' does not exist on type 'QueryResult'. - Property 'filter' does not exist on type 'OkPacket'. -src/app/api/warehouse/sales-outbound/route.ts(372,45): error TS2339: Property 'every' does not exist on type 'QueryResult'. - Property 'every' does not exist on type 'OkPacket'. -src/app/api/warehouse/sales-outbound/route.ts(375,45): error TS2339: Property 'some' does not exist on type 'QueryResult'. - Property 'some' does not exist on type 'OkPacket'. -src/app/api/warehouse/split-order/route.ts(65,34): error TS2362: The left-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. -src/app/api/warehouse/split-order/route.ts(66,60): error TS2322: Type 'string | number | true | Date | Buffer' is not assignable to type 'number'. - Type 'string' is not assignable to type 'number'. -src/app/api/warehouse/split-order/route.ts(87,39): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/warehouse/split-order/route.ts(91,21): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/warehouse/split-order/route.ts(118,49): error TS2339: Property 'slice' does not exist on type 'string | number | true | Date | Buffer'. - Property 'slice' does not exist on type 'number'. -src/app/api/warehouse/split-order/route.ts(199,29): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/warehouse/split-order/route.ts(203,21): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/app/api/warehouse/split-order/route.ts(215,33): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/app/api/warehouse/split-order/route.ts(291,42): error TS2339: Property 'slice' does not exist on type 'string | number | true | Date | Buffer'. - Property 'slice' does not exist on type 'number'. -src/app/api/warehouse/stocktaking/[id]/items/route.ts(39,41): error TS2538: Type 'Buffer' cannot be used as an index type. -src/app/api/warehouse/stocktaking/[id]/items/route.ts(39,41): error TS2538: Type 'Date' cannot be used as an index type. -src/app/api/warehouse/stocktaking/[id]/items/route.ts(39,41): error TS2538: Type 'false' cannot be used as an index type. -src/app/api/warehouse/stocktaking/[id]/items/route.ts(39,41): error TS2538: Type 'null' cannot be used as an index type. -src/app/api/warehouse/stocktaking/[id]/items/route.ts(39,41): error TS2538: Type 'true' cannot be used as an index type. -src/app/api/warehouse/stocktaking/[id]/items/route.ts(39,41): error TS2538: Type 'undefined' cannot be used as an index type. -src/app/api/warehouse/stocktaking/[id]/items/route.ts(40,33): error TS2538: Type 'Buffer' cannot be used as an index type. -src/app/api/warehouse/stocktaking/[id]/items/route.ts(40,33): error TS2538: Type 'Date' cannot be used as an index type. -src/app/api/warehouse/stocktaking/[id]/items/route.ts(40,33): error TS2538: Type 'false' cannot be used as an index type. -src/app/api/warehouse/stocktaking/[id]/items/route.ts(40,33): error TS2538: Type 'null' cannot be used as an index type. -src/app/api/warehouse/stocktaking/[id]/items/route.ts(40,33): error TS2538: Type 'true' cannot be used as an index type. -src/app/api/warehouse/stocktaking/[id]/items/route.ts(40,33): error TS2538: Type 'undefined' cannot be used as an index type. -src/app/api/warehouse/stocktaking/route.ts(68,27): error TS2538: Type 'Buffer' cannot be used as an index type. -src/app/api/warehouse/stocktaking/route.ts(68,27): error TS2538: Type 'Date' cannot be used as an index type. -src/app/api/warehouse/stocktaking/route.ts(68,27): error TS2538: Type 'false' cannot be used as an index type. -src/app/api/warehouse/stocktaking/route.ts(68,27): error TS2538: Type 'null' cannot be used as an index type. -src/app/api/warehouse/stocktaking/route.ts(68,27): error TS2538: Type 'true' cannot be used as an index type. -src/app/api/warehouse/stocktaking/route.ts(68,27): error TS2538: Type 'undefined' cannot be used as an index type. -src/app/api/warehouse/stocktaking/route.ts(69,31): error TS2538: Type 'Buffer' cannot be used as an index type. -src/app/api/warehouse/stocktaking/route.ts(69,31): error TS2538: Type 'Date' cannot be used as an index type. -src/app/api/warehouse/stocktaking/route.ts(69,31): error TS2538: Type 'false' cannot be used as an index type. -src/app/api/warehouse/stocktaking/route.ts(69,31): error TS2538: Type 'null' cannot be used as an index type. -src/app/api/warehouse/stocktaking/route.ts(69,31): error TS2538: Type 'true' cannot be used as an index type. -src/app/api/warehouse/stocktaking/route.ts(69,31): error TS2538: Type 'undefined' cannot be used as an index type. -src/app/api/warehouse/stocktaking/route.ts(231,44): error TS2339: Property 'quantity' does not exist on type '{}'. -src/app/api/warehouse/transfer/[id]/inbound/route.ts(88,22): error TS2769: No overload matches this call. - Overload 1 of 2, '(sql: string, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to type 'ExecuteValues'. - Type 'undefined' is not assignable to type 'ExecuteValues'. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/warehouse/transfer/[id]/outbound/route.ts(84,22): error TS2769: No overload matches this call. - Overload 1 of 2, '(sql: string, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to type 'ExecuteValues'. - Type 'undefined' is not assignable to type 'ExecuteValues'. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/warehouse/transfer/route.ts(62,27): error TS2538: Type 'Buffer' cannot be used as an index type. -src/app/api/warehouse/transfer/route.ts(62,27): error TS2538: Type 'Date' cannot be used as an index type. -src/app/api/warehouse/transfer/route.ts(62,27): error TS2538: Type 'false' cannot be used as an index type. -src/app/api/warehouse/transfer/route.ts(62,27): error TS2538: Type 'null' cannot be used as an index type. -src/app/api/warehouse/transfer/route.ts(62,27): error TS2538: Type 'true' cannot be used as an index type. -src/app/api/warehouse/transfer/route.ts(62,27): error TS2538: Type 'undefined' cannot be used as an index type. -src/app/api/warehouse/transfer/route.ts(63,31): error TS2538: Type 'Buffer' cannot be used as an index type. -src/app/api/warehouse/transfer/route.ts(63,31): error TS2538: Type 'Date' cannot be used as an index type. -src/app/api/warehouse/transfer/route.ts(63,31): error TS2538: Type 'false' cannot be used as an index type. -src/app/api/warehouse/transfer/route.ts(63,31): error TS2538: Type 'null' cannot be used as an index type. -src/app/api/warehouse/transfer/route.ts(63,31): error TS2538: Type 'true' cannot be used as an index type. -src/app/api/warehouse/transfer/route.ts(63,31): error TS2538: Type 'undefined' cannot be used as an index type. -src/app/api/warehouse/transfer/route.ts(119,59): error TS2345: Argument of type '(string | number | boolean | Date | Buffer | null | undefined)[]' is not assignable to parameter of type '(number | null | undefined)[]'. - Type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to type 'number | null | undefined'. - Type 'string' is not assignable to type 'number'. -src/app/api/workflow/process/route.ts(100,5): error TS18046: 'workflow' is of type 'unknown'. -src/app/api/workflow/process/route.ts(102,11): error TS18046: 'workflow' is of type 'unknown'. -src/app/api/workflow/process/route.ts(123,9): error TS18046: 'workflow' is of type 'unknown'. -src/app/api/workflow/process/route.ts(125,19): error TS18046: 'workflow' is of type 'unknown'. -src/app/api/workflow/process/route.ts(126,21): error TS18046: 'workflow' is of type 'unknown'. -src/app/api/workorders/route.ts(141,11): error TS2533: Object is possibly 'null' or 'undefined'. -src/app/api/workorders/route.ts(141,11): error TS2365: Operator '>' cannot be applied to types 'string | number | boolean | Date | Buffer' and 'number'. -src/app/api/workorders/route.ts(148,39): error TS2365: Operator '+' cannot be applied to types 'number' and 'string | number | boolean | Date | Buffer'. -src/app/api/workorders/route.ts(172,28): error TS2352: Conversion of type 'QueryResult' to type 'DbRow' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first. - Type '[RowDataPacket[], ResultSetHeader]' is not comparable to type 'DbRow'. - Index signature for type 'string' is missing in type '[RowDataPacket[], ResultSetHeader]'. -src/app/api/workorders/route.ts(202,13): error TS2362: The left-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. -src/app/api/workorders/route.ts(202,13): error TS18049: 'bomLine.quantity' is possibly 'null' or 'undefined'. -src/app/api/workorders/route.ts(202,53): error TS2362: The left-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. -src/app/api/workorders/route.ts(204,13): error TS2769: No overload matches this call. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/workorders/route.ts(339,26): error TS2769: No overload matches this call. - Overload 1 of 2, '(sql: string, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'SqlValue[]' is not assignable to parameter of type 'ExecuteValues | undefined'. - Type 'SqlValue[]' is not assignable to type 'ExecuteValues[]'. - Type 'SqlValue' is not assignable to type 'ExecuteValues'. - Type 'undefined' is not assignable to type 'ExecuteValues'. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/workorders/route.ts(346,44): error TS2769: No overload matches this call. - Overload 1 of 2, '(sql: string, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to type 'ExecuteValues'. - Type 'undefined' is not assignable to type 'ExecuteValues'. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/workorders/route.ts(424,46): error TS2769: No overload matches this call. - Overload 1 of 2, '(sql: string, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to type 'ExecuteValues'. - Type 'undefined' is not assignable to type 'ExecuteValues'. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/app/api/workorders/route.ts(433,24): error TS2769: No overload matches this call. - Overload 1 of 2, '(sql: string, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to type 'ExecuteValues'. - Type 'undefined' is not assignable to type 'ExecuteValues'. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/application/handlers/DeliveryShippedHandler.ts(24,25): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/application/handlers/DeliveryShippedHandler.ts(25,41): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/application/handlers/DeliveryShippedHandler.ts(33,29): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/application/handlers/DeliveryShippedHandler.ts(42,27): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/application/handlers/DeliveryShippedHandler.ts(43,46): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/application/handlers/DeliveryShippedHandler.ts(44,37): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/application/handlers/DeliveryShippedHandler.ts(48,16): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/application/handlers/DeliveryShippedHandler.ts(53,46): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/application/handlers/MaterialReturnInventoryHandler.ts(21,25): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/application/handlers/MaterialReturnInventoryHandler.ts(22,26): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/application/handlers/MaterialReturnInventoryHandler.ts(56,29): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/application/handlers/MaterialReturnInventoryHandler.ts(59,46): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/application/handlers/OutboundInventoryHandler.ts(20,25): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/application/handlers/OutboundInventoryHandler.ts(21,41): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/application/handlers/OutboundInventoryHandler.ts(29,44): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/application/handlers/OutboundInventoryHandler.ts(38,27): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/application/handlers/OutboundInventoryHandler.ts(39,46): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/application/handlers/OutboundInventoryHandler.ts(40,37): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/application/handlers/OutboundInventoryHandler.ts(44,16): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/application/handlers/OutboundInventoryHandler.ts(49,46): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/application/handlers/OutboundInventoryHandler.ts(58,25): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/application/handlers/OutboundInventoryHandler.ts(59,26): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/application/handlers/PurchasePayableHandler.ts(64,20): error TS2769: No overload matches this call. - Overload 1 of 2, '(sql: string, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to type 'ExecuteValues'. - Type 'undefined' is not assignable to type 'ExecuteValues'. - Overload 2 of 2, '(options: QueryOptions, values?: ExecuteValues | undefined): Promise<[QueryResult, FieldPacket[]]>', gave the following error. - Argument of type 'string' is not assignable to parameter of type 'QueryOptions'. -src/application/handlers/PurchaseReceivedHandler.ts(17,25): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/application/handlers/PurchaseReceivedHandler.ts(20,29): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/application/handlers/PurchaseReceivedHandler.ts(35,27): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/application/handlers/PurchaseReceivedHandler.ts(38,44): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/application/handlers/PurchaseReconciliationWrittenOffHandler.ts(29,15): error TS2322: Type '[QueryResult, FieldPacket[]]' is not assignable to type 'DbRow[]'. - Type 'QueryResult | FieldPacket[]' is not assignable to type 'DbRow'. - Type 'OkPacket' is not assignable to type 'DbRow'. - Index signature for type 'string' is missing in type 'OkPacket'. -src/application/handlers/PurchaseReconciliationWrittenOffHandler.ts(46,36): error TS18049: 'payable' is possibly 'null' or 'undefined'. -src/application/handlers/PurchaseReconciliationWrittenOffHandler.ts(46,44): error TS2339: Property 'paid_amount' does not exist on type 'string | number | boolean | Date | Buffer'. - Property 'paid_amount' does not exist on type 'string'. -src/application/handlers/PurchaseReconciliationWrittenOffHandler.ts(47,39): error TS18049: 'payable' is possibly 'null' or 'undefined'. -src/application/handlers/PurchaseReconciliationWrittenOffHandler.ts(47,47): error TS2339: Property 'balance' does not exist on type 'string | number | boolean | Date | Buffer'. - Property 'balance' does not exist on type 'string'. -src/application/handlers/PurchaseReconciliationWrittenOffHandler.ts(69,32): error TS18049: 'payable' is possibly 'null' or 'undefined'. -src/application/handlers/PurchaseReconciliationWrittenOffHandler.ts(69,40): error TS2339: Property 'status' does not exist on type 'string | number | boolean | Date | Buffer'. - Property 'status' does not exist on type 'string'. -src/application/handlers/PurchaseReconciliationWrittenOffHandler.ts(85,22): error TS18049: 'payable' is possibly 'null' or 'undefined'. -src/application/handlers/PurchaseReconciliationWrittenOffHandler.ts(85,30): error TS2339: Property 'payable_no' does not exist on type 'string | number | boolean | Date | Buffer'. - Property 'payable_no' does not exist on type 'string'. -src/application/handlers/ReconciliationWriteOffHandler.ts(47,14): error TS2304: Cannot find name 'DbResult'. -src/application/handlers/SalesReceivableHandler.ts(26,14): error TS2304: Cannot find name 'DbResult'. -src/application/handlers/SalesShippedHandler.ts(18,25): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/application/handlers/SalesShippedHandler.ts(19,41): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/application/handlers/SalesShippedHandler.ts(27,29): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/application/handlers/SalesShippedHandler.ts(36,27): error TS2339: Property 'length' does not exist on type 'QueryResult'. - Property 'length' does not exist on type 'OkPacket'. -src/application/handlers/SalesShippedHandler.ts(37,46): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/application/handlers/SalesShippedHandler.ts(38,37): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/application/handlers/SalesShippedHandler.ts(42,16): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/application/handlers/SalesShippedHandler.ts(47,46): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'QueryResult'. - Property '0' does not exist on type 'QueryResult'. -src/application/handlers/SalesToWorkOrderHandler.ts(167,5): error TS2322: Type '{ materialId: string | number | boolean | Date | Buffer | null | undefined; materialCode: string | number | boolean | Date | Buffer<...> | null | undefined; materialName: string | ... 5 more ... | undefined; requiredQty: number; unit: string | ... 3 more ... | Buffer<...>; }[]' is not assignable to type 'MaterialRequirementItem[]'. - Type '{ materialId: string | number | boolean | Date | Buffer | null | undefined; materialCode: string | number | boolean | Date | Buffer | null | undefined; materialName: string | ... 5 more ... | undefined; requiredQty: number; unit: string | ... 3 more ... | Buffer; }' is not assignable to type 'MaterialRequirementItem'. - Types of property 'materialId' are incompatible. - Type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to type 'number'. - Type 'undefined' is not assignable to type 'number'. -src/application/handlers/SalesToWorkOrderHandler.ts(171,20): error TS2362: The left-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. -src/application/handlers/SalesToWorkOrderHandler.ts(171,20): error TS18049: 'row.quantity' is possibly 'null' or 'undefined'. -src/application/services/SampleProcessCardService.ts(370,12): error TS2304: Cannot find name 'DbResult'. -src/components/layout/header.tsx(170,48): error TS2304: Cannot find name 'DbRow'. -src/components/SystemConfigInitializer.tsx(39,38): error TS2345: Argument of type 'unknown' is not assignable to parameter of type 'string'. -src/infrastructure/repositories/DrizzleInboundOrderRepository.ts(327,5): error TS2322: Type '{ id: number; orderNo: string | number | boolean | Date | Buffer | null | undefined; }' is not assignable to type '{ id: number; orderNo: string; }'. - Types of property 'orderNo' are incompatible. - Type 'string | number | boolean | Date | Buffer | null | undefined' is not assignable to type 'string'. - Type 'undefined' is not assignable to type 'string'. -src/lib/fifo-config.ts(15,43): error TS2345: Argument of type 'string | number | true | Date | Buffer' is not assignable to parameter of type '"off" | "hint" | "force"'. - Type 'string' is not assignable to type '"off" | "hint" | "force"'. -src/lib/fifo-config.ts(38,10): error TS2365: Operator '>' cannot be applied to types 'string | number | boolean | Date | Buffer' and 'number'. diff --git a/_vitest_inbound.txt b/_vitest_inbound.txt deleted file mode 100644 index 90612bc9..00000000 --- a/_vitest_inbound.txt +++ /dev/null @@ -1,297 +0,0 @@ - - RUN  v4.1.5 D:/dcprint/erp-project - - ✓ tests/unit/domain/purchase/entities/purchase-order-line-receive.test.ts > PurchaseOrderLine.receive 部分收货 / 超收守卫 > IT-IN-002 部分收货:多次入库累计 receivedQty 4ms - ✓ tests/unit/domain/purchase/entities/purchase-order-line-receive.test.ts > PurchaseOrderLine.receive 部分收货 / 超收守卫 > ERR-IN 超收拒绝:累计超过 orderQty*(1+tolerance) 抛 DomainError,状态不变 2ms - ✓ tests/unit/domain/purchase/entities/purchase-order-line-receive.test.ts > PurchaseOrderLine.receive 部分收货 / 超收守卫 > ERR-IN 容差边界:恰好等于上限不抛错 1ms - ✓ tests/unit/domain/purchase/entities/purchase-order-line-receive.test.ts > PurchaseOrderLine.receive 部分收货 / 超收守卫 > ERR-IN 入库数量必须大于0 1ms - ✓ tests/unit/domain/purchase/entities/purchase-order-line-receive.test.ts > PurchaseOrderLine.receive 部分收货 / 超收守卫 > IT-IN 已关闭行不允许再入库 1ms - ✓ tests/unit/domain/purchase/entities/purchase-order-line-receive.test.ts > PurchaseOrderLine.receive 部分收货 / 超收守卫 > reverseReceive 回补:减少 receivedQty,且不能超过已收量 1ms - ✓ tests/unit/domain/purchase/entities/purchase-order-line-receive.test.ts > PurchaseOrderLine.receive 部分收货 / 超收守卫 > isFullyReceived 正确反映收货完成度 0ms -stderr | tests/integration/inbound-api.test.ts -[RepoRegistry] active impl = mysql (default; set REPOSITORY_IMPL=drizzle to switch) - -[2026-08-27 11:37:22.373 +0800] DEBUG: Inventory cost recalculated (rollback) - inventoryId: 413 - rollbackQty: 50 - rollbackUnitPrice: 10 - oldCostPrice: 0 - newCostPrice: 0 - newTotalAmount: 0 -[2026-08-27 11:37:22.395 +0800] INFO: Inventory rolled back for unapproved inbound order - orderNo: "IN_RB_1787801842340" -[2026-08-27 11:37:22.404 +0800] WARN: Inbound order not found for rollback - inboundId: 99999999 -[2026-08-27 11:37:22.405 +0800] INFO: Inventory rolled back for unapproved inbound order - orderNo: "IN_RB_MISSING" -[2026-08-27 11:37:22.443 +0800] DEBUG: Inventory cost recalculated (inbound) - inventoryId: 414 - inboundQty: 50 - inboundUnitPrice: 10 - oldCostPrice: 0 - newCostPrice: 5 - newTotalAmount: 500 -[2026-08-27 11:37:22.447 +0800] INFO: 采购订单聚合根重建完成 - module: "purchase-inbound" - action: "sync" - inboundId: 1 - poId: 151 - ✓ tests/integration/inbound-unapprove-rollback.test.ts > 入库反审核回滚 (unapprove → 库存/批次/流水回退) > IT-unapprove-1 库存数量回到审核前(0) 39ms - ✓ tests/integration/inbound-unapprove-rollback.test.ts > 入库反审核回滚 (unapprove → 库存/批次/流水回退) > IT-unapprove-2 批次归零后软删 2ms - ✓ tests/integration/inbound-unapprove-rollback.test.ts > 入库反审核回滚 (unapprove → 库存/批次/流水回退) > IT-unapprove-3 生成反向(return)流水 2ms - ✓ tests/integration/inbound-unapprove-rollback.test.ts > 入库反审核回滚 (unapprove → 库存/批次/流水回退) > IT-unapprove-4 入库单不存在时不抛错、安全跳过 4ms -[2026-08-27 11:37:22.452 +0800] INFO: 采购订单已收量与状态更新完成 - module: "purchase-inbound" - action: "sync" - inboundId: 1 - poId: 151 -[2026-08-27 11:37:22.452 +0800] INFO: Purchase order synced from inbound approval - poId: 151 - inboundNo: "IN_FP_1787801842433_1" - newStatus: "partially_received" - receivedQuantity: 50 -[2026-08-27 11:37:22.462 +0800] INFO: Inventory synced for inbound order - orderNo: "IN_TEST_1787801842433_1" - itemCount: 1 -[2026-08-27 11:37:22.473 +0800] INFO: 采购订单聚合根重建完成 - module: "purchase-inbound" - action: "sync" - inboundId: 2 - poId: 151 -[2026-08-27 11:37:22.476 +0800] INFO: 采购订单已收量与状态更新完成 - module: "purchase-inbound" - action: "sync" - inboundId: 2 - poId: 151 -[2026-08-27 11:37:22.476 +0800] INFO: Purchase order synced from inbound approval - poId: 151 - inboundNo: "IN_FP_1787801842470_2" - newStatus: "partially_received" - receivedQuantity: 110 -[2026-08-27 11:37:22.478 +0800] INFO: 采购订单聚合根重建完成 - module: "purchase-inbound" - action: "sync" - inboundId: 2 - poId: 152 -[2026-08-27 11:37:22.483 +0800] INFO: 采购订单已收量与状态更新完成 - module: "purchase-inbound" - action: "sync" - inboundId: 2 - poId: 152 -[2026-08-27 11:37:22.483 +0800] INFO: Purchase order synced from inbound approval - poId: 152 - inboundNo: "IN_TEST_1787801842469_2" - newStatus: "partially_received" - receivedQuantity: 50 -stderr | tests/integration/inbound-api.test.ts > 入库 API 集成测试 > GET /api/warehouse/inbound - 查询入库单列表 > 正常流程:返回分页列表数据 -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) - -[2026-08-27 11:37:22.492 +0800] INFO: 采购订单聚合根重建完成 - module: "purchase-inbound" - action: "sync" - inboundId: 3 - poId: 151 -[2026-08-27 11:37:22.492 +0800] ERROR: PurchaseInboundSync 失败 [phase=receive] - module: "purchase-inbound" - action: "sync" - inboundId: 3 - poId: 151 -stderr | tests/integration/inbound-api.test.ts > 入库 API 集成测试 > GET /api/warehouse/inbound - 查询入库单列表 > 支持按状态筛选 -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) - -stderr | tests/integration/inbound-api.test.ts > 入库 API 集成测试 > GET /api/warehouse/inbound - 查询入库单列表 > 使用默认分页参数(page=1, pageSize=10) -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) - -[2026-08-27 11:37:22.502 +0800] DEBUG: Inventory cost recalculated (inbound) - inventoryId: 415 - inboundQty: 40 - inboundUnitPrice: 10 - oldCostPrice: 0 - newCostPrice: 5 - newTotalAmount: 400 -stderr | tests/integration/inbound-api.test.ts > 入库 API 集成测试 > GET /api/warehouse/inbound - 查询入库单列表 > service 抛错时返回 500 -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) - -[2026-08-27 11:37:22.506 +0800] INFO: Payable created - inboundNo: "IN_TEST_1787801842495_3" - totalAmount: 500 - supplierId: 1 - sourceType: 1 -[2026-08-27 11:37:22.509 +0800] INFO: Payable already exists for source, skip - inboundNo: "IN_TEST_1787801842495_3" - sourceType: 1 - existingId: 41 -stderr | tests/integration/inbound-api.test.ts > 入库 API 集成测试 > POST /api/warehouse/inbound - 创建入库单 > 正常流程:创建成功返回 order_id 和 order_no -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) - -[2026-08-27 11:37:22.516 +0800] INFO: Inventory synced for inbound order - orderNo: "IN_FP_1787801842495_4" - itemCount: 1 -[2026-08-27 11:37:22.519 +0800] DEBUG: Inventory cost recalculated (inbound) - inventoryId: 414 - inboundQty: 60 - inboundUnitPrice: 10 - oldCostPrice: 5 - newCostPrice: 6.4706 - newTotalAmount: 1100 -stderr | tests/integration/inbound-api.test.ts > 入库 API 集成测试 > POST /api/warehouse/inbound - 创建入库单 > service 抛 DomainError 时返回 500(未被路由捕获) -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) - -stderr | tests/integration/inbound-api.test.ts > 入库 API 集成测试 > PUT /api/warehouse/inbound - 更新入库单 > 正常流程:submit 动作调用 submitOrder -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) - -stderr | tests/integration/inbound-api.test.ts > 入库 API 集成测试 > PUT /api/warehouse/inbound - 更新入库单 > 正常流程:approve 动作调用 approveOrder -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) - -stderr | tests/integration/inbound-api.test.ts > 入库 API 集成测试 > PUT /api/warehouse/inbound - 更新入库单 > 正常流程:cancel 动作调用 cancelOrder -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) - -stderr | tests/integration/inbound-api.test.ts > 入库 API 集成测试 > PUT /api/warehouse/inbound - 更新入库单 > 正常流程:unapprove 动作调用 unapproveOrder -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) - -stderr | tests/integration/inbound-api.test.ts > 入库 API 集成测试 > PUT /api/warehouse/inbound - 更新入库单 > 使用 status=pending 等价于 submit 动作 -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) - -stderr | tests/integration/inbound-api.test.ts > 入库 API 集成测试 > PUT /api/warehouse/inbound - 更新入库单 > 使用 status=approved 等价于 approve 动作 -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) - -stderr | tests/integration/inbound-api.test.ts > 入库 API 集成测试 > PUT /api/warehouse/inbound - 更新入库单 > 领域异常:NotFoundError 返回 404 -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) - -stderr | tests/integration/inbound-api.test.ts > 入库 API 集成测试 > PUT /api/warehouse/inbound - 更新入库单 > 领域异常:VersionConflictError 返回 409 -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) - -stderr | tests/integration/inbound-api.test.ts > 入库 API 集成测试 > PUT /api/warehouse/inbound - 更新入库单 > 领域异常:DomainError 返回 400 -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) - -[2026-08-27 11:37:22.534 +0800] INFO: Inventory synced for inbound order - orderNo: "IN_TEST_1787801842511_4" - itemCount: 1 -[2026-08-27 11:37:22.537 +0800] INFO: 采购订单聚合根重建完成 - module: "purchase-inbound" - action: "sync" - inboundId: 4 - poId: 152 -stderr | tests/integration/inbound-api.test.ts > 入库 API 集成测试 > PUT /api/warehouse/inbound - 更新入库单 > 仅更新 remark 字段时走直接 SQL 更新 -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) - -[2026-08-27 11:37:22.540 +0800] INFO: 采购订单已收量与状态更新完成 - module: "purchase-inbound" - action: "sync" - inboundId: 4 - poId: 152 -[2026-08-27 11:37:22.540 +0800] INFO: Purchase order synced from inbound approval - poId: 152 - inboundNo: "IN_TEST_1787801842511_4" - newStatus: "partially_received" - receivedQuantity: 110 -stderr | tests/integration/inbound-api.test.ts > 入库 API 集成测试 > DELETE /api/warehouse/inbound - 删除入库单 > 正常流程:删除草稿状态入库单 -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) - -stderr | tests/integration/inbound-api.test.ts > 入库 API 集成测试 > DELETE /api/warehouse/inbound - 删除入库单 > 领域异常:NotFoundError 返回 404 -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) - -stderr | tests/integration/inbound-api.test.ts > 入库 API 集成测试 > DELETE /api/warehouse/inbound - 删除入库单 > 领域异常:DomainError(已审核不能删除)返回 400 -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) - -stderr | tests/integration/inbound-api.test.ts > 入库 API 集成测试 > DELETE /api/warehouse/inbound - 删除入库单 > 参数异常:id 非数字时正常处理为 parseInt -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) - - ✓ tests/integration/inbound-api.test.ts > 入库 API 集成测试 > GET /api/warehouse/inbound - 查询入库单列表 > 正常流程:返回分页列表数据 17ms - ✓ tests/integration/inbound-api.test.ts > 入库 API 集成测试 > GET /api/warehouse/inbound - 查询入库单列表 > 支持按状态筛选 4ms - ✓ tests/integration/inbound-api.test.ts > 入库 API 集成测试 > GET /api/warehouse/inbound - 查询入库单列表 > 使用默认分页参数(page=1, pageSize=10) 3ms - ✓ tests/integration/inbound-api.test.ts > 入库 API 集成测试 > GET /api/warehouse/inbound - 查询入库单列表 > service 抛错时返回 500 2ms - ✓ tests/integration/inbound-api.test.ts > 入库 API 集成测试 > POST /api/warehouse/inbound - 创建入库单 > 正常流程:创建成功返回 order_id 和 order_no 8ms - ✓ tests/integration/inbound-api.test.ts > 入库 API 集成测试 > POST /api/warehouse/inbound - 创建入库单 > 参数异常:缺少 warehouse_id 返回 422 3ms - ✓ tests/integration/inbound-api.test.ts > 入库 API 集成测试 > POST /api/warehouse/inbound - 创建入库单 > 参数异常:items 为空数组返回 422 2ms - ✓ tests/integration/inbound-api.test.ts > 入库 API 集成测试 > POST /api/warehouse/inbound - 创建入库单 > 参数异常:quantity 为负数返回 422 1ms - ✓ tests/integration/inbound-api.test.ts > 入库 API 集成测试 > POST /api/warehouse/inbound - 创建入库单 > 参数异常:unit_price 为负数返回 422 2ms - ✓ tests/integration/inbound-api.test.ts > 入库 API 集成测试 > POST /api/warehouse/inbound - 创建入库单 > service 抛 DomainError 时返回 500(未被路由捕获) 2ms - ✓ tests/integration/inbound-api.test.ts > 入库 API 集成测试 > POST /api/warehouse/inbound - 创建入库单 > ERR-IN 物料未归类且 require_on_business 阻断时返回 400 1ms - ✓ tests/integration/inbound-api.test.ts > 入库 API 集成测试 > PUT /api/warehouse/inbound - 更新入库单 > 正常流程:submit 动作调用 submitOrder 2ms - ✓ tests/integration/inbound-api.test.ts > 入库 API 集成测试 > PUT /api/warehouse/inbound - 更新入库单 > 正常流程:approve 动作调用 approveOrder 2ms - ✓ tests/integration/inbound-api.test.ts > 入库 API 集成测试 > PUT /api/warehouse/inbound - 更新入库单 > 正常流程:cancel 动作调用 cancelOrder 1ms - ✓ tests/integration/inbound-api.test.ts > 入库 API 集成测试 > PUT /api/warehouse/inbound - 更新入库单 > 正常流程:unapprove 动作调用 unapproveOrder 1ms - ✓ tests/integration/inbound-api.test.ts > 入库 API 集成测试 > PUT /api/warehouse/inbound - 更新入库单 > 使用 status=pending 等价于 submit 动作 1ms - ✓ tests/integration/inbound-api.test.ts > 入库 API 集成测试 > PUT /api/warehouse/inbound - 更新入库单 > 使用 status=approved 等价于 approve 动作 1ms - ✓ tests/integration/inbound-api.test.ts > 入库 API 集成测试 > PUT /api/warehouse/inbound - 更新入库单 > 领域异常:NotFoundError 返回 404 1ms - ✓ tests/integration/inbound-api.test.ts > 入库 API 集成测试 > PUT /api/warehouse/inbound - 更新入库单 > 领域异常:VersionConflictError 返回 409 1ms - ✓ tests/integration/inbound-api.test.ts > 入库 API 集成测试 > PUT /api/warehouse/inbound - 更新入库单 > 领域异常:DomainError 返回 400 2ms - ✓ tests/integration/inbound-api.test.ts > 入库 API 集成测试 > PUT /api/warehouse/inbound - 更新入库单 > 参数异常:缺少 id 返回 422 1ms - ✓ tests/integration/inbound-api.test.ts > 入库 API 集成测试 > PUT /api/warehouse/inbound - 更新入库单 > 未知 action 返回 422(schema 枚举校验) 1ms - ✓ tests/integration/inbound-api.test.ts > 入库 API 集成测试 > PUT /api/warehouse/inbound - 更新入库单 > 仅更新 remark 字段时走直接 SQL 更新 2ms - ✓ tests/integration/inbound-api.test.ts > 入库 API 集成测试 > DELETE /api/warehouse/inbound - 删除入库单 > 正常流程:删除草稿状态入库单 1ms - ✓ tests/integration/inbound-api.test.ts > 入库 API 集成测试 > DELETE /api/warehouse/inbound - 删除入库单 > 参数异常:缺少 id 返回 400 1ms - ✓ tests/integration/inbound-api.test.ts > 入库 API 集成测试 > DELETE /api/warehouse/inbound - 删除入库单 > 领域异常:NotFoundError 返回 404 1ms - ✓ tests/integration/inbound-api.test.ts > 入库 API 集成测试 > DELETE /api/warehouse/inbound - 删除入库单 > 领域异常:DomainError(已审核不能删除)返回 400 1ms - ✓ tests/integration/inbound-api.test.ts > 入库 API 集成测试 > DELETE /api/warehouse/inbound - 删除入库单 > 参数异常:id 非数字时正常处理为 parseInt 1ms - ✓ tests/integration/inbound-api.test.ts > 入库 API 集成测试 > PATCH /api/warehouse/inbound - 恢复已删除入库单(软删除撤销) > 正常流程:恢复已删除的入库单 2ms - ✓ tests/integration/inbound-api.test.ts > 入库 API 集成测试 > PATCH /api/warehouse/inbound - 恢复已删除入库单(软删除撤销) > 参数异常:缺少 id 返回 400 1ms - ✓ tests/integration/inbound-api.test.ts > 入库 API 集成测试 > PATCH /api/warehouse/inbound - 恢复已删除入库单(软删除撤销) > 记录不存在:未找到已删除记录返回 404 1ms - ✓ tests/integration/inbound-api.test.ts > 入库 API 集成测试 > PATCH /api/warehouse/inbound - 恢复已删除入库单(软删除撤销) > DB 查询错误返回 500 1ms - ✓ tests/integration/inbound-api.test.ts > 入库 API 集成测试 > PATCH /api/warehouse/inbound - 恢复已删除入库单(软删除撤销) > DB 更新错误返回 500 1ms - ✓ tests/integration/inbound-api.test.ts > 入库 API 集成测试 > PATCH /api/warehouse/inbound - 恢复已删除入库单(软删除撤销) > 非数字 id 正常处理为 parseInt 1ms -[2026-08-27 11:37:22.510 +0800] INFO: [warehouse/inbound] 开始物料分类校验 - itemCount: 1 - materialIds: [ - 101 - ] -[2026-08-27 11:37:22.510 +0800] INFO: [warehouse/inbound] 物料分类校验完成 - blocked: false - uncategorizedCount: 0 -[2026-08-27 11:37:22.521 +0800] INFO: [warehouse/inbound] 开始物料分类校验 - itemCount: 1 - materialIds: [ - 101 - ] -[2026-08-27 11:37:22.521 +0800] INFO: [warehouse/inbound] 物料分类校验完成 - blocked: false - uncategorizedCount: 0 -[2026-08-27 11:37:22.523 +0800] INFO: [warehouse/inbound] 开始物料分类校验 - itemCount: 1 - materialIds: [ - 101 - ] -[2026-08-27 11:37:22.523 +0800] INFO: [warehouse/inbound] 物料分类校验完成 - blocked: true - uncategorizedCount: 1 -[2026-08-27 11:37:22.523 +0800] WARN: [warehouse/inbound] 物料分类校验阻断提交 - message: "以下物料尚未设置物料分类:M001(材料1)。请先在「基础数据 → 物料分类」中归类。" -[2026-08-27 11:37:22.565 +0800] INFO: Payable created - inboundNo: "IN_TEST_1787801842511_4" - totalAmount: 600 - supplierId: 1 - sourceType: 1 - ✓ tests/integration/inbound-approve-chain.test.ts > 入库审核全链路 (approve → 库存/批次/流水/PO/财务) > IT-approve-1 InventorySyncHandler:库存+批次+流水正确写入 37ms - ✓ tests/integration/inbound-approve-chain.test.ts > 入库审核全链路 (approve → 库存/批次/流水/PO/财务) > IT-approve-2 PurchaseInboundSyncHandler:PO 收货量回写 26ms - ✓ tests/integration/inbound-approve-chain.test.ts > 入库审核全链路 (approve → 库存/批次/流水/PO/财务) > IT-approve-3 FinanceVoucherHandler:财务过账且幂等(ERR-IN-006) 16ms - ✓ tests/integration/inbound-from-po.test.ts > 入库 from-PO 业务集成(部分收货 / 超收拒绝 / 库存联动) > IT-IN-002 部分收货:两次入库累计回写 PO received_qty 与状态 56ms - ✓ tests/integration/inbound-from-po.test.ts > 入库 from-PO 业务集成(部分收货 / 超收拒绝 / 库存联动) > ERR-IN / IT-IN-003 超收拒绝:累计超上限抛错且 PO 状态回滚不变 6ms - ✓ tests/integration/inbound-from-po.test.ts > 入库 from-PO 业务集成(部分收货 / 超收拒绝 / 库存联动) > IT-IN 库存联动:from-PO 入库在守卫通过后会写入库存 25ms - ✓ tests/integration/inbound-approve-chain.test.ts > 入库审核全链路 (approve → 库存/批次/流水/PO/财务) > IT-approve-4 全链路:同一事件被三 handler 消费后各表一致 58ms - - Test Files  5 passed (5) - Tests  52 passed (52) - Start at  11:37:16 - Duration  11.56s (transform 3.31s, setup 106ms, import 9.21s, tests 801ms, environment 15.30s) - diff --git a/_vitest_run.txt b/_vitest_run.txt deleted file mode 100644 index cd9eeb4a..00000000 --- a/_vitest_run.txt +++ /dev/null @@ -1,9065 +0,0 @@ - - RUN  v4.1.5 D:/dcprint/erp-project - - ✓ src/lib/__tests__/fifo-allocation.test.ts > FIFO Allocation Logic > allocateFIFO - quantity calculation > should calculate shortage when total available is less than required 3468ms - ✓ src/lib/__tests__/fifo-allocation.test.ts > FIFO Allocation Logic > allocateFIFO - quantity calculation > should allocate across multiple batches when single batch is insufficient 5ms - ✓ src/lib/__tests__/fifo-allocation.test.ts > FIFO Allocation Logic > allocateFIFO - quantity calculation > should return zero allocations when no batches available 2ms - ✓ src/lib/__tests__/fifo-allocation.test.ts > FIFO Allocation Logic > allocateFIFO - quantity calculation > should return zero shortage when exactly enough stock 1ms - ✓ src/lib/__tests__/fifo-allocation.test.ts > FIFO Allocation Logic > allocateFIFO - quantity calculation > should include version field in allocation items 1ms - ✓ src/lib/__tests__/fifo-allocation.test.ts > FIFO Allocation Logic > allocateFIFO - quantity calculation > should use FOR UPDATE in batch query SQL 1ms - ✓ src/lib/__tests__/fifo-allocation.test.ts > FIFO Allocation Logic > executeFIFODeductionWithRetry - concurrency safety > should throw error when optimistic lock fails (affectedRows = 0) 4ms - ✓ src/lib/__tests__/fifo-allocation.test.ts > FIFO Allocation Logic > executeFIFODeductionWithRetry - concurrency safety > should include version increment in UPDATE SQL 2ms - ✓ src/lib/__tests__/fifo-allocation.test.ts > FIFO Allocation Logic > executeSpecifiedBatchDeduction - validation > should throw error when batch does not exist 2ms - ✓ src/lib/__tests__/fifo-allocation.test.ts > FIFO Allocation Logic > executeSpecifiedBatchDeduction - validation > should throw error when available quantity is insufficient 1ms - ✓ src/lib/__tests__/fifo-allocation.test.ts > FIFO Allocation Logic > executeSpecifiedBatchDeduction - validation > should use SELECT FOR UPDATE to lock the batch row 1ms - ✓ tests/unit/domain/warehouse/entities/inbound-item.test.ts > 8.2 InboundItem 实体 > create() 工厂 > 合法参数创建成功 13ms - × tests/unit/domain/warehouse/entities/inbound-item.test.ts > 8.2 InboundItem 实体 > create() 工厂 > materialId 为 0 抛错 22ms - → expected [Function] to throw an error - ✓ tests/unit/domain/warehouse/entities/inbound-item.test.ts > 8.2 InboundItem 实体 > create() 工厂 > quantity <= 0 抛错 2ms - ✓ tests/unit/domain/warehouse/entities/inbound-item.test.ts > 8.2 InboundItem 实体 > create() 工厂 > 默认值回退(unit/unitPrice/materialCode 等) 1ms - ✓ tests/unit/domain/warehouse/entities/inbound-item.test.ts > 8.2 InboundItem 实体 > reconstitute() 重建 > 从 DB 字段重建(跳过校验) 1ms - ✓ tests/unit/domain/warehouse/entities/inbound-item.test.ts > 8.2 InboundItem 实体 > totalPrice 计算 > quantity * unitPrice 四舍五入到 2 位小数 1ms - ✓ tests/unit/domain/warehouse/entities/inbound-item.test.ts > 8.2 InboundItem 实体 > totalPrice 计算 > unitPrice 缺省为 0 时 totalPrice 为 0 1ms - × src/tests/utils/auth.test.ts > Authentication Utils > authFetch > should add Authorization header when token exists 50ms - → expected { 'Content-Type': 'application/json' } to have property "Authorization" with value 'Bearer test-token' - ✓ src/tests/utils/auth.test.ts > Authentication Utils > authFetch > should not add Authorization header when token does not exist 2ms - ✓ tests/unit/inventory-sync.test.ts > inventory-sync > checkInventoryAvailability > 库存充足时返回 success 14ms - ✓ tests/unit/inventory-sync.test.ts > inventory-sync > checkInventoryAvailability > 无库存记录时返回失败 2ms - ✓ tests/unit/inventory-sync.test.ts > inventory-sync > checkInventoryAvailability > 可用库存不足时返回失败 1ms - ✓ tests/unit/inventory-sync.test.ts > inventory-sync > checkInventoryAvailability > 指定 batchNo 时 SQL 包含批次过滤 7ms - ✓ tests/unit/inventory-sync.test.ts > inventory-sync > checkInventoryAvailability > 查询抛错时返回失败并记录错误 2ms - ✓ tests/unit/inventory-sync.test.ts > inventory-sync > adjustInventory - 入库 > 库存记录已存在时更新库存并记录流水 7ms - × tests/unit/inventory-sync.test.ts > inventory-sync > adjustInventory - 入库 > 库存记录不存在且为入库时创建新记录 44ms - → expected false to be true // Object.is equality - ✓ tests/unit/inventory-sync.test.ts > inventory-sync > adjustInventory - 出库 > 库存不足时返回失败不执行调整 1ms - ✓ tests/unit/inventory-sync.test.ts > inventory-sync > adjustInventory - 出库 > 库存充足时执行出库扣减 2ms - × tests/unit/inventory-sync.test.ts > inventory-sync > adjustInventory - 出库 > 事务内更新后可用库存变负时抛错回滚 6ms - → expected '库存调整失败,请稍后重试' to contain '可用库存不足' - × tests/unit/inventory-sync.test.ts > inventory-sync > adjustInventory - 出库 > 库存记录不存在且为出库时抛错 3ms - → expected '库存调整失败,请稍后重试' to contain '库存记录不存在' - ✓ tests/unit/inventory-sync.test.ts > inventory-sync > adjustInventory - 出库 > 事务抛错时记录日志并返回失败 1ms - ✓ tests/unit/inventory-sync.test.ts > inventory-sync > adjustInventory - 出库 > 使用默认值(batchNo 为空、operatorId 缺省) 2ms - ✓ tests/unit/inventory-sync.test.ts > inventory-sync > lockInventory > 库存检查不通过时直接返回失败 1ms - ✓ tests/unit/inventory-sync.test.ts > inventory-sync > lockInventory > 库存充足时锁定成功 2ms - × tests/unit/inventory-sync.test.ts > inventory-sync > lockInventory > 锁定量超过可用时抛错 3ms - → expected '库存锁定失败,请稍后重试' to contain '可用库存不足' - × tests/unit/inventory-sync.test.ts > inventory-sync > lockInventory > 库存记录不存在时抛错 5ms - → expected '库存锁定失败,请稍后重试' to contain '库存记录不存在' - ✓ tests/unit/inventory-sync.test.ts > inventory-sync > unlockInventory > 解锁成功并恢复可用库存 2ms - ✓ tests/unit/inventory-sync.test.ts > inventory-sync > unlockInventory > 解锁量大于锁定量时 newLocked 不为负 1ms - × tests/unit/inventory-sync.test.ts > inventory-sync > unlockInventory > 库存记录不存在时抛错 1ms - → expected '库存解锁失败,请稍后重试' to contain '库存记录不存在' - ✓ tests/unit/inventory-sync.test.ts > inventory-sync > unlockInventory > 事务抛错时返回失败 1ms - ✓ tests/unit/inventory-sync.test.ts > inventory-sync > getNegativeStockWarnings > 返回预警列表并标记类型与级别 2ms - ✓ tests/unit/inventory-sync.test.ts > inventory-sync > getNegativeStockWarnings > 查询无数据时返回空数组 1ms - ✓ tests/unit/inventory-sync.test.ts > inventory-sync > getInventoryLogs > 返回带操作类型标签的流水列表 1ms - ✓ tests/unit/inventory-sync.test.ts > inventory-sync > getInventoryLogs > 无筛选条件时查询全部 1ms - ✓ tests/unit/inventory-sync.test.ts > inventory-sync > getInventoryLogs > 未知 operation_type 时使用默认标签 0ms - × tests/unit/inventory-sync.test.ts > inventory-sync > 乐观锁并发冲突保护 > adjustInventory 检测到 version 不匹配时返回并发冲突失败 2ms - → expected '库存调整失败,请稍后重试' to contain '并发冲突' - × tests/unit/inventory-sync.test.ts > inventory-sync > 乐观锁并发冲突保护 > lockInventory 检测到 version 不匹配时返回并发冲突失败 1ms - → expected '库存锁定失败,请稍后重试' to contain '并发冲突' - × tests/unit/inventory-sync.test.ts > inventory-sync > 乐观锁并发冲突保护 > unlockInventory 检测到 version 不匹配时返回并发冲突失败 1ms - → expected '库存解锁失败,请稍后重试' to contain '并发冲突' - ✓ tests/unit/inventory-sync.test.ts > inventory-sync > 乐观锁并发冲突保护 > adjustInventory version 匹配时正常更新并自增 version 1ms - ✓ tests/unit/material-requisition.test.ts > 物料领用管理 - material-requisition > autoGenerateRequisition - 自动生成领料单 > 工单不存在时返回失败 9ms - ✓ tests/unit/material-requisition.test.ts > 物料领用管理 - material-requisition > autoGenerateRequisition - 自动生成领料单 > BOM 不存在时返回失败 1ms - ✓ tests/unit/material-requisition.test.ts > 物料领用管理 - material-requisition > autoGenerateRequisition - 自动生成领料单 > 成功生成领料单(含 FIFO 推荐) 12ms - ✓ tests/unit/material-requisition.test.ts > 物料领用管理 - material-requisition > autoGenerateRequisition - 自动生成领料单 > 事务异常时返回失败并记录日志 3ms - ✓ tests/unit/material-requisition.test.ts > 物料领用管理 - material-requisition > issueMaterial - 扫码领料出库 > 领料单不存在时返回失败 1ms - ✓ tests/unit/material-requisition.test.ts > 物料领用管理 - material-requisition > issueMaterial - 扫码领料出库 > 领料单状态不正确时返回失败 1ms - ✓ tests/unit/material-requisition.test.ts > 物料领用管理 - material-requisition > issueMaterial - 扫码领料出库 > 整料检查失败时抛错并返回失败 1ms - ✓ tests/unit/material-requisition.test.ts > 物料领用管理 - material-requisition > issueMaterial - 扫码领料出库 > FIFO 校验失败时返回失败 1ms - ✓ tests/unit/material-requisition.test.ts > 物料领用管理 - material-requisition > issueMaterial - 扫码领料出库 > 库存扣减失败时返回失败 2ms - ✓ tests/unit/material-requisition.test.ts > 物料领用管理 - material-requisition > issueMaterial - 扫码领料出库 > 成功出库(单物料) 3ms - ✓ tests/unit/material-requisition.test.ts > 物料领用管理 - material-requisition > issueMaterial - 扫码领料出库 > 成功出库(多物料) 1ms - ✓ tests/unit/material-requisition.test.ts > 物料领用管理 - material-requisition > createReturn - 创建退料单 > 成功创建退料单(单物料) 2ms - ✓ tests/unit/material-requisition.test.ts > 物料领用管理 - material-requisition > createReturn - 创建退料单 > 批次成本不存在时单价为 0 1ms - ✓ tests/unit/material-requisition.test.ts > 物料领用管理 - material-requisition > createReturn - 创建退料单 > 事务异常时返回失败并记录日志 2ms - ✓ tests/unit/material-requisition.test.ts > 物料领用管理 - material-requisition > confirmReturn - 确认退料入库 > 退料单不存在时返回失败 2ms - ✓ tests/unit/material-requisition.test.ts > 物料领用管理 - material-requisition > confirmReturn - 确认退料入库 > 退料单状态不正确时返回失败 1ms - ✓ tests/unit/material-requisition.test.ts > 物料领用管理 - material-requisition > confirmReturn - 确认退料入库 > 库存回写失败时返回失败 1ms - ✓ tests/unit/material-requisition.test.ts > 物料领用管理 - material-requisition > confirmReturn - 确认退料入库 > 成功确认退料入库(单明细) 0ms - ✓ tests/unit/material-requisition.test.ts > 物料领用管理 - material-requisition > confirmReturn - 确认退料入库 > 成功确认退料入库(多明细) 0ms - ✓ tests/unit/material-requisition.test.ts > 物料领用管理 - material-requisition > confirmReturn - 确认退料入库 > 异常时返回失败并记录日志 3ms - ✓ tests/unit/material-requisition.test.ts > 物料领用管理 - material-requisition > 分支覆盖 - 空可选参数与异常路径 > autoGenerateRequisition 不传申请人且 warehouse_id 为 null 1ms - ✓ tests/unit/material-requisition.test.ts > 物料领用管理 - material-requisition > 分支覆盖 - 空可选参数与异常路径 > submitOverRequisition 不传申请人参数 1ms - × tests/unit/material-requisition.test.ts > 物料领用管理 - material-requisition > 分支覆盖 - 空可选参数与异常路径 > submitOverRequisition 异常路径触发 catch 16ms - → expected true to be false // Object.is equality - × tests/unit/material-requisition.test.ts > 物料领用管理 - material-requisition > 分支覆盖 - 空可选参数与异常路径 > submitSupplementaryRequisition 不需要双重审批且不传申请人 2ms - → expected false to be true // Object.is equality - × tests/unit/material-requisition.test.ts > 物料领用管理 - material-requisition > 分支覆盖 - 空可选参数与异常路径 > submitSupplementaryRequisition 异常路径触发 catch 2ms - → expected true to be false // Object.is equality - ✓ tests/unit/material-requisition.test.ts > 物料领用管理 - material-requisition > 分支覆盖 - 空可选参数与异常路径 > approveRequisition 异常路径触发 catch 2ms - ✓ tests/unit/material-requisition.test.ts > 物料领用管理 - material-requisition > 分支覆盖 - 空可选参数与异常路径 > createReturn 物料查询为空时使用默认值 1ms - ✓ tests/unit/material-requisition.test.ts > 物料领用管理 - material-requisition > 分支覆盖 - 空可选参数与异常路径 > issueMaterial 不传 operatorId 1ms - ✓ tests/unit/material-requisition.test.ts > 物料领用管理 - material-requisition > 分支覆盖 - 空可选参数与异常路径 > autoGenerateRequisition 配置 mr_prefix 为空时使用默认 MR 0ms - × tests/unit/material-requisition.test.ts > 物料领用管理 - material-requisition > 分支覆盖 - 空可选参数与异常路径 > submitOverRequisition 配置 mr_prefix 为空时使用默认 MR 1ms - → expected false to be true // Object.is equality - ✓ tests/unit/material-requisition.test.ts > 物料领用管理 - material-requisition > 分支覆盖 - 空可选参数与异常路径 > submitSupplementaryRequisition 配置 mr_prefix 为空时使用默认 MR 0ms - ✓ tests/unit/material-requisition.test.ts > 物料领用管理 - material-requisition > 分支覆盖 - 空可选参数与异常路径 > issueMaterial 不传 batchNo 时使用空字符串 1ms - ✓ tests/unit/material-requisition.test.ts > 物料领用管理 - material-requisition > 分支覆盖 - 空可选参数与异常路径 > approveRequisition 不传审批人参数 0ms - ✓ tests/unit/material-requisition.test.ts > 物料领用管理 - material-requisition > 分支覆盖 - 空可选参数与异常路径 > autoGenerateRequisition FIFO 推荐批次字段为空时使用默认值 1ms - ✓ tests/unit/material-requisition.test.ts > 物料领用管理 - material-requisition > 分支覆盖 - 空可选参数与异常路径 > issueMaterial FIFO 校验无效但无需审批时继续出库 1ms - × src/app/api/system/config/route.test.ts > 系统配置API测试 > GET - 查询配置 > 应该返回配置列表 191ms - → Target cannot be null or undefined. - ✓ src/app/api/system/config/route.test.ts > 系统配置API测试 > GET - 查询配置 > 应该支持按名称搜索 94ms - ✓ src/app/api/system/config/route.test.ts > 系统配置API测试 > GET - 查询配置 > 应该支持按键搜索 3ms - ✓ src/app/api/system/config/route.test.ts > 系统配置API测试 > GET - 查询配置 > 应该使用默认分页参数 18ms - ✓ src/app/api/system/config/route.test.ts > 系统配置API测试 > POST - 创建配置 > 应该创建新配置 11ms - ✓ src/app/api/system/config/route.test.ts > 系统配置API测试 > POST - 创建配置 > 应该使用默认类型 5ms - ✓ src/app/api/system/config/route.test.ts > 系统配置API测试 > PUT - 更新配置 > 应该更新配置 3ms - ✓ src/app/api/system/config/route.test.ts > 系统配置API测试 > DELETE - 删除配置 > 应该删除配置 3ms - ✓ src/app/api/system/config/route.test.ts > 系统配置API测试 > DELETE - 删除配置 > 应该拒绝缺少id的删除 6ms - × tests/integration/test-data-validation.test.ts > 步骤 11: FIFO 追溯链 > 11a. 批次可用量扣减 > 批次 A 可用量 = 1200(初始量 1500,扣减后) 52ms - → expected [] to have a length of 1 but got +0 - × tests/integration/test-data-validation.test.ts > 步骤 11: FIFO 追溯链 > 11a. 批次可用量扣减 > 批次 B 可用量 = 900(初始量 1000,扣减后) 5ms - → expected [] to have a length of 1 but got +0 - × tests/integration/test-data-validation.test.ts > 步骤 11: FIFO 追溯链 > 11a. 批次可用量扣减 > 批次 C 可用量 = 300(初始量 500,扣减后) 4ms - → expected [] to have a length of 1 but got +0 - × tests/integration/test-data-validation.test.ts > 步骤 11: FIFO 追溯链 > 11b. 领料明细批次号一致性 > 领料明细数量 = 3 25ms - → expected 'BATCH-20260701-A' to be null // Object.is equality - × tests/integration/test-data-validation.test.ts > 步骤 11: FIFO 追溯链 > 11c. FIFO 模拟验证 > 最早入库批次仍有可用量(优先消耗) 17ms - → expected 'TRF-TR202608259027-MAT006' to be 'BATCH-20260701-A' // Object.is equality - ✓ tests/integration/test-data-validation.test.ts > 步骤 12: 生产全链路 > 12a. 工单状态 > 工单 WO-2026-001 存在且状态 = 1(待生产),关联销售订单 7ms - ✓ tests/integration/test-data-validation.test.ts > 步骤 12: 生产全链路 > 12b. 排程关联 > 排程 SCH-2026-001 关联工单 WO-2026-001 和销售订单 SO-2026-001 5ms - ✓ tests/integration/test-data-validation.test.ts > 步骤 12: 生产全链路 > 12c. 领料单关联 > 领料单 MR-2026-001 关联工单 WO-2026-001,状态 = 2(已发料) 5ms - ✓ tests/integration/test-data-validation.test.ts > 步骤 12: 生产全链路 > 12c. 领料单关联 > 领料明细 = 3 条(黑色油墨300kg / 白色油墨100kg / 网版200个) 9ms - ✓ tests/integration/test-data-validation.test.ts > 步骤 13: HR 薪资计算 > 13a. 计件明细总产量 > 计件明细总产量 = 1266 件,记录数 = 4 8ms - ✓ tests/integration/test-data-validation.test.ts > 步骤 13: HR 薪资计算 > 13a. 计件明细总产量 > 计件总额 = 549.80 元 6ms - ✓ tests/integration/test-data-validation.test.ts > 步骤 13: HR 薪资计算 > 13b. 计件单价 > 计件单价 PRINT = 0.5 7ms - ✓ tests/integration/test-data-validation.test.ts > 步骤 13: HR 薪资计算 > 13b. 计件单价 > 计件单价 DIE_CUT = 0.3 5ms - ✓ tests/integration/test-data-validation.test.ts > 步骤 13: HR 薪资计算 > 13b. 计件单价 > 计件单价 INSPECT = 0.2 8ms - ✓ tests/integration/test-data-validation.test.ts > 步骤 13: HR 薪资计算 > 13c. 薪资计算结果 > 基本工资 = 8000 5ms - ✓ tests/integration/test-data-validation.test.ts > 步骤 13: HR 薪资计算 > 13c. 薪资计算结果 > 计件工资 = 549.80 3ms - ✓ tests/integration/test-data-validation.test.ts > 步骤 13: HR 薪资计算 > 13c. 薪资计算结果 > 绩效工资 = 1000,补贴 = 500 4ms - ✓ tests/integration/test-data-validation.test.ts > 步骤 13: HR 薪资计算 > 13c. 薪资计算结果 > 应发工资 = 10049.8(基本+计件+绩效+补贴) 10ms - ✓ tests/integration/test-data-validation.test.ts > 步骤 13: HR 薪资计算 > 13c. 薪资计算结果 > 社保个人 = 840(基数8000 * 10.5%) 10ms - ✓ tests/integration/test-data-validation.test.ts > 步骤 13: HR 薪资计算 > 13c. 薪资计算结果 > 公积金个人 = 960(基数8000 * 12%) 4ms - ✓ tests/integration/test-data-validation.test.ts > 步骤 13: HR 薪资计算 > 13c. 薪资计算结果 > 个税 = 324.98(应税3249.7999999999993 * 10%) 15ms - ✓ tests/integration/test-data-validation.test.ts > 步骤 13: HR 薪资计算 > 13c. 薪资计算结果 > 扣款合计 = 2124.98(社保+公积金+个税) 6ms - ✓ tests/integration/test-data-validation.test.ts > 步骤 13: HR 薪资计算 > 13c. 薪资计算结果 > 实发工资 = 7924.82,且实发 = 应发 - 扣款合计 25ms - ✓ tests/integration/test-data-validation.test.ts > 步骤 13: HR 薪资计算 > 13d. 薪资档案 > 薪资档案存在:基本工资8000,社保基数8000,公积金比例12%,个税起征点5000 6ms - ✓ tests/integration/test-data-validation.test.ts > 步骤 14: 多币种与汇率 > 汇率表有 USD→CNY 记录,汇率 = 7.2 6ms - × tests/integration/test-data-validation.test.ts > 步骤 14: 多币种与汇率 > PO-2026-001 (CNY, rate=1.0) > base_* = 原币 * 1.0 16ms - → expected [] to have a length of 1 but got +0 - × tests/integration/test-data-validation.test.ts > 步骤 14: 多币种与汇率 > PO-2026-002 (USD, rate=7.2) > 币种 = USD,汇率 = 7.2 48ms - → expected [] to have a length of 1 but got +0 - × tests/integration/test-data-validation.test.ts > 步骤 14: 多币种与汇率 > PO-2026-002 (USD, rate=7.2) > base_total = 12000 * 7.2 = 86400 5ms - → Cannot read properties of undefined (reading 'base_total_amount') - × tests/integration/test-data-validation.test.ts > 步骤 14: 多币种与汇率 > PO-2026-002 (USD, rate=7.2) > base_tax = 1560 * 7.2 = 11232 6ms - → Cannot read properties of undefined (reading 'base_tax_amount') - × tests/integration/test-data-validation.test.ts > 步骤 14: 多币种与汇率 > PO-2026-002 (USD, rate=7.2) > base_grand = 13560 * 7.2 = 97632 3ms - → Cannot read properties of undefined (reading 'base_grand_total') - × tests/integration/test-data-validation.test.ts > 步骤 14: 多币种与汇率 > PO-2026-002 (USD, rate=7.2) > 明细 base_unit_price = 12 * 7.2 = 86.4,base_amount = 86400 3ms - → Cannot read properties of undefined (reading 'id') - × tests/integration/test-data-validation.test.ts > 步骤 14: 多币种与汇率 > PO-2026-003 (CNY, rate=1.0) > base_total = total * rate 3ms - → Cannot read properties of undefined (reading 'currency') - × tests/integration/test-data-validation.test.ts > 步骤 14: 多币种与汇率 > 入库单 INB-2026-002 (USD) > 币种 = USD,汇率 = 7.2,base_total = total * rate 7ms - → expected [] to have a length of 1 but got +0 - ✓ tests/integration/test-data-validation.test.ts > 步骤 15: 外键关联完整性 > 采购订单明细 → 采购订单 无孤立记录 3ms - ✓ tests/integration/test-data-validation.test.ts > 步骤 15: 外键关联完整性 > 采购订单明细 → 物料 无孤立记录 5ms - ✓ tests/integration/test-data-validation.test.ts > 步骤 15: 外键关联完整性 > 入库明细 → 入库单 无孤立记录 6ms - ✓ tests/integration/test-data-validation.test.ts > 步骤 15: 外键关联完整性 > 库存批次 → 物料 无孤立记录 39ms - ✓ tests/integration/test-data-validation.test.ts > 步骤 15: 外键关联完整性 > 库存批次 → 仓库 无孤立记录 3ms - ✓ tests/integration/test-data-validation.test.ts > 步骤 15: 外键关联完整性 > 排程 → 工单 无孤立记录 4ms - ✓ tests/integration/test-data-validation.test.ts > 步骤 15: 外键关联完整性 > 领料明细 → 领料单 无孤立记录 4ms - ✓ tests/integration/test-data-validation.test.ts > 步骤 15: 外键关联完整性 > 薪资计算 → 员工 无孤立记录 5ms - × src/app/[locale]/warehouse/inbound/__tests__/useInboundData.test.ts > useInboundData > 初始状态为空 149ms - → Failed to call `useTranslations` because the context from `NextIntlClientProvider` was not found. - -This can happen because: -1) You intended to render this component as a Server Component, the render - failed, and therefore React attempted to render the component on the client - instead. If this is the case, check the console for server errors. -2) You intended to render this component on the client side, but no context was found. - Learn more about this error here: https://next-intl.dev/docs/environments/server-client-components#missing-context - × src/app/[locale]/warehouse/inbound/__tests__/useInboundData.test.ts > useInboundData > 挂载后并行拉取入库单、仓库、分类、供应商、标签 24ms - → Failed to call `useTranslations` because the context from `NextIntlClientProvider` was not found. - -This can happen because: -1) You intended to render this component as a Server Component, the render - failed, and therefore React attempted to render the component on the client - instead. If this is the case, check the console for server errors. -2) You intended to render this component on the client side, but no context was found. - Learn more about this error here: https://next-intl.dev/docs/environments/server-client-components#missing-context - × src/app/[locale]/warehouse/inbound/__tests__/useInboundData.test.ts > useInboundData > fetchInboundRecords 处理 data 为数组的响应 33ms - → Failed to call `useTranslations` because the context from `NextIntlClientProvider` was not found. - -This can happen because: -1) You intended to render this component as a Server Component, the render - failed, and therefore React attempted to render the component on the client - instead. If this is the case, check the console for server errors. -2) You intended to render this component on the client side, but no context was found. - Learn more about this error here: https://next-intl.dev/docs/environments/server-client-components#missing-context - × src/app/[locale]/warehouse/inbound/__tests__/useInboundData.test.ts > useInboundData > fetchInboundRecords 处理 API 返回失败 8ms - → Failed to call `useTranslations` because the context from `NextIntlClientProvider` was not found. - -This can happen because: -1) You intended to render this component as a Server Component, the render - failed, and therefore React attempted to render the component on the client - instead. If this is the case, check the console for server errors. -2) You intended to render this component on the client side, but no context was found. - Learn more about this error here: https://next-intl.dev/docs/environments/server-client-components#missing-context - × src/app/[locale]/warehouse/inbound/__tests__/useInboundData.test.ts > useInboundData > fetchInboundRecords 处理网络异常 9ms - → Failed to call `useTranslations` because the context from `NextIntlClientProvider` was not found. - -This can happen because: -1) You intended to render this component as a Server Component, the render - failed, and therefore React attempted to render the component on the client - instead. If this is the case, check the console for server errors. -2) You intended to render this component on the client side, but no context was found. - Learn more about this error here: https://next-intl.dev/docs/environments/server-client-components#missing-context - × src/app/[locale]/warehouse/inbound/__tests__/useInboundData.test.ts > useInboundData > handleRefresh 刷新数据并显示成功提示 4ms - → Failed to call `useTranslations` because the context from `NextIntlClientProvider` was not found. - -This can happen because: -1) You intended to render this component as a Server Component, the render - failed, and therefore React attempted to render the component on the client - instead. If this is the case, check the console for server errors. -2) You intended to render this component on the client side, but no context was found. - Learn more about this error here: https://next-intl.dev/docs/environments/server-client-components#missing-context - × src/components/ui/input.test.tsx > Input 组件测试 > 应该正确渲染基础输入框 104ms - → Invalid Chai property: toBeInTheDocument - × src/components/ui/table.perf.test.tsx > Table组件渲染性能测试 > 应该在合理时间内渲染基础表格 219ms - → Invalid Chai property: toBeInTheDocument - × src/components/ui/input.test.tsx > Input 组件测试 > 应该支持不同类型 420ms - → Invalid Chai property: toHaveAttribute - ✓ src/components/ui/input.test.tsx > Input 组件测试 > 应该处理输入事件 35ms - × src/components/ui/input.test.tsx > Input 组件测试 > 应该支持禁用状态 40ms - → Invalid Chai property: toBeDisabled - × src/app/[locale]/warehouse/inbound/__tests__/useInboundData.test.ts > useInboundData > setSearchQuery 更新搜索关键词 6ms - → Failed to call `useTranslations` because the context from `NextIntlClientProvider` was not found. - -This can happen because: -1) You intended to render this component as a Server Component, the render - failed, and therefore React attempted to render the component on the client - instead. If this is the case, check the console for server errors. -2) You intended to render this component on the client side, but no context was found. - Learn more about this error here: https://next-intl.dev/docs/environments/server-client-components#missing-context - × src/app/[locale]/warehouse/inbound/__tests__/useInboundData.test.ts > useInboundData > setSelectedRecords 更新选中记录 6ms - → Failed to call `useTranslations` because the context from `NextIntlClientProvider` was not found. - -This can happen because: -1) You intended to render this component as a Server Component, the render - failed, and therefore React attempted to render the component on the client - instead. If this is the case, check the console for server errors. -2) You intended to render this component on the client side, but no context was found. - Learn more about this error here: https://next-intl.dev/docs/environments/server-client-components#missing-context - × src/app/[locale]/warehouse/inbound/__tests__/useInboundData.test.ts > useInboundData > totalInboundToday 统计今日入库数量 7ms - → Failed to call `useTranslations` because the context from `NextIntlClientProvider` was not found. - -This can happen because: -1) You intended to render this component as a Server Component, the render - failed, and therefore React attempted to render the component on the client - instead. If this is the case, check the console for server errors. -2) You intended to render this component on the client side, but no context was found. - Learn more about this error here: https://next-intl.dev/docs/environments/server-client-components#missing-context - × src/app/[locale]/warehouse/inbound/__tests__/useInboundData.test.ts > useInboundData > totalInboundMonth 统计本月入库数量 6ms - → Failed to call `useTranslations` because the context from `NextIntlClientProvider` was not found. - -This can happen because: -1) You intended to render this component as a Server Component, the render - failed, and therefore React attempted to render the component on the client - instead. If this is the case, check the console for server errors. -2) You intended to render this component on the client side, but no context was found. - Learn more about this error here: https://next-intl.dev/docs/environments/server-client-components#missing-context - × src/app/[locale]/warehouse/inbound/__tests__/useInboundData.test.ts > useInboundData > data.data 为 null 时记录列表为空 5ms - → Failed to call `useTranslations` because the context from `NextIntlClientProvider` was not found. - -This can happen because: -1) You intended to render this component as a Server Component, the render - failed, and therefore React attempted to render the component on the client - instead. If this is the case, check the console for server errors. -2) You intended to render this component on the client side, but no context was found. - Learn more about this error here: https://next-intl.dev/docs/environments/server-client-components#missing-context - × src/app/[locale]/warehouse/inbound/__tests__/useInboundData.test.ts > useInboundData > 使用 createTime(驼峰)字段也能正确统计今日入库 4ms - → Failed to call `useTranslations` because the context from `NextIntlClientProvider` was not found. - -This can happen because: -1) You intended to render this component as a Server Component, the render - failed, and therefore React attempted to render the component on the client - instead. If this is the case, check the console for server errors. -2) You intended to render this component on the client side, but no context was found. - Learn more about this error here: https://next-intl.dev/docs/environments/server-client-components#missing-context - × src/app/[locale]/warehouse/inbound/__tests__/useInboundData.test.ts > useInboundData > create_time 为空字符串的记录不计入今日/本月统计 5ms - → Failed to call `useTranslations` because the context from `NextIntlClientProvider` was not found. - -This can happen because: -1) You intended to render this component as a Server Component, the render - failed, and therefore React attempted to render the component on the client - instead. If this is the case, check the console for server errors. -2) You intended to render this component on the client side, but no context was found. - Learn more about this error here: https://next-intl.dev/docs/environments/server-client-components#missing-context - × src/app/[locale]/warehouse/inbound/__tests__/useInboundData.test.ts > useInboundData > statusFilter 变更时触发带 status 参数的重新拉取 5ms - → Failed to call `useTranslations` because the context from `NextIntlClientProvider` was not found. - -This can happen because: -1) You intended to render this component as a Server Component, the render - failed, and therefore React attempted to render the component on the client - instead. If this is the case, check the console for server errors. -2) You intended to render this component on the client side, but no context was found. - Learn more about this error here: https://next-intl.dev/docs/environments/server-client-components#missing-context - × src/app/[locale]/warehouse/inbound/__tests__/useInboundData.test.ts > useInboundData > fetchWarehouses API 返回失败时仓库列表保持为空 4ms - → Failed to call `useTranslations` because the context from `NextIntlClientProvider` was not found. - -This can happen because: -1) You intended to render this component as a Server Component, the render - failed, and therefore React attempted to render the component on the client - instead. If this is the case, check the console for server errors. -2) You intended to render this component on the client side, but no context was found. - Learn more about this error here: https://next-intl.dev/docs/environments/server-client-components#missing-context - × src/app/[locale]/warehouse/inbound/__tests__/useInboundData.test.ts > useInboundData > fetchSuppliers API 返回失败时供应商列表保持为空 4ms - → Failed to call `useTranslations` because the context from `NextIntlClientProvider` was not found. - -This can happen because: -1) You intended to render this component as a Server Component, the render - failed, and therefore React attempted to render the component on the client - instead. If this is the case, check the console for server errors. -2) You intended to render this component on the client side, but no context was found. - Learn more about this error here: https://next-intl.dev/docs/environments/server-client-components#missing-context - × src/components/ui/table.test.tsx > Table 组件测试 > 应该正确渲染完整表格 160ms - → Invalid Chai property: toBeInTheDocument - × src/components/ui/table.test.tsx > Table 组件测试 > 应该渲染表格容器 13ms - → Invalid Chai property: toBeInTheDocument - × src/components/ui/input.test.tsx > Input 组件测试 > 应该支持只读状态 87ms - → Invalid Chai property: toHaveAttribute - × src/components/ui/input.test.tsx > Input 组件测试 > 应该支持默认值 19ms - → Invalid Chai property: toHaveValue - × src/components/ui/input.test.tsx > Input 组件测试 > 应该支持自定义className 17ms - → Invalid Chai property: toHaveClass - × src/components/ui/input.test.tsx > Input 组件测试 > 应该支持必填属性 15ms - → Invalid Chai property: toHaveAttribute - ✓ src/components/ui/table.perf.test.tsx > Table组件渲染性能测试 > 应该高效渲染大数据量表格 417ms - × src/components/ui/table.perf.test.tsx > Table组件渲染性能测试 > 应该高效渲染复杂表格结构 83ms - → Invalid Chai property: toBeInTheDocument - × src/components/ui/table.test.tsx > Table 组件测试 > 应该渲染表头 513ms - → Invalid Chai property: toHaveTextContent - ✓ src/components/ui/table.test.tsx > Table 组件测试 > 应该渲染表格主体 67ms - × src/components/ui/table.test.tsx > Table 组件测试 > 应该渲染表格底部 16ms - → Invalid Chai property: toBeInTheDocument - × src/components/ui/table.test.tsx > Table 组件测试 > 应该支持自定义className 8ms - → Invalid Chai property: toHaveClass - × src/components/ui/button.perf.test.tsx > Button组件渲染性能测试 > 应该在合理时间内渲染基础按钮 98ms - → Invalid Chai property: toBeInTheDocument - ✓ src/components/ui/button.perf.test.tsx > Button组件渲染性能测试 > 应该高效渲染多个按钮 84ms - × src/components/ui/button.perf.test.tsx > Button组件渲染性能测试 > 应该高效渲染带图标的按钮 11ms - → Invalid Chai property: toBeInTheDocument - × src/components/ui/button.test.tsx > Button 组件测试 > 应该正确渲染基础按钮 491ms - → Invalid Chai property: toBeInTheDocument - × src/components/ui/button.test.tsx > Button 组件测试 > 应该支持不同变体样式 26ms - → Invalid Chai property: toHaveAttribute - × src/components/ui/button.test.tsx > Button 组件测试 > 应该支持不同尺寸 22ms - → Invalid Chai property: toHaveAttribute - ✓ src/components/ui/button.test.tsx > Button 组件测试 > 应该处理点击事件 32ms - × src/components/ui/input.test.tsx > Input 组件测试 > 应该支持name属性 39ms - → Invalid Chai property: toHaveAttribute - × src/components/ui/input.test.tsx > Input 组件测试 > 应该支持id属性 42ms - → Invalid Chai property: toHaveAttribute - × src/components/ui/input.test.tsx > Input 组件测试 > 应该正确应用样式类 12ms - → Invalid Chai property: toHaveClass - × src/components/ui/button.test.tsx > Button 组件测试 > 应该在禁用时不可点击 21ms - → Invalid Chai property: toBeDisabled - × src/components/ui/button.test.tsx > Button 组件测试 > 应该显示加载状态 24ms - → Invalid Chai property: toHaveAttribute - × src/components/ui/button.test.tsx > Button 组件测试 > 应该支持全宽模式 22ms - → Invalid Chai property: toHaveClass - × src/components/ui/button.test.tsx > Button 组件测试 > 应该支持自定义className 16ms - → Invalid Chai property: toHaveClass - × src/components/ui/button.test.tsx > Button 组件测试 > 应该支持asChild属性 19ms - → Invalid Chai property: toBeInTheDocument - × src/components/ui/button.test.tsx > Button 组件测试 > 应该正确渲染子元素 10ms - → Invalid Chai property: toBeInTheDocument - × src/components/ui/table.test.tsx > Table 组件测试 > 应该支持行自定义className 27ms - → Invalid Chai property: toHaveClass - × src/components/ui/table.test.tsx > Table 组件测试 > 应该支持单元格自定义className 28ms - → Invalid Chai property: toHaveClass - × src/components/ui/table.test.tsx > Table 组件测试 > 应该渲染空表格 8ms - → Invalid Chai property: toBeInTheDocument - × src/components/ui/table.test.tsx > Table 组件测试 > 应该支持表头自定义className 17ms - → Invalid Chai property: toHaveClass - ✓ src/components/ui/button.perf.test.tsx > Button组件渲染性能测试 > 应该高效渲染不同变体按钮 12ms - ✓ src/components/ui/button.perf.test.tsx > Button组件渲染性能测试 > 应该高效渲染不同尺寸按钮 12ms - × src/components/ui/button.test.tsx > Button 组件测试 > 应该支持不同形状 15ms - → Invalid Chai property: toHaveClass - × src/components/ui/button.test.tsx > Button 组件测试 > 应该在加载时显示加载图标 11ms - → Invalid Chai property: toBeInTheDocument - × src/components/ui/button.test.tsx > Button 组件测试 > 应该支持type属性 13ms - → Invalid Chai property: toHaveAttribute - × src/components/ui/button.test.tsx > Button 组件测试 > 应该支持新增变体 success 13ms - → Invalid Chai property: toHaveAttribute - × src/components/ui/button.test.tsx > Button 组件测试 > 应该支持新增变体 warning 13ms - → Invalid Chai property: toHaveAttribute - × src/components/ui/button.test.tsx > Button 组件测试 > 应该支持新增变体 info 11ms - → Invalid Chai property: toHaveAttribute - × src/components/ui/button.test.tsx > Button 组件测试 > 应该支持新增尺寸 xl 15ms - → Invalid Chai property: toHaveAttribute - × src/components/ui/button.test.tsx > Button 组件测试 > 应该支持新增尺寸 xs 10ms - → Invalid Chai property: toHaveAttribute - × src/components/ui/button.test.tsx > Button 组件测试 > 应该支持新增尺寸 icon-sm 11ms - → Invalid Chai property: toHaveAttribute - × src/components/ui/button.test.tsx > Button 组件测试 > 应该支持新增尺寸 icon-lg 12ms - → Invalid Chai property: toHaveAttribute - × src/components/ui/button.test.tsx > Button 组件测试 > 应该在loading时同时设置disabled 15ms - → Invalid Chai property: toBeDisabled - × src/components/ui/button.test.tsx > Button 组件测试 > 应该在loading时忽略disabled=false 25ms - → Invalid Chai property: toBeDisabled - × src/components/ui/button.test.tsx > Button 组件测试 > 应该设置data-slot属性为button 15ms - → Invalid Chai property: toHaveAttribute - × src/components/ui/button.test.tsx > Button 组件测试 > 应该支持data-testid属性透传 6ms - → Invalid Chai property: toBeInTheDocument - × src/components/ui/button.test.tsx > Button 组件测试 > 应该在非loading状态下不渲染Loader2 6ms - → Invalid Chai property: toBeInTheDocument - × src/components/ui/button.test.tsx > Button 组件测试 > 应该支持shape与variant组合使用 9ms - → Invalid Chai property: toHaveAttribute - × src/components/ui/button.test.tsx > Button 组件测试 > 应该支持size与shape组合使用 10ms - → Invalid Chai property: toHaveAttribute - × src/components/ui/button.test.tsx > Button 组件测试 > 应该支持fullWidth与loading组合 15ms - → Invalid Chai property: toHaveClass - × src/components/ui/button.test.tsx > Button 组件测试 > 应该支持所有props组合 4ms - → Invalid Chai property: toHaveAttribute - ✓ tests/integration/workorder-api.test.ts > 工单 API 集成测试 > GET /api/workorders - 查询工单 > 按 id 查询单个工单:返回工单详情和明细 29ms - ✓ tests/integration/workorder-api.test.ts > 工单 API 集成测试 > GET /api/workorders - 查询工单 > 按 id 查询:工单不存在返回 404 4ms - ✓ tests/integration/workorder-api.test.ts > 工单 API 集成测试 > GET /api/workorders - 查询工单 > 按 order_no 查询:返回工单列表 3ms - ✓ tests/integration/workorder-api.test.ts > 工单 API 集成测试 > GET /api/workorders - 查询工单 > 列表查询:支持分页和状态筛选 8ms - ✓ tests/integration/workorder-api.test.ts > 工单 API 集成测试 > GET /api/workorders - 查询工单 > 列表查询:默认分页参数(page=1, page_size=20) 7ms - ✓ tests/integration/workorder-api.test.ts > 工单 API 集成测试 > GET /api/workorders - 查询工单 > 查询数据库异常返回 500 2ms - ✓ tests/integration/workorder-api.test.ts > 工单 API 集成测试 > POST /api/workorders - 创建工单 > 正常流程:创建成功返回 work_order_id 和 qr_code 8ms - ✓ tests/integration/workorder-api.test.ts > 工单 API 集成测试 > POST /api/workorders - 创建工单 > 参数异常:缺少 order_no 返回 400 7ms - ✓ tests/integration/workorder-api.test.ts > 工单 API 集成测试 > POST /api/workorders - 创建工单 > 参数异常:items 为空数组返回 400 4ms - ✓ tests/integration/workorder-api.test.ts > 工单 API 集成测试 > POST /api/workorders - 创建工单 > 参数异常:items 不是数组返回 400 3ms - ✓ tests/integration/workorder-api.test.ts > 工单 API 集成测试 > POST /api/workorders - 创建工单 > 业务异常:销售订单不存在时抛错(500) 2ms - ✓ tests/integration/workorder-api.test.ts > 工单 API 集成测试 > POST /api/workorders - 创建工单 > 业务异常:销售订单已取消时抛错(500) 1ms - ✓ tests/integration/workorder-api.test.ts > 工单 API 集成测试 > POST /api/workorders - 创建工单 > 业务异常:销售订单已存在未取消工单时抛错(500) 1ms - ✓ tests/integration/workorder-api.test.ts > 工单 API 集成测试 > PUT /api/workorders - 更新工单 > 正常流程:更新工单状态为 confirmed 3ms - ✓ tests/integration/workorder-api.test.ts > 工单 API 集成测试 > PUT /api/workorders - 更新工单 > 正常流程:更新优先级和计划日期 1ms - ✓ tests/integration/workorder-api.test.ts > 工单 API 集成测试 > PUT /api/workorders - 更新工单 > 参数异常:缺少 id 返回 400 1ms - ✓ tests/integration/workorder-api.test.ts > 工单 API 集成测试 > PUT /api/workorders - 更新工单 > 业务异常:工单不存在抛错(500) 2ms - ✓ tests/integration/workorder-api.test.ts > 工单 API 集成测试 > PUT /api/workorders - 更新工单 > 业务异常:已完成工单不能修改状态抛错(500) 1ms - ✓ tests/integration/workorder-api.test.ts > 工单 API 集成测试 > PUT /api/workorders - 更新工单 > 业务异常:已取消工单不能修改状态抛错(500) 1ms - ✓ tests/integration/workorder-api.test.ts > 工单 API 集成测试 > PUT /api/workorders - 更新工单 > 正常流程:完成工单时联动更新销售订单状态 2ms - ✓ tests/integration/workorder-api.test.ts > 工单 API 集成测试 > PUT /api/workorders - 更新工单 > 正常流程:取消工单时联动更新销售订单状态 2ms - × tests/integration/workorder-api.test.ts > 工单 API 集成测试 > DELETE /api/workorders - 删除工单 > 正常流程:pending 状态允许删除 24ms - → expected 500 to be 200 // Object.is equality - ✓ tests/integration/workorder-api.test.ts > 工单 API 集成测试 > DELETE /api/workorders - 删除工单 > 参数异常:缺少 id 返回 400 5ms - ✓ tests/integration/workorder-api.test.ts > 工单 API 集成测试 > DELETE /api/workorders - 删除工单 > 业务异常:工单不存在抛错(500) 2ms - × tests/integration/workorder-api.test.ts > 工单 API 集成测试 > DELETE /api/workorders - 删除工单 > 业务异常:生产中的工单不能删除抛错(500) 5ms - → expected '(intermediate value) is not iterable' to contain '生产中' - ✓ tests/integration/workorder-api.test.ts > 工单 API 集成测试 > 事务异常处理 > POST 事务中抛错时返回 500 1ms - ✓ tests/integration/workorder-api.test.ts > 工单 API 集成测试 > 事务异常处理 > PUT 事务中抛错时返回 500 2ms - × src/app/[locale]/warehouse/inbound/__tests__/usePrintLabels.test.ts > usePrintLabels > window.open 被阻止时显示错误提示 68ms - → Failed to call `useTranslations` because the context from `NextIntlClientProvider` was not found. - -This can happen because: -1) You intended to render this component as a Server Component, the render - failed, and therefore React attempted to render the component on the client - instead. If this is the case, check the console for server errors. -2) You intended to render this component on the client side, but no context was found. - Learn more about this error here: https://next-intl.dev/docs/environments/server-client-components#missing-context - × src/app/[locale]/warehouse/inbound/__tests__/usePrintLabels.test.ts > usePrintLabels > 成功打开打印窗口并写入内容 10ms - → Failed to call `useTranslations` because the context from `NextIntlClientProvider` was not found. - -This can happen because: -1) You intended to render this component as a Server Component, the render - failed, and therefore React attempted to render the component on the client - instead. If this is the case, check the console for server errors. -2) You intended to render this component on the client side, but no context was found. - Learn more about this error here: https://next-intl.dev/docs/environments/server-client-components#missing-context - × src/app/[locale]/warehouse/inbound/__tests__/usePrintLabels.test.ts > usePrintLabels > generatePrintContent 抛出异常时显示 printFailed 16ms - → Failed to call `useTranslations` because the context from `NextIntlClientProvider` was not found. - -This can happen because: -1) You intended to render this component as a Server Component, the render - failed, and therefore React attempted to render the component on the client - instead. If this is the case, check the console for server errors. -2) You intended to render this component on the client side, but no context was found. - Learn more about this error here: https://next-intl.dev/docs/environments/server-client-components#missing-context - × src/app/[locale]/warehouse/inbound/__tests__/usePrintLabels.test.ts > usePrintLabels > 空记录列表也能正常调用(不报错) 7ms - → Failed to call `useTranslations` because the context from `NextIntlClientProvider` was not found. - -This can happen because: -1) You intended to render this component as a Server Component, the render - failed, and therefore React attempted to render the component on the client - instead. If this is the case, check the console for server errors. -2) You intended to render this component on the client side, but no context was found. - Learn more about this error here: https://next-intl.dev/docs/environments/server-client-components#missing-context - × src/hooks/useCompanyName.test.ts > useCompanyName Hook测试 > 应该返回默认公司名称 74ms - → Failed to call `useTranslations` because the context from `NextIntlClientProvider` was not found. - -This can happen because: -1) You intended to render this component as a Server Component, the render - failed, and therefore React attempted to render the component on the client - instead. If this is the case, check the console for server errors. -2) You intended to render this component on the client side, but no context was found. - Learn more about this error here: https://next-intl.dev/docs/environments/server-client-components#missing-context - × src/hooks/useCompanyName.test.ts > useCompanyName Hook测试 > 应该从系统配置获取公司名称(config 优先) 15ms - → Failed to call `useTranslations` because the context from `NextIntlClientProvider` was not found. - -This can happen because: -1) You intended to render this component as a Server Component, the render - failed, and therefore React attempted to render the component on the client - instead. If this is the case, check the console for server errors. -2) You intended to render this component on the client side, but no context was found. - Learn more about this error here: https://next-intl.dev/docs/environments/server-client-components#missing-context - × src/hooks/useCompanyName.test.ts > useCompanyName Hook测试 > 应该使用配置短名称当全称不存在 12ms - → Failed to call `useTranslations` because the context from `NextIntlClientProvider` was not found. - -This can happen because: -1) You intended to render this component as a Server Component, the render - failed, and therefore React attempted to render the component on the client - instead. If this is the case, check the console for server errors. -2) You intended to render this component on the client side, but no context was found. - Learn more about this error here: https://next-intl.dev/docs/environments/server-client-components#missing-context - × src/hooks/useCompanyName.test.ts > useCompanyName Hook测试 > config 无公司配置时应从组织API获取全称 9ms - → Failed to call `useTranslations` because the context from `NextIntlClientProvider` was not found. - -This can happen because: -1) You intended to render this component as a Server Component, the render - failed, and therefore React attempted to render the component on the client - instead. If this is the case, check the console for server errors. -2) You intended to render this component on the client side, but no context was found. - Learn more about this error here: https://next-intl.dev/docs/environments/server-client-components#missing-context - × src/hooks/useCompanyName.test.ts > useCompanyName Hook测试 > 组织API无全称时应使用短名称 12ms - → Failed to call `useTranslations` because the context from `NextIntlClientProvider` was not found. - -This can happen because: -1) You intended to render this component as a Server Component, the render - failed, and therefore React attempted to render the component on the client - instead. If this is the case, check the console for server errors. -2) You intended to render this component on the client side, but no context was found. - Learn more about this error here: https://next-intl.dev/docs/environments/server-client-components#missing-context - × src/hooks/useCompanyName.test.ts > useCompanyName Hook测试 > 所有API失败时应保持默认值 7ms - → Failed to call `useTranslations` because the context from `NextIntlClientProvider` was not found. - -This can happen because: -1) You intended to render this component as a Server Component, the render - failed, and therefore React attempted to render the component on the client - instead. If this is the case, check the console for server errors. -2) You intended to render this component on the client side, but no context was found. - Learn more about this error here: https://next-intl.dev/docs/environments/server-client-components#missing-context - × src/hooks/useCompanyName.test.ts > useCompanyName Hook测试 > 401响应应被视为未授权并保持默认值 5ms - → Failed to call `useTranslations` because the context from `NextIntlClientProvider` was not found. - -This can happen because: -1) You intended to render this component as a Server Component, the render - failed, and therefore React attempted to render the component on the client - instead. If this is the case, check the console for server errors. -2) You intended to render this component on the client side, but no context was found. - Learn more about this error here: https://next-intl.dev/docs/environments/server-client-components#missing-context - × src/hooks/useCompanyName.test.ts > useCompanyName Hook测试 > 空数据响应应保持默认值 6ms - → Failed to call `useTranslations` because the context from `NextIntlClientProvider` was not found. - -This can happen because: -1) You intended to render this component as a Server Component, the render - failed, and therefore React attempted to render the component on the client - instead. If this is the case, check the console for server errors. -2) You intended to render this component on the client side, but no context was found. - Learn more about this error here: https://next-intl.dev/docs/environments/server-client-components#missing-context - × src/app/[locale]/warehouse/inbound/__tests__/useCutting.test.ts > useCutting > 初始表单状态包含操作员信息 73ms - → Failed to call `useTranslations` because the context from `NextIntlClientProvider` was not found. - -This can happen because: -1) You intended to render this component as a Server Component, the render - failed, and therefore React attempted to render the component on the client - instead. If this is the case, check the console for server errors. -2) You intended to render this component on the client side, but no context was found. - Learn more about this error here: https://next-intl.dev/docs/environments/server-client-components#missing-context - × src/app/[locale]/warehouse/inbound/__tests__/useCutting.test.ts > useCutting > currentLabel 为空时显示错误并返回 12ms - → Failed to call `useTranslations` because the context from `NextIntlClientProvider` was not found. - -This can happen because: -1) You intended to render this component as a Server Component, the render - failed, and therefore React attempted to render the component on the client - instead. If this is the case, check the console for server errors. -2) You intended to render this component on the client side, but no context was found. - Learn more about this error here: https://next-intl.dev/docs/environments/server-client-components#missing-context - × src/app/[locale]/warehouse/inbound/__tests__/useCutting.test.ts > useCutting > 物料不可分切时显示错误并返回 13ms - → Failed to call `useTranslations` because the context from `NextIntlClientProvider` was not found. - -This can happen because: -1) You intended to render this component as a Server Component, the render - failed, and therefore React attempted to render the component on the client - instead. If this is the case, check the console for server errors. -2) You intended to render this component on the client side, but no context was found. - Learn more about this error here: https://next-intl.dev/docs/environments/server-client-components#missing-context - × src/app/[locale]/warehouse/inbound/__tests__/useCutting.test.ts > useCutting > cutWidths 为空时显示错误并返回 6ms - → Failed to call `useTranslations` because the context from `NextIntlClientProvider` was not found. - -This can happen because: -1) You intended to render this component as a Server Component, the render - failed, and therefore React attempted to render the component on the client - instead. If this is the case, check the console for server errors. -2) You intended to render this component on the client side, but no context was found. - Learn more about this error here: https://next-intl.dev/docs/environments/server-client-components#missing-context - × src/app/[locale]/warehouse/inbound/__tests__/useCutting.test.ts > useCutting > 分切总宽度超过规格宽度时显示错误并返回 12ms - → Failed to call `useTranslations` because the context from `NextIntlClientProvider` was not found. - -This can happen because: -1) You intended to render this component as a Server Component, the render - failed, and therefore React attempted to render the component on the client - instead. If this is the case, check the console for server errors. -2) You intended to render this component on the client side, but no context was found. - Learn more about this error here: https://next-intl.dev/docs/environments/server-client-components#missing-context - × src/app/[locale]/warehouse/inbound/__tests__/useCutting.test.ts > useCutting > 成功分切后调用 API、刷新记录、映射标签 7ms - → Failed to call `useTranslations` because the context from `NextIntlClientProvider` was not found. - -This can happen because: -1) You intended to render this component as a Server Component, the render - failed, and therefore React attempted to render the component on the client - instead. If this is the case, check the console for server errors. -2) You intended to render this component on the client side, but no context was found. - Learn more about this error here: https://next-intl.dev/docs/environments/server-client-components#missing-context - × src/app/[locale]/warehouse/inbound/__tests__/useCutting.test.ts > useCutting > API 返回失败时显示错误消息 7ms - → Failed to call `useTranslations` because the context from `NextIntlClientProvider` was not found. - -This can happen because: -1) You intended to render this component as a Server Component, the render - failed, and therefore React attempted to render the component on the client - instead. If this is the case, check the console for server errors. -2) You intended to render this component on the client side, but no context was found. - Learn more about this error here: https://next-intl.dev/docs/environments/server-client-components#missing-context - × src/app/[locale]/warehouse/inbound/__tests__/useCutting.test.ts > useCutting > 网络异常时显示 cutFailed 15ms - → Failed to call `useTranslations` because the context from `NextIntlClientProvider` was not found. - -This can happen because: -1) You intended to render this component as a Server Component, the render - failed, and therefore React attempted to render the component on the client - instead. If this is the case, check the console for server errors. -2) You intended to render this component on the client side, but no context was found. - Learn more about this error here: https://next-intl.dev/docs/environments/server-client-components#missing-context - × src/app/[locale]/warehouse/inbound/__tests__/useCutting.test.ts > useCutting > 成功后清空 cutWidths 和 remark 12ms - → Failed to call `useTranslations` because the context from `NextIntlClientProvider` was not found. - -This can happen because: -1) You intended to render this component as a Server Component, the render - failed, and therefore React attempted to render the component on the client - instead. If this is the case, check the console for server errors. -2) You intended to render this component on the client side, but no context was found. - Learn more about this error here: https://next-intl.dev/docs/environments/server-client-components#missing-context - × src/app/[locale]/warehouse/inbound/__tests__/useCutting.test.ts > useCutting > API 成功但无新标签时不打开结果对话框 4ms - → Failed to call `useTranslations` because the context from `NextIntlClientProvider` was not found. - -This can happen because: -1) You intended to render this component as a Server Component, the render - failed, and therefore React attempted to render the component on the client - instead. If this is the case, check the console for server errors. -2) You intended to render this component on the client side, but no context was found. - Learn more about this error here: https://next-intl.dev/docs/environments/server-client-components#missing-context - × src/app/[locale]/warehouse/inbound/__tests__/useCutting.test.ts > useCutting > PET、PC、PVC 物料均可分切 5ms - → Failed to call `useTranslations` because the context from `NextIntlClientProvider` was not found. - -This can happen because: -1) You intended to render this component as a Server Component, the render - failed, and therefore React attempted to render the component on the client - instead. If this is the case, check the console for server errors. -2) You intended to render this component on the client side, but no context was found. - Learn more about this error here: https://next-intl.dev/docs/environments/server-client-components#missing-context - × src/app/[locale]/warehouse/inbound/__tests__/useCutting.test.ts > useCutting > user 为 null 时操作员默认为 id=1 / 系统管理员 4ms - → Failed to call `useTranslations` because the context from `NextIntlClientProvider` was not found. - -This can happen because: -1) You intended to render this component as a Server Component, the render - failed, and therefore React attempted to render the component on the client - instead. If this is the case, check the console for server errors. -2) You intended to render this component on the client side, but no context was found. - Learn more about this error here: https://next-intl.dev/docs/environments/server-client-components#missing-context - × src/app/[locale]/warehouse/inbound/__tests__/useCutting.test.ts > useCutting > 规格不可解析时跳过总量校验直接请求 API 3ms - → Failed to call `useTranslations` because the context from `NextIntlClientProvider` was not found. - -This can happen because: -1) You intended to render this component as a Server Component, the render - failed, and therefore React attempted to render the component on the client - instead. If this is the case, check the console for server errors. -2) You intended to render this component on the client side, but no context was found. - Learn more about this error here: https://next-intl.dev/docs/environments/server-client-components#missing-context - × src/app/[locale]/warehouse/inbound/__tests__/useCutting.test.ts > useCutting > 单个宽度值(无 + 分隔符)也能正常分切 5ms - → Failed to call `useTranslations` because the context from `NextIntlClientProvider` was not found. - -This can happen because: -1) You intended to render this component as a Server Component, the render - failed, and therefore React attempted to render the component on the client - instead. If this is the case, check the console for server errors. -2) You intended to render this component on the client side, but no context was found. - Learn more about this error here: https://next-intl.dev/docs/environments/server-client-components#missing-context - × src/app/[locale]/warehouse/inbound/__tests__/useCutting.test.ts > useCutting > 余料标签(isRemainder)的 materialName 包含余料前缀 4ms - → Failed to call `useTranslations` because the context from `NextIntlClientProvider` was not found. - -This can happen because: -1) You intended to render this component as a Server Component, the render - failed, and therefore React attempted to render the component on the client - instead. If this is the case, check the console for server errors. -2) You intended to render this component on the client side, but no context was found. - Learn more about this error here: https://next-intl.dev/docs/environments/server-client-components#missing-context - × src/app/[locale]/warehouse/inbound/__tests__/useCutting.test.ts > useCutting > newLabels 缺少 labelNo 时回退生成 ${orderNo}-C${idx+1} 4ms - → Failed to call `useTranslations` because the context from `NextIntlClientProvider` was not found. - -This can happen because: -1) You intended to render this component as a Server Component, the render - failed, and therefore React attempted to render the component on the client - instead. If this is the case, check the console for server errors. -2) You intended to render this component on the client side, but no context was found. - Learn more about this error here: https://next-intl.dev/docs/environments/server-client-components#missing-context - × src/app/[locale]/warehouse/inbound/__tests__/useCutting.test.ts > useCutting > 宽度值为 NaN 时仍发送请求(hasInvalid 仅告警不阻断) 4ms - → Failed to call `useTranslations` because the context from `NextIntlClientProvider` was not found. - -This can happen because: -1) You intended to render this component as a Server Component, the render - failed, and therefore React attempted to render the component on the client - instead. If this is the case, check the console for server errors. -2) You intended to render this component on the client side, but no context was found. - Learn more about this error here: https://next-intl.dev/docs/environments/server-client-components#missing-context - × src/app/[locale]/warehouse/inbound/__tests__/useCutting.test.ts > useCutting > newLabels 包含 newSpec 字段时 specification 使用 newSpec 11ms - → Failed to call `useTranslations` because the context from `NextIntlClientProvider` was not found. - -This can happen because: -1) You intended to render this component as a Server Component, the render - failed, and therefore React attempted to render the component on the client - instead. If this is the case, check the console for server errors. -2) You intended to render this component on the client side, but no context was found. - Learn more about this error here: https://next-intl.dev/docs/environments/server-client-components#missing-context - × src/app/[locale]/warehouse/inbound/__tests__/useCutting.test.ts > useCutting > newLabels 缺少 id 时回退为 cut-${idx} 5ms - → Failed to call `useTranslations` because the context from `NextIntlClientProvider` was not found. - -This can happen because: -1) You intended to render this component as a Server Component, the render - failed, and therefore React attempted to render the component on the client - instead. If this is the case, check the console for server errors. -2) You intended to render this component on the client side, but no context was found. - Learn more about this error here: https://next-intl.dev/docs/environments/server-client-components#missing-context - × src/app/[locale]/warehouse/inbound/__tests__/useCutting.test.ts > useCutting > API 返回失败且无 message 时回退到 cutFailed 5ms - → Failed to call `useTranslations` because the context from `NextIntlClientProvider` was not found. - -This can happen because: -1) You intended to render this component as a Server Component, the render - failed, and therefore React attempted to render the component on the client - instead. If this is the case, check the console for server errors. -2) You intended to render this component on the client side, but no context was found. - Learn more about this error here: https://next-intl.dev/docs/environments/server-client-components#missing-context - × src/app/[locale]/warehouse/inbound/__tests__/useCutting.test.ts > useCutting > user 为 null 且表单为空时请求体 operatorId/operatorName 回退到默认值 3ms - → Failed to call `useTranslations` because the context from `NextIntlClientProvider` was not found. - -This can happen because: -1) You intended to render this component as a Server Component, the render - failed, and therefore React attempted to render the component on the client - instead. If this is the case, check the console for server errors. -2) You intended to render this component on the client side, but no context was found. - Learn more about this error here: https://next-intl.dev/docs/environments/server-client-components#missing-context - ✓ src/lib/__tests__/db-transaction.test.ts > transactionWithRetry > should succeed on first attempt when no conflict 7ms - ✓ src/lib/__tests__/db-transaction.test.ts > transactionWithRetry > should retry on optimistic lock failure with "已被其他操作修改" 152ms - ✓ src/lib/__tests__/db-transaction.test.ts > transactionWithRetry > should retry on optimistic lock failure with "version" keyword 112ms - ✓ src/lib/__tests__/db-transaction.test.ts > transactionWithRetry > should retry on optimistic lock failure with "affectedRows" keyword 152ms - ✓ src/lib/__tests__/db-transaction.test.ts > transactionWithRetry > should not retry on non-optimistic-lock errors 5ms - ✓ src/lib/__tests__/db-transaction.test.ts > transactionWithRetry > should throw after max retries exceeded 135ms - ✓ src/lib/__tests__/db-transaction.test.ts > transactionWithRetry > should respect maxRetries parameter 385ms - ✓ src/lib/__tests__/db-transaction.test.ts > transactionWithRetry > should use exponential backoff between retries 153ms -stderr | tests/unit/hr/salary-engine.test.ts > salary-engine > should batch calculate multiple employees -员工 1 薪资计算失败: TypeError: db.update is not a function - at SalaryCalculationRepository.save (D:/dcprint/erp-project/src/infrastructure/repositories/SalaryCalculationRepository.ts:21:16) - at processTicksAndRejections (node:internal/process/task_queues:103:5) - at calculateMonthlySalary (D:/dcprint/erp-project/src/lib/hr/salary-engine.ts:171:3) - at Module.batchCalculateSalary (D:/dcprint/erp-project/src/lib/hr/salary-engine.ts:209:17) - at D:/dcprint/erp-project/tests/unit/hr/salary-engine.test.ts:171:21 - at file:///D:/dcprint/erp-project/node_modules/.pnpm/@vitest+runner@4.1.5/node_modules/@vitest/runner/dist/chunk-artifact.js:1903:20 - -stderr | tests/unit/hr/salary-engine.test.ts > salary-engine > should batch calculate multiple employees -员工 2 薪资计算失败: TypeError: Cannot read properties of undefined (reading 'then') - at calculateMonthlySalary (D:/dcprint/erp-project/src/lib/hr/salary-engine.ts:80:91) - at processTicksAndRejections (node:internal/process/task_queues:103:5) - at Module.batchCalculateSalary (D:/dcprint/erp-project/src/lib/hr/salary-engine.ts:209:17) - at D:/dcprint/erp-project/tests/unit/hr/salary-engine.test.ts:171:21 - at file:///D:/dcprint/erp-project/node_modules/.pnpm/@vitest+runner@4.1.5/node_modules/@vitest/runner/dist/chunk-artifact.js:1903:20 - - × tests/unit/hr/salary-engine.test.ts > salary-engine > should calculate full salary correctly 17ms - → Cannot read properties of undefined (reading 'then') - × tests/unit/hr/salary-engine.test.ts > salary-engine > should exclude piece salary when option is false 7ms - → Cannot read properties of undefined (reading 'then') - × tests/unit/hr/salary-engine.test.ts > salary-engine > should exclude overtime when option is false 3ms - → Cannot read properties of undefined (reading 'then') - × tests/unit/hr/salary-engine.test.ts > salary-engine > should exclude insurance when option is false 2ms - → Cannot read properties of undefined (reading 'then') - × tests/unit/hr/salary-engine.test.ts > salary-engine > should exclude tax when option is false 2ms - → Cannot read properties of undefined (reading 'then') - × tests/unit/hr/salary-engine.test.ts > salary-engine > should handle zero base salary 2ms - → Cannot read properties of undefined (reading 'then') - × tests/unit/hr/salary-engine.test.ts > salary-engine > should cap net pay at 0 when deductions exceed gross pay 2ms - → Cannot read properties of undefined (reading 'then') - × tests/unit/hr/salary-engine.test.ts > salary-engine > should batch calculate multiple employees 16ms - → expected [] to have a length of 2 but got +0 -stderr | src/components/printing/__tests__/LabelPrintPreview.test.tsx > LabelPrintPreview - 多语言标题与字段 > [zh-CN] 对话框标题显示对应语言的"标签打印预览" -IntlError: INVALID_KEY: Namespace keys cannot contain the character "." as this is used to express nesting. Please remove it or replace it with another character. - -Invalid keys: status.pending (at Production), status.confirmed (at Production), status.producing (at Production), status.completed (at Production), status.cancelled (at Production), status.label (at Production), priority.urgent (at Production), priority.high (at Production), priority.normal (at Production), priority.low (at Production), priority.label (at Production), units.sheet (at Production), units.roll (at Production), units.piece (at Production) - -If you're migrating from a flat structure, you can convert your messages as follows: - -import {set} from "lodash"; - -const input = { - "one.one": "1.1", - "one.two": "1.2", - "two.one.one": "2.1.1" -}; - -const output = Object.entries(input).reduce( - (acc, [key, value]) => set(acc, key, value), - {} -); - -// Output: -// -// { -// "one": { -// "one": "1.1", -// "two": "1.2" -// }, -// "two": { -// "one": { -// "one": "2.1.1" -// } -// } -// } - - at validateMessages (file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/initializeConfig-CUsOI8u2.js:465:13) - at initializeConfig (file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/initializeConfig-CUsOI8u2.js:515:7) - at file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/react.js:39:8 - at mountMemo (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:8777:23) - at Object.useMemo (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:26216:18) - at process.env.NODE_ENV.exports.useMemo (D:\dcprint\erp-project\node_modules\.pnpm\react@19.2.3\node_modules\react\cjs\react.development.js:1251:34) - at IntlProvider (file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/react.js:38:17) - at Object.react_stack_bottom_frame (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:25904:20) - at renderWithHooks (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:7662:22) - at updateFunctionComponent (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:10166:19) { - code: 'INVALID_KEY', - originalMessage: 'Namespace keys cannot contain the character "." as this is used to express nesting. Please remove it or replace it with another character.\n' + - '\n' + - 'Invalid keys: status.pending (at Production), status.confirmed (at Production), status.producing (at Production), status.completed (at Production), status.cancelled (at Production), status.label (at Production), priority.urgent (at Production), priority.high (at Production), priority.normal (at Production), priority.low (at Production), priority.label (at Production), units.sheet (at Production), units.roll (at Production), units.piece (at Production)\n' + - '\n' + - "If you're migrating from a flat structure, you can convert your messages as follows:\n" + - '\n' + - 'import {set} from "lodash";\n' + - '\n' + - 'const input = {\n' + - ' "one.one": "1.1",\n' + - ' "one.two": "1.2",\n' + - ' "two.one.one": "2.1.1"\n' + - '};\n' + - '\n' + - 'const output = Object.entries(input).reduce(\n' + - ' (acc, [key, value]) => set(acc, key, value),\n' + - ' {}\n' + - ');\n' + - '\n' + - '// Output:\n' + - '//\n' + - '// {\n' + - '// "one": {\n' + - '// "one": "1.1",\n' + - '// "two": "1.2"\n' + - '// },\n' + - '// "two": {\n' + - '// "one": {\n' + - '// "one": "2.1.1"\n' + - '// }\n' + - '// }\n' + - '// }\n' -} -Warning: Missing `Description` or `aria-describedby={undefined}` for {DialogContent}. - - × tests/integration/qr-trace-flow.test.ts > QR Trace Full Chain Integration > full chain: create → find → split → find children → update quantity 128ms - → Unknown column 'parent_qr_code' in 'field list' - × tests/integration/qr-trace-flow.test.ts > QR Trace Full Chain Integration > query trace timeline returns structured data 10ms - → Unknown column 'parent_qr_code' in 'field list' - ✓ tests/integration/qr-trace-flow.test.ts > QR Trace Full Chain Integration > findByContent returns null for non-existent QR 18ms -stderr | src/components/qr-code/__tests__/QRCodeGenerator.test.tsx > QRCodeGenerator - 多语言文本渲染 > [zh-CN] 触发按钮显示对应语言的"生成二维码"文案 -IntlError: INVALID_KEY: Namespace keys cannot contain the character "." as this is used to express nesting. Please remove it or replace it with another character. - -Invalid keys: status.pending (at Production), status.confirmed (at Production), status.producing (at Production), status.completed (at Production), status.cancelled (at Production), status.label (at Production), priority.urgent (at Production), priority.high (at Production), priority.normal (at Production), priority.low (at Production), priority.label (at Production), units.sheet (at Production), units.roll (at Production), units.piece (at Production) - -If you're migrating from a flat structure, you can convert your messages as follows: - -import {set} from "lodash"; - -const input = { - "one.one": "1.1", - "one.two": "1.2", - "two.one.one": "2.1.1" -}; - -const output = Object.entries(input).reduce( - (acc, [key, value]) => set(acc, key, value), - {} -); - -// Output: -// -// { -// "one": { -// "one": "1.1", -// "two": "1.2" -// }, -// "two": { -// "one": { -// "one": "2.1.1" -// } -// } -// } - - at validateMessages (file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/initializeConfig-CUsOI8u2.js:465:13) - at initializeConfig (file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/initializeConfig-CUsOI8u2.js:515:7) - at file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/react.js:39:8 - at mountMemo (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:8777:23) - at Object.useMemo (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:26216:18) - at process.env.NODE_ENV.exports.useMemo (D:\dcprint\erp-project\node_modules\.pnpm\react@19.2.3\node_modules\react\cjs\react.development.js:1251:34) - at IntlProvider (file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/react.js:38:17) - at Object.react_stack_bottom_frame (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:25904:20) - at renderWithHooks (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:7662:22) - at updateFunctionComponent (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:10166:19) { - code: 'INVALID_KEY', - originalMessage: 'Namespace keys cannot contain the character "." as this is used to express nesting. Please remove it or replace it with another character.\n' + - '\n' + - 'Invalid keys: status.pending (at Production), status.confirmed (at Production), status.producing (at Production), status.completed (at Production), status.cancelled (at Production), status.label (at Production), priority.urgent (at Production), priority.high (at Production), priority.normal (at Production), priority.low (at Production), priority.label (at Production), units.sheet (at Production), units.roll (at Production), units.piece (at Production)\n' + - '\n' + - "If you're migrating from a flat structure, you can convert your messages as follows:\n" + - '\n' + - 'import {set} from "lodash";\n' + - '\n' + - 'const input = {\n' + - ' "one.one": "1.1",\n' + - ' "one.two": "1.2",\n' + - ' "two.one.one": "2.1.1"\n' + - '};\n' + - '\n' + - 'const output = Object.entries(input).reduce(\n' + - ' (acc, [key, value]) => set(acc, key, value),\n' + - ' {}\n' + - ');\n' + - '\n' + - '// Output:\n' + - '//\n' + - '// {\n' + - '// "one": {\n' + - '// "one": "1.1",\n' + - '// "two": "1.2"\n' + - '// },\n' + - '// "two": {\n' + - '// "one": {\n' + - '// "one": "2.1.1"\n' + - '// }\n' + - '// }\n' + - '// }\n' -} - -stderr | src/components/printing/__tests__/LabelPrintPreview.test.tsx > LabelPrintPreview - 多语言标题与字段 > [en] 对话框标题显示对应语言的"标签打印预览" -IntlError: INVALID_KEY: Namespace keys cannot contain the character "." as this is used to express nesting. Please remove it or replace it with another character. - -Invalid keys: status.pending (at Production), status.confirmed (at Production), status.producing (at Production), status.completed (at Production), status.cancelled (at Production), status.label (at Production), priority.urgent (at Production), priority.high (at Production), priority.normal (at Production), priority.low (at Production), priority.label (at Production), units.sheet (at Production), units.roll (at Production), units.piece (at Production) - -If you're migrating from a flat structure, you can convert your messages as follows: - -import {set} from "lodash"; - -const input = { - "one.one": "1.1", - "one.two": "1.2", - "two.one.one": "2.1.1" -}; - -const output = Object.entries(input).reduce( - (acc, [key, value]) => set(acc, key, value), - {} -); - -// Output: -// -// { -// "one": { -// "one": "1.1", -// "two": "1.2" -// }, -// "two": { -// "one": { -// "one": "2.1.1" -// } -// } -// } - - at validateMessages (file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/initializeConfig-CUsOI8u2.js:465:13) - at initializeConfig (file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/initializeConfig-CUsOI8u2.js:515:7) - at file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/react.js:39:8 - at mountMemo (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:8777:23) - at Object.useMemo (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:26216:18) - at process.env.NODE_ENV.exports.useMemo (D:\dcprint\erp-project\node_modules\.pnpm\react@19.2.3\node_modules\react\cjs\react.development.js:1251:34) - at IntlProvider (file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/react.js:38:17) - at Object.react_stack_bottom_frame (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:25904:20) - at renderWithHooks (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:7662:22) - at updateFunctionComponent (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:10166:19) { - code: 'INVALID_KEY', - originalMessage: 'Namespace keys cannot contain the character "." as this is used to express nesting. Please remove it or replace it with another character.\n' + - '\n' + - 'Invalid keys: status.pending (at Production), status.confirmed (at Production), status.producing (at Production), status.completed (at Production), status.cancelled (at Production), status.label (at Production), priority.urgent (at Production), priority.high (at Production), priority.normal (at Production), priority.low (at Production), priority.label (at Production), units.sheet (at Production), units.roll (at Production), units.piece (at Production)\n' + - '\n' + - "If you're migrating from a flat structure, you can convert your messages as follows:\n" + - '\n' + - 'import {set} from "lodash";\n' + - '\n' + - 'const input = {\n' + - ' "one.one": "1.1",\n' + - ' "one.two": "1.2",\n' + - ' "two.one.one": "2.1.1"\n' + - '};\n' + - '\n' + - 'const output = Object.entries(input).reduce(\n' + - ' (acc, [key, value]) => set(acc, key, value),\n' + - ' {}\n' + - ');\n' + - '\n' + - '// Output:\n' + - '//\n' + - '// {\n' + - '// "one": {\n' + - '// "one": "1.1",\n' + - '// "two": "1.2"\n' + - '// },\n' + - '// "two": {\n' + - '// "one": {\n' + - '// "one": "2.1.1"\n' + - '// }\n' + - '// }\n' + - '// }\n' -} -Warning: Missing `Description` or `aria-describedby={undefined}` for {DialogContent}. - - ✓ src/components/printing/__tests__/LabelPrintPreview.test.tsx > LabelPrintPreview - 多语言标题与字段 > [zh-CN] 对话框标题显示对应语言的"标签打印预览" 1053ms -stderr | src/components/qr-code/__tests__/QRCodeGenerator.test.tsx > QRCodeGenerator - 多语言文本渲染 > [en] 触发按钮显示对应语言的"生成二维码"文案 -IntlError: INVALID_KEY: Namespace keys cannot contain the character "." as this is used to express nesting. Please remove it or replace it with another character. - -Invalid keys: status.pending (at Production), status.confirmed (at Production), status.producing (at Production), status.completed (at Production), status.cancelled (at Production), status.label (at Production), priority.urgent (at Production), priority.high (at Production), priority.normal (at Production), priority.low (at Production), priority.label (at Production), units.sheet (at Production), units.roll (at Production), units.piece (at Production) - -If you're migrating from a flat structure, you can convert your messages as follows: - -import {set} from "lodash"; - -const input = { - "one.one": "1.1", - "one.two": "1.2", - "two.one.one": "2.1.1" -}; - -const output = Object.entries(input).reduce( - (acc, [key, value]) => set(acc, key, value), - {} -); - -// Output: -// -// { -// "one": { -// "one": "1.1", -// "two": "1.2" -// }, -// "two": { -// "one": { -// "one": "2.1.1" -// } -// } -// } - - at validateMessages (file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/initializeConfig-CUsOI8u2.js:465:13) - at initializeConfig (file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/initializeConfig-CUsOI8u2.js:515:7) - at file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/react.js:39:8 - at mountMemo (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:8777:23) - at Object.useMemo (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:26216:18) - at process.env.NODE_ENV.exports.useMemo (D:\dcprint\erp-project\node_modules\.pnpm\react@19.2.3\node_modules\react\cjs\react.development.js:1251:34) - at IntlProvider (file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/react.js:38:17) - at Object.react_stack_bottom_frame (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:25904:20) - at renderWithHooks (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:7662:22) - at updateFunctionComponent (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:10166:19) { - code: 'INVALID_KEY', - originalMessage: 'Namespace keys cannot contain the character "." as this is used to express nesting. Please remove it or replace it with another character.\n' + - '\n' + - 'Invalid keys: status.pending (at Production), status.confirmed (at Production), status.producing (at Production), status.completed (at Production), status.cancelled (at Production), status.label (at Production), priority.urgent (at Production), priority.high (at Production), priority.normal (at Production), priority.low (at Production), priority.label (at Production), units.sheet (at Production), units.roll (at Production), units.piece (at Production)\n' + - '\n' + - "If you're migrating from a flat structure, you can convert your messages as follows:\n" + - '\n' + - 'import {set} from "lodash";\n' + - '\n' + - 'const input = {\n' + - ' "one.one": "1.1",\n' + - ' "one.two": "1.2",\n' + - ' "two.one.one": "2.1.1"\n' + - '};\n' + - '\n' + - 'const output = Object.entries(input).reduce(\n' + - ' (acc, [key, value]) => set(acc, key, value),\n' + - ' {}\n' + - ');\n' + - '\n' + - '// Output:\n' + - '//\n' + - '// {\n' + - '// "one": {\n' + - '// "one": "1.1",\n' + - '// "two": "1.2"\n' + - '// },\n' + - '// "two": {\n' + - '// "one": {\n' + - '// "one": "2.1.1"\n' + - '// }\n' + - '// }\n' + - '// }\n' -} - - ✓ src/components/qr-code/__tests__/QRCodeGenerator.test.tsx > QRCodeGenerator - 多语言文本渲染 > [zh-CN] 触发按钮显示对应语言的"生成二维码"文案 637ms - ✓ tests/unit/infrastructure/event-bus/event-bus-type-switch.test.ts > 1.6 EVENT_BUS_TYPE 切换工厂 > EVENT_BUS_TYPE=memory 时返回 MemoryDomainEventOutbox 实例 6ms - ✓ tests/unit/infrastructure/event-bus/event-bus-type-switch.test.ts > 1.6 EVENT_BUS_TYPE 切换工厂 > EVENT_BUS_TYPE=db 时返回 DbDomainEventOutbox 实例 2ms - ✓ tests/unit/infrastructure/event-bus/event-bus-type-switch.test.ts > 1.6 EVENT_BUS_TYPE 切换工厂 > 未设置 EVENT_BUS_TYPE 时默认为 memory 模式(向后兼容) 1ms - ✓ tests/unit/infrastructure/event-bus/event-bus-type-switch.test.ts > 1.6 EVENT_BUS_TYPE 切换工厂 > EVENT_BUS_TYPE=invalid 时降级为 memory 模式 1ms - ✓ tests/unit/infrastructure/event-bus/event-bus-type-switch.test.ts > 1.6 MemoryDomainEventOutbox 行为(no-op) > saveEvents 不抛错且不写库(no-op) 3ms - ✓ tests/unit/infrastructure/event-bus/event-bus-type-switch.test.ts > 1.6 MemoryDomainEventOutbox 行为(no-op) > fetchPendingEvents 返回空数组 2ms - ✓ tests/unit/infrastructure/event-bus/event-bus-type-switch.test.ts > 1.6 MemoryDomainEventOutbox 行为(no-op) > markAsProcessed 不抛错(no-op) 2ms - ✓ tests/unit/infrastructure/event-bus/event-bus-type-switch.test.ts > 1.6 MemoryDomainEventOutbox 行为(no-op) > markAsFailed 不抛错(no-op) 1ms - ✓ tests/unit/infrastructure/event-bus/event-bus-type-switch.test.ts > 1.6 MemoryDomainEventOutbox 行为(no-op) > markAsDeadLetter 不抛错(no-op) 2ms - ✓ tests/unit/infrastructure/event-bus/event-bus-type-switch.test.ts > 1.6 MemoryDomainEventOutbox 行为(no-op) > markForRetry 不抛错(no-op) 1ms - ✓ tests/unit/infrastructure/event-bus/event-bus-type-switch.test.ts > 1.6 MemoryDomainEventOutbox 行为(no-op) > reclaimStaleDispatching 返回 0(no-op) 1ms -stderr | src/components/printing/__tests__/LabelPrintPreview.test.tsx > LabelPrintPreview - 多语言标题与字段 > [zh-TW] 对话框标题显示对应语言的"标签打印预览" -IntlError: INVALID_KEY: Namespace keys cannot contain the character "." as this is used to express nesting. Please remove it or replace it with another character. - -Invalid keys: status.pending (at Production), status.confirmed (at Production), status.producing (at Production), status.completed (at Production), status.cancelled (at Production), status.label (at Production), priority.urgent (at Production), priority.high (at Production), priority.normal (at Production), priority.low (at Production), priority.label (at Production), units.sheet (at Production), units.roll (at Production), units.piece (at Production) - -If you're migrating from a flat structure, you can convert your messages as follows: - -import {set} from "lodash"; - -const input = { - "one.one": "1.1", - "one.two": "1.2", - "two.one.one": "2.1.1" -}; - -const output = Object.entries(input).reduce( - (acc, [key, value]) => set(acc, key, value), - {} -); - -// Output: -// -// { -// "one": { -// "one": "1.1", -// "two": "1.2" -// }, -// "two": { -// "one": { -// "one": "2.1.1" -// } -// } -// } - - at validateMessages (file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/initializeConfig-CUsOI8u2.js:465:13) - at initializeConfig (file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/initializeConfig-CUsOI8u2.js:515:7) - at file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/react.js:39:8 - at mountMemo (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:8777:23) - at Object.useMemo (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:26216:18) - at process.env.NODE_ENV.exports.useMemo (D:\dcprint\erp-project\node_modules\.pnpm\react@19.2.3\node_modules\react\cjs\react.development.js:1251:34) - at IntlProvider (file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/react.js:38:17) - at Object.react_stack_bottom_frame (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:25904:20) - at renderWithHooks (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:7662:22) - at updateFunctionComponent (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:10166:19) { - code: 'INVALID_KEY', - originalMessage: 'Namespace keys cannot contain the character "." as this is used to express nesting. Please remove it or replace it with another character.\n' + - '\n' + - 'Invalid keys: status.pending (at Production), status.confirmed (at Production), status.producing (at Production), status.completed (at Production), status.cancelled (at Production), status.label (at Production), priority.urgent (at Production), priority.high (at Production), priority.normal (at Production), priority.low (at Production), priority.label (at Production), units.sheet (at Production), units.roll (at Production), units.piece (at Production)\n' + - '\n' + - "If you're migrating from a flat structure, you can convert your messages as follows:\n" + - '\n' + - 'import {set} from "lodash";\n' + - '\n' + - 'const input = {\n' + - ' "one.one": "1.1",\n' + - ' "one.two": "1.2",\n' + - ' "two.one.one": "2.1.1"\n' + - '};\n' + - '\n' + - 'const output = Object.entries(input).reduce(\n' + - ' (acc, [key, value]) => set(acc, key, value),\n' + - ' {}\n' + - ');\n' + - '\n' + - '// Output:\n' + - '//\n' + - '// {\n' + - '// "one": {\n' + - '// "one": "1.1",\n' + - '// "two": "1.2"\n' + - '// },\n' + - '// "two": {\n' + - '// "one": {\n' + - '// "one": "2.1.1"\n' + - '// }\n' + - '// }\n' + - '// }\n' -} -Warning: Missing `Description` or `aria-describedby={undefined}` for {DialogContent}. - - ✓ src/components/printing/__tests__/LabelPrintPreview.test.tsx > LabelPrintPreview - 多语言标题与字段 > [en] 对话框标题显示对应语言的"标签打印预览" 176ms -stderr | src/components/qr-code/__tests__/QRCodeGenerator.test.tsx > QRCodeGenerator - 多语言文本渲染 > [zh-TW] 触发按钮显示对应语言的"生成二维码"文案 -IntlError: INVALID_KEY: Namespace keys cannot contain the character "." as this is used to express nesting. Please remove it or replace it with another character. - -Invalid keys: status.pending (at Production), status.confirmed (at Production), status.producing (at Production), status.completed (at Production), status.cancelled (at Production), status.label (at Production), priority.urgent (at Production), priority.high (at Production), priority.normal (at Production), priority.low (at Production), priority.label (at Production), units.sheet (at Production), units.roll (at Production), units.piece (at Production) - -If you're migrating from a flat structure, you can convert your messages as follows: - -import {set} from "lodash"; - -const input = { - "one.one": "1.1", - "one.two": "1.2", - "two.one.one": "2.1.1" -}; - -const output = Object.entries(input).reduce( - (acc, [key, value]) => set(acc, key, value), - {} -); - -// Output: -// -// { -// "one": { -// "one": "1.1", -// "two": "1.2" -// }, -// "two": { -// "one": { -// "one": "2.1.1" -// } -// } -// } - - at validateMessages (file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/initializeConfig-CUsOI8u2.js:465:13) - at initializeConfig (file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/initializeConfig-CUsOI8u2.js:515:7) - at file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/react.js:39:8 - at mountMemo (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:8777:23) - at Object.useMemo (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:26216:18) - at process.env.NODE_ENV.exports.useMemo (D:\dcprint\erp-project\node_modules\.pnpm\react@19.2.3\node_modules\react\cjs\react.development.js:1251:34) - at IntlProvider (file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/react.js:38:17) - at Object.react_stack_bottom_frame (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:25904:20) - at renderWithHooks (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:7662:22) - at updateFunctionComponent (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:10166:19) { - code: 'INVALID_KEY', - originalMessage: 'Namespace keys cannot contain the character "." as this is used to express nesting. Please remove it or replace it with another character.\n' + - '\n' + - 'Invalid keys: status.pending (at Production), status.confirmed (at Production), status.producing (at Production), status.completed (at Production), status.cancelled (at Production), status.label (at Production), priority.urgent (at Production), priority.high (at Production), priority.normal (at Production), priority.low (at Production), priority.label (at Production), units.sheet (at Production), units.roll (at Production), units.piece (at Production)\n' + - '\n' + - "If you're migrating from a flat structure, you can convert your messages as follows:\n" + - '\n' + - 'import {set} from "lodash";\n' + - '\n' + - 'const input = {\n' + - ' "one.one": "1.1",\n' + - ' "one.two": "1.2",\n' + - ' "two.one.one": "2.1.1"\n' + - '};\n' + - '\n' + - 'const output = Object.entries(input).reduce(\n' + - ' (acc, [key, value]) => set(acc, key, value),\n' + - ' {}\n' + - ');\n' + - '\n' + - '// Output:\n' + - '//\n' + - '// {\n' + - '// "one": {\n' + - '// "one": "1.1",\n' + - '// "two": "1.2"\n' + - '// },\n' + - '// "two": {\n' + - '// "one": {\n' + - '// "one": "2.1.1"\n' + - '// }\n' + - '// }\n' + - '// }\n' -} - - ✓ src/components/qr-code/__tests__/QRCodeGenerator.test.tsx > QRCodeGenerator - 多语言文本渲染 > [en] 触发按钮显示对应语言的"生成二维码"文案 166ms -stderr | src/components/printing/__tests__/LabelPrintPreview.test.tsx > LabelPrintPreview - 多语言标题与字段 > [vi] 对话框标题显示对应语言的"标签打印预览" -IntlError: INVALID_KEY: Namespace keys cannot contain the character "." as this is used to express nesting. Please remove it or replace it with another character. - -Invalid keys: status.pending (at Production), status.confirmed (at Production), status.producing (at Production), status.completed (at Production), status.cancelled (at Production), status.label (at Production), priority.urgent (at Production), priority.high (at Production), priority.normal (at Production), priority.low (at Production), priority.label (at Production), units.sheet (at Production), units.roll (at Production), units.piece (at Production) - -If you're migrating from a flat structure, you can convert your messages as follows: - -import {set} from "lodash"; - -const input = { - "one.one": "1.1", - "one.two": "1.2", - "two.one.one": "2.1.1" -}; - -const output = Object.entries(input).reduce( - (acc, [key, value]) => set(acc, key, value), - {} -); - -// Output: -// -// { -// "one": { -// "one": "1.1", -// "two": "1.2" -// }, -// "two": { -// "one": { -// "one": "2.1.1" -// } -// } -// } - - at validateMessages (file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/initializeConfig-CUsOI8u2.js:465:13) - at initializeConfig (file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/initializeConfig-CUsOI8u2.js:515:7) - at file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/react.js:39:8 - at mountMemo (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:8777:23) - at Object.useMemo (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:26216:18) - at process.env.NODE_ENV.exports.useMemo (D:\dcprint\erp-project\node_modules\.pnpm\react@19.2.3\node_modules\react\cjs\react.development.js:1251:34) - at IntlProvider (file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/react.js:38:17) - at Object.react_stack_bottom_frame (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:25904:20) - at renderWithHooks (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:7662:22) - at updateFunctionComponent (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:10166:19) { - code: 'INVALID_KEY', - originalMessage: 'Namespace keys cannot contain the character "." as this is used to express nesting. Please remove it or replace it with another character.\n' + - '\n' + - 'Invalid keys: status.pending (at Production), status.confirmed (at Production), status.producing (at Production), status.completed (at Production), status.cancelled (at Production), status.label (at Production), priority.urgent (at Production), priority.high (at Production), priority.normal (at Production), priority.low (at Production), priority.label (at Production), units.sheet (at Production), units.roll (at Production), units.piece (at Production)\n' + - '\n' + - "If you're migrating from a flat structure, you can convert your messages as follows:\n" + - '\n' + - 'import {set} from "lodash";\n' + - '\n' + - 'const input = {\n' + - ' "one.one": "1.1",\n' + - ' "one.two": "1.2",\n' + - ' "two.one.one": "2.1.1"\n' + - '};\n' + - '\n' + - 'const output = Object.entries(input).reduce(\n' + - ' (acc, [key, value]) => set(acc, key, value),\n' + - ' {}\n' + - ');\n' + - '\n' + - '// Output:\n' + - '//\n' + - '// {\n' + - '// "one": {\n' + - '// "one": "1.1",\n' + - '// "two": "1.2"\n' + - '// },\n' + - '// "two": {\n' + - '// "one": {\n' + - '// "one": "2.1.1"\n' + - '// }\n' + - '// }\n' + - '// }\n' -} -Warning: Missing `Description` or `aria-describedby={undefined}` for {DialogContent}. - - ✓ src/components/printing/__tests__/LabelPrintPreview.test.tsx > LabelPrintPreview - 多语言标题与字段 > [zh-TW] 对话框标题显示对应语言的"标签打印预览" 152ms -stderr | src/components/qr-code/__tests__/QRCodeGenerator.test.tsx > QRCodeGenerator - 多语言文本渲染 > [vi] 触发按钮显示对应语言的"生成二维码"文案 -IntlError: INVALID_KEY: Namespace keys cannot contain the character "." as this is used to express nesting. Please remove it or replace it with another character. - -Invalid keys: status.pending (at Production), status.confirmed (at Production), status.producing (at Production), status.completed (at Production), status.cancelled (at Production), status.label (at Production), priority.urgent (at Production), priority.high (at Production), priority.normal (at Production), priority.low (at Production), priority.label (at Production), units.sheet (at Production), units.roll (at Production), units.piece (at Production) - -If you're migrating from a flat structure, you can convert your messages as follows: - -import {set} from "lodash"; - -const input = { - "one.one": "1.1", - "one.two": "1.2", - "two.one.one": "2.1.1" -}; - -const output = Object.entries(input).reduce( - (acc, [key, value]) => set(acc, key, value), - {} -); - -// Output: -// -// { -// "one": { -// "one": "1.1", -// "two": "1.2" -// }, -// "two": { -// "one": { -// "one": "2.1.1" -// } -// } -// } - - at validateMessages (file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/initializeConfig-CUsOI8u2.js:465:13) - at initializeConfig (file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/initializeConfig-CUsOI8u2.js:515:7) - at file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/react.js:39:8 - at mountMemo (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:8777:23) - at Object.useMemo (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:26216:18) - at process.env.NODE_ENV.exports.useMemo (D:\dcprint\erp-project\node_modules\.pnpm\react@19.2.3\node_modules\react\cjs\react.development.js:1251:34) - at IntlProvider (file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/react.js:38:17) - at Object.react_stack_bottom_frame (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:25904:20) - at renderWithHooks (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:7662:22) - at updateFunctionComponent (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:10166:19) { - code: 'INVALID_KEY', - originalMessage: 'Namespace keys cannot contain the character "." as this is used to express nesting. Please remove it or replace it with another character.\n' + - '\n' + - 'Invalid keys: status.pending (at Production), status.confirmed (at Production), status.producing (at Production), status.completed (at Production), status.cancelled (at Production), status.label (at Production), priority.urgent (at Production), priority.high (at Production), priority.normal (at Production), priority.low (at Production), priority.label (at Production), units.sheet (at Production), units.roll (at Production), units.piece (at Production)\n' + - '\n' + - "If you're migrating from a flat structure, you can convert your messages as follows:\n" + - '\n' + - 'import {set} from "lodash";\n' + - '\n' + - 'const input = {\n' + - ' "one.one": "1.1",\n' + - ' "one.two": "1.2",\n' + - ' "two.one.one": "2.1.1"\n' + - '};\n' + - '\n' + - 'const output = Object.entries(input).reduce(\n' + - ' (acc, [key, value]) => set(acc, key, value),\n' + - ' {}\n' + - ');\n' + - '\n' + - '// Output:\n' + - '//\n' + - '// {\n' + - '// "one": {\n' + - '// "one": "1.1",\n' + - '// "two": "1.2"\n' + - '// },\n' + - '// "two": {\n' + - '// "one": {\n' + - '// "one": "2.1.1"\n' + - '// }\n' + - '// }\n' + - '// }\n' -} - - ✓ src/components/qr-code/__tests__/QRCodeGenerator.test.tsx > QRCodeGenerator - 多语言文本渲染 > [zh-TW] 触发按钮显示对应语言的"生成二维码"文案 63ms -stderr | src/components/qr-code/__tests__/QRCodeGenerator.test.tsx > QRCodeGenerator - 多语言文本渲染 > [zh-CN vs en] 同一按钮在不同语言下文案不同 -IntlError: INVALID_KEY: Namespace keys cannot contain the character "." as this is used to express nesting. Please remove it or replace it with another character. - -Invalid keys: status.pending (at Production), status.confirmed (at Production), status.producing (at Production), status.completed (at Production), status.cancelled (at Production), status.label (at Production), priority.urgent (at Production), priority.high (at Production), priority.normal (at Production), priority.low (at Production), priority.label (at Production), units.sheet (at Production), units.roll (at Production), units.piece (at Production) - -If you're migrating from a flat structure, you can convert your messages as follows: - -import {set} from "lodash"; - -const input = { - "one.one": "1.1", - "one.two": "1.2", - "two.one.one": "2.1.1" -}; - -const output = Object.entries(input).reduce( - (acc, [key, value]) => set(acc, key, value), - {} -); - -// Output: -// -// { -// "one": { -// "one": "1.1", -// "two": "1.2" -// }, -// "two": { -// "one": { -// "one": "2.1.1" -// } -// } -// } - - at validateMessages (file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/initializeConfig-CUsOI8u2.js:465:13) - at initializeConfig (file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/initializeConfig-CUsOI8u2.js:515:7) - at file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/react.js:39:8 - at mountMemo (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:8777:23) - at Object.useMemo (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:26216:18) - at process.env.NODE_ENV.exports.useMemo (D:\dcprint\erp-project\node_modules\.pnpm\react@19.2.3\node_modules\react\cjs\react.development.js:1251:34) - at IntlProvider (file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/react.js:38:17) - at Object.react_stack_bottom_frame (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:25904:20) - at renderWithHooks (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:7662:22) - at updateFunctionComponent (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:10166:19) { - code: 'INVALID_KEY', - originalMessage: 'Namespace keys cannot contain the character "." as this is used to express nesting. Please remove it or replace it with another character.\n' + - '\n' + - 'Invalid keys: status.pending (at Production), status.confirmed (at Production), status.producing (at Production), status.completed (at Production), status.cancelled (at Production), status.label (at Production), priority.urgent (at Production), priority.high (at Production), priority.normal (at Production), priority.low (at Production), priority.label (at Production), units.sheet (at Production), units.roll (at Production), units.piece (at Production)\n' + - '\n' + - "If you're migrating from a flat structure, you can convert your messages as follows:\n" + - '\n' + - 'import {set} from "lodash";\n' + - '\n' + - 'const input = {\n' + - ' "one.one": "1.1",\n' + - ' "one.two": "1.2",\n' + - ' "two.one.one": "2.1.1"\n' + - '};\n' + - '\n' + - 'const output = Object.entries(input).reduce(\n' + - ' (acc, [key, value]) => set(acc, key, value),\n' + - ' {}\n' + - ');\n' + - '\n' + - '// Output:\n' + - '//\n' + - '// {\n' + - '// "one": {\n' + - '// "one": "1.1",\n' + - '// "two": "1.2"\n' + - '// },\n' + - '// "two": {\n' + - '// "one": {\n' + - '// "one": "2.1.1"\n' + - '// }\n' + - '// }\n' + - '// }\n' -} -IntlError: INVALID_KEY: Namespace keys cannot contain the character "." as this is used to express nesting. Please remove it or replace it with another character. - -Invalid keys: status.pending (at Production), status.confirmed (at Production), status.producing (at Production), status.completed (at Production), status.cancelled (at Production), status.label (at Production), priority.urgent (at Production), priority.high (at Production), priority.normal (at Production), priority.low (at Production), priority.label (at Production), units.sheet (at Production), units.roll (at Production), units.piece (at Production) - -If you're migrating from a flat structure, you can convert your messages as follows: - -import {set} from "lodash"; - -const input = { - "one.one": "1.1", - "one.two": "1.2", - "two.one.one": "2.1.1" -}; - -const output = Object.entries(input).reduce( - (acc, [key, value]) => set(acc, key, value), - {} -); - -// Output: -// -// { -// "one": { -// "one": "1.1", -// "two": "1.2" -// }, -// "two": { -// "one": { -// "one": "2.1.1" -// } -// } -// } - - at validateMessages (file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/initializeConfig-CUsOI8u2.js:465:13) - at initializeConfig (file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/initializeConfig-CUsOI8u2.js:515:7) - at file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/react.js:39:8 - at mountMemo (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:8777:23) - at Object.useMemo (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:26216:18) - at process.env.NODE_ENV.exports.useMemo (D:\dcprint\erp-project\node_modules\.pnpm\react@19.2.3\node_modules\react\cjs\react.development.js:1251:34) - at IntlProvider (file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/react.js:38:17) - at Object.react_stack_bottom_frame (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:25904:20) - at renderWithHooks (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:7662:22) - at updateFunctionComponent (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:10166:19) { - code: 'INVALID_KEY', - originalMessage: 'Namespace keys cannot contain the character "." as this is used to express nesting. Please remove it or replace it with another character.\n' + - '\n' + - 'Invalid keys: status.pending (at Production), status.confirmed (at Production), status.producing (at Production), status.completed (at Production), status.cancelled (at Production), status.label (at Production), priority.urgent (at Production), priority.high (at Production), priority.normal (at Production), priority.low (at Production), priority.label (at Production), units.sheet (at Production), units.roll (at Production), units.piece (at Production)\n' + - '\n' + - "If you're migrating from a flat structure, you can convert your messages as follows:\n" + - '\n' + - 'import {set} from "lodash";\n' + - '\n' + - 'const input = {\n' + - ' "one.one": "1.1",\n' + - ' "one.two": "1.2",\n' + - ' "two.one.one": "2.1.1"\n' + - '};\n' + - '\n' + - 'const output = Object.entries(input).reduce(\n' + - ' (acc, [key, value]) => set(acc, key, value),\n' + - ' {}\n' + - ');\n' + - '\n' + - '// Output:\n' + - '//\n' + - '// {\n' + - '// "one": {\n' + - '// "one": "1.1",\n' + - '// "two": "1.2"\n' + - '// },\n' + - '// "two": {\n' + - '// "one": {\n' + - '// "one": "2.1.1"\n' + - '// }\n' + - '// }\n' + - '// }\n' -} - - ✓ src/components/qr-code/__tests__/QRCodeGenerator.test.tsx > QRCodeGenerator - 多语言文本渲染 > [vi] 触发按钮显示对应语言的"生成二维码"文案 148ms -stderr | src/components/printing/__tests__/LabelPrintPreview.test.tsx > LabelPrintPreview - 多语言标题与字段 > [zh-CN] 标签字段显示中文标签前缀 -IntlError: INVALID_KEY: Namespace keys cannot contain the character "." as this is used to express nesting. Please remove it or replace it with another character. - -Invalid keys: status.pending (at Production), status.confirmed (at Production), status.producing (at Production), status.completed (at Production), status.cancelled (at Production), status.label (at Production), priority.urgent (at Production), priority.high (at Production), priority.normal (at Production), priority.low (at Production), priority.label (at Production), units.sheet (at Production), units.roll (at Production), units.piece (at Production) - -If you're migrating from a flat structure, you can convert your messages as follows: - -import {set} from "lodash"; - -const input = { - "one.one": "1.1", - "one.two": "1.2", - "two.one.one": "2.1.1" -}; - -const output = Object.entries(input).reduce( - (acc, [key, value]) => set(acc, key, value), - {} -); - -// Output: -// -// { -// "one": { -// "one": "1.1", -// "two": "1.2" -// }, -// "two": { -// "one": { -// "one": "2.1.1" -// } -// } -// } - - at validateMessages (file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/initializeConfig-CUsOI8u2.js:465:13) - at initializeConfig (file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/initializeConfig-CUsOI8u2.js:515:7) - at file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/react.js:39:8 - at mountMemo (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:8777:23) - at Object.useMemo (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:26216:18) - at process.env.NODE_ENV.exports.useMemo (D:\dcprint\erp-project\node_modules\.pnpm\react@19.2.3\node_modules\react\cjs\react.development.js:1251:34) - at IntlProvider (file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/react.js:38:17) - at Object.react_stack_bottom_frame (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:25904:20) - at renderWithHooks (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:7662:22) - at updateFunctionComponent (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:10166:19) { - code: 'INVALID_KEY', - originalMessage: 'Namespace keys cannot contain the character "." as this is used to express nesting. Please remove it or replace it with another character.\n' + - '\n' + - 'Invalid keys: status.pending (at Production), status.confirmed (at Production), status.producing (at Production), status.completed (at Production), status.cancelled (at Production), status.label (at Production), priority.urgent (at Production), priority.high (at Production), priority.normal (at Production), priority.low (at Production), priority.label (at Production), units.sheet (at Production), units.roll (at Production), units.piece (at Production)\n' + - '\n' + - "If you're migrating from a flat structure, you can convert your messages as follows:\n" + - '\n' + - 'import {set} from "lodash";\n' + - '\n' + - 'const input = {\n' + - ' "one.one": "1.1",\n' + - ' "one.two": "1.2",\n' + - ' "two.one.one": "2.1.1"\n' + - '};\n' + - '\n' + - 'const output = Object.entries(input).reduce(\n' + - ' (acc, [key, value]) => set(acc, key, value),\n' + - ' {}\n' + - ');\n' + - '\n' + - '// Output:\n' + - '//\n' + - '// {\n' + - '// "one": {\n' + - '// "one": "1.1",\n' + - '// "two": "1.2"\n' + - '// },\n' + - '// "two": {\n' + - '// "one": {\n' + - '// "one": "2.1.1"\n' + - '// }\n' + - '// }\n' + - '// }\n' -} -Warning: Missing `Description` or `aria-describedby={undefined}` for {DialogContent}. - - ✓ src/components/printing/__tests__/LabelPrintPreview.test.tsx > LabelPrintPreview - 多语言标题与字段 > [vi] 对话框标题显示对应语言的"标签打印预览" 148ms -[2026-08-27 11:31:22.901 +0800] DEBUG: No handlers for event - eventType: "saga.completed" - ✓ tests/integration/cross-module-consistency.test.ts > 跨模块一致性集成测试 > 工单完工 Saga > 完整链路成功时应记录所有步骤为 success 391ms -[2026-08-27 11:31:23.025 +0800] ERROR: Event handler failed - eventType: "workorder.completed" - handlerIndex: 0 - error: "Error: 库存入库失败" -[2026-08-27 11:31:23.054 +0800] DEBUG: No handlers for event - eventType: "saga.failed" -stderr | src/components/printing/__tests__/LabelPrintPreview.test.tsx > LabelPrintPreview - 多语言标题与字段 > [en] 标签字段显示英文标签前缀 -IntlError: INVALID_KEY: Namespace keys cannot contain the character "." as this is used to express nesting. Please remove it or replace it with another character. - -Invalid keys: status.pending (at Production), status.confirmed (at Production), status.producing (at Production), status.completed (at Production), status.cancelled (at Production), status.label (at Production), priority.urgent (at Production), priority.high (at Production), priority.normal (at Production), priority.low (at Production), priority.label (at Production), units.sheet (at Production), units.roll (at Production), units.piece (at Production) - -If you're migrating from a flat structure, you can convert your messages as follows: - -import {set} from "lodash"; - -const input = { - "one.one": "1.1", - "one.two": "1.2", - "two.one.one": "2.1.1" -}; - -const output = Object.entries(input).reduce( - (acc, [key, value]) => set(acc, key, value), - {} -); - -// Output: -// -// { -// "one": { -// "one": "1.1", -// "two": "1.2" -// }, -// "two": { -// "one": { -// "one": "2.1.1" -// } -// } -// } - - at validateMessages (file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/initializeConfig-CUsOI8u2.js:465:13) - at initializeConfig (file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/initializeConfig-CUsOI8u2.js:515:7) - at file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/react.js:39:8 - at mountMemo (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:8777:23) - at Object.useMemo (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:26216:18) - at process.env.NODE_ENV.exports.useMemo (D:\dcprint\erp-project\node_modules\.pnpm\react@19.2.3\node_modules\react\cjs\react.development.js:1251:34) - at IntlProvider (file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/react.js:38:17) - at Object.react_stack_bottom_frame (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:25904:20) - at renderWithHooks (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:7662:22) - at updateFunctionComponent (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:10166:19) { - code: 'INVALID_KEY', - originalMessage: 'Namespace keys cannot contain the character "." as this is used to express nesting. Please remove it or replace it with another character.\n' + - '\n' + - 'Invalid keys: status.pending (at Production), status.confirmed (at Production), status.producing (at Production), status.completed (at Production), status.cancelled (at Production), status.label (at Production), priority.urgent (at Production), priority.high (at Production), priority.normal (at Production), priority.low (at Production), priority.label (at Production), units.sheet (at Production), units.roll (at Production), units.piece (at Production)\n' + - '\n' + - "If you're migrating from a flat structure, you can convert your messages as follows:\n" + - '\n' + - 'import {set} from "lodash";\n' + - '\n' + - 'const input = {\n' + - ' "one.one": "1.1",\n' + - ' "one.two": "1.2",\n' + - ' "two.one.one": "2.1.1"\n' + - '};\n' + - '\n' + - 'const output = Object.entries(input).reduce(\n' + - ' (acc, [key, value]) => set(acc, key, value),\n' + - ' {}\n' + - ');\n' + - '\n' + - '// Output:\n' + - '//\n' + - '// {\n' + - '// "one": {\n' + - '// "one": "1.1",\n' + - '// "two": "1.2"\n' + - '// },\n' + - '// "two": {\n' + - '// "one": {\n' + - '// "one": "2.1.1"\n' + - '// }\n' + - '// }\n' + - '// }\n' -} -Warning: Missing `Description` or `aria-describedby={undefined}` for {DialogContent}. - - ✓ src/components/printing/__tests__/LabelPrintPreview.test.tsx > LabelPrintPreview - 多语言标题与字段 > [zh-CN] 标签字段显示中文标签前缀 176ms - ✓ tests/integration/cross-module-consistency.test.ts > 跨模块一致性集成测试 > 工单完工 Saga > 中间步骤失败时应触发补偿事件 206ms - ✓ tests/unit/infrastructure/event-bus/event-bus-type-switch.test.ts > 1.6 OutboxPoller 自动启动(AppInitializer) > EVENT_BUS_TYPE=db 时 AppInitializer 自动启动 OutboxPoller 794ms - ✓ tests/unit/infrastructure/event-bus/event-bus-type-switch.test.ts > 1.6 OutboxPoller 自动启动(AppInitializer) > EVENT_BUS_TYPE=memory 时 AppInitializer 不启动 OutboxPoller 4ms -[2026-08-27 11:31:23.282 +0800] DEBUG: No handlers for event - eventType: "saga.completed" -stderr | src/components/qr-code/__tests__/QRCodeGenerator.test.tsx > QRCodeGenerator - 核心逻辑分支 > 未选择类型时显示 selectType 提示,且不调用 fetch -IntlError: INVALID_KEY: Namespace keys cannot contain the character "." as this is used to express nesting. Please remove it or replace it with another character. - -Invalid keys: status.pending (at Production), status.confirmed (at Production), status.producing (at Production), status.completed (at Production), status.cancelled (at Production), status.label (at Production), priority.urgent (at Production), priority.high (at Production), priority.normal (at Production), priority.low (at Production), priority.label (at Production), units.sheet (at Production), units.roll (at Production), units.piece (at Production) - -If you're migrating from a flat structure, you can convert your messages as follows: - -import {set} from "lodash"; - -const input = { - "one.one": "1.1", - "one.two": "1.2", - "two.one.one": "2.1.1" -}; - -const output = Object.entries(input).reduce( - (acc, [key, value]) => set(acc, key, value), - {} -); - -// Output: -// -// { -// "one": { -// "one": "1.1", -// "two": "1.2" -// }, -// "two": { -// "one": { -// "one": "2.1.1" -// } -// } -// } - - at validateMessages (file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/initializeConfig-CUsOI8u2.js:465:13) - at initializeConfig (file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/initializeConfig-CUsOI8u2.js:515:7) - at file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/react.js:39:8 - at mountMemo (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:8777:23) - at Object.useMemo (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:26216:18) - at process.env.NODE_ENV.exports.useMemo (D:\dcprint\erp-project\node_modules\.pnpm\react@19.2.3\node_modules\react\cjs\react.development.js:1251:34) - at IntlProvider (file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/react.js:38:17) - at Object.react_stack_bottom_frame (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:25904:20) - at renderWithHooks (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:7662:22) - at updateFunctionComponent (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:10166:19) { - code: 'INVALID_KEY', - originalMessage: 'Namespace keys cannot contain the character "." as this is used to express nesting. Please remove it or replace it with another character.\n' + - '\n' + - 'Invalid keys: status.pending (at Production), status.confirmed (at Production), status.producing (at Production), status.completed (at Production), status.cancelled (at Production), status.label (at Production), priority.urgent (at Production), priority.high (at Production), priority.normal (at Production), priority.low (at Production), priority.label (at Production), units.sheet (at Production), units.roll (at Production), units.piece (at Production)\n' + - '\n' + - "If you're migrating from a flat structure, you can convert your messages as follows:\n" + - '\n' + - 'import {set} from "lodash";\n' + - '\n' + - 'const input = {\n' + - ' "one.one": "1.1",\n' + - ' "one.two": "1.2",\n' + - ' "two.one.one": "2.1.1"\n' + - '};\n' + - '\n' + - 'const output = Object.entries(input).reduce(\n' + - ' (acc, [key, value]) => set(acc, key, value),\n' + - ' {}\n' + - ');\n' + - '\n' + - '// Output:\n' + - '//\n' + - '// {\n' + - '// "one": {\n' + - '// "one": "1.1",\n' + - '// "two": "1.2"\n' + - '// },\n' + - '// "two": {\n' + - '// "one": {\n' + - '// "one": "2.1.1"\n' + - '// }\n' + - '// }\n' + - '// }\n' -} -Warning: Missing `Description` or `aria-describedby={undefined}` for {DialogContent}. - - ✓ src/components/qr-code/__tests__/QRCodeGenerator.test.tsx > QRCodeGenerator - 多语言文本渲染 > [zh-CN vs en] 同一按钮在不同语言下文案不同 112ms - ✓ tests/integration/cross-module-consistency.test.ts > 跨模块一致性集成测试 > 领料 Saga > 领料完整链路应成功执行 169ms -[2026-08-27 11:31:23.383 +0800] ERROR: Event handler failed - eventType: "inventory.material.deduct" - handlerIndex: 0 - error: "Error: 库存不足" -[2026-08-27 11:31:23.407 +0800] DEBUG: No handlers for event - eventType: "saga.failed" - ✓ tests/integration/cross-module-consistency.test.ts > 跨模块一致性集成测试 > 领料 Saga > 库存扣减失败时应补偿领料单状态 173ms -stderr | src/components/qr-code/__tests__/QRCodeGenerator.test.tsx > QRCodeGenerator - 核心逻辑分支 > 生成成功后显示 generateSuccess toast 并展示二维码 -IntlError: INVALID_KEY: Namespace keys cannot contain the character "." as this is used to express nesting. Please remove it or replace it with another character. - -Invalid keys: status.pending (at Production), status.confirmed (at Production), status.producing (at Production), status.completed (at Production), status.cancelled (at Production), status.label (at Production), priority.urgent (at Production), priority.high (at Production), priority.normal (at Production), priority.low (at Production), priority.label (at Production), units.sheet (at Production), units.roll (at Production), units.piece (at Production) - -If you're migrating from a flat structure, you can convert your messages as follows: - -import {set} from "lodash"; - -const input = { - "one.one": "1.1", - "one.two": "1.2", - "two.one.one": "2.1.1" -}; - -const output = Object.entries(input).reduce( - (acc, [key, value]) => set(acc, key, value), - {} -); - -// Output: -// -// { -// "one": { -// "one": "1.1", -// "two": "1.2" -// }, -// "two": { -// "one": { -// "one": "2.1.1" -// } -// } -// } - - at validateMessages (file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/initializeConfig-CUsOI8u2.js:465:13) - at initializeConfig (file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/initializeConfig-CUsOI8u2.js:515:7) - at file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/react.js:39:8 - at mountMemo (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:8777:23) - at Object.useMemo (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:26216:18) - at process.env.NODE_ENV.exports.useMemo (D:\dcprint\erp-project\node_modules\.pnpm\react@19.2.3\node_modules\react\cjs\react.development.js:1251:34) - at IntlProvider (file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/react.js:38:17) - at Object.react_stack_bottom_frame (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:25904:20) - at renderWithHooks (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:7662:22) - at updateFunctionComponent (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:10166:19) { - code: 'INVALID_KEY', - originalMessage: 'Namespace keys cannot contain the character "." as this is used to express nesting. Please remove it or replace it with another character.\n' + - '\n' + - 'Invalid keys: status.pending (at Production), status.confirmed (at Production), status.producing (at Production), status.completed (at Production), status.cancelled (at Production), status.label (at Production), priority.urgent (at Production), priority.high (at Production), priority.normal (at Production), priority.low (at Production), priority.label (at Production), units.sheet (at Production), units.roll (at Production), units.piece (at Production)\n' + - '\n' + - "If you're migrating from a flat structure, you can convert your messages as follows:\n" + - '\n' + - 'import {set} from "lodash";\n' + - '\n' + - 'const input = {\n' + - ' "one.one": "1.1",\n' + - ' "one.two": "1.2",\n' + - ' "two.one.one": "2.1.1"\n' + - '};\n' + - '\n' + - 'const output = Object.entries(input).reduce(\n' + - ' (acc, [key, value]) => set(acc, key, value),\n' + - ' {}\n' + - ');\n' + - '\n' + - '// Output:\n' + - '//\n' + - '// {\n' + - '// "one": {\n' + - '// "one": "1.1",\n' + - '// "two": "1.2"\n' + - '// },\n' + - '// "two": {\n' + - '// "one": {\n' + - '// "one": "2.1.1"\n' + - '// }\n' + - '// }\n' + - '// }\n' -} -Warning: Missing `Description` or `aria-describedby={undefined}` for {DialogContent}. - -stderr | src/components/printing/__tests__/LabelPrintPreview.test.tsx > LabelPrintPreview - 多语言标题与字段 > [zh-TW vs zh-CN] 物料编码前缀使用繁简不同文案 -IntlError: INVALID_KEY: Namespace keys cannot contain the character "." as this is used to express nesting. Please remove it or replace it with another character. - -Invalid keys: status.pending (at Production), status.confirmed (at Production), status.producing (at Production), status.completed (at Production), status.cancelled (at Production), status.label (at Production), priority.urgent (at Production), priority.high (at Production), priority.normal (at Production), priority.low (at Production), priority.label (at Production), units.sheet (at Production), units.roll (at Production), units.piece (at Production) - -If you're migrating from a flat structure, you can convert your messages as follows: - -import {set} from "lodash"; - -const input = { - "one.one": "1.1", - "one.two": "1.2", - "two.one.one": "2.1.1" -}; - -const output = Object.entries(input).reduce( - (acc, [key, value]) => set(acc, key, value), - {} -); - -// Output: -// -// { -// "one": { -// "one": "1.1", -// "two": "1.2" -// }, -// "two": { -// "one": { -// "one": "2.1.1" -// } -// } -// } - - at validateMessages (file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/initializeConfig-CUsOI8u2.js:465:13) - at initializeConfig (file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/initializeConfig-CUsOI8u2.js:515:7) - at file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/react.js:39:8 - at mountMemo (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:8777:23) - at Object.useMemo (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:26216:18) - at process.env.NODE_ENV.exports.useMemo (D:\dcprint\erp-project\node_modules\.pnpm\react@19.2.3\node_modules\react\cjs\react.development.js:1251:34) - at IntlProvider (file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/react.js:38:17) - at Object.react_stack_bottom_frame (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:25904:20) - at renderWithHooks (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:7662:22) - at updateFunctionComponent (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:10166:19) { - code: 'INVALID_KEY', - originalMessage: 'Namespace keys cannot contain the character "." as this is used to express nesting. Please remove it or replace it with another character.\n' + - '\n' + - 'Invalid keys: status.pending (at Production), status.confirmed (at Production), status.producing (at Production), status.completed (at Production), status.cancelled (at Production), status.label (at Production), priority.urgent (at Production), priority.high (at Production), priority.normal (at Production), priority.low (at Production), priority.label (at Production), units.sheet (at Production), units.roll (at Production), units.piece (at Production)\n' + - '\n' + - "If you're migrating from a flat structure, you can convert your messages as follows:\n" + - '\n' + - 'import {set} from "lodash";\n' + - '\n' + - 'const input = {\n' + - ' "one.one": "1.1",\n' + - ' "one.two": "1.2",\n' + - ' "two.one.one": "2.1.1"\n' + - '};\n' + - '\n' + - 'const output = Object.entries(input).reduce(\n' + - ' (acc, [key, value]) => set(acc, key, value),\n' + - ' {}\n' + - ');\n' + - '\n' + - '// Output:\n' + - '//\n' + - '// {\n' + - '// "one": {\n' + - '// "one": "1.1",\n' + - '// "two": "1.2"\n' + - '// },\n' + - '// "two": {\n' + - '// "one": {\n' + - '// "one": "2.1.1"\n' + - '// }\n' + - '// }\n' + - '// }\n' -} -Warning: Missing `Description` or `aria-describedby={undefined}` for {DialogContent}. -IntlError: INVALID_KEY: Namespace keys cannot contain the character "." as this is used to express nesting. Please remove it or replace it with another character. - -Invalid keys: status.pending (at Production), status.confirmed (at Production), status.producing (at Production), status.completed (at Production), status.cancelled (at Production), status.label (at Production), priority.urgent (at Production), priority.high (at Production), priority.normal (at Production), priority.low (at Production), priority.label (at Production), units.sheet (at Production), units.roll (at Production), units.piece (at Production) - -If you're migrating from a flat structure, you can convert your messages as follows: - -import {set} from "lodash"; - -const input = { - "one.one": "1.1", - "one.two": "1.2", - "two.one.one": "2.1.1" -}; - -const output = Object.entries(input).reduce( - (acc, [key, value]) => set(acc, key, value), - {} -); - -// Output: -// -// { -// "one": { -// "one": "1.1", -// "two": "1.2" -// }, -// "two": { -// "one": { -// "one": "2.1.1" -// } -// } -// } - - at validateMessages (file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/initializeConfig-CUsOI8u2.js:465:13) - at initializeConfig (file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/initializeConfig-CUsOI8u2.js:515:7) - at file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/react.js:39:8 - at mountMemo (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:8777:23) - at Object.useMemo (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:26216:18) - at process.env.NODE_ENV.exports.useMemo (D:\dcprint\erp-project\node_modules\.pnpm\react@19.2.3\node_modules\react\cjs\react.development.js:1251:34) - at IntlProvider (file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/react.js:38:17) - at Object.react_stack_bottom_frame (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:25904:20) - at renderWithHooks (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:7662:22) - at updateFunctionComponent (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:10166:19) { - code: 'INVALID_KEY', - originalMessage: 'Namespace keys cannot contain the character "." as this is used to express nesting. Please remove it or replace it with another character.\n' + - '\n' + - 'Invalid keys: status.pending (at Production), status.confirmed (at Production), status.producing (at Production), status.completed (at Production), status.cancelled (at Production), status.label (at Production), priority.urgent (at Production), priority.high (at Production), priority.normal (at Production), priority.low (at Production), priority.label (at Production), units.sheet (at Production), units.roll (at Production), units.piece (at Production)\n' + - '\n' + - "If you're migrating from a flat structure, you can convert your messages as follows:\n" + - '\n' + - 'import {set} from "lodash";\n' + - '\n' + - 'const input = {\n' + - ' "one.one": "1.1",\n' + - ' "one.two": "1.2",\n' + - ' "two.one.one": "2.1.1"\n' + - '};\n' + - '\n' + - 'const output = Object.entries(input).reduce(\n' + - ' (acc, [key, value]) => set(acc, key, value),\n' + - ' {}\n' + - ');\n' + - '\n' + - '// Output:\n' + - '//\n' + - '// {\n' + - '// "one": {\n' + - '// "one": "1.1",\n' + - '// "two": "1.2"\n' + - '// },\n' + - '// "two": {\n' + - '// "one": {\n' + - '// "one": "2.1.1"\n' + - '// }\n' + - '// }\n' + - '// }\n' -} -Warning: Missing `Description` or `aria-describedby={undefined}` for {DialogContent}. - - ✓ src/components/printing/__tests__/LabelPrintPreview.test.tsx > LabelPrintPreview - 多语言标题与字段 > [en] 标签字段显示英文标签前缀 166ms -[2026-08-27 11:31:23.659 +0800] DEBUG: No handlers for event - eventType: "saga.completed" - ✓ src/components/qr-code/__tests__/QRCodeGenerator.test.tsx > QRCodeGenerator - 核心逻辑分支 > 未选择类型时显示 selectType 提示,且不调用 fetch 439ms -stderr | src/components/printing/__tests__/LabelPrintPreview.test.tsx > LabelPrintPreview - 统计文本 totalLabelsCount > 单个标签 1 份时显示 1 × 1 = 1 -IntlError: INVALID_KEY: Namespace keys cannot contain the character "." as this is used to express nesting. Please remove it or replace it with another character. - -Invalid keys: status.pending (at Production), status.confirmed (at Production), status.producing (at Production), status.completed (at Production), status.cancelled (at Production), status.label (at Production), priority.urgent (at Production), priority.high (at Production), priority.normal (at Production), priority.low (at Production), priority.label (at Production), units.sheet (at Production), units.roll (at Production), units.piece (at Production) - -If you're migrating from a flat structure, you can convert your messages as follows: - -import {set} from "lodash"; - -const input = { - "one.one": "1.1", - "one.two": "1.2", - "two.one.one": "2.1.1" -}; - -const output = Object.entries(input).reduce( - (acc, [key, value]) => set(acc, key, value), - {} -); - -// Output: -// -// { -// "one": { -// "one": "1.1", -// "two": "1.2" -// }, -// "two": { -// "one": { -// "one": "2.1.1" -// } -// } -// } - - at validateMessages (file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/initializeConfig-CUsOI8u2.js:465:13) - at initializeConfig (file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/initializeConfig-CUsOI8u2.js:515:7) - at file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/react.js:39:8 - at mountMemo (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:8777:23) - at Object.useMemo (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:26216:18) - at process.env.NODE_ENV.exports.useMemo (D:\dcprint\erp-project\node_modules\.pnpm\react@19.2.3\node_modules\react\cjs\react.development.js:1251:34) - at IntlProvider (file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/react.js:38:17) - at Object.react_stack_bottom_frame (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:25904:20) - at renderWithHooks (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:7662:22) - at updateFunctionComponent (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:10166:19) { - code: 'INVALID_KEY', - originalMessage: 'Namespace keys cannot contain the character "." as this is used to express nesting. Please remove it or replace it with another character.\n' + - '\n' + - 'Invalid keys: status.pending (at Production), status.confirmed (at Production), status.producing (at Production), status.completed (at Production), status.cancelled (at Production), status.label (at Production), priority.urgent (at Production), priority.high (at Production), priority.normal (at Production), priority.low (at Production), priority.label (at Production), units.sheet (at Production), units.roll (at Production), units.piece (at Production)\n' + - '\n' + - "If you're migrating from a flat structure, you can convert your messages as follows:\n" + - '\n' + - 'import {set} from "lodash";\n' + - '\n' + - 'const input = {\n' + - ' "one.one": "1.1",\n' + - ' "one.two": "1.2",\n' + - ' "two.one.one": "2.1.1"\n' + - '};\n' + - '\n' + - 'const output = Object.entries(input).reduce(\n' + - ' (acc, [key, value]) => set(acc, key, value),\n' + - ' {}\n' + - ');\n' + - '\n' + - '// Output:\n' + - '//\n' + - '// {\n' + - '// "one": {\n' + - '// "one": "1.1",\n' + - '// "two": "1.2"\n' + - '// },\n' + - '// "two": {\n' + - '// "one": {\n' + - '// "one": "2.1.1"\n' + - '// }\n' + - '// }\n' + - '// }\n' -} -Warning: Missing `Description` or `aria-describedby={undefined}` for {DialogContent}. - - ✓ src/components/printing/__tests__/LabelPrintPreview.test.tsx > LabelPrintPreview - 多语言标题与字段 > [zh-TW vs zh-CN] 物料编码前缀使用繁简不同文案 540ms - ✓ tests/integration/cross-module-consistency.test.ts > 跨模块一致性集成测试 > 报工 Saga > 报工完整链路应成功执行 212ms -[2026-08-27 11:31:23.925 +0800] DEBUG: No handlers for event - eventType: "saga.completed" - × tests/integration/cross-module-consistency.test.ts > 跨模块一致性集成测试 > Saga 状态流转 > 应正确处理 pending → executing → success 流程 276ms - → expected 1 to be +0 // Object.is equality -stderr | src/components/qr-code/__tests__/QRCodeGenerator.test.tsx > QRCodeGenerator - 核心逻辑分支 > 生成接口返回失败时显示 generateFailed toast -IntlError: INVALID_KEY: Namespace keys cannot contain the character "." as this is used to express nesting. Please remove it or replace it with another character. - -Invalid keys: status.pending (at Production), status.confirmed (at Production), status.producing (at Production), status.completed (at Production), status.cancelled (at Production), status.label (at Production), priority.urgent (at Production), priority.high (at Production), priority.normal (at Production), priority.low (at Production), priority.label (at Production), units.sheet (at Production), units.roll (at Production), units.piece (at Production) - -If you're migrating from a flat structure, you can convert your messages as follows: - -import {set} from "lodash"; - -const input = { - "one.one": "1.1", - "one.two": "1.2", - "two.one.one": "2.1.1" -}; - -const output = Object.entries(input).reduce( - (acc, [key, value]) => set(acc, key, value), - {} -); - -// Output: -// -// { -// "one": { -// "one": "1.1", -// "two": "1.2" -// }, -// "two": { -// "one": { -// "one": "2.1.1" -// } -// } -// } - - at validateMessages (file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/initializeConfig-CUsOI8u2.js:465:13) - at initializeConfig (file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/initializeConfig-CUsOI8u2.js:515:7) - at file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/react.js:39:8 - at mountMemo (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:8777:23) - at Object.useMemo (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:26216:18) - at process.env.NODE_ENV.exports.useMemo (D:\dcprint\erp-project\node_modules\.pnpm\react@19.2.3\node_modules\react\cjs\react.development.js:1251:34) - at IntlProvider (file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/react.js:38:17) - at Object.react_stack_bottom_frame (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:25904:20) - at renderWithHooks (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:7662:22) - at updateFunctionComponent (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:10166:19) { - code: 'INVALID_KEY', - originalMessage: 'Namespace keys cannot contain the character "." as this is used to express nesting. Please remove it or replace it with another character.\n' + - '\n' + - 'Invalid keys: status.pending (at Production), status.confirmed (at Production), status.producing (at Production), status.completed (at Production), status.cancelled (at Production), status.label (at Production), priority.urgent (at Production), priority.high (at Production), priority.normal (at Production), priority.low (at Production), priority.label (at Production), units.sheet (at Production), units.roll (at Production), units.piece (at Production)\n' + - '\n' + - "If you're migrating from a flat structure, you can convert your messages as follows:\n" + - '\n' + - 'import {set} from "lodash";\n' + - '\n' + - 'const input = {\n' + - ' "one.one": "1.1",\n' + - ' "one.two": "1.2",\n' + - ' "two.one.one": "2.1.1"\n' + - '};\n' + - '\n' + - 'const output = Object.entries(input).reduce(\n' + - ' (acc, [key, value]) => set(acc, key, value),\n' + - ' {}\n' + - ');\n' + - '\n' + - '// Output:\n' + - '//\n' + - '// {\n' + - '// "one": {\n' + - '// "one": "1.1",\n' + - '// "two": "1.2"\n' + - '// },\n' + - '// "two": {\n' + - '// "one": {\n' + - '// "one": "2.1.1"\n' + - '// }\n' + - '// }\n' + - '// }\n' -} -Warning: Missing `Description` or `aria-describedby={undefined}` for {DialogContent}. - -stdout | tests/concurrency/inventory-outbound.test.ts > 库存出库并发测试 -准备测试环境... - - ✓ src/components/qr-code/__tests__/QRCodeGenerator.test.tsx > QRCodeGenerator - 核心逻辑分支 > 生成成功后显示 generateSuccess toast 并展示二维码 412ms -stderr | src/components/printing/__tests__/LabelPrintPreview.test.tsx > LabelPrintPreview - 统计文本 totalLabelsCount > 多个标签 + copies 显示正确总数 -IntlError: INVALID_KEY: Namespace keys cannot contain the character "." as this is used to express nesting. Please remove it or replace it with another character. - -Invalid keys: status.pending (at Production), status.confirmed (at Production), status.producing (at Production), status.completed (at Production), status.cancelled (at Production), status.label (at Production), priority.urgent (at Production), priority.high (at Production), priority.normal (at Production), priority.low (at Production), priority.label (at Production), units.sheet (at Production), units.roll (at Production), units.piece (at Production) - -If you're migrating from a flat structure, you can convert your messages as follows: - -import {set} from "lodash"; - -const input = { - "one.one": "1.1", - "one.two": "1.2", - "two.one.one": "2.1.1" -}; - -const output = Object.entries(input).reduce( - (acc, [key, value]) => set(acc, key, value), - {} -); - -// Output: -// -// { -// "one": { -// "one": "1.1", -// "two": "1.2" -// }, -// "two": { -// "one": { -// "one": "2.1.1" -// } -// } -// } - - at validateMessages (file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/initializeConfig-CUsOI8u2.js:465:13) - at initializeConfig (file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/initializeConfig-CUsOI8u2.js:515:7) - at file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/react.js:39:8 - at mountMemo (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:8777:23) - at Object.useMemo (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:26216:18) - at process.env.NODE_ENV.exports.useMemo (D:\dcprint\erp-project\node_modules\.pnpm\react@19.2.3\node_modules\react\cjs\react.development.js:1251:34) - at IntlProvider (file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/react.js:38:17) - at Object.react_stack_bottom_frame (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:25904:20) - at renderWithHooks (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:7662:22) - at updateFunctionComponent (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:10166:19) { - code: 'INVALID_KEY', - originalMessage: 'Namespace keys cannot contain the character "." as this is used to express nesting. Please remove it or replace it with another character.\n' + - '\n' + - 'Invalid keys: status.pending (at Production), status.confirmed (at Production), status.producing (at Production), status.completed (at Production), status.cancelled (at Production), status.label (at Production), priority.urgent (at Production), priority.high (at Production), priority.normal (at Production), priority.low (at Production), priority.label (at Production), units.sheet (at Production), units.roll (at Production), units.piece (at Production)\n' + - '\n' + - "If you're migrating from a flat structure, you can convert your messages as follows:\n" + - '\n' + - 'import {set} from "lodash";\n' + - '\n' + - 'const input = {\n' + - ' "one.one": "1.1",\n' + - ' "one.two": "1.2",\n' + - ' "two.one.one": "2.1.1"\n' + - '};\n' + - '\n' + - 'const output = Object.entries(input).reduce(\n' + - ' (acc, [key, value]) => set(acc, key, value),\n' + - ' {}\n' + - ');\n' + - '\n' + - '// Output:\n' + - '//\n' + - '// {\n' + - '// "one": {\n' + - '// "one": "1.1",\n' + - '// "two": "1.2"\n' + - '// },\n' + - '// "two": {\n' + - '// "one": {\n' + - '// "one": "2.1.1"\n' + - '// }\n' + - '// }\n' + - '// }\n' -} -Warning: Missing `Description` or `aria-describedby={undefined}` for {DialogContent}. - - ✓ src/components/printing/__tests__/LabelPrintPreview.test.tsx > LabelPrintPreview - 统计文本 totalLabelsCount > 单个标签 1 份时显示 1 × 1 = 1 123ms -stdout | tests/concurrency/stocktaking-approve.test.ts > 盘点审批并发测试 -准备测试环境... - -stdout | tests/concurrency/inventory-outbound.test.ts > 库存出库并发测试 -测试仓库: 测试仓库_1787801484143_n05e3 (ID: 213) -测试物料: 测试物料_1787801484285_pat3yn (ID: 99044, 初始库存: 1000) - -stdout | tests/concurrency/inventory-outbound.test.ts > 库存出库并发测试 > 并发创建出库单并确认出库 - -步骤1: 创建 10 个出库单... - -stdout | tests/concurrency/stocktaking-approve.test.ts > 盘点审批并发测试 -测试仓库: 测试仓库_1787801484305_o12ma (ID: 214) -测试物料: 测试物料_1787801484404_6kmyk (ID: 99045, 初始库存: 500) - -stdout | tests/concurrency/stocktaking-approve.test.ts > 盘点审批并发测试 > 并发创建和审批盘点单 - -步骤1: 创建 5 个盘点单... - -stdout | tests/concurrency/inventory-outbound.test.ts > 库存出库并发测试 > 并发创建出库单并确认出库 -已创建 10 个出库单 - -步骤2: 并发确认出库... - -stderr | src/components/printing/__tests__/LabelPrintPreview.test.tsx > LabelPrintPreview - 交互与异常 > 关闭按钮触发 onClose 回调 -IntlError: INVALID_KEY: Namespace keys cannot contain the character "." as this is used to express nesting. Please remove it or replace it with another character. - -Invalid keys: status.pending (at Production), status.confirmed (at Production), status.producing (at Production), status.completed (at Production), status.cancelled (at Production), status.label (at Production), priority.urgent (at Production), priority.high (at Production), priority.normal (at Production), priority.low (at Production), priority.label (at Production), units.sheet (at Production), units.roll (at Production), units.piece (at Production) - -If you're migrating from a flat structure, you can convert your messages as follows: - -import {set} from "lodash"; - -const input = { - "one.one": "1.1", - "one.two": "1.2", - "two.one.one": "2.1.1" -}; - -const output = Object.entries(input).reduce( - (acc, [key, value]) => set(acc, key, value), - {} -); - -// Output: -// -// { -// "one": { -// "one": "1.1", -// "two": "1.2" -// }, -// "two": { -// "one": { -// "one": "2.1.1" -// } -// } -// } - - at validateMessages (file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/initializeConfig-CUsOI8u2.js:465:13) - at initializeConfig (file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/initializeConfig-CUsOI8u2.js:515:7) - at file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/react.js:39:8 - at mountMemo (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:8777:23) - at Object.useMemo (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:26216:18) - at process.env.NODE_ENV.exports.useMemo (D:\dcprint\erp-project\node_modules\.pnpm\react@19.2.3\node_modules\react\cjs\react.development.js:1251:34) - at IntlProvider (file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/react.js:38:17) - at Object.react_stack_bottom_frame (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:25904:20) - at renderWithHooks (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:7662:22) - at updateFunctionComponent (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:10166:19) { - code: 'INVALID_KEY', - originalMessage: 'Namespace keys cannot contain the character "." as this is used to express nesting. Please remove it or replace it with another character.\n' + - '\n' + - 'Invalid keys: status.pending (at Production), status.confirmed (at Production), status.producing (at Production), status.completed (at Production), status.cancelled (at Production), status.label (at Production), priority.urgent (at Production), priority.high (at Production), priority.normal (at Production), priority.low (at Production), priority.label (at Production), units.sheet (at Production), units.roll (at Production), units.piece (at Production)\n' + - '\n' + - "If you're migrating from a flat structure, you can convert your messages as follows:\n" + - '\n' + - 'import {set} from "lodash";\n' + - '\n' + - 'const input = {\n' + - ' "one.one": "1.1",\n' + - ' "one.two": "1.2",\n' + - ' "two.one.one": "2.1.1"\n' + - '};\n' + - '\n' + - 'const output = Object.entries(input).reduce(\n' + - ' (acc, [key, value]) => set(acc, key, value),\n' + - ' {}\n' + - ');\n' + - '\n' + - '// Output:\n' + - '//\n' + - '// {\n' + - '// "one": {\n' + - '// "one": "1.1",\n' + - '// "two": "1.2"\n' + - '// },\n' + - '// "two": {\n' + - '// "one": {\n' + - '// "one": "2.1.1"\n' + - '// }\n' + - '// }\n' + - '// }\n' -} -Warning: Missing `Description` or `aria-describedby={undefined}` for {DialogContent}. - - ✓ src/components/printing/__tests__/LabelPrintPreview.test.tsx > LabelPrintPreview - 统计文本 totalLabelsCount > 多个标签 + copies 显示正确总数 544ms -stdout | tests/concurrency/stocktaking-approve.test.ts > 盘点审批并发测试 > 并发创建和审批盘点单 -已创建 5 个盘点单 - -步骤2: 并发审批盘点单... - -stderr | src/components/qr-code/__tests__/QRCodeGenerator.test.tsx > QRCodeGenerator - 核心逻辑分支 > fetch 抛出异常时显示 operationFailed toast -IntlError: INVALID_KEY: Namespace keys cannot contain the character "." as this is used to express nesting. Please remove it or replace it with another character. - -Invalid keys: status.pending (at Production), status.confirmed (at Production), status.producing (at Production), status.completed (at Production), status.cancelled (at Production), status.label (at Production), priority.urgent (at Production), priority.high (at Production), priority.normal (at Production), priority.low (at Production), priority.label (at Production), units.sheet (at Production), units.roll (at Production), units.piece (at Production) - -If you're migrating from a flat structure, you can convert your messages as follows: - -import {set} from "lodash"; - -const input = { - "one.one": "1.1", - "one.two": "1.2", - "two.one.one": "2.1.1" -}; - -const output = Object.entries(input).reduce( - (acc, [key, value]) => set(acc, key, value), - {} -); - -// Output: -// -// { -// "one": { -// "one": "1.1", -// "two": "1.2" -// }, -// "two": { -// "one": { -// "one": "2.1.1" -// } -// } -// } - - at validateMessages (file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/initializeConfig-CUsOI8u2.js:465:13) - at initializeConfig (file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/initializeConfig-CUsOI8u2.js:515:7) - at file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/react.js:39:8 - at mountMemo (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:8777:23) - at Object.useMemo (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:26216:18) - at process.env.NODE_ENV.exports.useMemo (D:\dcprint\erp-project\node_modules\.pnpm\react@19.2.3\node_modules\react\cjs\react.development.js:1251:34) - at IntlProvider (file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/react.js:38:17) - at Object.react_stack_bottom_frame (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:25904:20) - at renderWithHooks (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:7662:22) - at updateFunctionComponent (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:10166:19) { - code: 'INVALID_KEY', - originalMessage: 'Namespace keys cannot contain the character "." as this is used to express nesting. Please remove it or replace it with another character.\n' + - '\n' + - 'Invalid keys: status.pending (at Production), status.confirmed (at Production), status.producing (at Production), status.completed (at Production), status.cancelled (at Production), status.label (at Production), priority.urgent (at Production), priority.high (at Production), priority.normal (at Production), priority.low (at Production), priority.label (at Production), units.sheet (at Production), units.roll (at Production), units.piece (at Production)\n' + - '\n' + - "If you're migrating from a flat structure, you can convert your messages as follows:\n" + - '\n' + - 'import {set} from "lodash";\n' + - '\n' + - 'const input = {\n' + - ' "one.one": "1.1",\n' + - ' "one.two": "1.2",\n' + - ' "two.one.one": "2.1.1"\n' + - '};\n' + - '\n' + - 'const output = Object.entries(input).reduce(\n' + - ' (acc, [key, value]) => set(acc, key, value),\n' + - ' {}\n' + - ');\n' + - '\n' + - '// Output:\n' + - '//\n' + - '// {\n' + - '// "one": {\n' + - '// "one": "1.1",\n' + - '// "two": "1.2"\n' + - '// },\n' + - '// "two": {\n' + - '// "one": {\n' + - '// "one": "2.1.1"\n' + - '// }\n' + - '// }\n' + - '// }\n' -} -Warning: Missing `Description` or `aria-describedby={undefined}` for {DialogContent}. - -stderr | src/components/printing/__tests__/LabelPrintPreview.test.tsx > LabelPrintPreview - 交互与异常 > 缺数据字段时标签内不渲染对应行 -IntlError: INVALID_KEY: Namespace keys cannot contain the character "." as this is used to express nesting. Please remove it or replace it with another character. - -Invalid keys: status.pending (at Production), status.confirmed (at Production), status.producing (at Production), status.completed (at Production), status.cancelled (at Production), status.label (at Production), priority.urgent (at Production), priority.high (at Production), priority.normal (at Production), priority.low (at Production), priority.label (at Production), units.sheet (at Production), units.roll (at Production), units.piece (at Production) - -If you're migrating from a flat structure, you can convert your messages as follows: - -import {set} from "lodash"; - -const input = { - "one.one": "1.1", - "one.two": "1.2", - "two.one.one": "2.1.1" -}; - -const output = Object.entries(input).reduce( - (acc, [key, value]) => set(acc, key, value), - {} -); - -// Output: -// -// { -// "one": { -// "one": "1.1", -// "two": "1.2" -// }, -// "two": { -// "one": { -// "one": "2.1.1" -// } -// } -// } - - at validateMessages (file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/initializeConfig-CUsOI8u2.js:465:13) - at initializeConfig (file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/initializeConfig-CUsOI8u2.js:515:7) - at file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/react.js:39:8 - at mountMemo (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:8777:23) - at Object.useMemo (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:26216:18) - at process.env.NODE_ENV.exports.useMemo (D:\dcprint\erp-project\node_modules\.pnpm\react@19.2.3\node_modules\react\cjs\react.development.js:1251:34) - at IntlProvider (file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/react.js:38:17) - at Object.react_stack_bottom_frame (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:25904:20) - at renderWithHooks (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:7662:22) - at updateFunctionComponent (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:10166:19) { - code: 'INVALID_KEY', - originalMessage: 'Namespace keys cannot contain the character "." as this is used to express nesting. Please remove it or replace it with another character.\n' + - '\n' + - 'Invalid keys: status.pending (at Production), status.confirmed (at Production), status.producing (at Production), status.completed (at Production), status.cancelled (at Production), status.label (at Production), priority.urgent (at Production), priority.high (at Production), priority.normal (at Production), priority.low (at Production), priority.label (at Production), units.sheet (at Production), units.roll (at Production), units.piece (at Production)\n' + - '\n' + - "If you're migrating from a flat structure, you can convert your messages as follows:\n" + - '\n' + - 'import {set} from "lodash";\n' + - '\n' + - 'const input = {\n' + - ' "one.one": "1.1",\n' + - ' "one.two": "1.2",\n' + - ' "two.one.one": "2.1.1"\n' + - '};\n' + - '\n' + - 'const output = Object.entries(input).reduce(\n' + - ' (acc, [key, value]) => set(acc, key, value),\n' + - ' {}\n' + - ');\n' + - '\n' + - '// Output:\n' + - '//\n' + - '// {\n' + - '// "one": {\n' + - '// "one": "1.1",\n' + - '// "two": "1.2"\n' + - '// },\n' + - '// "two": {\n' + - '// "one": {\n' + - '// "one": "2.1.1"\n' + - '// }\n' + - '// }\n' + - '// }\n' -} -Warning: Missing `Description` or `aria-describedby={undefined}` for {DialogContent}. - - ✓ src/components/printing/__tests__/LabelPrintPreview.test.tsx > LabelPrintPreview - 交互与异常 > 关闭按钮触发 onClose 回调 220ms - ✓ src/components/qr-code/__tests__/QRCodeGenerator.test.tsx > QRCodeGenerator - 核心逻辑分支 > 生成接口返回失败时显示 generateFailed toast 523ms -stdout | tests/concurrency/stocktaking-approve.test.ts > 盘点审批并发测试 > 并发创建和审批盘点单 - -============================================================ -测试报告: 盘点审批并发测试 -============================================================ -执行时间: 2026-08-27T03:31:24.752Z -总请求数: 5 -成功数量: 5 -失败数量: 0 -成功率: 100.00% -失败率: 0.00% -平均响应时间: 176.00ms -最小响应时间: 44ms -最大响应时间: 236ms -总耗时: 240ms -============================================================ - -测试报告已保存: D:\dcprint\erp-project\tests\concurrency\reports\stocktaking-approve-1787801484754.json - -步骤3: 验证数据一致性... - -stdout | tests/concurrency/stocktaking-approve.test.ts > 盘点审批并发测试 > 并发创建和审批盘点单 -初始库存: 500 -成功审批数量: 5 -最终库存: 489 - -stdout | tests/concurrency/stocktaking-approve.test.ts > 盘点审批并发测试 > 并发创建和审批盘点单 -库存不为负验证: 通过 - - ✓ tests/concurrency/stocktaking-approve.test.ts > 盘点审批并发测试 > 并发创建和审批盘点单 329ms -stdout | tests/concurrency/inventory-outbound.test.ts > 库存出库并发测试 > 并发创建出库单并确认出库 - -============================================================ -测试报告: 出库并发测试 -============================================================ -执行时间: 2026-08-27T03:31:24.772Z -总请求数: 10 -成功数量: 10 -失败数量: 0 -成功率: 100.00% -失败率: 0.00% -平均响应时间: 217.40ms -最小响应时间: 133ms -最大响应时间: 298ms -总耗时: 307ms -============================================================ - -测试报告已保存: D:\dcprint\erp-project\tests\concurrency\reports\outbound-concurrent-1787801484774.json - -步骤3: 验证数据一致性... - -stdout | tests/concurrency/inventory-outbound.test.ts > 库存出库并发测试 > 并发创建出库单并确认出库 -初始库存: 1000 -成功出库数量: 10 -每单出库数量: 50 -预期剩余库存: 500 -实际剩余库存: 500 - -stdout | tests/concurrency/stocktaking-approve.test.ts > 盘点审批并发测试 > 测试盘点产生负库存的保护 - -测试盘点产生负库存场景: 初始库存=5, 并发请求数=3 - -stdout | tests/concurrency/inventory-outbound.test.ts > 库存出库并发测试 > 并发创建出库单并确认出库 -库存不为负验证: 通过 - -stdout | tests/concurrency/inventory-outbound.test.ts > 库存出库并发测试 > 并发创建出库单并确认出库 -库存一致性验证: 通过 - - ✓ tests/concurrency/inventory-outbound.test.ts > 库存出库并发测试 > 并发创建出库单并确认出库 467ms -stderr | src/components/printing/__tests__/LabelPrintPreview.test.tsx > LabelPrintPreview - 交互与异常 > 点击打印按钮调用 onPrintClick(不抛错即视为通过) -IntlError: INVALID_KEY: Namespace keys cannot contain the character "." as this is used to express nesting. Please remove it or replace it with another character. - -Invalid keys: status.pending (at Production), status.confirmed (at Production), status.producing (at Production), status.completed (at Production), status.cancelled (at Production), status.label (at Production), priority.urgent (at Production), priority.high (at Production), priority.normal (at Production), priority.low (at Production), priority.label (at Production), units.sheet (at Production), units.roll (at Production), units.piece (at Production) - -If you're migrating from a flat structure, you can convert your messages as follows: - -import {set} from "lodash"; - -const input = { - "one.one": "1.1", - "one.two": "1.2", - "two.one.one": "2.1.1" -}; - -const output = Object.entries(input).reduce( - (acc, [key, value]) => set(acc, key, value), - {} -); - -// Output: -// -// { -// "one": { -// "one": "1.1", -// "two": "1.2" -// }, -// "two": { -// "one": { -// "one": "2.1.1" -// } -// } -// } - - at validateMessages (file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/initializeConfig-CUsOI8u2.js:465:13) - at initializeConfig (file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/initializeConfig-CUsOI8u2.js:515:7) - at file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/react.js:39:8 - at mountMemo (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:8777:23) - at Object.useMemo (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:26216:18) - at process.env.NODE_ENV.exports.useMemo (D:\dcprint\erp-project\node_modules\.pnpm\react@19.2.3\node_modules\react\cjs\react.development.js:1251:34) - at IntlProvider (file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/react.js:38:17) - at Object.react_stack_bottom_frame (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:25904:20) - at renderWithHooks (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:7662:22) - at updateFunctionComponent (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:10166:19) { - code: 'INVALID_KEY', - originalMessage: 'Namespace keys cannot contain the character "." as this is used to express nesting. Please remove it or replace it with another character.\n' + - '\n' + - 'Invalid keys: status.pending (at Production), status.confirmed (at Production), status.producing (at Production), status.completed (at Production), status.cancelled (at Production), status.label (at Production), priority.urgent (at Production), priority.high (at Production), priority.normal (at Production), priority.low (at Production), priority.label (at Production), units.sheet (at Production), units.roll (at Production), units.piece (at Production)\n' + - '\n' + - "If you're migrating from a flat structure, you can convert your messages as follows:\n" + - '\n' + - 'import {set} from "lodash";\n' + - '\n' + - 'const input = {\n' + - ' "one.one": "1.1",\n' + - ' "one.two": "1.2",\n' + - ' "two.one.one": "2.1.1"\n' + - '};\n' + - '\n' + - 'const output = Object.entries(input).reduce(\n' + - ' (acc, [key, value]) => set(acc, key, value),\n' + - ' {}\n' + - ');\n' + - '\n' + - '// Output:\n' + - '//\n' + - '// {\n' + - '// "one": {\n' + - '// "one": "1.1",\n' + - '// "two": "1.2"\n' + - '// },\n' + - '// "two": {\n' + - '// "one": {\n' + - '// "one": "2.1.1"\n' + - '// }\n' + - '// }\n' + - '// }\n' -} -Warning: Missing `Description` or `aria-describedby={undefined}` for {DialogContent}. - - ✓ src/components/printing/__tests__/LabelPrintPreview.test.tsx > LabelPrintPreview - 交互与异常 > 缺数据字段时标签内不渲染对应行 99ms -stdout | tests/concurrency/inventory-outbound.test.ts > 库存出库并发测试 > 测试库存不足时的并发处理 - -测试库存不足场景: 初始库存=100, 并发请求数=5, 每单数量=30 - -stdout | tests/concurrency/stocktaking-approve.test.ts > 盘点审批并发测试 > 测试盘点产生负库存的保护 - -============================================================ -测试报告: 盘点负库存保护测试 -============================================================ -执行时间: 2026-08-27T03:31:24.815Z -总请求数: 3 -成功数量: 0 -失败数量: 3 -成功率: 0.00% -失败率: 100.00% -平均响应时间: 25.00ms -最小响应时间: 18ms -最大响应时间: 33ms -总耗时: 33ms - -错误统计: - - 盘点后将产生负库存: 当前 5, 差异 -10: 3 次 -============================================================ - - -stdout | tests/concurrency/stocktaking-approve.test.ts > 盘点审批并发测试 > 测试盘点产生负库存的保护 - -结果分析: -- 成功数量: 0 -- 失败数量: 3 -- 最终库存: 5 -- 库存不为负: 是 - - ✓ src/components/printing/__tests__/LabelPrintPreview.test.tsx > LabelPrintPreview - 交互与异常 > 点击打印按钮调用 onPrintClick(不抛错即视为通过) 185ms -stdout | tests/concurrency/inventory-outbound.test.ts > 库存出库并发测试 > 测试库存不足时的并发处理 - -============================================================ -测试报告: 库存不足并发测试 -============================================================ -执行时间: 2026-08-27T03:31:24.841Z -总请求数: 5 -成功数量: 0 -失败数量: 5 -成功率: 0.00% -失败率: 100.00% -平均响应时间: 35.20ms -最小响应时间: 35ms -最大响应时间: 36ms -总耗时: 36ms - -错误统计: - - Unknown column 'qty' in 'field list': 5 次 -============================================================ - - -stdout | tests/concurrency/inventory-outbound.test.ts > 库存出库并发测试 > 测试库存不足时的并发处理 - -结果分析: -- 成功数量: 0 -- 失败数量: 5 -- 最终库存: 100 -- 库存不为负: 是 - -stdout | tests/concurrency/stocktaking-approve.test.ts > 盘点审批并发测试 -清理测试环境... - - ✓ tests/concurrency/stocktaking-approve.test.ts > 盘点审批并发测试 > 测试盘点产生负库存的保护 82ms -stdout | tests/concurrency/inventory-outbound.test.ts > 库存出库并发测试 -清理测试环境... - -stdout | tests/concurrency/stocktaking-approve.test.ts > 盘点审批并发测试 -测试环境清理完成 - - ✓ tests/concurrency/inventory-outbound.test.ts > 库存出库并发测试 > 测试库存不足时的并发处理 80ms -stdout | tests/concurrency/inventory-outbound.test.ts > 库存出库并发测试 -测试环境清理完成 - -stderr | src/components/qr-code/__tests__/QRCodeGenerator.test.tsx > QRCodeGenerator - 核心逻辑分支 > [en] 失败分支文案随语言切换为英文 -IntlError: INVALID_KEY: Namespace keys cannot contain the character "." as this is used to express nesting. Please remove it or replace it with another character. - -Invalid keys: status.pending (at Production), status.confirmed (at Production), status.producing (at Production), status.completed (at Production), status.cancelled (at Production), status.label (at Production), priority.urgent (at Production), priority.high (at Production), priority.normal (at Production), priority.low (at Production), priority.label (at Production), units.sheet (at Production), units.roll (at Production), units.piece (at Production) - -If you're migrating from a flat structure, you can convert your messages as follows: - -import {set} from "lodash"; - -const input = { - "one.one": "1.1", - "one.two": "1.2", - "two.one.one": "2.1.1" -}; - -const output = Object.entries(input).reduce( - (acc, [key, value]) => set(acc, key, value), - {} -); - -// Output: -// -// { -// "one": { -// "one": "1.1", -// "two": "1.2" -// }, -// "two": { -// "one": { -// "one": "2.1.1" -// } -// } -// } - - at validateMessages (file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/initializeConfig-CUsOI8u2.js:465:13) - at initializeConfig (file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/initializeConfig-CUsOI8u2.js:515:7) - at file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/react.js:39:8 - at mountMemo (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:8777:23) - at Object.useMemo (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:26216:18) - at process.env.NODE_ENV.exports.useMemo (D:\dcprint\erp-project\node_modules\.pnpm\react@19.2.3\node_modules\react\cjs\react.development.js:1251:34) - at IntlProvider (file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/react.js:38:17) - at Object.react_stack_bottom_frame (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:25904:20) - at renderWithHooks (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:7662:22) - at updateFunctionComponent (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:10166:19) { - code: 'INVALID_KEY', - originalMessage: 'Namespace keys cannot contain the character "." as this is used to express nesting. Please remove it or replace it with another character.\n' + - '\n' + - 'Invalid keys: status.pending (at Production), status.confirmed (at Production), status.producing (at Production), status.completed (at Production), status.cancelled (at Production), status.label (at Production), priority.urgent (at Production), priority.high (at Production), priority.normal (at Production), priority.low (at Production), priority.label (at Production), units.sheet (at Production), units.roll (at Production), units.piece (at Production)\n' + - '\n' + - "If you're migrating from a flat structure, you can convert your messages as follows:\n" + - '\n' + - 'import {set} from "lodash";\n' + - '\n' + - 'const input = {\n' + - ' "one.one": "1.1",\n' + - ' "one.two": "1.2",\n' + - ' "two.one.one": "2.1.1"\n' + - '};\n' + - '\n' + - 'const output = Object.entries(input).reduce(\n' + - ' (acc, [key, value]) => set(acc, key, value),\n' + - ' {}\n' + - ');\n' + - '\n' + - '// Output:\n' + - '//\n' + - '// {\n' + - '// "one": {\n' + - '// "one": "1.1",\n' + - '// "two": "1.2"\n' + - '// },\n' + - '// "two": {\n' + - '// "one": {\n' + - '// "one": "2.1.1"\n' + - '// }\n' + - '// }\n' + - '// }\n' -} -Warning: Missing `Description` or `aria-describedby={undefined}` for {DialogContent}. - - ✓ src/components/qr-code/__tests__/QRCodeGenerator.test.tsx > QRCodeGenerator - 核心逻辑分支 > fetch 抛出异常时显示 operationFailed toast 429ms - ✓ src/components/qr-code/__tests__/QRCodeGenerator.test.tsx > QRCodeGenerator - 核心逻辑分支 > [en] 失败分支文案随语言切换为英文 350ms -stdout | tests/concurrency/material-issue.test.ts > 领料并发测试 -准备测试环境... - -stdout | tests/concurrency/material-issue.test.ts > 领料并发测试 -测试仓库: 测试仓库_1787801485589_8fd10a (ID: 215) -测试物料: 测试物料_1787801485689_kpgm8 (ID: 99048, 初始库存: 800) - -stdout | tests/concurrency/material-issue.test.ts > 领料并发测试 > 并发创建和执行领料 - -步骤1: 创建 8 个领料单... - -stdout | tests/concurrency/material-issue.test.ts > 领料并发测试 > 并发创建和执行领料 -已创建 8 个领料单 - -步骤2: 并发执行领料... - -stdout | tests/concurrency/purchase-inbound.test.ts > 采购入库并发测试 -准备测试环境... - -stdout | tests/concurrency/purchase-inbound.test.ts > 采购入库并发测试 -测试仓库: 测试仓库_1787801485930_rslr0a (ID: 216) -测试物料: 测试物料_1787801486035_b2h9fk (ID: 99049, 初始库存: 0) - -stdout | tests/concurrency/purchase-inbound.test.ts > 采购入库并发测试 > 并发创建采购入库单并审核 - -步骤1: 创建 6 个入库单... - -stdout | tests/concurrency/purchase-inbound.test.ts > 采购入库并发测试 > 并发创建采购入库单并审核 -已创建 6 个入库单 - -步骤2: 并发审核入库单... - -stdout | tests/concurrency/material-issue.test.ts > 领料并发测试 > 并发创建和执行领料 - -============================================================ -测试报告: 领料并发测试 -============================================================ -执行时间: 2026-08-27T03:31:26.356Z -总请求数: 8 -成功数量: 8 -失败数量: 0 -成功率: 100.00% -失败率: 0.00% -平均响应时间: 357.00ms -最小响应时间: 289ms -最大响应时间: 429ms -总耗时: 433ms -============================================================ - -测试报告已保存: D:\dcprint\erp-project\tests\concurrency\reports\material-issue-1787801486357.json - -步骤3: 验证数据一致性... - -stdout | tests/concurrency/material-issue.test.ts > 领料并发测试 > 并发创建和执行领料 -初始库存: 800 -成功领料数量: 8 -每单领料数量: 60 -预期剩余库存: 320 -实际剩余库存: 320 - -stdout | tests/concurrency/material-issue.test.ts > 领料并发测试 > 并发创建和执行领料 -库存不为负验证: 通过 - - ✓ tests/concurrency/material-issue.test.ts > 领料并发测试 > 并发创建和执行领料 634ms -stdout | tests/concurrency/material-issue.test.ts > 领料并发测试 > 测试领料库存不足时的并发处理 - -测试领料库存不足场景: 初始库存=150, 并发请求数=5, 每单数量=40 - -stdout | tests/concurrency/material-issue.test.ts > 领料并发测试 > 测试领料库存不足时的并发处理 - -============================================================ -测试报告: 领料库存不足并发测试 -============================================================ -执行时间: 2026-08-27T03:31:26.419Z -总请求数: 5 -成功数量: 0 -失败数量: 5 -成功率: 0.00% -失败率: 100.00% -平均响应时间: 24.00ms -最小响应时间: 24ms -最大响应时间: 24ms -总耗时: 24ms - -错误统计: - - Field 'work_order_no' doesn't have a default value: 5 次 -============================================================ - - -stdout | tests/concurrency/material-issue.test.ts > 领料并发测试 > 测试领料库存不足时的并发处理 - -结果分析: -- 成功数量: 0 -- 失败数量: 5 -- 最终库存: 150 -- 库存不为负: 是 - -stdout | tests/concurrency/material-issue.test.ts > 领料并发测试 -清理测试环境... - - ✓ tests/concurrency/material-issue.test.ts > 领料并发测试 > 测试领料库存不足时的并发处理 79ms -stdout | tests/concurrency/material-issue.test.ts > 领料并发测试 -测试环境清理完成 - -stdout | tests/concurrency/purchase-inbound.test.ts > 采购入库并发测试 > 并发创建采购入库单并审核 - -============================================================ -测试报告: 采购入库并发测试 -============================================================ -执行时间: 2026-08-27T03:31:26.502Z -总请求数: 6 -成功数量: 6 -失败数量: 0 -成功率: 100.00% -失败率: 0.00% -平均响应时间: 203.17ms -最小响应时间: 53ms -最大响应时间: 313ms -总耗时: 315ms -============================================================ - -测试报告已保存: D:\dcprint\erp-project\tests\concurrency\reports\purchase-inbound-1787801486504.json - -步骤3: 验证数据一致性... - -stdout | tests/concurrency/purchase-inbound.test.ts > 采购入库并发测试 > 并发创建采购入库单并审核 -初始库存: 0 -成功入库数量: 6 -每单入库数量: 100 -预期库存: 600 -实际库存: 600 - -stdout | tests/concurrency/purchase-inbound.test.ts > 采购入库并发测试 > 并发创建采购入库单并审核 -库存不为负验证: 通过 - - ✓ tests/concurrency/purchase-inbound.test.ts > 采购入库并发测试 > 并发创建采购入库单并审核 426ms -stdout | tests/concurrency/purchase-inbound.test.ts > 采购入库并发测试 > 测试超收校验并发 - -测试超收校验场景: 采购订单数量=100, 入库数量=120, 并发数=4 - -stdout | tests/concurrency/purchase-inbound.test.ts > 采购入库并发测试 > 测试超收校验并发 - -============================================================ -测试报告: 超收校验并发测试 -============================================================ -执行时间: 2026-08-27T03:31:26.555Z -总请求数: 4 -成功数量: 0 -失败数量: 4 -成功率: 0.00% -失败率: 100.00% -平均响应时间: 21.50ms -最小响应时间: 21ms -最大响应时间: 22ms -总耗时: 23ms - -错误统计: - - Table 'vnerpdacahng.po_purchase_order' doesn't exist: 4 次 -============================================================ - - -stdout | tests/concurrency/purchase-inbound.test.ts > 采购入库并发测试 > 测试超收校验并发 - -结果分析: -- 成功数量: 0 -- 失败数量: 4 -- 最终库存: 0 -- 库存不为负: 是 - -stdout | tests/concurrency/purchase-inbound.test.ts > 采购入库并发测试 -清理测试环境... - -stdout | tests/concurrency/purchase-inbound.test.ts > 采购入库并发测试 -测试环境清理完成 - - ✓ tests/concurrency/purchase-inbound.test.ts > 采购入库并发测试 > 测试超收校验并发 62ms -stderr | src/components/qr-code/__tests__/QRCodePrinter.test.tsx > QRCodePrinter - 多语言按钮文本 > [zh-CN] icon=both 显示预览和打印两个按钮(对应语言文案) -IntlError: INVALID_KEY: Namespace keys cannot contain the character "." as this is used to express nesting. Please remove it or replace it with another character. - -Invalid keys: status.pending (at Production), status.confirmed (at Production), status.producing (at Production), status.completed (at Production), status.cancelled (at Production), status.label (at Production), priority.urgent (at Production), priority.high (at Production), priority.normal (at Production), priority.low (at Production), priority.label (at Production), units.sheet (at Production), units.roll (at Production), units.piece (at Production) - -If you're migrating from a flat structure, you can convert your messages as follows: - -import {set} from "lodash"; - -const input = { - "one.one": "1.1", - "one.two": "1.2", - "two.one.one": "2.1.1" -}; - -const output = Object.entries(input).reduce( - (acc, [key, value]) => set(acc, key, value), - {} -); - -// Output: -// -// { -// "one": { -// "one": "1.1", -// "two": "1.2" -// }, -// "two": { -// "one": { -// "one": "2.1.1" -// } -// } -// } - - at validateMessages (file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/initializeConfig-CUsOI8u2.js:465:13) - at initializeConfig (file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/initializeConfig-CUsOI8u2.js:515:7) - at file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/react.js:39:8 - at mountMemo (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:8777:23) - at Object.useMemo (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:26216:18) - at process.env.NODE_ENV.exports.useMemo (D:\dcprint\erp-project\node_modules\.pnpm\react@19.2.3\node_modules\react\cjs\react.development.js:1251:34) - at IntlProvider (file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/react.js:38:17) - at Object.react_stack_bottom_frame (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:25904:20) - at renderWithHooks (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:7662:22) - at updateFunctionComponent (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:10166:19) { - code: 'INVALID_KEY', - originalMessage: 'Namespace keys cannot contain the character "." as this is used to express nesting. Please remove it or replace it with another character.\n' + - '\n' + - 'Invalid keys: status.pending (at Production), status.confirmed (at Production), status.producing (at Production), status.completed (at Production), status.cancelled (at Production), status.label (at Production), priority.urgent (at Production), priority.high (at Production), priority.normal (at Production), priority.low (at Production), priority.label (at Production), units.sheet (at Production), units.roll (at Production), units.piece (at Production)\n' + - '\n' + - "If you're migrating from a flat structure, you can convert your messages as follows:\n" + - '\n' + - 'import {set} from "lodash";\n' + - '\n' + - 'const input = {\n' + - ' "one.one": "1.1",\n' + - ' "one.two": "1.2",\n' + - ' "two.one.one": "2.1.1"\n' + - '};\n' + - '\n' + - 'const output = Object.entries(input).reduce(\n' + - ' (acc, [key, value]) => set(acc, key, value),\n' + - ' {}\n' + - ');\n' + - '\n' + - '// Output:\n' + - '//\n' + - '// {\n' + - '// "one": {\n' + - '// "one": "1.1",\n' + - '// "two": "1.2"\n' + - '// },\n' + - '// "two": {\n' + - '// "one": {\n' + - '// "one": "2.1.1"\n' + - '// }\n' + - '// }\n' + - '// }\n' -} - -stderr | src/components/qr-code/__tests__/QRCodePrinter.test.tsx > QRCodePrinter - 多语言按钮文本 > [en] icon=both 显示预览和打印两个按钮(对应语言文案) -IntlError: INVALID_KEY: Namespace keys cannot contain the character "." as this is used to express nesting. Please remove it or replace it with another character. - -Invalid keys: status.pending (at Production), status.confirmed (at Production), status.producing (at Production), status.completed (at Production), status.cancelled (at Production), status.label (at Production), priority.urgent (at Production), priority.high (at Production), priority.normal (at Production), priority.low (at Production), priority.label (at Production), units.sheet (at Production), units.roll (at Production), units.piece (at Production) - -If you're migrating from a flat structure, you can convert your messages as follows: - -import {set} from "lodash"; - -const input = { - "one.one": "1.1", - "one.two": "1.2", - "two.one.one": "2.1.1" -}; - -const output = Object.entries(input).reduce( - (acc, [key, value]) => set(acc, key, value), - {} -); - -// Output: -// -// { -// "one": { -// "one": "1.1", -// "two": "1.2" -// }, -// "two": { -// "one": { -// "one": "2.1.1" -// } -// } -// } - - at validateMessages (file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/initializeConfig-CUsOI8u2.js:465:13) - at initializeConfig (file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/initializeConfig-CUsOI8u2.js:515:7) - at file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/react.js:39:8 - at mountMemo (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:8777:23) - at Object.useMemo (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:26216:18) - at process.env.NODE_ENV.exports.useMemo (D:\dcprint\erp-project\node_modules\.pnpm\react@19.2.3\node_modules\react\cjs\react.development.js:1251:34) - at IntlProvider (file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/react.js:38:17) - at Object.react_stack_bottom_frame (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:25904:20) - at renderWithHooks (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:7662:22) - at updateFunctionComponent (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:10166:19) { - code: 'INVALID_KEY', - originalMessage: 'Namespace keys cannot contain the character "." as this is used to express nesting. Please remove it or replace it with another character.\n' + - '\n' + - 'Invalid keys: status.pending (at Production), status.confirmed (at Production), status.producing (at Production), status.completed (at Production), status.cancelled (at Production), status.label (at Production), priority.urgent (at Production), priority.high (at Production), priority.normal (at Production), priority.low (at Production), priority.label (at Production), units.sheet (at Production), units.roll (at Production), units.piece (at Production)\n' + - '\n' + - "If you're migrating from a flat structure, you can convert your messages as follows:\n" + - '\n' + - 'import {set} from "lodash";\n' + - '\n' + - 'const input = {\n' + - ' "one.one": "1.1",\n' + - ' "one.two": "1.2",\n' + - ' "two.one.one": "2.1.1"\n' + - '};\n' + - '\n' + - 'const output = Object.entries(input).reduce(\n' + - ' (acc, [key, value]) => set(acc, key, value),\n' + - ' {}\n' + - ');\n' + - '\n' + - '// Output:\n' + - '//\n' + - '// {\n' + - '// "one": {\n' + - '// "one": "1.1",\n' + - '// "two": "1.2"\n' + - '// },\n' + - '// "two": {\n' + - '// "one": {\n' + - '// "one": "2.1.1"\n' + - '// }\n' + - '// }\n' + - '// }\n' -} - -stderr | src/components/qr-code/__tests__/QRCodePrinter.test.tsx > QRCodePrinter - 多语言按钮文本 > [zh-TW] icon=both 显示预览和打印两个按钮(对应语言文案) -IntlError: INVALID_KEY: Namespace keys cannot contain the character "." as this is used to express nesting. Please remove it or replace it with another character. - -Invalid keys: status.pending (at Production), status.confirmed (at Production), status.producing (at Production), status.completed (at Production), status.cancelled (at Production), status.label (at Production), priority.urgent (at Production), priority.high (at Production), priority.normal (at Production), priority.low (at Production), priority.label (at Production), units.sheet (at Production), units.roll (at Production), units.piece (at Production) - -If you're migrating from a flat structure, you can convert your messages as follows: - -import {set} from "lodash"; - -const input = { - "one.one": "1.1", - "one.two": "1.2", - "two.one.one": "2.1.1" -}; - -const output = Object.entries(input).reduce( - (acc, [key, value]) => set(acc, key, value), - {} -); - -// Output: -// -// { -// "one": { -// "one": "1.1", -// "two": "1.2" -// }, -// "two": { -// "one": { -// "one": "2.1.1" -// } -// } -// } - - at validateMessages (file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/initializeConfig-CUsOI8u2.js:465:13) - at initializeConfig (file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/initializeConfig-CUsOI8u2.js:515:7) - at file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/react.js:39:8 - at mountMemo (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:8777:23) - at Object.useMemo (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:26216:18) - at process.env.NODE_ENV.exports.useMemo (D:\dcprint\erp-project\node_modules\.pnpm\react@19.2.3\node_modules\react\cjs\react.development.js:1251:34) - at IntlProvider (file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/react.js:38:17) - at Object.react_stack_bottom_frame (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:25904:20) - at renderWithHooks (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:7662:22) - at updateFunctionComponent (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:10166:19) { - code: 'INVALID_KEY', - originalMessage: 'Namespace keys cannot contain the character "." as this is used to express nesting. Please remove it or replace it with another character.\n' + - '\n' + - 'Invalid keys: status.pending (at Production), status.confirmed (at Production), status.producing (at Production), status.completed (at Production), status.cancelled (at Production), status.label (at Production), priority.urgent (at Production), priority.high (at Production), priority.normal (at Production), priority.low (at Production), priority.label (at Production), units.sheet (at Production), units.roll (at Production), units.piece (at Production)\n' + - '\n' + - "If you're migrating from a flat structure, you can convert your messages as follows:\n" + - '\n' + - 'import {set} from "lodash";\n' + - '\n' + - 'const input = {\n' + - ' "one.one": "1.1",\n' + - ' "one.two": "1.2",\n' + - ' "two.one.one": "2.1.1"\n' + - '};\n' + - '\n' + - 'const output = Object.entries(input).reduce(\n' + - ' (acc, [key, value]) => set(acc, key, value),\n' + - ' {}\n' + - ');\n' + - '\n' + - '// Output:\n' + - '//\n' + - '// {\n' + - '// "one": {\n' + - '// "one": "1.1",\n' + - '// "two": "1.2"\n' + - '// },\n' + - '// "two": {\n' + - '// "one": {\n' + - '// "one": "2.1.1"\n' + - '// }\n' + - '// }\n' + - '// }\n' -} - - ✓ src/components/qr-code/__tests__/QRCodePrinter.test.tsx > QRCodePrinter - 多语言按钮文本 > [zh-CN] icon=both 显示预览和打印两个按钮(对应语言文案) 656ms - ✓ src/components/qr-code/__tests__/QRCodePrinter.test.tsx > QRCodePrinter - 多语言按钮文本 > [en] icon=both 显示预览和打印两个按钮(对应语言文案) 55ms -stderr | src/components/qr-code/__tests__/QRCodePrinter.test.tsx > QRCodePrinter - 多语言按钮文本 > [vi] icon=both 显示预览和打印两个按钮(对应语言文案) -IntlError: INVALID_KEY: Namespace keys cannot contain the character "." as this is used to express nesting. Please remove it or replace it with another character. - -Invalid keys: status.pending (at Production), status.confirmed (at Production), status.producing (at Production), status.completed (at Production), status.cancelled (at Production), status.label (at Production), priority.urgent (at Production), priority.high (at Production), priority.normal (at Production), priority.low (at Production), priority.label (at Production), units.sheet (at Production), units.roll (at Production), units.piece (at Production) - -If you're migrating from a flat structure, you can convert your messages as follows: - -import {set} from "lodash"; - -const input = { - "one.one": "1.1", - "one.two": "1.2", - "two.one.one": "2.1.1" -}; - -const output = Object.entries(input).reduce( - (acc, [key, value]) => set(acc, key, value), - {} -); - -// Output: -// -// { -// "one": { -// "one": "1.1", -// "two": "1.2" -// }, -// "two": { -// "one": { -// "one": "2.1.1" -// } -// } -// } - - at validateMessages (file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/initializeConfig-CUsOI8u2.js:465:13) - at initializeConfig (file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/initializeConfig-CUsOI8u2.js:515:7) - at file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/react.js:39:8 - at mountMemo (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:8777:23) - at Object.useMemo (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:26216:18) - at process.env.NODE_ENV.exports.useMemo (D:\dcprint\erp-project\node_modules\.pnpm\react@19.2.3\node_modules\react\cjs\react.development.js:1251:34) - at IntlProvider (file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/react.js:38:17) - at Object.react_stack_bottom_frame (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:25904:20) - at renderWithHooks (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:7662:22) - at updateFunctionComponent (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:10166:19) { - code: 'INVALID_KEY', - originalMessage: 'Namespace keys cannot contain the character "." as this is used to express nesting. Please remove it or replace it with another character.\n' + - '\n' + - 'Invalid keys: status.pending (at Production), status.confirmed (at Production), status.producing (at Production), status.completed (at Production), status.cancelled (at Production), status.label (at Production), priority.urgent (at Production), priority.high (at Production), priority.normal (at Production), priority.low (at Production), priority.label (at Production), units.sheet (at Production), units.roll (at Production), units.piece (at Production)\n' + - '\n' + - "If you're migrating from a flat structure, you can convert your messages as follows:\n" + - '\n' + - 'import {set} from "lodash";\n' + - '\n' + - 'const input = {\n' + - ' "one.one": "1.1",\n' + - ' "one.two": "1.2",\n' + - ' "two.one.one": "2.1.1"\n' + - '};\n' + - '\n' + - 'const output = Object.entries(input).reduce(\n' + - ' (acc, [key, value]) => set(acc, key, value),\n' + - ' {}\n' + - ');\n' + - '\n' + - '// Output:\n' + - '//\n' + - '// {\n' + - '// "one": {\n' + - '// "one": "1.1",\n' + - '// "two": "1.2"\n' + - '// },\n' + - '// "two": {\n' + - '// "one": {\n' + - '// "one": "2.1.1"\n' + - '// }\n' + - '// }\n' + - '// }\n' -} - -stderr | src/components/qr-code/__tests__/QRCodePrinter.test.tsx > QRCodePrinter - 多语言按钮文本 > icon=print 时只显示打印按钮 -IntlError: INVALID_KEY: Namespace keys cannot contain the character "." as this is used to express nesting. Please remove it or replace it with another character. - -Invalid keys: status.pending (at Production), status.confirmed (at Production), status.producing (at Production), status.completed (at Production), status.cancelled (at Production), status.label (at Production), priority.urgent (at Production), priority.high (at Production), priority.normal (at Production), priority.low (at Production), priority.label (at Production), units.sheet (at Production), units.roll (at Production), units.piece (at Production) - -If you're migrating from a flat structure, you can convert your messages as follows: - -import {set} from "lodash"; - -const input = { - "one.one": "1.1", - "one.two": "1.2", - "two.one.one": "2.1.1" -}; - -const output = Object.entries(input).reduce( - (acc, [key, value]) => set(acc, key, value), - {} -); - -// Output: -// -// { -// "one": { -// "one": "1.1", -// "two": "1.2" -// }, -// "two": { -// "one": { -// "one": "2.1.1" -// } -// } -// } - - at validateMessages (file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/initializeConfig-CUsOI8u2.js:465:13) - at initializeConfig (file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/initializeConfig-CUsOI8u2.js:515:7) - at file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/react.js:39:8 - at mountMemo (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:8777:23) - at Object.useMemo (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:26216:18) - at process.env.NODE_ENV.exports.useMemo (D:\dcprint\erp-project\node_modules\.pnpm\react@19.2.3\node_modules\react\cjs\react.development.js:1251:34) - at IntlProvider (file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/react.js:38:17) - at Object.react_stack_bottom_frame (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:25904:20) - at renderWithHooks (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:7662:22) - at updateFunctionComponent (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:10166:19) { - code: 'INVALID_KEY', - originalMessage: 'Namespace keys cannot contain the character "." as this is used to express nesting. Please remove it or replace it with another character.\n' + - '\n' + - 'Invalid keys: status.pending (at Production), status.confirmed (at Production), status.producing (at Production), status.completed (at Production), status.cancelled (at Production), status.label (at Production), priority.urgent (at Production), priority.high (at Production), priority.normal (at Production), priority.low (at Production), priority.label (at Production), units.sheet (at Production), units.roll (at Production), units.piece (at Production)\n' + - '\n' + - "If you're migrating from a flat structure, you can convert your messages as follows:\n" + - '\n' + - 'import {set} from "lodash";\n' + - '\n' + - 'const input = {\n' + - ' "one.one": "1.1",\n' + - ' "one.two": "1.2",\n' + - ' "two.one.one": "2.1.1"\n' + - '};\n' + - '\n' + - 'const output = Object.entries(input).reduce(\n' + - ' (acc, [key, value]) => set(acc, key, value),\n' + - ' {}\n' + - ');\n' + - '\n' + - '// Output:\n' + - '//\n' + - '// {\n' + - '// "one": {\n' + - '// "one": "1.1",\n' + - '// "two": "1.2"\n' + - '// },\n' + - '// "two": {\n' + - '// "one": {\n' + - '// "one": "2.1.1"\n' + - '// }\n' + - '// }\n' + - '// }\n' -} - -stderr | src/components/qr-code/__tests__/QRCodePrinter.test.tsx > QRCodePrinter - 标签模板多语言渲染 > [zh-CN] 预览对话框标题包含对应语言的模板名称 -IntlError: INVALID_KEY: Namespace keys cannot contain the character "." as this is used to express nesting. Please remove it or replace it with another character. - -Invalid keys: status.pending (at Production), status.confirmed (at Production), status.producing (at Production), status.completed (at Production), status.cancelled (at Production), status.label (at Production), priority.urgent (at Production), priority.high (at Production), priority.normal (at Production), priority.low (at Production), priority.label (at Production), units.sheet (at Production), units.roll (at Production), units.piece (at Production) - -If you're migrating from a flat structure, you can convert your messages as follows: - -import {set} from "lodash"; - -const input = { - "one.one": "1.1", - "one.two": "1.2", - "two.one.one": "2.1.1" -}; - -const output = Object.entries(input).reduce( - (acc, [key, value]) => set(acc, key, value), - {} -); - -// Output: -// -// { -// "one": { -// "one": "1.1", -// "two": "1.2" -// }, -// "two": { -// "one": { -// "one": "2.1.1" -// } -// } -// } - - at validateMessages (file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/initializeConfig-CUsOI8u2.js:465:13) - at initializeConfig (file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/initializeConfig-CUsOI8u2.js:515:7) - at file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/react.js:39:8 - at mountMemo (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:8777:23) - at Object.useMemo (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:26216:18) - at process.env.NODE_ENV.exports.useMemo (D:\dcprint\erp-project\node_modules\.pnpm\react@19.2.3\node_modules\react\cjs\react.development.js:1251:34) - at IntlProvider (file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/react.js:38:17) - at Object.react_stack_bottom_frame (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:25904:20) - at renderWithHooks (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:7662:22) - at updateFunctionComponent (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:10166:19) { - code: 'INVALID_KEY', - originalMessage: 'Namespace keys cannot contain the character "." as this is used to express nesting. Please remove it or replace it with another character.\n' + - '\n' + - 'Invalid keys: status.pending (at Production), status.confirmed (at Production), status.producing (at Production), status.completed (at Production), status.cancelled (at Production), status.label (at Production), priority.urgent (at Production), priority.high (at Production), priority.normal (at Production), priority.low (at Production), priority.label (at Production), units.sheet (at Production), units.roll (at Production), units.piece (at Production)\n' + - '\n' + - "If you're migrating from a flat structure, you can convert your messages as follows:\n" + - '\n' + - 'import {set} from "lodash";\n' + - '\n' + - 'const input = {\n' + - ' "one.one": "1.1",\n' + - ' "one.two": "1.2",\n' + - ' "two.one.one": "2.1.1"\n' + - '};\n' + - '\n' + - 'const output = Object.entries(input).reduce(\n' + - ' (acc, [key, value]) => set(acc, key, value),\n' + - ' {}\n' + - ');\n' + - '\n' + - '// Output:\n' + - '//\n' + - '// {\n' + - '// "one": {\n' + - '// "one": "1.1",\n' + - '// "two": "1.2"\n' + - '// },\n' + - '// "two": {\n' + - '// "one": {\n' + - '// "one": "2.1.1"\n' + - '// }\n' + - '// }\n' + - '// }\n' -} -Warning: Missing `Description` or `aria-describedby={undefined}` for {DialogContent}. - - ✓ src/components/qr-code/__tests__/QRCodePrinter.test.tsx > QRCodePrinter - 多语言按钮文本 > [zh-TW] icon=both 显示预览和打印两个按钮(对应语言文案) 58ms - ✓ src/components/qr-code/__tests__/QRCodePrinter.test.tsx > QRCodePrinter - 多语言按钮文本 > [vi] icon=both 显示预览和打印两个按钮(对应语言文案) 47ms - ✓ src/components/qr-code/__tests__/QRCodePrinter.test.tsx > QRCodePrinter - 多语言按钮文本 > icon=print 时只显示打印按钮 42ms -stderr | src/components/qr-code/__tests__/QRCodePrinter.test.tsx > QRCodePrinter - 标签模板多语言渲染 > [en] 打印配置对话框标题显示英文 -IntlError: INVALID_KEY: Namespace keys cannot contain the character "." as this is used to express nesting. Please remove it or replace it with another character. - -Invalid keys: status.pending (at Production), status.confirmed (at Production), status.producing (at Production), status.completed (at Production), status.cancelled (at Production), status.label (at Production), priority.urgent (at Production), priority.high (at Production), priority.normal (at Production), priority.low (at Production), priority.label (at Production), units.sheet (at Production), units.roll (at Production), units.piece (at Production) - -If you're migrating from a flat structure, you can convert your messages as follows: - -import {set} from "lodash"; - -const input = { - "one.one": "1.1", - "one.two": "1.2", - "two.one.one": "2.1.1" -}; - -const output = Object.entries(input).reduce( - (acc, [key, value]) => set(acc, key, value), - {} -); - -// Output: -// -// { -// "one": { -// "one": "1.1", -// "two": "1.2" -// }, -// "two": { -// "one": { -// "one": "2.1.1" -// } -// } -// } - - at validateMessages (file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/initializeConfig-CUsOI8u2.js:465:13) - at initializeConfig (file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/initializeConfig-CUsOI8u2.js:515:7) - at file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/react.js:39:8 - at mountMemo (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:8777:23) - at Object.useMemo (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:26216:18) - at process.env.NODE_ENV.exports.useMemo (D:\dcprint\erp-project\node_modules\.pnpm\react@19.2.3\node_modules\react\cjs\react.development.js:1251:34) - at IntlProvider (file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/react.js:38:17) - at Object.react_stack_bottom_frame (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:25904:20) - at renderWithHooks (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:7662:22) - at updateFunctionComponent (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:10166:19) { - code: 'INVALID_KEY', - originalMessage: 'Namespace keys cannot contain the character "." as this is used to express nesting. Please remove it or replace it with another character.\n' + - '\n' + - 'Invalid keys: status.pending (at Production), status.confirmed (at Production), status.producing (at Production), status.completed (at Production), status.cancelled (at Production), status.label (at Production), priority.urgent (at Production), priority.high (at Production), priority.normal (at Production), priority.low (at Production), priority.label (at Production), units.sheet (at Production), units.roll (at Production), units.piece (at Production)\n' + - '\n' + - "If you're migrating from a flat structure, you can convert your messages as follows:\n" + - '\n' + - 'import {set} from "lodash";\n' + - '\n' + - 'const input = {\n' + - ' "one.one": "1.1",\n' + - ' "one.two": "1.2",\n' + - ' "two.one.one": "2.1.1"\n' + - '};\n' + - '\n' + - 'const output = Object.entries(input).reduce(\n' + - ' (acc, [key, value]) => set(acc, key, value),\n' + - ' {}\n' + - ');\n' + - '\n' + - '// Output:\n' + - '//\n' + - '// {\n' + - '// "one": {\n' + - '// "one": "1.1",\n' + - '// "two": "1.2"\n' + - '// },\n' + - '// "two": {\n' + - '// "one": {\n' + - '// "one": "2.1.1"\n' + - '// }\n' + - '// }\n' + - '// }\n' -} -Warning: Missing `Description` or `aria-describedby={undefined}` for {DialogContent}. - - ✓ src/components/qr-code/__tests__/QRCodePrinter.test.tsx > QRCodePrinter - 标签模板多语言渲染 > [zh-CN] 预览对话框标题包含对应语言的模板名称 189ms -stderr | src/components/qr-code/__tests__/QRCodePrinter.test.tsx > QRCodePrinter - 核心逻辑分支 > 打印成功时显示 printJobSent + printCopiesSent toast -IntlError: INVALID_KEY: Namespace keys cannot contain the character "." as this is used to express nesting. Please remove it or replace it with another character. - -Invalid keys: status.pending (at Production), status.confirmed (at Production), status.producing (at Production), status.completed (at Production), status.cancelled (at Production), status.label (at Production), priority.urgent (at Production), priority.high (at Production), priority.normal (at Production), priority.low (at Production), priority.label (at Production), units.sheet (at Production), units.roll (at Production), units.piece (at Production) - -If you're migrating from a flat structure, you can convert your messages as follows: - -import {set} from "lodash"; - -const input = { - "one.one": "1.1", - "one.two": "1.2", - "two.one.one": "2.1.1" -}; - -const output = Object.entries(input).reduce( - (acc, [key, value]) => set(acc, key, value), - {} -); - -// Output: -// -// { -// "one": { -// "one": "1.1", -// "two": "1.2" -// }, -// "two": { -// "one": { -// "one": "2.1.1" -// } -// } -// } - - at validateMessages (file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/initializeConfig-CUsOI8u2.js:465:13) - at initializeConfig (file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/initializeConfig-CUsOI8u2.js:515:7) - at file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/react.js:39:8 - at mountMemo (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:8777:23) - at Object.useMemo (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:26216:18) - at process.env.NODE_ENV.exports.useMemo (D:\dcprint\erp-project\node_modules\.pnpm\react@19.2.3\node_modules\react\cjs\react.development.js:1251:34) - at IntlProvider (file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/react.js:38:17) - at Object.react_stack_bottom_frame (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:25904:20) - at renderWithHooks (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:7662:22) - at updateFunctionComponent (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:10166:19) { - code: 'INVALID_KEY', - originalMessage: 'Namespace keys cannot contain the character "." as this is used to express nesting. Please remove it or replace it with another character.\n' + - '\n' + - 'Invalid keys: status.pending (at Production), status.confirmed (at Production), status.producing (at Production), status.completed (at Production), status.cancelled (at Production), status.label (at Production), priority.urgent (at Production), priority.high (at Production), priority.normal (at Production), priority.low (at Production), priority.label (at Production), units.sheet (at Production), units.roll (at Production), units.piece (at Production)\n' + - '\n' + - "If you're migrating from a flat structure, you can convert your messages as follows:\n" + - '\n' + - 'import {set} from "lodash";\n' + - '\n' + - 'const input = {\n' + - ' "one.one": "1.1",\n' + - ' "one.two": "1.2",\n' + - ' "two.one.one": "2.1.1"\n' + - '};\n' + - '\n' + - 'const output = Object.entries(input).reduce(\n' + - ' (acc, [key, value]) => set(acc, key, value),\n' + - ' {}\n' + - ');\n' + - '\n' + - '// Output:\n' + - '//\n' + - '// {\n' + - '// "one": {\n' + - '// "one": "1.1",\n' + - '// "two": "1.2"\n' + - '// },\n' + - '// "two": {\n' + - '// "one": {\n' + - '// "one": "2.1.1"\n' + - '// }\n' + - '// }\n' + - '// }\n' -} -Warning: Missing `Description` or `aria-describedby={undefined}` for {DialogContent}. - - ✓ src/components/qr-code/__tests__/QRCodePrinter.test.tsx > QRCodePrinter - 标签模板多语言渲染 > [en] 打印配置对话框标题显示英文 205ms -stderr | src/components/qr-code/__tests__/QRCodePrinter.test.tsx > QRCodePrinter - 核心逻辑分支 > 打印接口返回失败时显示 printFailed toast -IntlError: INVALID_KEY: Namespace keys cannot contain the character "." as this is used to express nesting. Please remove it or replace it with another character. - -Invalid keys: status.pending (at Production), status.confirmed (at Production), status.producing (at Production), status.completed (at Production), status.cancelled (at Production), status.label (at Production), priority.urgent (at Production), priority.high (at Production), priority.normal (at Production), priority.low (at Production), priority.label (at Production), units.sheet (at Production), units.roll (at Production), units.piece (at Production) - -If you're migrating from a flat structure, you can convert your messages as follows: - -import {set} from "lodash"; - -const input = { - "one.one": "1.1", - "one.two": "1.2", - "two.one.one": "2.1.1" -}; - -const output = Object.entries(input).reduce( - (acc, [key, value]) => set(acc, key, value), - {} -); - -// Output: -// -// { -// "one": { -// "one": "1.1", -// "two": "1.2" -// }, -// "two": { -// "one": { -// "one": "2.1.1" -// } -// } -// } - - at validateMessages (file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/initializeConfig-CUsOI8u2.js:465:13) - at initializeConfig (file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/initializeConfig-CUsOI8u2.js:515:7) - at file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/react.js:39:8 - at mountMemo (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:8777:23) - at Object.useMemo (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:26216:18) - at process.env.NODE_ENV.exports.useMemo (D:\dcprint\erp-project\node_modules\.pnpm\react@19.2.3\node_modules\react\cjs\react.development.js:1251:34) - at IntlProvider (file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/react.js:38:17) - at Object.react_stack_bottom_frame (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:25904:20) - at renderWithHooks (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:7662:22) - at updateFunctionComponent (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:10166:19) { - code: 'INVALID_KEY', - originalMessage: 'Namespace keys cannot contain the character "." as this is used to express nesting. Please remove it or replace it with another character.\n' + - '\n' + - 'Invalid keys: status.pending (at Production), status.confirmed (at Production), status.producing (at Production), status.completed (at Production), status.cancelled (at Production), status.label (at Production), priority.urgent (at Production), priority.high (at Production), priority.normal (at Production), priority.low (at Production), priority.label (at Production), units.sheet (at Production), units.roll (at Production), units.piece (at Production)\n' + - '\n' + - "If you're migrating from a flat structure, you can convert your messages as follows:\n" + - '\n' + - 'import {set} from "lodash";\n' + - '\n' + - 'const input = {\n' + - ' "one.one": "1.1",\n' + - ' "one.two": "1.2",\n' + - ' "two.one.one": "2.1.1"\n' + - '};\n' + - '\n' + - 'const output = Object.entries(input).reduce(\n' + - ' (acc, [key, value]) => set(acc, key, value),\n' + - ' {}\n' + - ');\n' + - '\n' + - '// Output:\n' + - '//\n' + - '// {\n' + - '// "one": {\n' + - '// "one": "1.1",\n' + - '// "two": "1.2"\n' + - '// },\n' + - '// "two": {\n' + - '// "one": {\n' + - '// "one": "2.1.1"\n' + - '// }\n' + - '// }\n' + - '// }\n' -} -Warning: Missing `Description` or `aria-describedby={undefined}` for {DialogContent}. - - ✓ src/components/qr-code/__tests__/QRCodePrinter.test.tsx > QRCodePrinter - 核心逻辑分支 > 打印成功时显示 printJobSent + printCopiesSent toast 344ms - ✓ src/tests/i18n/translations.test.ts > Translation Keys > should have same namespaces in all language files 8ms - ✓ src/tests/i18n/translations.test.ts > Translation Keys > should have Common namespace with required keys 3ms - ✓ src/tests/i18n/translations.test.ts > Translation Keys > should not have empty translations in zh-CN 187ms -stderr | src/components/qr-code/__tests__/QRCodePrinter.test.tsx > QRCodePrinter - 核心逻辑分支 > fetch 异常时显示 printFailed + printServiceError -IntlError: INVALID_KEY: Namespace keys cannot contain the character "." as this is used to express nesting. Please remove it or replace it with another character. - -Invalid keys: status.pending (at Production), status.confirmed (at Production), status.producing (at Production), status.completed (at Production), status.cancelled (at Production), status.label (at Production), priority.urgent (at Production), priority.high (at Production), priority.normal (at Production), priority.low (at Production), priority.label (at Production), units.sheet (at Production), units.roll (at Production), units.piece (at Production) - -If you're migrating from a flat structure, you can convert your messages as follows: - -import {set} from "lodash"; - -const input = { - "one.one": "1.1", - "one.two": "1.2", - "two.one.one": "2.1.1" -}; - -const output = Object.entries(input).reduce( - (acc, [key, value]) => set(acc, key, value), - {} -); - -// Output: -// -// { -// "one": { -// "one": "1.1", -// "two": "1.2" -// }, -// "two": { -// "one": { -// "one": "2.1.1" -// } -// } -// } - - at validateMessages (file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/initializeConfig-CUsOI8u2.js:465:13) - at initializeConfig (file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/initializeConfig-CUsOI8u2.js:515:7) - at file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/react.js:39:8 - at mountMemo (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:8777:23) - at Object.useMemo (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:26216:18) - at process.env.NODE_ENV.exports.useMemo (D:\dcprint\erp-project\node_modules\.pnpm\react@19.2.3\node_modules\react\cjs\react.development.js:1251:34) - at IntlProvider (file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/react.js:38:17) - at Object.react_stack_bottom_frame (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:25904:20) - at renderWithHooks (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:7662:22) - at updateFunctionComponent (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:10166:19) { - code: 'INVALID_KEY', - originalMessage: 'Namespace keys cannot contain the character "." as this is used to express nesting. Please remove it or replace it with another character.\n' + - '\n' + - 'Invalid keys: status.pending (at Production), status.confirmed (at Production), status.producing (at Production), status.completed (at Production), status.cancelled (at Production), status.label (at Production), priority.urgent (at Production), priority.high (at Production), priority.normal (at Production), priority.low (at Production), priority.label (at Production), units.sheet (at Production), units.roll (at Production), units.piece (at Production)\n' + - '\n' + - "If you're migrating from a flat structure, you can convert your messages as follows:\n" + - '\n' + - 'import {set} from "lodash";\n' + - '\n' + - 'const input = {\n' + - ' "one.one": "1.1",\n' + - ' "one.two": "1.2",\n' + - ' "two.one.one": "2.1.1"\n' + - '};\n' + - '\n' + - 'const output = Object.entries(input).reduce(\n' + - ' (acc, [key, value]) => set(acc, key, value),\n' + - ' {}\n' + - ');\n' + - '\n' + - '// Output:\n' + - '//\n' + - '// {\n' + - '// "one": {\n' + - '// "one": "1.1",\n' + - '// "two": "1.2"\n' + - '// },\n' + - '// "two": {\n' + - '// "one": {\n' + - '// "one": "2.1.1"\n' + - '// }\n' + - '// }\n' + - '// }\n' -} -Warning: Missing `Description` or `aria-describedby={undefined}` for {DialogContent}. - - ✓ src/components/qr-code/__tests__/QRCodePrinter.test.tsx > QRCodePrinter - 核心逻辑分支 > 打印接口返回失败时显示 printFailed toast 284ms -stderr | src/components/qr-code/__tests__/QRCodePrinter.test.tsx > QRCodePrinter - 核心逻辑分支 > [vi] 成功分支文案随语言切换为越南语 -IntlError: INVALID_KEY: Namespace keys cannot contain the character "." as this is used to express nesting. Please remove it or replace it with another character. - -Invalid keys: status.pending (at Production), status.confirmed (at Production), status.producing (at Production), status.completed (at Production), status.cancelled (at Production), status.label (at Production), priority.urgent (at Production), priority.high (at Production), priority.normal (at Production), priority.low (at Production), priority.label (at Production), units.sheet (at Production), units.roll (at Production), units.piece (at Production) - -If you're migrating from a flat structure, you can convert your messages as follows: - -import {set} from "lodash"; - -const input = { - "one.one": "1.1", - "one.two": "1.2", - "two.one.one": "2.1.1" -}; - -const output = Object.entries(input).reduce( - (acc, [key, value]) => set(acc, key, value), - {} -); - -// Output: -// -// { -// "one": { -// "one": "1.1", -// "two": "1.2" -// }, -// "two": { -// "one": { -// "one": "2.1.1" -// } -// } -// } - - at validateMessages (file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/initializeConfig-CUsOI8u2.js:465:13) - at initializeConfig (file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/initializeConfig-CUsOI8u2.js:515:7) - at file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/react.js:39:8 - at mountMemo (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:8777:23) - at Object.useMemo (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:26216:18) - at process.env.NODE_ENV.exports.useMemo (D:\dcprint\erp-project\node_modules\.pnpm\react@19.2.3\node_modules\react\cjs\react.development.js:1251:34) - at IntlProvider (file:///D:/dcprint/erp-project/node_modules/.pnpm/use-intl@4.13.0_react@19.2.3/node_modules/use-intl/dist/esm/development/react.js:38:17) - at Object.react_stack_bottom_frame (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:25904:20) - at renderWithHooks (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:7662:22) - at updateFunctionComponent (D:\dcprint\erp-project\node_modules\.pnpm\react-dom@19.2.3_react@19.2.3\node_modules\react-dom\cjs\react-dom-client.development.js:10166:19) { - code: 'INVALID_KEY', - originalMessage: 'Namespace keys cannot contain the character "." as this is used to express nesting. Please remove it or replace it with another character.\n' + - '\n' + - 'Invalid keys: status.pending (at Production), status.confirmed (at Production), status.producing (at Production), status.completed (at Production), status.cancelled (at Production), status.label (at Production), priority.urgent (at Production), priority.high (at Production), priority.normal (at Production), priority.low (at Production), priority.label (at Production), units.sheet (at Production), units.roll (at Production), units.piece (at Production)\n' + - '\n' + - "If you're migrating from a flat structure, you can convert your messages as follows:\n" + - '\n' + - 'import {set} from "lodash";\n' + - '\n' + - 'const input = {\n' + - ' "one.one": "1.1",\n' + - ' "one.two": "1.2",\n' + - ' "two.one.one": "2.1.1"\n' + - '};\n' + - '\n' + - 'const output = Object.entries(input).reduce(\n' + - ' (acc, [key, value]) => set(acc, key, value),\n' + - ' {}\n' + - ');\n' + - '\n' + - '// Output:\n' + - '//\n' + - '// {\n' + - '// "one": {\n' + - '// "one": "1.1",\n' + - '// "two": "1.2"\n' + - '// },\n' + - '// "two": {\n' + - '// "one": {\n' + - '// "one": "2.1.1"\n' + - '// }\n' + - '// }\n' + - '// }\n' -} -Warning: Missing `Description` or `aria-describedby={undefined}` for {DialogContent}. - - ✓ src/components/qr-code/__tests__/QRCodePrinter.test.tsx > QRCodePrinter - 核心逻辑分支 > fetch 异常时显示 printFailed + printServiceError 295ms - ✓ src/components/qr-code/__tests__/QRCodePrinter.test.tsx > QRCodePrinter - 核心逻辑分支 > [vi] 成功分支文案随语言切换为越南语 439ms - ✓ src/app/api/auth/login/route.test.ts > 登录API测试 > 应该成功登录有效用户 28ms - ✓ src/app/api/auth/login/route.test.ts > 登录API测试 > 应该拒绝空用户名或密码 3ms - ✓ src/app/api/auth/login/route.test.ts > 登录API测试 > 应该拒绝不存在的用户 4ms - ✓ src/app/api/auth/login/route.test.ts > 登录API测试 > 应该拒绝错误的密码 3ms - ✓ src/app/api/auth/login/route.test.ts > 登录API测试 > 应该锁定多次登录失败的用户 4ms - ✓ src/app/api/auth/login/route.test.ts > 登录API测试 > 应该拒绝被禁用的用户 2ms - ✓ src/app/api/auth/login/route.test.ts > 登录API测试 > 应该处理数据库查询错误 2ms - ✓ src/app/api/auth/login/route.test.ts > 登录API测试 > 应该记录登录日志 8ms - ✓ src/app/api/auth/login/route.test.ts > 登录API测试 > 应该在锁定时间过后允许重新登录 6ms - ✓ src/app/api/auth/login/route.test.ts > 登录API测试 > 应该在锁定时间内拒绝登录 2ms - ✓ src/app/api/auth/login/route.test.ts > 登录API测试 > 应该在密码错误时显示剩余尝试次数 2ms - ✓ src/app/api/auth/login/route.test.ts > 登录API测试 > 应该在首次登录时返回firstLogin标志 2ms - ✓ src/app/api/auth/login/route.test.ts > 登录API测试 > 应该在非首次登录时返回firstLogin为false 3ms - ✓ src/app/api/auth/login/route.test.ts > 登录API测试 > 应该在密码错误达到上限时锁定账号 2ms -[2026-08-27 11:31:29.763 +0800] DEBUG: [auth.login] 用户登录 - ctx: { - "module": "auth", - "action": "login", - "traceId": "mtayui5e-s10wl6o" - } - step: "用户登录" - data: { - "clientIP": "127.0.0.1" - } - ✓ src/hooks/use-toast.test.ts > useToast Hook测试 > 应该返回初始状态 54ms - ✓ src/hooks/use-toast.test.ts > useToast Hook测试 > 应该添加toast 13ms - ✓ src/hooks/use-toast.test.ts > useToast Hook测试 > 应该支持destructive变体 7ms - ✓ src/hooks/use-toast.test.ts > useToast Hook测试 > 应该支持描述 6ms - ✓ src/hooks/use-toast.test.ts > useToast Hook测试 > 应该自动生成唯一ID 7ms - ✓ src/hooks/use-toast.test.ts > useToast Hook测试 > 应该自动移除toast 8ms - ✓ src/hooks/use-toast.test.ts > useToast Hook测试 > 应该手动移除toast 6ms - ✓ src/hooks/use-toast.test.ts > useToast Hook测试 > 应该支持多个toast同时存在 6ms - ✓ src/hooks/use-toast.test.ts > useToast Hook测试 > 应该只移除指定的toast 8ms - ✓ src/hooks/use-toast.test.ts > useToast Hook测试 > 应该处理不存在的toast ID 5ms - ✓ src/hooks/use-toast.test.ts > useToast Hook测试 > 应该正确清理定时器 8ms - ✓ src/app/[locale]/warehouse/inbound/__tests__/usePurchaseOrderSearch.test.ts > usePurchaseOrderSearch > 初始状态为空 48ms - ✓ src/app/[locale]/warehouse/inbound/__tests__/usePurchaseOrderSearch.test.ts > usePurchaseOrderSearch > 空关键词不触发搜索并清空结果 10ms - ✓ src/lib/__tests__/performance.test.ts > Cache Strategy > should set and get cached data 27ms - ✓ src/lib/__tests__/performance.test.ts > Cache Strategy > should return null for non-existent key 1ms - ✓ src/lib/__tests__/performance.test.ts > Cache Strategy > should delete cached data 1ms - ✓ src/lib/__tests__/performance.test.ts > Cache Strategy > should expire data after TTL 6ms - ✓ src/lib/__tests__/performance.test.ts > Cache Strategy > should use getOrSet to fetch and cache 3ms - ✓ src/lib/__tests__/performance.test.ts > Error Handling > should create AppError with correct properties 39ms - ✓ src/lib/__tests__/performance.test.ts > Error Handling > should handle different error types 1ms - ✓ src/lib/__tests__/performance.test.ts > Circuit Breaker > should execute function successfully 1ms - ✓ src/lib/__tests__/performance.test.ts > Circuit Breaker > should open circuit after threshold failures 5ms - ✓ src/lib/__tests__/performance.test.ts > Rate Limiter > should allow requests within limit 43ms - ✓ src/lib/__tests__/performance.test.ts > Rate Limiter > should block requests over limit 2ms - ✓ src/lib/__tests__/performance.test.ts > Performance Monitor > should record and retrieve metrics 38ms - ✓ src/lib/__tests__/performance.test.ts > Query Analyzer > should log slow queries 17ms - ✓ src/lib/__tests__/performance.test.ts > Query Analyzer > should calculate statistics 1ms - ✓ src/app/[locale]/warehouse/inbound/__tests__/usePurchaseOrderSearch.test.ts > usePurchaseOrderSearch > 输入关键词后 300ms 触发搜索 148ms - ✓ src/app/[locale]/warehouse/inbound/__tests__/usePurchaseOrderSearch.test.ts > usePurchaseOrderSearch > 搜索结果为空时隐藏下拉 58ms - ✓ src/app/[locale]/warehouse/inbound/__tests__/usePurchaseOrderSearch.test.ts > usePurchaseOrderSearch > API 失败时清空结果并隐藏下拉 121ms - ✓ src/app/[locale]/warehouse/inbound/__tests__/usePurchaseOrderSearch.test.ts > usePurchaseOrderSearch > handlePoSelect 填充表单并关闭下拉 8ms - ✓ src/app/[locale]/warehouse/inbound/__tests__/usePurchaseOrderSearch.test.ts > usePurchaseOrderSearch > handlePoSelect 无 lines 时保持表单原值 5ms - ✓ src/app/[locale]/warehouse/inbound/__tests__/usePurchaseOrderSearch.test.ts > usePurchaseOrderSearch > handlePoSearchChange 调用 setFormData 更新 purchaseOrderNo 5ms - ✓ src/app/[locale]/warehouse/inbound/__tests__/usePurchaseOrderSearch.test.ts > usePurchaseOrderSearch > 连续输入时只触发最后一次搜索(防抖) 39ms - ✓ src/app/[locale]/warehouse/inbound/__tests__/usePurchaseOrderSearch.test.ts > usePurchaseOrderSearch > API 返回 success=false 时清空结果并隐藏下拉 128ms - ✓ src/app/[locale]/warehouse/inbound/__tests__/usePurchaseOrderSearch.test.ts > usePurchaseOrderSearch > data 为裸数组(非 {list: [...]})时也能正确处理 119ms - ✓ src/app/[locale]/warehouse/inbound/__tests__/usePurchaseOrderSearch.test.ts > usePurchaseOrderSearch > handlePoSelect 中 lines 缺少 received_qty 时 quantity 为 order_qty 全量 4ms - ✓ src/app/[locale]/warehouse/inbound/__tests__/usePurchaseOrderSearch.test.ts > usePurchaseOrderSearch > handlePoSelect 中 order_qty 为 0 时 quantity 保持 prev.quantity 3ms - ✓ src/app/[locale]/warehouse/inbound/__tests__/usePurchaseOrderSearch.test.ts > usePurchaseOrderSearch > handlePoLineSelect 选择具体明细行并填充 PO 关联字段 4ms - ✓ src/app/[locale]/warehouse/inbound/__tests__/usePurchaseOrderSearch.test.ts > usePurchaseOrderSearch > handlePoLineSelect 已全部收货行数量为 0 3ms - ✓ src/app/[locale]/warehouse/inbound/__tests__/usePurchaseOrderSearch.test.ts > usePurchaseOrderSearch > handlePoToggleExpand 切换展开的 PO ID 3ms - ✓ src/app/[locale]/warehouse/inbound/__tests__/usePurchaseOrderSearch.test.ts > usePurchaseOrderSearch > handlePoSelect 传入 line 参数时使用指定行(向后兼容) 3ms - ✓ tests/integration/purchase-orders-category-validation.test.ts > POST /api/purchase/orders — 物料分类校验集成 > 场景 1:所有物料已归类 > 放行,正常创建采购订单 95ms - ✓ tests/integration/purchase-orders-category-validation.test.ts > POST /api/purchase/orders — 物料分类校验集成 > 场景 2:部分未归类 + require_on_business=false > 放行,附带警告日志 4ms - ✓ tests/integration/purchase-orders-category-validation.test.ts > POST /api/purchase/orders — 物料分类校验集成 > 场景 3:未归类 + require_on_business=true(硬拦截) > 返回 400,不创建订单 4ms - ✓ tests/integration/purchase-orders-category-validation.test.ts > POST /api/purchase/orders — 物料分类校验集成 > 场景 4:空明细 — 无物料需校验 > 返回必填校验错误,不调用 checkMaterialsCategorized 2ms - ✓ tests/integration/purchase-orders-category-validation.test.ts > POST /api/purchase/orders — 物料分类校验集成 > 场景 5:material_id 非法值被过滤 > null/0/undefined 被路由过滤,-1 被 checkMaterialsCategorized 内部过滤 5ms - ✓ tests/integration/purchase-orders-category-validation.test.ts > POST /api/purchase/orders — 物料分类校验集成 > 场景 6:重复 material_id > 路由传入原始数组,内部去重 3ms - ✓ tests/integration/purchase-orders-category-validation.test.ts > POST /api/purchase/orders — 物料分类校验集成 > 场景 7:校验异常 — 当前实现返回 500 > checkMaterialsCategorized 抛错时,路由返回 500 2ms - ✓ tests/integration/purchase-orders-category-validation.test.ts > POST /api/purchase/orders — 物料分类校验集成 > 场景 8:合规编码 MAT-CAT-001 通过 > 正常走通路由 4ms - ✓ tests/integration/event-bus/xautoclaim-redelivery-simulation.test.ts > XAUTOCLAIM 重投递幂等性集成模拟 > 场景 A: handler 首次成功 → mark 保留 → 重投递被拦截 > 成功执行后,重投递应被幂等拦截(callCount=1) 52ms - ✓ tests/integration/event-bus/xautoclaim-redelivery-simulation.test.ts > XAUTOCLAIM 重投递幂等性集成模拟 > 场景 B: handler 首次失败 → deleteMark → 重投递再次执行(修复后行为) > handler 失败后 deleteMark 删除预占位,XAUTOCLAIM 重投递应再次执行(callCount=2) 66ms -[2026-08-27 11:31:32.856 +0800] DEBUG: IdempotentHandler: handler succeeded, mark confirmed as processed - eventId: 9001001 - handlerName: "TestXautoclaimHandler" - eventType: "TestSimulationEvent" -[2026-08-27 11:31:32.869 +0800] DEBUG: IdempotencyGuard: event already processed or in progress, skipping - eventId: 9001001 - handlerName: "TestXautoclaimHandler" -[2026-08-27 11:31:32.869 +0800] DEBUG: IdempotentHandler: skipping duplicate event - eventId: 9001001 - handlerName: "TestXautoclaimHandler" - eventType: "TestSimulationEvent" -[2026-08-27 11:31:32.890 +0800] WARN: IdempotentHandler: handler failed, deleting mark for retry - eventId: 9001002 - handlerName: "TestXautoclaimHandler" - eventType: "TestSimulationEvent" - error: "Simulated handler failure (first attempt)" -[2026-08-27 11:31:32.900 +0800] DEBUG: IdempotencyGuard: mark deleted (handler failed, allowing retry) - eventId: 9001002 - handlerName: "TestXautoclaimHandler" -[2026-08-27 11:31:32.925 +0800] DEBUG: IdempotentHandler: handler succeeded, mark confirmed as processed - eventId: 9001002 - handlerName: "TestXautoclaimHandler" - eventType: "TestSimulationEvent" -[2026-08-27 11:31:32.936 +0800] DEBUG: IdempotencyGuard: event already processed or in progress, skipping - eventId: 9001002 - handlerName: "TestXautoclaimHandler" -[2026-08-27 11:31:32.937 +0800] DEBUG: IdempotentHandler: skipping duplicate event - eventId: 9001002 - handlerName: "TestXautoclaimHandler" - eventType: "TestSimulationEvent" -[2026-08-27 11:31:32.963 +0800] DEBUG: IdempotencyGuard: event already processed or in progress, skipping - eventId: 9001003 - handlerName: "OldLogicHandler_NoDeleteMark" -[2026-08-27 11:31:32.994 +0800] DEBUG: IdempotencyGuard: event already processed or in progress, skipping - eventId: 9001004 - handlerName: "TestXautoclaimHandler" -[2026-08-27 11:31:33.009 +0800] WARN: IdempotentHandler: handler failed, deleting mark for retry - eventId: 9001005 - handlerName: "TestXautoclaimHandler" - eventType: "TestSimulationEvent" - error: "Simulated handler failure (attempt 1)" -[2026-08-27 11:31:33.019 +0800] DEBUG: IdempotencyGuard: mark deleted (handler failed, allowing retry) - eventId: 9001005 - handlerName: "TestXautoclaimHandler" -[2026-08-27 11:31:33.033 +0800] WARN: IdempotentHandler: handler failed, deleting mark for retry - eventId: 9001005 - handlerName: "TestXautoclaimHandler" - eventType: "TestSimulationEvent" - error: "Simulated handler failure (attempt 2)" - ✓ tests/integration/event-bus/xautoclaim-redelivery-simulation.test.ts > XAUTOCLAIM 重投递幂等性集成模拟 > 场景 C: 模拟旧逻辑(无 deleteMark)→ handler 失败 → 重投递被错误拦截(BUG) > 无 deleteMark 时,handler 失败后重投递被错误拦截(callCount=1,消息丢失) 33ms - ✓ tests/integration/event-bus/xautoclaim-redelivery-simulation.test.ts > XAUTOCLAIM 重投递幂等性集成模拟 > 场景 D: 并发 claim 竞争(两个消费者同时拉取同一事件) > 两个消费者同时 checkAndMark,仅一个返回 true 21ms -[2026-08-27 11:31:33.045 +0800] DEBUG: IdempotencyGuard: mark deleted (handler failed, allowing retry) - eventId: 9001005 - handlerName: "TestXautoclaimHandler" -[2026-08-27 11:31:33.057 +0800] WARN: IdempotentHandler: handler failed, deleting mark for retry - eventId: 9001005 - handlerName: "TestXautoclaimHandler" - eventType: "TestSimulationEvent" - error: "Simulated handler failure (attempt 3)" -[2026-08-27 11:31:33.067 +0800] DEBUG: IdempotencyGuard: mark deleted (handler failed, allowing retry) - eventId: 9001005 - handlerName: "TestXautoclaimHandler" -[2026-08-27 11:31:33.095 +0800] DEBUG: IdempotencyGuard: event already processed or in progress, skipping - eventId: 9001007 - handlerName: "TestXautoclaimHandler" - ✓ tests/integration/event-bus/xautoclaim-redelivery-simulation.test.ts > XAUTOCLAIM 重投递幂等性集成模拟 > 场景 E: handler 持续失败 → 多次重投递 → 每次都允许执行 > 持续失败时 deleteMark 确保每次重投递都能重试 75ms - ✓ tests/integration/event-bus/xautoclaim-redelivery-simulation.test.ts > XAUTOCLAIM 重投递幂等性集成模拟 > 场景 F: 无 eventId 的事件(非 outbox 来源) > 无 eventId 时 IdempotentHandler 透传执行,不做幂等保护 4ms - ✓ tests/integration/event-bus/xautoclaim-redelivery-simulation.test.ts > XAUTOCLAIM 重投递幂等性集成模拟 > 场景 G: 进程在 checkAndMark 与 handle 之间崩溃(已知限制) > mark 已存在但 handler 未执行 → 重投递被错误拦截(mark-before-execute 固有窗口) 21ms -[2026-08-27 11:31:33.190 +0800] WARN: IdempotencyGuard: mark failed, allowing execution - eventId: 9001008 - handlerName: "TestXautoclaimHandler" - error: "Table 'vnerpdacahng.sys_event_processed' doesn't exist" -[2026-08-27 11:31:33.271 +0800] DEBUG: IdempotentHandler: handler succeeded, mark confirmed as processed - eventId: 9001009 - handlerName: "TestXautoclaimHandler" - eventType: "TestSimulationEvent" - ✓ tests/integration/event-bus/xautoclaim-redelivery-simulation.test.ts > XAUTOCLAIM 重投递幂等性集成模拟 > 场景 H: DB 故障时 checkAndMark 降级 → 无持久化 → 重投递重复执行 > 真实 DB 故障(表不存在)→ checkAndMark 降级返回 true → handler 执行 → 无 mark → 恢复后重投递再次执行 153ms - ✓ tests/integration/event-bus/xautoclaim-redelivery-simulation.test.ts > XAUTOCLAIM 重投递幂等性集成模拟 > 场景 I: 两阶段标记 — markAsProcessed 状态流转验证 > checkAndMark 插入 processing → handler 成功 → markAsProcessed 更新为 processed → 重投递被拦截 34ms -[2026-08-27 11:31:33.281 +0800] DEBUG: IdempotencyGuard: event already processed or in progress, skipping - eventId: 9001009 - handlerName: "TestXautoclaimHandler" -[2026-08-27 11:31:33.281 +0800] DEBUG: IdempotentHandler: skipping duplicate event - eventId: 9001009 - handlerName: "TestXautoclaimHandler" - eventType: "TestSimulationEvent" -[2026-08-27 11:31:33.378 +0800] WARN: IdempotencyGuard: reclaimed stale processing records - count: 3 - thresholdMinutes: 5 - note: "If handlers routinely take longer than the threshold, increase IDEMPOTENCY_STALE_THRESHOLD_MINUTES" - ✓ tests/integration/event-bus/xautoclaim-redelivery-simulation.test.ts > XAUTOCLAIM 重投递幂等性集成模拟 > 场景 J: 崩溃恢复 — 过期 processing 记录被回收后允许重试 > 模拟崩溃残留 processing 记录 → 5 分钟后 checkAndMark 自动清理并允许重试 34ms - ✓ tests/integration/event-bus/xautoclaim-redelivery-simulation.test.ts > XAUTOCLAIM 重投递幂等性集成模拟 > 场景 K: reclaimStaleProcessing 批量回收 > 批量清理多个过期 processing 记录,返回清理数量 66ms - ✓ src/app/[locale]/warehouse/inbound/__tests__/generatePrintContent.test.ts > generatePrintContent > 生成包含指定标题的 HTML 文档 50ms - ✓ src/app/[locale]/warehouse/inbound/__tests__/generatePrintContent.test.ts > generatePrintContent > 只包含 approved 和 completed 状态的记录标签 2ms - ✓ src/app/[locale]/warehouse/inbound/__tests__/generatePrintContent.test.ts > generatePrintContent > 标签包含物料名称和规格 1ms - ✓ src/app/[locale]/warehouse/inbound/__tests__/generatePrintContent.test.ts > generatePrintContent > 包含 QR 码 data URI 1ms - ✓ src/app/[locale]/warehouse/inbound/__tests__/generatePrintContent.test.ts > generatePrintContent > 空记录列表生成有效 HTML 但无标签 1ms - ✓ src/app/[locale]/warehouse/inbound/__tests__/generatePrintContent.test.ts > generatePrintContent > 全部为 draft 状态时不生成任何标签 1ms - ✓ src/app/[locale]/warehouse/inbound/__tests__/generatePrintContent.test.ts > generatePrintContent > 包含 @page A4 landscape 样式 1ms - ✓ src/app/[locale]/warehouse/inbound/__tests__/generatePrintContent.test.ts > generatePrintContent > 包含"已入库"状态标签 1ms - ✓ src/app/[locale]/warehouse/inbound/__tests__/generatePrintContent.test.ts > generatePrintContent > specification 为空时渲染 "-" 1ms - ✓ src/app/[locale]/warehouse/inbound/__tests__/generatePrintContent.test.ts > generatePrintContent > supplier 为空时渲染 "-" 1ms - ✓ src/app/[locale]/warehouse/inbound/__tests__/generatePrintContent.test.ts > generatePrintContent > inboundTime 为空时渲染 "-" 1ms -[2026-08-27 11:31:36.760 +0800] DEBUG: Inventory cost recalculated (inbound) - inventoryId: 407 - inboundQty: 50 - inboundUnitPrice: 10 - oldCostPrice: 0 - newCostPrice: 5 - newTotalAmount: 500 -[2026-08-27 11:31:36.789 +0800] INFO: Inventory synced for inbound order - orderNo: "IN_TEST_1787801496741_1" - itemCount: 1 -[2026-08-27 11:31:36.816 +0800] INFO: 采购订单聚合根重建完成 - module: "purchase-inbound" - action: "sync" - inboundId: 2 - poId: 149 -[2026-08-27 11:31:36.823 +0800] INFO: 采购订单已收量与状态更新完成 - module: "purchase-inbound" - action: "sync" - inboundId: 2 - poId: 149 -[2026-08-27 11:31:36.823 +0800] INFO: Purchase order synced from inbound approval - poId: 149 - inboundNo: "IN_TEST_1787801496799_2" - newStatus: "partially_received" - receivedQuantity: 50 - ✓ tests/integration/inbound-approve-chain.test.ts > 入库审核全链路 (approve → 库存/批次/流水/PO/财务) > IT-approve-1 InventorySyncHandler:库存+批次+流水正确写入 60ms -[2026-08-27 11:31:36.862 +0800] INFO: Payable created - inboundNo: "IN_TEST_1787801496843_3" - totalAmount: 500 - supplierId: 1 - sourceType: 1 -[2026-08-27 11:31:36.867 +0800] INFO: Payable already exists for source, skip - inboundNo: "IN_TEST_1787801496843_3" - sourceType: 1 - existingId: 39 -[2026-08-27 11:31:36.879 +0800] DEBUG: Inventory cost recalculated (inbound) - inventoryId: 407 - inboundQty: 60 - inboundUnitPrice: 10 - oldCostPrice: 5 - newCostPrice: 6.4706 - newTotalAmount: 1100 -[2026-08-27 11:31:36.894 +0800] INFO: Inventory synced for inbound order - orderNo: "IN_TEST_1787801496871_4" - itemCount: 1 -[2026-08-27 11:31:36.899 +0800] INFO: 采购订单聚合根重建完成 - module: "purchase-inbound" - action: "sync" - inboundId: 4 - poId: 149 -[2026-08-27 11:31:36.902 +0800] INFO: 采购订单已收量与状态更新完成 - module: "purchase-inbound" - action: "sync" - inboundId: 4 - poId: 149 -[2026-08-27 11:31:36.903 +0800] INFO: Purchase order synced from inbound approval - poId: 149 - inboundNo: "IN_TEST_1787801496871_4" - newStatus: "partially_received" - receivedQuantity: 110 -[2026-08-27 11:31:36.929 +0800] INFO: Payable created - inboundNo: "IN_TEST_1787801496871_4" - totalAmount: 600 - supplierId: 1 - sourceType: 1 - ✓ tests/integration/inbound-approve-chain.test.ts > 入库审核全链路 (approve → 库存/批次/流水/PO/财务) > IT-approve-2 PurchaseInboundSyncHandler:PO 收货量回写 44ms - ✓ tests/integration/inbound-approve-chain.test.ts > 入库审核全链路 (approve → 库存/批次/流水/PO/财务) > IT-approve-3 FinanceVoucherHandler:财务过账且幂等(ERR-IN-006) 28ms - ✓ tests/integration/inbound-approve-chain.test.ts > 入库审核全链路 (approve → 库存/批次/流水/PO/财务) > IT-approve-4 全链路:同一事件被三 handler 消费后各表一致 65ms - ✓ src/app/[locale]/warehouse/inbound/__tests__/useInboundDialogs.test.ts > useInboundDialogs > 初始状态全部为关闭/空 57ms - ✓ src/app/[locale]/warehouse/inbound/__tests__/useInboundDialogs.test.ts > useInboundDialogs > setIsAddDialogOpen(true) 开启新增对话框 8ms - ✓ src/app/[locale]/warehouse/inbound/__tests__/useInboundDialogs.test.ts > useInboundDialogs > setIsCuttingDialogOpen 切换分切对话框状态 8ms - ✓ src/app/[locale]/warehouse/inbound/__tests__/useInboundDialogs.test.ts > useInboundDialogs > setPrintLabels 设置打印标签列表 5ms - ✓ src/app/[locale]/warehouse/inbound/__tests__/useInboundDialogs.test.ts > useInboundDialogs > setCurrentRecord 设置当前记录 6ms - ✓ src/app/[locale]/warehouse/inbound/__tests__/useInboundDialogs.test.ts > useInboundDialogs > setCurrentLabel 设置当前标签 4ms - ✓ src/app/[locale]/warehouse/inbound/__tests__/useInboundDialogs.test.ts > useInboundDialogs > setLabelSupplier 设置标签供应商 4ms - ✓ src/app/[locale]/warehouse/inbound/__tests__/useInboundDialogs.test.ts > useInboundDialogs > 多个对话框可同时独立开启 4ms - ✓ tests/unit/domain/sales/aggregates/reconciliation.test.ts > Reconciliation 聚合根 > create() 工厂方法 > 合法参数创建成功(用例1) 7ms - ✓ tests/unit/domain/sales/aggregates/reconciliation.test.ts > Reconciliation 聚合根 > create() 工厂方法 > 退货金额为0正常创建(用例2) 1ms - ✓ tests/unit/domain/sales/aggregates/reconciliation.test.ts > Reconciliation 聚合根 > create() 工厂方法 > 退货大于发货抛错(用例3) 2ms - ✓ tests/unit/domain/sales/aggregates/reconciliation.test.ts > Reconciliation 聚合根 > create() 工厂方法 > customerId为0抛错(用例4) 1ms - ✓ tests/unit/domain/sales/aggregates/reconciliation.test.ts > Reconciliation 聚合根 > create() 工厂方法 > 时段无效抛错(用例5) 1ms - ✓ tests/unit/domain/sales/aggregates/reconciliation.test.ts > Reconciliation 聚合根 > create() 工厂方法 > 有id发布CreatedEvent,无id不发布(用例6) 1ms - ✓ tests/unit/domain/sales/aggregates/reconciliation.test.ts > Reconciliation 聚合根 > reconstitute() 重建方法 > 从DB字段完整重建(用例7) 1ms - ✓ tests/unit/domain/sales/aggregates/reconciliation.test.ts > Reconciliation 聚合根 > reconstitute() 重建方法 > 无writeOffRecords默认空数组(用例8) 1ms - ✓ tests/unit/domain/sales/aggregates/reconciliation.test.ts > Reconciliation 聚合根 > reconstitute() 重建方法 > 未指定balance自动计算(用例9) 1ms - ✓ tests/unit/domain/sales/aggregates/reconciliation.test.ts > Reconciliation 聚合根 > confirm() 确认流程 > draft→confirmed正常确认(用例10) 2ms - ✓ tests/unit/domain/sales/aggregates/reconciliation.test.ts > Reconciliation 聚合根 > confirm() 确认流程 > 非draft状态确认抛错(用例11) 1ms - ✓ tests/unit/domain/sales/aggregates/reconciliation.test.ts > Reconciliation 聚合根 > confirm() 确认流程 > draft可编辑可删除(用例12) 1ms - ✓ tests/unit/domain/sales/aggregates/reconciliation.test.ts > Reconciliation 聚合根 > confirm() 确认流程 > confirmed不可编辑不可删除(用例13) 0ms - ✓ tests/unit/domain/sales/aggregates/reconciliation.test.ts > Reconciliation 聚合根 > writeOff() 部分核销场景 > 首次部分核销30%(用例14) 2ms - ✓ tests/unit/domain/sales/aggregates/reconciliation.test.ts > Reconciliation 聚合根 > writeOff() 部分核销场景 > 首次部分核销极小值1元(用例15) 1ms - ✓ tests/unit/domain/sales/aggregates/reconciliation.test.ts > Reconciliation 聚合根 > writeOff() 部分核销场景 > 首次部分核销99%(用例16) 0ms - ✓ tests/unit/domain/sales/aggregates/reconciliation.test.ts > Reconciliation 聚合根 > writeOff() 部分核销场景 > 首次核销恰好等于余额→全额核销(用例17) 1ms - ✓ tests/unit/domain/sales/aggregates/reconciliation.test.ts > Reconciliation 聚合根 > writeOff() 部分核销场景 > 部分核销后继续核销剩余→完成(用例18) 0ms - ✓ tests/unit/domain/sales/aggregates/reconciliation.test.ts > Reconciliation 聚合根 > writeOff() 部分核销场景 > 核销金额为0抛错(用例19) 1ms - ✓ tests/unit/domain/sales/aggregates/reconciliation.test.ts > Reconciliation 聚合根 > writeOff() 部分核销场景 > 核销金额为负抛错(用例20) 1ms - ✓ tests/unit/domain/sales/aggregates/reconciliation.test.ts > Reconciliation 聚合根 > writeOff() 部分核销场景 > 核销金额超过余额抛错(用例21) 1ms - ✓ tests/unit/domain/sales/aggregates/reconciliation.test.ts > Reconciliation 聚合根 > writeOff() 部分核销场景 > 浮点金额核销精度处理(用例22) 1ms - ✓ tests/unit/domain/sales/aggregates/reconciliation.test.ts > Reconciliation 聚合根 > writeOff() 多次核销场景 > 2次核销凑满(用例23) 1ms - ✓ tests/unit/domain/sales/aggregates/reconciliation.test.ts > Reconciliation 聚合根 > writeOff() 多次核销场景 > 3次核销凑满(用例24) 1ms - ✓ tests/unit/domain/sales/aggregates/reconciliation.test.ts > Reconciliation 聚合根 > writeOff() 多次核销场景 > 多次核销后第4次超余额抛错(用例25) 1ms - ✓ tests/unit/domain/sales/aggregates/reconciliation.test.ts > Reconciliation 聚合根 > writeOff() 多次核销场景 > 同一应收单分3次核销凑满(用例26) 1ms - ✓ tests/unit/domain/sales/aggregates/reconciliation.test.ts > Reconciliation 聚合根 > writeOff() 多次核销场景 > 多次核销后恰好凑满-边界(用例27) 0ms - ✓ tests/unit/domain/sales/aggregates/reconciliation.test.ts > Reconciliation 聚合根 > writeOff() 多次核销场景 > 多次核销中间状态事件发布(用例28) 0ms - ✓ tests/unit/domain/sales/aggregates/reconciliation.test.ts > Reconciliation 聚合根 > writeOff() 多次核销场景 > 5次小额核销累计完成(用例29) 1ms - ✓ tests/unit/domain/sales/aggregates/reconciliation.test.ts > Reconciliation 聚合根 > writeOff() 多次核销场景 > 失败操作不影响已成功核销(用例30) 1ms - ✓ tests/unit/domain/sales/aggregates/reconciliation.test.ts > Reconciliation 聚合根 > writeOff() 多次核销场景 > 多次核销后查询累计已核销应收单列表(用例31) 2ms - ✓ tests/unit/domain/sales/aggregates/reconciliation.test.ts > Reconciliation 聚合根 > writeOff() 一对多核销场景 > 一张对账单核销3张不同应收单(用例32) 2ms - ✓ tests/unit/domain/sales/aggregates/reconciliation.test.ts > Reconciliation 聚合根 > writeOff() 一对多核销场景 > 一张对账单核销2张-1部分+1全额(用例33) 1ms - ✓ tests/unit/domain/sales/aggregates/reconciliation.test.ts > Reconciliation 聚合根 > writeOff() 一对多核销场景 > 一张对账单对同一应收单多次部分核销(用例34) 1ms - ✓ tests/unit/domain/sales/aggregates/reconciliation.test.ts > Reconciliation 聚合根 > writeOff() 一对多核销场景 > 一张对账单核销5张应收单每张1800(用例35) 1ms - ✓ tests/unit/domain/sales/aggregates/reconciliation.test.ts > Reconciliation 聚合根 > writeOff() 一对多核销场景 > 一对多核销中某张失败不影响其他(用例36) 1ms - ✓ tests/unit/domain/sales/aggregates/reconciliation.test.ts > Reconciliation 聚合根 > writeOff() 状态边界与异常 > draft状态核销抛错(用例37) 1ms - ✓ tests/unit/domain/sales/aggregates/reconciliation.test.ts > Reconciliation 聚合根 > writeOff() 状态边界与异常 > written_off状态核销抛错(用例38) 1ms - ✓ tests/unit/domain/sales/aggregates/reconciliation.test.ts > Reconciliation 聚合根 > writeOff() 状态边界与异常 > closed状态核销抛错(用例39) 1ms - ✓ tests/unit/domain/sales/aggregates/reconciliation.test.ts > Reconciliation 聚合根 > writeOff() 状态边界与异常 > receivableId为0抛错(用例40) 1ms - ✓ tests/unit/domain/sales/aggregates/reconciliation.test.ts > Reconciliation 聚合根 > writeOff() 状态边界与异常 > 自定义核销日期(用例41) 0ms - ✓ tests/unit/domain/sales/aggregates/reconciliation.test.ts > Reconciliation 聚合根 > close() 关闭流程 > written_off→closed正常关闭(用例42) 1ms - ✓ tests/unit/domain/sales/aggregates/reconciliation.test.ts > Reconciliation 聚合根 > close() 关闭流程 > 未完成核销时关闭抛错(用例43) 1ms - ✓ tests/unit/domain/sales/aggregates/reconciliation.test.ts > Reconciliation 聚合根 > close() 关闭流程 > 余额为0的partial状态允许关闭-浮点容差(用例44) 1ms - ✓ tests/unit/domain/sales/aggregates/reconciliation.test.ts > Reconciliation 聚合根 > close() 关闭流程 > 已关闭再次关闭抛错(用例45) 0ms - ✓ tests/unit/domain/sales/aggregates/reconciliation.test.ts > Reconciliation 聚合根 > 领域事件 > 完整核销发布WrittenOffEvent含全部记录(用例46) 1ms - ✓ tests/unit/domain/sales/aggregates/reconciliation.test.ts > Reconciliation 聚合根 > 领域事件 > 部分核销发布PartialWrittenOffEvent含本次详情(用例47) 1ms - ✓ tests/unit/domain/sales/aggregates/reconciliation.test.ts > Reconciliation 聚合根 > 领域事件 > getDomainEvents返回副本不可变(用例48) 1ms - ✓ tests/unit/domain/warehouse/aggregates/transfer-order.test.ts > TransferOrder 聚合根 > create() 工厂方法 > 合法参数创建成功,状态为 DRAFT(0) 8ms - ✓ tests/unit/domain/warehouse/aggregates/transfer-order.test.ts > TransferOrder 聚合根 > create() 工厂方法 > 有 id 时发布 TransferOrderCreatedEvent 3ms - ✓ tests/unit/domain/warehouse/aggregates/transfer-order.test.ts > TransferOrder 聚合根 > create() 工厂方法 > 无 id 时不发布创建事件 1ms - ✓ tests/unit/domain/warehouse/aggregates/transfer-order.test.ts > TransferOrder 聚合根 > create() 工厂方法 > 自动计算 totalQuantity(多 item 累加) 1ms - ✓ tests/unit/domain/warehouse/aggregates/transfer-order.test.ts > TransferOrder 聚合根 > create() 工厂方法 > 自动计算 totalAmount(多 item 累加) 1ms - ✓ tests/unit/domain/warehouse/aggregates/transfer-order.test.ts > TransferOrder 聚合根 > create() 工厂方法 > 源仓库和目标仓库相同抛 DomainError 2ms - ✓ tests/unit/domain/warehouse/aggregates/transfer-order.test.ts > TransferOrder 聚合根 > create() 工厂方法 > 源仓库为 0 抛 DomainError 1ms - ✓ tests/unit/domain/warehouse/aggregates/transfer-order.test.ts > TransferOrder 聚合根 > create() 工厂方法 > items 为空数组抛 DomainError 1ms - ✓ tests/unit/domain/warehouse/aggregates/transfer-order.test.ts > TransferOrder 聚合根 > reconstitute() 重建方法 > 从 DB 字段重建聚合,使用指定 totalAmount/totalQuantity 2ms - ✓ tests/unit/domain/warehouse/aggregates/transfer-order.test.ts > TransferOrder 聚合根 > reconstitute() 重建方法 > 未指定 totalQuantity 时自动计算 1ms - ✓ tests/unit/domain/warehouse/aggregates/transfer-order.test.ts > TransferOrder 聚合根 > reconstitute() 重建方法 > 未指定 totalAmount 时自动计算 1ms - ✓ tests/unit/domain/warehouse/aggregates/transfer-order.test.ts > TransferOrder 聚合根 > submit() 提交流程 > DRAFT → PENDING,发布 TransferOrderSubmittedEvent 1ms - ✓ tests/unit/domain/warehouse/aggregates/transfer-order.test.ts > TransferOrder 聚合根 > submit() 提交流程 > 非 DRAFT 状态提交抛错 0ms - ✓ tests/unit/domain/warehouse/aggregates/transfer-order.test.ts > TransferOrder 聚合根 > approve() 审批流程 > PENDING → SHIPPED,设置审批人信息 1ms - ✓ tests/unit/domain/warehouse/aggregates/transfer-order.test.ts > TransferOrder 聚合根 > approve() 审批流程 > 非 PENDING 状态审批抛错 1ms - ✓ tests/unit/domain/warehouse/aggregates/transfer-order.test.ts > TransferOrder 聚合根 > shipOut() 出库流程 > PENDING → SHIPPED,设置 outTime 2ms - ✓ tests/unit/domain/warehouse/aggregates/transfer-order.test.ts > TransferOrder 聚合根 > shipOut() 出库流程 > 已 SHIPPED 状态再次出库抛错 0ms - ✓ tests/unit/domain/warehouse/aggregates/transfer-order.test.ts > TransferOrder 聚合根 > receiveIn() 入库流程 > SHIPPED → RECEIVED,设置 inTime 1ms - ✓ tests/unit/domain/warehouse/aggregates/transfer-order.test.ts > TransferOrder 聚合根 > receiveIn() 入库流程 > 非 SHIPPED 状态入库抛错 0ms - ✓ tests/unit/domain/warehouse/aggregates/transfer-order.test.ts > TransferOrder 聚合根 > cancel() 取消流程 > DRAFT → CANCELLED,发布取消事件 1ms - ✓ tests/unit/domain/warehouse/aggregates/transfer-order.test.ts > TransferOrder 聚合根 > cancel() 取消流程 > PENDING → CANCELLED 0ms - ✓ tests/unit/domain/warehouse/aggregates/transfer-order.test.ts > TransferOrder 聚合根 > cancel() 取消流程 > RECEIVED → CANCELLED 抛错(终态不可取消) 0ms - ✓ tests/unit/domain/warehouse/aggregates/transfer-order.test.ts > TransferOrder 聚合根 > 权限委托 > canEdit/canDelete 委托给 TransferStatus 1ms - ✓ tests/unit/domain/warehouse/aggregates/transfer-order.test.ts > TransferOrder 聚合根 > 领域事件管理 > getDomainEvents 返回副本(不可变) 0ms - ✓ tests/unit/domain/warehouse/aggregates/transfer-order.test.ts > TransferOrder 聚合根 > 领域事件管理 > clearDomainEvents 清空事件 1ms - ✓ tests/unit/domain/warehouse/aggregates/transfer-order.test.ts > TransferOrder 聚合根 > items 访问器返回副本 > 外部修改 items 数组不影响内部状态 1ms - ✓ tests/unit/bom-expansion.test.ts > BOM展开计算 > expandBom - 基础功能 > 应该正确展开单层BOM 39ms - ✓ tests/unit/bom-expansion.test.ts > BOM展开计算 > expandBom - 基础功能 > 应该正确展开多层BOM(半成品) 4ms - ✓ tests/unit/bom-expansion.test.ts > BOM展开计算 > expandBom - 基础功能 > 应该正确处理损耗率累加 5ms - ✓ tests/unit/bom-expansion.test.ts > BOM展开计算 > expandBom - 基础功能 > 应该检测循环引用并发出警告 2ms - ✓ tests/unit/bom-expansion.test.ts > BOM展开计算 > expandBom - 基础功能 > 应该限制最大递归深度 1ms - ✓ tests/unit/bom-expansion.test.ts > BOM展开计算 > expandBom - 基础功能 > 应该正确合并相同物料 1ms - ✓ tests/unit/bom-expansion.test.ts > BOM展开计算 > expandBom - 基础功能 > 应该抛出错误当产品不存在 5ms - ✓ tests/unit/bom-expansion.test.ts > BOM展开计算 > expandBom - 缓存功能 > 应该使用缓存提高性能 41ms - ✓ tests/unit/bom-expansion.test.ts > BOM展开计算 > expandBom - 缓存功能 > 应该能够清除缓存 11ms - ✓ tests/unit/bom-expansion.test.ts > BOM展开计算 > expandBomBatch - 批量展开 > 应该正确展开多个产品 7ms - ✓ tests/unit/bom-expansion.test.ts > BOM展开计算 > mergeExpansionResults - 合并结果 > 应该正确合并多个展开结果 2ms - ✓ tests/unit/bom-expansion.test.ts > BOM展开计算 > mergeExpansionResults - 合并结果 > 应该忽略非叶子节点 0ms - ✓ tests/unit/bom-expansion.test.ts > BOM展开计算 > 统计信息 > 应该正确计算统计信息 1ms - ✓ tests/unit/application/services/ToolManagementService.test.ts > ToolManagementService.recordUsage — 报工寿命累计 + 成本分摊 > 正常累计:used_count / remain_life / accumulated_cost / net_value 正确更新 16ms - ✓ tests/unit/application/services/ToolManagementService.test.ts > ToolManagementService.recordUsage — 报工寿命累计 + 成本分摊 > 状态转预警:used_count 达到 warning_threshold → status=4 2ms - ✓ tests/unit/application/services/ToolManagementService.test.ts > ToolManagementService.recordUsage — 报工寿命累计 + 成本分摊 > 状态转报废:remain_life <= 0 → status=5 并记录 warn 日志 4ms - ✓ tests/unit/application/services/ToolManagementService.test.ts > ToolManagementService.recordUsage — 报工寿命累计 + 成本分摊 > 刚好用完寿命:remain_life = 0 → status=5 1ms - ✓ tests/unit/application/services/ToolManagementService.test.ts > ToolManagementService.recordUsage — 报工寿命累计 + 成本分摊 > 成本分摊精度:unit_cost=0.075 * useCount=4000 = 300 1ms - ✓ tests/unit/application/services/ToolManagementService.test.ts > ToolManagementService.recordUsage — 报工寿命累计 + 成本分摊 > 多次累计:已有 accumulated_cost=500 + 新 100 = 600 1ms - ✓ tests/unit/application/services/ToolManagementService.test.ts > ToolManagementService.recordUsage — 报工寿命累计 + 成本分摊 > useCount 超过 remain_life 时抛出错误 4ms - ✓ tests/unit/application/services/ToolManagementService.test.ts > ToolManagementService.recordUsage — 报工寿命累计 + 成本分摊 > 工具不存在时抛出错误 1ms - ✓ tests/unit/application/services/ToolManagementService.test.ts > ToolManagementService.recordUsage — 报工寿命累计 + 成本分摊 > 工具状态为 1(备用)时抛出错误 2ms - ✓ tests/unit/application/services/ToolManagementService.test.ts > ToolManagementService.recordUsage — 报工寿命累计 + 成本分摊 > 工具状态为 3(维修中)时抛出错误 1ms - ✓ tests/unit/application/services/ToolManagementService.test.ts > ToolManagementService.recordUsage — 报工寿命累计 + 成本分摊 > 工具状态为 5(已报废)时抛出错误 1ms - ✓ tests/unit/application/services/ToolManagementService.test.ts > ToolManagementService.recordUsage — 报工寿命累计 + 成本分摊 > 状态 4(预警)仍可使用 1ms - ✓ tests/unit/application/services/ToolManagementService.test.ts > ToolManagementService.recordUsage — 报工寿命累计 + 成本分摊 > 使用记录快照包含 work_order_no 和 process_name 1ms - ✓ tests/unit/application/services/ToolManagementService.test.ts > ToolManagementService.completeMaintenance — 维修后成本重算 > 正常维修完成:重算 unit_cost / net_value / original_cost 2ms - ✓ tests/unit/application/services/ToolManagementService.test.ts > ToolManagementService.completeMaintenance — 维修后成本重算 > 维修后 used_count >= warning_threshold → status=4 1ms - ✓ tests/unit/application/services/ToolManagementService.test.ts > ToolManagementService.completeMaintenance — 维修后成本重算 > lifeAfter = 0 时 unit_cost = 0(避免除零) 1ms - ✓ tests/unit/application/services/ToolManagementService.test.ts > ToolManagementService.completeMaintenance — 维修后成本重算 > 维修记录不存在时抛出错误 1ms - ✓ tests/unit/application/services/ToolManagementService.test.ts > ToolManagementService.completeMaintenance — 维修后成本重算 > 工具不存在时抛出错误 1ms - ✓ tests/unit/application/services/ToolManagementService.test.ts > ToolManagementService.completeMaintenance — 维修后成本重算 > 维修费用为 0 时不增加 original_cost 1ms - ✓ tests/unit/application/services/ToolManagementService.test.ts > ToolManagementService — 网版工装(tool_type=2) 寿命与成本分摊 > 网版正常累计:used_count / remain_life / accumulated_cost / net_value 正确更新 1ms - ✓ tests/unit/application/services/ToolManagementService.test.ts > ToolManagementService — 网版工装(tool_type=2) 寿命与成本分摊 > 网版状态转预警:used_count 达到 warning_threshold → status=4 5ms - ✓ tests/unit/application/services/ToolManagementService.test.ts > ToolManagementService — 网版工装(tool_type=2) 寿命与成本分摊 > 网版状态转报废:remain_life <= 0 → status=5 并记录 warn 日志 2ms - ✓ tests/unit/application/services/ToolManagementService.test.ts > ToolManagementService — 网版工装(tool_type=2) 寿命与成本分摊 > 网版刚好用完寿命:remain_life = 0 → status=5 1ms - ✓ tests/unit/application/services/ToolManagementService.test.ts > ToolManagementService — 网版工装(tool_type=2) 寿命与成本分摊 > 网版成本分摊精度:unit_cost=0.04 * useCount=10000 = 400 1ms - ✓ tests/unit/application/services/ToolManagementService.test.ts > ToolManagementService — 网版工装(tool_type=2) 寿命与成本分摊 > 网版多次累计:已有 accumulated_cost=800 + 新 200 = 1000 1ms - ✓ tests/unit/application/services/ToolManagementService.test.ts > ToolManagementService — 网版工装(tool_type=2) 寿命与成本分摊 > 网版 useCount 超过 remain_life 时抛出错误 1ms - ✓ tests/unit/application/services/ToolManagementService.test.ts > ToolManagementService — 网版工装(tool_type=2) 寿命与成本分摊 > 网版状态为 1(备用)时抛出错误 2ms - ✓ tests/unit/application/services/ToolManagementService.test.ts > ToolManagementService — 网版工装(tool_type=2) 寿命与成本分摊 > 网版状态为 5(已报废)时抛出错误 1ms - ✓ tests/unit/application/services/ToolManagementService.test.ts > ToolManagementService — 网版工装(tool_type=2) 寿命与成本分摊 > 网版状态 4(预警)仍可使用 1ms - ✓ tests/unit/application/services/ToolManagementService.test.ts > ToolManagementService — 网版工装(tool_type=2) 寿命与成本分摊 > 网版使用记录快照包含 work_order_no 和 process_name 2ms - ✓ tests/unit/application/services/ToolManagementService.test.ts > ToolManagementService — 网版工装维修后成本重算 > 网版正常维修完成:重算 unit_cost / net_value / original_cost 2ms - ✓ tests/unit/application/services/ToolManagementService.test.ts > ToolManagementService — 网版工装维修后成本重算 > 网版维修后 used_count >= warning_threshold → status=4 1ms - ✓ tests/unit/application/services/ToolManagementService.test.ts > ToolManagementService — 网版工装维修后成本重算 > 网版 lifeAfter = 0 时 unit_cost = 0(避免除零) 1ms - ✓ tests/unit/application/services/ToolManagementService.test.ts > ToolManagementService — 网版工装维修后成本重算 > 网版维修费用为 0 时不增加 original_cost 1ms - ✓ tests/unit/application/services/ToolManagementService.test.ts > ToolManagementService — logger.info 核心分支日志验证 > recordUsage 正常流程输出寿命成本计算日志和报工记录日志 2ms - ✓ tests/unit/application/services/ToolManagementService.test.ts > ToolManagementService — logger.info 核心分支日志验证 > recordUsage 状态转报废时输出报废日志 1ms - ✓ tests/unit/application/services/ToolManagementService.test.ts > ToolManagementService — logger.info 核心分支日志验证 > recordUsage 状态转预警时输出预警日志 1ms - ✓ tests/unit/application/services/ToolManagementService.test.ts > ToolManagementService — logger.info 核心分支日志验证 > recordUsage 工具不存在时输出 warn 日志和 error 日志 4ms - ✓ tests/unit/application/services/ToolManagementService.test.ts > ToolManagementService — logger.info 核心分支日志验证 > completeMaintenance 正常流程输出成本重算和维修完成日志 1ms - ✓ tests/unit/application/services/ToolManagementService.test.ts > ToolManagementService — logger.info 核心分支日志验证 > completeMaintenance 维修后达预警阈值输出 status=4 日志 1ms - ✓ tests/unit/application/services/ToolManagementService.test.ts > ToolManagementService — SQL 参数索引一致性防回归 > UPDATE dcprint_tool: ? 占位符数 === params.length 1ms - ✓ tests/unit/application/services/ToolManagementService.test.ts > ToolManagementService — SQL 参数索引一致性防回归 > INSERT dcprint_tool_usage: ? 占位符数 === params.length 2ms - ✓ tests/unit/application/services/SampleOrderApplicationService.test.ts > SampleOrderApplicationService.createOrder — 事务行为 > 正常创建:事务内 save + persistEvents,返回 id 与 orderNo 13ms - ✓ tests/unit/application/services/SampleOrderApplicationService.test.ts > SampleOrderApplicationService.createOrder — 事务行为 > save 抛错时:不调用 persistEvents,异常向上抛出 5ms - ✓ tests/unit/application/services/SampleOrderApplicationService.test.ts > SampleOrderApplicationService.submitOrder — 状态流转与事务 > 正常提交:DRAFT→PENDING,事务内 update + persistEvents 3ms - ✓ tests/unit/application/services/SampleOrderApplicationService.test.ts > SampleOrderApplicationService.submitOrder — 状态流转与事务 > 非 DRAFT 状态不可提交:抛 DomainError,不调用 update 2ms - ✓ tests/unit/application/services/SampleOrderApplicationService.test.ts > SampleOrderApplicationService.convertOrder — 转大货事务行为 > 正常转大货:CONFIRMED→CONVERTED,事件 payload 含 salesOrderId 2ms - ✓ tests/unit/application/services/SampleOrderApplicationService.test.ts > SampleOrderApplicationService.convertOrder — 转大货事务行为 > 非 CONFIRMED 状态不可转大货:抛 DomainError 1ms - ✓ tests/unit/application/services/SampleOrderApplicationService.test.ts > SampleOrderApplicationService.cancelOrder — 作废与 reason 透传 > 正常作废:reason 透传到事件 payload 1ms - ✓ tests/unit/application/services/SampleOrderApplicationService.test.ts > SampleOrderApplicationService.cancelOrder — 作废与 reason 透传 > 已作废状态不可重复作废:抛 DomainError 1ms - ✓ tests/unit/application/services/SampleOrderApplicationService.test.ts > SampleOrderApplicationService.createSalesOrderFromSample — T305 自动创建销售订单 > 正常创建:生成销售订单号、查询物料、写入 sal_order + sal_order_detail 7ms - ✓ tests/unit/application/services/SampleOrderApplicationService.test.ts > SampleOrderApplicationService.createSalesOrderFromSample — T305 自动创建销售订单 > 无 materialNo 时:materialId=0,不查询 inv_material 2ms - ✓ tests/unit/application/services/SampleOrderApplicationService.test.ts > SampleOrderApplicationService.createSalesOrderFromSample — T305 自动创建销售订单 > 打样单不存在时抛 NotFoundError 1ms - ✓ tests/unit/application/services/SampleOrderApplicationService.test.ts > SampleOrderApplicationService.createSalesOrderFromSample — T305 自动创建销售订单 > 正常流转:CONFIRMED 打样单创建销售订单后返回 salesOrderId,打样单状态不变 1ms - ✓ tests/unit/application/services/SampleOrderApplicationService.test.ts > SampleOrderApplicationService.createSalesOrderFromSample — T305 自动创建销售订单 > 异常回滚:INSERT sal_order 失败时,错误向上抛出,打样单状态不变 3ms - ✓ tests/unit/application/services/SampleOrderApplicationService.test.ts > SampleOrderApplicationService.createSalesOrderFromSample — T305 自动创建销售订单 > 幂等性:已有 salesOrderId 时直接返回,不重复创建 1ms - ✓ tests/unit/application/services/SampleOrderApplicationService.test.ts > SampleOrderApplicationService.createSalesOrderFromSample — T305 自动创建销售订单 > 订单号生成:格式为 SO + YYYYMMDD + 4 位序号,序号基于当日计数递增 2ms - ✓ tests/unit/application/services/SampleOrderApplicationService.test.ts > SampleOrderApplicationService.createSalesOrderFromSample — T305 自动创建销售订单 > ER_DUP_ENTRY 冲突时重试:序号递增 +1,第二次成功 3ms - ✓ tests/unit/application/services/SampleOrderApplicationService.test.ts > SampleOrderApplicationService.createSalesOrderFromSample — T305 自动创建销售订单 > ER_DUP_ENTRY 重试 3 次耗尽后抛出 DomainError 3ms - ✓ tests/unit/application/services/SampleOrderApplicationService.test.ts > SampleOrderApplicationService.createSalesOrderFromSample — T305 自动创建销售订单 > 非 ER_DUP_ENTRY 错误立即抛出,不重试 2ms - ✓ tests/unit/application/services/SampleOrderApplicationService.test.ts > SampleOrderApplicationService.getOrderById — 未找到抛错 > findById 返回 null 时抛 NotFoundError 1ms - ✓ tests/unit/application/services/SampleOrderApplicationService.test.ts > SampleOrderApplicationService.getOrderById — 未找到抛错 > findById 返回 order 时正常返回 1ms - ✓ tests/unit/application/services/SampleOrderApplicationService.test.ts > SampleOrderApplicationService — 事务错误回滚 > transaction 抛错时:异常向上抛出,update 未被调用 2ms - ✓ tests/unit/application/services/SampleOrderApplicationService.test.ts > SampleOrderApplicationService — 事务错误回滚 > persistEvents 抛错时:异常向上抛出(事务回滚) 1ms - ✓ tests/unit/application/services/SampleOrderApplicationService.test.ts > SampleOrderApplicationService — 其他状态转换错误处理 > confirmOrder 在 DRAFT 状态时抛 DomainError 2ms - ✓ tests/unit/application/services/SampleOrderApplicationService.test.ts > SampleOrderApplicationService — 其他状态转换错误处理 > completeOrder 在 DRAFT 状态时抛 DomainError 2ms - ✓ tests/unit/application/services/SampleOrderApplicationService.test.ts > SampleOrderApplicationService — 其他状态转换错误处理 > startProduction 正常流程:PENDING→IN_PROGRESS,事件持久化 1ms - ✓ tests/unit/fifo-allocation.test.ts > FIFO分配算法 > allocateFIFO - 基础分配 > 应该按FIFO顺序分配库存 17ms - ✓ tests/unit/fifo-allocation.test.ts > FIFO分配算法 > allocateFIFO - 基础分配 > 应该优先分配已开封的批次 2ms - ✓ tests/unit/fifo-allocation.test.ts > FIFO分配算法 > allocateFIFO - 基础分配 > 应该优先分配即将过期的批次 3ms - ✓ tests/unit/fifo-allocation.test.ts > FIFO分配算法 > allocateFIFO - 基础分配 > 应该正确处理库存不足的情况 2ms - ✓ tests/unit/fifo-allocation.test.ts > FIFO分配算法 > allocateFIFO - 基础分配 > 应该正确处理无库存的情况 1ms - ✓ tests/unit/fifo-allocation.test.ts > FIFO分配算法 > allocateFIFO - 基础分配 > 应该排除过期的批次(默认行为) 1ms - ✓ tests/unit/fifo-allocation.test.ts > FIFO分配算法 > allocateFIFO - 基础分配 > 应该允许分配过期批次(当allowExpired=true) 1ms - ✓ tests/unit/fifo-allocation.test.ts > FIFO分配算法 > allocateFIFO - 基础分配 > 应该排除指定的批次 1ms - ✓ tests/unit/fifo-allocation.test.ts > FIFO分配算法 > allocateFIFO - 精度计算 > 应该正确处理小数数量 2ms - ✓ tests/unit/fifo-allocation.test.ts > FIFO分配算法 > allocateFIFO - 精度计算 > 应该避免浮点数精度问题 1ms - ✓ tests/unit/fifo-allocation.test.ts > FIFO分配算法 > checkShortageAndWarn - 缺货预警 > 应该返回缺货预警信息 1ms - ✓ tests/unit/fifo-allocation.test.ts > FIFO分配算法 > checkShortageAndWarn - 缺货预警 > 应该返回null当没有缺货 0ms - ✓ tests/unit/fifo-allocation.test.ts > FIFO分配算法 > checkShortageAndWarn - 缺货预警 > 应该返回null当物料不存在 0ms - ✓ tests/unit/fifo-allocation.test.ts > FIFO分配算法 > executeFIFOWithTransaction - 事务执行 > 应该成功执行多个物料的FIFO分配 3ms - ✓ tests/unit/fifo-allocation.test.ts > FIFO分配算法 > executeFIFOWithTransaction - 事务执行 > 应该处理分配失败的情况 8ms - ✓ tests/unit/fifo-allocation.test.ts > FIFO分配算法 > 乐观锁重试机制 > 应该在版本冲突时重试 1ms - ✓ tests/unit/fifo-allocation.test.ts > FIFO分配算法 > 边界情况 > 应该处理需求数量为0的情况 1ms - ✓ tests/unit/fifo-allocation.test.ts > FIFO分配算法 > 边界情况 > 应该处理单价为null的情况 1ms - ✓ tests/integration/inbound-from-po.test.ts > 入库 from-PO 业务集成(部分收货 / 超收拒绝 / 库存联动) > IT-IN-002 部分收货:两次入库累计回写 PO received_qty 与状态 75ms - ✓ tests/unit/application/services/SampleProcessCardService.test.ts > SampleProcessCardService.createCard > 正常创建:生成编号、计算成本、写入主表+明细 9ms - ✓ tests/unit/application/services/SampleProcessCardService.test.ts > SampleProcessCardService.createCard > 已有 sample_no 时不重新生成编号 2ms - ✓ tests/unit/application/services/SampleProcessCardService.test.ts > SampleProcessCardService.createCard > logger.error 在异常时输出 phase 信息 8ms - ✓ tests/unit/application/services/SampleProcessCardService.test.ts > SampleProcessCardService.submitCard > 正常提交:状态 1→2,生成打样工单并回写 4ms - ✓ tests/unit/application/services/SampleProcessCardService.test.ts > SampleProcessCardService.submitCard > 工艺卡不存在时抛出错误 1ms - ✓ tests/unit/application/services/SampleProcessCardService.test.ts > SampleProcessCardService.submitCard > 状态非草稿时抛出错误 1ms - ✓ tests/unit/application/services/SampleProcessCardService.test.ts > SampleProcessCardService.confirmCard > 正常确认:状态 2→3 2ms - ✓ tests/unit/application/services/SampleProcessCardService.test.ts > SampleProcessCardService.confirmCard > 工艺卡不存在时抛出错误 1ms - ✓ tests/unit/application/services/SampleProcessCardService.test.ts > SampleProcessCardService.confirmCard > 状态非打样中时抛出错误 1ms - ✓ tests/unit/application/services/SampleProcessCardService.test.ts > SampleProcessCardService.cancelCard > 正常作废:任意非已作废状态 → 4 1ms - ✓ tests/unit/application/services/SampleProcessCardService.test.ts > SampleProcessCardService.cancelCard > 已作废状态重复操作时抛出错误 1ms - ✓ tests/unit/application/services/SampleProcessCardService.test.ts > SampleProcessCardService.generateQuote > 正常生成报价:成本×加价率×数量 = 报价 2ms - ✓ tests/unit/application/services/SampleProcessCardService.test.ts > SampleProcessCardService.generateQuote > 工艺卡状态非已确认时抛出错误 1ms - ✓ tests/unit/application/services/SampleProcessCardService.test.ts > SampleProcessCardService.generateQuote > 已生成过报价单时抛出错误 1ms - ✓ tests/unit/application/services/SampleProcessCardService.test.ts > SampleProcessCardService.generateQuote > 工艺卡不存在时抛出错误 2ms - ✓ tests/unit/application/services/SampleProcessCardService.test.ts > SampleProcessCardService.generateQuote > 使用默认 markupRate=30 和 quantity=1 1ms - ✓ tests/unit/application/services/SampleProcessCardService.test.ts > SampleProcessCardService.convertToFormalWorkOrder > 正常转换:生成正式工单 + BOM 明细 + 回写 5ms - ✓ tests/unit/application/services/SampleProcessCardService.test.ts > SampleProcessCardService.convertToFormalWorkOrder > 工艺卡状态非已确认时抛出错误 1ms - ✓ tests/unit/application/services/SampleProcessCardService.test.ts > SampleProcessCardService.convertToFormalWorkOrder > 已转过正式工单时抛出错误 1ms - ✓ tests/unit/application/services/SampleProcessCardService.test.ts > SampleProcessCardService.convertToFormalWorkOrder > 使用默认 planQty=1000 2ms - ✓ tests/unit/application/services/SampleProcessCardService.test.ts > SampleProcessCardService.convertToFormalWorkOrder > 无物料明细时跳过 BOM 写入 1ms - ✓ tests/unit/application/services/SampleProcessCardService.test.ts > SampleProcessCardService.getCostVariance > 无正式工单时:实际 = 预估,差异为 0 1ms - ✓ tests/unit/application/services/SampleProcessCardService.test.ts > SampleProcessCardService.getCostVariance > 有正式工单时:实际物料成本从 BOM 汇总 1ms - ✓ tests/unit/application/services/SampleProcessCardService.test.ts > SampleProcessCardService.getCostVariance > 工艺卡不存在时抛出错误 1ms - ✓ tests/unit/application/services/SampleProcessCardService.test.ts > SampleProcessCardService.duplicateVersion > 正常复制:V1.0 → V1.1,复制物料和工序 2ms - ✓ tests/unit/application/services/SampleProcessCardService.test.ts > SampleProcessCardService.duplicateVersion > 源工艺卡不存在时抛出错误 1ms - ✓ tests/unit/application/services/SampleProcessCardService.test.ts > SampleProcessCardService.deleteCard > 正常删除草稿工艺卡 1ms - ✓ tests/unit/application/services/SampleProcessCardService.test.ts > SampleProcessCardService.deleteCard > 非草稿状态时抛出错误 2ms - ✓ tests/unit/application/services/SampleProcessCardService.test.ts > SampleProcessCardService.deleteCard > 工艺卡不存在时抛出错误 1ms - ✓ tests/unit/application/services/SampleProcessCardService.test.ts > SampleProcessCardService.previewCost > 正确计算物料+人工+工装成本 1ms - ✓ tests/unit/application/services/SampleProcessCardService.test.ts > SampleProcessCardService.previewCost > 无工装时 toolCost = 0 1ms - ✓ tests/unit/application/services/SampleProcessCardService.test.ts > SampleProcessCardService — 全链路集成测试 > 完整流程:创建→提交→确认→报价→转工单→成本差异 3ms -[2026-08-27 11:31:40.282 +0800] INFO: 采购订单聚合根重建完成 - module: "purchase-inbound" - action: "sync" - inboundId: 1 - poId: 150 -[2026-08-27 11:31:40.291 +0800] INFO: 采购订单已收量与状态更新完成 - module: "purchase-inbound" - action: "sync" - inboundId: 1 - poId: 150 -[2026-08-27 11:31:40.291 +0800] INFO: Purchase order synced from inbound approval - poId: 150 - inboundNo: "IN_FP_1787801500264_1" - newStatus: "partially_received" - receivedQuantity: 50 -[2026-08-27 11:31:40.316 +0800] INFO: 采购订单聚合根重建完成 - module: "purchase-inbound" - action: "sync" - inboundId: 2 - poId: 150 -[2026-08-27 11:31:40.319 +0800] INFO: 采购订单已收量与状态更新完成 - module: "purchase-inbound" - action: "sync" - inboundId: 2 - poId: 150 -[2026-08-27 11:31:40.319 +0800] INFO: Purchase order synced from inbound approval - poId: 150 - inboundNo: "IN_FP_1787801500311_2" - newStatus: "partially_received" - receivedQuantity: 110 -[2026-08-27 11:31:40.344 +0800] INFO: 采购订单聚合根重建完成 - module: "purchase-inbound" - action: "sync" - inboundId: 3 - poId: 150 -[2026-08-27 11:31:40.346 +0800] ERROR: PurchaseInboundSync 失败 [phase=receive] - module: "purchase-inbound" - action: "sync" - inboundId: 3 - poId: 150 -[2026-08-27 11:31:40.364 +0800] DEBUG: Inventory cost recalculated (inbound) - inventoryId: 410 - inboundQty: 40 - inboundUnitPrice: 10 - oldCostPrice: 0 - newCostPrice: 5 - newTotalAmount: 400 -[2026-08-27 11:31:40.388 +0800] INFO: Inventory synced for inbound order - orderNo: "IN_FP_1787801500351_4" - itemCount: 1 - ✓ tests/integration/inbound-from-po.test.ts > 入库 from-PO 业务集成(部分收货 / 超收拒绝 / 库存联动) > ERR-IN / IT-IN-003 超收拒绝:累计超上限抛错且 PO 状态回滚不变 13ms - ✓ tests/integration/inbound-from-po.test.ts > 入库 from-PO 业务集成(部分收货 / 超收拒绝 / 库存联动) > IT-IN 库存联动:from-PO 入库在守卫通过后会写入库存 42ms - ✓ tests/unit/app/api/warehouse/production-inbound.test.ts > production-inbound POST — 创建入库单 > 正常创建:无工单关联,写入主表+明细 19ms - ✓ tests/unit/app/api/warehouse/production-inbound.test.ts > production-inbound POST — 创建入库单 > 有工单关联且工单已审核:写入工单号 4ms - ✓ tests/unit/app/api/warehouse/production-inbound.test.ts > production-inbound POST — 创建入库单 > 工单不存在时抛错返回 500 3ms - ✓ tests/unit/app/api/warehouse/production-inbound.test.ts > production-inbound POST — 创建入库单 > 工单未审核(status<20)时抛错 3ms - ✓ tests/unit/app/api/warehouse/production-inbound.test.ts > production-inbound POST — 创建入库单 > 工单已关闭(status>=90)时抛错 4ms - ✓ tests/unit/app/api/warehouse/production-inbound.test.ts > production-inbound POST — 创建入库单 > 缺少 warehouse_id 返回 400 3ms - ✓ tests/unit/app/api/warehouse/production-inbound.test.ts > production-inbound POST — 创建入库单 > items 为空数组返回 400 2ms - ✓ tests/unit/app/api/warehouse/production-inbound.test.ts > production-inbound PUT action=post — 入库过账 + FinishOrderApprovedEvent 发布 > 正常过账:库存累加、工单完成、FinishOrderApprovedEvent 发布 8ms - ✓ tests/unit/app/api/warehouse/production-inbound.test.ts > production-inbound PUT action=post — 入库过账 + FinishOrderApprovedEvent 发布 > 入库单不存在时抛错且不发布事件 3ms - ✓ tests/unit/app/api/warehouse/production-inbound.test.ts > production-inbound PUT action=post — 入库过账 + FinishOrderApprovedEvent 发布 > 入库单已完成(status>=3)时抛错:不能重复过账 3ms - ✓ tests/unit/app/api/warehouse/production-inbound.test.ts > production-inbound PUT action=post — 入库过账 + FinishOrderApprovedEvent 发布 > 质检不合格时抛错:不能入库 3ms - ✓ tests/unit/app/api/warehouse/production-inbound.test.ts > production-inbound PUT action=qc — 质检流程 > 质检全部通过:qc_status=pass,不生成不合格记录 2ms - ✓ tests/unit/app/api/warehouse/production-inbound.test.ts > production-inbound PUT action=qc — 质检流程 > 质检存在不合格:qc_status=fail,自动生成 qc_unqualified 记录 4ms - ✓ tests/unit/app/api/warehouse/production-inbound.test.ts > production-inbound PUT — 通用字段更新 > 同时更新 status/qc_status/remark 三个字段 3ms - ✓ tests/unit/app/api/warehouse/production-inbound.test.ts > production-inbound DELETE — 删除入库单 > 正常删除草稿入库单 2ms - ✓ tests/unit/app/api/warehouse/production-inbound.test.ts > production-inbound DELETE — 删除入库单 > 已完成的入库单(status>=3)不能删除 1ms - ✓ tests/unit/app/api/warehouse/production-inbound.test.ts > production-inbound DELETE — 删除入库单 > 入库单不存在时返回 404 1ms - ✓ tests/unit/application/api/alert-push.test.ts > alert-push POST mark_read — IN (?) 占位符展开 > 多个 ID 时生成对应数量的占位符(核心修复验证) 22ms - ✓ tests/unit/application/api/alert-push.test.ts > alert-push POST mark_read — IN (?) 占位符展开 > 单个 ID 时占位符为 1 个 3ms - ✓ tests/unit/application/api/alert-push.test.ts > alert-push POST mark_read — IN (?) 占位符展开 > 空数组时占位符为 0(IN () 不会匹配任何行,安全降级) 2ms - ✓ tests/unit/application/api/alert-push.test.ts > alert-push POST mark_read — IN (?) 占位符展开 > 10 个 ID 展开正确 3ms - ✓ tests/unit/application/api/alert-push.test.ts > alert-push POST mark_read — IN (?) 占位符展开 > 字符串类型 ID 也能正确展开 3ms - ✓ tests/unit/application/api/alert-push.test.ts > alert-push POST mark_read — 参数校验 > 缺少 notification_ids 返回 400 3ms - ✓ tests/unit/application/api/alert-push.test.ts > alert-push POST mark_read — 参数校验 > notification_ids 为 null 返回 400 2ms - ✓ tests/unit/application/api/alert-push.test.ts > alert-push POST mark_read — 参数校验 > notification_ids 非数组(字符串)返回 400 2ms - ✓ tests/unit/application/api/alert-push.test.ts > alert-push POST mark_read — 回归保护 > 修复前 Bug 模式验证:join(',') 不会产生多占位符 3ms - ✓ tests/unit/application/api/alert-push.test.ts > alert-push POST mark_read — 回归保护 > UPDATE 语句包含 is_read 和 read_time 字段 2ms - ✓ tests/unit/domain/sales/aggregates/return-order.test.ts > ReturnOrder 聚合根 > create() 工厂方法 > 合法参数创建成功 9ms - ✓ tests/unit/domain/sales/aggregates/return-order.test.ts > ReturnOrder 聚合根 > create() 工厂方法 > 自动计算 totalAmount 等于明细金额之和 1ms - ✓ tests/unit/domain/sales/aggregates/return-order.test.ts > ReturnOrder 聚合根 > create() 工厂方法 > orderId 缺失抛错 2ms - ✓ tests/unit/domain/sales/aggregates/return-order.test.ts > ReturnOrder 聚合根 > create() 工厂方法 > customerId 缺失抛错 1ms - ✓ tests/unit/domain/sales/aggregates/return-order.test.ts > ReturnOrder 聚合根 > create() 工厂方法 > warehouseId 缺失抛错 1ms - ✓ tests/unit/domain/sales/aggregates/return-order.test.ts > ReturnOrder 聚合根 > create() 工厂方法 > lines 为空抛错 1ms - ✓ tests/unit/domain/sales/aggregates/return-order.test.ts > ReturnOrder 聚合根 > create() 工厂方法 > reason 为空抛错 1ms - ✓ tests/unit/domain/sales/aggregates/return-order.test.ts > ReturnOrder 聚合根 > create() 工厂方法 > reason 仅空白字符抛错 1ms - ✓ tests/unit/domain/sales/aggregates/return-order.test.ts > ReturnOrder 聚合根 > reconstitute() 工厂方法 > 从 DB 重建 pending 状态 2ms - ✓ tests/unit/domain/sales/aggregates/return-order.test.ts > ReturnOrder 聚合根 > reconstitute() 工厂方法 > 从 DB 重建 completed 状态含关联单号 1ms - ✓ tests/unit/domain/sales/aggregates/return-order.test.ts > ReturnOrder 聚合根 > approve() 方法 > pending → approved 成功 1ms - ✓ tests/unit/domain/sales/aggregates/return-order.test.ts > ReturnOrder 聚合根 > approve() 方法 > approveBy 缺失抛错 1ms - ✓ tests/unit/domain/sales/aggregates/return-order.test.ts > ReturnOrder 聚合根 > approve() 方法 > 已 approved 再次 approve 抛错 1ms - ✓ tests/unit/domain/sales/aggregates/return-order.test.ts > ReturnOrder 聚合根 > approve() 方法 > cancelled 的 approve 抛错 1ms - ✓ tests/unit/domain/sales/aggregates/return-order.test.ts > ReturnOrder 聚合根 > complete() 方法 > approved → completed 成功(回调正常) 3ms - ✓ tests/unit/domain/sales/aggregates/return-order.test.ts > ReturnOrder 聚合根 > complete() 方法 > complete 设置 inboundOrderId/No 和 receivableId/No 1ms - ✓ tests/unit/domain/sales/aggregates/return-order.test.ts > ReturnOrder 聚合根 > complete() 方法 > complete 调用 inboundCallback 传入正确参数 4ms - ✓ tests/unit/domain/sales/aggregates/return-order.test.ts > ReturnOrder 聚合根 > complete() 方法 > complete 调用 receivableCallback 传入正确参数 1ms - ✓ tests/unit/domain/sales/aggregates/return-order.test.ts > ReturnOrder 聚合根 > complete() 方法 > inboundCallback 返回空 inboundOrderId 抛错 1ms - ✓ tests/unit/domain/sales/aggregates/return-order.test.ts > ReturnOrder 聚合根 > complete() 方法 > receivableCallback 返回空 receivableId 抛错 1ms - ✓ tests/unit/domain/sales/aggregates/return-order.test.ts > ReturnOrder 聚合根 > complete() 方法 > completeBy 缺失抛错 1ms - ✓ tests/unit/domain/sales/aggregates/return-order.test.ts > ReturnOrder 聚合根 > complete() 方法 > pending 的 complete 抛错 1ms - ✓ tests/unit/domain/sales/aggregates/return-order.test.ts > ReturnOrder 聚合根 > complete() 方法 > completed 的 complete 抛错 1ms - ✓ tests/unit/domain/sales/aggregates/return-order.test.ts > ReturnOrder 聚合根 > cancel() 方法 > pending → cancelled 成功 0ms - ✓ tests/unit/domain/sales/aggregates/return-order.test.ts > ReturnOrder 聚合根 > cancel() 方法 > approved → cancelled 成功 0ms - ✓ tests/unit/domain/sales/aggregates/return-order.test.ts > ReturnOrder 聚合根 > cancel() 方法 > completed 的 cancel 抛错 1ms - ✓ tests/unit/domain/sales/aggregates/return-order.test.ts > ReturnOrder 聚合根 > cancel() 方法 > 已 cancelled 再次 cancel 抛错 1ms - ✓ tests/unit/domain/sales/aggregates/return-order.test.ts > ReturnOrder 聚合根 > 状态判断方法 > canEdit/canDelete 仅 pending 返回 true 1ms - ✓ tests/unit/domain/sales/aggregates/return-order.test.ts > ReturnOrder 聚合根 > 状态判断方法 > validateAgainstShippedQuantities 退货量 ≤ 已发货量 时通过(T204) 1ms - ✓ tests/unit/domain/sales/aggregates/return-order.test.ts > ReturnOrder 聚合根 > 状态判断方法 > validateAgainstShippedQuantities 退货量超过已发货量时抛错(T204) 1ms - ✓ tests/unit/domain/sales/aggregates/return-order.test.ts > ReturnOrder 聚合根 > 状态判断方法 > validateAgainstShippedQuantities 扣减已退数量后校验 1ms - ✓ tests/unit/domain/sales/aggregates/return-order.test.ts > ReturnOrder 聚合根 > 状态判断方法 > validateAgainstShippedQuantities 物料不在 Map 中视为 0 抛错 0ms - ✓ tests/unit/domain/sales/aggregates/return-order.test.ts > ReturnOrder 聚合根 > 状态判断方法 > validateAgainstShippedQuantities 多物料混合校验 0ms - ✓ tests/unit/domain/sales/aggregates/return-order.test.ts > ReturnOrder 聚合根 > 状态判断方法 > isTerminal 在 completed/cancelled 返回 true 1ms - ✓ tests/unit/domain/sales/aggregates/return-order.test.ts > ReturnOrder 聚合根 > 状态判断方法 > 状态 label 正确 2ms - ✓ tests/unit/domain/sales/aggregates/return-order.test.ts > ReturnOrder 聚合根 > 事件发布 > create 发布 ReturnOrderCreatedEvent 2ms - ✓ tests/unit/domain/sales/aggregates/return-order.test.ts > ReturnOrder 聚合根 > 事件发布 > approve 发布 ReturnOrderApprovedEvent 1ms - ✓ tests/unit/domain/sales/aggregates/return-order.test.ts > ReturnOrder 聚合根 > 事件发布 > complete 发布 ReturnOrderCompletedEvent 含关联单号 1ms - ✓ tests/unit/domain/sales/aggregates/return-order.test.ts > ReturnOrder 聚合根 > 事件发布 > cancel 发布 ReturnOrderCancelledEvent 1ms - ✓ tests/unit/domain/sales/aggregates/return-order.test.ts > ReturnOrder 聚合根 > 事件发布 > clearDomainEvents 清空事件 1ms - ✓ tests/unit/domain/sales/aggregates/return-order.test.ts > ReturnOrder 聚合根 > lines 防御性拷贝 > 外部修改 lines 不影响聚合内部状态 1ms - ✓ tests/unit/domain/sales/aggregates/return-order.test.ts > ReturnOrder 聚合根 > 浮点精度处理 > 明细金额四舍五入到 2 位小数 1ms -[2026-08-27 11:31:43.507 +0800] DEBUG: Inventory cost recalculated (rollback) - inventoryId: 412 - rollbackQty: 50 - rollbackUnitPrice: 10 - oldCostPrice: 0 - newCostPrice: 0 - newTotalAmount: 0 -[2026-08-27 11:31:43.536 +0800] INFO: Inventory rolled back for unapproved inbound order - orderNo: "IN_RB_1787801503458" -[2026-08-27 11:31:43.554 +0800] WARN: Inbound order not found for rollback - inboundId: 99999999 -[2026-08-27 11:31:43.556 +0800] INFO: Inventory rolled back for unapproved inbound order - orderNo: "IN_RB_MISSING" - ✓ tests/integration/inbound-unapprove-rollback.test.ts > 入库反审核回滚 (unapprove → 库存/批次/流水回退) > IT-unapprove-1 库存数量回到审核前(0) 59ms - ✓ tests/integration/inbound-unapprove-rollback.test.ts > 入库反审核回滚 (unapprove → 库存/批次/流水回退) > IT-unapprove-2 批次归零后软删 4ms - ✓ tests/integration/inbound-unapprove-rollback.test.ts > 入库反审核回滚 (unapprove → 库存/批次/流水回退) > IT-unapprove-3 生成反向(return)流水 3ms - ✓ tests/integration/inbound-unapprove-rollback.test.ts > 入库反审核回滚 (unapprove → 库存/批次/流水回退) > IT-unapprove-4 入库单不存在时不抛错、安全跳过 8ms - ✓ tests/unit/lib/lodop.test.ts > lodop > getLodop > should return null when CLODOP is not available 22ms - ✓ tests/unit/lib/lodop.test.ts > lodop > getLodop > should return CLODOP instance when available 3ms - ✓ tests/unit/lib/lodop.test.ts > lodop > printLabel with CLODOP available > should return success and call all Lodop APIs 10ms - ✓ tests/unit/lib/lodop.test.ts > lodop > printLabel with CLODOP available > should print without specification 3ms - ✓ tests/unit/lib/lodop.test.ts > lodop > printZPL with CLODOP available > should send ZPL content with printer name 3ms - ✓ tests/unit/lib/lodop.test.ts > lodop > printZPL with CLODOP available > should send ZPL without printer name 3ms - ✓ tests/unit/lib/lodop.test.ts > lodop > checkLodopStatus with CLODOP available > should return printer list and version 3ms - ✓ tests/unit/warehouse-core.test.ts > warehouse-core > planFIFOBatches > FIFO 启用:余料优先排序分配 11ms - ✓ tests/unit/warehouse-core.test.ts > warehouse-core > planFIFOBatches > FIFO 禁用:按入库时间排序 6ms - ✓ tests/unit/warehouse-core.test.ts > warehouse-core > planFIFOBatches > 带 warehouseId 时 SQL 包含仓库过滤 3ms - ✓ tests/unit/warehouse-core.test.ts > warehouse-core > planFIFOBatches > 库存不足时返回部分分配结果 1ms - ✓ tests/unit/warehouse-core.test.ts > warehouse-core > planFIFOBatches > 无可用批次时返回失败 1ms - ✓ tests/unit/warehouse-core.test.ts > warehouse-core > planFIFOBatches > 查询抛错时返回失败 1ms - ✓ tests/unit/warehouse-core.test.ts > warehouse-core > checkWholeMaterial > 二维码不存在时返回 isWhole=false 1ms - ✓ tests/unit/warehouse-core.test.ts > warehouse-core > checkWholeMaterial > 整料且禁止直接领用时返回 isWhole=true 1ms - ✓ tests/unit/warehouse-core.test.ts > warehouse-core > checkWholeMaterial > 允许整料直接领用时返回 isWhole=false 2ms - ✓ tests/unit/warehouse-core.test.ts > warehouse-core > checkWholeMaterial > 小料(split_flag=1)允许直接领用 1ms - ✓ tests/unit/warehouse-core.test.ts > warehouse-core > splitMaterial > 整料拆分成功生成小料和余料 2ms - ✓ tests/unit/warehouse-core.test.ts > warehouse-core > splitMaterial > 无余料时不创建余料批次 1ms - ✓ tests/unit/warehouse-core.test.ts > warehouse-core > splitMaterial > 整料不存在时抛错 1ms - ✓ tests/unit/warehouse-core.test.ts > warehouse-core > splitMaterial > 物料记录不存在时抛错 1ms - ✓ tests/unit/warehouse-core.test.ts > warehouse-core > splitMaterial > 数量不足按标准拆分时抛错 1ms - ✓ tests/unit/warehouse-core.test.ts > warehouse-core > splitMaterial > 未知 material_type 使用默认拆分标准 1ms - ✓ tests/unit/warehouse-core.test.ts > warehouse-core > splitMaterial > 自定义 splitQuantity 时使用传入值 1ms - ✓ tests/unit/warehouse-core.test.ts > warehouse-core > splitMaterial > 自定义 splitQuantity <= 0 时抛错 1ms - ✓ tests/unit/warehouse-core.test.ts > warehouse-core > enforceFIFO > FIFO 禁用时直接通过 1ms - ✓ tests/unit/warehouse-core.test.ts > warehouse-core > enforceFIFO > 批次与推荐批次一致时通过 1ms - ✓ tests/unit/warehouse-core.test.ts > warehouse-core > enforceFIFO > 批次与推荐批次不一致时需要审批 1ms - ✓ tests/unit/warehouse-core.test.ts > warehouse-core > enforceFIFO > 无可用批次时返回失败 0ms - ✓ tests/unit/warehouse-core.test.ts > warehouse-core > logFIFOOverride > 记录 FIFO 异常并返回 insertId 2ms - ✓ tests/unit/warehouse-core.test.ts > warehouse-core > logFIFOOverride > 缺省参数时使用 null 1ms - ✓ tests/unit/warehouse-core.test.ts > warehouse-core > queryMaterialBatches > 不带 warehouseId 查询全部 2ms - ✓ tests/unit/warehouse-core.test.ts > warehouse-core > queryMaterialBatches > 带 warehouseId 时加入仓库过滤 1ms - ✓ tests/unit/warehouse-core.test.ts > warehouse-core > queryExpiringMaterials > 返回即将过期物料列表 1ms - ✓ tests/unit/warehouse-core.test.ts > warehouse-core > queryExpiringMaterials > 使用默认 30 天 1ms - ✓ tests/unit/warehouse-core.test.ts > warehouse-core > queryObsoleteMaterials > 返回呆滞料列表 5ms - ✓ tests/unit/warehouse-core.test.ts > warehouse-core > queryObsoleteMaterials > 使用默认 90 天 1ms - ✓ tests/unit/domain/purchase/aggregates/purchase-order.test.ts > PurchaseOrder Aggregate > create() > 应成功创建草稿状态采购单 10ms - ✓ tests/unit/domain/purchase/aggregates/purchase-order.test.ts > PurchaseOrder Aggregate > create() > 应自动计算总金额和税额 1ms - ✓ tests/unit/domain/purchase/aggregates/purchase-order.test.ts > PurchaseOrder Aggregate > create() > 应自动分配行号(若未提供) 1ms - ✓ tests/unit/domain/purchase/aggregates/purchase-order.test.ts > PurchaseOrder Aggregate > create() > 供应商为空时应抛出 DomainError 1ms - ✓ tests/unit/domain/purchase/aggregates/purchase-order.test.ts > PurchaseOrder Aggregate > create() > 明细为空时应抛出 DomainError 1ms - ✓ tests/unit/domain/purchase/aggregates/purchase-order.test.ts > PurchaseOrder Aggregate > create() > 无 id 时不应发布 PurchaseOrderCreatedEvent 1ms - ✓ tests/unit/domain/purchase/aggregates/purchase-order.test.ts > PurchaseOrder Aggregate > create() > 有 id 时应发布 PurchaseOrderCreatedEvent 1ms - ✓ tests/unit/domain/purchase/aggregates/purchase-order.test.ts > PurchaseOrder Aggregate > create() > 应支持自定义税率和默认 13% 税率 1ms - ✓ tests/unit/domain/purchase/aggregates/purchase-order.test.ts > PurchaseOrder Aggregate > reconstitute() > 应从持久化数据重建聚合根 2ms - ✓ tests/unit/domain/purchase/aggregates/purchase-order.test.ts > PurchaseOrder Aggregate > reconstitute() > 重建时不触发 domain events 1ms - ✓ tests/unit/domain/purchase/aggregates/purchase-order.test.ts > PurchaseOrder Aggregate > submit() > 草稿状态可提交 1ms - ✓ tests/unit/domain/purchase/aggregates/purchase-order.test.ts > PurchaseOrder Aggregate > submit() > 已提交状态再次提交应抛出错误 1ms - ✓ tests/unit/domain/purchase/aggregates/purchase-order.test.ts > PurchaseOrder Aggregate > approve() > 已提交状态可审核 2ms - ✓ tests/unit/domain/purchase/aggregates/purchase-order.test.ts > PurchaseOrder Aggregate > approve() > 草稿状态不允许审核 1ms - ✓ tests/unit/domain/purchase/aggregates/purchase-order.test.ts > PurchaseOrder Aggregate > approve() > 审核事件应包含明细行信息 1ms - ✓ tests/unit/domain/purchase/aggregates/purchase-order.test.ts > PurchaseOrder Aggregate > receive() > 已审核状态可部分入库 1ms - ✓ tests/unit/domain/purchase/aggregates/purchase-order.test.ts > PurchaseOrder Aggregate > receive() > 全部入库后状态应变为已完成 1ms - ✓ tests/unit/domain/purchase/aggregates/purchase-order.test.ts > PurchaseOrder Aggregate > receive() > 草稿状态不允许入库 0ms - ✓ tests/unit/domain/purchase/aggregates/purchase-order.test.ts > PurchaseOrder Aggregate > receive() > 不存在的行号应抛出错误 1ms - ✓ tests/unit/domain/purchase/aggregates/purchase-order.test.ts > PurchaseOrder Aggregate > receive() > 部分入库状态可继续入库 1ms - ✓ tests/unit/domain/purchase/aggregates/purchase-order.test.ts > PurchaseOrder Aggregate > receive() > 入库事件应包含批次和仓库信息 1ms - ✓ tests/unit/domain/purchase/aggregates/purchase-order.test.ts > PurchaseOrder Aggregate > receive() 超收容差 (overReceiptTolerance) > 超过 orderQty*(1+tolerance/100) 应抛 DomainError 0ms - ✓ tests/unit/domain/purchase/aggregates/purchase-order.test.ts > PurchaseOrder Aggregate > receive() 超收容差 (overReceiptTolerance) > 等于容差上限(105)应允许 2ms - ✓ tests/unit/domain/purchase/aggregates/purchase-order.test.ts > PurchaseOrder Aggregate > receive() 超收容差 (overReceiptTolerance) > 未指定容差时默认 0,上限=订购量 1ms - ✓ tests/unit/domain/purchase/aggregates/purchase-order.test.ts > PurchaseOrder Aggregate > reverseReceive() 回补 > 回补全部已收应回退状态到 approved 1ms - ✓ tests/unit/domain/purchase/aggregates/purchase-order.test.ts > PurchaseOrder Aggregate > reverseReceive() 回补 > 回补超过已收数应抛 DomainError 0ms - ✓ tests/unit/domain/purchase/aggregates/purchase-order.test.ts > PurchaseOrder Aggregate > reverseReceive() 回补 > 已完成单回补部分应转为部分入库 1ms - ✓ tests/unit/domain/purchase/aggregates/purchase-order.test.ts > PurchaseOrder Aggregate > reverseReceive() 回补 > 无收货的已审核单不允许回补 1ms - ✓ tests/unit/domain/purchase/aggregates/purchase-order.test.ts > PurchaseOrder Aggregate > close() > draft 状态可关闭 1ms - ✓ tests/unit/domain/purchase/aggregates/purchase-order.test.ts > PurchaseOrder Aggregate > close() > submitted 状态可关闭 0ms - ✓ tests/unit/domain/purchase/aggregates/purchase-order.test.ts > PurchaseOrder Aggregate > close() > approved 状态可关闭 0ms - ✓ tests/unit/domain/purchase/aggregates/purchase-order.test.ts > PurchaseOrder Aggregate > close() > partially_received 状态可关闭 0ms - ✓ tests/unit/domain/purchase/aggregates/purchase-order.test.ts > PurchaseOrder Aggregate > close() > 已完成状态可关闭(支持作废全链路回滚) 0ms - ✓ tests/unit/domain/purchase/aggregates/purchase-order.test.ts > PurchaseOrder Aggregate > close() > 已关闭状态不可再次关闭 0ms - ✓ tests/unit/domain/purchase/aggregates/purchase-order.test.ts > PurchaseOrder Aggregate > canEdit() > 草稿状态可编辑 0ms - ✓ tests/unit/domain/purchase/aggregates/purchase-order.test.ts > PurchaseOrder Aggregate > canEdit() > submitted 状态不可编辑 0ms - ✓ tests/unit/domain/purchase/aggregates/purchase-order.test.ts > PurchaseOrder Aggregate > canEdit() > approved 状态不可编辑 0ms - ✓ tests/unit/domain/purchase/aggregates/purchase-order.test.ts > PurchaseOrder Aggregate > canEdit() > partially_received 状态不可编辑 0ms - ✓ tests/unit/domain/purchase/aggregates/purchase-order.test.ts > PurchaseOrder Aggregate > canEdit() > completed 状态不可编辑 0ms - ✓ tests/unit/domain/purchase/aggregates/purchase-order.test.ts > PurchaseOrder Aggregate > canEdit() > closed 状态不可编辑 0ms - ✓ tests/unit/domain/purchase/aggregates/purchase-order.test.ts > PurchaseOrder Aggregate > domainEvents 生命周期 > 完整生命周期应累积所有事件 2ms - ✓ tests/unit/domain/purchase/aggregates/purchase-order.test.ts > PurchaseOrder Aggregate > domainEvents 生命周期 > clearDomainEvents 后事件列表应为空 1ms - ✓ tests/unit/domain/purchase/aggregates/purchase-order.test.ts > PurchaseOrderStatus Value Object > 状态流转 > 草稿可流转到已提交 0ms - ✓ tests/unit/domain/purchase/aggregates/purchase-order.test.ts > PurchaseOrderStatus Value Object > 状态流转 > 草稿可流转到已关闭 0ms - ✓ tests/unit/domain/purchase/aggregates/purchase-order.test.ts > PurchaseOrderStatus Value Object > 状态流转 > 已提交可流转到已审核 0ms - ✓ tests/unit/domain/purchase/aggregates/purchase-order.test.ts > PurchaseOrderStatus Value Object > 状态流转 > 已审核可流转到部分入库 0ms - ✓ tests/unit/domain/purchase/aggregates/purchase-order.test.ts > PurchaseOrderStatus Value Object > 状态流转 > 部分入库可流转到已完成 0ms - ✓ tests/unit/domain/purchase/aggregates/purchase-order.test.ts > PurchaseOrderStatus Value Object > 状态流转 > 已完成可流转到已关闭(支持作废全链路回滚) 0ms - ✓ tests/unit/domain/purchase/aggregates/purchase-order.test.ts > PurchaseOrderStatus Value Object > 状态流转 > 已作废不可流转 0ms - ✓ tests/unit/domain/purchase/aggregates/purchase-order.test.ts > PurchaseOrderStatus Value Object > 状态流转 > 草稿/已提交/已审核/部分入库可流转到已作废 1ms - ✓ tests/unit/domain/purchase/aggregates/purchase-order.test.ts > PurchaseOrderStatus Value Object > 状态流转 > 非法流转应抛出 DomainError 0ms - ✓ tests/unit/domain/purchase/aggregates/purchase-order.test.ts > PurchaseOrderStatus Value Object > 操作权限 > 草稿状态可编辑/删除/提交 0ms - ✓ tests/unit/domain/purchase/aggregates/purchase-order.test.ts > PurchaseOrderStatus Value Object > 操作权限 > 已提交状态可审核 0ms - ✓ tests/unit/domain/purchase/aggregates/purchase-order.test.ts > PurchaseOrderStatus Value Object > 操作权限 > 已审核状态可入库 0ms - ✓ tests/unit/domain/purchase/aggregates/purchase-order.test.ts > PurchaseOrderStatus Value Object > 操作权限 > 部分入库状态可入库 0ms - ✓ tests/unit/domain/purchase/aggregates/purchase-order.test.ts > PurchaseOrderStatus Value Object > 操作权限 > 已完成状态不可入库 0ms - ✓ tests/unit/domain/purchase/aggregates/purchase-order.test.ts > PurchaseOrderStatus Value Object > 数据库状态码映射 > 状态码 10 对应状态 draft 1ms - ✓ tests/unit/domain/purchase/aggregates/purchase-order.test.ts > PurchaseOrderStatus Value Object > 数据库状态码映射 > 状态码 20 对应状态 submitted 0ms - ✓ tests/unit/domain/purchase/aggregates/purchase-order.test.ts > PurchaseOrderStatus Value Object > 数据库状态码映射 > 状态码 30 对应状态 approved 0ms - ✓ tests/unit/domain/purchase/aggregates/purchase-order.test.ts > PurchaseOrderStatus Value Object > 数据库状态码映射 > 状态码 40 对应状态 partially_received 0ms - ✓ tests/unit/domain/purchase/aggregates/purchase-order.test.ts > PurchaseOrderStatus Value Object > 数据库状态码映射 > 状态码 50 对应状态 completed 0ms - ✓ tests/unit/domain/purchase/aggregates/purchase-order.test.ts > PurchaseOrderStatus Value Object > 数据库状态码映射 > 状态码 90 对应状态 closed 0ms - ✓ tests/unit/domain/purchase/aggregates/purchase-order.test.ts > PurchaseOrderStatus Value Object > 数据库状态码映射 > 状态码 99 对应状态 voided 0ms - ✓ tests/unit/domain/purchase/aggregates/purchase-order.test.ts > PurchaseOrderStatus Value Object > 数据库状态码映射 > 无效状态码应抛出错误 0ms - ✓ tests/unit/domain/purchase/aggregates/purchase-order.test.ts > PurchaseOrderStatus Value Object > equals() > 相同状态应相等 0ms - ✓ tests/unit/domain/purchase/aggregates/purchase-order.test.ts > PurchaseOrderStatus Value Object > equals() > 不同状态不应相等 0ms - ✓ tests/unit/domain/purchase/aggregates/purchase-order.test.ts > PurchaseOrderStatus Value Object > label() > 应返回中文标签 0ms - ✓ src/__tests__/integration/business-flow.test.ts > 集成测试 - 业务流程 > 流程1: 用户登录认证 > 正常登录成功 6ms - ✓ src/__tests__/integration/business-flow.test.ts > 集成测试 - 业务流程 > 流程1: 用户登录认证 > 用户名不存在 1ms - ✓ src/__tests__/integration/business-flow.test.ts > 集成测试 - 业务流程 > 流程1: 用户登录认证 > 密码错误累计锁定 1ms - ✓ src/__tests__/integration/business-flow.test.ts > 集成测试 - 业务流程 > 流程1: 用户登录认证 > 禁用账号无法登录 0ms - ✓ src/__tests__/integration/business-flow.test.ts > 集成测试 - 业务流程 > 流程1: 用户登录认证 > 锁定账号在锁定期内无法登录 1ms - ✓ src/__tests__/integration/business-flow.test.ts > 集成测试 - 业务流程 > 流程2: 库存查询与高级搜索 > 无筛选条件:返回全部库存 1ms - ✓ src/__tests__/integration/business-flow.test.ts > 集成测试 - 业务流程 > 流程2: 库存查询与高级搜索 > 关键词搜索:按物料名称模糊匹配 2ms - ✓ src/__tests__/integration/business-flow.test.ts > 集成测试 - 业务流程 > 流程2: 库存查询与高级搜索 > 关键词搜索:按物料编码模糊匹配 1ms - ✓ src/__tests__/integration/business-flow.test.ts > 集成测试 - 业务流程 > 流程2: 库存查询与高级搜索 > 仓库筛选:只返回指定仓库 2ms - ✓ src/__tests__/integration/business-flow.test.ts > 集成测试 - 业务流程 > 流程2: 库存查询与高级搜索 > 状态筛选:只返回冻结状态 1ms - ✓ src/__tests__/integration/business-flow.test.ts > 集成测试 - 业务流程 > 流程2: 库存查询与高级搜索 > 组合筛选:关键词+仓库+状态 1ms - ✓ src/__tests__/integration/business-flow.test.ts > 集成测试 - 业务流程 > 流程2: 库存查询与高级搜索 > 高级搜索:按物料编码精确筛选 2ms - ✓ src/__tests__/integration/business-flow.test.ts > 集成测试 - 业务流程 > 流程2: 库存查询与高级搜索 > 高级搜索:按批次号筛选 1ms - ✓ src/__tests__/integration/business-flow.test.ts > 集成测试 - 业务流程 > 流程2: 库存查询与高级搜索 > 高级搜索:按有效期范围筛选 1ms - ✓ src/__tests__/integration/business-flow.test.ts > 集成测试 - 业务流程 > 流程2: 库存查询与高级搜索 > 预警级别计算:零库存为critical 0ms - ✓ src/__tests__/integration/business-flow.test.ts > 集成测试 - 业务流程 > 流程2: 库存查询与高级搜索 > 预警级别计算:库存<=50%安全库存为critical 0ms - ✓ src/__tests__/integration/business-flow.test.ts > 集成测试 - 业务流程 > 流程2: 库存查询与高级搜索 > 预警级别计算:库存在50%-100%安全库存为warning 0ms - ✓ src/__tests__/integration/business-flow.test.ts > 集成测试 - 业务流程 > 流程2: 库存查询与高级搜索 > 预警级别计算:库存>安全库存为normal 0ms - ✓ src/__tests__/integration/business-flow.test.ts > 集成测试 - 业务流程 > 流程2: 库存查询与高级搜索 > 无结果搜索:返回空列表 1ms - ✓ src/__tests__/integration/business-flow.test.ts > 集成测试 - 业务流程 > 流程3: 批量操作 > 批量冻结:选择多条正常库存 1ms - ✓ src/__tests__/integration/business-flow.test.ts > 集成测试 - 业务流程 > 流程3: 批量操作 > 批量冻结:已冻结的库存跳过 0ms - ✓ src/__tests__/integration/business-flow.test.ts > 集成测试 - 业务流程 > 流程3: 批量操作 > 批量解冻:选择已冻结的库存 0ms - ✓ src/__tests__/integration/business-flow.test.ts > 集成测试 - 业务流程 > 流程3: 批量操作 > 批量解冻:未冻结的库存跳过 0ms - ✓ src/__tests__/integration/business-flow.test.ts > 集成测试 - 业务流程 > 流程3: 批量操作 > 批量冻结:零库存无法冻结 0ms - ✓ src/__tests__/integration/business-flow.test.ts > 集成测试 - 业务流程 > 流程3: 批量操作 > 批量操作:空ID列表 0ms - ✓ src/__tests__/integration/business-flow.test.ts > 集成测试 - 业务流程 > 流程3: 批量操作 > 批量操作:不存在的ID 1ms - ✓ src/__tests__/integration/business-flow.test.ts > 集成测试 - 业务流程 > 流程4: 完整用户操作流程 > 登录→查询库存→高级搜索→批量冻结→解冻 5ms - ✓ src/__tests__/integration/business-flow.test.ts > 集成测试 - 业务流程 > 流程4: 完整用户操作流程 > 登录→查询预警库存→查看详情→处理预警 1ms - ✓ src/__tests__/integration/business-flow.test.ts > 集成测试 - 业务流程 > 流程5: 错误处理与边界条件 > 并发冻结同一库存:第二次应失败 0ms - ✓ src/__tests__/integration/business-flow.test.ts > 集成测试 - 业务流程 > 流程5: 错误处理与边界条件 > 搜索特殊字符:防止SQL注入 0ms - ✓ src/__tests__/integration/business-flow.test.ts > 集成测试 - 业务流程 > 流程5: 错误处理与边界条件 > 超大分页请求:限制pageSize 0ms - ✓ src/__tests__/integration/business-flow.test.ts > 集成测试 - 业务流程 > 流程5: 错误处理与边界条件 > 无效状态值筛选:返回空结果 0ms - ✓ tests/integration/purchase-reconciliation-flow.test.ts > 采购对账模块集成测试 > 确认对账单 > 草稿状态对账单应成功确认(status 1→2) 20ms - ✓ tests/integration/purchase-reconciliation-flow.test.ts > 采购对账模块集成测试 > 确认对账单 > 已确认状态再次确认应抛出 DomainError 5ms - ✓ tests/integration/purchase-reconciliation-flow.test.ts > 采购对账模块集成测试 > 核销对账单 > 部分核销应更新余额并保持部分核销状态(status 2→3) 6ms - ✓ tests/integration/purchase-reconciliation-flow.test.ts > 采购对账模块集成测试 > 核销对账单 > 全额核销应将对账单状态转为已核销完成(status→4) 2ms - ✓ tests/integration/purchase-reconciliation-flow.test.ts > 采购对账模块集成测试 > 核销对账单 > 核销金额超过对账单余额应抛出 DomainError 1ms - ✓ tests/integration/purchase-reconciliation-flow.test.ts > 采购对账模块集成测试 > 核销对账单 > 核销金额超过应付单余额应抛出 DomainError 2ms - ✓ tests/integration/purchase-reconciliation-flow.test.ts > 采购对账模块集成测试 > 核销对账单 > 并发核销(余额已被其他事务修改)应抛出 VersionConflictError 2ms - ✓ tests/integration/purchase-reconciliation-flow.test.ts > 采购对账模块集成测试 > 核销对账单 > 应付单不存在应抛出 NotFoundError 2ms - ✓ tests/integration/purchase-reconciliation-flow.test.ts > 采购对账模块集成测试 > 关闭对账单 > 已核销完成状态对账单应成功关闭(status 4→9) 5ms - ✓ tests/integration/purchase-reconciliation-flow.test.ts > 采购对账模块集成测试 > 关闭对账单 > 未核销完成状态对账单关闭应抛出 DomainError 2ms - ✓ tests/integration/purchase-reconciliation-flow.test.ts > 采购对账模块集成测试 > 删除对账单 > 草稿状态对账单应成功删除 3ms - ✓ tests/integration/purchase-reconciliation-flow.test.ts > 采购对账模块集成测试 > 删除对账单 > 已确认状态对账单删除应抛出 DomainError 2ms - ✓ tests/integration/purchase-reconciliation-flow.test.ts > 采购对账模块集成测试 > 创建对账单 > 应创建草稿状态对账单并持久化事件 3ms - ✓ tests/integration/purchase-reconciliation-flow.test.ts > 采购对账模块集成测试 > 创建对账单 > 退货金额超过收货金额应抛出 DomainError 2ms - ✓ tests/unit/domain/warehouse/aggregates/stocktaking-order.test.ts > StocktakingOrder 聚合根 > create() 工厂方法 > 合法参数创建成功,状态为 DRAFT(0) 10ms - ✓ tests/unit/domain/warehouse/aggregates/stocktaking-order.test.ts > StocktakingOrder 聚合根 > create() 工厂方法 > 有 id 时发布 StocktakingOrderCreatedEvent 3ms - ✓ tests/unit/domain/warehouse/aggregates/stocktaking-order.test.ts > StocktakingOrder 聚合根 > create() 工厂方法 > 无 id 时不发布创建事件 1ms - ✓ tests/unit/domain/warehouse/aggregates/stocktaking-order.test.ts > StocktakingOrder 聚合根 > create() 工厂方法 > warehouseId 为 0 抛 DomainError 2ms - ✓ tests/unit/domain/warehouse/aggregates/stocktaking-order.test.ts > StocktakingOrder 聚合根 > create() 工厂方法 > items 为空数组抛 DomainError 1ms - ✓ tests/unit/domain/warehouse/aggregates/stocktaking-order.test.ts > StocktakingOrder 聚合根 > create() 工厂方法 > totalItems 自动计算为 items 长度 0ms - ✓ tests/unit/domain/warehouse/aggregates/stocktaking-order.test.ts > StocktakingOrder 聚合根 > reconstitute() 重建方法 > 从 DB 字段重建聚合 1ms - ✓ tests/unit/domain/warehouse/aggregates/stocktaking-order.test.ts > StocktakingOrder 聚合根 > reconstitute() 重建方法 > 未指定 totalItems 时自动计算为 items 长度 0ms - ✓ tests/unit/domain/warehouse/aggregates/stocktaking-order.test.ts > StocktakingOrder 聚合根 > start() 开始盘点流程 > DRAFT → IN_PROGRESS,发布开始事件 2ms - ✓ tests/unit/domain/warehouse/aggregates/stocktaking-order.test.ts > StocktakingOrder 聚合根 > start() 开始盘点流程 > 非 DRAFT 状态开始盘点抛错 1ms - ✓ tests/unit/domain/warehouse/aggregates/stocktaking-order.test.ts > StocktakingOrder 聚合根 > submitForApproval() 提交审批流程 > IN_PROGRESS → PENDING_APPROVAL,计算差异统计 1ms - ✓ tests/unit/domain/warehouse/aggregates/stocktaking-order.test.ts > StocktakingOrder 聚合根 > submitForApproval() 提交审批流程 > 非 IN_PROGRESS 状态提交审批抛错 0ms - ✓ tests/unit/domain/warehouse/aggregates/stocktaking-order.test.ts > StocktakingOrder 聚合根 > approve() 审批流程 > PENDING_APPROVAL → APPROVED,设置审批人信息 2ms - ✓ tests/unit/domain/warehouse/aggregates/stocktaking-order.test.ts > StocktakingOrder 聚合根 > approve() 审批流程 > 盘盈项 adjustType 为 1 0ms - ✓ tests/unit/domain/warehouse/aggregates/stocktaking-order.test.ts > StocktakingOrder 聚合根 > approve() 审批流程 > 非 PENDING_APPROVAL 状态审批抛错 0ms - ✓ tests/unit/domain/warehouse/aggregates/stocktaking-order.test.ts > StocktakingOrder 聚合根 > cancel() 取消流程 > DRAFT → CANCELLED,发布取消事件 1ms - ✓ tests/unit/domain/warehouse/aggregates/stocktaking-order.test.ts > StocktakingOrder 聚合根 > cancel() 取消流程 > IN_PROGRESS → CANCELLED 0ms - ✓ tests/unit/domain/warehouse/aggregates/stocktaking-order.test.ts > StocktakingOrder 聚合根 > cancel() 取消流程 > APPROVED → CANCELLED 抛错(终态不可取消) 0ms - ✓ tests/unit/domain/warehouse/aggregates/stocktaking-order.test.ts > StocktakingOrder 聚合根 > processDiff() 差异处理 > 有差异的盘点项标记为已处理 1ms - ✓ tests/unit/domain/warehouse/aggregates/stocktaking-order.test.ts > StocktakingOrder 聚合根 > processDiff() 差异处理 > 不存在的盘点项抛 DomainError 1ms - ✓ tests/unit/domain/warehouse/aggregates/stocktaking-order.test.ts > StocktakingOrder 聚合根 > processDiff() 差异处理 > 无差异的盘点项抛 DomainError 1ms - ✓ tests/unit/domain/warehouse/aggregates/stocktaking-order.test.ts > StocktakingOrder 聚合根 > 权限委托 > canEdit/canDelete 委托给 StocktakingStatus 1ms - ✓ tests/unit/domain/warehouse/aggregates/stocktaking-order.test.ts > StocktakingOrder 聚合根 > 领域事件管理 > getDomainEvents 返回副本(不可变) 0ms - ✓ tests/unit/domain/warehouse/aggregates/stocktaking-order.test.ts > StocktakingOrder 聚合根 > 领域事件管理 > clearDomainEvents 清空事件 1ms - ✓ tests/unit/domain/warehouse/aggregates/stocktaking-order.test.ts > StocktakingOrder 聚合根 > items 访问器返回副本 > 外部修改 items 数组不影响内部状态 0ms - ✓ tests/unit/app/api/sample/orders/status.test.ts > sample/orders/status PUT — 参数校验 > 缺少 id 返回 400 16ms - ✓ tests/unit/app/api/sample/orders/status.test.ts > sample/orders/status PUT — 参数校验 > 缺少 action 返回 400 3ms - ✓ tests/unit/app/api/sample/orders/status.test.ts > sample/orders/status PUT — 参数校验 > 不支持的 action 返回 400 3ms - ✓ tests/unit/app/api/sample/orders/status.test.ts > sample/orders/status PUT — 各状态转换调用对应 service 方法 > submit 调用 service.submitOrder(id, userId) 6ms - ✓ tests/unit/app/api/sample/orders/status.test.ts > sample/orders/status PUT — 各状态转换调用对应 service 方法 > startProduction 调用 service.startProduction(id, userId) 4ms - ✓ tests/unit/app/api/sample/orders/status.test.ts > sample/orders/status PUT — 各状态转换调用对应 service 方法 > complete 调用 service.completeOrder(id, userId) 2ms - ✓ tests/unit/app/api/sample/orders/status.test.ts > sample/orders/status PUT — 各状态转换调用对应 service 方法 > confirm 调用 service.confirmOrder(id, userId) 2ms - ✓ tests/unit/app/api/sample/orders/status.test.ts > sample/orders/status PUT convert — T305 自动创建销售订单 > 有 salesOrderId:直接调用 convertOrder,不调用 createSalesOrderFromSample 4ms - ✓ tests/unit/app/api/sample/orders/status.test.ts > sample/orders/status PUT convert — T305 自动创建销售订单 > 无 salesOrderId:自动调用 createSalesOrderFromSample 并以返回值转大货 4ms - ✓ tests/unit/app/api/sample/orders/status.test.ts > sample/orders/status PUT convert — T305 自动创建销售订单 > salesOrderId 为 0(falsy):触发自动创建销售订单 2ms - ✓ tests/unit/app/api/sample/orders/status.test.ts > sample/orders/status PUT cancel — reason 默认值 > 未传 reason:默认使用 "手动作废" 3ms - ✓ tests/unit/app/api/sample/orders/status.test.ts > sample/orders/status PUT cancel — reason 默认值 > 传入 reason:使用传入的 reason 2ms - ✓ tests/unit/app/api/sample/orders/status.test.ts > sample/orders/status PUT — 错误处理 > service 抛错时返回 400 且包含错误消息 2ms - ✓ tests/unit/app/api/sample/orders/status.test.ts > sample/orders/status PUT — 错误处理 > service 抛错时 error 日志包含 id 和 action 3ms - ✓ tests/unit/app/api/sample/orders/status.test.ts > sample/orders/status PUT — 错误处理 > convert 自动创建销售订单抛错时返回 400 1ms - ✓ tests/unit/app/api/sample/orders/status.test.ts > sample/orders/status PUT — 成功响应与日志 > 成功响应包含 id 和 action 字段 2ms - ✓ tests/unit/app/api/sample/orders/status.test.ts > sample/orders/status PUT — 成功响应与日志 > 成功时输出 info 日志记录状态变更 2ms - ✓ tests/unit/domain/finance/aggregates/receivable.test.ts > Receivable 聚合根 > create() 工厂方法 > 合法参数创建成功,状态为 UNPAID(1) 6ms - ✓ tests/unit/domain/finance/aggregates/receivable.test.ts > Receivable 聚合根 > create() 工厂方法 > 有 id 时发布 ReceivableCreatedEvent 3ms - ✓ tests/unit/domain/finance/aggregates/receivable.test.ts > Receivable 聚合根 > create() 工厂方法 > 无 id 时不发布创建事件 1ms - ✓ tests/unit/domain/finance/aggregates/receivable.test.ts > Receivable 聚合根 > create() 工厂方法 > customerId 为 0 抛 DomainError 2ms - ✓ tests/unit/domain/finance/aggregates/receivable.test.ts > Receivable 聚合根 > create() 工厂方法 > amount 为 0 抛 DomainError 1ms - ✓ tests/unit/domain/finance/aggregates/receivable.test.ts > Receivable 聚合根 > createRedLetter() 红字应收工厂(T305) > 负数金额创建成功,状态为 UNPAID(1) 1ms - ✓ tests/unit/domain/finance/aggregates/receivable.test.ts > Receivable 聚合根 > createRedLetter() 红字应收工厂(T305) > sourceType 默认为 3(退货红字) 1ms - ✓ tests/unit/domain/finance/aggregates/receivable.test.ts > Receivable 聚合根 > createRedLetter() 红字应收工厂(T305) > 有 id 时发布 ReceivableCreatedEvent(含负数金额) 1ms - ✓ tests/unit/domain/finance/aggregates/receivable.test.ts > Receivable 聚合根 > createRedLetter() 红字应收工厂(T305) > 无 id 时不发布创建事件 1ms - ✓ tests/unit/domain/finance/aggregates/receivable.test.ts > Receivable 聚合根 > createRedLetter() 红字应收工厂(T305) > customerId 为 0 抛 DomainError 1ms - ✓ tests/unit/domain/finance/aggregates/receivable.test.ts > Receivable 聚合根 > createRedLetter() 红字应收工厂(T305) > 正数金额抛 DomainError 1ms - ✓ tests/unit/domain/finance/aggregates/receivable.test.ts > Receivable 聚合根 > createRedLetter() 红字应收工厂(T305) > 金额为 0 抛 DomainError 0ms - ✓ tests/unit/domain/finance/aggregates/receivable.test.ts > Receivable 聚合根 > createRedLetter() 红字应收工厂(T305) > 对红字应收记录收款应抛错(不可收款) 1ms - ✓ tests/unit/domain/finance/aggregates/receivable.test.ts > Receivable 聚合根 > reconstitute() 重建方法 > 从 DB 字段重建聚合 1ms - ✓ tests/unit/domain/finance/aggregates/receivable.test.ts > Receivable 聚合根 > reconstitute() 重建方法 > 未指定 balance 时回退为 amount 0ms - ✓ tests/unit/domain/finance/aggregates/receivable.test.ts > Receivable 聚合根 > recordReceipt() 收款流程 > 部分收款 → PARTIAL 状态,发布 PartialReceivedEvent 1ms - ✓ tests/unit/domain/finance/aggregates/receivable.test.ts > Receivable 聚合根 > recordReceipt() 收款流程 > 全额收款 → SETTLED 状态,发布 SettledEvent 1ms - ✓ tests/unit/domain/finance/aggregates/receivable.test.ts > Receivable 聚合根 > recordReceipt() 收款流程 > 多次部分收款后结清 0ms - ✓ tests/unit/domain/finance/aggregates/receivable.test.ts > Receivable 聚合根 > recordReceipt() 收款流程 > 收款金额超过余额抛 DomainError 1ms - ✓ tests/unit/domain/finance/aggregates/receivable.test.ts > Receivable 聚合根 > recordReceipt() 收款流程 > 收款金额为 0 抛 DomainError 0ms - ✓ tests/unit/domain/finance/aggregates/receivable.test.ts > Receivable 聚合根 > recordReceipt() 收款流程 > 已结清状态收款抛 DomainError 1ms - ✓ tests/unit/domain/finance/aggregates/receivable.test.ts > Receivable 聚合根 > writeOff() 坏账处理 > UNPAID → BAD_DEBT,发布 WrittenOffEvent 2ms - ✓ tests/unit/domain/finance/aggregates/receivable.test.ts > Receivable 聚合根 > writeOff() 坏账处理 > 已结清状态坏账处理抛 DomainError 0ms - ✓ tests/unit/domain/finance/aggregates/receivable.test.ts > Receivable 聚合根 > isOverdue() 逾期判断 > 到期日早于当前日期且未结清 → 逾期 0ms - ✓ tests/unit/domain/finance/aggregates/receivable.test.ts > Receivable 聚合根 > isOverdue() 逾期判断 > 到期日晚于当前日期 → 未逾期 0ms - ✓ tests/unit/domain/finance/aggregates/receivable.test.ts > Receivable 聚合根 > isOverdue() 逾期判断 > 已结清 → 未逾期 0ms - ✓ tests/unit/domain/finance/aggregates/receivable.test.ts > Receivable 聚合根 > isOverdue() 逾期判断 > 无到期日 → 未逾期 0ms - ✓ tests/unit/domain/finance/aggregates/receivable.test.ts > Receivable 聚合根 > 领域事件管理 > getDomainEvents 返回副本(不可变) 0ms - ✓ tests/unit/domain/finance/aggregates/receivable.test.ts > Receivable 聚合根 > 领域事件管理 > clearDomainEvents 清空事件 1ms - ✓ tests/unit/domain/sales/aggregates/sales-order.test.ts > SalesOrder Aggregate > create() > 应创建草稿状态的销售订单 10ms - ✓ tests/unit/domain/sales/aggregates/sales-order.test.ts > SalesOrder Aggregate > create() > 应自动计算总金额和总数量 1ms - ✓ tests/unit/domain/sales/aggregates/sales-order.test.ts > SalesOrder Aggregate > create() > 应自动分配行号 1ms - ✓ tests/unit/domain/sales/aggregates/sales-order.test.ts > SalesOrder Aggregate > create() > 有id时应生成SalesOrderCreatedEvent 1ms - ✓ tests/unit/domain/sales/aggregates/sales-order.test.ts > SalesOrder Aggregate > create() > 无id时不应生成事件 1ms - ✓ tests/unit/domain/sales/aggregates/sales-order.test.ts > SalesOrder Aggregate > create() > 客户为空时应抛出DomainError 2ms - ✓ tests/unit/domain/sales/aggregates/sales-order.test.ts > SalesOrder Aggregate > create() > 明细为空时应抛出DomainError 1ms - ✓ tests/unit/domain/sales/aggregates/sales-order.test.ts > SalesOrder Aggregate > create() > 应支持可选字段的默认值 1ms - ✓ tests/unit/domain/sales/aggregates/sales-order.test.ts > SalesOrder Aggregate > reconstitute() > 应从持久化数据恢复订单 2ms - ✓ tests/unit/domain/sales/aggregates/sales-order.test.ts > SalesOrder Aggregate > submit() > 应从草稿转为已提交 1ms - ✓ tests/unit/domain/sales/aggregates/sales-order.test.ts > SalesOrder Aggregate > submit() > 应生成SalesOrderSubmittedEvent 1ms - ✓ tests/unit/domain/sales/aggregates/sales-order.test.ts > SalesOrder Aggregate > approve() > 应从已提交转为已审核 2ms - ✓ tests/unit/domain/sales/aggregates/sales-order.test.ts > SalesOrder Aggregate > approve() > 应生成SalesOrderApprovedEvent 1ms - ✓ tests/unit/domain/sales/aggregates/sales-order.test.ts > SalesOrder Aggregate > approve() > 草稿状态不允许审核 1ms - ✓ tests/unit/domain/sales/aggregates/sales-order.test.ts > SalesOrder Aggregate > approve() > 已审核状态不允许重复审核 1ms - ✓ tests/unit/domain/sales/aggregates/sales-order.test.ts > SalesOrder Aggregate > ship() > 部分出库应转为partially_shipped 1ms - ✓ tests/unit/domain/sales/aggregates/sales-order.test.ts > SalesOrder Aggregate > ship() > 全部出库应转为completed 1ms - ✓ tests/unit/domain/sales/aggregates/sales-order.test.ts > SalesOrder Aggregate > ship() > 分批出库应正确累计 1ms - ✓ tests/unit/domain/sales/aggregates/sales-order.test.ts > SalesOrder Aggregate > ship() > 应生成SalesOrderShippedEvent 1ms - ✓ tests/unit/domain/sales/aggregates/sales-order.test.ts > SalesOrder Aggregate > ship() > 库存校验失败时应抛出DomainError 0ms - ✓ tests/unit/domain/sales/aggregates/sales-order.test.ts > SalesOrder Aggregate > ship() > 库存校验通过时应正常出库 0ms - ✓ tests/unit/domain/sales/aggregates/sales-order.test.ts > SalesOrder Aggregate > ship() > 不存在的行号应抛出DomainError 0ms - ✓ tests/unit/domain/sales/aggregates/sales-order.test.ts > SalesOrder Aggregate > ship() > 出库数量超过订购数量应抛出DomainError 0ms - ✓ tests/unit/domain/sales/aggregates/sales-order.test.ts > SalesOrder Aggregate > ship() > 草稿状态不允许出库 0ms - ✓ tests/unit/domain/sales/aggregates/sales-order.test.ts > SalesOrder Aggregate > close() > 应从草稿关闭 0ms - ✓ tests/unit/domain/sales/aggregates/sales-order.test.ts > SalesOrder Aggregate > close() > 应从已提交关闭 0ms - ✓ tests/unit/domain/sales/aggregates/sales-order.test.ts > SalesOrder Aggregate > close() > 应从已审核关闭 0ms - ✓ tests/unit/domain/sales/aggregates/sales-order.test.ts > SalesOrder Aggregate > close() > 应生成SalesOrderClosedEvent 1ms - ✓ tests/unit/domain/sales/aggregates/sales-order.test.ts > SalesOrder Aggregate > close() > 已完成状态可关闭(支持作废全链路回滚 T403) 1ms - ✓ tests/unit/domain/sales/aggregates/sales-order.test.ts > SalesOrder Aggregate > void() > 草稿状态可作废 1ms - ✓ tests/unit/domain/sales/aggregates/sales-order.test.ts > SalesOrder Aggregate > void() > 已审核状态可作废 0ms - ✓ tests/unit/domain/sales/aggregates/sales-order.test.ts > SalesOrder Aggregate > void() > 部分出库状态可作废 0ms - ✓ tests/unit/domain/sales/aggregates/sales-order.test.ts > SalesOrder Aggregate > void() > 已完成状态不可作废(需先 close 后处理回滚) 1ms - ✓ tests/unit/domain/sales/aggregates/sales-order.test.ts > SalesOrder Aggregate > void() > 已关闭状态不可作废 0ms - ✓ tests/unit/domain/sales/aggregates/sales-order.test.ts > SalesOrder Aggregate > void() > 已作废状态再次作废抛错 0ms - ✓ tests/unit/domain/sales/aggregates/sales-order.test.ts > SalesOrder Aggregate > void() > canVoid() 在允许状态返回 true 0ms - ✓ tests/unit/domain/sales/aggregates/sales-order.test.ts > SalesOrder Aggregate > void() > canVoid() 在终态返回 false 1ms - ✓ tests/unit/domain/sales/aggregates/sales-order.test.ts > SalesOrder Aggregate > canEdit() / canDelete() > 草稿状态可编辑可删除 1ms - ✓ tests/unit/domain/sales/aggregates/sales-order.test.ts > SalesOrder Aggregate > canEdit() / canDelete() > 已提交状态不可编辑不可删除 0ms - ✓ tests/unit/domain/sales/aggregates/sales-order.test.ts > SalesOrder Aggregate > canEdit() / canDelete() > 已审核状态不可编辑不可删除 0ms - ✓ tests/unit/domain/sales/aggregates/sales-order.test.ts > SalesOrder Aggregate > DomainEvents > clearDomainEvents应清空事件 1ms - ✓ tests/unit/domain/sales/aggregates/sales-order.test.ts > SalesOrder Aggregate > DomainEvents > 多步操作应累积事件 0ms - ✓ tests/unit/domain/sales/aggregates/sales-order.test.ts > SalesOrderStatus Value Object > fromDbCode() / toDbCode() > 应正确映射状态码 1ms - ✓ tests/unit/domain/sales/aggregates/sales-order.test.ts > SalesOrderStatus Value Object > fromDbCode() / toDbCode() > 应从数据库码恢复状态 1ms - ✓ tests/unit/domain/sales/aggregates/sales-order.test.ts > SalesOrderStatus Value Object > fromDbCode() / toDbCode() > 无效状态码应抛出DomainError 0ms - ✓ tests/unit/domain/sales/aggregates/sales-order.test.ts > SalesOrderStatus Value Object > 状态流转 > 草稿可流转到已提交 0ms - ✓ tests/unit/domain/sales/aggregates/sales-order.test.ts > SalesOrderStatus Value Object > 状态流转 > 草稿可流转到已关闭 0ms - ✓ tests/unit/domain/sales/aggregates/sales-order.test.ts > SalesOrderStatus Value Object > 状态流转 > 草稿不可直接流转到已审核 0ms - ✓ tests/unit/domain/sales/aggregates/sales-order.test.ts > SalesOrderStatus Value Object > 状态流转 > 已完成可流转到已关闭(支持作废全链路回滚 T403) 0ms - ✓ tests/unit/domain/sales/aggregates/sales-order.test.ts > SalesOrderStatus Value Object > 状态流转 > 已作废不可流转 0ms - ✓ tests/unit/domain/sales/aggregates/sales-order.test.ts > SalesOrderStatus Value Object > 状态流转 > 草稿/已提交/已审核/部分出库可流转到已作废 0ms - ✓ tests/unit/domain/sales/aggregates/sales-order.test.ts > SalesOrderStatus Value Object > 状态流转 > 已关闭不可流转 0ms - ✓ tests/unit/domain/sales/aggregates/sales-order.test.ts > SalesOrderStatus Value Object > 状态流转 > 非法流转应抛出DomainError 0ms - ✓ tests/unit/domain/sales/aggregates/sales-order.test.ts > SalesOrderStatus Value Object > label() > 应返回中文标签 1ms - ✓ tests/unit/domain/sales/aggregates/sales-order.test.ts > SalesOrderStatus Value Object > equals() > 相同状态应相等 0ms - ✓ tests/unit/domain/sales/aggregates/sales-order.test.ts > SalesOrderStatus Value Object > equals() > 不同状态应不相等 0ms - ✓ tests/unit/domain/sales/aggregates/delivery.test.ts > Delivery 聚合根 > create() 工厂方法 > 合法参数创建成功 9ms - ✓ tests/unit/domain/sales/aggregates/delivery.test.ts > Delivery 聚合根 > create() 工厂方法 > 自动计算 totalAmount 等于明细金额之和 1ms - ✓ tests/unit/domain/sales/aggregates/delivery.test.ts > Delivery 聚合根 > create() 工厂方法 > orderId 缺失抛错 3ms - ✓ tests/unit/domain/sales/aggregates/delivery.test.ts > Delivery 聚合根 > create() 工厂方法 > customerId 缺失抛错 1ms - ✓ tests/unit/domain/sales/aggregates/delivery.test.ts > Delivery 聚合根 > create() 工厂方法 > warehouseId 缺失抛错 1ms - ✓ tests/unit/domain/sales/aggregates/delivery.test.ts > Delivery 聚合根 > create() 工厂方法 > lines 为空抛错 1ms - ✓ tests/unit/domain/sales/aggregates/delivery.test.ts > Delivery 聚合根 > reconstitute() 工厂方法 > 从 DB 重建 pending 状态 1ms - ✓ tests/unit/domain/sales/aggregates/delivery.test.ts > Delivery 聚合根 > reconstitute() 工厂方法 > 从 DB 重建 shipped 状态 1ms - ✓ tests/unit/domain/sales/aggregates/delivery.test.ts > Delivery 聚合根 > ship() 方法 > pending → shipped 成功 2ms - ✓ tests/unit/domain/sales/aggregates/delivery.test.ts > Delivery 聚合根 > ship() 方法 > ship 不传物流信息时保留空字符串 1ms - ✓ tests/unit/domain/sales/aggregates/delivery.test.ts > Delivery 聚合根 > ship() 方法 > ship 时 inventoryCheck 全部通过则发货成功 4ms - ✓ tests/unit/domain/sales/aggregates/delivery.test.ts > Delivery 聚合根 > ship() 方法 > ship 时 inventoryCheck 返回 false 抛错 1ms - ✓ tests/unit/domain/sales/aggregates/delivery.test.ts > Delivery 聚合根 > ship() 方法 > shipBy 缺失抛错 1ms - ✓ tests/unit/domain/sales/aggregates/delivery.test.ts > Delivery 聚合根 > ship() 方法 > 已 shipped 再次 ship 抛错 1ms - ✓ tests/unit/domain/sales/aggregates/delivery.test.ts > Delivery 聚合根 > ship() 方法 > 已 cancelled 的 ship 抛错 2ms - ✓ tests/unit/domain/sales/aggregates/delivery.test.ts > Delivery 聚合根 > sign() 方法 > shipped → signed 成功 1ms - ✓ tests/unit/domain/sales/aggregates/delivery.test.ts > Delivery 聚合根 > sign() 方法 > sign 不传 signBy 时仍成功 1ms - ✓ tests/unit/domain/sales/aggregates/delivery.test.ts > Delivery 聚合根 > sign() 方法 > pending 的 sign 抛错 1ms - ✓ tests/unit/domain/sales/aggregates/delivery.test.ts > Delivery 聚合根 > sign() 方法 > 已 signed 再次 sign 抛错 1ms - ✓ tests/unit/domain/sales/aggregates/delivery.test.ts > Delivery 聚合根 > sign() 方法 > cancelled 的 sign 抛错 1ms - ✓ tests/unit/domain/sales/aggregates/delivery.test.ts > Delivery 聚合根 > cancel() 方法 > pending → cancelled 成功 0ms - ✓ tests/unit/domain/sales/aggregates/delivery.test.ts > Delivery 聚合根 > cancel() 方法 > shipped → cancelled 成功 0ms - ✓ tests/unit/domain/sales/aggregates/delivery.test.ts > Delivery 聚合根 > cancel() 方法 > signed 的 cancel 抛错 0ms - ✓ tests/unit/domain/sales/aggregates/delivery.test.ts > Delivery 聚合根 > cancel() 方法 > 已 cancelled 再次 cancel 抛错 0ms - ✓ tests/unit/domain/sales/aggregates/delivery.test.ts > Delivery 聚合根 > 状态判断方法 > canEdit/canDelete 仅 pending 返回 true 1ms - ✓ tests/unit/domain/sales/aggregates/delivery.test.ts > Delivery 聚合根 > 状态判断方法 > isTerminal 在 signed/cancelled 返回 true 1ms - ✓ tests/unit/domain/sales/aggregates/delivery.test.ts > Delivery 聚合根 > 状态判断方法 > 状态 label 正确 1ms - ✓ tests/unit/domain/sales/aggregates/delivery.test.ts > Delivery 聚合根 > 事件发布 > create 发布 DeliveryCreatedEvent 2ms - ✓ tests/unit/domain/sales/aggregates/delivery.test.ts > Delivery 聚合根 > 事件发布 > ship 发布 DeliveryShippedEvent 含 shippedItems 1ms - ✓ tests/unit/domain/sales/aggregates/delivery.test.ts > Delivery 聚合根 > 事件发布 > sign 发布 DeliverySignedEvent 1ms - ✓ tests/unit/domain/sales/aggregates/delivery.test.ts > Delivery 聚合根 > 事件发布 > cancel 发布 DeliveryCancelledEvent 1ms - ✓ tests/unit/domain/sales/aggregates/delivery.test.ts > Delivery 聚合根 > 事件发布 > clearDomainEvents 清空事件 1ms - ✓ tests/unit/domain/sales/aggregates/delivery.test.ts > Delivery 聚合根 > lines 防御性拷贝 > 外部修改 lines 不影响聚合内部状态 1ms - ✓ tests/unit/domain/sample/aggregates/sample-order.test.ts > SampleOrder 聚合根 > create() > 应使用完整属性创建草稿打样单 7ms - ✓ tests/unit/domain/sample/aggregates/sample-order.test.ts > SampleOrder 聚合根 > create() > 应使用默认值填充缺失字段 2ms - ✓ tests/unit/domain/sample/aggregates/sample-order.test.ts > SampleOrder 聚合根 > create() > 缺少 orderNo 时应抛出 DomainError 1ms - ✓ tests/unit/domain/sample/aggregates/sample-order.test.ts > SampleOrder 聚合根 > create() > 创建时应发布 SampleOrderCreated 事件 2ms - ✓ tests/unit/domain/sample/aggregates/sample-order.test.ts > SampleOrder 聚合根 > reconstitute() 与 toProps() > reconstitute 应保留所有字段 1ms - ✓ tests/unit/domain/sample/aggregates/sample-order.test.ts > SampleOrder 聚合根 > reconstitute() 与 toProps() > toProps 应与 reconstitute 输入往返一致 4ms - ✓ tests/unit/domain/sample/aggregates/sample-order.test.ts > SampleOrder 聚合根 > 合法状态流转 > draft → pending (submit) 1ms - ✓ tests/unit/domain/sample/aggregates/sample-order.test.ts > SampleOrder 聚合根 > 合法状态流转 > pending → in_progress (startProduction) 1ms - ✓ tests/unit/domain/sample/aggregates/sample-order.test.ts > SampleOrder 聚合根 > 合法状态流转 > in_progress → completed (complete) 2ms - ✓ tests/unit/domain/sample/aggregates/sample-order.test.ts > SampleOrder 聚合根 > 合法状态流转 > completed → confirmed (confirm) 1ms - ✓ tests/unit/domain/sample/aggregates/sample-order.test.ts > SampleOrder 聚合根 > 合法状态流转 > confirmed → converted (convertToSalesOrder) 1ms - ✓ tests/unit/domain/sample/aggregates/sample-order.test.ts > SampleOrder 聚合根 > 合法状态流转 > draft → cancelled (cancel) 1ms - ✓ tests/unit/domain/sample/aggregates/sample-order.test.ts > SampleOrder 聚合根 > 非法状态流转拦截 > draft 不能直接 complete 1ms - ✓ tests/unit/domain/sample/aggregates/sample-order.test.ts > SampleOrder 聚合根 > 非法状态流转拦截 > draft 不能直接 startProduction 0ms - ✓ tests/unit/domain/sample/aggregates/sample-order.test.ts > SampleOrder 聚合根 > 非法状态流转拦截 > draft 不能直接 confirm 0ms - ✓ tests/unit/domain/sample/aggregates/sample-order.test.ts > SampleOrder 聚合根 > 非法状态流转拦截 > draft 不能直接 convertToSalesOrder 0ms - ✓ tests/unit/domain/sample/aggregates/sample-order.test.ts > SampleOrder 聚合根 > 非法状态流转拦截 > pending 不能直接 complete 0ms - ✓ tests/unit/domain/sample/aggregates/sample-order.test.ts > SampleOrder 聚合根 > 非法状态流转拦截 > in_progress 不能直接 confirm 0ms - ✓ tests/unit/domain/sample/aggregates/sample-order.test.ts > SampleOrder 聚合根 > 非法状态流转拦截 > completed 不能直接 submit 0ms - ✓ tests/unit/domain/sample/aggregates/sample-order.test.ts > SampleOrder 聚合根 > 非法状态流转拦截 > converted 是终态,不能再流转 1ms - ✓ tests/unit/domain/sample/aggregates/sample-order.test.ts > SampleOrder 聚合根 > 非法状态流转拦截 > cancelled 是终态,不能再流转 1ms - ✓ tests/unit/domain/sample/aggregates/sample-order.test.ts > SampleOrder 聚合根 > 非法状态流转拦截 > confirmed 不能 cancel 1ms - ✓ tests/unit/domain/sample/aggregates/sample-order.test.ts > SampleOrder 聚合根 > convertToSalesOrder() 转大货逻辑 > 应设置 salesOrderId、convertedAt、convertedBy 1ms - ✓ tests/unit/domain/sample/aggregates/sample-order.test.ts > SampleOrder 聚合根 > convertToSalesOrder() 转大货逻辑 > feeCharged=1 且 feeDeductible=1 时应标记 feeDeducted=1 1ms - ✓ tests/unit/domain/sample/aggregates/sample-order.test.ts > SampleOrder 聚合根 > convertToSalesOrder() 转大货逻辑 > feeCharged=0 时不应标记 feeDeducted 0ms - ✓ tests/unit/domain/sample/aggregates/sample-order.test.ts > SampleOrder 聚合根 > convertToSalesOrder() 转大货逻辑 > feeDeductible=0 时不应标记 feeDeducted 0ms - ✓ tests/unit/domain/sample/aggregates/sample-order.test.ts > SampleOrder 聚合根 > convertToSalesOrder() 转大货逻辑 > 转大货后 toProps 应包含 salesOrderId 和 convertedAt 1ms - ✓ tests/unit/domain/sample/aggregates/sample-order.test.ts > SampleOrder 聚合根 > 关联与费用操作 > linkProcessCard 应设置 processCardId 0ms - ✓ tests/unit/domain/sample/aggregates/sample-order.test.ts > SampleOrder 聚合根 > 关联与费用操作 > linkWorkOrder 应设置 workOrderId 1ms - ✓ tests/unit/domain/sample/aggregates/sample-order.test.ts > SampleOrder 聚合根 > 关联与费用操作 > updateSampleFee 应更新费用三字段 0ms - ✓ tests/unit/domain/sample/aggregates/sample-order.test.ts > SampleOrder 聚合根 > 领域事件管理 > 完整流转链应累积 6 个事件 1ms - ✓ tests/unit/domain/sample/aggregates/sample-order.test.ts > SampleOrder 聚合根 > 领域事件管理 > clearDomainEvents 应清空事件 1ms - ✓ tests/unit/domain/sample/aggregates/sample-order.test.ts > SampleOrder 聚合根 > 领域事件管理 > domainEvents getter 应返回副本,不泄露内部引用 1ms - ✓ tests/unit/domain/sample/aggregates/sample-order.test.ts > SampleOrder 聚合根 > generateCode() > 应生成 SP + 日期 + 4位序号 格式 1ms - ✓ tests/unit/domain/sample/aggregates/sample-order.test.ts > SampleOrder 聚合根 > generateCode() > 序号应补零至4位 0ms - ✓ tests/unit/domain/sample/aggregates/sample-order.test.ts > SampleOrder 聚合根 > generateCode() > 大序号不截断 0ms - ✓ tests/unit/app/sample/standard-card/input-card-logic.test.ts > Standard Card Input — createEmptyData > returns object with all expected fields 6ms - ✓ tests/unit/app/sample/standard-card/input-card-logic.test.ts > Standard Card Input — createEmptyData > initializes 7 empty print sequences 4ms - ✓ tests/unit/app/sample/standard-card/input-card-logic.test.ts > Standard Card Input — createEmptyData > initializes sheetSpecs with empty width and length 2ms - ✓ tests/unit/app/sample/standard-card/input-card-logic.test.ts > Standard Card Input — createEmptyData > sets date to today in YYYY-MM-DD format 1ms - ✓ tests/unit/app/sample/standard-card/input-card-logic.test.ts > Standard Card Input — createEmptySequence > creates sequence with given id and empty fields 1ms - ✓ tests/unit/app/sample/standard-card/input-card-logic.test.ts > Standard Card Input — toggleMultiValue > adds value to empty string 0ms - ✓ tests/unit/app/sample/standard-card/input-card-logic.test.ts > Standard Card Input — toggleMultiValue > adds value to existing single value 1ms - ✓ tests/unit/app/sample/standard-card/input-card-logic.test.ts > Standard Card Input — toggleMultiValue > adds value to existing multiple values 0ms - ✓ tests/unit/app/sample/standard-card/input-card-logic.test.ts > Standard Card Input — toggleMultiValue > removes value from middle of list 1ms - ✓ tests/unit/app/sample/standard-card/input-card-logic.test.ts > Standard Card Input — toggleMultiValue > removes value from start of list 0ms - ✓ tests/unit/app/sample/standard-card/input-card-logic.test.ts > Standard Card Input — toggleMultiValue > removes value from end of list 0ms - ✓ tests/unit/app/sample/standard-card/input-card-logic.test.ts > Standard Card Input — toggleMultiValue > removes only value, resulting in empty string 0ms - ✓ tests/unit/app/sample/standard-card/input-card-logic.test.ts > Standard Card Input — toggleMultiValue > handles value not in list (adds it) 0ms - ✓ tests/unit/app/sample/standard-card/input-card-logic.test.ts > Standard Card Input — toggleMultiValue > handles null/undefined current as empty 0ms - ✓ tests/unit/app/sample/standard-card/input-card-logic.test.ts > Standard Card Input — toggleMultiValue > filters out empty strings from current value 0ms - ✓ tests/unit/app/sample/standard-card/input-card-logic.test.ts > Standard Card Input — toggleMultiValue > is idempotent when toggling twice (add then remove) 0ms - ✓ tests/unit/app/sample/standard-card/input-card-logic.test.ts > Standard Card Input — toggleMultiValue > preserves order when removing non-adjacent values 0ms - ✓ tests/unit/app/sample/standard-card/input-card-logic.test.ts > Standard Card Input — mapCardDataToApiPayload > maps camelCase fields to snake_case 2ms - ✓ tests/unit/app/sample/standard-card/input-card-logic.test.ts > Standard Card Input — mapCardDataToApiPayload > converts dashedKnife boolean to integer (true → 1) 1ms - ✓ tests/unit/app/sample/standard-card/input-card-logic.test.ts > Standard Card Input — mapCardDataToApiPayload > converts dashedKnife boolean to integer (false → 0) 1ms - ✓ tests/unit/app/sample/standard-card/input-card-logic.test.ts > Standard Card Input — mapCardDataToApiPayload > serializes sequences array to JSON string 1ms - ✓ tests/unit/app/sample/standard-card/input-card-logic.test.ts > Standard Card Input — mapCardDataToApiPayload > maps sheetSpecs nested object to flat fields 0ms - ✓ tests/unit/app/sample/standard-card/input-card-logic.test.ts > Standard Card Input — mapCardDataToApiPayload > includes status=1 by default 0ms - ✓ tests/unit/app/sample/standard-card/input-card-logic.test.ts > Standard Card Input — mapCardDataToApiPayload > does NOT include status in edit mode (preserve existing status) 0ms - ✓ tests/unit/app/sample/standard-card/input-card-logic.test.ts > Standard Card Input — mapCardDataToApiPayload > does not include id in create mode 0ms - ✓ tests/unit/app/sample/standard-card/input-card-logic.test.ts > Standard Card Input — mapCardDataToApiPayload > includes id in edit mode 0ms - ✓ tests/unit/app/sample/standard-card/input-card-logic.test.ts > Standard Card Input — mapCardDataToApiPayload > includes id as integer when editId is string 0ms - ✓ tests/unit/app/sample/standard-card/input-card-logic.test.ts > Standard Card Input — mapCardDataToApiPayload > preserves multi-value fields as comma-separated strings 0ms - ✓ tests/unit/app/sample/standard-card/input-card-logic.test.ts > Standard Card Input — mapCardDataToApiPayload > maps all 70+ fields without omission 1ms - ✓ tests/unit/app/sample/standard-card/input-card-logic.test.ts > Standard Card Input — mapApiDataToCardData > maps snake_case fields to camelCase 1ms - ✓ tests/unit/app/sample/standard-card/input-card-logic.test.ts > Standard Card Input — mapApiDataToCardData > splits date string at T to get YYYY-MM-DD 0ms - ✓ tests/unit/app/sample/standard-card/input-card-logic.test.ts > Standard Card Input — mapApiDataToCardData > converts dashed_knife integer to boolean (1 → true) 0ms - ✓ tests/unit/app/sample/standard-card/input-card-logic.test.ts > Standard Card Input — mapApiDataToCardData > converts dashed_knife integer to boolean (0 → false) 0ms - ✓ tests/unit/app/sample/standard-card/input-card-logic.test.ts > Standard Card Input — mapApiDataToCardData > converts dashed_knife boolean true to boolean true 0ms - ✓ tests/unit/app/sample/standard-card/input-card-logic.test.ts > Standard Card Input — mapApiDataToCardData > parses sequences JSON string to array 1ms - ✓ tests/unit/app/sample/standard-card/input-card-logic.test.ts > Standard Card Input — mapApiDataToCardData > handles already-parsed sequences array 1ms - ✓ tests/unit/app/sample/standard-card/input-card-logic.test.ts > Standard Card Input — mapApiDataToCardData > falls back to 7 empty sequences when sequences is null 2ms - ✓ tests/unit/app/sample/standard-card/input-card-logic.test.ts > Standard Card Input — mapApiDataToCardData > falls back to 7 empty sequences when sequences is undefined 1ms - ✓ tests/unit/app/sample/standard-card/input-card-logic.test.ts > Standard Card Input — mapApiDataToCardData > falls back to 7 empty sequences when sequences is empty array 1ms - ✓ tests/unit/app/sample/standard-card/input-card-logic.test.ts > Standard Card Input — mapApiDataToCardData > maps sheet_width/sheet_length to sheetSpecs object 0ms - ✓ tests/unit/app/sample/standard-card/input-card-logic.test.ts > Standard Card Input — mapApiDataToCardData > handles missing fields with empty string defaults 1ms - ✓ tests/unit/app/sample/standard-card/input-card-logic.test.ts > Standard Card Input — mapApiDataToCardData > handles missing date as empty string 0ms - ✓ tests/unit/app/sample/standard-card/input-card-logic.test.ts > Standard Card Input — mapApiDataToCardData > handles missing dashed_knife as false 0ms - ✓ tests/unit/app/sample/standard-card/input-card-logic.test.ts > Standard Card Input — round-trip mapping > cardData → payload → cardData preserves all values 2ms - ✓ tests/unit/app/sample/standard-card/input-card-logic.test.ts > Standard Card Input — round-trip mapping > round-trip preserves 7 sequences 1ms - ✓ tests/unit/app/sample/standard-card/input-card-logic.test.ts > Standard Card Input — round-trip mapping > round-trip preserves multi-value checkbox state 2ms - ✓ tests/unit/app/sample/standard-card/standard-card-consistency.test.ts > StandardCard Consistency — API payload equivalence > produces identical payload structure regardless of form mode 8ms - ✓ tests/unit/app/sample/standard-card/standard-card-consistency.test.ts > StandardCard Consistency — API payload equivalence > submits all 78+ baseline fields without omission 4ms - ✓ tests/unit/app/sample/standard-card/standard-card-consistency.test.ts > StandardCard Consistency — API payload equivalence > round-trip preserves all field values 3ms - ✓ tests/unit/app/sample/standard-card/standard-card-consistency.test.ts > StandardCard Consistency — validation rules equivalence > rejects empty customer 1ms - ✓ tests/unit/app/sample/standard-card/standard-card-consistency.test.ts > StandardCard Consistency — validation rules equivalence > rejects empty customerCode 1ms - ✓ tests/unit/app/sample/standard-card/standard-card-consistency.test.ts > StandardCard Consistency — validation rules equivalence > rejects empty productName 0ms - ✓ tests/unit/app/sample/standard-card/standard-card-consistency.test.ts > StandardCard Consistency — validation rules equivalence > accepts fully filled data 1ms - ✓ tests/unit/app/sample/standard-card/standard-card-consistency.test.ts > StandardCard Consistency — Zod schema alignment > CardDataSchema validates correct data 10ms - ✓ tests/unit/app/sample/standard-card/standard-card-consistency.test.ts > StandardCard Consistency — Zod schema alignment > CardDataSchema rejects missing customer 3ms - ✓ tests/unit/app/sample/standard-card/standard-card-consistency.test.ts > StandardCard Consistency — Zod schema alignment > toggleMultiValue adds and removes values correctly 1ms - ✓ tests/unit/app/sample/standard-card/standard-card-consistency.test.ts > StandardCard Consistency — Zod schema alignment > sequences always contains 7 items 1ms - ✓ tests/unit/app/sample/standard-card/standard-card-consistency.test.ts > StandardCard Consistency — Zod schema alignment > dashedKnife round-trips as boolean via 0/1 1ms - ✓ tests/unit/domain/purchase/aggregates/purchase-return.test.ts > PurchaseReturn 聚合根 > create() 工厂方法 > 合法参数创建成功,状态为待审核(1) 9ms - ✓ tests/unit/domain/purchase/aggregates/purchase-return.test.ts > PurchaseReturn 聚合根 > create() 工厂方法 > 自动计算总金额(多行) 1ms - ✓ tests/unit/domain/purchase/aggregates/purchase-return.test.ts > PurchaseReturn 聚合根 > create() 工厂方法 > 采购订单ID为空抛错 1ms - ✓ tests/unit/domain/purchase/aggregates/purchase-return.test.ts > PurchaseReturn 聚合根 > create() 工厂方法 > 供应商ID为空抛错 1ms - ✓ tests/unit/domain/purchase/aggregates/purchase-return.test.ts > PurchaseReturn 聚合根 > create() 工厂方法 > 仓库ID为空抛错 1ms - ✓ tests/unit/domain/purchase/aggregates/purchase-return.test.ts > PurchaseReturn 聚合根 > create() 工厂方法 > 退货明细为空抛错 1ms - ✓ tests/unit/domain/purchase/aggregates/purchase-return.test.ts > PurchaseReturn 聚合根 > create() 工厂方法 > 退货原因为空抛错 1ms - ✓ tests/unit/domain/purchase/aggregates/purchase-return.test.ts > PurchaseReturn 聚合根 > create() 工厂方法 > 有 id 时发布 PurchaseReturnCreatedEvent 1ms - ✓ tests/unit/domain/purchase/aggregates/purchase-return.test.ts > PurchaseReturn 聚合根 > create() 工厂方法 > 无 id 时不发布创建事件 1ms - ✓ tests/unit/domain/purchase/aggregates/purchase-return.test.ts > PurchaseReturn 聚合根 > reconstitute() 重建方法 > 从 DB 字段重建聚合 1ms - ✓ tests/unit/domain/purchase/aggregates/purchase-return.test.ts > PurchaseReturn 聚合根 > reconstitute() 重建方法 > 未指定 status 时默认为 1 1ms - ✓ tests/unit/domain/purchase/aggregates/purchase-return.test.ts > PurchaseReturn 聚合根 > approve() 审核流程 > 待审核 → 已审核,设置审核人和审核时间 1ms - ✓ tests/unit/domain/purchase/aggregates/purchase-return.test.ts > PurchaseReturn 聚合根 > approve() 审核流程 > 发布 PurchaseReturnApprovedEvent 1ms - ✓ tests/unit/domain/purchase/aggregates/purchase-return.test.ts > PurchaseReturn 聚合根 > approve() 审核流程 > 已审核状态再次审核抛错 1ms - ✓ tests/unit/domain/purchase/aggregates/purchase-return.test.ts > PurchaseReturn 聚合根 > approve() 审核流程 > 审核人为空抛错 0ms - ✓ tests/unit/domain/purchase/aggregates/purchase-return.test.ts > PurchaseReturn 聚合根 > complete() 完成流程 > 已审核 → 已完成,创建出库单和红字应付单 1ms - ✓ tests/unit/domain/purchase/aggregates/purchase-return.test.ts > PurchaseReturn 聚合根 > complete() 完成流程 > 发布 PurchaseReturnCompletedEvent 含明细 1ms - ✓ tests/unit/domain/purchase/aggregates/purchase-return.test.ts > PurchaseReturn 聚合根 > complete() 完成流程 > 出库单创建失败抛错 0ms - ✓ tests/unit/domain/purchase/aggregates/purchase-return.test.ts > PurchaseReturn 聚合根 > complete() 完成流程 > 红字应付单创建失败抛错 1ms - ✓ tests/unit/domain/purchase/aggregates/purchase-return.test.ts > PurchaseReturn 聚合根 > complete() 完成流程 > 待审核状态完成抛错 0ms - ✓ tests/unit/domain/purchase/aggregates/purchase-return.test.ts > PurchaseReturn 聚合根 > complete() 完成流程 > 完成人为空抛错 0ms - ✓ tests/unit/domain/purchase/aggregates/purchase-return.test.ts > PurchaseReturn 聚合根 > cancel() 取消流程 > 待审核 → 已取消 1ms - ✓ tests/unit/domain/purchase/aggregates/purchase-return.test.ts > PurchaseReturn 聚合根 > cancel() 取消流程 > 已审核 → 已取消 0ms - ✓ tests/unit/domain/purchase/aggregates/purchase-return.test.ts > PurchaseReturn 聚合根 > cancel() 取消流程 > 已完成状态取消抛错 0ms - ✓ tests/unit/domain/purchase/aggregates/purchase-return.test.ts > PurchaseReturn 聚合根 > cancel() 取消流程 > 已取消状态再次取消抛错 0ms - ✓ tests/unit/domain/purchase/aggregates/purchase-return.test.ts > PurchaseReturn 聚合根 > canEdit / canDelete > 待审核状态可编辑可删除 0ms - ✓ tests/unit/domain/purchase/aggregates/purchase-return.test.ts > PurchaseReturn 聚合根 > canEdit / canDelete > 已审核状态不可编辑不可删除 0ms - ✓ tests/unit/domain/purchase/aggregates/purchase-return.test.ts > PurchaseReturn 聚合根 > validateAgainstReceivedQuantities() 退货量校验(T104) > 退货数量 <= 已入库数量 - 已退数量 时通过 2ms - ✓ tests/unit/domain/purchase/aggregates/purchase-return.test.ts > PurchaseReturn 聚合根 > validateAgainstReceivedQuantities() 退货量校验(T104) > 退货数量超过可退数量时抛出 DomainError 1ms - ✓ tests/unit/domain/purchase/aggregates/purchase-return.test.ts > PurchaseReturn 聚合根 > validateAgainstReceivedQuantities() 退货量校验(T104) > 未提供已退数量 Map 时仅校验退货量 ≤ 已入库量 1ms - ✓ tests/unit/domain/purchase/aggregates/purchase-return.test.ts > PurchaseReturn 聚合根 > validateAgainstReceivedQuantities() 退货量校验(T104) > 物料不在已入库 Map 中时视为 0,抛出 DomainError 0ms - ✓ tests/unit/domain/purchase/aggregates/purchase-return.test.ts > PurchaseReturn 聚合根 > validateAgainstReceivedQuantities() 退货量校验(T104) > 多物料行混合校验 1ms - ✓ tests/unit/domain/purchase/aggregates/purchase-return.test.ts > PurchaseReturn 聚合根 > validateAgainstReceivedQuantities() 退货量校验(T104) > 多物料行其中一行超量时抛错 3ms - ✓ tests/unit/domain/purchase/aggregates/purchase-return.test.ts > PurchaseReturn 聚合根 > 领域事件管理 > getDomainEvents 返回副本(不可变) 0ms - ✓ tests/unit/domain/purchase/aggregates/purchase-return.test.ts > PurchaseReturn 聚合根 > 领域事件管理 > clearDomainEvents 清空事件 0ms - ✓ tests/unit/domain/purchase/aggregates/purchase-return.test.ts > PurchaseReturn 聚合根 > PurchaseReturnStatus 状态机 > 状态流转: 1→2→3 0ms - ✓ tests/unit/domain/purchase/aggregates/purchase-return.test.ts > PurchaseReturn 聚合根 > PurchaseReturnStatus 状态机 > 非法流转 1→3 抛错 0ms - ✓ tests/unit/domain/purchase/aggregates/purchase-return.test.ts > PurchaseReturn 聚合根 > PurchaseReturnStatus 状态机 > 非法流转 3→1 抛错(终态) 0ms - ✓ tests/unit/domain/purchase/aggregates/purchase-return.test.ts > PurchaseReturn 聚合根 > PurchaseReturnStatus 状态机 > equals 正确比较 0ms - ✓ tests/integration/e2e-sales-to-receivable-flow.test.ts > 端到端流程:销售→MRP→生产→库存→应收 > Step 1: 销售订单审核 → 生成生产工单 > SalesToWorkOrderHandler 应根据销售订单行创建工单(prd_work_order INSERT) 12ms - ✓ tests/integration/e2e-sales-to-receivable-flow.test.ts > 端到端流程:销售→MRP→生产→库存→应收 > Step 2: 工单领料 → 库存扣减 > WorkOrderMaterialIssuedHandler 应扣减 inv_inventory 和 inv_inventory_batch 5ms - ✓ tests/integration/e2e-sales-to-receivable-flow.test.ts > 端到端流程:销售→MRP→生产→库存→应收 > Step 2: 工单领料 → 库存扣减 > 库存不足时应抛出错误 4ms - ✓ tests/integration/e2e-sales-to-receivable-flow.test.ts > 端到端流程:销售→MRP→生产→库存→应收 > Step 3: 工单完工 → 成品入库 > WorkOrderCompletedHandler 应增加 inv_inventory 和创建 inv_inventory_batch 2ms - ✓ tests/integration/e2e-sales-to-receivable-flow.test.ts > 端到端流程:销售→MRP→生产→库存→应收 > Step 3: 工单完工 → 成品入库 > 库存记录不存在时应创建新记录 3ms - ✓ tests/integration/e2e-sales-to-receivable-flow.test.ts > 端到端流程:销售→MRP→生产→库存→应收 > Step 4: 销售出库 → 库存扣减 + 应收凭证 > SalesShippedHandler 应扣减库存并记录出库流水 2ms - ✓ tests/integration/e2e-sales-to-receivable-flow.test.ts > 端到端流程:销售→MRP→生产→库存→应收 > Step 4: 销售出库 → 库存扣减 + 应收凭证 > SalesReceivableHandler 应创建 fin_receivable 记录 2ms - ✓ tests/integration/e2e-sales-to-receivable-flow.test.ts > 端到端流程:销售→MRP→生产→库存→应收 > Step 4: 销售出库 → 库存扣减 + 应收凭证 > 出库金额为0时不应创建应收凭证 1ms - ✓ tests/integration/e2e-sales-to-receivable-flow.test.ts > 端到端流程:销售→MRP→生产→库存→应收 > 端到端数据流验证 > 完整流程:审核→工单→领料→完工→出库→应收 3ms -stderr | src/lib/api-response.test.ts > API响应工具测试 > withErrorHandler - 错误处理包装器 > 应该捕获并处理错误 -[withErrorHandler] Error: 测试错误 - at D:/dcprint/erp-project/src/lib/api-response.test.ts:142:21 - at file:///D:/dcprint/erp-project/node_modules/.pnpm/@vitest+runner@4.1.5/node_modules/@vitest/runner/dist/chunk-artifact.js:302:11 - at file:///D:/dcprint/erp-project/node_modules/.pnpm/@vitest+runner@4.1.5/node_modules/@vitest/runner/dist/chunk-artifact.js:1903:26 - at file:///D:/dcprint/erp-project/node_modules/.pnpm/@vitest+runner@4.1.5/node_modules/@vitest/runner/dist/chunk-artifact.js:2326:20 - at new Promise () - at runWithCancel (file:///D:/dcprint/erp-project/node_modules/.pnpm/@vitest+runner@4.1.5/node_modules/@vitest/runner/dist/chunk-artifact.js:2323:10) - at file:///D:/dcprint/erp-project/node_modules/.pnpm/@vitest+runner@4.1.5/node_modules/@vitest/runner/dist/chunk-artifact.js:2305:20 - at new Promise () - at runWithTimeout (file:///D:/dcprint/erp-project/node_modules/.pnpm/@vitest+runner@4.1.5/node_modules/@vitest/runner/dist/chunk-artifact.js:2272:10) - at file:///D:/dcprint/erp-project/node_modules/.pnpm/@vitest+runner@4.1.5/node_modules/@vitest/runner/dist/chunk-artifact.js:2955:64 - - ✓ src/lib/api-response.test.ts > API响应工具测试 > successResponse - 成功响应 > 应该创建成功响应 7ms - ✓ src/lib/api-response.test.ts > API响应工具测试 > successResponse - 成功响应 > 应该使用默认消息 0ms - ✓ src/lib/api-response.test.ts > API响应工具测试 > successResponse - 成功响应 > 应该支持自定义状态码 0ms - ✓ src/lib/api-response.test.ts > API响应工具测试 > errorResponse - 错误响应 > 应该创建错误响应 1ms - ✓ src/lib/api-response.test.ts > API响应工具测试 > errorResponse - 错误响应 > 应该使用默认状态码 0ms - ✓ src/lib/api-response.test.ts > API响应工具测试 > paginatedResponse - 分页响应 > 应该创建分页响应 1ms - ✓ src/lib/api-response.test.ts > API响应工具测试 > commonErrors - 常见错误 > 应该创建未授权错误 1ms - ✓ src/lib/api-response.test.ts > API响应工具测试 > commonErrors - 常见错误 > 应该创建禁止访问错误 0ms - ✓ src/lib/api-response.test.ts > API响应工具测试 > commonErrors - 常见错误 > 应该创建资源不存在错误 1ms - ✓ src/lib/api-response.test.ts > API响应工具测试 > commonErrors - 常见错误 > 应该创建请求参数错误 1ms - ✓ src/lib/api-response.test.ts > API响应工具测试 > commonErrors - 常见错误 > 应该创建资源冲突错误 1ms - ✓ src/lib/api-response.test.ts > API响应工具测试 > commonErrors - 常见错误 > 应该创建验证错误 0ms - ✓ src/lib/api-response.test.ts > API响应工具测试 > commonErrors - 常见错误 > 应该创建服务器错误 0ms - ✓ src/lib/api-response.test.ts > API响应工具测试 > commonErrors - 常见错误 > 应该支持自定义消息 0ms - ✓ src/lib/api-response.test.ts > API响应工具测试 > withErrorHandler - 错误处理包装器 > 应该成功执行处理器 1ms - ✓ src/lib/api-response.test.ts > API响应工具测试 > withErrorHandler - 错误处理包装器 > 应该捕获并处理错误 10ms - ✓ src/lib/api-response.test.ts > API响应工具测试 > withErrorHandler - 错误处理包装器 > 应该使用默认错误消息 1ms - ✓ src/lib/api-response.test.ts > API响应工具测试 > validateRequestBody - 请求体验证 > 应该验证必填字段 1ms - ✓ src/lib/api-response.test.ts > API响应工具测试 > validateRequestBody - 请求体验证 > 应该通过完整数据 0ms - ✓ src/lib/api-response.test.ts > API响应工具测试 > validateRequestBody - 请求体验证 > 应该检测空字符串 2ms - ✓ src/lib/api-response.test.ts > API响应工具测试 > validateRequestBody - 请求体验证 > 应该检测null值 1ms - ✓ src/lib/api-response.test.ts > API响应工具测试 > validateRequestBody - 请求体验证 > 应该检测undefined值 0ms - ✓ src/lib/api-response.test.ts > API响应工具测试 > validateRequestBody - 请求体验证 > 应该允许可选字段为空 0ms - ✓ src/lib/api-response.test.ts > API响应工具测试 > logOperation - 操作日志 > 应该记录操作日志 0ms - ✓ src/lib/api-response.test.ts > API响应工具测试 > logOperation - 操作日志 > 应该使用默认值 0ms - ✓ src/lib/api-response.test.ts > API响应工具测试 > logOperation - 操作日志 > 应该处理截断参数 0ms - ✓ src/lib/api-response.test.ts > API响应工具测试 > logOperation - 操作日志 > 应该处理null参数 0ms - ✓ src/lib/validation-schemas.test.ts > RegisterSchema > valid data passes 11ms - ✓ src/lib/validation-schemas.test.ts > RegisterSchema > valid data with all fields passes 2ms - ✓ src/lib/validation-schemas.test.ts > RegisterSchema > username too short fails 2ms - ✓ src/lib/validation-schemas.test.ts > RegisterSchema > username too long fails 1ms - ✓ src/lib/validation-schemas.test.ts > RegisterSchema > username with special characters fails 1ms - ✓ src/lib/validation-schemas.test.ts > RegisterSchema > password too short fails 1ms - ✓ src/lib/validation-schemas.test.ts > RegisterSchema > invalid email fails 1ms - ✓ src/lib/validation-schemas.test.ts > RegisterSchema > invalid phone fails 1ms - ✓ src/lib/validation-schemas.test.ts > RegisterSchema > missing username fails 3ms - ✓ src/lib/validation-schemas.test.ts > RegisterSchema > missing password fails 6ms - ✓ src/lib/validation-schemas.test.ts > LoginSchema > valid data passes 2ms - ✓ src/lib/validation-schemas.test.ts > LoginSchema > missing username fails 1ms - ✓ src/lib/validation-schemas.test.ts > LoginSchema > missing password fails 1ms - ✓ src/lib/validation-schemas.test.ts > PaginationSchema > defaults to page=1, pageSize=20 2ms - ✓ src/lib/validation-schemas.test.ts > PaginationSchema > accepts valid page and pageSize 2ms - ✓ src/lib/validation-schemas.test.ts > PaginationSchema > pageSize > 100 fails 1ms - ✓ src/lib/validation-schemas.test.ts > PaginationSchema > page < 1 fails 1ms - ✓ src/lib/validation-schemas.test.ts > PaginationSchema > pageSize < 1 fails 0ms - ✓ src/lib/validation-schemas.test.ts > InboundCreateSchema > valid data passes 4ms - ✓ src/lib/validation-schemas.test.ts > InboundCreateSchema > empty items is allowed by schema 1ms - ✓ src/lib/validation-schemas.test.ts > InboundCreateSchema > missing warehouse_id fails 2ms - ✓ src/lib/validation-schemas.test.ts > InboundCreateSchema > missing inbound_date fails 1ms - ✓ src/lib/validation-schemas.test.ts > OutboundCreateSchema > valid data passes 3ms - ✓ src/lib/validation-schemas.test.ts > OutboundCreateSchema > missing items fails 1ms - ✓ src/lib/validation-schemas.test.ts > OutboundCreateSchema > empty items is allowed by schema 1ms - ✓ src/lib/validation-schemas.test.ts > OutboundCreateSchema > missing warehouseId fails 1ms - ✓ src/lib/validation-schemas.test.ts > WorkOrderCreateSchema > valid data passes 2ms - ✓ src/lib/validation-schemas.test.ts > WorkOrderCreateSchema > valid data with all optional fields passes 1ms - ✓ src/lib/validation-schemas.test.ts > WorkOrderCreateSchema > empty items is allowed by schema 0ms - ✓ src/lib/validation-schemas.test.ts > WorkOrderCreateSchema > missing order_no fails 1ms - ✓ src/lib/validation-schemas.test.ts > QualityInspectionSchema > valid data passes 1ms - ✓ src/lib/validation-schemas.test.ts > QualityInspectionSchema > valid data with all fields passes 0ms - ✓ src/lib/validation-schemas.test.ts > QualityInspectionSchema > invalid inspectResult fails 1ms - ✓ src/lib/validation-schemas.test.ts > QualityInspectionSchema > all valid inspect results pass 1ms - ✓ src/lib/validation-schemas.test.ts > QualityInspectionSchema > missing cardId fails 1ms - ✓ src/lib/validation-schemas.test.ts > validateWithZod > returns success for valid data 0ms - ✓ src/lib/validation-schemas.test.ts > validateWithZod > returns errors array for invalid data 1ms - ✓ src/lib/validation-schemas.test.ts > validateWithZod > errors contain path information 1ms - ✓ tests/unit/domain/purchase/aggregates/purchase-reconciliation.test.ts > PurchaseReconciliation 聚合根 > create() 工厂方法 > 合法参数创建成功,状态为草稿(1) 6ms - ✓ tests/unit/domain/purchase/aggregates/purchase-reconciliation.test.ts > PurchaseReconciliation 聚合根 > create() 工厂方法 > 含折扣金额时正确计算余额 1ms - ✓ tests/unit/domain/purchase/aggregates/purchase-reconciliation.test.ts > PurchaseReconciliation 聚合根 > create() 工厂方法 > 供应商ID为空抛错 1ms - ✓ tests/unit/domain/purchase/aggregates/purchase-reconciliation.test.ts > PurchaseReconciliation 聚合根 > create() 工厂方法 > 对账时段为空抛错 1ms - ✓ tests/unit/domain/purchase/aggregates/purchase-reconciliation.test.ts > PurchaseReconciliation 聚合根 > create() 工厂方法 > 开始日期晚于结束日期抛错 1ms - ✓ tests/unit/domain/purchase/aggregates/purchase-reconciliation.test.ts > PurchaseReconciliation 聚合根 > create() 工厂方法 > 退货金额超过收货金额抛错 1ms - ✓ tests/unit/domain/purchase/aggregates/purchase-reconciliation.test.ts > PurchaseReconciliation 聚合根 > create() 工厂方法 > 有 id 时发布 CreatedEvent 3ms - ✓ tests/unit/domain/purchase/aggregates/purchase-reconciliation.test.ts > PurchaseReconciliation 聚合根 > reconstitute() 重建方法 > 从 DB 字段重建聚合 1ms - ✓ tests/unit/domain/purchase/aggregates/purchase-reconciliation.test.ts > PurchaseReconciliation 聚合根 > reconstitute() 重建方法 > 未指定 balanceAmount 时自动计算 1ms - ✓ tests/unit/domain/purchase/aggregates/purchase-reconciliation.test.ts > PurchaseReconciliation 聚合根 > confirm() 确认流程 > 草稿 → 已确认,设置确认人 3ms - ✓ tests/unit/domain/purchase/aggregates/purchase-reconciliation.test.ts > PurchaseReconciliation 聚合根 > confirm() 确认流程 > 发布 ConfirmedEvent 1ms - ✓ tests/unit/domain/purchase/aggregates/purchase-reconciliation.test.ts > PurchaseReconciliation 聚合根 > confirm() 确认流程 > 已确认状态再次确认抛错 1ms - ✓ tests/unit/domain/purchase/aggregates/purchase-reconciliation.test.ts > PurchaseReconciliation 聚合根 > confirm() 确认流程 > 确认人为空抛错 0ms - ✓ tests/unit/domain/purchase/aggregates/purchase-reconciliation.test.ts > PurchaseReconciliation 聚合根 > writeOff() 核销流程 > 已确认 → 部分核销 1ms - ✓ tests/unit/domain/purchase/aggregates/purchase-reconciliation.test.ts > PurchaseReconciliation 聚合根 > writeOff() 核销流程 > 发布 PartialWrittenOffEvent 1ms - ✓ tests/unit/domain/purchase/aggregates/purchase-reconciliation.test.ts > PurchaseReconciliation 聚合根 > writeOff() 核销流程 > 全额核销 → 已核销完成(4) 1ms - ✓ tests/unit/domain/purchase/aggregates/purchase-reconciliation.test.ts > PurchaseReconciliation 聚合根 > writeOff() 核销流程 > 多次部分核销 1ms - ✓ tests/unit/domain/purchase/aggregates/purchase-reconciliation.test.ts > PurchaseReconciliation 聚合根 > writeOff() 核销流程 > 核销金额超过余额抛错 0ms - ✓ tests/unit/domain/purchase/aggregates/purchase-reconciliation.test.ts > PurchaseReconciliation 聚合根 > writeOff() 核销流程 > 核销金额为0抛错 1ms - ✓ tests/unit/domain/purchase/aggregates/purchase-reconciliation.test.ts > PurchaseReconciliation 聚合根 > writeOff() 核销流程 > 应付单ID为空抛错 0ms - ✓ tests/unit/domain/purchase/aggregates/purchase-reconciliation.test.ts > PurchaseReconciliation 聚合根 > writeOff() 核销流程 > 草稿状态核销抛错 1ms - ✓ tests/unit/domain/purchase/aggregates/purchase-reconciliation.test.ts > PurchaseReconciliation 聚合根 > writeOff() 核销流程 > getWriteOffSummary 返回按应付单汇总 2ms - ✓ tests/unit/domain/purchase/aggregates/purchase-reconciliation.test.ts > PurchaseReconciliation 聚合根 > close() 关闭流程 > 已核销完成 → 已关闭 1ms - ✓ tests/unit/domain/purchase/aggregates/purchase-reconciliation.test.ts > PurchaseReconciliation 聚合根 > close() 关闭流程 > 发布 ClosedEvent 1ms - ✓ tests/unit/domain/purchase/aggregates/purchase-reconciliation.test.ts > PurchaseReconciliation 聚合根 > close() 关闭流程 > 部分核销且有余额时关闭抛错 1ms - ✓ tests/unit/domain/purchase/aggregates/purchase-reconciliation.test.ts > PurchaseReconciliation 聚合根 > close() 关闭流程 > 部分核销但余额为0时关闭自动转为已核销 0ms - ✓ tests/unit/domain/purchase/aggregates/purchase-reconciliation.test.ts > PurchaseReconciliation 聚合根 > close() 关闭流程 > 草稿状态关闭抛错 0ms - ✓ tests/unit/domain/purchase/aggregates/purchase-reconciliation.test.ts > PurchaseReconciliation 聚合根 > close() 关闭流程 > 关闭人为空抛错 0ms - ✓ tests/unit/domain/purchase/aggregates/purchase-reconciliation.test.ts > PurchaseReconciliation 聚合根 > canEdit / canDelete > 草稿状态可编辑可删除 1ms - ✓ tests/unit/domain/purchase/aggregates/purchase-reconciliation.test.ts > PurchaseReconciliation 聚合根 > canEdit / canDelete > 已确认状态不可编辑不可删除 0ms - ✓ tests/unit/domain/purchase/aggregates/purchase-reconciliation.test.ts > PurchaseReconciliation 聚合根 > 领域事件管理 > getDomainEvents 返回副本(不可变) 1ms - ✓ tests/unit/domain/purchase/aggregates/purchase-reconciliation.test.ts > PurchaseReconciliation 聚合根 > 领域事件管理 > clearDomainEvents 清空事件 1ms - ✓ tests/unit/domain/purchase/aggregates/purchase-reconciliation.test.ts > PurchaseReconciliation 聚合根 > PurchaseReconciliationStatus 状态机 > 状态流转: 1→2→3→4→9 0ms - ✓ tests/unit/domain/purchase/aggregates/purchase-reconciliation.test.ts > PurchaseReconciliation 聚合根 > PurchaseReconciliationStatus 状态机 > 非法流转 1→3 抛错 1ms - ✓ tests/unit/domain/purchase/aggregates/purchase-reconciliation.test.ts > PurchaseReconciliation 聚合根 > PurchaseReconciliationStatus 状态机 > 非法流转 1→4 抛错 1ms - ✓ tests/unit/domain/purchase/aggregates/purchase-reconciliation.test.ts > PurchaseReconciliation 聚合根 > PurchaseReconciliationStatus 状态机 > canWriteOff: 已确认和部分核销可核销 0ms - ✓ tests/unit/domain/purchase/aggregates/purchase-reconciliation.test.ts > PurchaseReconciliation 聚合根 > PurchaseReconciliationStatus 状态机 > canClose: 仅已核销完成可关闭 0ms - ✓ tests/unit/domain/purchase/aggregates/purchase-reconciliation.test.ts > PurchaseReconciliation 聚合根 > PurchaseReconciliationStatus 状态机 > equals 正确比较 0ms - ✓ src/lib/validation.test.ts > 验证工具测试 > Validator.validateValue - 单值验证 > 应该验证必填字段 4ms - ✓ src/lib/validation.test.ts > 验证工具测试 > Validator.validateValue - 单值验证 > 应该通过非空必填字段 1ms - ✓ src/lib/validation.test.ts > 验证工具测试 > Validator.validateValue - 单值验证 > 应该跳过非必填空字段的其他验证 0ms - ✓ src/lib/validation.test.ts > 验证工具测试 > Validator.validateValue - 单值验证 > 应该验证字符串类型 1ms - ✓ src/lib/validation.test.ts > 验证工具测试 > Validator.validateValue - 单值验证 > 应该验证数字类型 0ms - ✓ src/lib/validation.test.ts > 验证工具测试 > Validator.validateValue - 单值验证 > 应该验证邮箱格式 0ms - ✓ src/lib/validation.test.ts > 验证工具测试 > Validator.validateValue - 单值验证 > 应该通过有效邮箱 0ms - ✓ src/lib/validation.test.ts > 验证工具测试 > Validator.validateValue - 单值验证 > 应该验证手机号格式 0ms - ✓ src/lib/validation.test.ts > 验证工具测试 > Validator.validateValue - 单值验证 > 应该通过有效手机号 1ms - ✓ src/lib/validation.test.ts > 验证工具测试 > Validator.validateValue - 单值验证 > 应该验证最小长度 0ms - ✓ src/lib/validation.test.ts > 验证工具测试 > Validator.validateValue - 单值验证 > 应该验证最大长度 0ms - ✓ src/lib/validation.test.ts > 验证工具测试 > Validator.validateValue - 单值验证 > 应该验证数值最小值 0ms - ✓ src/lib/validation.test.ts > 验证工具测试 > Validator.validateValue - 单值验证 > 应该验证数值最大值 0ms - ✓ src/lib/validation.test.ts > 验证工具测试 > Validator.validateValue - 单值验证 > 应该验证枚举值 0ms - ✓ src/lib/validation.test.ts > 验证工具测试 > Validator.validateValue - 单值验证 > 应该通过有效枚举值 0ms - ✓ src/lib/validation.test.ts > 验证工具测试 > Validator.validateValue - 单值验证 > 应该验证正则表达式 0ms - ✓ src/lib/validation.test.ts > 验证工具测试 > Validator.validateValue - 单值验证 > 应该通过正则表达式验证 0ms - ✓ src/lib/validation.test.ts > 验证工具测试 > Validator.validateValue - 单值验证 > 应该支持自定义验证函数 0ms - ✓ src/lib/validation.test.ts > 验证工具测试 > Validator.validateValue - 单值验证 > 应该通过自定义验证 0ms - ✓ src/lib/validation.test.ts > 验证工具测试 > Validator.validate - 对象验证 > 应该验证对象中的多个字段 2ms - ✓ src/lib/validation.test.ts > 验证工具测试 > Validator.validate - 对象验证 > 应该通过有效对象验证 1ms - ✓ src/lib/validation.test.ts > 验证工具测试 > CommonValidations - 常用验证 > 应该验证分页参数 1ms - ✓ src/lib/validation.test.ts > 验证工具测试 > CommonValidations - 常用验证 > 应该通过有效分页参数 0ms - ✓ src/lib/validation.test.ts > 验证工具测试 > CommonValidations - 常用验证 > 应该验证员工信息 1ms - ✓ src/lib/validation.test.ts > 验证工具测试 > CommonValidations - 常用验证 > 应该通过有效员工信息 0ms - ✓ src/lib/validation.test.ts > 验证工具测试 > CommonValidations - 常用验证 > 应该验证薪资信息 1ms - ✓ src/lib/validation.test.ts > 验证工具测试 > CommonValidations - 常用验证 > 应该通过有效薪资信息 1ms - ✓ src/lib/validation.test.ts > 验证工具测试 > CommonValidations - 常用验证 > 应该验证品质检验信息 1ms - ✓ src/lib/validation.test.ts > 验证工具测试 > CommonValidations - 常用验证 > 应该通过有效品质检验信息 0ms - ✓ src/lib/validation.test.ts > 验证工具测试 > ValidationPresets - 预设验证 > 应该验证编码格式 0ms - ✓ src/lib/validation.test.ts > 验证工具测试 > ValidationPresets - 预设验证 > 应该验证名称格式 0ms - ✓ src/lib/validation.test.ts > 验证工具测试 > ValidationPresets - 预设验证 > 应该验证邮箱格式 0ms - ✓ src/lib/validation.test.ts > 验证工具测试 > ValidationPresets - 预设验证 > 应该验证手机号格式 0ms - ✓ src/lib/validation.test.ts > 验证工具测试 > ValidationPresets - 预设验证 > 应该验证数量范围 0ms - ✓ src/lib/validation.test.ts > 验证工具测试 > ValidationPresets - 预设验证 > 应该验证金额小数位 1ms - ✓ src/lib/validation.test.ts > 验证工具测试 > ValidationPresets - 预设验证 > 应该验证状态枚举 1ms - ✓ src/lib/validation.test.ts > 验证工具测试 > ValidationPresets - 预设验证 > 应该验证日期类型 0ms - ✓ src/lib/validation.test.ts > 验证工具测试 > ValidationPresets - 预设验证 > 应该验证备注长度 1ms - ✓ src/lib/validation.test.ts > 验证工具测试 > validateRequestBody - 请求体验证 > 应该验证请求体 0ms - ✓ src/lib/validation.test.ts > 验证工具测试 > validateRequestBody - 请求体验证 > 应该通过有效请求体 0ms - ✓ src/lib/validation.test.ts > 验证工具测试 > Validator.validateValue - 边界情况 > 应该处理空字符串 0ms - ✓ src/lib/validation.test.ts > 验证工具测试 > Validator.validateValue - 边界情况 > 应该处理null值 0ms - ✓ src/lib/validation.test.ts > 验证工具测试 > Validator.validateValue - 边界情况 > 应该处理undefined值 0ms - ✓ src/lib/validation.test.ts > 验证工具测试 > Validator.validateValue - 边界情况 > 应该处理非必填的null值 0ms - ✓ src/lib/validation.test.ts > 验证工具测试 > Validator.validateValue - 边界情况 > 应该验证字符串最小长度 0ms - ✓ src/lib/validation.test.ts > 验证工具测试 > Validator.validateValue - 边界情况 > 应该验证字符串最大长度 0ms - ✓ src/lib/validation.test.ts > 验证工具测试 > Validator.validateValue - 边界情况 > 应该验证数字最小值 0ms - ✓ src/lib/validation.test.ts > 验证工具测试 > Validator.validateValue - 边界情况 > 应该验证数字最大值 0ms - ✓ src/lib/validation.test.ts > 验证工具测试 > Validator.validateValue - 边界情况 > 应该验证枚举值 0ms - ✓ src/lib/validation.test.ts > 验证工具测试 > Validator.validateValue - 边界情况 > 应该验证正则表达式 0ms -stderr | tests/integration/inbound-api.test.ts -[RepoRegistry] active impl = mysql (default; set REPOSITORY_IMPL=drizzle to switch) - -stderr | tests/integration/inbound-api.test.ts > 入库 API 集成测试 > GET /api/warehouse/inbound - 查询入库单列表 > 正常流程:返回分页列表数据 -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) - -stderr | tests/integration/inbound-api.test.ts > 入库 API 集成测试 > GET /api/warehouse/inbound - 查询入库单列表 > 支持按状态筛选 -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) - -stderr | tests/integration/inbound-api.test.ts > 入库 API 集成测试 > GET /api/warehouse/inbound - 查询入库单列表 > 使用默认分页参数(page=1, pageSize=10) -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) - -stderr | tests/integration/inbound-api.test.ts > 入库 API 集成测试 > GET /api/warehouse/inbound - 查询入库单列表 > service 抛错时返回 500 -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) - -stderr | tests/integration/inbound-api.test.ts > 入库 API 集成测试 > POST /api/warehouse/inbound - 创建入库单 > 正常流程:创建成功返回 order_id 和 order_no -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) - -stderr | tests/integration/inbound-api.test.ts > 入库 API 集成测试 > POST /api/warehouse/inbound - 创建入库单 > service 抛 DomainError 时返回 500(未被路由捕获) -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) - -stderr | tests/integration/inbound-api.test.ts > 入库 API 集成测试 > PUT /api/warehouse/inbound - 更新入库单 > 正常流程:submit 动作调用 submitOrder -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) - -stderr | tests/integration/inbound-api.test.ts > 入库 API 集成测试 > PUT /api/warehouse/inbound - 更新入库单 > 正常流程:approve 动作调用 approveOrder -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) - -stderr | tests/integration/inbound-api.test.ts > 入库 API 集成测试 > PUT /api/warehouse/inbound - 更新入库单 > 正常流程:cancel 动作调用 cancelOrder -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) - -stderr | tests/integration/inbound-api.test.ts > 入库 API 集成测试 > PUT /api/warehouse/inbound - 更新入库单 > 正常流程:unapprove 动作调用 unapproveOrder -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) - -stderr | tests/integration/inbound-api.test.ts > 入库 API 集成测试 > PUT /api/warehouse/inbound - 更新入库单 > 使用 status=pending 等价于 submit 动作 -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) - -stderr | tests/integration/inbound-api.test.ts > 入库 API 集成测试 > PUT /api/warehouse/inbound - 更新入库单 > 使用 status=approved 等价于 approve 动作 -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) - - ✓ tests/integration/inbound-api.test.ts > 入库 API 集成测试 > GET /api/warehouse/inbound - 查询入库单列表 > 正常流程:返回分页列表数据 26ms - ✓ tests/integration/inbound-api.test.ts > 入库 API 集成测试 > GET /api/warehouse/inbound - 查询入库单列表 > 支持按状态筛选 5ms - ✓ tests/integration/inbound-api.test.ts > 入库 API 集成测试 > GET /api/warehouse/inbound - 查询入库单列表 > 使用默认分页参数(page=1, pageSize=10) 4ms - ✓ tests/integration/inbound-api.test.ts > 入库 API 集成测试 > GET /api/warehouse/inbound - 查询入库单列表 > service 抛错时返回 500 3ms - ✓ tests/integration/inbound-api.test.ts > 入库 API 集成测试 > POST /api/warehouse/inbound - 创建入库单 > 正常流程:创建成功返回 order_id 和 order_no 17ms - ✓ tests/integration/inbound-api.test.ts > 入库 API 集成测试 > POST /api/warehouse/inbound - 创建入库单 > 参数异常:缺少 warehouse_id 返回 422 6ms - ✓ tests/integration/inbound-api.test.ts > 入库 API 集成测试 > POST /api/warehouse/inbound - 创建入库单 > 参数异常:items 为空数组返回 422 4ms - ✓ tests/integration/inbound-api.test.ts > 入库 API 集成测试 > POST /api/warehouse/inbound - 创建入库单 > 参数异常:quantity 为负数返回 422 4ms - ✓ tests/integration/inbound-api.test.ts > 入库 API 集成测试 > POST /api/warehouse/inbound - 创建入库单 > 参数异常:unit_price 为负数返回 422 7ms - ✓ tests/integration/inbound-api.test.ts > 入库 API 集成测试 > POST /api/warehouse/inbound - 创建入库单 > service 抛 DomainError 时返回 500(未被路由捕获) 3ms - ✓ tests/integration/inbound-api.test.ts > 入库 API 集成测试 > POST /api/warehouse/inbound - 创建入库单 > ERR-IN 物料未归类且 require_on_business 阻断时返回 400 3ms - ✓ tests/integration/inbound-api.test.ts > 入库 API 集成测试 > PUT /api/warehouse/inbound - 更新入库单 > 正常流程:submit 动作调用 submitOrder 4ms - ✓ tests/integration/inbound-api.test.ts > 入库 API 集成测试 > PUT /api/warehouse/inbound - 更新入库单 > 正常流程:approve 动作调用 approveOrder 2ms - ✓ tests/integration/inbound-api.test.ts > 入库 API 集成测试 > PUT /api/warehouse/inbound - 更新入库单 > 正常流程:cancel 动作调用 cancelOrder 2ms - ✓ tests/integration/inbound-api.test.ts > 入库 API 集成测试 > PUT /api/warehouse/inbound - 更新入库单 > 正常流程:unapprove 动作调用 unapproveOrder 3ms - ✓ tests/integration/inbound-api.test.ts > 入库 API 集成测试 > PUT /api/warehouse/inbound - 更新入库单 > 使用 status=pending 等价于 submit 动作 2ms - ✓ tests/integration/inbound-api.test.ts > 入库 API 集成测试 > PUT /api/warehouse/inbound - 更新入库单 > 使用 status=approved 等价于 approve 动作 2ms -stderr | tests/integration/inbound-api.test.ts > 入库 API 集成测试 > PUT /api/warehouse/inbound - 更新入库单 > 领域异常:NotFoundError 返回 404 -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) - -stderr | tests/integration/inbound-api.test.ts > 入库 API 集成测试 > PUT /api/warehouse/inbound - 更新入库单 > 领域异常:VersionConflictError 返回 409 -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) - -stderr | tests/integration/inbound-api.test.ts > 入库 API 集成测试 > PUT /api/warehouse/inbound - 更新入库单 > 领域异常:DomainError 返回 400 -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) - -stderr | tests/integration/inbound-api.test.ts > 入库 API 集成测试 > PUT /api/warehouse/inbound - 更新入库单 > 仅更新 remark 字段时走直接 SQL 更新 -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) - -stderr | tests/integration/inbound-api.test.ts > 入库 API 集成测试 > DELETE /api/warehouse/inbound - 删除入库单 > 正常流程:删除草稿状态入库单 -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) - -stderr | tests/integration/inbound-api.test.ts > 入库 API 集成测试 > DELETE /api/warehouse/inbound - 删除入库单 > 领域异常:NotFoundError 返回 404 -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) - -stderr | tests/integration/inbound-api.test.ts > 入库 API 集成测试 > DELETE /api/warehouse/inbound - 删除入库单 > 领域异常:DomainError(已审核不能删除)返回 400 -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) - -stderr | tests/integration/inbound-api.test.ts > 入库 API 集成测试 > DELETE /api/warehouse/inbound - 删除入库单 > 参数异常:id 非数字时正常处理为 parseInt -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) - - ✓ tests/integration/inbound-api.test.ts > 入库 API 集成测试 > PUT /api/warehouse/inbound - 更新入库单 > 领域异常:NotFoundError 返回 404 3ms - ✓ tests/integration/inbound-api.test.ts > 入库 API 集成测试 > PUT /api/warehouse/inbound - 更新入库单 > 领域异常:VersionConflictError 返回 409 2ms - ✓ tests/integration/inbound-api.test.ts > 入库 API 集成测试 > PUT /api/warehouse/inbound - 更新入库单 > 领域异常:DomainError 返回 400 2ms - ✓ tests/integration/inbound-api.test.ts > 入库 API 集成测试 > PUT /api/warehouse/inbound - 更新入库单 > 参数异常:缺少 id 返回 422 2ms - ✓ tests/integration/inbound-api.test.ts > 入库 API 集成测试 > PUT /api/warehouse/inbound - 更新入库单 > 未知 action 返回 422(schema 枚举校验) 2ms - ✓ tests/integration/inbound-api.test.ts > 入库 API 集成测试 > PUT /api/warehouse/inbound - 更新入库单 > 仅更新 remark 字段时走直接 SQL 更新 4ms - ✓ tests/integration/inbound-api.test.ts > 入库 API 集成测试 > DELETE /api/warehouse/inbound - 删除入库单 > 正常流程:删除草稿状态入库单 2ms - ✓ tests/integration/inbound-api.test.ts > 入库 API 集成测试 > DELETE /api/warehouse/inbound - 删除入库单 > 参数异常:缺少 id 返回 400 1ms - ✓ tests/integration/inbound-api.test.ts > 入库 API 集成测试 > DELETE /api/warehouse/inbound - 删除入库单 > 领域异常:NotFoundError 返回 404 1ms - ✓ tests/integration/inbound-api.test.ts > 入库 API 集成测试 > DELETE /api/warehouse/inbound - 删除入库单 > 领域异常:DomainError(已审核不能删除)返回 400 2ms - ✓ tests/integration/inbound-api.test.ts > 入库 API 集成测试 > DELETE /api/warehouse/inbound - 删除入库单 > 参数异常:id 非数字时正常处理为 parseInt 2ms - ✓ tests/integration/inbound-api.test.ts > 入库 API 集成测试 > PATCH /api/warehouse/inbound - 恢复已删除入库单(软删除撤销) > 正常流程:恢复已删除的入库单 3ms - ✓ tests/integration/inbound-api.test.ts > 入库 API 集成测试 > PATCH /api/warehouse/inbound - 恢复已删除入库单(软删除撤销) > 参数异常:缺少 id 返回 400 1ms - ✓ tests/integration/inbound-api.test.ts > 入库 API 集成测试 > PATCH /api/warehouse/inbound - 恢复已删除入库单(软删除撤销) > 记录不存在:未找到已删除记录返回 404 1ms - ✓ tests/integration/inbound-api.test.ts > 入库 API 集成测试 > PATCH /api/warehouse/inbound - 恢复已删除入库单(软删除撤销) > DB 查询错误返回 500 2ms - ✓ tests/integration/inbound-api.test.ts > 入库 API 集成测试 > PATCH /api/warehouse/inbound - 恢复已删除入库单(软删除撤销) > DB 更新错误返回 500 2ms - ✓ tests/integration/inbound-api.test.ts > 入库 API 集成测试 > PATCH /api/warehouse/inbound - 恢复已删除入库单(软删除撤销) > 非数字 id 正常处理为 parseInt 2ms -[2026-08-27 11:31:56.670 +0800] INFO: [warehouse/inbound] 开始物料分类校验 - itemCount: 1 - materialIds: [ - 101 - ] -[2026-08-27 11:31:56.672 +0800] INFO: [warehouse/inbound] 物料分类校验完成 - blocked: false - uncategorizedCount: 0 -[2026-08-27 11:31:56.699 +0800] INFO: [warehouse/inbound] 开始物料分类校验 - itemCount: 1 - materialIds: [ - 101 - ] -[2026-08-27 11:31:56.699 +0800] INFO: [warehouse/inbound] 物料分类校验完成 - blocked: false - uncategorizedCount: 0 -[2026-08-27 11:31:56.702 +0800] INFO: [warehouse/inbound] 开始物料分类校验 - itemCount: 1 - materialIds: [ - 101 - ] -[2026-08-27 11:31:56.703 +0800] INFO: [warehouse/inbound] 物料分类校验完成 - blocked: true - uncategorizedCount: 1 -[2026-08-27 11:31:56.703 +0800] WARN: [warehouse/inbound] 物料分类校验阻断提交 - message: "以下物料尚未设置物料分类:M001(材料1)。请先在「基础数据 → 物料分类」中归类。" - ✓ tests/unit/domain/quality/aggregates/unqualified-product.test.ts > UnqualifiedProduct Aggregate > create() > creates a valid product with pending status 6ms - ✓ tests/unit/domain/quality/aggregates/unqualified-product.test.ts > UnqualifiedProduct Aggregate > create() > pushes UnqualifiedCreatedEvent when id is set 4ms - ✓ tests/unit/domain/quality/aggregates/unqualified-product.test.ts > UnqualifiedProduct Aggregate > create() > does not push event when id is undefined 1ms - ✓ tests/unit/domain/quality/aggregates/unqualified-product.test.ts > UnqualifiedProduct Aggregate > create() > throws DomainError when inspectionId <= 0 1ms - ✓ tests/unit/domain/quality/aggregates/unqualified-product.test.ts > UnqualifiedProduct Aggregate > create() > throws DomainError when quantity <= 0 1ms - ✓ tests/unit/domain/quality/aggregates/unqualified-product.test.ts > UnqualifiedProduct Aggregate > create() > throws DomainError when quantity is undefined 0ms - ✓ tests/unit/domain/quality/aggregates/unqualified-product.test.ts > UnqualifiedProduct Aggregate > create() > initializes handleType value object when provided 1ms - ✓ tests/unit/domain/quality/aggregates/unqualified-product.test.ts > UnqualifiedProduct Aggregate > reconstitute() > rebuilds product without triggering events 1ms - ✓ tests/unit/domain/quality/aggregates/unqualified-product.test.ts > UnqualifiedProduct Aggregate > reconstitute() > preserves all fields from props 1ms - ✓ tests/unit/domain/quality/aggregates/unqualified-product.test.ts > UnqualifiedProduct Aggregate > assignResponsible() > assigns dept and person when pending 1ms - ✓ tests/unit/domain/quality/aggregates/unqualified-product.test.ts > UnqualifiedProduct Aggregate > assignResponsible() > assigns dept and person when handling 0ms - ✓ tests/unit/domain/quality/aggregates/unqualified-product.test.ts > UnqualifiedProduct Aggregate > assignResponsible() > throws DomainError when completed 0ms - ✓ tests/unit/domain/quality/aggregates/unqualified-product.test.ts > UnqualifiedProduct Aggregate > assignResponsible() > throws DomainError when dept is empty 1ms - ✓ tests/unit/domain/quality/aggregates/unqualified-product.test.ts > UnqualifiedProduct Aggregate > assignResponsible() > throws DomainError when person is empty 0ms - ✓ tests/unit/domain/quality/aggregates/unqualified-product.test.ts > UnqualifiedProduct Aggregate > assignResponsible() > trims whitespace in dept and person 0ms - ✓ tests/unit/domain/quality/aggregates/unqualified-product.test.ts > UnqualifiedProduct Aggregate > startHandle() > transitions pending -> handling and pushes HandlingStartedEvent 1ms - ✓ tests/unit/domain/quality/aggregates/unqualified-product.test.ts > UnqualifiedProduct Aggregate > startHandle() > throws DomainError when status is completed 0ms - ✓ tests/unit/domain/quality/aggregates/unqualified-product.test.ts > UnqualifiedProduct Aggregate > startHandle() > throws DomainError when status is already handling 0ms - ✓ tests/unit/domain/quality/aggregates/unqualified-product.test.ts > UnqualifiedProduct Aggregate > startHandle() > throws DomainError when responsibleDept is empty 0ms - ✓ tests/unit/domain/quality/aggregates/unqualified-product.test.ts > UnqualifiedProduct Aggregate > startHandle() > throws DomainError when responsiblePerson is empty 1ms - ✓ tests/unit/domain/quality/aggregates/unqualified-product.test.ts > UnqualifiedProduct Aggregate > completeHandle() > transitions handling -> completed and pushes UnqualifiedCompletedEvent 1ms - ✓ tests/unit/domain/quality/aggregates/unqualified-product.test.ts > UnqualifiedProduct Aggregate > completeHandle() > throws DomainError when status is pending (no skip) 0ms - ✓ tests/unit/domain/quality/aggregates/unqualified-product.test.ts > UnqualifiedProduct Aggregate > completeHandle() > throws DomainError when responsibleDept is missing 0ms - ✓ tests/unit/domain/quality/aggregates/unqualified-product.test.ts > UnqualifiedProduct Aggregate > completeHandle() > throws DomainError when responsiblePerson is missing 0ms - ✓ tests/unit/domain/quality/aggregates/unqualified-product.test.ts > UnqualifiedProduct Aggregate > completeHandle() > throws DomainError when handler is empty 0ms - ✓ tests/unit/domain/quality/aggregates/unqualified-product.test.ts > UnqualifiedProduct Aggregate > completeHandle() > throws DomainError when handleResult is invalid 0ms - ✓ tests/unit/domain/quality/aggregates/unqualified-product.test.ts > UnqualifiedProduct Aggregate > completeHandle() > throws DomainError when costAmount is negative 0ms - ✓ tests/unit/domain/quality/aggregates/unqualified-product.test.ts > UnqualifiedProduct Aggregate > completeHandle() > accepts costAmount = 0 0ms - ✓ tests/unit/domain/quality/aggregates/unqualified-product.test.ts > UnqualifiedProduct Aggregate > canEdit() / canDelete() > pending allows edit and delete 0ms - ✓ tests/unit/domain/quality/aggregates/unqualified-product.test.ts > UnqualifiedProduct Aggregate > canEdit() / canDelete() > handling allows edit but not delete 0ms - ✓ tests/unit/domain/quality/aggregates/unqualified-product.test.ts > UnqualifiedProduct Aggregate > canEdit() / canDelete() > completed disallows both edit and delete 0ms - ✓ tests/unit/domain/quality/aggregates/unqualified-product.test.ts > UnqualifiedProduct Aggregate > lifecycle events > accumulates events through full lifecycle 2ms - ✓ tests/unit/domain/quality/aggregates/unqualified-product.test.ts > UnqualifiedProduct Aggregate > lifecycle events > clearDomainEvents() empties the events list 1ms - ✓ tests/unit/domain/quality/aggregates/unqualified-product.test.ts > UnqualifiedProduct Aggregate > lifecycle events > getDomainEvents() returns a copy (immutable) 1ms - ✓ tests/unit/application/services/QRCodeApplicationService.test.ts > QRCodeApplicationService.generateBatchQr > generates batch QR codes and persists events 12ms - ✓ tests/unit/application/services/QRCodeApplicationService.test.ts > QRCodeApplicationService.generateBatchQr > throws if count <= 0 3ms - ✓ tests/unit/application/services/QRCodeApplicationService.test.ts > QRCodeApplicationService.generateBatchQr > throws if quantity <= 0 1ms - ✓ tests/unit/application/services/QRCodeApplicationService.test.ts > QRCodeApplicationService.splitParentQr > splits parent QR into children 7ms - ✓ tests/unit/application/services/QRCodeApplicationService.test.ts > QRCodeApplicationService.splitParentQr > throws if parent not found 1ms - ✓ tests/unit/application/services/QRCodeApplicationService.test.ts > QRCodeApplicationService.splitParentQr > throws if total split quantity exceeds parent 1ms - ✓ tests/unit/application/services/QRCodeApplicationService.test.ts > QRCodeApplicationService.recordScan > records scan and updates scan count 2ms - ✓ tests/unit/application/services/QRCodeApplicationService.test.ts > QRCodeApplicationService.recordScan > throws if QR not found 1ms - ✓ tests/unit/application/services/QRCodeApplicationService.test.ts > QRCodeApplicationService.getTraceTimeline > returns timeline for existing QR 2ms - ✓ tests/unit/application/services/QRCodeApplicationService.test.ts > QRCodeApplicationService.getTraceTimeline > throws if QR not found 1ms - ✓ tests/unit/application/services/QRCodeApplicationService.test.ts > QRCodeApplicationService.recordPrint > records print log and updates print count 2ms - ✓ tests/unit/application/services/QRCodeApplicationService.test.ts > QRCodeApplicationService.recordPrint > throws if QR not found 1ms - ✓ tests/unit/application/handlers/FinishOrderInventoryHandler-idempotent.test.ts > T402: FinishOrderInventoryHandler 幂等性 + 批次回写 > 已存在事务时跳过处理(INSERT IGNORE affectedRows=0,幂等) 9ms - ✓ tests/unit/application/handlers/FinishOrderInventoryHandler-idempotent.test.ts > T402: FinishOrderInventoryHandler 幂等性 + 批次回写 > 首次处理:建批次 + 派生汇总(recompute 被调用一次) 2ms - ✓ tests/unit/application/handlers/FinishOrderInventoryHandler-idempotent.test.ts > T402: FinishOrderInventoryHandler 幂等性 + 批次回写 > 批次已存在时累加而非重复建批次 1ms - ✓ tests/unit/application/handlers/FinishOrderInventoryHandler-idempotent.test.ts > T402: FinishOrderInventoryHandler 幂等性 + 批次回写 > 并发场景:两个消费者同时处理同一事件,仅一个成功(TOCTOU 修复验证) 3ms - ✓ tests/unit/application/handlers/FinishOrderInventoryHandler-idempotent.test.ts > T402: FinishOrderInventoryHandler 幂等性 + 批次回写 > 并发场景:Promise.all 同时处理同一事件,INSERT IGNORE + 唯一索引保证仅一个成功 1ms - ✓ tests/unit/application/handlers/FinishOrderInventoryHandler-idempotent.test.ts > T402: FinishOrderInventoryHandler 幂等性 + 批次回写 > 第一个消费者失败后,第二个消费者可以成功处理(事务回滚不阻塞重试) 5ms - ✓ tests/unit/application/handlers/FinishOrderInventoryHandler-idempotent.test.ts > T402: FinishOrderInventoryHandler 幂等性 + 批次回写 > 事务回滚后幂等记录也被回滚,允许重试(同一 handler 重试成功) 2ms - ✓ src/__tests__/validations/inbound-schema.test.ts > 入库单 Zod 校验回归测试 > createInboundOrderSchema > 应通过合法的入库单数据 11ms - ✓ src/__tests__/validations/inbound-schema.test.ts > 入库单 Zod 校验回归测试 > createInboundOrderSchema > 应拒绝空的 items 数组 4ms - ✓ src/__tests__/validations/inbound-schema.test.ts > 入库单 Zod 校验回归测试 > createInboundOrderSchema > 应拒绝缺少 warehouse_id 2ms - ✓ src/__tests__/validations/inbound-schema.test.ts > 入库单 Zod 校验回归测试 > createInboundOrderSchema > 应拒绝负数的 quantity 1ms - ✓ src/__tests__/validations/inbound-schema.test.ts > 入库单 Zod 校验回归测试 > createInboundOrderSchema > 应拒绝负数的 unit_price 1ms - ✓ src/__tests__/validations/inbound-schema.test.ts > 入库单 Zod 校验回归测试 > createInboundOrderSchema > 应拒绝超过100个入库项 3ms - ✓ src/__tests__/validations/inbound-schema.test.ts > 入库单 Zod 校验回归测试 > createInboundOrderSchema > 应设置默认值 1ms - ✓ src/__tests__/validations/inbound-schema.test.ts > 入库单 Zod 校验回归测试 > createInboundOrderSchema > 应拒绝过长的物料名称 1ms - ✓ src/__tests__/validations/inbound-schema.test.ts > 入库单 Zod 校验回归测试 > createInboundOrderSchema > 应拒绝非整数的 material_id 2ms - ✓ src/__tests__/validations/inbound-schema.test.ts > 入库单 Zod 校验回归测试 > updateInboundOrderSchema > 应通过合法的更新数据 2ms - ✓ src/__tests__/validations/inbound-schema.test.ts > 入库单 Zod 校验回归测试 > updateInboundOrderSchema > 应拒绝缺少 id 1ms - ✓ src/__tests__/validations/inbound-schema.test.ts > 入库单 Zod 校验回归测试 > updateInboundOrderSchema > 应拒绝无效的 action 2ms - ✓ src/__tests__/validations/inbound-schema.test.ts > 入库单 Zod 校验回归测试 > updateInboundOrderSchema > 应接受所有合法的 action 1ms - ✓ src/__tests__/validations/inbound-schema.test.ts > 入库单 Zod 校验回归测试 > updateInboundOrderSchema > 应拒绝负数的 id 1ms - ✓ src/__tests__/validations/inbound-schema.test.ts > 入库单 Zod 校验回归测试 > updateInboundOrderSchema > 应拒绝过长的备注 1ms - ✓ tests/unit/domain/finance/aggregates/voucher.test.ts > Voucher 聚合根 > create() 工厂方法 > 合法参数创建成功,状态为 DRAFT(0) 7ms - ✓ tests/unit/domain/finance/aggregates/voucher.test.ts > Voucher 聚合根 > create() 工厂方法 > 有 id 时发布 VoucherCreatedEvent 3ms - ✓ tests/unit/domain/finance/aggregates/voucher.test.ts > Voucher 聚合根 > create() 工厂方法 > 无 id 时不发布创建事件 1ms - ✓ tests/unit/domain/finance/aggregates/voucher.test.ts > Voucher 聚合根 > create() 工厂方法 > periodCode 为空抛 DomainError 2ms - ✓ tests/unit/domain/finance/aggregates/voucher.test.ts > Voucher 聚合根 > create() 工厂方法 > lines 为空数组抛 DomainError 1ms - ✓ tests/unit/domain/finance/aggregates/voucher.test.ts > Voucher 聚合根 > create() 工厂方法 > 借贷不平衡抛 DomainError 1ms - ✓ tests/unit/domain/finance/aggregates/voucher.test.ts > Voucher 聚合根 > reconstitute() 重建方法 > 从 DB 字段重建聚合 1ms - ✓ tests/unit/domain/finance/aggregates/voucher.test.ts > Voucher 聚合根 > reconstitute() 重建方法 > 未指定 totalDebit/totalCredit 时自动计算 1ms - ✓ tests/unit/domain/finance/aggregates/voucher.test.ts > Voucher 聚合根 > submit() 提交流程 > DRAFT → SUBMITTED,发布 SubmittedEvent 2ms - ✓ tests/unit/domain/finance/aggregates/voucher.test.ts > Voucher 聚合根 > submit() 提交流程 > 非 DRAFT 状态提交抛错 1ms - ✓ tests/unit/domain/finance/aggregates/voucher.test.ts > Voucher 聚合根 > audit() 审核流程 > SUBMITTED → AUDITED,设置审核人 1ms - ✓ tests/unit/domain/finance/aggregates/voucher.test.ts > Voucher 聚合根 > audit() 审核流程 > 非 SUBMITTED 状态审核抛错 0ms - ✓ tests/unit/domain/finance/aggregates/voucher.test.ts > Voucher 聚合根 > post() 记账流程 > AUDITED → POSTED,设置记账人,发布 PostedEvent(含明细) 1ms - ✓ tests/unit/domain/finance/aggregates/voucher.test.ts > Voucher 聚合根 > post() 记账流程 > 非 AUDITED 状态记账抛错 0ms - ✓ tests/unit/domain/finance/aggregates/voucher.test.ts > Voucher 聚合根 > void() 作废流程 > DRAFT → VOIDED,发布 VoidedEvent 1ms - ✓ tests/unit/domain/finance/aggregates/voucher.test.ts > Voucher 聚合根 > void() 作废流程 > SUBMITTED → VOIDED 0ms - ✓ tests/unit/domain/finance/aggregates/voucher.test.ts > Voucher 聚合根 > void() 作废流程 > POSTED → VOIDED 抛错(已记账不可作废) 0ms - ✓ tests/unit/domain/finance/aggregates/voucher.test.ts > Voucher 聚合根 > canEdit/canDelete 权限 > DRAFT 状态可编辑可删除 0ms - ✓ tests/unit/domain/finance/aggregates/voucher.test.ts > Voucher 聚合根 > canEdit/canDelete 权限 > SUBMITTED 状态不可编辑不可删除 0ms - ✓ tests/unit/domain/finance/aggregates/voucher.test.ts > Voucher 聚合根 > 领域事件管理 > getDomainEvents 返回副本(不可变) 0ms - ✓ tests/unit/domain/finance/aggregates/voucher.test.ts > Voucher 聚合根 > 领域事件管理 > clearDomainEvents 清空事件 1ms - ✓ tests/unit/domain/finance/aggregates/voucher.test.ts > Voucher 聚合根 > lines 访问器返回副本 > 外部修改 lines 数组不影响内部状态 1ms - ✓ tests/integration/material-issue-flow.test.ts > 领料流程集成测试 > 正常领料流程 > 应该完成正常领料流程:生成 → 审批 → 出库 8ms - ✓ tests/integration/material-issue-flow.test.ts > 领料流程集成测试 > 正常领料流程 > 应该正确计算领料成本 1ms - ✓ tests/integration/material-issue-flow.test.ts > 领料流程集成测试 > 超领流程 > 应该完成超领流程:申请 → 审批 → 出库 2ms - ✓ tests/integration/material-issue-flow.test.ts > 领料流程集成测试 > 超领流程 > 应该验证超领比例 1ms - ✓ tests/integration/material-issue-flow.test.ts > 领料流程集成测试 > 超领流程 > 应该拒绝超过上限的超领 1ms - ✓ tests/integration/material-issue-flow.test.ts > 领料流程集成测试 > 补料流程 > 应该完成补料流程:申请 → 审批 → 出库 1ms - ✓ tests/integration/material-issue-flow.test.ts > 领料流程集成测试 > 退料流程 > 应该完成退料流程:创建 → 确认 → 入库 1ms - ✓ tests/integration/material-issue-flow.test.ts > 领料流程集成测试 > 退料流程 > 应该正确计算退料成本 1ms - ✓ tests/integration/material-issue-flow.test.ts > 领料流程集成测试 > FIFO校验 > 应该验证是否按FIFO顺序出库 1ms - ✓ tests/integration/material-issue-flow.test.ts > 领料流程集成测试 > FIFO校验 > 应该允许跳过FIFO(需要审批) 1ms - ✓ tests/integration/material-issue-flow.test.ts > 领料流程集成测试 > 整料校验 > 应该检测整料出库 1ms - ✓ tests/integration/material-issue-flow.test.ts > 领料流程集成测试 > 整料校验 > 应该允许整料出库 0ms - ✓ tests/integration/material-issue-flow.test.ts > 领料流程集成测试 > 并发控制 > 应该防止重复出库 0ms - ✓ tests/integration/material-issue-flow.test.ts > 领料流程集成测试 > 并发控制 > 应该使用乐观锁防止并发修改 2ms - ✓ tests/integration/material-issue-flow.test.ts > 领料流程集成测试 > 业务场景 > 场景1:生产领料 0ms - ✓ tests/integration/material-issue-flow.test.ts > 领料流程集成测试 > 业务场景 > 场景2:维修领料 1ms - ✓ tests/integration/material-issue-flow.test.ts > 领料流程集成测试 > 业务场景 > 场景3:研发领料 0ms - ✓ tests/integration/material-issue-flow.test.ts > 领料流程集成测试 > 数据一致性 > 应该保证领料单和库存的一致性 1ms - ✓ tests/integration/material-issue-flow.test.ts > 领料流程集成测试 > 审计日志 > 应该记录领料操作的审计日志 1ms - ✓ src/lib/utils.test.ts > 工具函数测试 > cn - 样式合并 > 应该合并基础样式 17ms - ✓ src/lib/utils.test.ts > 工具函数测试 > cn - 样式合并 > 应该处理条件样式 1ms - ✓ src/lib/utils.test.ts > 工具函数测试 > cn - 样式合并 > 应该处理空值 1ms - ✓ src/lib/utils.test.ts > 工具函数测试 > formatMoney - 格式化金额 > 应该格式化数字金额并加 ¥ 前缀与千分位 1ms - ✓ src/lib/utils.test.ts > 工具函数测试 > formatMoney - 格式化金额 > 应该格式化字符串金额 0ms - ✓ src/lib/utils.test.ts > 工具函数测试 > formatMoney - 格式化金额 > null/undefined/空串应返回 ¥0.00 1ms - ✓ src/lib/utils.test.ts > 工具函数测试 > formatMoney - 格式化金额 > NaN 应返回 ¥0.00 0ms - ✓ src/lib/utils.test.ts > 工具函数测试 > formatMoney - 格式化金额 > 应该支持自定义小数位 0ms - ✓ src/lib/utils.test.ts > 工具函数测试 > formatAmount - formatMoney 别名 > 应该与 formatMoney 行为一致 1ms - ✓ src/lib/utils.test.ts > 工具函数测试 > formatCurrency - 自定义货币 > 默认货币符号为 ¥ 1ms - ✓ src/lib/utils.test.ts > 工具函数测试 > formatCurrency - 自定义货币 > 应该使用自定义货币符号 1ms - ✓ src/lib/utils.test.ts > 工具函数测试 > formatCurrency - 自定义货币 > null 应返回 货币符号 + 0.00 0ms - ✓ src/lib/utils.test.ts > 工具函数测试 > formatCurrency - 自定义货币 > NaN 应返回 货币符号 + 0.00 0ms - ✓ src/lib/utils.test.ts > 工具函数测试 > formatPercent - 格式化百分比 > 应该把小数转为百分比 0ms - ✓ src/lib/utils.test.ts > 工具函数测试 > formatPercent - 格式化百分比 > null/undefined 应返回 0% 0ms - ✓ src/lib/utils.test.ts > 工具函数测试 > formatPercent - 格式化百分比 > 应该支持自定义小数位 0ms - ✓ src/lib/utils.test.ts > 工具函数测试 > formatDate - 格式化日期 > 应该格式化为 YYYY-MM-DD 1ms - ✓ src/lib/utils.test.ts > 工具函数测试 > formatDate - 格式化日期 > 应该格式化为 YYYY-MM-DD HH:mm:ss 0ms - ✓ src/lib/utils.test.ts > 工具函数测试 > formatDate - 格式化日期 > 应该处理字符串日期 0ms - ✓ src/lib/utils.test.ts > 工具函数测试 > formatDate - 格式化日期 > 默认格式应为 YYYY-MM-DD 0ms - ✓ src/lib/utils.test.ts > 工具函数测试 > formatDate - 格式化日期 > null/undefined 应返回空字符串 0ms - ✓ src/lib/utils.test.ts > 工具函数测试 > formatDate - 格式化日期 > 无效日期应返回空字符串 0ms - ✓ src/lib/utils.test.ts > 工具函数测试 > generateBatchNo - 生成批次号 > 默认前缀 BAT 并包含 10 位日期随机串 1ms - ✓ src/lib/utils.test.ts > 工具函数测试 > generateBatchNo - 生成批次号 > 应该使用传入前缀 0ms - ✓ src/lib/utils.test.ts > 工具函数测试 > generateTransNo - 生成交易号 > 默认前缀 TRN 并包含 14 位时间随机串 1ms - ✓ src/lib/utils.test.ts > 工具函数测试 > generateTransNo - 生成交易号 > 应该使用传入前缀 0ms - ✓ tests/unit/infrastructure/event-bus/outbox-poller-dead-letter.test.ts > 1.5 端到端:OutboxPoller 失败重试 + 死信标记 > retry_count 0→1→2→3 时依次触发 markAsFailed,retry_count=3 时触发 markAsDeadLetter 27ms - ✓ tests/unit/infrastructure/event-bus/outbox-poller-dead-letter.test.ts > 1.5 端到端:OutboxPoller 失败重试 + 死信标记 > handler 成功时调用 markAsProcessed 6ms - ✓ tests/unit/domain/sample/value-objects/sample-order-status.test.ts > SampleOrderStatus 值对象 > 状态机配置 > 应包含 7 种状态 8ms - ✓ tests/unit/domain/sample/value-objects/sample-order-status.test.ts > SampleOrderStatus 值对象 > 状态机配置 > converted 和 cancelled 是终态(无允许流转) 2ms - ✓ tests/unit/domain/sample/value-objects/sample-order-status.test.ts > SampleOrderStatus 值对象 > 状态机配置 > 每个状态都应有 label 和 color 1ms - ✓ tests/unit/domain/sample/value-objects/sample-order-status.test.ts > SampleOrderStatus 值对象 > canTransition() 合法路径 > draft → pending ✓ 1ms - ✓ tests/unit/domain/sample/value-objects/sample-order-status.test.ts > SampleOrderStatus 值对象 > canTransition() 合法路径 > draft → cancelled ✓ 0ms - ✓ tests/unit/domain/sample/value-objects/sample-order-status.test.ts > SampleOrderStatus 值对象 > canTransition() 合法路径 > pending → in_progress ✓ 0ms - ✓ tests/unit/domain/sample/value-objects/sample-order-status.test.ts > SampleOrderStatus 值对象 > canTransition() 合法路径 > pending → cancelled ✓ 1ms - ✓ tests/unit/domain/sample/value-objects/sample-order-status.test.ts > SampleOrderStatus 值对象 > canTransition() 合法路径 > in_progress → completed ✓ 0ms - ✓ tests/unit/domain/sample/value-objects/sample-order-status.test.ts > SampleOrderStatus 值对象 > canTransition() 合法路径 > in_progress → cancelled ✓ 1ms - ✓ tests/unit/domain/sample/value-objects/sample-order-status.test.ts > SampleOrderStatus 值对象 > canTransition() 合法路径 > completed → confirmed ✓ 0ms - ✓ tests/unit/domain/sample/value-objects/sample-order-status.test.ts > SampleOrderStatus 值对象 > canTransition() 合法路径 > completed → cancelled ✓ 0ms - ✓ tests/unit/domain/sample/value-objects/sample-order-status.test.ts > SampleOrderStatus 值对象 > canTransition() 合法路径 > confirmed → converted ✓ 0ms - ✓ tests/unit/domain/sample/value-objects/sample-order-status.test.ts > SampleOrderStatus 值对象 > canTransition() 非法路径 > draft → in_progress ✗ 0ms - ✓ tests/unit/domain/sample/value-objects/sample-order-status.test.ts > SampleOrderStatus 值对象 > canTransition() 非法路径 > draft → completed ✗ 1ms - ✓ tests/unit/domain/sample/value-objects/sample-order-status.test.ts > SampleOrderStatus 值对象 > canTransition() 非法路径 > draft → confirmed ✗ 0ms - ✓ tests/unit/domain/sample/value-objects/sample-order-status.test.ts > SampleOrderStatus 值对象 > canTransition() 非法路径 > draft → converted ✗ 0ms - ✓ tests/unit/domain/sample/value-objects/sample-order-status.test.ts > SampleOrderStatus 值对象 > canTransition() 非法路径 > pending → completed ✗ (跳级) 0ms - ✓ tests/unit/domain/sample/value-objects/sample-order-status.test.ts > SampleOrderStatus 值对象 > canTransition() 非法路径 > in_progress → confirmed ✗ (跳级) 0ms - ✓ tests/unit/domain/sample/value-objects/sample-order-status.test.ts > SampleOrderStatus 值对象 > canTransition() 非法路径 > completed → converted ✗ (跳级) 0ms - ✓ tests/unit/domain/sample/value-objects/sample-order-status.test.ts > SampleOrderStatus 值对象 > canTransition() 非法路径 > confirmed → cancelled ✗ 0ms - ✓ tests/unit/domain/sample/value-objects/sample-order-status.test.ts > SampleOrderStatus 值对象 > canTransition() 非法路径 > converted → 任意 ✗ (终态) 0ms - ✓ tests/unit/domain/sample/value-objects/sample-order-status.test.ts > SampleOrderStatus 值对象 > canTransition() 非法路径 > cancelled → 任意 ✗ (终态) 0ms - ✓ tests/unit/domain/sample/value-objects/sample-order-status.test.ts > SampleOrderStatus 值对象 > getStatusLabel() > 应返回各状态中文名 1ms - ✓ tests/unit/domain/sample/value-objects/sample-order-status.test.ts > SampleOrderStatus 值对象 > getStatusColor() > 应返回有效 hex 颜色 1ms - ✓ tests/unit/domain/sample/value-objects/sample-order-status.test.ts > SampleOrderStatus 值对象 > statusTransitionActions > 应包含 submit/startProduction/complete/confirm/convert/cancel 动作 1ms - ✓ tests/unit/domain/sample/value-objects/sample-order-status.test.ts > SampleOrderStatus 值对象 > statusTransitionActions > 每条动作的 from→to 应与 canTransition 一致 1ms - ✓ tests/unit/menu-tree.test.ts > buildMenuTree > treats null parent_id as root (0) — the regression fix 8ms - ✓ tests/unit/menu-tree.test.ts > buildMenuTree > treats 0 parent_id as root (backward compat) 2ms - ✓ tests/unit/menu-tree.test.ts > buildMenuTree > handles mixed null and 0 parent_id as roots 1ms - ✓ tests/unit/menu-tree.test.ts > buildMenuTree > returns empty array when no root menus exist 1ms - ✓ tests/unit/menu-tree.test.ts > buildMenuTree > builds deeply nested tree (3+ levels) 1ms - ✓ tests/unit/menu-tree.test.ts > buildMenuTree > converts null icon/path/component/permission to undefined 1ms - ✓ tests/unit/menu-tree.test.ts > buildMenuTree > preserves non-null field values 1ms - ✓ tests/unit/menu-tree.test.ts > buildMenuTree > returns empty children array for leaf nodes 2ms - ✓ tests/unit/menu-tree.test.ts > buildMenuTree > handles empty input 2ms - ✓ tests/unit/menu-tree.test.ts > buildMenuTree > does not mutate input array 2ms - ✓ tests/unit/menu-tree.test.ts > extractPermissions > extracts unique permission strings 2ms - ✓ tests/unit/menu-tree.test.ts > extractPermissions > skips menus without permission 1ms - ✓ tests/unit/menu-tree.test.ts > extractPermissions > returns empty array when no permissions exist 0ms - ✓ tests/unit/domain/warehouse/aggregates/outbound-order.test.ts > OutboundOrder 聚合根 > create() 工厂方法 > 合法参数创建成功,状态为 draft 7ms - ✓ tests/unit/domain/warehouse/aggregates/outbound-order.test.ts > OutboundOrder 聚合根 > create() 工厂方法 > 有 id 时发布 OutboundOrderCreatedEvent 3ms - ✓ tests/unit/domain/warehouse/aggregates/outbound-order.test.ts > OutboundOrder 聚合根 > create() 工厂方法 > 无 id 时不发布创建事件 1ms - ✓ tests/unit/domain/warehouse/aggregates/outbound-order.test.ts > OutboundOrder 聚合根 > create() 工厂方法 > 自动计算 totalAmount(多 item 累加) 1ms - ✓ tests/unit/domain/warehouse/aggregates/outbound-order.test.ts > OutboundOrder 聚合根 > create() 工厂方法 > 自动计算 totalQuantity(多 item 累加) 1ms - ✓ tests/unit/domain/warehouse/aggregates/outbound-order.test.ts > OutboundOrder 聚合根 > create() 工厂方法 > warehouseId 为 0 抛 DomainError 2ms - ✓ tests/unit/domain/warehouse/aggregates/outbound-order.test.ts > OutboundOrder 聚合根 > create() 工厂方法 > items 为空数组抛 DomainError 1ms - ✓ tests/unit/domain/warehouse/aggregates/outbound-order.test.ts > OutboundOrder 聚合根 > create() 工厂方法 > 默认值回退(outboundType/orderDate/remark 为空时使用默认) 1ms - ✓ tests/unit/domain/warehouse/aggregates/outbound-order.test.ts > OutboundOrder 聚合根 > reconstitute() 重建方法 > 从 DB 字段重建聚合,使用指定 totalAmount/totalQuantity 2ms - ✓ tests/unit/domain/warehouse/aggregates/outbound-order.test.ts > OutboundOrder 聚合根 > reconstitute() 重建方法 > 未指定 totalAmount 时自动计算 1ms - ✓ tests/unit/domain/warehouse/aggregates/outbound-order.test.ts > OutboundOrder 聚合根 > reconstitute() 重建方法 > 未指定 totalQuantity 时自动计算 1ms - ✓ tests/unit/domain/warehouse/aggregates/outbound-order.test.ts > OutboundOrder 聚合根 > reconstitute() 重建方法 > DB status approved 映射为 completed 0ms - ✓ tests/unit/domain/warehouse/aggregates/outbound-order.test.ts > OutboundOrder 聚合根 > submit() 提交流程 > draft → pending,发布 OutboundOrderSubmittedEvent 1ms - ✓ tests/unit/domain/warehouse/aggregates/outbound-order.test.ts > OutboundOrder 聚合根 > submit() 提交流程 > 非 draft 状态提交抛错 1ms - ✓ tests/unit/domain/warehouse/aggregates/outbound-order.test.ts > OutboundOrder 聚合根 > approve() 审核流程 > pending → completed,设置 auditStatus=1 和 financePosted=true 2ms - ✓ tests/unit/domain/warehouse/aggregates/outbound-order.test.ts > OutboundOrder 聚合根 > approve() 审核流程 > 非 pending 状态审核抛错 1ms - ✓ tests/unit/domain/warehouse/aggregates/outbound-order.test.ts > OutboundOrder 聚合根 > cancel() 取消流程 > draft → cancelled,发布取消事件 1ms - ✓ tests/unit/domain/warehouse/aggregates/outbound-order.test.ts > OutboundOrder 聚合根 > cancel() 取消流程 > pending → cancelled 0ms - ✓ tests/unit/domain/warehouse/aggregates/outbound-order.test.ts > OutboundOrder 聚合根 > cancel() 取消流程 > completed → cancelled 抛错(已完成不可取消) 0ms - ✓ tests/unit/domain/warehouse/aggregates/outbound-order.test.ts > OutboundOrder 聚合根 > 权限委托 > canEdit/canDelete 委托给 OrderStatus 1ms - ✓ tests/unit/domain/warehouse/aggregates/outbound-order.test.ts > OutboundOrder 聚合根 > 领域事件管理 > getDomainEvents 返回副本(不可变) 0ms - ✓ tests/unit/domain/warehouse/aggregates/outbound-order.test.ts > OutboundOrder 聚合根 > 领域事件管理 > clearDomainEvents 清空事件 1ms - ✓ tests/unit/domain/warehouse/aggregates/outbound-order.test.ts > OutboundOrder 聚合根 > items 访问器返回副本 > 外部修改 items 数组不影响内部状态 0ms - ✓ src/lib/state-machine.test.ts > StateMachineValidator > canTransitionInspect > allows pending → inspecting 6ms - ✓ src/lib/state-machine.test.ts > StateMachineValidator > canTransitionInspect > allows inspecting → pass 0ms - ✓ src/lib/state-machine.test.ts > StateMachineValidator > canTransitionInspect > allows inspecting → fail 0ms - ✓ src/lib/state-machine.test.ts > StateMachineValidator > canTransitionInspect > disallows inspecting → pending 0ms - ✓ src/lib/state-machine.test.ts > StateMachineValidator > canTransitionInspect > disallows pass → anything except pass 1ms - ✓ src/lib/state-machine.test.ts > StateMachineValidator > canTransitionInspect > allows fail → rework 1ms - ✓ src/lib/state-machine.test.ts > StateMachineValidator > canTransitionInspect > allows fail → scrap 2ms - ✓ src/lib/state-machine.test.ts > StateMachineValidator > canTransitionInspect > allows rework → pending 1ms - ✓ src/lib/state-machine.test.ts > StateMachineValidator > canTransitionInspect > allows rework → scrap 1ms - ✓ src/lib/state-machine.test.ts > StateMachineValidator > canTransitionInspect > allows same-to-same transition 0ms - ✓ src/lib/state-machine.test.ts > StateMachineValidator > canTransitionInspect > disallows scrap → anything except scrap 0ms - ✓ src/lib/state-machine.test.ts > StateMachineValidator > canTransitionProcess > allows created → material_ready 0ms - ✓ src/lib/state-machine.test.ts > StateMachineValidator > canTransitionProcess > allows material_ready → in_progress 0ms - ✓ src/lib/state-machine.test.ts > StateMachineValidator > canTransitionProcess > allows in_progress → qc_pending 0ms - ✓ src/lib/state-machine.test.ts > StateMachineValidator > canTransitionProcess > allows qc_pending → qc_pass 0ms - ✓ src/lib/state-machine.test.ts > StateMachineValidator > canTransitionProcess > allows qc_pending → qc_fail 0ms - ✓ src/lib/state-machine.test.ts > StateMachineValidator > canTransitionProcess > allows qc_pass → completed 0ms - ✓ src/lib/state-machine.test.ts > StateMachineValidator > canTransitionProcess > allows qc_fail → rework 0ms - ✓ src/lib/state-machine.test.ts > StateMachineValidator > canTransitionProcess > allows rework → qc_pending 0ms - ✓ src/lib/state-machine.test.ts > StateMachineValidator > canTransitionProcess > disallows completed → anything except completed 1ms - ✓ src/lib/state-machine.test.ts > StateMachineValidator > canTransitionProcess > allows same-to-same transition 3ms - ✓ src/lib/state-machine.test.ts > StateMachineValidator > getInspectStatusLabel > returns 合格 for pass 0ms - ✓ src/lib/state-machine.test.ts > StateMachineValidator > getInspectStatusLabel > returns 不合格 for fail 0ms - ✓ src/lib/state-machine.test.ts > StateMachineValidator > getInspectStatusLabel > returns 待检验 for pending 0ms - ✓ src/lib/state-machine.test.ts > StateMachineValidator > getInspectStatusLabel > returns 检验中 for inspecting 0ms - ✓ src/lib/state-machine.test.ts > StateMachineValidator > getProcessStatusLabel > returns 已完成 for completed 0ms - ✓ src/lib/state-machine.test.ts > StateMachineValidator > getProcessStatusLabel > returns 已创建 for created 0ms - ✓ src/lib/state-machine.test.ts > StateMachineValidator > getProcessStatusLabel > returns 生产中 for in_progress 0ms - ✓ src/lib/state-machine.test.ts > StateMachineValidator > getAllowedInspectTransitions > returns [inspecting] for pending 2ms - ✓ src/lib/state-machine.test.ts > StateMachineValidator > getAllowedInspectTransitions > returns [pass, fail] for inspecting 0ms - ✓ src/lib/state-machine.test.ts > StateMachineValidator > getAllowedInspectTransitions > returns [] for pass 0ms - ✓ src/lib/state-machine.test.ts > StateMachineValidator > getAllowedProcessTransitions > returns [material_ready] for created 0ms - ✓ src/lib/state-machine.test.ts > StateMachineValidator > getAllowedProcessTransitions > returns [] for completed 0ms - ✓ tests/unit/application/handlers/ToolCostHandler.test.ts > ToolCostHandler > 汇总 amortized_cost 并累加到 work_order_costs 9ms - ✓ tests/unit/application/handlers/ToolCostHandler.test.ts > ToolCostHandler > 总成本为 0 时跳过写入 2ms - ✓ tests/unit/application/handlers/ToolCostHandler.test.ts > ToolCostHandler > 总成本为 null 时安全处理(跳过) 1ms - ✓ tests/unit/application/handlers/ToolCostHandler.test.ts > ToolCostHandler > 查询结果为空时安全处理(跳过) 1ms - ✓ tests/unit/application/handlers/ToolCostHandler.test.ts > ToolCostHandler > 查询异常被吞掉(不抛出) 4ms - ✓ tests/unit/application/handlers/ToolCostHandler.test.ts > ToolCostHandler > recordToolCost 执行异常被吞掉(不抛出) 1ms - ✓ tests/unit/application/handlers/ToolCostHandler.test.ts > ToolCostHandler > workOrderNo 出现在 remark 中 1ms - ✓ tests/unit/application/handlers/ToolCostHandler.test.ts > ToolCostHandler > 小数精度保持正确 1ms - ✓ tests/unit/application/handlers/ToolCostHandler.test.ts > ToolCostHandler > 字符串类型 total_tool_cost 正确解析 2ms - ✓ tests/unit/application/handlers/ToolCostHandler.test.ts > ToolCostHandler > 数字类型 total_tool_cost 正确解析 1ms - ✓ tests/unit/infrastructure/event-bus/stream-flow-simulation.test.ts > Stream 流程模拟测试 > 场景 1:StreamPublisher XADD 日志 > XADD 成功:输出 debug 日志,含 eventId 和 streamMessageId 12ms - ✓ tests/unit/infrastructure/event-bus/stream-flow-simulation.test.ts > Stream 流程模拟测试 > 场景 1:StreamPublisher XADD 日志 > XADD 失败:输出 error 日志并抛出异常 4ms - ✓ tests/unit/infrastructure/event-bus/stream-flow-simulation.test.ts > Stream 流程模拟测试 > 场景 2:IdempotentHandler mark-before-execute + delete-on-failure 修复验证 > 修复验证:handler 首次失败 → deleteMark 删除记录 → 重试时 handler 再次执行 3ms - ✓ tests/unit/infrastructure/event-bus/stream-flow-simulation.test.ts > Stream 流程模拟测试 > 场景 2:IdempotentHandler mark-before-execute + delete-on-failure 修复验证 > 重复事件跳过:handler 成功后,重投递时 IdempotentHandler 正确跳过 2ms - ✓ tests/unit/infrastructure/event-bus/stream-flow-simulation.test.ts > Stream 流程模拟测试 > 场景 3:XAUTOCLAIM 重投递后 IdempotentHandler 未拦截的原因 > 原因 A:sys_event_processed 表不存在 → checkAndMark 抛异常 → 降级返回 true → 重复执行 1ms - ✓ tests/unit/infrastructure/event-bus/stream-flow-simulation.test.ts > Stream 流程模拟测试 > 场景 3:XAUTOCLAIM 重投递后 IdempotentHandler 未拦截的原因 > 原因 B:event.id 缺失 → IdempotentHandler 旁路检查 → 直接执行 1ms - ✓ tests/unit/infrastructure/event-bus/stream-flow-simulation.test.ts > Stream 流程模拟测试 > 场景 3:XAUTOCLAIM 重投递后 IdempotentHandler 未拦截的原因 > 原因 C:首次 DB 故障降级 → 记录未持久化 → 重投递时再次执行 1ms - ✓ tests/unit/integration/event-flow-template.test.ts > 阶段 1:事件发布 — 聚合根状态变更后领域事件被正确添加 > approve() 后状态变为 approved,_domainEvents 包含 FinishOrderApprovedEvent 12ms - ✓ tests/unit/integration/event-flow-template.test.ts > 阶段 1:事件发布 — 聚合根状态变更后领域事件被正确添加 > 非 draft 状态调用 approve() 抛出 DomainError,不产生新事件 3ms - ✓ tests/unit/integration/event-flow-template.test.ts > 阶段 2:事件投递 — saveEvents 将事件写入 domain_event_outbox 表 > saveEvents 在事务连接上执行 INSERT INTO domain_event_outbox 3ms - ✓ tests/unit/integration/event-flow-template.test.ts > 阶段 2:事件投递 — saveEvents 将事件写入 domain_event_outbox 表 > 多个事件时 saveEvents 逐条写入(每事件一条 INSERT) 1ms - ✓ tests/unit/integration/event-flow-template.test.ts > 阶段 3:事件消费 — Handler 通过 EventBus 消费事件并执行库存写操作 > EventBus.publish 触发 FinishOrderInventoryHandler 执行库存入库 3ms - ✓ tests/unit/integration/event-flow-template.test.ts > 阶段 3:事件消费 — Handler 通过 EventBus 消费事件并执行库存写操作 > 工单不存在时 Handler 安全跳过(不抛错,不写库存) 3ms - ✓ tests/unit/integration/event-flow-template.test.ts > 阶段 4:事件幂等 — 重复投递同一事件时 Handler 不重复处理 > 同一事件投递两次:首次处理库存,第二次幂等跳过 2ms - ✓ tests/unit/application/services/CurrencyApplicationService.test.ts > CurrencyApplicationService > getLatestRate() > 同币种返回 1 9ms - ✓ tests/unit/application/services/CurrencyApplicationService.test.ts > CurrencyApplicationService > getLatestRate() > 有缓存时返回缓存值 2ms - ✓ tests/unit/application/services/CurrencyApplicationService.test.ts > CurrencyApplicationService > getLatestRate() > 汇率未配置抛错 4ms - ✓ tests/unit/application/services/CurrencyApplicationService.test.ts > CurrencyApplicationService > convertToBaseCurrency() > 同币种直接返回 2ms - ✓ tests/unit/application/services/CurrencyApplicationService.test.ts > CurrencyApplicationService > convertToBaseCurrency() > USD 转 CNY 正确换算 4ms - ✓ tests/unit/application/services/CurrencyApplicationService.test.ts > CurrencyApplicationService > convertToBaseCurrency() > VND 转 CNY 零小数位 1ms - ✓ tests/unit/application/services/CurrencyApplicationService.test.ts > CurrencyApplicationService > convertToBaseCurrency() > 本位币未配置抛错 1ms - ✓ tests/unit/application/services/CurrencyApplicationService.test.ts > CurrencyApplicationService > clearCache() > 清除缓存后重新查询 DB 1ms - ✓ tests/unit/infrastructure/event-bus/outbox-poller-lifecycle.test.ts > 1.7.2 OutboxPoller 生命周期与并发防重入 > 处理成功 → markAsProcessed,processed=1 15ms - ✓ tests/unit/infrastructure/event-bus/outbox-poller-lifecycle.test.ts > 1.7.2 OutboxPoller 生命周期与并发防重入 > 处理失败且 retry_count<3 → markAsFailed,retried=1 9ms - ✓ tests/unit/infrastructure/event-bus/outbox-poller-lifecycle.test.ts > 1.7.2 OutboxPoller 生命周期与并发防重入 > 并发 poll 防重入:第二次 poll 立即返回 0 2ms - ✓ tests/unit/infrastructure/event-bus/outbox-poller-lifecycle.test.ts > 1.7.2 OutboxPoller 生命周期与并发防重入 > start 后 isRunning 为 true,stop 后为 false 1ms - ✓ tests/unit/infrastructure/event-bus/outbox-poller-lifecycle.test.ts > 1.7.2 OutboxPoller 生命周期与并发防重入 > start 重复调用幂等:不创建多个 timer 1ms - ✓ src/lib/__tests__/cost-engine.test.ts > CostEngine - 成本核算引擎 > 移动加权平均法 > 首次入库:当前库存为0 6ms - ✓ src/lib/__tests__/cost-engine.test.ts > CostEngine - 成本核算引擎 > 移动加权平均法 > 第二次入库:不同价格 2ms - ✓ src/lib/__tests__/cost-engine.test.ts > CostEngine - 成本核算引擎 > 移动加权平均法 > 第三次入库:价格下降 1ms - ✓ src/lib/__tests__/cost-engine.test.ts > CostEngine - 成本核算引擎 > 移动加权平均法 > 入库数量必须大于0 3ms - ✓ src/lib/__tests__/cost-engine.test.ts > CostEngine - 成本核算引擎 > 移动加权平均法 > 入库单价不能为负 1ms - ✓ src/lib/__tests__/cost-engine.test.ts > CostEngine - 成本核算引擎 > 移动加权平均法 > 当前库存数量不能为负 1ms - ✓ src/lib/__tests__/cost-engine.test.ts > CostEngine - 成本核算引擎 > 移动加权平均法 > 当前成本单价不能为负 1ms - ✓ src/lib/__tests__/cost-engine.test.ts > CostEngine - 成本核算引擎 > 移动加权平均法 > 价格变动计算正确 1ms - ✓ src/lib/__tests__/cost-engine.test.ts > CostEngine - 成本核算引擎 > 移动加权平均法 > 使用totalAmount计算,避免浮点误差 1ms - ✓ src/lib/__tests__/cost-engine.test.ts > CostEngine - 成本核算引擎 > 月末一次加权平均法 > 期初+入库计算平均单价 1ms - ✓ src/lib/__tests__/cost-engine.test.ts > CostEngine - 成本核算引擎 > 月末一次加权平均法 > 期初为0时,平均单价等于入库单价 1ms - ✓ src/lib/__tests__/cost-engine.test.ts > CostEngine - 成本核算引擎 > 月末一次加权平均法 > 入库为0时,平均单价等于期初单价 1ms - ✓ src/lib/__tests__/cost-engine.test.ts > CostEngine - 成本核算引擎 > 月末一次加权平均法 > 全部为0时,平均单价为0 0ms - ✓ src/lib/__tests__/cost-engine.test.ts > CostEngine - 成本核算引擎 > 出库成本计算 > 正常出库成本计算 0ms - ✓ src/lib/__tests__/cost-engine.test.ts > CostEngine - 成本核算引擎 > 出库成本计算 > 出库数量为0时成本为0 0ms - ✓ src/lib/__tests__/cost-engine.test.ts > CostEngine - 成本核算引擎 > 出库成本计算 > 出库数量不能为负 1ms - ✓ src/lib/__tests__/cost-engine.test.ts > CostEngine - 成本核算引擎 > 出库成本计算 > 单位成本不能为负 1ms - ✓ src/lib/__tests__/cost-engine.test.ts > CostEngine - 成本核算引擎 > 标准成本差异分析 > 价格差异和数量差异计算正确 1ms - ✓ src/lib/__tests__/cost-engine.test.ts > CostEngine - 成本核算引擎 > 标准成本差异分析 > 实际价格低于标准价格:价差为负(有利差异) 1ms - ✓ src/lib/__tests__/cost-engine.test.ts > CostEngine - 成本核算引擎 > 标准成本差异分析 > 实际用量低于标准用量:量差为负(有利差异) 0ms - ✓ src/lib/__tests__/cost-engine.test.ts > CostEngine - 成本核算引擎 > 标准成本差异分析 > 完全符合标准:差异为0 0ms - ✓ src/lib/__tests__/cost-engine.test.ts > CostEngine - 成本核算引擎 > 产品成本卷积 > 直接材料 + 直接人工 + 制造费用 = 总成本 0ms - ✓ src/lib/__tests__/cost-engine.test.ts > CostEngine - 成本核算引擎 > 产品成本卷积 > 产出为0时,单位成本为0 0ms - ✓ src/lib/__tests__/cost-engine.test.ts > CostEngine - 成本核算引擎 > 产品成本卷积 > BOM展开成本累加 0ms - ✓ src/lib/__tests__/cost-engine.test.ts > CostEngine - 成本核算引擎 > 产品成本卷积 > 考虑损耗率的材料成本 0ms - ✓ src/lib/__tests__/cost-engine.test.ts > CostEngine - 成本核算引擎 > 产品成本卷积 > 多层BOM成本卷积:子件成本累加到父件 0ms - ✓ src/lib/__tests__/cost-engine.test.ts > CostEngine - 成本核算引擎 > EOQ 经济订货批量 > EOQ公式计算正确 0ms - ✓ src/lib/__tests__/cost-engine.test.ts > CostEngine - 成本核算引擎 > EOQ 经济订货批量 > 需求为0时返回0 0ms - ✓ src/lib/__tests__/cost-engine.test.ts > CostEngine - 成本核算引擎 > EOQ 经济订货批量 > 订货成本为0时返回0 0ms - ✓ src/lib/__tests__/cost-engine.test.ts > CostEngine - 成本核算引擎 > EOQ 经济订货批量 > 存储成本为0时返回0 0ms - ✓ src/lib/__tests__/cost-engine.test.ts > CostEngine - 成本核算引擎 > 成本核算方法切换 > 移动加权平均方法 0ms - ✓ src/lib/__tests__/cost-engine.test.ts > CostEngine - 成本核算引擎 > 成本核算方法切换 > 月末加权平均方法 0ms - ✓ src/lib/__tests__/cost-engine.test.ts > CostEngine - 成本核算引擎 > 成本核算方法切换 > 标准成本方法 0ms - ✓ src/lib/__tests__/general-ledger.test.ts > GeneralLedger - 财务总账 > 会计科目类型 > 资产类科目以1开头,借方余额 5ms - ✓ src/lib/__tests__/general-ledger.test.ts > GeneralLedger - 财务总账 > 会计科目类型 > 负债类科目以2开头,贷方余额 1ms - ✓ src/lib/__tests__/general-ledger.test.ts > GeneralLedger - 财务总账 > 会计科目类型 > 所有者权益类科目以3开头,贷方余额 1ms - ✓ src/lib/__tests__/general-ledger.test.ts > GeneralLedger - 财务总账 > 会计科目类型 > 成本类科目以4开头,借方余额 1ms - ✓ src/lib/__tests__/general-ledger.test.ts > GeneralLedger - 财务总账 > 会计科目类型 > 损益类科目以5开头(也可能是6),贷方为主 1ms - ✓ src/lib/__tests__/general-ledger.test.ts > GeneralLedger - 财务总账 > 凭证借贷平衡校验 > 借贷相等的凭证应该通过校验 1ms - ✓ src/lib/__tests__/general-ledger.test.ts > GeneralLedger - 财务总账 > 凭证借贷平衡校验 > 借贷不相等的凭证应该不通过校验 1ms - ✓ src/lib/__tests__/general-ledger.test.ts > GeneralLedger - 财务总账 > 凭证借贷平衡校验 > 空凭证应该通过校验(0=0) 0ms - ✓ src/lib/__tests__/general-ledger.test.ts > GeneralLedger - 财务总账 > 凭证借贷平衡校验 > 一借多贷应该平衡 1ms - ✓ src/lib/__tests__/general-ledger.test.ts > GeneralLedger - 财务总账 > 凭证借贷平衡校验 > 一贷多借应该平衡 1ms - ✓ src/lib/__tests__/general-ledger.test.ts > GeneralLedger - 财务总账 > 凭证借贷平衡校验 > 允许小额浮点误差(小于0.01) 0ms - ✓ src/lib/__tests__/general-ledger.test.ts > GeneralLedger - 财务总账 > 账户余额计算 > 资产类账户:期初借方 + 本期借方 - 本期贷方 = 期末借方 2ms - ✓ src/lib/__tests__/general-ledger.test.ts > GeneralLedger - 财务总账 > 账户余额计算 > 负债类账户:期初贷方 + 本期贷方 - 本期借方 = 期末贷方 1ms - ✓ src/lib/__tests__/general-ledger.test.ts > GeneralLedger - 财务总账 > 账户余额计算 > 借方账户期末为贷方余额时,应显示在贷方 1ms - ✓ src/lib/__tests__/general-ledger.test.ts > GeneralLedger - 财务总账 > 账户余额计算 > 贷方账户期末为借方余额时,应显示在借方 0ms - ✓ src/lib/__tests__/general-ledger.test.ts > GeneralLedger - 财务总账 > 账户余额计算 > 期初为0,本期只有借方发生 0ms - ✓ src/lib/__tests__/general-ledger.test.ts > GeneralLedger - 财务总账 > 账户余额计算 > 期初为0,本期只有贷方发生 0ms - ✓ src/lib/__tests__/general-ledger.test.ts > GeneralLedger - 财务总账 > 账户余额计算 > 本期借贷相等,余额不变 0ms - ✓ src/lib/__tests__/general-ledger.test.ts > GeneralLedger - 财务总账 > 会计恒等式验证 > 资产 = 负债 + 所有者权益 0ms - ✓ src/lib/__tests__/general-ledger.test.ts > GeneralLedger - 财务总账 > 会计恒等式验证 > 收入 - 费用 = 利润 0ms - ✓ src/lib/__tests__/general-ledger.test.ts > GeneralLedger - 财务总账 > 凭证类型 > 收款凭证:借方必有现金/银行存款 1ms - ✓ src/lib/__tests__/general-ledger.test.ts > GeneralLedger - 财务总账 > 凭证类型 > 付款凭证:贷方必有现金/银行存款 0ms - ✓ src/lib/__tests__/general-ledger.test.ts > GeneralLedger - 财务总账 > 凭证类型 > 转账凭证:不涉及现金/银行存款 0ms - ✓ src/lib/__tests__/general-ledger.test.ts > GeneralLedger - 财务总账 > 损益结转逻辑 > 收入类科目结转:从借方转至本年利润贷方 0ms - ✓ src/lib/__tests__/general-ledger.test.ts > GeneralLedger - 财务总账 > 损益结转逻辑 > 成本费用类科目结转:从贷方转至本年利润借方 0ms - ✓ src/lib/__tests__/general-ledger.test.ts > GeneralLedger - 财务总账 > 损益结转逻辑 > 净利润 = 收入 - 成本 - 费用 0ms - ✓ src/lib/__tests__/general-ledger.test.ts > GeneralLedger - 财务总账 > 试算平衡 > 期初借方合计 = 期初贷方合计 0ms - ✓ src/lib/__tests__/general-ledger.test.ts > GeneralLedger - 财务总账 > 试算平衡 > 本期借方发生额合计 = 本期贷方发生额合计 0ms - ✓ src/lib/__tests__/general-ledger.test.ts > GeneralLedger - 财务总账 > 试算平衡 > 期末借方余额合计 = 期末贷方余额合计 0ms - ✓ src/lib/__tests__/general-ledger.test.ts > GeneralLedger - 财务总账 > GeneralLedger 实例 > 应该能创建GeneralLedger实例 0ms - ✓ tests/unit/domain/finance/aggregates/payable.test.ts > Payable 聚合根 > create() 工厂方法 > 合法参数创建成功,状态为 UNPAID(1) 6ms - ✓ tests/unit/domain/finance/aggregates/payable.test.ts > Payable 聚合根 > create() 工厂方法 > 有 id 时发布 PayableCreatedEvent 3ms - ✓ tests/unit/domain/finance/aggregates/payable.test.ts > Payable 聚合根 > create() 工厂方法 > 无 id 时不发布创建事件 1ms - ✓ tests/unit/domain/finance/aggregates/payable.test.ts > Payable 聚合根 > create() 工厂方法 > supplierId 为 0 抛 DomainError 3ms - ✓ tests/unit/domain/finance/aggregates/payable.test.ts > Payable 聚合根 > create() 工厂方法 > amount 为 0 抛 DomainError 1ms - ✓ tests/unit/domain/finance/aggregates/payable.test.ts > Payable 聚合根 > reconstitute() 重建方法 > 从 DB 字段重建聚合 1ms - ✓ tests/unit/domain/finance/aggregates/payable.test.ts > Payable 聚合根 > reconstitute() 重建方法 > 未指定 balance 时回退为 amount 1ms - ✓ tests/unit/domain/finance/aggregates/payable.test.ts > Payable 聚合根 > recordPayment() 付款流程 > 部分付款 → PARTIAL 状态,发布 PartialPaidEvent 2ms - ✓ tests/unit/domain/finance/aggregates/payable.test.ts > Payable 聚合根 > recordPayment() 付款流程 > 全额付款 → SETTLED 状态,发布 SettledEvent 2ms - ✓ tests/unit/domain/finance/aggregates/payable.test.ts > Payable 聚合根 > recordPayment() 付款流程 > 多次部分付款后结清 1ms - ✓ tests/unit/domain/finance/aggregates/payable.test.ts > Payable 聚合根 > recordPayment() 付款流程 > 付款金额超过余额抛 DomainError 1ms - ✓ tests/unit/domain/finance/aggregates/payable.test.ts > Payable 聚合根 > recordPayment() 付款流程 > 付款金额为 0 抛 DomainError 0ms - ✓ tests/unit/domain/finance/aggregates/payable.test.ts > Payable 聚合根 > recordPayment() 付款流程 > 已结清状态付款抛 DomainError 1ms - ✓ tests/unit/domain/finance/aggregates/payable.test.ts > Payable 聚合根 > isOverdue() 逾期判断 > 到期日早于当前日期且未结清 → 逾期 0ms - ✓ tests/unit/domain/finance/aggregates/payable.test.ts > Payable 聚合根 > isOverdue() 逾期判断 > 已结清 → 未逾期 0ms - ✓ tests/unit/domain/finance/aggregates/payable.test.ts > Payable 聚合根 > 领域事件管理 > getDomainEvents 返回副本(不可变) 1ms - ✓ tests/unit/domain/finance/aggregates/payable.test.ts > Payable 聚合根 > 领域事件管理 > clearDomainEvents 清空事件 1ms - ✓ src/__tests__/domain/inbound-order.concurrency.test.ts > InboundOrder 并发冲突测试 > 应正确从 draft 提交到 pending 7ms - ✓ src/__tests__/domain/inbound-order.concurrency.test.ts > InboundOrder 并发冲突测试 > 应正确从 pending 审核到 completed 2ms - ✓ src/__tests__/domain/inbound-order.concurrency.test.ts > InboundOrder 并发冲突测试 > 应正确从 completed 反审核到 pending 1ms - ✓ src/__tests__/domain/inbound-order.concurrency.test.ts > InboundOrder 并发冲突测试 > draft 状态不能审核 4ms - ✓ src/__tests__/domain/inbound-order.concurrency.test.ts > InboundOrder 并发冲突测试 > pending 状态不能反审核 1ms - ✓ src/__tests__/domain/inbound-order.concurrency.test.ts > InboundOrder 并发冲突测试 > completed 状态不能再次审核 1ms - ✓ src/__tests__/domain/inbound-order.concurrency.test.ts > InboundOrder 并发冲突测试 > cancelled 状态不能审核 1ms - ✓ src/__tests__/domain/inbound-order.concurrency.test.ts > InboundOrder 并发冲突测试 > cancelled 状态不能反审核 1ms - ✓ src/__tests__/domain/inbound-order.concurrency.test.ts > InboundOrder 并发冲突测试 > 审核后应产生领域事件 2ms - ✓ src/__tests__/domain/inbound-order.concurrency.test.ts > InboundOrder 并发冲突测试 > 反审核后应产生领域事件 1ms - ✓ src/__tests__/domain/inbound-order.concurrency.test.ts > InboundOrder 并发冲突测试 > 清除领域事件后应为空 1ms - ✓ src/__tests__/domain/inbound-order.concurrency.test.ts > OrderStatus 状态机测试 > draft 可以转到 pending 0ms - ✓ src/__tests__/domain/inbound-order.concurrency.test.ts > OrderStatus 状态机测试 > pending 可以转到 completed 0ms - ✓ src/__tests__/domain/inbound-order.concurrency.test.ts > OrderStatus 状态机测试 > pending 可以转到 cancelled 0ms - ✓ src/__tests__/domain/inbound-order.concurrency.test.ts > OrderStatus 状态机测试 > completed 可以转到 pending (反审核) 0ms - ✓ src/__tests__/domain/inbound-order.concurrency.test.ts > OrderStatus 状态机测试 > completed 不能转到 draft 0ms - ✓ src/__tests__/domain/inbound-order.concurrency.test.ts > OrderStatus 状态机测试 > cancelled 不能转到任何状态 0ms - ✓ tests/unit/domain/trace/QRCode.test.ts > QRCode > create() > valid props creates successfully 6ms - ✓ tests/unit/domain/trace/QRCode.test.ts > QRCode > create() > throws if qrCode is empty 1ms - ✓ tests/unit/domain/trace/QRCode.test.ts > QRCode > create() > throws if qrType is empty 1ms - ✓ tests/unit/domain/trace/QRCode.test.ts > QRCode > create() > throws if quantity is negative 1ms - ✓ tests/unit/domain/trace/QRCode.test.ts > QRCode > create() > defaults optional fields correctly 1ms - ✓ tests/unit/domain/trace/QRCode.test.ts > QRCode > reconstitute() > restores from DB props without validation 1ms - ✓ tests/unit/domain/trace/QRCode.test.ts > QRCode > split() > creates valid child QRCode 1ms - ✓ tests/unit/domain/trace/QRCode.test.ts > QRCode > split() > throws if quantity <= 0 4ms - ✓ tests/unit/domain/trace/QRCode.test.ts > QRCode > split() > throws if quantity exceeds parent quantity 1ms - ✓ tests/unit/domain/trace/QRCode.test.ts > QRCode > split() > throws if index <= 0 1ms - ✓ tests/unit/domain/trace/QRCode.test.ts > QRCode > split() > throws if index > totalSplits 1ms - ✓ tests/unit/domain/trace/QRCode.test.ts > QRCode > split() > allows exact quantity split 0ms - ✓ tests/unit/domain/trace/QRCode.test.ts > QRCode > reduceQuantity() > reduces quantity correctly 0ms - ✓ tests/unit/domain/trace/QRCode.test.ts > QRCode > reduceQuantity() > throws if amount is negative 0ms - ✓ tests/unit/domain/trace/QRCode.test.ts > QRCode > reduceQuantity() > throws if amount exceeds current quantity 0ms - ✓ tests/unit/domain/trace/QRCode.test.ts > QRCode > reduceQuantity() > allows reducing to zero 0ms - ✓ tests/unit/domain/shared/value-objects/money.test.ts > 8.3 Money 值对象 > create() 工厂 > 合法金额创建成功(默认 CNY) 5ms - ✓ tests/unit/domain/shared/value-objects/money.test.ts > 8.3 Money 值对象 > create() 工厂 > 指定币种创建 1ms - ✓ tests/unit/domain/shared/value-objects/money.test.ts > 8.3 Money 值对象 > create() 工厂 > 金额四舍五入到 2 位小数 1ms - ✓ tests/unit/domain/shared/value-objects/money.test.ts > 8.3 Money 值对象 > create() 工厂 > 负数金额抛 DomainError 2ms - ✓ tests/unit/domain/shared/value-objects/money.test.ts > 8.3 Money 值对象 > zero() 工厂 > zero() 金额为 0,币种为 CNY 3ms - ✓ tests/unit/domain/shared/value-objects/money.test.ts > 8.3 Money 值对象 > add() 加法 > 同币种相加 1ms - ✓ tests/unit/domain/shared/value-objects/money.test.ts > 8.3 Money 值对象 > add() 加法 > 相加结果四舍五入 1ms - ✓ tests/unit/domain/shared/value-objects/money.test.ts > 8.3 Money 值对象 > add() 加法 > 异币种相加抛 DomainError 1ms - ✓ tests/unit/domain/shared/value-objects/money.test.ts > 8.3 Money 值对象 > subtract() 减法 > 同币种相减 1ms - ✓ tests/unit/domain/shared/value-objects/money.test.ts > 8.3 Money 值对象 > subtract() 减法 > 相减结果四舍五入 1ms - ✓ tests/unit/domain/shared/value-objects/money.test.ts > 8.3 Money 值对象 > subtract() 减法 > 异币种相减抛 DomainError 1ms - ✓ tests/unit/domain/shared/value-objects/money.test.ts > 8.3 Money 值对象 > subtract() 减法 > 相减结果为负数抛 DomainError 1ms - ✓ tests/unit/domain/shared/value-objects/money.test.ts > 8.3 Money 值对象 > subtract() 减法 > 相减结果为 0 合法 1ms - ✓ tests/unit/domain/shared/value-objects/money.test.ts > 8.3 Money 值对象 > multiply() 乘法 > 金额乘以系数 1ms - ✓ tests/unit/domain/shared/value-objects/money.test.ts > 8.3 Money 值对象 > multiply() 乘法 > 乘法结果四舍五入 0ms - ✓ tests/unit/domain/shared/value-objects/money.test.ts > 8.3 Money 值对象 > multiply() 乘法 > 乘以 0 结果为 0 0ms - ✓ tests/unit/domain/shared/value-objects/money.test.ts > 8.3 Money 值对象 > 判断方法 > isZero 判断 0ms - ✓ tests/unit/domain/shared/value-objects/money.test.ts > 8.3 Money 值对象 > 判断方法 > isPositive 判断 0ms - ✓ tests/unit/domain/shared/value-objects/money.test.ts > 8.3 Money 值对象 > 精度边界 > 大额整数运算正确 0ms - ✓ tests/unit/domain/shared/value-objects/money.test.ts > 8.3 Money 值对象 > 精度边界 > 小数 6 位精度收敛到 2 位 0ms - ✓ tests/unit/domain/shared/value-objects/money.test.ts > 8.3 Money 值对象 > convertTo() 汇率转换 > 同币种转换忽略汇率返回等价金额 0ms - ✓ tests/unit/domain/shared/value-objects/money.test.ts > 8.3 Money 值对象 > convertTo() 汇率转换 > USD 转 CNY 正确换算 0ms - ✓ tests/unit/domain/shared/value-objects/money.test.ts > 8.3 Money 值对象 > convertTo() 汇率转换 > VND 转 CNY 零小数位 0ms - ✓ tests/unit/domain/shared/value-objects/money.test.ts > 8.3 Money 值对象 > convertTo() 汇率转换 > 转换结果舍入到 2 位小数(舍去方向) 0ms - ✓ tests/unit/domain/shared/value-objects/money.test.ts > 8.3 Money 值对象 > convertTo() 汇率转换 > 转换结果舍入到 2 位小数(进位方向) 0ms - ✓ tests/unit/domain/shared/value-objects/money.test.ts > 8.3 Money 值对象 > convertTo() 汇率转换 > VND 0 位小数舍入(舍去方向) 0ms - ✓ tests/unit/domain/shared/value-objects/money.test.ts > 8.3 Money 值对象 > convertTo() 汇率转换 > VND 0 位小数舍入(进位方向) 0ms - ✓ tests/unit/domain/shared/value-objects/money.test.ts > 8.3 Money 值对象 > format() 格式化 > 默认 2 位小数 0ms - ✓ tests/unit/domain/shared/value-objects/money.test.ts > 8.3 Money 值对象 > format() 格式化 > VND 0 位小数 1ms - ✓ tests/unit/domain/shared/value-objects/money.test.ts > 8.3 Money 值对象 > format() 格式化 > 负数格式化(红字) 0ms -Warning: A vi.mock("@/lib/calc-param-service") call in "D:/dcprint/erp-project/tests/unit/mrp-engine.test.ts" is not at the top level of the module. Although it appears nested, it will be hoisted and executed before any tests run. Move it to the top level to reflect its actual execution order. This will become an error in a future version. -See: https://vitest.dev/guide/mocking/modules#how-it-works -Warning: A vi.mock("@/lib/calc-param-service") call in "D:/dcprint/erp-project/tests/unit/mrp-engine.test.ts" is not at the top level of the module. Although it appears nested, it will be hoisted and executed before any tests run. Move it to the top level to reflect its actual execution order. This will become an error in a future version. -See: https://vitest.dev/guide/mocking/modules#how-it-works -Warning: A vi.mock("@/lib/calc-param-service") call in "D:/dcprint/erp-project/tests/unit/mrp-engine.test.ts" is not at the top level of the module. Although it appears nested, it will be hoisted and executed before any tests run. Move it to the top level to reflect its actual execution order. This will become an error in a future version. -See: https://vitest.dev/guide/mocking/modules#how-it-works -Warning: A vi.mock("@/lib/calc-param-service") call in "D:/dcprint/erp-project/tests/unit/mrp-engine.test.ts" is not at the top level of the module. Although it appears nested, it will be hoisted and executed before any tests run. Move it to the top level to reflect its actual execution order. This will become an error in a future version. -See: https://vitest.dev/guide/mocking/modules#how-it-works -Warning: A vi.mock("@/lib/calc-param-service") call in "D:/dcprint/erp-project/tests/unit/mrp-engine.test.ts" is not at the top level of the module. Although it appears nested, it will be hoisted and executed before any tests run. Move it to the top level to reflect its actual execution order. This will become an error in a future version. -See: https://vitest.dev/guide/mocking/modules#how-it-works -Warning: A vi.mock("@/lib/calc-param-service") call in "D:/dcprint/erp-project/tests/unit/mrp-engine.test.ts" is not at the top level of the module. Although it appears nested, it will be hoisted and executed before any tests run. Move it to the top level to reflect its actual execution order. This will become an error in a future version. -See: https://vitest.dev/guide/mocking/modules#how-it-works -Warning: A vi.mock("@/lib/calc-param-service") call in "D:/dcprint/erp-project/tests/unit/mrp-engine.test.ts" is not at the top level of the module. Although it appears nested, it will be hoisted and executed before any tests run. Move it to the top level to reflect its actual execution order. This will become an error in a future version. -See: https://vitest.dev/guide/mocking/modules#how-it-works - ✓ tests/unit/mrp-engine.test.ts > MRP 引擎 - explodeBOM > 应该正确展开单层BOM为树形结构 9ms - ✓ tests/unit/mrp-engine.test.ts > MRP 引擎 - explodeBOM > 无BOM的产品应标记为叶子节点 1ms - ✓ tests/unit/mrp-engine.test.ts > MRP 引擎 - explodeBOM > 应该处理多层BOM展开(半成品) 3ms - ✓ tests/unit/mrp-engine.test.ts > MRP 引擎 - calculateNetRequirements > 空工单列表应返回空数组 3ms - ✓ tests/unit/mrp-engine.test.ts > MRP 引擎 - calculateNetRequirements > 应该正确计算净需求(考虑库存和在途) 3ms - ✓ tests/unit/mrp-engine.test.ts > MRP 引擎 - runFullMRP > 空工单列表应返回空结果 1ms - ✓ tests/unit/mrp-engine.test.ts > MRP 引擎 - runFullMRP > 应该执行完整MRP流程(不自动生成请购单) 3ms - ✓ tests/unit/application/handlers/ReconciliationWriteOffHandler.test.ts > ReconciliationWriteOffHandler > should update receivable received_amount/balance/status for each write-off record (T306) 7ms - ✓ tests/unit/application/handlers/ReconciliationWriteOffHandler.test.ts > ReconciliationWriteOffHandler > 全额核销后状态变为 3(已结清) 4ms - ✓ tests/unit/application/handlers/ReconciliationWriteOffHandler.test.ts > ReconciliationWriteOffHandler > 部分核销后状态变为 2(部分收款) 1ms - ✓ tests/unit/application/handlers/ReconciliationWriteOffHandler.test.ts > ReconciliationWriteOffHandler > 应收单不存在时跳过(不抛错) 3ms - ✓ tests/unit/application/handlers/ReconciliationWriteOffHandler.test.ts > ReconciliationWriteOffHandler > 余额为 0 时跳过核销 1ms - ✓ tests/unit/application/handlers/ReconciliationWriteOffHandler.test.ts > ReconciliationWriteOffHandler > 核销金额超过余额时截断为余额 1ms - ✓ tests/unit/application/handlers/ReconciliationWriteOffHandler.test.ts > ReconciliationWriteOffHandler > writeOffRecords 为空时跳过 1ms - ✓ tests/unit/application/handlers/ReconciliationWriteOffHandler.test.ts > ReconciliationWriteOffHandler > writeOffRecords 为 undefined 时跳过 0ms - ✓ tests/unit/application/handlers/ReconciliationWriteOffHandler.test.ts > ReconciliationWriteOffHandler > 多记录顺序处理 2ms - ✓ tests/unit/application/services/purchase-application-service.test.ts > PurchaseApplicationService.createOrder > 使用默认 CNY 币种时,base 字段与原始金额一致(无需换算) 12ms - ✓ tests/unit/application/services/purchase-application-service.test.ts > PurchaseApplicationService.createOrder > 使用非 CNY 币种时调用汇率服务换算本位币金额 3ms - ✓ tests/unit/application/services/purchase-application-service.test.ts > PurchaseApplicationService.createOrder > 显式传入币种时不查询系统默认币种配置 1ms - ✓ tests/unit/application/services/purchase-application-service.test.ts > PurchaseApplicationService.createOrder > 各行 base 金额 = 原始金额 × 汇率 1ms - ✓ tests/unit/application/services/purchase-application-service.test.ts > PurchaseApplicationService.createOrder > 表头 base 汇总金额 = 各行 base 金额之和 1ms - ✓ src/app/[locale]/warehouse/inbound/__tests__/mapRecordsToLabels.test.ts > filterApprovedRecords > 保留 approved 和 completed 状态的记录 7ms - ✓ src/app/[locale]/warehouse/inbound/__tests__/mapRecordsToLabels.test.ts > filterApprovedRecords > 空数组返回空数组 1ms - ✓ src/app/[locale]/warehouse/inbound/__tests__/mapRecordsToLabels.test.ts > filterApprovedRecords > 全部为非批准状态时返回空数组 1ms - ✓ src/app/[locale]/warehouse/inbound/__tests__/mapRecordsToLabels.test.ts > filterApprovedRecords > 不修改原始数组 1ms - ✓ src/app/[locale]/warehouse/inbound/__tests__/mapRecordsToLabels.test.ts > mapRecordsToLabels > 将含单个 item 的记录映射为一个标签 3ms - ✓ src/app/[locale]/warehouse/inbound/__tests__/mapRecordsToLabels.test.ts > mapRecordsToLabels > 将含多个 items 的记录展开为多个标签 1ms - ✓ src/app/[locale]/warehouse/inbound/__tests__/mapRecordsToLabels.test.ts > mapRecordsToLabels > 多条记录混合展开 1ms - ✓ src/app/[locale]/warehouse/inbound/__tests__/mapRecordsToLabels.test.ts > mapRecordsToLabels > items 为空数组时跳过该记录 1ms - ✓ src/app/[locale]/warehouse/inbound/__tests__/mapRecordsToLabels.test.ts > mapRecordsToLabels > items 为 undefined 时跳过该记录 5ms - ✓ src/app/[locale]/warehouse/inbound/__tests__/mapRecordsToLabels.test.ts > mapRecordsToLabels > 标签包含 record 和 item 原始引用 1ms - ✓ src/app/[locale]/warehouse/inbound/__tests__/mapRecordsToLabels.test.ts > mapRecordsToLabels > inboundTime 映射自 record.create_time 1ms - ✓ src/app/[locale]/warehouse/inbound/__tests__/mapRecordsToLabels.test.ts > mapRecordsToLabels > 空数组返回空数组 0ms - ✓ src/app/[locale]/warehouse/inbound/__tests__/mapRecordsToLabels.test.ts > mapRecordsToLabels > order_no 为 undefined 时 labelNo 回退为 "undefined-1" 1ms - ✓ tests/unit/infrastructure/event-bus/outbox-restart-recovery.test.ts > 1.7.3 Outbox 重启恢复场景 > 进程重启后首次 poll 消费所有遗留 pending 事件(2 成功 + 1 重试失败) 19ms - ✓ tests/unit/infrastructure/event-bus/outbox-restart-recovery.test.ts > 1.7.3 Outbox 重启恢复场景 > 重启后遗留 retry_count=3 的失败事件 → 首次 poll 触发死信 6ms - ✓ tests/unit/infrastructure/event-bus/outbox-restart-recovery.test.ts > 1.7.3 Outbox 重启恢复场景 > 重启后无遗留事件 → poll 返回空结果 2ms - ✓ tests/integration/stocktaking-flow.test.ts > 盘点流程集成测试 > 完整盘点流程 > 应该完成盘点流程:创建 → 录入 → 审批 → 调整 8ms - ✓ tests/integration/stocktaking-flow.test.ts > 盘点流程集成测试 > 完整盘点流程 > 应该正确计算盘点差异 1ms - ✓ tests/integration/stocktaking-flow.test.ts > 盘点流程集成测试 > 完整盘点流程 > 应该正确处理盘盈(库存增加) 2ms - ✓ tests/integration/stocktaking-flow.test.ts > 盘点流程集成测试 > 完整盘点流程 > 应该正确处理盘亏(库存扣减) 1ms - ✓ tests/integration/stocktaking-flow.test.ts > 盘点流程集成测试 > 盘点类型 > 应该支持全盘 3ms - ✓ tests/integration/stocktaking-flow.test.ts > 盘点流程集成测试 > 盘点类型 > 应该支持抽盘 1ms - ✓ tests/integration/stocktaking-flow.test.ts > 盘点流程集成测试 > 盘点类型 > 应该支持循环盘点 1ms - ✓ tests/integration/stocktaking-flow.test.ts > 盘点流程集成测试 > 盘点类型 > 应该支持指定物料盘点 1ms - ✓ tests/integration/stocktaking-flow.test.ts > 盘点流程集成测试 > 盘点状态流转 > 应该按正确顺序流转状态 3ms - ✓ tests/integration/stocktaking-flow.test.ts > 盘点流程集成测试 > 盘点状态流转 > 应该允许取消未审批的盘点单 1ms - ✓ tests/integration/stocktaking-flow.test.ts > 盘点流程集成测试 > 并发控制 > 应该防止盘点期间库存变动 1ms - ✓ tests/integration/stocktaking-flow.test.ts > 盘点流程集成测试 > 并发控制 > 应该使用快照保证数据一致性 1ms - ✓ tests/integration/stocktaking-flow.test.ts > 盘点流程集成测试 > 差异处理 > 应该正确计算差异金额 1ms - ✓ tests/integration/stocktaking-flow.test.ts > 盘点流程集成测试 > 差异处理 > 应该生成差异报告 1ms - ✓ tests/integration/stocktaking-flow.test.ts > 盘点流程集成测试 > 审批流程 > 应该根据差异金额决定审批级别 1ms - ✓ tests/integration/stocktaking-flow.test.ts > 盘点流程集成测试 > 审批流程 > 应该支持多级审批 1ms - ✓ tests/integration/stocktaking-flow.test.ts > 盘点流程集成测试 > 业务场景 > 场景1:月度盘点 1ms - ✓ tests/integration/stocktaking-flow.test.ts > 盘点流程集成测试 > 业务场景 > 场景2:年度大盘点 0ms - ✓ tests/integration/stocktaking-flow.test.ts > 盘点流程集成测试 > 业务场景 > 场景3:异常盘点(发现差异后立即盘点) 0ms - ✓ tests/integration/stocktaking-flow.test.ts > 盘点流程集成测试 > 数据一致性 > 应该保证盘点单和库存调整的一致性 1ms - ✓ tests/unit/application/handlers/ToolUsageSyncHandler.test.ts > ToolUsageSyncHandler > 为每个 toolId 调用 recordUsage 13ms - ✓ tests/unit/application/handlers/ToolUsageSyncHandler.test.ts > ToolUsageSyncHandler > completedQty <= 0 时跳过 2ms - ✓ tests/unit/application/handlers/ToolUsageSyncHandler.test.ts > ToolUsageSyncHandler > toolIds 为空时跳过 1ms - ✓ tests/unit/application/handlers/ToolUsageSyncHandler.test.ts > ToolUsageSyncHandler > 已有使用记录时跳过(业务幂等) 2ms - ✓ tests/unit/application/handlers/ToolUsageSyncHandler.test.ts > ToolUsageSyncHandler > 单个 toolId 出错不影响其他 2ms - ✓ tests/unit/application/handlers/ToolUsageSyncHandler.test.ts > ToolUsageSyncHandler > useCount 向上取整 1ms - ✓ tests/unit/application/handlers/ToolUsageSyncHandler.test.ts > ToolUsageSyncHandler > operatorId 为 undefined 时正常处理 1ms - ✓ tests/unit/over-issue-validation.test.ts > 超领校验 > submitOverRequisition - 超领申请 > 应该成功提交超领申请(需要审批) 8ms - ✓ tests/unit/over-issue-validation.test.ts > 超领校验 > submitOverRequisition - 超领申请 > 应该拒绝无原因的超领申请 1ms - ✓ tests/unit/over-issue-validation.test.ts > 超领校验 > submitOverRequisition - 超领申请 > 应该直接生成超领单(不需要审批) 3ms - ✓ tests/unit/over-issue-validation.test.ts > 超领校验 > submitOverRequisition - 超领申请 > 应该正确计算超领比例 1ms - ✓ tests/unit/over-issue-validation.test.ts > 超领校验 > submitOverRequisition - 超领申请 > 应该拒绝超过超领上限的申请 1ms - ✓ tests/unit/over-issue-validation.test.ts > 超领校验 > submitSupplementaryRequisition - 补料申请 > 应该成功提交补料申请 1ms - ✓ tests/unit/over-issue-validation.test.ts > 超领校验 > submitSupplementaryRequisition - 补料申请 > 应该拒绝无原因的补料申请 1ms - ✓ tests/unit/over-issue-validation.test.ts > 超领校验 > submitSupplementaryRequisition - 补料申请 > 应该拒绝原领料单不存在的补料申请 0ms - ✓ tests/unit/over-issue-validation.test.ts > 超领校验 > approveRequisition - 审批 > 应该成功审批通过 1ms - ✓ tests/unit/over-issue-validation.test.ts > 超领校验 > approveRequisition - 审批 > 应该成功审批驳回 1ms - ✓ tests/unit/over-issue-validation.test.ts > 超领校验 > 超领校验规则 > 应该正确计算超领金额 0ms - ✓ tests/unit/over-issue-validation.test.ts > 超领校验 > 超领校验规则 > 应该验证超领比例是否在允许范围内 0ms - ✓ tests/unit/over-issue-validation.test.ts > 超领校验 > 超领校验规则 > 应该验证超领数量是否合理 0ms - ✓ tests/unit/over-issue-validation.test.ts > 超领校验 > 边界情况 > 应该处理数量为0的超领申请 0ms - ✓ tests/unit/over-issue-validation.test.ts > 超领校验 > 边界情况 > 应该处理小数数量 1ms - ✓ tests/unit/over-issue-validation.test.ts > 超领校验 > 边界情况 > 应该处理负数数量(拒绝) 1ms - ✓ tests/unit/over-issue-validation.test.ts > 超领校验 > 并发控制 > 应该防止重复审批 1ms - ✓ tests/unit/over-issue-validation.test.ts > 超领校验 > 审计日志 > 应该记录超领申请的审计日志(异常路径触发 secureLog) 1ms - ✓ tests/unit/over-issue-validation.test.ts > 超领业务场景 > 场景1:正常超领流程 1ms - ✓ tests/unit/over-issue-validation.test.ts > 超领业务场景 > 场景2:超领申请被驳回 0ms - ✓ tests/unit/over-issue-validation.test.ts > 超领业务场景 > 场景3:连续超领累计校验 0ms - ✓ tests/unit/lib/label-service.test.ts > label-service - pure functions > escapeZPLText (internal) > should escape backslash 5ms - ✓ tests/unit/lib/label-service.test.ts > label-service - pure functions > escapeZPLText (internal) > should escape caret 1ms - ✓ tests/unit/lib/label-service.test.ts > label-service - pure functions > escapeZPLText (internal) > should escape tilde 0ms - ✓ tests/unit/lib/label-service.test.ts > label-service - pure functions > escapeZPLText (internal) > should escape mixed special chars 2ms - ✓ tests/unit/lib/label-service.test.ts > label-service - pure functions > escapeZPLText (internal) > should return plain text unchanged 1ms - ✓ tests/unit/lib/label-service.test.ts > label-service - pure functions > escapeZPLText (internal) > should handle empty string 0ms - ✓ tests/unit/lib/label-service.test.ts > label-service - pure functions > wrapText (internal) > should return empty array for falsy input 2ms - ✓ tests/unit/lib/label-service.test.ts > label-service - pure functions > wrapText (internal) > should return single line if text fits 1ms - ✓ tests/unit/lib/label-service.test.ts > label-service - pure functions > wrapText (internal) > should wrap to multiple lines 1ms - ✓ tests/unit/lib/label-service.test.ts > label-service - pure functions > wrapText (internal) > should handle partial remainder 1ms - ✓ tests/unit/lib/label-service.test.ts > label-service - pure functions > wrapText (internal) > should handle Chinese characters 0ms - ✓ tests/unit/lib/label-service.test.ts > label-service - pure functions > generateZPLLabel > should start with ^XA and end with ^XZ 1ms - ✓ tests/unit/lib/label-service.test.ts > label-service - pure functions > generateZPLLabel > should include CI28 for Chinese chars 0ms - ✓ tests/unit/lib/label-service.test.ts > label-service - pure functions > generateZPLLabel > should include QR code with JSON content 1ms - ✓ tests/unit/lib/label-service.test.ts > label-service - pure functions > generateZPLLabel > should include material code text 0ms - ✓ tests/unit/lib/label-service.test.ts > label-service - pure functions > generateZPLLabel > should include material name 0ms - ✓ tests/unit/lib/label-service.test.ts > label-service - pure functions > generateZPLLabel > should include quantity and unit 0ms - ✓ tests/unit/lib/label-service.test.ts > label-service - pure functions > generateZPLLabel > should include specification when provided 0ms - ✓ tests/unit/lib/label-service.test.ts > label-service - pure functions > generateZPLLabel > should not include specification when omitted 1ms - ✓ tests/unit/lib/label-service.test.ts > label-service - pure functions > generateZPLLabel > should include warehouse name when provided 0ms - ✓ tests/unit/lib/label-service.test.ts > label-service - pure functions > generateZPLLabel > should include label number at bottom 0ms - ✓ tests/unit/lib/label-service.test.ts > label-service - pure functions > generateZPLLabel > should calculate dimensions based on mm to dots (60x40mm) 0ms - ✓ tests/unit/lib/label-service.test.ts > label-service - pure functions > generateZPLLabel > should calculate dimensions based on mm to dots (100x60mm) 0ms - ✓ tests/unit/lib/label-service.test.ts > label-service - pure functions > generateZPLLabel > should escape special ZPL chars in text values 0ms - ✓ tests/unit/lib/label-service.test.ts > label-service - pure functions > generateZPLBatchLabels > should join multiple labels with newlines 1ms - ✓ tests/unit/lib/label-service.test.ts > label-service - pure functions > generateZPLBatchLabels > should handle single label 0ms - ✓ tests/unit/lib/label-service.test.ts > label-service - pure functions > generateZPLBatchLabels > should handle empty array 0ms - ✓ tests/unit/lib/label-service.test.ts > label-service - pure functions > QR code JSON format > should produce valid JSON for inbound labels 1ms - ✓ tests/unit/lib/label-service.test.ts > label-service - pure functions > QR code JSON format > should produce valid JSON for work order labels 1ms - ✓ tests/unit/lib/label-service.test.ts > label-service - pure functions > QR code JSON format > should produce valid JSON for FIFO priority labels 1ms - ✓ tests/unit/lib/label-service.test.ts > label-service - pure functions > QR code JSON format > should not contain UUID format (35-char hex) 1ms - ✓ tests/unit/lib/label-service.test.ts > label-service - pure functions > QR code JSON format > should be valid JSON when stored as qr_code field 2ms - ✓ tests/unit/warehouse/inbound-from-po.test.ts > InboundApplicationService.createInboundFromPO > 容差内通过:quantity <= maxAllowed - receivedQty 时创建成功 12ms - ✓ tests/unit/warehouse/inbound-from-po.test.ts > InboundApplicationService.createInboundFromPO > 超容差拦截:quantity > maxAllowed - receivedQty 时抛 DomainError,不落库 4ms - ✓ tests/unit/warehouse/inbound-from-po.test.ts > InboundApplicationService.createInboundFromPO > source 透传:sourceType/sourceOrderId/purchaseOrderItemId/purchaseOrderLineNo 写入聚合 3ms - ✓ tests/unit/warehouse/inbound-from-po.test.ts > InboundApplicationService.createInboundFromPO > 非法订单状态拦截:draft/completed 单拒绝创建 4ms - ✓ tests/unit/warehouse/inbound-from-po.test.ts > InboundApplicationService.createInboundFromPO > 采购单不存在时抛 NotFoundError 2ms - ✓ tests/unit/domain/dcprint/aggregates/formula-events.test.ts > T401: InkFormulaVersion 聚合根领域事件 > activate 触发 FormulaVersionActivatedEvent > 草稿版本 activate 后触发生效事件 6ms - ✓ tests/unit/domain/dcprint/aggregates/formula-events.test.ts > T401: InkFormulaVersion 聚合根领域事件 > activate 触发 FormulaVersionActivatedEvent > createDraft 创建的草稿可正常 activate(无 id 不触发事件) 3ms - ✓ tests/unit/domain/dcprint/aggregates/formula-events.test.ts > T401: InkFormulaVersion 聚合根领域事件 > cancel 触发 FormulaVersionCancelledEvent > 已生效版本 cancel 后触发作废事件 1ms - ✓ tests/unit/domain/dcprint/aggregates/formula-events.test.ts > T401: InkFormulaVersion 聚合根领域事件 > 非法状态流转抛 InvalidTransitionError > 已生效版本再次 activate 抛 InvalidTransitionError 1ms - ✓ tests/unit/domain/dcprint/aggregates/formula-events.test.ts > T401: InkFormulaVersion 聚合根领域事件 > 非法状态流转抛 InvalidTransitionError > 草稿版本不能直接 cancel 抛 InvalidTransitionError 1ms - ✓ tests/unit/domain/dcprint/aggregates/formula-events.test.ts > T401: InkFormulaVersion 聚合根领域事件 > 领域事件管理 > clearDomainEvents 清空事件数组 1ms - ✓ tests/unit/domain/warehouse/value-objects/order-status.test.ts > 8.2/8.3 OrderStatus 值对象与状态机 > 静态工厂方法 > draft() 创建草稿状态 5ms - ✓ tests/unit/domain/warehouse/value-objects/order-status.test.ts > 8.2/8.3 OrderStatus 值对象与状态机 > 静态工厂方法 > pending() 创建待审核状态 1ms - ✓ tests/unit/domain/warehouse/value-objects/order-status.test.ts > 8.2/8.3 OrderStatus 值对象与状态机 > 静态工厂方法 > completed() 创建已完成状态 1ms - ✓ tests/unit/domain/warehouse/value-objects/order-status.test.ts > 8.2/8.3 OrderStatus 值对象与状态机 > 静态工厂方法 > cancelled() 创建已取消状态 1ms - ✓ tests/unit/domain/warehouse/value-objects/order-status.test.ts > 8.2/8.3 OrderStatus 值对象与状态机 > from() 字符串转换 > 合法领域状态直接映射 1ms - ✓ tests/unit/domain/warehouse/value-objects/order-status.test.ts > 8.2/8.3 OrderStatus 值对象与状态机 > from() 字符串转换 > DB 状态 approved 映射为 completed(向后兼容) 1ms - ✓ tests/unit/domain/warehouse/value-objects/order-status.test.ts > 8.2/8.3 OrderStatus 值对象与状态机 > from() 字符串转换 > 非法状态字符串抛 DomainError 4ms - ✓ tests/unit/domain/warehouse/value-objects/order-status.test.ts > 8.2/8.3 OrderStatus 值对象与状态机 > 状态流转(合法路径) > draft → pending(提交) 1ms - ✓ tests/unit/domain/warehouse/value-objects/order-status.test.ts > 8.2/8.3 OrderStatus 值对象与状态机 > 状态流转(合法路径) > draft → cancelled(取消) 1ms - ✓ tests/unit/domain/warehouse/value-objects/order-status.test.ts > 8.2/8.3 OrderStatus 值对象与状态机 > 状态流转(合法路径) > pending → completed(审核通过) 1ms - ✓ tests/unit/domain/warehouse/value-objects/order-status.test.ts > 8.2/8.3 OrderStatus 值对象与状态机 > 状态流转(合法路径) > pending → cancelled(取消) 0ms - ✓ tests/unit/domain/warehouse/value-objects/order-status.test.ts > 8.2/8.3 OrderStatus 值对象与状态机 > 状态流转(合法路径) > completed → pending(反审) 0ms - ✓ tests/unit/domain/warehouse/value-objects/order-status.test.ts > 8.2/8.3 OrderStatus 值对象与状态机 > 非法状态流转 > draft → completed 抛 DomainError(不能直接完成) 1ms - ✓ tests/unit/domain/warehouse/value-objects/order-status.test.ts > 8.2/8.3 OrderStatus 值对象与状态机 > 非法状态流转 > cancelled → 任意状态抛 DomainError(终态不可流转) 1ms - ✓ tests/unit/domain/warehouse/value-objects/order-status.test.ts > 8.2/8.3 OrderStatus 值对象与状态机 > 非法状态流转 > completed → cancelled 抛 DomainError(已完成不可取消) 0ms - ✓ tests/unit/domain/warehouse/value-objects/order-status.test.ts > 8.2/8.3 OrderStatus 值对象与状态机 > 非法状态流转 > canTransitionTo 返回 false 不抛错 0ms - ✓ tests/unit/domain/warehouse/value-objects/order-status.test.ts > 8.2/8.3 OrderStatus 值对象与状态机 > 操作权限 > draft 状态可编辑可删除可提交 0ms - ✓ tests/unit/domain/warehouse/value-objects/order-status.test.ts > 8.2/8.3 OrderStatus 值对象与状态机 > 操作权限 > pending 状态不可编辑不可删除 0ms - ✓ tests/unit/domain/warehouse/value-objects/order-status.test.ts > 8.2/8.3 OrderStatus 值对象与状态机 > 操作权限 > completed 状态不可编辑不可删除 0ms - ✓ tests/unit/domain/warehouse/value-objects/order-status.test.ts > 8.2/8.3 OrderStatus 值对象与状态机 > 操作权限 > cancelled 状态不可编辑不可删除 0ms - ✓ tests/unit/domain/warehouse/value-objects/order-status.test.ts > 8.2/8.3 OrderStatus 值对象与状态机 > 相等性判断 > 相同状态值相等 0ms - ✓ tests/unit/domain/warehouse/value-objects/order-status.test.ts > 8.2/8.3 OrderStatus 值对象与状态机 > 相等性判断 > 不同状态值不相等 0ms - ✓ tests/unit/application/services/purchase-return-application-service.test.ts > PurchaseReturnApplicationService.createReturn > 从原采购单继承币种信息(默认 CNY) 10ms - ✓ tests/unit/application/services/purchase-return-application-service.test.ts > PurchaseReturnApplicationService.createReturn > 从原采购单继承非 CNY 币种 2ms - ✓ tests/unit/application/services/purchase-return-application-service.test.ts > PurchaseReturnApplicationService.createReturn > 正确计算明细行 baseUnitPrice 2ms - ✓ tests/unit/application/services/purchase-return-application-service.test.ts > PurchaseReturnApplicationService.createReturn > 正确计算明细行 baseAmount 1ms - ✓ tests/unit/application/services/purchase-return-application-service.test.ts > PurchaseReturnApplicationService.createReturn > 汇率 1:1 时 base 金额与原币金额一致 2ms - ✓ tests/unit/application/services/purchase-return-application-service.test.ts > PurchaseReturnApplicationService.createReturn > 原采购单不存在时抛出 NotFoundError 4ms - ✓ tests/unit/domain/dcprint/aggregates/tool-events.test.ts > T401: Tool 聚合根领域事件 > recordUsage 触发 ToolUsedEvent > context.workOrderId 提供时触发 ToolUsedEvent 8ms - ✓ tests/unit/domain/dcprint/aggregates/tool-events.test.ts > T401: Tool 聚合根领域事件 > recordUsage 触发 ToolUsedEvent > 未提供 context 时不触发 ToolUsedEvent 1ms - ✓ tests/unit/domain/dcprint/aggregates/tool-events.test.ts > T401: Tool 聚合根领域事件 > recordUsage 触发 ToolWarningTriggeredEvent > 跨越预警阈值时触发预警事件 1ms - ✓ tests/unit/domain/dcprint/aggregates/tool-events.test.ts > T401: Tool 聚合根领域事件 > recordUsage 触发 ToolWarningTriggeredEvent > 已在 WARNING 状态时不重复触发预警事件 1ms - ✓ tests/unit/domain/dcprint/aggregates/tool-events.test.ts > T401: Tool 聚合根领域事件 > scrap 触发 ToolScrappedEvent > scrap(reason, operatorId) 触发 ToolScrappedEvent 1ms - ✓ tests/unit/domain/dcprint/aggregates/tool-events.test.ts > T401: Tool 聚合根领域事件 > scrap 触发 ToolScrappedEvent > 已报废工装再次 scrap 抛 DomainError 1ms - ✓ tests/unit/domain/dcprint/aggregates/tool-events.test.ts > T401: Tool 聚合根领域事件 > 领域事件管理 > clearDomainEvents 清空事件数组 1ms - ✓ tests/unit/domain/dcprint/aggregates/tool-events.test.ts > T401: Tool 聚合根领域事件 > 领域事件管理 > getDomainEvents 返回副本(不可变) 1ms - ✓ src/lib/__tests__/mrp-engine-v2.test.ts > MRP Engine Core Algorithms > calculateEOQ - 经济订货批量 > 应该正确计算EOQ 5ms - ✓ src/lib/__tests__/mrp-engine-v2.test.ts > MRP Engine Core Algorithms > calculateEOQ - 经济订货批量 > 当需求为0时返回0 1ms - ✓ src/lib/__tests__/mrp-engine-v2.test.ts > MRP Engine Core Algorithms > calculateEOQ - 经济订货批量 > 当订货成本为0时返回0 0ms - ✓ src/lib/__tests__/mrp-engine-v2.test.ts > MRP Engine Core Algorithms > calculateEOQ - 经济订货批量 > 当存储费率为0时返回0 1ms - ✓ src/lib/__tests__/mrp-engine-v2.test.ts > MRP Engine Core Algorithms > calculateEOQ - 经济订货批量 > 当单价为0时返回0 0ms - ✓ src/lib/__tests__/mrp-engine-v2.test.ts > MRP Engine Core Algorithms > applyLotSizing - 批量调整 > 直接批量:净需求等于计划量 1ms - ✓ src/lib/__tests__/mrp-engine-v2.test.ts > MRP Engine Core Algorithms > applyLotSizing - 批量调整 > 净需求为0时返回0 1ms - ✓ src/lib/__tests__/mrp-engine-v2.test.ts > MRP Engine Core Algorithms > applyLotSizing - 批量调整 > 固定批量:向上取整到最近的固定批量倍数 1ms - ✓ src/lib/__tests__/mrp-engine-v2.test.ts > MRP Engine Core Algorithms > applyLotSizing - 批量调整 > 固定批量为0时退化为直接批量 1ms - ✓ src/lib/__tests__/mrp-engine-v2.test.ts > MRP Engine Core Algorithms > applyLotSizing - 批量调整 > 经济批量EOQ:向上取整到最近的EOQ倍数 1ms - ✓ src/lib/__tests__/mrp-engine-v2.test.ts > MRP Engine Core Algorithms > applyLotSizing - 批量调整 > EOQ为0时退化为直接批量 0ms - ✓ src/lib/__tests__/mrp-engine-v2.test.ts > MRP Engine Core Algorithms > applyLotSizing - 批量调整 > 期间供应批量 0ms - ✓ src/lib/__tests__/mrp-engine-v2.test.ts > MRP Engine Core Algorithms > calculateSafetyStock - 安全库存计算 > none策略:安全库存为0 0ms - ✓ src/lib/__tests__/mrp-engine-v2.test.ts > MRP Engine Core Algorithms > calculateSafetyStock - 安全库存计算 > fixed策略:使用固定安全库存 0ms - ✓ src/lib/__tests__/mrp-engine-v2.test.ts > MRP Engine Core Algorithms > calculateSafetyStock - 安全库存计算 > fixed策略:安全库存为0时返回0 0ms - ✓ src/lib/__tests__/mrp-engine-v2.test.ts > MRP Engine Core Algorithms > calculateSafetyStock - 安全库存计算 > days_of_coverage策略:日均需求 × 覆盖天数 0ms - ✓ src/lib/__tests__/mrp-engine-v2.test.ts > MRP Engine Core Algorithms > calculateSafetyStock - 安全库存计算 > days_of_coverage策略:覆盖天数为0时返回0 0ms - ✓ src/lib/__tests__/mrp-engine-v2.test.ts > MRP Engine Core Algorithms > calculateSafetyStock - 安全库存计算 > days_of_coverage策略:日均需求为0时返回0 0ms - ✓ src/lib/__tests__/mrp-engine-v2.test.ts > MRP Engine Core Algorithms > generateBucketDates - 时间桶生成 > 按天生成时间桶 1ms - ✓ src/lib/__tests__/mrp-engine-v2.test.ts > MRP Engine Core Algorithms > generateBucketDates - 时间桶生成 > 按周生成时间桶 1ms - ✓ src/lib/__tests__/mrp-engine-v2.test.ts > MRP Engine Core Algorithms > generateBucketDates - 时间桶生成 > 按月生成时间桶 1ms - ✓ src/lib/__tests__/mrp-engine-v2.test.ts > MRP Engine Core Algorithms > generateBucketDates - 时间桶生成 > 0天返回空数组 0ms - ✓ src/lib/__tests__/mrp-engine-v2.test.ts > MRP Engine Core Algorithms > generateBucketDates - 时间桶生成 > 1天返回1个桶 0ms - ✓ src/lib/__tests__/mrp-engine-v2.test.ts > MRPEngine - MRP引擎 > 应该创建MRP引擎实例 2ms - ✓ src/lib/__tests__/mrp-engine-v2.test.ts > MRPEngine - MRP引擎 > 应该使用默认配置 1ms - ✓ src/lib/__tests__/mrp-engine-v2.test.ts > MRPEngine - MRP引擎 > 应该使用自定义配置 0ms - ✓ src/lib/__tests__/mrp-engine-v2.test.ts > MRP Net Requirement Calculation > 净需求 = 毛需求 - 可用库存 - 预计入库 + 安全库存 0ms - ✓ src/lib/__tests__/mrp-engine-v2.test.ts > MRP Net Requirement Calculation > 当可用库存足够时,净需求为0 0ms - ✓ src/lib/__tests__/mrp-engine-v2.test.ts > MRP Net Requirement Calculation > 当可用库存不足时,产生净需求 0ms - ✓ src/lib/__tests__/mrp-engine-v2.test.ts > MRP Net Requirement Calculation > 安全库存必须保留,不能用于满足需求 0ms - ✓ src/lib/__tests__/mrp-engine-v2.test.ts > MRP Lead Time Offsetting > 提前期偏移:计划投入 = 计划产出日期 - 提前期 0ms - ✓ src/lib/__tests__/mrp-engine-v2.test.ts > MRP Lead Time Offsetting > 提前期为0时,投入和产出同期 0ms - ✓ src/lib/__tests__/mrp-engine-v2.test.ts > MRP Lead Time Offsetting > 提前期超出范围时,最早一期投入 0ms - ✓ src/lib/__tests__/mrp-engine-v2.test.ts > MRP BOM Explosion Logic > 单层BOM展开:父件需求 × 子件用量 0ms - ✓ src/lib/__tests__/mrp-engine-v2.test.ts > MRP BOM Explosion Logic > 损耗率为0时,需求=用量×数量 0ms - ✓ src/lib/__tests__/mrp-engine-v2.test.ts > MRP BOM Explosion Logic > 损耗率为10%时,需求增加10% 0ms - ✓ tests/unit/domain/quality/value-objects/unqualified-status.test.ts > UnqualifiedStatus Value Object > static factories > pending() creates pending status 5ms - ✓ tests/unit/domain/quality/value-objects/unqualified-status.test.ts > UnqualifiedStatus Value Object > static factories > handling() creates handling status 1ms - ✓ tests/unit/domain/quality/value-objects/unqualified-status.test.ts > UnqualifiedStatus Value Object > static factories > completed() creates completed status 0ms - ✓ tests/unit/domain/quality/value-objects/unqualified-status.test.ts > UnqualifiedStatus Value Object > from() > accepts valid string values 1ms - ✓ tests/unit/domain/quality/value-objects/unqualified-status.test.ts > UnqualifiedStatus Value Object > from() > throws DomainError on invalid value 1ms - ✓ tests/unit/domain/quality/value-objects/unqualified-status.test.ts > UnqualifiedStatus Value Object > fromDbCode() / toDbCode() > maps 1 -> pending 1ms - ✓ tests/unit/domain/quality/value-objects/unqualified-status.test.ts > UnqualifiedStatus Value Object > fromDbCode() / toDbCode() > maps 2 -> handling 0ms - ✓ tests/unit/domain/quality/value-objects/unqualified-status.test.ts > UnqualifiedStatus Value Object > fromDbCode() / toDbCode() > maps 3 -> completed 1ms - ✓ tests/unit/domain/quality/value-objects/unqualified-status.test.ts > UnqualifiedStatus Value Object > fromDbCode() / toDbCode() > throws DomainError on invalid code 1ms - ✓ tests/unit/domain/quality/value-objects/unqualified-status.test.ts > UnqualifiedStatus Value Object > fromDbCode() / toDbCode() > toDbCode() returns correct inverse mapping 1ms - ✓ tests/unit/domain/quality/value-objects/unqualified-status.test.ts > UnqualifiedStatus Value Object > transitions > pending -> handling is allowed 1ms - ✓ tests/unit/domain/quality/value-objects/unqualified-status.test.ts > UnqualifiedStatus Value Object > transitions > handling -> completed is allowed 0ms - ✓ tests/unit/domain/quality/value-objects/unqualified-status.test.ts > UnqualifiedStatus Value Object > transitions > completed -> handling is forbidden 0ms - ✓ tests/unit/domain/quality/value-objects/unqualified-status.test.ts > UnqualifiedStatus Value Object > transitions > pending -> completed is forbidden (no skip) 0ms - ✓ tests/unit/domain/quality/value-objects/unqualified-status.test.ts > UnqualifiedStatus Value Object > transitions > handling -> pending is forbidden (no backward) 1ms - ✓ tests/unit/domain/quality/value-objects/unqualified-status.test.ts > UnqualifiedStatus Value Object > transitions > completed has no outgoing transitions 0ms - ✓ tests/unit/domain/quality/value-objects/unqualified-status.test.ts > UnqualifiedStatus Value Object > operations > pending allows start_handle/edit/delete 1ms - ✓ tests/unit/domain/quality/value-objects/unqualified-status.test.ts > UnqualifiedStatus Value Object > operations > handling allows complete_handle/edit but not delete 0ms - ✓ tests/unit/domain/quality/value-objects/unqualified-status.test.ts > UnqualifiedStatus Value Object > operations > completed allows view only 0ms - ✓ tests/unit/domain/quality/value-objects/unqualified-status.test.ts > UnqualifiedStatus Value Object > label() > returns Chinese labels 0ms - ✓ tests/unit/domain/quality/value-objects/unqualified-status.test.ts > UnqualifiedStatus Value Object > equals() > returns true for same value 0ms - ✓ tests/unit/domain/quality/value-objects/unqualified-status.test.ts > UnqualifiedStatus Value Object > equals() > returns false for different value 0ms - ✓ src/lib/__tests__/inventory-alert.test.ts > 库存预警逻辑 > calculateAlertLevel > 库存为0:critical 5ms - ✓ src/lib/__tests__/inventory-alert.test.ts > 库存预警逻辑 > calculateAlertLevel > 库存低于安全库存50%:critical 1ms - ✓ src/lib/__tests__/inventory-alert.test.ts > 库存预警逻辑 > calculateAlertLevel > 库存等于安全库存50%:critical(<=50%为critical) 0ms - ✓ src/lib/__tests__/inventory-alert.test.ts > 库存预警逻辑 > calculateAlertLevel > 库存低于安全库存但高于50%:warning 1ms - ✓ src/lib/__tests__/inventory-alert.test.ts > 库存预警逻辑 > calculateAlertLevel > 库存等于安全库存:warning 0ms - ✓ src/lib/__tests__/inventory-alert.test.ts > 库存预警逻辑 > calculateAlertLevel > 库存高于安全库存:normal 0ms - ✓ src/lib/__tests__/inventory-alert.test.ts > 库存预警逻辑 > calculateAlertLevel > 安全库存为0:normal(不预警) 0ms - ✓ src/lib/__tests__/inventory-alert.test.ts > 库存预警逻辑 > calculateAlertLevel > 负库存:critical 0ms - ✓ src/lib/__tests__/inventory-alert.test.ts > 库存预警逻辑 > filterAlertItems > 正确筛选预警项 4ms - ✓ src/lib/__tests__/inventory-alert.test.ts > 库存预警逻辑 > filterAlertItems > 排除正常库存 2ms - ✓ src/lib/__tests__/inventory-alert.test.ts > 库存预警逻辑 > filterAlertItems > 排除未设置安全库存的物料 1ms - ✓ tests/unit/infrastructure/event-bus/event-bus-publish.test.ts > 1.7.1 EventBus.publish 全场景 > 单 handler 成功时 publish 不抛错且 handler 被调用 13ms - ✓ tests/unit/infrastructure/event-bus/event-bus-publish.test.ts > 1.7.1 EventBus.publish 全场景 > 无 handler 时不抛错(静默返回) 1ms - ✓ tests/unit/infrastructure/event-bus/event-bus-publish.test.ts > 1.7.1 EventBus.publish 全场景 > 多 handler 部分失败时 allSettled 全执行,抛第一个 Error 实例(保留 stack) 6ms - ✓ tests/unit/infrastructure/event-bus/event-bus-publish.test.ts > 1.7.1 EventBus.publish 全场景 > 多 handler 全部失败时抛第一个错误(按订阅顺序) 1ms - ✓ tests/unit/infrastructure/event-bus/event-bus-publish.test.ts > 1.7.1 EventBus.publish 全场景 > 非 Error 类型的拒绝原因被包装为 Error 1ms - ✓ tests/unit/infrastructure/event-bus/event-bus-publish.test.ts > 1.7.1 EventBus.publish 全场景 > getHandlerCount 返回订阅的 handler 数量 1ms - ✓ tests/unit/infrastructure/repositories/MysqlCurrencyRepository.test.ts > MysqlCurrencyRepository > getCurrency() > 返回存在的币种信息 7ms - ✓ tests/unit/infrastructure/repositories/MysqlCurrencyRepository.test.ts > MysqlCurrencyRepository > getCurrency() > 币种不存在返回 null 1ms - ✓ tests/unit/infrastructure/repositories/MysqlCurrencyRepository.test.ts > MysqlCurrencyRepository > getLatestRate() > 返回最新汇率记录 1ms - ✓ tests/unit/infrastructure/repositories/MysqlCurrencyRepository.test.ts > MysqlCurrencyRepository > getLatestRate() > 汇率未配置返回 null 1ms - ✓ tests/unit/infrastructure/repositories/MysqlCurrencyRepository.test.ts > MysqlCurrencyRepository > listActiveCurrencies() > 返回启用币种列表 4ms - ✓ tests/unit/infrastructure/repositories/MysqlCurrencyRepository.test.ts > MysqlCurrencyRepository > getRateOnDate() > 返回指定日期的汇率记录 1ms - ✓ tests/unit/infrastructure/repositories/MysqlCurrencyRepository.test.ts > MysqlCurrencyRepository > getRateOnDate() > 指定日期无汇率返回 null 1ms - ✓ tests/unit/application/handlers/DeliveryCancelledHandler.test.ts > DeliveryCancelledHandler > should rollback delivered_qty when delivery is cancelled 11ms - ✓ tests/unit/application/handlers/DeliveryCancelledHandler.test.ts > DeliveryCancelledHandler > should skip if no delivery details found 2ms - ✓ tests/unit/application/handlers/DeliveryCancelledHandler.test.ts > DeliveryCancelledHandler > should handle delivery without orderId 1ms - ✓ tests/unit/application/handlers/DeliveryCancelledHandler.test.ts > DeliveryCancelledHandler > should soft-delete corresponding receivable when delivery is cancelled (T403) 6ms - ✓ tests/unit/application/handlers/DeliveryCancelledHandler.test.ts > DeliveryCancelledHandler > should not call UPDATE fin_receivable when no receivable found (T403) 1ms - ✓ src/lib/__tests__/error-handler.test.ts > 前端错误处理 > getErrorMessage > 网络错误:Failed to fetch 6ms - ✓ src/lib/__tests__/error-handler.test.ts > 前端错误处理 > getErrorMessage > 错误码映射:UNAUTHORIZED 1ms - ✓ src/lib/__tests__/error-handler.test.ts > 前端错误处理 > getErrorMessage > 错误码映射:PERMISSION_DENIED 1ms - ✓ src/lib/__tests__/error-handler.test.ts > 前端错误处理 > getErrorMessage > 错误码映射:INSUFFICIENT_STOCK 1ms - ✓ src/lib/__tests__/error-handler.test.ts > 前端错误处理 > getErrorMessage > HTTP状态码映射:401 1ms - ✓ src/lib/__tests__/error-handler.test.ts > 前端错误处理 > getErrorMessage > HTTP状态码映射:403 1ms - ✓ src/lib/__tests__/error-handler.test.ts > 前端错误处理 > getErrorMessage > HTTP状态码映射:500 2ms - ✓ src/lib/__tests__/error-handler.test.ts > 前端错误处理 > getErrorMessage > SQL错误过滤 1ms - ✓ src/lib/__tests__/error-handler.test.ts > 前端错误处理 > getErrorMessage > 网络超时过滤 2ms - ✓ src/lib/__tests__/error-handler.test.ts > 前端错误处理 > getErrorMessage > JWT错误过滤 1ms - ✓ src/lib/__tests__/error-handler.test.ts > 前端错误处理 > getErrorMessage > 用户友好消息直接返回 1ms - ✓ src/lib/__tests__/error-handler.test.ts > 前端错误处理 > getErrorMessage > 技术性堆栈信息过滤 1ms - ✓ src/lib/__tests__/error-handler.test.ts > 前端错误处理 > getErrorMessage > 字符串错误直接返回 1ms - ✓ src/lib/__tests__/error-handler.test.ts > 前端错误处理 > getErrorMessage > 未知错误返回默认消息 0ms - ✓ src/lib/__tests__/error-handler.test.ts > 前端错误处理 > getErrorMessage > 过长字符串返回默认消息 0ms - ✓ src/lib/__tests__/error-handler.test.ts > 前端错误处理 > getErrorMessage > 优先使用错误码而非HTTP状态码 0ms - ✓ src/lib/__tests__/cost-calculation.test.ts > 移动加权平均成本核算 > calculateMovingAverageCost > 首次入库:当前库存为0 8ms - ✓ src/lib/__tests__/cost-calculation.test.ts > 移动加权平均成本核算 > calculateMovingAverageCost > 第二次入库:不同价格 2ms - ✓ src/lib/__tests__/cost-calculation.test.ts > 移动加权平均成本核算 > calculateMovingAverageCost > 第三次入库:价格下降 1ms - ✓ src/lib/__tests__/cost-calculation.test.ts > 移动加权平均成本核算 > calculateMovingAverageCost > 入库价格为0(赠品/样品) 1ms - ✓ src/lib/__tests__/cost-calculation.test.ts > 移动加权平均成本核算 > calculateMovingAverageCost > 大批量入库稀释成本 1ms - ✓ src/lib/__tests__/cost-calculation.test.ts > 移动加权平均成本核算 > calculateMovingAverageCost > 抛出错误:入库数量为0 2ms - ✓ src/lib/__tests__/cost-calculation.test.ts > 移动加权平均成本核算 > calculateMovingAverageCost > 抛出错误:入库数量为负 1ms - ✓ src/lib/__tests__/cost-calculation.test.ts > 移动加权平均成本核算 > calculateMovingAverageCost > 抛出错误:入库单价为负 1ms - ✓ src/lib/__tests__/cost-calculation.test.ts > 移动加权平均成本核算 > calculateMovingAverageCost > 抛出错误:当前库存为负 2ms - ✓ src/lib/__tests__/cost-calculation.test.ts > 移动加权平均成本核算 > calculateMovingAverageCost > 小数精度处理 1ms - ✓ tests/unit/lib/rate-limit-production.test.ts > rate-limit 生产环境强制 Redis > 生产环境 Redis 不可用时应抛出错误 24ms - ✓ tests/unit/lib/rate-limit-production.test.ts > rate-limit 生产环境强制 Redis > 生产环境 Redis 操作异常应抛出错误 2ms - ✓ tests/unit/lib/rate-limit-production.test.ts > rate-limit 开发环境降级 > 开发环境 Redis 不可用时应降级到内存 3ms - ✓ tests/unit/domain/shared/value-objects/currency-snapshot.test.ts > CurrencySnapshot > create > should create a valid CurrencySnapshot 6ms - ✓ tests/unit/domain/shared/value-objects/currency-snapshot.test.ts > CurrencySnapshot > create > should reject invalid currency code (not 3 chars) 3ms - ✓ tests/unit/domain/shared/value-objects/currency-snapshot.test.ts > CurrencySnapshot > create > should reject invalid exchangeRate (<=0 or NaN) 1ms - ✓ tests/unit/domain/shared/value-objects/currency-snapshot.test.ts > CurrencySnapshot > create > should reject invalid baseCurrency 1ms - ✓ tests/unit/domain/shared/value-objects/currency-snapshot.test.ts > CurrencySnapshot > isSameCurrency > should return true when same currency 1ms - ✓ tests/unit/domain/shared/value-objects/currency-snapshot.test.ts > CurrencySnapshot > isSameCurrency > should return false when different currency 0ms - ✓ tests/unit/domain/shared/value-objects/currency-snapshot.test.ts > CurrencySnapshot > convert > should short-circuit return same amount for same currency 3ms - ✓ tests/unit/domain/shared/value-objects/currency-snapshot.test.ts > CurrencySnapshot > convert > should convert by the locked exchange rate for cross-currency 1ms - ✓ tests/unit/domain/shared/value-objects/currency-snapshot.test.ts > CurrencySnapshot > convert > should convert VND with 0 decimal places 1ms - ✓ tests/unit/domain/shared/value-objects/currency-snapshot.test.ts > CurrencySnapshot > convert > should throw for negative amounts 2ms - ✓ tests/integration/e2e-trace-flow.test.ts > 端到端流程:入库→扫码→追溯 > Step 1: 入库审核后自动生成二维码 > 应在 qrcode_record 中插入原料类型二维码 13ms - ✓ tests/integration/e2e-trace-flow.test.ts > 端到端流程:入库→扫码→追溯 > Step 2: 扫码登记 > 应在 qrcode_scan_log 中记录扫码并更新 scan_count 4ms - ✓ tests/integration/e2e-trace-flow.test.ts > 端到端流程:入库→扫码→追溯 > Step 3: 追溯查询缓存 > 首次查询应从 DB 读取并写入缓存 3ms - ✓ tests/integration/e2e-trace-flow.test.ts > 端到端流程:入库→扫码→追溯 > Step 3: 追溯查询缓存 > 二次查询应命中缓存 1ms - ✓ src/lib/__tests__/api-response.test.ts > sanitizeInput > should escape HTML script tags 5ms - ✓ src/lib/__tests__/api-response.test.ts > sanitizeInput > should escape double quotes 1ms - ✓ src/lib/__tests__/api-response.test.ts > sanitizeInput > should escape single quotes 1ms - ✓ src/lib/__tests__/api-response.test.ts > sanitizeInput > should not modify forward slashes 1ms - ✓ src/lib/__tests__/api-response.test.ts > sanitizeInput > should escape angle brackets 0ms - ✓ src/lib/__tests__/api-response.test.ts > sanitizeInput > should handle empty string 0ms - ✓ src/lib/__tests__/api-response.test.ts > sanitizeInput > should handle plain text with minimal changes 1ms - ✓ src/lib/__tests__/api-response.test.ts > sanitizeObject > should sanitize all string fields in a flat object 1ms - ✓ src/lib/__tests__/api-response.test.ts > sanitizeObject > should handle nested objects recursively 1ms - ✓ src/lib/__tests__/api-response.test.ts > sanitizeObject > should handle arrays of strings 1ms - ✓ src/lib/__tests__/api-response.test.ts > sanitizeObject > should handle arrays of objects 0ms - ✓ src/lib/__tests__/api-response.test.ts > sanitizeObject > should preserve numbers and booleans 0ms - ✓ src/lib/__tests__/api-response.test.ts > sanitizeObject > should handle null and undefined values 0ms - ✓ src/lib/__tests__/api-response.test.ts > sanitizeObject > should handle empty object 1ms - ✓ src/lib/__tests__/api-response.test.ts > sanitizeObject > should handle string input directly 0ms - ✓ tests/unit/domain/dcprint/aggregates/process-card-events.test.ts > T401: SampleProcessCard 聚合根领域事件 > confirm 触发 ProcessCardConfirmedEvent > 打样中状态(status=2)确认后触发确认事件 8ms - ✓ tests/unit/domain/dcprint/aggregates/process-card-events.test.ts > T401: SampleProcessCard 聚合根领域事件 > confirm 触发 ProcessCardConfirmedEvent > 无 id 时确认不触发事件 3ms - ✓ tests/unit/domain/dcprint/aggregates/process-card-events.test.ts > T401: SampleProcessCard 聚合根领域事件 > confirm 状态校验 > 草稿状态(status=1)确认抛 DomainError 1ms - ✓ tests/unit/domain/dcprint/aggregates/process-card-events.test.ts > T401: SampleProcessCard 聚合根领域事件 > confirm 状态校验 > 已确认状态(status=3)再次确认抛 DomainError 1ms - ✓ tests/unit/domain/dcprint/aggregates/process-card-events.test.ts > T401: SampleProcessCard 聚合根领域事件 > submit 状态流转 > submit 将状态从 1(草稿) 变为 2(打样中) 1ms - ✓ tests/unit/domain/dcprint/aggregates/process-card-events.test.ts > T401: SampleProcessCard 聚合根领域事件 > submit 状态流转 > 非草稿状态 submit 抛 DomainError 1ms - ✓ tests/unit/domain/dcprint/aggregates/process-card-events.test.ts > T401: SampleProcessCard 聚合根领域事件 > 领域事件管理 > clearDomainEvents 清空事件数组 1ms - ✓ tests/unit/application/handlers/ReturnOrderInventoryHandler.test.ts > ReturnOrderInventoryHandler > should add inventory when return order is completed 10ms - ✓ tests/unit/application/handlers/ReturnOrderInventoryHandler.test.ts > ReturnOrderInventoryHandler > should create new inventory if not exists 2ms - ✓ tests/unit/application/handlers/ReturnOrderInventoryHandler.test.ts > ReturnOrderInventoryHandler > should skip if no items 1ms - ✓ tests/unit/application/handlers/ReturnOrderInventoryHandler.test.ts > ReturnOrderInventoryHandler > should handle multiple items 1ms - ✓ src/lib/production-scheduling.test.ts > calculatePriorityScore > urgent priority with material ready scores higher than normal priority without material 5ms - ✓ src/lib/production-scheduling.test.ts > calculatePriorityScore > older work orders get higher age bonus 1ms - ✓ src/lib/production-scheduling.test.ts > calculatePriorityScore > work order with customer_name gets bonus 1ms - ✓ src/lib/production-scheduling.test.ts > calculatePriorityScore > minimum score is >= 0 1ms - ✓ src/lib/production-scheduling.test.ts > calculatePriorityScore > maximum score is <= 100 1ms - × src/lib/production-scheduling.test.ts > calculatePriorityScore > material ready adds score compared to not ready 11ms - → expected 30.43 to be greater than 30.43 - ✓ src/lib/production-scheduling.test.ts > suggestScheduleDates > basic scheduling without conflicts 6ms - ✓ src/lib/production-scheduling.test.ts > suggestScheduleDates > weekend skipping moves Saturday to Monday 1ms - ✓ src/lib/production-scheduling.test.ts > suggestScheduleDates > weekend skipping moves Sunday to Monday 1ms - × src/lib/production-scheduling.test.ts > suggestScheduleDates > conflict detection pushes start date forward 5ms - → expected '2025-06-02' not to be '2025-06-02' // Object.is equality - ✓ src/lib/production-scheduling.test.ts > suggestScheduleDates > no conflicts when scheduled after existing 1ms - ✓ src/lib/production-scheduling.test.ts > suggestScheduleDates > uses current date as default start date 1ms - ✓ src/lib/production-scheduling.test.ts > suggestScheduleDates > end date is after start date for multi-day work 1ms - ✓ tests/unit/domain/quality/value-objects/handle-method.test.ts > HandleMethod Value Object > static factories > rework() creates rework method 6ms - ✓ tests/unit/domain/quality/value-objects/handle-method.test.ts > HandleMethod Value Object > static factories > scrap() creates scrap method 1ms - ✓ tests/unit/domain/quality/value-objects/handle-method.test.ts > HandleMethod Value Object > static factories > concession() creates concession method 1ms - ✓ tests/unit/domain/quality/value-objects/handle-method.test.ts > HandleMethod Value Object > static factories > return() creates return method 1ms - ✓ tests/unit/domain/quality/value-objects/handle-method.test.ts > HandleMethod Value Object > from() > accepts valid string values 1ms - ✓ tests/unit/domain/quality/value-objects/handle-method.test.ts > HandleMethod Value Object > from() > throws DomainError on invalid value 1ms - ✓ tests/unit/domain/quality/value-objects/handle-method.test.ts > HandleMethod Value Object > fromDbCode() / toDbCode() > maps 1 -> rework 1ms - ✓ tests/unit/domain/quality/value-objects/handle-method.test.ts > HandleMethod Value Object > fromDbCode() / toDbCode() > maps 2 -> scrap 0ms - ✓ tests/unit/domain/quality/value-objects/handle-method.test.ts > HandleMethod Value Object > fromDbCode() / toDbCode() > maps 3 -> concession 1ms - ✓ tests/unit/domain/quality/value-objects/handle-method.test.ts > HandleMethod Value Object > fromDbCode() / toDbCode() > maps 4 -> return 0ms - ✓ tests/unit/domain/quality/value-objects/handle-method.test.ts > HandleMethod Value Object > fromDbCode() / toDbCode() > throws DomainError on invalid code 1ms - ✓ tests/unit/domain/quality/value-objects/handle-method.test.ts > HandleMethod Value Object > fromDbCode() / toDbCode() > toDbCode() returns correct inverse mapping 0ms - ✓ tests/unit/domain/quality/value-objects/handle-method.test.ts > HandleMethod Value Object > label() > returns Chinese labels 1ms - ✓ tests/unit/domain/quality/value-objects/handle-method.test.ts > HandleMethod Value Object > equals() > returns true for same value 0ms - ✓ tests/unit/domain/quality/value-objects/handle-method.test.ts > HandleMethod Value Object > equals() > returns false for different value 0ms - ✓ tests/unit/domain/warehouse/events/inbound-order-events.test.ts > 8.3 InboundOrderEvents 领域事件 > InboundOrderCreatedEvent 6ms - ✓ tests/unit/domain/warehouse/events/inbound-order-events.test.ts > 8.3 InboundOrderEvents 领域事件 > InboundOrderSubmittedEvent 1ms - ✓ tests/unit/domain/warehouse/events/inbound-order-events.test.ts > 8.3 InboundOrderEvents 领域事件 > InboundOrderApprovedEvent(含 items 数组) 3ms - ✓ tests/unit/domain/warehouse/events/inbound-order-events.test.ts > 8.3 InboundOrderEvents 领域事件 > InboundOrderUnapprovedEvent 2ms - ✓ tests/unit/domain/warehouse/events/inbound-order-events.test.ts > 8.3 InboundOrderEvents 领域事件 > InboundOrderCancelledEvent 1ms - ✓ tests/unit/domain/warehouse/events/inbound-order-events.test.ts > 8.3 InboundOrderEvents 领域事件 > OutboundOrderShippedEvent(含 items 数组) 1ms - ✓ tests/unit/domain/sales/entities/ReturnOrderLine.test.ts > ReturnOrderLine > create > should create a return order line with valid props 6ms - ✓ tests/unit/domain/sales/entities/ReturnOrderLine.test.ts > ReturnOrderLine > create > should throw error if quantity is zero 3ms - ✓ tests/unit/domain/sales/entities/ReturnOrderLine.test.ts > ReturnOrderLine > create > should throw error if quantity is negative 1ms - ✓ tests/unit/domain/sales/entities/ReturnOrderLine.test.ts > ReturnOrderLine > create > should throw error if delivered quantity is exceeded 1ms - ✓ tests/unit/domain/sales/entities/ReturnOrderLine.test.ts > ReturnOrderLine > create > should allow return quantity equal to delivered quantity 1ms - ✓ tests/unit/domain/sales/entities/ReturnOrderLine.test.ts > ReturnOrderLine > create > should allow return quantity less than delivered quantity 0ms - ✓ tests/unit/domain/sales/entities/ReturnOrderLine.test.ts > ReturnOrderLine > create > should skip validation when deliveredQty is not provided 1ms - ✓ tests/unit/domain/sales/entities/ReturnOrderLine.test.ts > ReturnOrderLine > create > should throw error if materialId is empty 1ms - ✓ tests/unit/domain/sales/entities/ReturnOrderLine.test.ts > ReturnOrderLine > create > should throw error if lineNo is empty 1ms - ✓ tests/unit/application/handlers/FinanceVoucherHandler.test.ts > FinanceVoucherHandler > inbound.approved 无重复应付单时创建新应付单(T301) 9ms - ✓ tests/unit/application/handlers/FinanceVoucherHandler.test.ts > FinanceVoucherHandler > inbound.approved 已存在相同 source_no 的应付单时跳过(T301 幂等) 1ms - ✓ tests/unit/application/handlers/FinanceVoucherHandler.test.ts > FinanceVoucherHandler > inbound.approved totalAmount 为 0 时跳过 1ms - ✓ tests/unit/application/handlers/FinanceVoucherHandler.test.ts > FinanceVoucherHandler > inbound.approved 无 inboundNo 时跳过 1ms - ✓ tests/unit/application/handlers/FinanceVoucherHandler.test.ts > FinanceVoucherHandler > inbound.approved totalAmount 为负数时跳过 1ms - ✓ tests/unit/application/handlers/FinanceVoucherHandler.test.ts > FinanceVoucherHandler > workorder.completed totalAmount 为 0 时跳过 1ms - ✓ tests/unit/application/handlers/FinanceVoucherHandler.test.ts > FinanceVoucherHandler > 多次处理同一 inbound 事件不会重复创建应付单(幂等验证) 2ms - ✓ tests/unit/production-scheduling-enhanced.test.ts > calculateScheduleWithColorDependencies > should schedule single color sequence successfully 11ms - ✓ tests/unit/production-scheduling-enhanced.test.ts > calculateScheduleWithColorDependencies > should handle multiple color sequences with dependency 3ms - × tests/unit/production-scheduling-enhanced.test.ts > calculateScheduleWithColorDependencies > should return conflict when no suitable equipment 18ms - → expected 'scheduled' to be 'conflict' // Object.is equality - ✓ tests/unit/production-scheduling-enhanced.test.ts > calculateScheduleWithColorDependencies > should schedule on any day including weekends 2ms - ✓ tests/unit/production-scheduling-enhanced.test.ts > calculateScheduleWithColorDependencies > should calculate duration based on quantity and capacity 1ms - ✓ tests/unit/production-scheduling-enhanced.test.ts > calculateScheduleWithColorDependencies > should set overall_start and overall_end correctly 1ms - ✓ tests/unit/production-scheduling-enhanced.test.ts > calculateScheduleWithColorDependencies > should avoid time conflicts with existing schedules 1ms - ✓ tests/unit/production-scheduling-enhanced.test.ts > calculateScheduleWithColorDependencies > should handle work order with no color sequences 1ms - ✓ tests/unit/production-scheduling-enhanced.test.ts > getAvailableEquipment regression > SQL query should NOT reference nonexistent workshop column 2ms - ✓ tests/unit/production-scheduling-enhanced.test.ts > getAvailableEquipment regression > should return equipment with numeric status from tinyint 1ms - ✓ tests/unit/production-scheduling-enhanced.test.ts > calculateScheduleWithColorDependencies status matching > should match equipment status when DB returns numeric tinyint 1ms - ✓ tests/unit/production-scheduling-enhanced.test.ts > calculateScheduleWithColorDependencies status matching > should NOT match equipment with status 3 (maintenance) 1ms - ✓ tests/unit/production-scheduling-enhanced.test.ts > autoScheduleWorkOrders status filter > should only query work orders with status pending or confirmed 1ms - × tests/unit/production-scheduling-enhanced.test.ts > saveScheduleResult regression > should create prd_schedule record before prd_schedule_detail 5ms - → expected false to be true // Object.is equality - × tests/unit/production-scheduling-enhanced.test.ts > durationHours formula fix (P0-P2) > Q=200, capacity=50/h → duration_hours=4 (not 8) 1ms - → equipmentList is not defined - × tests/unit/production-scheduling-enhanced.test.ts > cross-day time slot (P1-P5) > should correctly detect overlap when existing schedule crosses day boundary 1ms - → existingSchedules is not defined - × tests/unit/production-scheduling-enhanced.test.ts > cross-day time slot (P1-P5) > ScheduleSlot should have crosses_day flag 1ms - → existingSchedules is not defined - ✓ tests/unit/infrastructure/event-bus/event-bus-production.test.ts > EventBusFactory 生产环境强制 DB 模式 > 生产环境未配置 EVENT_BUS_TYPE 应抛出错误 8ms - ✓ tests/unit/infrastructure/event-bus/event-bus-production.test.ts > EventBusFactory 生产环境强制 DB 模式 > 生产环境配置 EVENT_BUS_TYPE=memory 应抛出错误 2ms - ✓ tests/unit/infrastructure/event-bus/event-bus-production.test.ts > EventBusFactory 生产环境强制 DB 模式 > 生产环境配置 EVENT_BUS_TYPE=db 应返回 db 1ms - ✓ tests/unit/infrastructure/event-bus/event-bus-production.test.ts > EventBusFactory 开发环境降级 > 开发环境未配置 EVENT_BUS_TYPE 应默认返回 memory 1ms - ✓ tests/unit/infrastructure/event-bus/event-bus-production.test.ts > EventBusFactory 开发环境降级 > 开发环境配置 EVENT_BUS_TYPE=db 应返回 db 1ms - ✓ src/__tests__/domain/money.value-object.test.ts > Money 值对象测试 > 应正确创建金额 4ms - ✓ src/__tests__/domain/money.value-object.test.ts > Money 值对象测试 > 零金额 1ms - ✓ src/__tests__/domain/money.value-object.test.ts > Money 值对象测试 > 金额相加 0ms - ✓ src/__tests__/domain/money.value-object.test.ts > Money 值对象测试 > 金额相减 1ms - ✓ src/__tests__/domain/money.value-object.test.ts > Money 值对象测试 > 金额相减结果为负数应抛出异常 3ms - ✓ src/__tests__/domain/money.value-object.test.ts > Money 值对象测试 > 金额乘法 0ms - ✓ src/__tests__/domain/money.value-object.test.ts > Money 值对象测试 > 负数金额应抛出异常 1ms - ✓ src/__tests__/domain/money.value-object.test.ts > Money 值对象测试 > 不同币种不能相加 1ms - ✓ src/__tests__/domain/money.value-object.test.ts > Money 值对象测试 > 不同币种不能相减 1ms - ✓ src/__tests__/domain/money.value-object.test.ts > Money 值对象测试 > 浮点精度处理 0ms - ✓ tests/unit/domain/warehouse/aggregates/inbound-order-gaps.test.ts > 入库聚合根 - 文档缺口补充 > UT-IN-004 采购入库必填校验 > sourceType=purchase_order 且缺 poId → 抛「采购入库必须关联采购订单」 6ms - ✓ tests/unit/domain/warehouse/aggregates/inbound-order-gaps.test.ts > 入库聚合根 - 文档缺口补充 > UT-IN-004 采购入库必填校验 > 有 poId 但缺 poNo → 抛「采购入库缺少采购订单号」 1ms - ✓ tests/unit/domain/warehouse/aggregates/inbound-order-gaps.test.ts > 入库聚合根 - 文档缺口补充 > UT-IN-004 采购入库必填校验 > 有 poId/poNo 但缺 supplierId → 抛「采购入库缺少供应商」 0ms - ✓ tests/unit/domain/warehouse/aggregates/inbound-order-gaps.test.ts > 入库聚合根 - 文档缺口补充 > UT-IN-004 采购入库必填校验 > 非采购来源不触发该组校验 1ms - ✓ tests/unit/domain/warehouse/aggregates/inbound-order-gaps.test.ts > 入库聚合根 - 文档缺口补充 > UT-IN-007 审核无明细抛错 > reconstitute 出空明细后 approve → 抛「入库单不能为空」 1ms - ✓ tests/unit/domain/warehouse/aggregates/inbound-order-gaps.test.ts > 入库聚合根 - 文档缺口补充 > UT-IN-006 审核事件含明细快照 > approved 事件 payload.items 含 materialId/quantity/unitPrice/batchNo 5ms - ✓ tests/unit/domain/warehouse/aggregates/inbound-order-gaps.test.ts > 入库聚合根 - 文档缺口补充 > UT-IN-010 多币种字段落地(领域层透传) > create 透传 currency / exchangeRate / baseTotalAmount 1ms - ✓ tests/unit/domain/warehouse/aggregates/inbound-order-gaps.test.ts > 入库聚合根 - 文档缺口补充 > UT-IN-010 多币种字段落地(领域层透传) > reconstitute 还原多币种字段 1ms - ✓ tests/unit/domain/warehouse/aggregates/inbound-order-gaps.test.ts > 入库聚合根 - 文档缺口补充 > UT-IN-010 多币种字段落地(领域层透传) > 默认币种为 CNY、汇率 1 1ms - ✓ src/lib/__tests__/role-permission-inherit.test.ts > 角色权限继承 > resolveEffectivePermissions > 无父角色:返回自身权限 7ms - ✓ src/lib/__tests__/role-permission-inherit.test.ts > 角色权限继承 > resolveEffectivePermissions > 合并模式:合并父角色和自身权限 1ms - ✓ src/lib/__tests__/role-permission-inherit.test.ts > 角色权限继承 > resolveEffectivePermissions > 合并模式:去重 1ms - ✓ src/lib/__tests__/role-permission-inherit.test.ts > 角色权限继承 > resolveEffectivePermissions > 覆盖模式:自身有权限则不继承父权限 1ms - ✓ src/lib/__tests__/role-permission-inherit.test.ts > 角色权限继承 > resolveEffectivePermissions > 覆盖模式:自身无权限则继承父权限 1ms - ✓ src/lib/__tests__/role-permission-inherit.test.ts > 角色权限继承 > resolveEffectivePermissions > 三级继承:管理员→部门经理→普通员工 1ms - ✓ src/lib/__tests__/role-permission-inherit.test.ts > 角色权限继承 > resolveEffectivePermissions > 循环继承检测:A→B→A(检测到循环后只返回已访问的权限) 1ms - ✓ src/lib/__tests__/role-permission-inherit.test.ts > 角色权限继承 > resolveEffectivePermissions > 父角色不存在:返回自身权限 0ms - ✓ tests/unit/domain/shared/domain-types.test.ts > 8.3 DomainTypes 错误层级 > DomainError 基类 > 默认 code 为 DOMAIN_ERROR 5ms - ✓ tests/unit/domain/shared/domain-types.test.ts > 8.3 DomainTypes 错误层级 > DomainError 基类 > 自定义 code 1ms - ✓ tests/unit/domain/shared/domain-types.test.ts > 8.3 DomainTypes 错误层级 > DomainError 基类 > 继承自 Error 1ms - ✓ tests/unit/domain/shared/domain-types.test.ts > 8.3 DomainTypes 错误层级 > NotFoundError > code = NOT_FOUND 1ms - ✓ tests/unit/domain/shared/domain-types.test.ts > 8.3 DomainTypes 错误层级 > NotFoundError > 继承自 DomainError 0ms - ✓ tests/unit/domain/shared/domain-types.test.ts > 8.3 DomainTypes 错误层级 > InvalidTransitionError > message 包含 from 和 to 1ms - ✓ tests/unit/domain/shared/domain-types.test.ts > 8.3 DomainTypes 错误层级 > VersionConflictError > 默认 message 1ms - ✓ tests/unit/application/handlers/DeliveryReceivableHandler.test.ts > DeliveryReceivableHandler > should create receivable when delivery is shipped 10ms - ✓ tests/unit/application/handlers/DeliveryReceivableHandler.test.ts > DeliveryReceivableHandler > should skip if totalAmount is 0 2ms - ✓ tests/unit/application/handlers/DeliveryReceivableHandler.test.ts > DeliveryReceivableHandler > should skip if totalAmount is negative 1ms - ✓ tests/unit/lib/cutting-optimizer.test.ts > 切料排样算法 (CALC-015 / 016) > 单个 30x30 件放入 100x100 母料 → 1 张、1 切、利用率 0.09 27ms - ✓ tests/unit/lib/cutting-optimizer.test.ts > 切料排样算法 (CALC-015 / 016) > 多个相同件全部放入 → 切数等于需求数 1ms - ✓ tests/unit/lib/cutting-optimizer.test.ts > 切料排样算法 (CALC-015 / 016) > 件大于母料 → 无法放置,进入 unplaced 1ms - ✓ tests/unit/lib/cutting-optimizer.test.ts > 切料排样算法 (CALC-015 / 016) > 整料拆分后:已放置面积 = Σ小料面积,余料 = 母料面积 - 已放置 1ms - ✓ src/__tests__/lib/rate-limit.test.ts > Rate Limit 速率限制测试 > 应允许在限制内的请求 6ms - ✓ src/__tests__/lib/rate-limit.test.ts > Rate Limit 速率限制测试 > 应在达到限制后拒绝请求 1ms - ✓ src/__tests__/lib/rate-limit.test.ts > Rate Limit 速率限制测试 > 不同标识符应独立计数 0ms - ✓ src/__tests__/lib/rate-limit.test.ts > Rate Limit 速率限制测试 > 被拒绝的请求应返回重试时间 1ms - ✓ src/__tests__/lib/rate-limit.test.ts > Rate Limit 速率限制测试 > 重置后应重新允许请求 1ms - ✓ tests/unit/domain/warehouse/aggregates/inbound-order.test.ts > 8.2 InboundOrder 聚合根 > create() 工厂方法 > 合法参数创建成功,状态为 draft 6ms - ✓ tests/unit/domain/warehouse/aggregates/inbound-order.test.ts > 8.2 InboundOrder 聚合根 > create() 工厂方法 > 有 id 时发布 InboundOrderCreatedEvent 3ms - ✓ tests/unit/domain/warehouse/aggregates/inbound-order.test.ts > 8.2 InboundOrder 聚合根 > create() 工厂方法 > 无 id 时不发布创建事件 1ms - ✓ tests/unit/domain/warehouse/aggregates/inbound-order.test.ts > 8.2 InboundOrder 聚合根 > create() 工厂方法 > 自动计算 totalAmount(多 item 累加) 1ms - ✓ tests/unit/domain/warehouse/aggregates/inbound-order.test.ts > 8.2 InboundOrder 聚合根 > create() 工厂方法 > 自动计算 totalQuantity(多 item 累加) 0ms - ✓ tests/unit/domain/warehouse/aggregates/inbound-order.test.ts > 8.2 InboundOrder 聚合根 > create() 工厂方法 > warehouseId 为 0 抛 DomainError 2ms - ✓ tests/unit/domain/warehouse/aggregates/inbound-order.test.ts > 8.2 InboundOrder 聚合根 > create() 工厂方法 > items 为空数组抛 DomainError 1ms - ✓ tests/unit/domain/warehouse/aggregates/inbound-order.test.ts > 8.2 InboundOrder 聚合根 > create() 工厂方法 > 默认值回退(orderType/inboundDate/remark 为空时使用默认) 1ms - ✓ tests/unit/domain/warehouse/aggregates/inbound-order.test.ts > 8.2 InboundOrder 聚合根 > reconstitute() 重建方法 > 从 DB 字段重建聚合,使用指定 totalAmount/totalQuantity 2ms - ✓ tests/unit/domain/warehouse/aggregates/inbound-order.test.ts > 8.2 InboundOrder 聚合根 > reconstitute() 重建方法 > 未指定 totalAmount 时自动计算 1ms - ✓ tests/unit/domain/warehouse/aggregates/inbound-order.test.ts > 8.2 InboundOrder 聚合根 > reconstitute() 重建方法 > 未指定 totalQuantity 时自动计算 1ms - ✓ tests/unit/domain/warehouse/aggregates/inbound-order.test.ts > 8.2 InboundOrder 聚合根 > reconstitute() 重建方法 > DB status approved 映射为 completed 0ms - ✓ tests/unit/domain/warehouse/aggregates/inbound-order.test.ts > 8.2 InboundOrder 聚合根 > submit() 提交流程 > draft → pending,发布 InboundOrderSubmittedEvent 1ms - ✓ tests/unit/domain/warehouse/aggregates/inbound-order.test.ts > 8.2 InboundOrder 聚合根 > submit() 提交流程 > 非 draft 状态提交抛错 0ms - ✓ tests/unit/domain/warehouse/aggregates/inbound-order.test.ts > 8.2 InboundOrder 聚合根 > approve() 审核流程 > pending → completed,设置 inspectionStatus=3 和 financePosted=true 1ms - ✓ tests/unit/domain/warehouse/aggregates/inbound-order.test.ts > 8.2 InboundOrder 聚合根 > approve() 审核流程 > 非 pending 状态审核抛错 0ms - ✓ tests/unit/domain/warehouse/aggregates/inbound-order.test.ts > 8.2 InboundOrder 聚合根 > cancel() 取消流程 > draft → cancelled,发布取消事件 1ms - ✓ tests/unit/domain/warehouse/aggregates/inbound-order.test.ts > 8.2 InboundOrder 聚合根 > cancel() 取消流程 > pending → cancelled 0ms - ✓ tests/unit/domain/warehouse/aggregates/inbound-order.test.ts > 8.2 InboundOrder 聚合根 > cancel() 取消流程 > completed → cancelled 抛错(已完成不可取消) 0ms - ✓ tests/unit/domain/warehouse/aggregates/inbound-order.test.ts > 8.2 InboundOrder 聚合根 > unapprove() 反审流程 > completed → pending,重置 inspectionStatus 和 financePosted 1ms - ✓ tests/unit/domain/warehouse/aggregates/inbound-order.test.ts > 8.2 InboundOrder 聚合根 > unapprove() 反审流程 > cancelled 状态反审抛错(终态不可流转) 1ms - ✓ tests/unit/domain/warehouse/aggregates/inbound-order.test.ts > 8.2 InboundOrder 聚合根 > 权限委托 > canEdit/canDelete 委托给 OrderStatus 1ms - ✓ tests/unit/domain/warehouse/aggregates/inbound-order.test.ts > 8.2 InboundOrder 聚合根 > 领域事件管理 > getDomainEvents 返回副本(不可变) 0ms - ✓ tests/unit/domain/warehouse/aggregates/inbound-order.test.ts > 8.2 InboundOrder 聚合根 > 领域事件管理 > clearDomainEvents 清空事件 1ms - ✓ tests/unit/domain/warehouse/aggregates/inbound-order.test.ts > 8.2 InboundOrder 聚合根 > items 访问器返回副本 > 外部修改 items 数组不影响内部状态 1ms - ✓ tests/unit/lib/category-validation.test.ts > 物料分类强制校验 (IT-IN-002) > 空物料列表 → 不阻断、不查库 7ms - ✓ tests/unit/lib/category-validation.test.ts > 物料分类强制校验 (IT-IN-002) > 存在未归类物料 + 仅提示配置 → blocked=false,提示文案 2ms - ✓ tests/unit/lib/category-validation.test.ts > 物料分类强制校验 (IT-IN-002) > 存在未归类物料 + 强制配置 → blocked=true,阻断文案 1ms - ✓ tests/unit/lib/category-validation.test.ts > 物料分类强制校验 (IT-IN-002) > 全部已归类 → 不阻断 1ms - ✓ tests/unit/lib/currency-snapshot.test.ts > 多币种金额换算 (UT-IN-010) > USD 金额按汇率 7.2 换算为本位币 CNY → 720 4ms - ✓ tests/unit/lib/currency-snapshot.test.ts > 多币种金额换算 (UT-IN-010) > 同币种换算不改变金额 1ms - ✓ tests/unit/lib/currency-snapshot.test.ts > 多币种金额换算 (UT-IN-010) > 小数精度:33.33 USD @ 7.2 → 239.98 0ms - ✓ tests/unit/lib/currency-snapshot.test.ts > 多币种金额换算 (UT-IN-010) > 非法币种代码 / 非正汇率抛错 2ms - ✓ tests/unit/infrastructure/cache/cache-manager-production.test.ts > CacheManager 生产环境强制 Redis > 生产环境未配置 REDIS_URL 应抛出错误 7ms - ✓ tests/unit/infrastructure/cache/cache-manager-production.test.ts > CacheManager 开发环境降级 > 开发环境未配置 REDIS_URL 应降级到内存 3ms - ✓ tests/unit/domain/purchase/entities/purchase-order-line-receive.test.ts > PurchaseOrderLine.receive 部分收货 / 超收守卫 > IT-IN-002 部分收货:多次入库累计 receivedQty 4ms - ✓ tests/unit/domain/purchase/entities/purchase-order-line-receive.test.ts > PurchaseOrderLine.receive 部分收货 / 超收守卫 > ERR-IN 超收拒绝:累计超过 orderQty*(1+tolerance) 抛 DomainError,状态不变 2ms - ✓ tests/unit/domain/purchase/entities/purchase-order-line-receive.test.ts > PurchaseOrderLine.receive 部分收货 / 超收守卫 > ERR-IN 容差边界:恰好等于上限不抛错 0ms - ✓ tests/unit/domain/purchase/entities/purchase-order-line-receive.test.ts > PurchaseOrderLine.receive 部分收货 / 超收守卫 > ERR-IN 入库数量必须大于0 1ms - ✓ tests/unit/domain/purchase/entities/purchase-order-line-receive.test.ts > PurchaseOrderLine.receive 部分收货 / 超收守卫 > IT-IN 已关闭行不允许再入库 0ms - ✓ tests/unit/domain/purchase/entities/purchase-order-line-receive.test.ts > PurchaseOrderLine.receive 部分收货 / 超收守卫 > reverseReceive 回补:减少 receivedQty,且不能超过已收量 1ms - ✓ tests/unit/domain/purchase/entities/purchase-order-line-receive.test.ts > PurchaseOrderLine.receive 部分收货 / 超收守卫 > isFullyReceived 正确反映收货完成度 0ms - ✓ tests/unit/production-scheduling.test.ts > calculatePriorityScore > urgent priority with material ready scores higher than normal priority without material 4ms - ✓ tests/unit/production-scheduling.test.ts > calculatePriorityScore > older work orders get higher age bonus 0ms - ✓ tests/unit/production-scheduling.test.ts > calculatePriorityScore > work order with customer_name gets bonus 0ms - ✓ tests/unit/production-scheduling.test.ts > calculatePriorityScore > minimum score is >= 0 1ms - ✓ tests/unit/production-scheduling.test.ts > calculatePriorityScore > maximum score is <= 100 1ms - × tests/unit/production-scheduling.test.ts > calculatePriorityScore > material ready adds score compared to not ready 8ms - → expected 30.43 to be greater than 30.43 - ✓ tests/unit/production-scheduling.test.ts > suggestScheduleDates > basic scheduling without conflicts 2ms - ✓ tests/unit/production-scheduling.test.ts > suggestScheduleDates > weekend skipping moves Saturday to Monday 1ms - ✓ tests/unit/production-scheduling.test.ts > suggestScheduleDates > weekend skipping moves Sunday to Monday 1ms - × tests/unit/production-scheduling.test.ts > suggestScheduleDates > conflict detection pushes start date forward 3ms - → expected '2025-06-02' not to be '2025-06-02' // Object.is equality - ✓ tests/unit/production-scheduling.test.ts > suggestScheduleDates > no conflicts when scheduled after existing 1ms - ✓ tests/unit/production-scheduling.test.ts > suggestScheduleDates > uses current date as default start date 0ms - ✓ tests/unit/production-scheduling.test.ts > suggestScheduleDates > end date is after start date for multi-day work 0ms - ✓ tests/unit/production-scheduling.test.ts > suggestScheduleDates > capacity-based formula: Q=100, capacity=50/h, 8h/day → 1 day 0ms - ✓ tests/unit/production-scheduling.test.ts > suggestScheduleDates > capacity-based formula: Q=1000, capacity=50/h, 8h/day → 3 days 0ms - ✓ tests/unit/production-scheduling.test.ts > suggestScheduleDates > capacity-based formula: Q=1 → 1 day minimum 0ms - -⎯⎯⎯⎯⎯⎯ Failed Tests 151 ⎯⎯⎯⎯⎯⎯ - - FAIL  src/hooks/useCompanyName.test.ts > useCompanyName Hook测试 > 应该返回默认公司名称 -Error: Failed to call `useTranslations` because the context from `NextIntlClientProvider` was not found. - -This can happen because: -1) You intended to render this component as a Server Component, the render - failed, and therefore React attempted to render the component on the client - instead. If this is the case, check the console for server errors. -2) You intended to render this component on the client side, but no context was found. - Learn more about this error here: https://next-intl.dev/docs/environments/server-client-components#missing-context - ❯ node_modules/.pnpm/next-intl@4.13.0_@swc+helpers@0.5.15_next@16.1.1_@babel+core@7.29.0_@playwright+test@1.59.1_r_bduexwmuzix7ovejuqbqnee2bm/node_modules/next-intl/dist/esm/development/react-client/index.js:20:13 - ❯ useCompanyName src/hooks/useCompanyName.ts:6:14 -  4| -  5| export function useCompanyName() { -  6| const tc = useTranslations('Common'); -  | ^ -  7| const [companyName, setCompanyName] = useState(tc('companyName')); -  8| const [loading, setLoading] = useState(true); - ❯ src/hooks/useCompanyName.test.ts:28:41 - ❯ TestComponent node_modules/.pnpm/@testing-library+react@16.3.2_@testing-library+dom@10.4.1_@types+react-dom@19.2.3_@types+reac_wek2qea7xkhq5fgdzagsmrzoju/node_modules/@testing-library/react/dist/pure.js:330:27 - ❯ Object.react_stack_bottom_frame node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:25904:20 - ❯ renderWithHooks node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:7662:22 - ❯ updateFunctionComponent node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:10166:19 - ❯ beginWork node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:11778:18 - ❯ runWithFiberInDEV node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:874:13 - ❯ performUnitOfWork node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:17641:22 - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[1/151]⎯ - - FAIL  src/hooks/useCompanyName.test.ts > useCompanyName Hook测试 > 应该从系统配置获取公司名称(config 优先) -Error: Failed to call `useTranslations` because the context from `NextIntlClientProvider` was not found. - -This can happen because: -1) You intended to render this component as a Server Component, the render - failed, and therefore React attempted to render the component on the client - instead. If this is the case, check the console for server errors. -2) You intended to render this component on the client side, but no context was found. - Learn more about this error here: https://next-intl.dev/docs/environments/server-client-components#missing-context - ❯ node_modules/.pnpm/next-intl@4.13.0_@swc+helpers@0.5.15_next@16.1.1_@babel+core@7.29.0_@playwright+test@1.59.1_r_bduexwmuzix7ovejuqbqnee2bm/node_modules/next-intl/dist/esm/development/react-client/index.js:20:13 - ❯ useCompanyName src/hooks/useCompanyName.ts:6:14 -  4| -  5| export function useCompanyName() { -  6| const tc = useTranslations('Common'); -  | ^ -  7| const [companyName, setCompanyName] = useState(tc('companyName')); -  8| const [loading, setLoading] = useState(true); - ❯ src/hooks/useCompanyName.test.ts:43:41 - ❯ TestComponent node_modules/.pnpm/@testing-library+react@16.3.2_@testing-library+dom@10.4.1_@types+react-dom@19.2.3_@types+reac_wek2qea7xkhq5fgdzagsmrzoju/node_modules/@testing-library/react/dist/pure.js:330:27 - ❯ Object.react_stack_bottom_frame node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:25904:20 - ❯ renderWithHooks node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:7662:22 - ❯ updateFunctionComponent node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:10166:19 - ❯ beginWork node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:11778:18 - ❯ runWithFiberInDEV node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:874:13 - ❯ performUnitOfWork node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:17641:22 - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[2/151]⎯ - - FAIL  src/hooks/useCompanyName.test.ts > useCompanyName Hook测试 > 应该使用配置短名称当全称不存在 -Error: Failed to call `useTranslations` because the context from `NextIntlClientProvider` was not found. - -This can happen because: -1) You intended to render this component as a Server Component, the render - failed, and therefore React attempted to render the component on the client - instead. If this is the case, check the console for server errors. -2) You intended to render this component on the client side, but no context was found. - Learn more about this error here: https://next-intl.dev/docs/environments/server-client-components#missing-context - ❯ node_modules/.pnpm/next-intl@4.13.0_@swc+helpers@0.5.15_next@16.1.1_@babel+core@7.29.0_@playwright+test@1.59.1_r_bduexwmuzix7ovejuqbqnee2bm/node_modules/next-intl/dist/esm/development/react-client/index.js:20:13 - ❯ useCompanyName src/hooks/useCompanyName.ts:6:14 -  4| -  5| export function useCompanyName() { -  6| const tc = useTranslations('Common'); -  | ^ -  7| const [companyName, setCompanyName] = useState(tc('companyName')); -  8| const [loading, setLoading] = useState(true); - ❯ src/hooks/useCompanyName.test.ts:66:41 - ❯ TestComponent node_modules/.pnpm/@testing-library+react@16.3.2_@testing-library+dom@10.4.1_@types+react-dom@19.2.3_@types+reac_wek2qea7xkhq5fgdzagsmrzoju/node_modules/@testing-library/react/dist/pure.js:330:27 - ❯ Object.react_stack_bottom_frame node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:25904:20 - ❯ renderWithHooks node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:7662:22 - ❯ updateFunctionComponent node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:10166:19 - ❯ beginWork node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:11778:18 - ❯ runWithFiberInDEV node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:874:13 - ❯ performUnitOfWork node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:17641:22 - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[3/151]⎯ - - FAIL  src/hooks/useCompanyName.test.ts > useCompanyName Hook测试 > config 无公司配置时应从组织API获取全称 -Error: Failed to call `useTranslations` because the context from `NextIntlClientProvider` was not found. - -This can happen because: -1) You intended to render this component as a Server Component, the render - failed, and therefore React attempted to render the component on the client - instead. If this is the case, check the console for server errors. -2) You intended to render this component on the client side, but no context was found. - Learn more about this error here: https://next-intl.dev/docs/environments/server-client-components#missing-context - ❯ node_modules/.pnpm/next-intl@4.13.0_@swc+helpers@0.5.15_next@16.1.1_@babel+core@7.29.0_@playwright+test@1.59.1_r_bduexwmuzix7ovejuqbqnee2bm/node_modules/next-intl/dist/esm/development/react-client/index.js:20:13 - ❯ useCompanyName src/hooks/useCompanyName.ts:6:14 -  4| -  5| export function useCompanyName() { -  6| const tc = useTranslations('Common'); -  | ^ -  7| const [companyName, setCompanyName] = useState(tc('companyName')); -  8| const [loading, setLoading] = useState(true); - ❯ src/hooks/useCompanyName.test.ts:85:41 - ❯ TestComponent node_modules/.pnpm/@testing-library+react@16.3.2_@testing-library+dom@10.4.1_@types+react-dom@19.2.3_@types+reac_wek2qea7xkhq5fgdzagsmrzoju/node_modules/@testing-library/react/dist/pure.js:330:27 - ❯ Object.react_stack_bottom_frame node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:25904:20 - ❯ renderWithHooks node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:7662:22 - ❯ updateFunctionComponent node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:10166:19 - ❯ beginWork node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:11778:18 - ❯ runWithFiberInDEV node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:874:13 - ❯ performUnitOfWork node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:17641:22 - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[4/151]⎯ - - FAIL  src/hooks/useCompanyName.test.ts > useCompanyName Hook测试 > 组织API无全称时应使用短名称 -Error: Failed to call `useTranslations` because the context from `NextIntlClientProvider` was not found. - -This can happen because: -1) You intended to render this component as a Server Component, the render - failed, and therefore React attempted to render the component on the client - instead. If this is the case, check the console for server errors. -2) You intended to render this component on the client side, but no context was found. - Learn more about this error here: https://next-intl.dev/docs/environments/server-client-components#missing-context - ❯ node_modules/.pnpm/next-intl@4.13.0_@swc+helpers@0.5.15_next@16.1.1_@babel+core@7.29.0_@playwright+test@1.59.1_r_bduexwmuzix7ovejuqbqnee2bm/node_modules/next-intl/dist/esm/development/react-client/index.js:20:13 - ❯ useCompanyName src/hooks/useCompanyName.ts:6:14 -  4| -  5| export function useCompanyName() { -  6| const tc = useTranslations('Common'); -  | ^ -  7| const [companyName, setCompanyName] = useState(tc('companyName')); -  8| const [loading, setLoading] = useState(true); - ❯ src/hooks/useCompanyName.test.ts:106:41 - ❯ TestComponent node_modules/.pnpm/@testing-library+react@16.3.2_@testing-library+dom@10.4.1_@types+react-dom@19.2.3_@types+reac_wek2qea7xkhq5fgdzagsmrzoju/node_modules/@testing-library/react/dist/pure.js:330:27 - ❯ Object.react_stack_bottom_frame node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:25904:20 - ❯ renderWithHooks node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:7662:22 - ❯ updateFunctionComponent node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:10166:19 - ❯ beginWork node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:11778:18 - ❯ runWithFiberInDEV node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:874:13 - ❯ performUnitOfWork node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:17641:22 - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[5/151]⎯ - - FAIL  src/hooks/useCompanyName.test.ts > useCompanyName Hook测试 > 所有API失败时应保持默认值 -Error: Failed to call `useTranslations` because the context from `NextIntlClientProvider` was not found. - -This can happen because: -1) You intended to render this component as a Server Component, the render - failed, and therefore React attempted to render the component on the client - instead. If this is the case, check the console for server errors. -2) You intended to render this component on the client side, but no context was found. - Learn more about this error here: https://next-intl.dev/docs/environments/server-client-components#missing-context - ❯ node_modules/.pnpm/next-intl@4.13.0_@swc+helpers@0.5.15_next@16.1.1_@babel+core@7.29.0_@playwright+test@1.59.1_r_bduexwmuzix7ovejuqbqnee2bm/node_modules/next-intl/dist/esm/development/react-client/index.js:20:13 - ❯ useCompanyName src/hooks/useCompanyName.ts:6:14 -  4| -  5| export function useCompanyName() { -  6| const tc = useTranslations('Common'); -  | ^ -  7| const [companyName, setCompanyName] = useState(tc('companyName')); -  8| const [loading, setLoading] = useState(true); - ❯ src/hooks/useCompanyName.test.ts:119:41 - ❯ TestComponent node_modules/.pnpm/@testing-library+react@16.3.2_@testing-library+dom@10.4.1_@types+react-dom@19.2.3_@types+reac_wek2qea7xkhq5fgdzagsmrzoju/node_modules/@testing-library/react/dist/pure.js:330:27 - ❯ Object.react_stack_bottom_frame node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:25904:20 - ❯ renderWithHooks node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:7662:22 - ❯ updateFunctionComponent node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:10166:19 - ❯ beginWork node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:11778:18 - ❯ runWithFiberInDEV node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:874:13 - ❯ performUnitOfWork node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:17641:22 - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[6/151]⎯ - - FAIL  src/hooks/useCompanyName.test.ts > useCompanyName Hook测试 > 401响应应被视为未授权并保持默认值 -Error: Failed to call `useTranslations` because the context from `NextIntlClientProvider` was not found. - -This can happen because: -1) You intended to render this component as a Server Component, the render - failed, and therefore React attempted to render the component on the client - instead. If this is the case, check the console for server errors. -2) You intended to render this component on the client side, but no context was found. - Learn more about this error here: https://next-intl.dev/docs/environments/server-client-components#missing-context - ❯ node_modules/.pnpm/next-intl@4.13.0_@swc+helpers@0.5.15_next@16.1.1_@babel+core@7.29.0_@playwright+test@1.59.1_r_bduexwmuzix7ovejuqbqnee2bm/node_modules/next-intl/dist/esm/development/react-client/index.js:20:13 - ❯ useCompanyName src/hooks/useCompanyName.ts:6:14 -  4| -  5| export function useCompanyName() { -  6| const tc = useTranslations('Common'); -  | ^ -  7| const [companyName, setCompanyName] = useState(tc('companyName')); -  8| const [loading, setLoading] = useState(true); - ❯ src/hooks/useCompanyName.test.ts:133:41 - ❯ TestComponent node_modules/.pnpm/@testing-library+react@16.3.2_@testing-library+dom@10.4.1_@types+react-dom@19.2.3_@types+reac_wek2qea7xkhq5fgdzagsmrzoju/node_modules/@testing-library/react/dist/pure.js:330:27 - ❯ Object.react_stack_bottom_frame node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:25904:20 - ❯ renderWithHooks node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:7662:22 - ❯ updateFunctionComponent node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:10166:19 - ❯ beginWork node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:11778:18 - ❯ runWithFiberInDEV node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:874:13 - ❯ performUnitOfWork node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:17641:22 - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[7/151]⎯ - - FAIL  src/hooks/useCompanyName.test.ts > useCompanyName Hook测试 > 空数据响应应保持默认值 -Error: Failed to call `useTranslations` because the context from `NextIntlClientProvider` was not found. - -This can happen because: -1) You intended to render this component as a Server Component, the render - failed, and therefore React attempted to render the component on the client - instead. If this is the case, check the console for server errors. -2) You intended to render this component on the client side, but no context was found. - Learn more about this error here: https://next-intl.dev/docs/environments/server-client-components#missing-context - ❯ node_modules/.pnpm/next-intl@4.13.0_@swc+helpers@0.5.15_next@16.1.1_@babel+core@7.29.0_@playwright+test@1.59.1_r_bduexwmuzix7ovejuqbqnee2bm/node_modules/next-intl/dist/esm/development/react-client/index.js:20:13 - ❯ useCompanyName src/hooks/useCompanyName.ts:6:14 -  4| -  5| export function useCompanyName() { -  6| const tc = useTranslations('Common'); -  | ^ -  7| const [companyName, setCompanyName] = useState(tc('companyName')); -  8| const [loading, setLoading] = useState(true); - ❯ src/hooks/useCompanyName.test.ts:150:41 - ❯ TestComponent node_modules/.pnpm/@testing-library+react@16.3.2_@testing-library+dom@10.4.1_@types+react-dom@19.2.3_@types+reac_wek2qea7xkhq5fgdzagsmrzoju/node_modules/@testing-library/react/dist/pure.js:330:27 - ❯ Object.react_stack_bottom_frame node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:25904:20 - ❯ renderWithHooks node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:7662:22 - ❯ updateFunctionComponent node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:10166:19 - ❯ beginWork node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:11778:18 - ❯ runWithFiberInDEV node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:874:13 - ❯ performUnitOfWork node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:17641:22 - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[8/151]⎯ - - FAIL  src/lib/production-scheduling.test.ts > calculatePriorityScore > material ready adds score compared to not ready -AssertionError: expected 30.43 to be greater than 30.43 - ❯ src/lib/production-scheduling.test.ts:79:24 -  77| const scoreReady = calculatePriorityScore(order, true); -  78| const scoreNotReady = calculatePriorityScore(order, false); -  79| expect(scoreReady).toBeGreaterThan(scoreNotReady); -  | ^ -  80| }); -  81| }); - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[9/151]⎯ - - FAIL  src/lib/production-scheduling.test.ts > suggestScheduleDates > conflict detection pushes start date forward -AssertionError: expected '2025-06-02' not to be '2025-06-02' // Object.is equality - ❯ src/lib/production-scheduling.test.ts:134:45 - 132| startDate: '2025-06-02', - 133| }); - 134| expect(result.suggested_start_date).not.toBe('2025-06-02'); -  | ^ - 135| expect(result.conflicts.length).toBeGreaterThan(0); - 136| }); - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[10/151]⎯ - - FAIL  tests/integration/cross-module-consistency.test.ts > 跨模块一致性集成测试 > Saga 状态流转 > 应正确处理 pending → executing → success 流程 -AssertionError: expected 1 to be +0 // Object.is equality - -- Expected -+ Received - -- 0 -+ 1 - - ❯ tests/integration/cross-module-consistency.test.ts:145:28 - 143| - 144| const sagas = await sagaLogRepository.findPendingSagas(); - 145| expect(sagas.length).toBe(0); -  | ^ - 146| }); - 147| }); - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[11/151]⎯ - - FAIL  tests/integration/qr-trace-flow.test.ts > QR Trace Full Chain Integration > full chain: create → find → split → find children → update quantity -Error: Unknown column 'parent_qr_code' in 'field list' - ❯ execute src/lib/db/index.ts:154:33 - 152| // mysql2 的 execute 类型签名比 query 更严格(不接受 undefined),实际运行时支持 - 153| // eslint-disable-next-line @typescript-eslint/no-explicit-any - 154| const [result] = await pool.execute(sql, values as any[]); -  | ^ - 155| return result as mysql.ResultSetHeader; - 156| } catch (error) { - ❯ MysqlDbExecutor.execute src/infrastructure/repositories/MysqlDbExecutor.ts:19:12 - ❯ MysqlQRCodeRepository.create src/infrastructure/repositories/MysqlQRCodeRepository.ts:89:34 - ❯ tests/integration/qr-trace-flow.test.ts:42:27 - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ -Serialized Error: { code: 'ER_BAD_FIELD_ERROR', errno: 1054, sqlState: '42S22', sqlMessage: 'Unknown column \'parent_qr_code\' in \'field list\'', sql: 'INSERT INTO qrcode_record (qr_code, qr_type, parent_qr_code, split_flag, split_index, batch_no, material_id, material_code, material_name, specification, quantity, unit, warehouse_id, warehouse_name, ref_id, ref_no, work_order_id, work_order_no, supplier_id, supplier_name, customer_id, customer_name, production_date, expiry_date, extra_data, remark, status, create_time, update_time)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NOW(), NOW())' } -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[12/151]⎯ - - FAIL  tests/integration/qr-trace-flow.test.ts > QR Trace Full Chain Integration > query trace timeline returns structured data -Error: Unknown column 'parent_qr_code' in 'field list' - ❯ execute src/lib/db/index.ts:154:33 - 152| // mysql2 的 execute 类型签名比 query 更严格(不接受 undefined),实际运行时支持 - 153| // eslint-disable-next-line @typescript-eslint/no-explicit-any - 154| const [result] = await pool.execute(sql, values as any[]); -  | ^ - 155| return result as mysql.ResultSetHeader; - 156| } catch (error) { - ❯ MysqlDbExecutor.execute src/infrastructure/repositories/MysqlDbExecutor.ts:19:12 - ❯ MysqlQRCodeRepository.create src/infrastructure/repositories/MysqlQRCodeRepository.ts:89:34 - ❯ tests/integration/qr-trace-flow.test.ts:80:27 - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ -Serialized Error: { code: 'ER_BAD_FIELD_ERROR', errno: 1054, sqlState: '42S22', sqlMessage: 'Unknown column \'parent_qr_code\' in \'field list\'', sql: 'INSERT INTO qrcode_record (qr_code, qr_type, parent_qr_code, split_flag, split_index, batch_no, material_id, material_code, material_name, specification, quantity, unit, warehouse_id, warehouse_name, ref_id, ref_no, work_order_id, work_order_no, supplier_id, supplier_name, customer_id, customer_name, production_date, expiry_date, extra_data, remark, status, create_time, update_time)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NOW(), NOW())' } -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[13/151]⎯ - - FAIL  tests/integration/test-data-validation.test.ts > 步骤 11: FIFO 追溯链 > 11a. 批次可用量扣减 > 批次 A 可用量 = 1200(初始量 1500,扣减后) - FAIL  tests/integration/test-data-validation.test.ts > 步骤 11: FIFO 追溯链 > 11a. 批次可用量扣减 > 批次 B 可用量 = 900(初始量 1000,扣减后) - FAIL  tests/integration/test-data-validation.test.ts > 步骤 11: FIFO 追溯链 > 11a. 批次可用量扣减 > 批次 C 可用量 = 300(初始量 500,扣减后) -AssertionError: expected [] to have a length of 1 but got +0 - -- Expected -+ Received - -- 1 -+ 0 - - ❯ tests/integration/test-data-validation.test.ts:60:20 -  58| [`BATCH-20260701-${_}`], -  59| ); -  60| expect(rows).toHaveLength(1); -  | ^ -  61| expect(Number(rows[0].available_qty)).toBe(expectedAvail); -  62| expect(Number(rows[0].quantity)).toBe(expectedQty); - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[14/151]⎯ - - FAIL  tests/integration/test-data-validation.test.ts > 步骤 11: FIFO 追溯链 > 11b. 领料明细批次号一致性 > 领料明细数量 = 3 -AssertionError: expected 'BATCH-20260701-A' to be null // Object.is equality - -- Expected: -null - -+ Received: -"BATCH-20260701-A" - - ❯ tests/integration/test-data-validation.test.ts:81:31 -  79| // 每条明细:领料批次号 = 入库批次号,且批次价格不为 null -  80| for (const item of rows) { -  81| expect(item.batch_no).toBe(item.inbound_batch_no); -  | ^ -  82| expect(item.batch_price).not.toBeNull(); -  83| } - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[15/151]⎯ - - FAIL  tests/integration/test-data-validation.test.ts > 步骤 11: FIFO 追溯链 > 11c. FIFO 模拟验证 > 最早入库批次仍有可用量(优先消耗) -AssertionError: expected 'TRF-TR202608259027-MAT006' to be 'BATCH-20260701-A' // Object.is equality - -Expected: "BATCH-20260701-A" -Received: "TRF-TR202608259027-MAT006" - - ❯ tests/integration/test-data-validation.test.ts:97:32 -  95| ); -  96| expect(rows.length).toBeGreaterThan(0); -  97| expect(rows[0].batch_no).toBe('BATCH-20260701-A'); -  | ^ -  98| }); -  99| }); - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[16/151]⎯ - - FAIL  tests/integration/test-data-validation.test.ts > 步骤 14: 多币种与汇率 > PO-2026-001 (CNY, rate=1.0) > base_* = 原币 * 1.0 -AssertionError: expected [] to have a length of 1 but got +0 - -- Expected -+ Received - -- 1 -+ 0 - - ❯ tests/integration/test-data-validation.test.ts:336:20 - 334| tax_amount, base_tax_amount, grand_total, base_grand_… - 335| FROM pur_purchase_order WHERE po_no = 'PO-2026-001'`); - 336| expect(rows).toHaveLength(1); -  | ^ - 337| const po = rows[0]; - 338| expect(po.currency).toBe('CNY'); - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[17/151]⎯ - - FAIL  tests/integration/test-data-validation.test.ts > 步骤 14: 多币种与汇率 > PO-2026-002 (USD, rate=7.2) > 币种 = USD,汇率 = 7.2 -AssertionError: expected [] to have a length of 1 but got +0 - -- Expected -+ Received - -- 1 -+ 0 - - ❯ tests/integration/test-data-validation.test.ts:351:20 - 349| `SELECT currency, exchange_rate FROM pur_purchase_order WHERE … - 350| ); - 351| expect(rows).toHaveLength(1); -  | ^ - 352| expect(rows[0].currency).toBe('USD'); - 353| expect(Number(rows[0].exchange_rate)).toBe(7.2); - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[18/151]⎯ - - FAIL  tests/integration/test-data-validation.test.ts > 步骤 14: 多币种与汇率 > PO-2026-002 (USD, rate=7.2) > base_total = 12000 * 7.2 = 86400 -TypeError: Cannot read properties of undefined (reading 'base_total_amount') - ❯ tests/integration/test-data-validation.test.ts:360:29 - 358| `SELECT base_total_amount FROM pur_purchase_order WHERE po_no … - 359| ); - 360| expect(Number(rows[0].base_total_amount)).toBe(86400); -  | ^ - 361| }); - 362| - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[19/151]⎯ - - FAIL  tests/integration/test-data-validation.test.ts > 步骤 14: 多币种与汇率 > PO-2026-002 (USD, rate=7.2) > base_tax = 1560 * 7.2 = 11232 -TypeError: Cannot read properties of undefined (reading 'base_tax_amount') - ❯ tests/integration/test-data-validation.test.ts:367:29 - 365| `SELECT base_tax_amount FROM pur_purchase_order WHERE po_no = … - 366| ); - 367| expect(Number(rows[0].base_tax_amount)).toBe(11232); -  | ^ - 368| }); - 369| - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[20/151]⎯ - - FAIL  tests/integration/test-data-validation.test.ts > 步骤 14: 多币种与汇率 > PO-2026-002 (USD, rate=7.2) > base_grand = 13560 * 7.2 = 97632 -TypeError: Cannot read properties of undefined (reading 'base_grand_total') - ❯ tests/integration/test-data-validation.test.ts:374:29 - 372| `SELECT base_grand_total FROM pur_purchase_order WHERE po_no =… - 373| ); - 374| expect(Number(rows[0].base_grand_total)).toBe(97632); -  | ^ - 375| }); - 376| - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[21/151]⎯ - - FAIL  tests/integration/test-data-validation.test.ts > 步骤 14: 多币种与汇率 > PO-2026-002 (USD, rate=7.2) > 明细 base_unit_price = 12 * 7.2 = 86.4,base_amount = 86400 -TypeError: Cannot read properties of undefined (reading 'id') - ❯ tests/integration/test-data-validation.test.ts:383:20 - 381| const lineRows = await query<{ base_unit_price: number; base_amo… - 382| `SELECT base_unit_price, base_amount FROM pur_purchase_order_l… - 383| [poRows[0].id], -  | ^ - 384| ); - 385| expect(Number(lineRows[0].base_unit_price)).toBe(86.4); - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[22/151]⎯ - - FAIL  tests/integration/test-data-validation.test.ts > 步骤 14: 多币种与汇率 > PO-2026-003 (CNY, rate=1.0) > base_total = total * rate -TypeError: Cannot read properties of undefined (reading 'currency') - ❯ tests/integration/test-data-validation.test.ts:396:22 - 394| }>(`SELECT currency, total_amount, base_total_amount, exchange_r… - 395| FROM pur_purchase_order WHERE po_no = 'PO-2026-003'`); - 396| expect(rows[0].currency).toBe('CNY'); -  | ^ - 397| expect(Number(rows[0].base_total_amount)).toBe(Number(rows[0].to… - 398| }); - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[23/151]⎯ - - FAIL  tests/integration/test-data-validation.test.ts > 步骤 14: 多币种与汇率 > 入库单 INB-2026-002 (USD) > 币种 = USD,汇率 = 7.2,base_total = total * rate -AssertionError: expected [] to have a length of 1 but got +0 - -- Expected -+ Received - -- 1 -+ 0 - - ❯ tests/integration/test-data-validation.test.ts:408:20 - 406| }>(`SELECT currency, exchange_rate, total_amount, base_total_amo… - 407| FROM inv_inbound_order WHERE order_no = 'INB-2026-002'`); - 408| expect(rows).toHaveLength(1); -  | ^ - 409| expect(rows[0].currency).toBe('USD'); - 410| expect(Number(rows[0].exchange_rate)).toBe(7.2); - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[24/151]⎯ - - FAIL  tests/integration/workorder-api.test.ts > 工单 API 集成测试 > DELETE /api/workorders - 删除工单 > 正常流程:pending 状态允许删除 -AssertionError: expected 500 to be 200 // Object.is equality - -- Expected -+ Received - -- 200 -+ 500 - - ❯ tests/integration/workorder-api.test.ts:426:22 - 424| const { status, data } = await parseResponse(await DELETE(req as… - 425| - 426| expect(status).toBe(200); -  | ^ - 427| expect(data.success).toBe(true); - 428| expect(mockConnection.execute).toHaveBeenCalledWith( - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[25/151]⎯ - - FAIL  tests/integration/workorder-api.test.ts > 工单 API 集成测试 > DELETE /api/workorders - 删除工单 > 业务异常:生产中的工单不能删除抛错(500) -AssertionError: expected '(intermediate value) is not iterable' to contain '生产中' - -Expected: "生产中" -Received: "(intermediate value) is not iterable" - - ❯ tests/integration/workorder-api.test.ts:461:28 - 459| - 460| expect(status).toBe(500); - 461| expect(data.message).toContain('生产中'); -  | ^ - 462| expect(data.message).toContain('请先取消'); - 463| }); - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[26/151]⎯ - - FAIL  tests/unit/inventory-sync.test.ts > inventory-sync > adjustInventory - 入库 > 库存记录不存在且为入库时创建新记录 -AssertionError: expected false to be true // Object.is equality - -- Expected -+ Received - -- true -+ false - - ❯ tests/unit/inventory-sync.test.ts:161:30 - 159| }); - 160| - 161| expect(result.success).toBe(true); -  | ^ - 162| expect(result.currentStock).toBe(50); - 163| // 验证 INSERT 调用 - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[27/151]⎯ - - FAIL  tests/unit/inventory-sync.test.ts > inventory-sync > adjustInventory - 出库 > 事务内更新后可用库存变负时抛错回滚 -AssertionError: expected '库存调整失败,请稍后重试' to contain '可用库存不足' - -Expected: "可用库存不足" -Received: "库存调整失败,请稍后重试" - - ❯ tests/unit/inventory-sync.test.ts:237:30 - 235| // 事务内 available_qty=20,扣减 30 后 newAvailableQty=-10 触发错误 - 236| expect(result.success).toBe(false); - 237| expect(result.message).toContain('可用库存不足'); -  | ^ - 238| }); - 239| - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[28/151]⎯ - - FAIL  tests/unit/inventory-sync.test.ts > inventory-sync > adjustInventory - 出库 > 库存记录不存在且为出库时抛错 -AssertionError: expected '库存调整失败,请稍后重试' to contain '库存记录不存在' - -Expected: "库存记录不存在" -Received: "库存调整失败,请稍后重试" - - ❯ tests/unit/inventory-sync.test.ts:256:30 - 254| - 255| expect(result.success).toBe(false); - 256| expect(result.message).toContain('库存记录不存在'); -  | ^ - 257| }); - 258| - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[29/151]⎯ - - FAIL  tests/unit/inventory-sync.test.ts > inventory-sync > lockInventory > 锁定量超过可用时抛错 -AssertionError: expected '库存锁定失败,请稍后重试' to contain '可用库存不足' - -Expected: "可用库存不足" -Received: "库存锁定失败,请稍后重试" - - ❯ tests/unit/inventory-sync.test.ts:352:30 - 350| - 351| expect(result.success).toBe(false); - 352| expect(result.message).toContain('可用库存不足'); -  | ^ - 353| }); - 354| - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[30/151]⎯ - - FAIL  tests/unit/inventory-sync.test.ts > inventory-sync > lockInventory > 库存记录不存在时抛错 -AssertionError: expected '库存锁定失败,请稍后重试' to contain '库存记录不存在' - -Expected: "库存记录不存在" -Received: "库存锁定失败,请稍后重试" - - ❯ tests/unit/inventory-sync.test.ts:364:30 - 362| - 363| expect(result.success).toBe(false); - 364| expect(result.message).toContain('库存记录不存在'); -  | ^ - 365| }); - 366| }); - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[31/151]⎯ - - FAIL  tests/unit/inventory-sync.test.ts > inventory-sync > unlockInventory > 库存记录不存在时抛错 -AssertionError: expected '库存解锁失败,请稍后重试' to contain '库存记录不存在' - -Expected: "库存记录不存在" -Received: "库存解锁失败,请稍后重试" - - ❯ tests/unit/inventory-sync.test.ts:411:30 - 409| - 410| expect(result.success).toBe(false); - 411| expect(result.message).toContain('库存记录不存在'); -  | ^ - 412| }); - 413| - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[32/151]⎯ - - FAIL  tests/unit/inventory-sync.test.ts > inventory-sync > 乐观锁并发冲突保护 > adjustInventory 检测到 version 不匹配时返回并发冲突失败 -AssertionError: expected '库存调整失败,请稍后重试' to contain '并发冲突' - -Expected: "并发冲突" -Received: "库存调整失败,请稍后重试" - - ❯ tests/unit/inventory-sync.test.ts:556:30 - 554| - 555| expect(result.success).toBe(false); - 556| expect(result.message).toContain('并发冲突'); -  | ^ - 557| // 验证 UPDATE 带 version 条件 - 558| expect(mockConn.execute).toHaveBeenCalledWith( - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[33/151]⎯ - - FAIL  tests/unit/inventory-sync.test.ts > inventory-sync > 乐观锁并发冲突保护 > lockInventory 检测到 version 不匹配时返回并发冲突失败 -AssertionError: expected '库存锁定失败,请稍后重试' to contain '并发冲突' - -Expected: "并发冲突" -Received: "库存锁定失败,请稍后重试" - - ❯ tests/unit/inventory-sync.test.ts:580:30 - 578| - 579| expect(result.success).toBe(false); - 580| expect(result.message).toContain('并发冲突'); -  | ^ - 581| }); - 582| - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[34/151]⎯ - - FAIL  tests/unit/inventory-sync.test.ts > inventory-sync > 乐观锁并发冲突保护 > unlockInventory 检测到 version 不匹配时返回并发冲突失败 -AssertionError: expected '库存解锁失败,请稍后重试' to contain '并发冲突' - -Expected: "并发冲突" -Received: "库存解锁失败,请稍后重试" - - ❯ tests/unit/inventory-sync.test.ts:593:30 - 591| - 592| expect(result.success).toBe(false); - 593| expect(result.message).toContain('并发冲突'); -  | ^ - 594| }); - 595| - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[35/151]⎯ - - FAIL  tests/unit/material-requisition.test.ts > 物料领用管理 - material-requisition > 分支覆盖 - 空可选参数与异常路径 > submitOverRequisition 异常路径触发 catch -AssertionError: expected true to be false // Object.is equality - -- Expected -+ Received - -- false -+ true - - ❯ tests/unit/material-requisition.test.ts:478:30 - 476| const result = await submitOverRequisition(1, 101, 50, '测试'); - 477| - 478| expect(result.success).toBe(false); -  | ^ - 479| expect(result.message).toContain('申请失败'); - 480| }); - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[36/151]⎯ - - FAIL  tests/unit/material-requisition.test.ts > 物料领用管理 - material-requisition > 分支覆盖 - 空可选参数与异常路径 > submitSupplementaryRequisition 不需要双重审批且不传申请人 -AssertionError: expected false to be true // Object.is equality - -- Expected -+ Received - -- true -+ false - - ❯ tests/unit/material-requisition.test.ts:491:30 - 489| const result = await submitSupplementaryRequisition(1, 101, 30, … - 490| - 491| expect(result.success).toBe(true); -  | ^ - 492| expect(result.message).toContain('已生成'); - 493| }); - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[37/151]⎯ - - FAIL  tests/unit/material-requisition.test.ts > 物料领用管理 - material-requisition > 分支覆盖 - 空可选参数与异常路径 > submitSupplementaryRequisition 异常路径触发 catch -AssertionError: expected true to be false // Object.is equality - -- Expected -+ Received - -- false -+ true - - ❯ tests/unit/material-requisition.test.ts:501:30 - 499| const result = await submitSupplementaryRequisition(1, 101, 30, … - 500| - 501| expect(result.success).toBe(false); -  | ^ - 502| expect(result.message).toContain('申请失败'); - 503| expect(secureLog).toHaveBeenCalledWith('error', expect.stringCon… - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[38/151]⎯ - - FAIL  tests/unit/material-requisition.test.ts > 物料领用管理 - material-requisition > 分支覆盖 - 空可选参数与异常路径 > submitOverRequisition 配置 mr_prefix 为空时使用默认 MR -AssertionError: expected false to be true // Object.is equality - -- Expected -+ Received - -- true -+ false - - ❯ tests/unit/material-requisition.test.ts:569:30 - 567| - 568| const result = await submitOverRequisition(1, 101, 50, '测试', 1, … - 569| expect(result.success).toBe(true); -  | ^ - 570| }); - 571| - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[39/151]⎯ - - FAIL  tests/unit/production-scheduling-enhanced.test.ts > calculateScheduleWithColorDependencies > should return conflict when no suitable equipment -AssertionError: expected 'scheduled' to be 'conflict' // Object.is equality - -Expected: "conflict" -Received: "scheduled" - - ❯ tests/unit/production-scheduling-enhanced.test.ts:171:46 - 169| - 170| expect(result.color_sequences).toHaveLength(1); - 171| expect(result.color_sequences[0].status).toBe('conflict'); -  | ^ - 172| expect(result.conflicts).toHaveLength(1); - 173| expect(result.conflicts[0].reason).toContain('无可用设备类型'); - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[40/151]⎯ - - FAIL  tests/unit/production-scheduling-enhanced.test.ts > saveScheduleResult regression > should create prd_schedule record before prd_schedule_detail -AssertionError: expected false to be true // Object.is equality - -- Expected -+ Received - -- true -+ false - - ❯ tests/unit/production-scheduling-enhanced.test.ts:436:31 - 434| (s) => s.startsWith('INSERT INTO prd_schedule ') - 435| ); - 436| expect(hasScheduleInsert).toBe(true); -  | ^ - 437| - 438| const detailInsertIdx = allCalls.findIndex((s) => - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[41/151]⎯ - - FAIL  tests/unit/production-scheduling-enhanced.test.ts > durationHours formula fix (P0-P2) > Q=200, capacity=50/h → duration_hours=4 (not 8) -ReferenceError: equipmentList is not defined - ❯ tests/unit/production-scheduling-enhanced.test.ts:464:7 - 462| const result = calculateScheduleWithColorDependencies( - 463| workOrder, - 464| equipmentList, -  | ^ - 465| existingSchedules, - 466| startDate - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[42/151]⎯ - - FAIL  tests/unit/production-scheduling-enhanced.test.ts > cross-day time slot (P1-P5) > should correctly detect overlap when existing schedule crosses day boundary -ReferenceError: existingSchedules is not defined - ❯ tests/unit/production-scheduling-enhanced.test.ts:481:5 - 479| - 480| // 模拟一个跨天排程:23:00 开始,次日 02:00 结束 - 481| existingSchedules.push({ -  | ^ - 482| equipment_id: 1, - 483| equipment_name: '印刷机1号', - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[43/151]⎯ - - FAIL  tests/unit/production-scheduling-enhanced.test.ts > cross-day time slot (P1-P5) > ScheduleSlot should have crosses_day flag -ReferenceError: existingSchedules is not defined - ❯ tests/unit/production-scheduling-enhanced.test.ts:512:12 - 510| - 511| it('ScheduleSlot should have crosses_day flag', () => { - 512| expect(existingSchedules.length).toBe(0); -  | ^ - 513| const equipment = createEquipment({ id: 1, capacity_per_hour: 100 … - 514| const workOrder = createWorkOrder({ plan_qty: 100 }); - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[44/151]⎯ - - FAIL  tests/unit/production-scheduling.test.ts > calculatePriorityScore > material ready adds score compared to not ready -AssertionError: expected 30.43 to be greater than 30.43 - ❯ tests/unit/production-scheduling.test.ts:79:24 -  77| const order = createWorkOrder({ priority: 'normal' }); -  78| const scoreReady = calculatePriorityScore(order, true); -  79| const scoreNotReady = calculatePriorityScore(order, false); -  | ^ -  80| expect(scoreReady).toBeGreaterThan(scoreNotReady); -  81| }); - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[45/151]⎯ - - FAIL  tests/unit/production-scheduling.test.ts > suggestScheduleDates > conflict detection pushes start date forward -AssertionError: expected '2025-06-02' not to be '2025-06-02' // Object.is equality - ❯ tests/unit/production-scheduling.test.ts:134:45 - 133| startDate: '2025-06-02', - 134| }); - 135| expect(result.suggested_start_date).not.toBe('2025-06-02'); -  | ^ - 136| expect(result.conflicts.length).toBeGreaterThan(0); - 137| }); - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[46/151]⎯ - - FAIL  src/components/ui/button.perf.test.tsx > Button组件渲染性能测试 > 应该在合理时间内渲染基础按钮 -Error: Invalid Chai property: toBeInTheDocument - ❯ src/components/ui/button.perf.test.tsx:15:46 -  13| const end = performance.now(); -  14| -  15| expect(container.querySelector('button')).toBeInTheDocument(); -  | ^ -  16| expect(end - start).toBeLessThan(250); // jsdom 环境下单按钮渲染 250ms 内完成 -  17| }); - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[47/151]⎯ - - FAIL  src/components/ui/button.perf.test.tsx > Button组件渲染性能测试 > 应该高效渲染带图标的按钮 -Error: Invalid Chai property: toBeInTheDocument - ❯ src/components/ui/button.perf.test.tsx:44:46 -  42| const end = performance.now(); -  43| -  44| expect(container.querySelector('button')).toBeInTheDocument(); -  | ^ -  45| expect(end - start).toBeLessThan(250); -  46| }); - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[48/151]⎯ - - FAIL  src/components/ui/button.test.tsx > Button 组件测试 > 应该正确渲染基础按钮 -Error: Invalid Chai property: toBeInTheDocument - ❯ src/components/ui/button.test.tsx:8:56 -  6| it('应该正确渲染基础按钮', () => { -  7| render(<Button>点击我</Button>); -  8| expect(screen.getByRole('button', { name: '点击我' })).toBeInTheDocum… -  | ^ -  9| }); -  10| - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[49/151]⎯ - - FAIL  src/components/ui/button.test.tsx > Button 组件测试 > 应该支持不同变体样式 -Error: Invalid Chai property: toHaveAttribute - ❯ src/components/ui/button.test.tsx:13:39 -  11| it('应该支持不同变体样式', () => { -  12| const { rerender } = render(<Button variant="default">默认</Button>); -  13| expect(screen.getByRole('button')).toHaveAttribute('data-variant',… -  | ^ -  14| -  15| rerender(<Button variant="destructive">危险</Button>); - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[50/151]⎯ - - FAIL  src/components/ui/button.test.tsx > Button 组件测试 > 应该支持不同尺寸 -Error: Invalid Chai property: toHaveAttribute - ❯ src/components/ui/button.test.tsx:30:39 -  28| it('应该支持不同尺寸', () => { -  29| const { rerender } = render(<Button size="default">默认</Button>); -  30| expect(screen.getByRole('button')).toHaveAttribute('data-size', 'd… -  | ^ -  31| -  32| rerender(<Button size="sm">小</Button>); - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[51/151]⎯ - - FAIL  src/components/ui/button.test.tsx > Button 组件测试 > 应该在禁用时不可点击 -Error: Invalid Chai property: toBeDisabled - ❯ src/components/ui/button.test.tsx:59:19 -  57| -  58| const button = screen.getByRole('button'); -  59| expect(button).toBeDisabled(); -  | ^ -  60| -  61| fireEvent.click(button); - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[52/151]⎯ - - FAIL  src/components/ui/button.test.tsx > Button 组件测试 > 应该显示加载状态 -Error: Invalid Chai property: toHaveAttribute - ❯ src/components/ui/button.test.tsx:69:19 -  67| -  68| const button = screen.getByRole('button'); -  69| expect(button).toHaveAttribute('data-loading', 'true'); -  | ^ -  70| expect(button).toBeDisabled(); -  71| expect(document.querySelector('[data-slot="button"]')).toHaveClass… - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[53/151]⎯ - - FAIL  src/components/ui/button.test.tsx > Button 组件测试 > 应该支持全宽模式 -Error: Invalid Chai property: toHaveClass - ❯ src/components/ui/button.test.tsx:76:39 -  74| it('应该支持全宽模式', () => { -  75| render(<Button fullWidth>全宽按钮</Button>); -  76| expect(screen.getByRole('button')).toHaveClass('w-full'); -  | ^ -  77| }); -  78| - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[54/151]⎯ - - FAIL  src/components/ui/button.test.tsx > Button 组件测试 > 应该支持自定义className -Error: Invalid Chai property: toHaveClass - ❯ src/components/ui/button.test.tsx:81:39 -  79| it('应该支持自定义className', () => { -  80| render(<Button className="custom-class">自定义</Button>); -  81| expect(screen.getByRole('button')).toHaveClass('custom-class'); -  | ^ -  82| }); -  83| - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[55/151]⎯ - - FAIL  src/components/ui/button.test.tsx > Button 组件测试 > 应该支持asChild属性 -Error: Invalid Chai property: toBeInTheDocument - ❯ src/components/ui/button.test.tsx:94:17 -  92| ); -  93| const link = container.querySelector('a[href="/test"]'); -  94| expect(link).toBeInTheDocument(); -  | ^ -  95| expect(link).toHaveTextContent('链接按钮'); -  96| }); - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[56/151]⎯ - - FAIL  src/components/ui/button.test.tsx > Button 组件测试 > 应该正确渲染子元素 -Error: Invalid Chai property: toBeInTheDocument - ❯ src/components/ui/button.test.tsx:105:35 - 103| </Button> - 104| ); - 105| expect(screen.getByText('图标')).toBeInTheDocument(); -  | ^ - 106| expect(screen.getByText('文本')).toBeInTheDocument(); - 107| }); - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[57/151]⎯ - - FAIL  src/components/ui/button.test.tsx > Button 组件测试 > 应该支持不同形状 -Error: Invalid Chai property: toHaveClass - ❯ src/components/ui/button.test.tsx:111:39 - 109| it('应该支持不同形状', () => { - 110| const { rerender } = render(<Button shape="default">默认</Button>); - 111| expect(screen.getByRole('button')).toHaveClass('rounded-md'); -  | ^ - 112| - 113| rerender(<Button shape="pill">胶囊</Button>); - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[58/151]⎯ - - FAIL  src/components/ui/button.test.tsx > Button 组件测试 > 应该在加载时显示加载图标 -Error: Invalid Chai property: toBeInTheDocument - ❯ src/components/ui/button.test.tsx:122:52 - 120| it('应该在加载时显示加载图标', () => { - 121| render(<Button loading>加载</Button>); - 122| expect(document.querySelector('.animate-spin')).toBeInTheDocument(… -  | ^ - 123| }); - 124| - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[59/151]⎯ - - FAIL  src/components/ui/button.test.tsx > Button 组件测试 > 应该支持type属性 -Error: Invalid Chai property: toHaveAttribute - ❯ src/components/ui/button.test.tsx:127:39 - 125| it('应该支持type属性', () => { - 126| render(<Button type="submit">提交</Button>); - 127| expect(screen.getByRole('button')).toHaveAttribute('type', 'submit… -  | ^ - 128| }); - 129| - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[60/151]⎯ - - FAIL  src/components/ui/button.test.tsx > Button 组件测试 > 应该支持新增变体 success -Error: Invalid Chai property: toHaveAttribute - ❯ src/components/ui/button.test.tsx:132:39 - 130| it('应该支持新增变体 success', () => { - 131| render(<Button variant="success">成功</Button>); - 132| expect(screen.getByRole('button')).toHaveAttribute('data-variant',… -  | ^ - 133| expect(screen.getByRole('button')).toHaveClass('bg-green-500'); - 134| }); - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[61/151]⎯ - - FAIL  src/components/ui/button.test.tsx > Button 组件测试 > 应该支持新增变体 warning -Error: Invalid Chai property: toHaveAttribute - ❯ src/components/ui/button.test.tsx:138:39 - 136| it('应该支持新增变体 warning', () => { - 137| render(<Button variant="warning">警告</Button>); - 138| expect(screen.getByRole('button')).toHaveAttribute('data-variant',… -  | ^ - 139| expect(screen.getByRole('button')).toHaveClass('bg-yellow-500'); - 140| }); - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[62/151]⎯ - - FAIL  src/components/ui/button.test.tsx > Button 组件测试 > 应该支持新增变体 info -Error: Invalid Chai property: toHaveAttribute - ❯ src/components/ui/button.test.tsx:144:39 - 142| it('应该支持新增变体 info', () => { - 143| render(<Button variant="info">信息</Button>); - 144| expect(screen.getByRole('button')).toHaveAttribute('data-variant',… -  | ^ - 145| expect(screen.getByRole('button')).toHaveClass('bg-blue-500'); - 146| }); - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[63/151]⎯ - - FAIL  src/components/ui/button.test.tsx > Button 组件测试 > 应该支持新增尺寸 xl -Error: Invalid Chai property: toHaveAttribute - ❯ src/components/ui/button.test.tsx:150:39 - 148| it('应该支持新增尺寸 xl', () => { - 149| render(<Button size="xl">超大</Button>); - 150| expect(screen.getByRole('button')).toHaveAttribute('data-size', 'x… -  | ^ - 151| expect(screen.getByRole('button')).toHaveClass('h-12'); - 152| }); - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[64/151]⎯ - - FAIL  src/components/ui/button.test.tsx > Button 组件测试 > 应该支持新增尺寸 xs -Error: Invalid Chai property: toHaveAttribute - ❯ src/components/ui/button.test.tsx:156:39 - 154| it('应该支持新增尺寸 xs', () => { - 155| render(<Button size="xs">超小</Button>); - 156| expect(screen.getByRole('button')).toHaveAttribute('data-size', 'x… -  | ^ - 157| expect(screen.getByRole('button')).toHaveClass('h-7'); - 158| }); - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[65/151]⎯ - - FAIL  src/components/ui/button.test.tsx > Button 组件测试 > 应该支持新增尺寸 icon-sm -Error: Invalid Chai property: toHaveAttribute - ❯ src/components/ui/button.test.tsx:162:39 - 160| it('应该支持新增尺寸 icon-sm', () => { - 161| render(<Button size="icon-sm">图标</Button>); - 162| expect(screen.getByRole('button')).toHaveAttribute('data-size', 'i… -  | ^ - 163| expect(screen.getByRole('button')).toHaveClass('size-8'); - 164| }); - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[66/151]⎯ - - FAIL  src/components/ui/button.test.tsx > Button 组件测试 > 应该支持新增尺寸 icon-lg -Error: Invalid Chai property: toHaveAttribute - ❯ src/components/ui/button.test.tsx:168:39 - 166| it('应该支持新增尺寸 icon-lg', () => { - 167| render(<Button size="icon-lg">图标</Button>); - 168| expect(screen.getByRole('button')).toHaveAttribute('data-size', 'i… -  | ^ - 169| expect(screen.getByRole('button')).toHaveClass('size-10'); - 170| }); - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[67/151]⎯ - - FAIL  src/components/ui/button.test.tsx > Button 组件测试 > 应该在loading时同时设置disabled -Error: Invalid Chai property: toBeDisabled - ❯ src/components/ui/button.test.tsx:179:19 - 177| ); - 178| const button = screen.getByRole('button'); - 179| expect(button).toBeDisabled(); -  | ^ - 180| }); - 181| - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[68/151]⎯ - - FAIL  src/components/ui/button.test.tsx > Button 组件测试 > 应该在loading时忽略disabled=false -Error: Invalid Chai property: toBeDisabled - ❯ src/components/ui/button.test.tsx:188:39 - 186| </Button> - 187| ); - 188| expect(screen.getByRole('button')).toBeDisabled(); -  | ^ - 189| }); - 190| - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[69/151]⎯ - - FAIL  src/components/ui/button.test.tsx > Button 组件测试 > 应该设置data-slot属性为button -Error: Invalid Chai property: toHaveAttribute - ❯ src/components/ui/button.test.tsx:193:39 - 191| it('应该设置data-slot属性为button', () => { - 192| render(<Button>按钮</Button>); - 193| expect(screen.getByRole('button')).toHaveAttribute('data-slot', 'b… -  | ^ - 194| }); - 195| - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[70/151]⎯ - - FAIL  src/components/ui/button.test.tsx > Button 组件测试 > 应该支持data-testid属性透传 -Error: Invalid Chai property: toBeInTheDocument - ❯ src/components/ui/button.test.tsx:198:44 - 196| it('应该支持data-testid属性透传', () => { - 197| render(<Button data-testid="my-button">按钮</Button>); - 198| expect(screen.getByTestId('my-button')).toBeInTheDocument(); -  | ^ - 199| }); - 200| - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[71/151]⎯ - - FAIL  src/components/ui/button.test.tsx > Button 组件测试 > 应该在非loading状态下不渲染Loader2 -Error: Invalid Chai property: toBeInTheDocument - ❯ src/components/ui/button.test.tsx:203:53 - 201| it('应该在非loading状态下不渲染Loader2', () => { - 202| render(<Button loading={false}>按钮</Button>); - 203| expect(document.querySelector('.animate-spin')).not.toBeInTheDocum… -  | ^ - 204| }); - 205| - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[72/151]⎯ - - FAIL  src/components/ui/button.test.tsx > Button 组件测试 > 应该支持shape与variant组合使用 -Error: Invalid Chai property: toHaveAttribute - ❯ src/components/ui/button.test.tsx:213:19 - 211| ); - 212| const button = screen.getByRole('button'); - 213| expect(button).toHaveAttribute('data-variant', 'destructive'); -  | ^ - 214| expect(button).toHaveClass('rounded-full'); - 215| }); - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[73/151]⎯ - - FAIL  src/components/ui/button.test.tsx > Button 组件测试 > 应该支持size与shape组合使用 -Error: Invalid Chai property: toHaveAttribute - ❯ src/components/ui/button.test.tsx:224:19 - 222| ); - 223| const button = screen.getByRole('button'); - 224| expect(button).toHaveAttribute('data-size', 'lg'); -  | ^ - 225| expect(button).toHaveClass('rounded-none'); - 226| }); - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[74/151]⎯ - - FAIL  src/components/ui/button.test.tsx > Button 组件测试 > 应该支持fullWidth与loading组合 -Error: Invalid Chai property: toHaveClass - ❯ src/components/ui/button.test.tsx:235:19 - 233| ); - 234| const button = screen.getByRole('button'); - 235| expect(button).toHaveClass('w-full'); -  | ^ - 236| expect(button).toBeDisabled(); - 237| expect(document.querySelector('.animate-spin')).toBeInTheDocument(… - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[75/151]⎯ - - FAIL  src/components/ui/button.test.tsx > Button 组件测试 > 应该支持所有props组合 -Error: Invalid Chai property: toHaveAttribute - ❯ src/components/ui/button.test.tsx:254:19 - 252| ); - 253| const button = screen.getByTestId('combo-btn'); - 254| expect(button).toHaveAttribute('data-variant', 'success'); -  | ^ - 255| expect(button).toHaveAttribute('data-size', 'xl'); - 256| expect(button).toHaveClass('rounded-full'); - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[76/151]⎯ - - FAIL  src/components/ui/input.test.tsx > Input 组件测试 > 应该正确渲染基础输入框 -Error: Invalid Chai property: toBeInTheDocument - ❯ src/components/ui/input.test.tsx:8:47 -  6| it('应该正确渲染基础输入框', () => { -  7| render(<Input placeholder="请输入" />); -  8| expect(screen.getByPlaceholderText('请输入')).toBeInTheDocument(); -  | ^ -  9| }); -  10| - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[77/151]⎯ - - FAIL  src/components/ui/input.test.tsx > Input 组件测试 > 应该支持不同类型 -Error: Invalid Chai property: toHaveAttribute - ❯ src/components/ui/input.test.tsx:13:40 -  11| it('应该支持不同类型', () => { -  12| const { rerender } = render(<Input type="text" />); -  13| expect(screen.getByRole('textbox')).toHaveAttribute('type', 'text'… -  | ^ -  14| -  15| rerender(<Input type="password" />); - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[78/151]⎯ - - FAIL  src/components/ui/input.test.tsx > Input 组件测试 > 应该支持禁用状态 -Error: Invalid Chai property: toBeDisabled - ❯ src/components/ui/input.test.tsx:37:40 -  35| it('应该支持禁用状态', () => { -  36| render(<Input disabled />); -  37| expect(screen.getByRole('textbox')).toBeDisabled(); -  | ^ -  38| }); -  39| - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[79/151]⎯ - - FAIL  src/components/ui/input.test.tsx > Input 组件测试 > 应该支持只读状态 -Error: Invalid Chai property: toHaveAttribute - ❯ src/components/ui/input.test.tsx:42:40 -  40| it('应该支持只读状态', () => { -  41| render(<Input readOnly value="只读内容" />); -  42| expect(screen.getByRole('textbox')).toHaveAttribute('readOnly'); -  | ^ -  43| }); -  44| - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[80/151]⎯ - - FAIL  src/components/ui/input.test.tsx > Input 组件测试 > 应该支持默认值 -Error: Invalid Chai property: toHaveValue - ❯ src/components/ui/input.test.tsx:47:40 -  45| it('应该支持默认值', () => { -  46| render(<Input defaultValue="默认值" />); -  47| expect(screen.getByRole('textbox')).toHaveValue('默认值'); -  | ^ -  48| }); -  49| - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[81/151]⎯ - - FAIL  src/components/ui/input.test.tsx > Input 组件测试 > 应该支持自定义className -Error: Invalid Chai property: toHaveClass - ❯ src/components/ui/input.test.tsx:52:40 -  50| it('应该支持自定义className', () => { -  51| render(<Input className="custom-input" />); -  52| expect(screen.getByRole('textbox')).toHaveClass('custom-input'); -  | ^ -  53| }); -  54| - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[82/151]⎯ - - FAIL  src/components/ui/input.test.tsx > Input 组件测试 > 应该支持必填属性 -Error: Invalid Chai property: toHaveAttribute - ❯ src/components/ui/input.test.tsx:57:40 -  55| it('应该支持必填属性', () => { -  56| render(<Input required />); -  57| expect(screen.getByRole('textbox')).toHaveAttribute('required'); -  | ^ -  58| }); -  59| - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[83/151]⎯ - - FAIL  src/components/ui/input.test.tsx > Input 组件测试 > 应该支持name属性 -Error: Invalid Chai property: toHaveAttribute - ❯ src/components/ui/input.test.tsx:62:40 -  60| it('应该支持name属性', () => { -  61| render(<Input name="username" />); -  62| expect(screen.getByRole('textbox')).toHaveAttribute('name', 'usern… -  | ^ -  63| }); -  64| - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[84/151]⎯ - - FAIL  src/components/ui/input.test.tsx > Input 组件测试 > 应该支持id属性 -Error: Invalid Chai property: toHaveAttribute - ❯ src/components/ui/input.test.tsx:67:40 -  65| it('应该支持id属性', () => { -  66| render(<Input id="user-input" />); -  67| expect(screen.getByRole('textbox')).toHaveAttribute('id', 'user-in… -  | ^ -  68| }); -  69| - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[85/151]⎯ - - FAIL  src/components/ui/input.test.tsx > Input 组件测试 > 应该正确应用样式类 -Error: Invalid Chai property: toHaveClass - ❯ src/components/ui/input.test.tsx:73:18 -  71| render(<Input />); -  72| const input = screen.getByRole('textbox'); -  73| expect(input).toHaveClass('w-full'); -  | ^ -  74| expect(input).toHaveClass('rounded-md'); -  75| expect(input).toHaveClass('border'); - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[86/151]⎯ - - FAIL  src/components/ui/table.perf.test.tsx > Table组件渲染性能测试 > 应该在合理时间内渲染基础表格 -Error: Invalid Chai property: toBeInTheDocument - ❯ src/components/ui/table.perf.test.tsx:55:45 -  53| const end = performance.now(); -  54| -  55| expect(container.querySelector('table')).toBeInTheDocument(); -  | ^ -  56| expect(container.querySelectorAll('tbody tr')).toHaveLength(10); -  57| expect(end - start).toBeLessThan(200); - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[87/151]⎯ - - FAIL  src/components/ui/table.perf.test.tsx > Table组件渲染性能测试 > 应该高效渲染复杂表格结构 -Error: Invalid Chai property: toBeInTheDocument - ❯ src/components/ui/table.perf.test.tsx:121:47 - 119| const end = performance.now(); - 120| - 121| expect(container.querySelector('caption')).toBeInTheDocument(); -  | ^ - 122| expect(container.querySelector('tfoot')).toBeInTheDocument(); - 123| expect(end - start).toBeLessThan(300); - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[88/151]⎯ - - FAIL  src/components/ui/table.test.tsx > Table 组件测试 > 应该正确渲染完整表格 -Error: Invalid Chai property: toBeInTheDocument - ❯ src/components/ui/table.test.tsx:34:37 -  32| ); -  33| -  34| expect(screen.getByText('用户列表')).toBeInTheDocument(); -  | ^ -  35| expect(screen.getByText('姓名')).toBeInTheDocument(); -  36| expect(screen.getByText('张三')).toBeInTheDocument(); - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[89/151]⎯ - - FAIL  src/components/ui/table.test.tsx > Table 组件测试 > 应该渲染表格容器 -Error: Invalid Chai property: toBeInTheDocument - ❯ src/components/ui/table.test.tsx:50:69 -  48| ); -  49| -  50| expect(container.querySelector('[data-slot="table-container"]')).t… -  | ^ -  51| expect(container.querySelector('[data-slot="table"]')).toBeInTheDo… -  52| }); - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[90/151]⎯ - - FAIL  src/components/ui/table.test.tsx > Table 组件测试 > 应该渲染表头 -Error: Invalid Chai property: toHaveTextContent - ❯ src/components/ui/table.test.tsx:68:23 -  66| const headers = screen.getAllByRole('columnheader'); -  67| expect(headers).toHaveLength(2); -  68| expect(headers[0]).toHaveTextContent('列1'); -  | ^ -  69| expect(headers[1]).toHaveTextContent('列2'); -  70| }); - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[91/151]⎯ - - FAIL  src/components/ui/table.test.tsx > Table 组件测试 > 应该渲染表格底部 -Error: Invalid Chai property: toBeInTheDocument - ❯ src/components/ui/table.test.tsx:102:35 - 100| ); - 101| - 102| expect(screen.getByText('总计')).toBeInTheDocument(); -  | ^ - 103| expect(screen.getByText('100')).toBeInTheDocument(); - 104| }); - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[92/151]⎯ - - FAIL  src/components/ui/table.test.tsx > Table 组件测试 > 应该支持自定义className -Error: Invalid Chai property: toHaveClass - ❯ src/components/ui/table.test.tsx:117:59 - 115| ); - 116| - 117| expect(container.querySelector('[data-slot="table"]')).toHaveClass… -  | ^ - 118| }); - 119| - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[93/151]⎯ - - FAIL  src/components/ui/table.test.tsx > Table 组件测试 > 应该支持行自定义className -Error: Invalid Chai property: toHaveClass - ❯ src/components/ui/table.test.tsx:131:36 - 129| ); - 130| - 131| expect(screen.getByRole('row')).toHaveClass('highlight-row'); -  | ^ - 132| }); - 133| - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[94/151]⎯ - - FAIL  src/components/ui/table.test.tsx > Table 组件测试 > 应该支持单元格自定义className -Error: Invalid Chai property: toHaveClass - ❯ src/components/ui/table.test.tsx:145:37 - 143| ); - 144| - 145| expect(screen.getByRole('cell')).toHaveClass('text-right'); -  | ^ - 146| }); - 147| - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[95/151]⎯ - - FAIL  src/components/ui/table.test.tsx > Table 组件测试 > 应该渲染空表格 -Error: Invalid Chai property: toBeInTheDocument - ❯ src/components/ui/table.test.tsx:155:64 - 153| ); - 154| - 155| expect(container.querySelector('[data-slot="table-body"]')).toBeIn… -  | ^ - 156| }); - 157| - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[96/151]⎯ - - FAIL  src/components/ui/table.test.tsx > Table 组件测试 > 应该支持表头自定义className -Error: Invalid Chai property: toHaveClass - ❯ src/components/ui/table.test.tsx:169:47 - 167| ); - 168| - 169| expect(screen.getAllByRole('rowgroup')[0]).toHaveClass('sticky-hea… -  | ^ - 170| }); - 171| }); - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[97/151]⎯ - - FAIL  src/tests/utils/auth.test.ts > Authentication Utils > authFetch > should add Authorization header when token exists -AssertionError: expected { 'Content-Type': 'application/json' } to have property "Authorization" with value 'Bearer test-token' - -- Expected: -"Bearer test-token" - -+ Received: -undefined - - ❯ src/tests/utils/auth.test.ts:32:30 -  30| const result = await authFetch('/api/test'); -  31| -  32| expect(result.headers).toHaveProperty('Authorization', 'Bearer t… -  | ^ -  33| }); -  34| - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[98/151]⎯ - - FAIL  tests/unit/hr/salary-engine.test.ts > salary-engine > should calculate full salary correctly -TypeError: Cannot read properties of undefined (reading 'then') - ❯ SalaryCalculationRepository.findByEmployeeMonth src/infrastructure/repositories/SalaryCalculationRepository.ts:13:9 -  11| eq(hrSalaryCalculation.employeeId, employeeId), -  12| eq(hrSalaryCalculation.calcMonth, month) -  13| )) -  | ^ -  14| .then(rows => rows[0]); -  15| return row || null; - ❯ SalaryCalculationRepository.save src/infrastructure/repositories/SalaryCalculationRepository.ts:19:33 - ❯ Module.calculateMonthlySalary src/lib/hr/salary-engine.ts:171:25 - ❯ tests/unit/hr/salary-engine.test.ts:97:20 - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[99/151]⎯ - - FAIL  tests/unit/hr/salary-engine.test.ts > salary-engine > should exclude piece salary when option is false -TypeError: Cannot read properties of undefined (reading 'then') - ❯ SalaryCalculationRepository.findByEmployeeMonth src/infrastructure/repositories/SalaryCalculationRepository.ts:13:9 -  11| eq(hrSalaryCalculation.employeeId, employeeId), -  12| eq(hrSalaryCalculation.calcMonth, month) -  13| )) -  | ^ -  14| .then(rows => rows[0]); -  15| return row || null; - ❯ SalaryCalculationRepository.save src/infrastructure/repositories/SalaryCalculationRepository.ts:19:33 - ❯ Module.calculateMonthlySalary src/lib/hr/salary-engine.ts:171:25 - ❯ tests/unit/hr/salary-engine.test.ts:115:20 - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[100/151]⎯ - - FAIL  tests/unit/hr/salary-engine.test.ts > salary-engine > should exclude overtime when option is false -TypeError: Cannot read properties of undefined (reading 'then') - ❯ SalaryCalculationRepository.findByEmployeeMonth src/infrastructure/repositories/SalaryCalculationRepository.ts:13:9 -  11| eq(hrSalaryCalculation.employeeId, employeeId), -  12| eq(hrSalaryCalculation.calcMonth, month) -  13| )) -  | ^ -  14| .then(rows => rows[0]); -  15| return row || null; - ❯ SalaryCalculationRepository.save src/infrastructure/repositories/SalaryCalculationRepository.ts:19:33 - ❯ Module.calculateMonthlySalary src/lib/hr/salary-engine.ts:171:25 - ❯ tests/unit/hr/salary-engine.test.ts:122:20 - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[101/151]⎯ - - FAIL  tests/unit/hr/salary-engine.test.ts > salary-engine > should exclude insurance when option is false -TypeError: Cannot read properties of undefined (reading 'then') - ❯ SalaryCalculationRepository.findByEmployeeMonth src/infrastructure/repositories/SalaryCalculationRepository.ts:13:9 -  11| eq(hrSalaryCalculation.employeeId, employeeId), -  12| eq(hrSalaryCalculation.calcMonth, month) -  13| )) -  | ^ -  14| .then(rows => rows[0]); -  15| return row || null; - ❯ SalaryCalculationRepository.save src/infrastructure/repositories/SalaryCalculationRepository.ts:19:33 - ❯ Module.calculateMonthlySalary src/lib/hr/salary-engine.ts:171:25 - ❯ tests/unit/hr/salary-engine.test.ts:129:20 - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[102/151]⎯ - - FAIL  tests/unit/hr/salary-engine.test.ts > salary-engine > should exclude tax when option is false -TypeError: Cannot read properties of undefined (reading 'then') - ❯ SalaryCalculationRepository.findByEmployeeMonth src/infrastructure/repositories/SalaryCalculationRepository.ts:13:9 -  11| eq(hrSalaryCalculation.employeeId, employeeId), -  12| eq(hrSalaryCalculation.calcMonth, month) -  13| )) -  | ^ -  14| .then(rows => rows[0]); -  15| return row || null; - ❯ SalaryCalculationRepository.save src/infrastructure/repositories/SalaryCalculationRepository.ts:19:33 - ❯ Module.calculateMonthlySalary src/lib/hr/salary-engine.ts:171:25 - ❯ tests/unit/hr/salary-engine.test.ts:137:20 - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[103/151]⎯ - - FAIL  tests/unit/hr/salary-engine.test.ts > salary-engine > should handle zero base salary -TypeError: Cannot read properties of undefined (reading 'then') - ❯ SalaryCalculationRepository.findByEmployeeMonth src/infrastructure/repositories/SalaryCalculationRepository.ts:13:9 -  11| eq(hrSalaryCalculation.employeeId, employeeId), -  12| eq(hrSalaryCalculation.calcMonth, month) -  13| )) -  | ^ -  14| .then(rows => rows[0]); -  15| return row || null; - ❯ SalaryCalculationRepository.save src/infrastructure/repositories/SalaryCalculationRepository.ts:19:33 - ❯ Module.calculateMonthlySalary src/lib/hr/salary-engine.ts:171:25 - ❯ tests/unit/hr/salary-engine.test.ts:143:20 - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[104/151]⎯ - - FAIL  tests/unit/hr/salary-engine.test.ts > salary-engine > should cap net pay at 0 when deductions exceed gross pay -TypeError: Cannot read properties of undefined (reading 'then') - ❯ SalaryCalculationRepository.findByEmployeeMonth src/infrastructure/repositories/SalaryCalculationRepository.ts:13:9 -  11| eq(hrSalaryCalculation.employeeId, employeeId), -  12| eq(hrSalaryCalculation.calcMonth, month) -  13| )) -  | ^ -  14| .then(rows => rows[0]); -  15| return row || null; - ❯ SalaryCalculationRepository.save src/infrastructure/repositories/SalaryCalculationRepository.ts:19:33 - ❯ Module.calculateMonthlySalary src/lib/hr/salary-engine.ts:171:25 - ❯ tests/unit/hr/salary-engine.test.ts:159:20 - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[105/151]⎯ - - FAIL  tests/unit/hr/salary-engine.test.ts > salary-engine > should batch calculate multiple employees -AssertionError: expected [] to have a length of 2 but got +0 - -- Expected -+ Received - -- 2 -+ 0 - - ❯ tests/unit/hr/salary-engine.test.ts:173:21 - 171| const results = await batchCalculateSalary([1, 2], '2026-07'); - 172| - 173| expect(results).toHaveLength(2); -  | ^ - 174| expect(results[0].employeeName).toBe('员工1'); - 175| expect(results[1].employeeName).toBe('员工2'); - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[106/151]⎯ - - FAIL  src/app/api/system/config/route.test.ts > 系统配置API测试 > GET - 查询配置 > 应该返回配置列表 -AssertionError: Target cannot be null or undefined. - ❯ src/app/api/system/config/route.test.ts:49:30 -  47| -  48| expect(data.success).toBe(true); -  49| expect(data.data.list).toHaveLength(2); -  | ^ -  50| expect(data.data.total).toBe(2); -  51| }); - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[107/151]⎯ - - FAIL  tests/unit/domain/warehouse/entities/inbound-item.test.ts > 8.2 InboundItem 实体 > create() 工厂 > materialId 为 0 抛错 -AssertionError: expected [Function] to throw an error - -- Expected: -null - -+ Received: -undefined - - ❯ tests/unit/domain/warehouse/entities/inbound-item.test.ts:34:70 -  32| -  33| it('materialId 为 0 抛错', () => { -  34| expect(() => InboundItem.create(makeProps({ materialId: 0 }))).t… -  | ^ -  35| }); -  36| - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[108/151]⎯ - - FAIL  src/app/[locale]/warehouse/inbound/__tests__/useCutting.test.ts > useCutting > 初始表单状态包含操作员信息 -Error: Failed to call `useTranslations` because the context from `NextIntlClientProvider` was not found. - -This can happen because: -1) You intended to render this component as a Server Component, the render - failed, and therefore React attempted to render the component on the client - instead. If this is the case, check the console for server errors. -2) You intended to render this component on the client side, but no context was found. - Learn more about this error here: https://next-intl.dev/docs/environments/server-client-components#missing-context - ❯ node_modules/.pnpm/next-intl@4.13.0_@swc+helpers@0.5.15_next@16.1.1_@babel+core@7.29.0_@playwright+test@1.59.1_r_bduexwmuzix7ovejuqbqnee2bm/node_modules/next-intl/dist/esm/development/react-client/index.js:20:13 - ❯ Module.useCutting src/app/[locale]/warehouse/inbound/hooks/useCutting.ts:22:13 -  20| -  21| export function useCutting(deps: UseCuttingDeps) { -  22| const t = useTranslations('Warehouse'); -  | ^ -  23| const { -  24| currentLabel, - ❯ src/app/[locale]/warehouse/inbound/__tests__/useCutting.test.ts:84:7 - ❯ TestComponent node_modules/.pnpm/@testing-library+react@16.3.2_@testing-library+dom@10.4.1_@types+react-dom@19.2.3_@types+reac_wek2qea7xkhq5fgdzagsmrzoju/node_modules/@testing-library/react/dist/pure.js:330:27 - ❯ Object.react_stack_bottom_frame node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:25904:20 - ❯ renderWithHooks node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:7662:22 - ❯ updateFunctionComponent node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:10166:19 - ❯ beginWork node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:11778:18 - ❯ runWithFiberInDEV node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:874:13 - ❯ performUnitOfWork node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:17641:22 - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[109/151]⎯ - - FAIL  src/app/[locale]/warehouse/inbound/__tests__/useCutting.test.ts > useCutting > currentLabel 为空时显示错误并返回 -Error: Failed to call `useTranslations` because the context from `NextIntlClientProvider` was not found. - -This can happen because: -1) You intended to render this component as a Server Component, the render - failed, and therefore React attempted to render the component on the client - instead. If this is the case, check the console for server errors. -2) You intended to render this component on the client side, but no context was found. - Learn more about this error here: https://next-intl.dev/docs/environments/server-client-components#missing-context - ❯ node_modules/.pnpm/next-intl@4.13.0_@swc+helpers@0.5.15_next@16.1.1_@babel+core@7.29.0_@playwright+test@1.59.1_r_bduexwmuzix7ovejuqbqnee2bm/node_modules/next-intl/dist/esm/development/react-client/index.js:20:13 - ❯ Module.useCutting src/app/[locale]/warehouse/inbound/hooks/useCutting.ts:22:13 -  20| -  21| export function useCutting(deps: UseCuttingDeps) { -  22| const t = useTranslations('Warehouse'); -  | ^ -  23| const { -  24| currentLabel, - ❯ src/app/[locale]/warehouse/inbound/__tests__/useCutting.test.ts:97:7 - ❯ TestComponent node_modules/.pnpm/@testing-library+react@16.3.2_@testing-library+dom@10.4.1_@types+react-dom@19.2.3_@types+reac_wek2qea7xkhq5fgdzagsmrzoju/node_modules/@testing-library/react/dist/pure.js:330:27 - ❯ Object.react_stack_bottom_frame node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:25904:20 - ❯ renderWithHooks node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:7662:22 - ❯ updateFunctionComponent node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:10166:19 - ❯ beginWork node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:11778:18 - ❯ runWithFiberInDEV node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:874:13 - ❯ performUnitOfWork node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:17641:22 - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[110/151]⎯ - - FAIL  src/app/[locale]/warehouse/inbound/__tests__/useCutting.test.ts > useCutting > 物料不可分切时显示错误并返回 -Error: Failed to call `useTranslations` because the context from `NextIntlClientProvider` was not found. - -This can happen because: -1) You intended to render this component as a Server Component, the render - failed, and therefore React attempted to render the component on the client - instead. If this is the case, check the console for server errors. -2) You intended to render this component on the client side, but no context was found. - Learn more about this error here: https://next-intl.dev/docs/environments/server-client-components#missing-context - ❯ node_modules/.pnpm/next-intl@4.13.0_@swc+helpers@0.5.15_next@16.1.1_@babel+core@7.29.0_@playwright+test@1.59.1_r_bduexwmuzix7ovejuqbqnee2bm/node_modules/next-intl/dist/esm/development/react-client/index.js:20:13 - ❯ Module.useCutting src/app/[locale]/warehouse/inbound/hooks/useCutting.ts:22:13 -  20| -  21| export function useCutting(deps: UseCuttingDeps) { -  22| const t = useTranslations('Warehouse'); -  | ^ -  23| const { -  24| currentLabel, - ❯ src/app/[locale]/warehouse/inbound/__tests__/useCutting.test.ts:108:7 - ❯ TestComponent node_modules/.pnpm/@testing-library+react@16.3.2_@testing-library+dom@10.4.1_@types+react-dom@19.2.3_@types+reac_wek2qea7xkhq5fgdzagsmrzoju/node_modules/@testing-library/react/dist/pure.js:330:27 - ❯ Object.react_stack_bottom_frame node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:25904:20 - ❯ renderWithHooks node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:7662:22 - ❯ updateFunctionComponent node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:10166:19 - ❯ beginWork node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:11778:18 - ❯ runWithFiberInDEV node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:874:13 - ❯ performUnitOfWork node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:17641:22 - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[111/151]⎯ - - FAIL  src/app/[locale]/warehouse/inbound/__tests__/useCutting.test.ts > useCutting > cutWidths 为空时显示错误并返回 -Error: Failed to call `useTranslations` because the context from `NextIntlClientProvider` was not found. - -This can happen because: -1) You intended to render this component as a Server Component, the render - failed, and therefore React attempted to render the component on the client - instead. If this is the case, check the console for server errors. -2) You intended to render this component on the client side, but no context was found. - Learn more about this error here: https://next-intl.dev/docs/environments/server-client-components#missing-context - ❯ node_modules/.pnpm/next-intl@4.13.0_@swc+helpers@0.5.15_next@16.1.1_@babel+core@7.29.0_@playwright+test@1.59.1_r_bduexwmuzix7ovejuqbqnee2bm/node_modules/next-intl/dist/esm/development/react-client/index.js:20:13 - ❯ Module.useCutting src/app/[locale]/warehouse/inbound/hooks/useCutting.ts:22:13 -  20| -  21| export function useCutting(deps: UseCuttingDeps) { -  22| const t = useTranslations('Warehouse'); -  | ^ -  23| const { -  24| currentLabel, - ❯ src/app/[locale]/warehouse/inbound/__tests__/useCutting.test.ts:121:41 - ❯ TestComponent node_modules/.pnpm/@testing-library+react@16.3.2_@testing-library+dom@10.4.1_@types+react-dom@19.2.3_@types+reac_wek2qea7xkhq5fgdzagsmrzoju/node_modules/@testing-library/react/dist/pure.js:330:27 - ❯ Object.react_stack_bottom_frame node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:25904:20 - ❯ renderWithHooks node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:7662:22 - ❯ updateFunctionComponent node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:10166:19 - ❯ beginWork node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:11778:18 - ❯ runWithFiberInDEV node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:874:13 - ❯ performUnitOfWork node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:17641:22 - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[112/151]⎯ - - FAIL  src/app/[locale]/warehouse/inbound/__tests__/useCutting.test.ts > useCutting > 分切总宽度超过规格宽度时显示错误并返回 -Error: Failed to call `useTranslations` because the context from `NextIntlClientProvider` was not found. - -This can happen because: -1) You intended to render this component as a Server Component, the render - failed, and therefore React attempted to render the component on the client - instead. If this is the case, check the console for server errors. -2) You intended to render this component on the client side, but no context was found. - Learn more about this error here: https://next-intl.dev/docs/environments/server-client-components#missing-context - ❯ node_modules/.pnpm/next-intl@4.13.0_@swc+helpers@0.5.15_next@16.1.1_@babel+core@7.29.0_@playwright+test@1.59.1_r_bduexwmuzix7ovejuqbqnee2bm/node_modules/next-intl/dist/esm/development/react-client/index.js:20:13 - ❯ Module.useCutting src/app/[locale]/warehouse/inbound/hooks/useCutting.ts:22:13 -  20| -  21| export function useCutting(deps: UseCuttingDeps) { -  22| const t = useTranslations('Warehouse'); -  | ^ -  23| const { -  24| currentLabel, - ❯ src/app/[locale]/warehouse/inbound/__tests__/useCutting.test.ts:131:7 - ❯ TestComponent node_modules/.pnpm/@testing-library+react@16.3.2_@testing-library+dom@10.4.1_@types+react-dom@19.2.3_@types+reac_wek2qea7xkhq5fgdzagsmrzoju/node_modules/@testing-library/react/dist/pure.js:330:27 - ❯ Object.react_stack_bottom_frame node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:25904:20 - ❯ renderWithHooks node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:7662:22 - ❯ updateFunctionComponent node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:10166:19 - ❯ beginWork node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:11778:18 - ❯ runWithFiberInDEV node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:874:13 - ❯ performUnitOfWork node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:17641:22 - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[113/151]⎯ - - FAIL  src/app/[locale]/warehouse/inbound/__tests__/useCutting.test.ts > useCutting > 成功分切后调用 API、刷新记录、映射标签 -Error: Failed to call `useTranslations` because the context from `NextIntlClientProvider` was not found. - -This can happen because: -1) You intended to render this component as a Server Component, the render - failed, and therefore React attempted to render the component on the client - instead. If this is the case, check the console for server errors. -2) You intended to render this component on the client side, but no context was found. - Learn more about this error here: https://next-intl.dev/docs/environments/server-client-components#missing-context - ❯ node_modules/.pnpm/next-intl@4.13.0_@swc+helpers@0.5.15_next@16.1.1_@babel+core@7.29.0_@playwright+test@1.59.1_r_bduexwmuzix7ovejuqbqnee2bm/node_modules/next-intl/dist/esm/development/react-client/index.js:20:13 - ❯ Module.useCutting src/app/[locale]/warehouse/inbound/hooks/useCutting.ts:22:13 -  20| -  21| export function useCutting(deps: UseCuttingDeps) { -  22| const t = useTranslations('Warehouse'); -  | ^ -  23| const { -  24| currentLabel, - ❯ src/app/[locale]/warehouse/inbound/__tests__/useCutting.test.ts:165:7 - ❯ TestComponent node_modules/.pnpm/@testing-library+react@16.3.2_@testing-library+dom@10.4.1_@types+react-dom@19.2.3_@types+reac_wek2qea7xkhq5fgdzagsmrzoju/node_modules/@testing-library/react/dist/pure.js:330:27 - ❯ Object.react_stack_bottom_frame node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:25904:20 - ❯ renderWithHooks node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:7662:22 - ❯ updateFunctionComponent node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:10166:19 - ❯ beginWork node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:11778:18 - ❯ runWithFiberInDEV node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:874:13 - ❯ performUnitOfWork node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:17641:22 - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[114/151]⎯ - - FAIL  src/app/[locale]/warehouse/inbound/__tests__/useCutting.test.ts > useCutting > API 返回失败时显示错误消息 -Error: Failed to call `useTranslations` because the context from `NextIntlClientProvider` was not found. - -This can happen because: -1) You intended to render this component as a Server Component, the render - failed, and therefore React attempted to render the component on the client - instead. If this is the case, check the console for server errors. -2) You intended to render this component on the client side, but no context was found. - Learn more about this error here: https://next-intl.dev/docs/environments/server-client-components#missing-context - ❯ node_modules/.pnpm/next-intl@4.13.0_@swc+helpers@0.5.15_next@16.1.1_@babel+core@7.29.0_@playwright+test@1.59.1_r_bduexwmuzix7ovejuqbqnee2bm/node_modules/next-intl/dist/esm/development/react-client/index.js:20:13 - ❯ Module.useCutting src/app/[locale]/warehouse/inbound/hooks/useCutting.ts:22:13 -  20| -  21| export function useCutting(deps: UseCuttingDeps) { -  22| const t = useTranslations('Warehouse'); -  | ^ -  23| const { -  24| currentLabel, - ❯ src/app/[locale]/warehouse/inbound/__tests__/useCutting.test.ts:200:7 - ❯ TestComponent node_modules/.pnpm/@testing-library+react@16.3.2_@testing-library+dom@10.4.1_@types+react-dom@19.2.3_@types+reac_wek2qea7xkhq5fgdzagsmrzoju/node_modules/@testing-library/react/dist/pure.js:330:27 - ❯ Object.react_stack_bottom_frame node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:25904:20 - ❯ renderWithHooks node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:7662:22 - ❯ updateFunctionComponent node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:10166:19 - ❯ beginWork node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:11778:18 - ❯ runWithFiberInDEV node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:874:13 - ❯ performUnitOfWork node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:17641:22 - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[115/151]⎯ - - FAIL  src/app/[locale]/warehouse/inbound/__tests__/useCutting.test.ts > useCutting > 网络异常时显示 cutFailed -Error: Failed to call `useTranslations` because the context from `NextIntlClientProvider` was not found. - -This can happen because: -1) You intended to render this component as a Server Component, the render - failed, and therefore React attempted to render the component on the client - instead. If this is the case, check the console for server errors. -2) You intended to render this component on the client side, but no context was found. - Learn more about this error here: https://next-intl.dev/docs/environments/server-client-components#missing-context - ❯ node_modules/.pnpm/next-intl@4.13.0_@swc+helpers@0.5.15_next@16.1.1_@babel+core@7.29.0_@playwright+test@1.59.1_r_bduexwmuzix7ovejuqbqnee2bm/node_modules/next-intl/dist/esm/development/react-client/index.js:20:13 - ❯ Module.useCutting src/app/[locale]/warehouse/inbound/hooks/useCutting.ts:22:13 -  20| -  21| export function useCutting(deps: UseCuttingDeps) { -  22| const t = useTranslations('Warehouse'); -  | ^ -  23| const { -  24| currentLabel, - ❯ src/app/[locale]/warehouse/inbound/__tests__/useCutting.test.ts:220:7 - ❯ TestComponent node_modules/.pnpm/@testing-library+react@16.3.2_@testing-library+dom@10.4.1_@types+react-dom@19.2.3_@types+reac_wek2qea7xkhq5fgdzagsmrzoju/node_modules/@testing-library/react/dist/pure.js:330:27 - ❯ Object.react_stack_bottom_frame node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:25904:20 - ❯ renderWithHooks node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:7662:22 - ❯ updateFunctionComponent node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:10166:19 - ❯ beginWork node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:11778:18 - ❯ runWithFiberInDEV node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:874:13 - ❯ performUnitOfWork node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:17641:22 - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[116/151]⎯ - - FAIL  src/app/[locale]/warehouse/inbound/__tests__/useCutting.test.ts > useCutting > 成功后清空 cutWidths 和 remark -Error: Failed to call `useTranslations` because the context from `NextIntlClientProvider` was not found. - -This can happen because: -1) You intended to render this component as a Server Component, the render - failed, and therefore React attempted to render the component on the client - instead. If this is the case, check the console for server errors. -2) You intended to render this component on the client side, but no context was found. - Learn more about this error here: https://next-intl.dev/docs/environments/server-client-components#missing-context - ❯ node_modules/.pnpm/next-intl@4.13.0_@swc+helpers@0.5.15_next@16.1.1_@babel+core@7.29.0_@playwright+test@1.59.1_r_bduexwmuzix7ovejuqbqnee2bm/node_modules/next-intl/dist/esm/development/react-client/index.js:20:13 - ❯ Module.useCutting src/app/[locale]/warehouse/inbound/hooks/useCutting.ts:22:13 -  20| -  21| export function useCutting(deps: UseCuttingDeps) { -  22| const t = useTranslations('Warehouse'); -  | ^ -  23| const { -  24| currentLabel, - ❯ src/app/[locale]/warehouse/inbound/__tests__/useCutting.test.ts:241:7 - ❯ TestComponent node_modules/.pnpm/@testing-library+react@16.3.2_@testing-library+dom@10.4.1_@types+react-dom@19.2.3_@types+reac_wek2qea7xkhq5fgdzagsmrzoju/node_modules/@testing-library/react/dist/pure.js:330:27 - ❯ Object.react_stack_bottom_frame node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:25904:20 - ❯ renderWithHooks node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:7662:22 - ❯ updateFunctionComponent node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:10166:19 - ❯ beginWork node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:11778:18 - ❯ runWithFiberInDEV node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:874:13 - ❯ performUnitOfWork node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:17641:22 - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[117/151]⎯ - - FAIL  src/app/[locale]/warehouse/inbound/__tests__/useCutting.test.ts > useCutting > API 成功但无新标签时不打开结果对话框 -Error: Failed to call `useTranslations` because the context from `NextIntlClientProvider` was not found. - -This can happen because: -1) You intended to render this component as a Server Component, the render - failed, and therefore React attempted to render the component on the client - instead. If this is the case, check the console for server errors. -2) You intended to render this component on the client side, but no context was found. - Learn more about this error here: https://next-intl.dev/docs/environments/server-client-components#missing-context - ❯ node_modules/.pnpm/next-intl@4.13.0_@swc+helpers@0.5.15_next@16.1.1_@babel+core@7.29.0_@playwright+test@1.59.1_r_bduexwmuzix7ovejuqbqnee2bm/node_modules/next-intl/dist/esm/development/react-client/index.js:20:13 - ❯ Module.useCutting src/app/[locale]/warehouse/inbound/hooks/useCutting.ts:22:13 -  20| -  21| export function useCutting(deps: UseCuttingDeps) { -  22| const t = useTranslations('Warehouse'); -  | ^ -  23| const { -  24| currentLabel, - ❯ src/app/[locale]/warehouse/inbound/__tests__/useCutting.test.ts:266:7 - ❯ TestComponent node_modules/.pnpm/@testing-library+react@16.3.2_@testing-library+dom@10.4.1_@types+react-dom@19.2.3_@types+reac_wek2qea7xkhq5fgdzagsmrzoju/node_modules/@testing-library/react/dist/pure.js:330:27 - ❯ Object.react_stack_bottom_frame node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:25904:20 - ❯ renderWithHooks node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:7662:22 - ❯ updateFunctionComponent node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:10166:19 - ❯ beginWork node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:11778:18 - ❯ runWithFiberInDEV node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:874:13 - ❯ performUnitOfWork node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:17641:22 - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[118/151]⎯ - - FAIL  src/app/[locale]/warehouse/inbound/__tests__/useCutting.test.ts > useCutting > PET、PC、PVC 物料均可分切 -Error: Failed to call `useTranslations` because the context from `NextIntlClientProvider` was not found. - -This can happen because: -1) You intended to render this component as a Server Component, the render - failed, and therefore React attempted to render the component on the client - instead. If this is the case, check the console for server errors. -2) You intended to render this component on the client side, but no context was found. - Learn more about this error here: https://next-intl.dev/docs/environments/server-client-components#missing-context - ❯ node_modules/.pnpm/next-intl@4.13.0_@swc+helpers@0.5.15_next@16.1.1_@babel+core@7.29.0_@playwright+test@1.59.1_r_bduexwmuzix7ovejuqbqnee2bm/node_modules/next-intl/dist/esm/development/react-client/index.js:20:13 - ❯ Module.useCutting src/app/[locale]/warehouse/inbound/hooks/useCutting.ts:22:13 -  20| -  21| export function useCutting(deps: UseCuttingDeps) { -  22| const t = useTranslations('Warehouse'); -  | ^ -  23| const { -  24| currentLabel, - ❯ src/app/[locale]/warehouse/inbound/__tests__/useCutting.test.ts:291:9 - ❯ TestComponent node_modules/.pnpm/@testing-library+react@16.3.2_@testing-library+dom@10.4.1_@types+react-dom@19.2.3_@types+reac_wek2qea7xkhq5fgdzagsmrzoju/node_modules/@testing-library/react/dist/pure.js:330:27 - ❯ Object.react_stack_bottom_frame node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:25904:20 - ❯ renderWithHooks node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:7662:22 - ❯ updateFunctionComponent node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:10166:19 - ❯ beginWork node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:11778:18 - ❯ runWithFiberInDEV node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:874:13 - ❯ performUnitOfWork node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:17641:22 - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[119/151]⎯ - - FAIL  src/app/[locale]/warehouse/inbound/__tests__/useCutting.test.ts > useCutting > user 为 null 时操作员默认为 id=1 / 系统管理员 -Error: Failed to call `useTranslations` because the context from `NextIntlClientProvider` was not found. - -This can happen because: -1) You intended to render this component as a Server Component, the render - failed, and therefore React attempted to render the component on the client - instead. If this is the case, check the console for server errors. -2) You intended to render this component on the client side, but no context was found. - Learn more about this error here: https://next-intl.dev/docs/environments/server-client-components#missing-context - ❯ node_modules/.pnpm/next-intl@4.13.0_@swc+helpers@0.5.15_next@16.1.1_@babel+core@7.29.0_@playwright+test@1.59.1_r_bduexwmuzix7ovejuqbqnee2bm/node_modules/next-intl/dist/esm/development/react-client/index.js:20:13 - ❯ Module.useCutting src/app/[locale]/warehouse/inbound/hooks/useCutting.ts:22:13 -  20| -  21| export function useCutting(deps: UseCuttingDeps) { -  22| const t = useTranslations('Warehouse'); -  | ^ -  23| const { -  24| currentLabel, - ❯ src/app/[locale]/warehouse/inbound/__tests__/useCutting.test.ts:317:7 - ❯ TestComponent node_modules/.pnpm/@testing-library+react@16.3.2_@testing-library+dom@10.4.1_@types+react-dom@19.2.3_@types+reac_wek2qea7xkhq5fgdzagsmrzoju/node_modules/@testing-library/react/dist/pure.js:330:27 - ❯ Object.react_stack_bottom_frame node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:25904:20 - ❯ renderWithHooks node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:7662:22 - ❯ updateFunctionComponent node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:10166:19 - ❯ beginWork node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:11778:18 - ❯ runWithFiberInDEV node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:874:13 - ❯ performUnitOfWork node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:17641:22 - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[120/151]⎯ - - FAIL  src/app/[locale]/warehouse/inbound/__tests__/useCutting.test.ts > useCutting > 规格不可解析时跳过总量校验直接请求 API -Error: Failed to call `useTranslations` because the context from `NextIntlClientProvider` was not found. - -This can happen because: -1) You intended to render this component as a Server Component, the render - failed, and therefore React attempted to render the component on the client - instead. If this is the case, check the console for server errors. -2) You intended to render this component on the client side, but no context was found. - Learn more about this error here: https://next-intl.dev/docs/environments/server-client-components#missing-context - ❯ node_modules/.pnpm/next-intl@4.13.0_@swc+helpers@0.5.15_next@16.1.1_@babel+core@7.29.0_@playwright+test@1.59.1_r_bduexwmuzix7ovejuqbqnee2bm/node_modules/next-intl/dist/esm/development/react-client/index.js:20:13 - ❯ Module.useCutting src/app/[locale]/warehouse/inbound/hooks/useCutting.ts:22:13 -  20| -  21| export function useCutting(deps: UseCuttingDeps) { -  22| const t = useTranslations('Warehouse'); -  | ^ -  23| const { -  24| currentLabel, - ❯ src/app/[locale]/warehouse/inbound/__tests__/useCutting.test.ts:332:7 - ❯ TestComponent node_modules/.pnpm/@testing-library+react@16.3.2_@testing-library+dom@10.4.1_@types+react-dom@19.2.3_@types+reac_wek2qea7xkhq5fgdzagsmrzoju/node_modules/@testing-library/react/dist/pure.js:330:27 - ❯ Object.react_stack_bottom_frame node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:25904:20 - ❯ renderWithHooks node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:7662:22 - ❯ updateFunctionComponent node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:10166:19 - ❯ beginWork node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:11778:18 - ❯ runWithFiberInDEV node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:874:13 - ❯ performUnitOfWork node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:17641:22 - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[121/151]⎯ - - FAIL  src/app/[locale]/warehouse/inbound/__tests__/useCutting.test.ts > useCutting > 单个宽度值(无 + 分隔符)也能正常分切 -Error: Failed to call `useTranslations` because the context from `NextIntlClientProvider` was not found. - -This can happen because: -1) You intended to render this component as a Server Component, the render - failed, and therefore React attempted to render the component on the client - instead. If this is the case, check the console for server errors. -2) You intended to render this component on the client side, but no context was found. - Learn more about this error here: https://next-intl.dev/docs/environments/server-client-components#missing-context - ❯ node_modules/.pnpm/next-intl@4.13.0_@swc+helpers@0.5.15_next@16.1.1_@babel+core@7.29.0_@playwright+test@1.59.1_r_bduexwmuzix7ovejuqbqnee2bm/node_modules/next-intl/dist/esm/development/react-client/index.js:20:13 - ❯ Module.useCutting src/app/[locale]/warehouse/inbound/hooks/useCutting.ts:22:13 -  20| -  21| export function useCutting(deps: UseCuttingDeps) { -  22| const t = useTranslations('Warehouse'); -  | ^ -  23| const { -  24| currentLabel, - ❯ src/app/[locale]/warehouse/inbound/__tests__/useCutting.test.ts:366:7 - ❯ TestComponent node_modules/.pnpm/@testing-library+react@16.3.2_@testing-library+dom@10.4.1_@types+react-dom@19.2.3_@types+reac_wek2qea7xkhq5fgdzagsmrzoju/node_modules/@testing-library/react/dist/pure.js:330:27 - ❯ Object.react_stack_bottom_frame node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:25904:20 - ❯ renderWithHooks node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:7662:22 - ❯ updateFunctionComponent node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:10166:19 - ❯ beginWork node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:11778:18 - ❯ runWithFiberInDEV node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:874:13 - ❯ performUnitOfWork node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:17641:22 - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[122/151]⎯ - - FAIL  src/app/[locale]/warehouse/inbound/__tests__/useCutting.test.ts > useCutting > 余料标签(isRemainder)的 materialName 包含余料前缀 -Error: Failed to call `useTranslations` because the context from `NextIntlClientProvider` was not found. - -This can happen because: -1) You intended to render this component as a Server Component, the render - failed, and therefore React attempted to render the component on the client - instead. If this is the case, check the console for server errors. -2) You intended to render this component on the client side, but no context was found. - Learn more about this error here: https://next-intl.dev/docs/environments/server-client-components#missing-context - ❯ node_modules/.pnpm/next-intl@4.13.0_@swc+helpers@0.5.15_next@16.1.1_@babel+core@7.29.0_@playwright+test@1.59.1_r_bduexwmuzix7ovejuqbqnee2bm/node_modules/next-intl/dist/esm/development/react-client/index.js:20:13 - ❯ Module.useCutting src/app/[locale]/warehouse/inbound/hooks/useCutting.ts:22:13 -  20| -  21| export function useCutting(deps: UseCuttingDeps) { -  22| const t = useTranslations('Warehouse'); -  | ^ -  23| const { -  24| currentLabel, - ❯ src/app/[locale]/warehouse/inbound/__tests__/useCutting.test.ts:401:7 - ❯ TestComponent node_modules/.pnpm/@testing-library+react@16.3.2_@testing-library+dom@10.4.1_@types+react-dom@19.2.3_@types+reac_wek2qea7xkhq5fgdzagsmrzoju/node_modules/@testing-library/react/dist/pure.js:330:27 - ❯ Object.react_stack_bottom_frame node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:25904:20 - ❯ renderWithHooks node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:7662:22 - ❯ updateFunctionComponent node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:10166:19 - ❯ beginWork node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:11778:18 - ❯ runWithFiberInDEV node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:874:13 - ❯ performUnitOfWork node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:17641:22 - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[123/151]⎯ - - FAIL  src/app/[locale]/warehouse/inbound/__tests__/useCutting.test.ts > useCutting > newLabels 缺少 labelNo 时回退生成 ${orderNo}-C${idx+1} -Error: Failed to call `useTranslations` because the context from `NextIntlClientProvider` was not found. - -This can happen because: -1) You intended to render this component as a Server Component, the render - failed, and therefore React attempted to render the component on the client - instead. If this is the case, check the console for server errors. -2) You intended to render this component on the client side, but no context was found. - Learn more about this error here: https://next-intl.dev/docs/environments/server-client-components#missing-context - ❯ node_modules/.pnpm/next-intl@4.13.0_@swc+helpers@0.5.15_next@16.1.1_@babel+core@7.29.0_@playwright+test@1.59.1_r_bduexwmuzix7ovejuqbqnee2bm/node_modules/next-intl/dist/esm/development/react-client/index.js:20:13 - ❯ Module.useCutting src/app/[locale]/warehouse/inbound/hooks/useCutting.ts:22:13 -  20| -  21| export function useCutting(deps: UseCuttingDeps) { -  22| const t = useTranslations('Warehouse'); -  | ^ -  23| const { -  24| currentLabel, - ❯ src/app/[locale]/warehouse/inbound/__tests__/useCutting.test.ts:443:7 - ❯ TestComponent node_modules/.pnpm/@testing-library+react@16.3.2_@testing-library+dom@10.4.1_@types+react-dom@19.2.3_@types+reac_wek2qea7xkhq5fgdzagsmrzoju/node_modules/@testing-library/react/dist/pure.js:330:27 - ❯ Object.react_stack_bottom_frame node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:25904:20 - ❯ renderWithHooks node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:7662:22 - ❯ updateFunctionComponent node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:10166:19 - ❯ beginWork node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:11778:18 - ❯ runWithFiberInDEV node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:874:13 - ❯ performUnitOfWork node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:17641:22 - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[124/151]⎯ - - FAIL  src/app/[locale]/warehouse/inbound/__tests__/useCutting.test.ts > useCutting > 宽度值为 NaN 时仍发送请求(hasInvalid 仅告警不阻断) -Error: Failed to call `useTranslations` because the context from `NextIntlClientProvider` was not found. - -This can happen because: -1) You intended to render this component as a Server Component, the render - failed, and therefore React attempted to render the component on the client - instead. If this is the case, check the console for server errors. -2) You intended to render this component on the client side, but no context was found. - Learn more about this error here: https://next-intl.dev/docs/environments/server-client-components#missing-context - ❯ node_modules/.pnpm/next-intl@4.13.0_@swc+helpers@0.5.15_next@16.1.1_@babel+core@7.29.0_@playwright+test@1.59.1_r_bduexwmuzix7ovejuqbqnee2bm/node_modules/next-intl/dist/esm/development/react-client/index.js:20:13 - ❯ Module.useCutting src/app/[locale]/warehouse/inbound/hooks/useCutting.ts:22:13 -  20| -  21| export function useCutting(deps: UseCuttingDeps) { -  22| const t = useTranslations('Warehouse'); -  | ^ -  23| const { -  24| currentLabel, - ❯ src/app/[locale]/warehouse/inbound/__tests__/useCutting.test.ts:472:7 - ❯ TestComponent node_modules/.pnpm/@testing-library+react@16.3.2_@testing-library+dom@10.4.1_@types+react-dom@19.2.3_@types+reac_wek2qea7xkhq5fgdzagsmrzoju/node_modules/@testing-library/react/dist/pure.js:330:27 - ❯ Object.react_stack_bottom_frame node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:25904:20 - ❯ renderWithHooks node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:7662:22 - ❯ updateFunctionComponent node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:10166:19 - ❯ beginWork node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:11778:18 - ❯ runWithFiberInDEV node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:874:13 - ❯ performUnitOfWork node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:17641:22 - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[125/151]⎯ - - FAIL  src/app/[locale]/warehouse/inbound/__tests__/useCutting.test.ts > useCutting > newLabels 包含 newSpec 字段时 specification 使用 newSpec -Error: Failed to call `useTranslations` because the context from `NextIntlClientProvider` was not found. - -This can happen because: -1) You intended to render this component as a Server Component, the render - failed, and therefore React attempted to render the component on the client - instead. If this is the case, check the console for server errors. -2) You intended to render this component on the client side, but no context was found. - Learn more about this error here: https://next-intl.dev/docs/environments/server-client-components#missing-context - ❯ node_modules/.pnpm/next-intl@4.13.0_@swc+helpers@0.5.15_next@16.1.1_@babel+core@7.29.0_@playwright+test@1.59.1_r_bduexwmuzix7ovejuqbqnee2bm/node_modules/next-intl/dist/esm/development/react-client/index.js:20:13 - ❯ Module.useCutting src/app/[locale]/warehouse/inbound/hooks/useCutting.ts:22:13 -  20| -  21| export function useCutting(deps: UseCuttingDeps) { -  22| const t = useTranslations('Warehouse'); -  | ^ -  23| const { -  24| currentLabel, - ❯ src/app/[locale]/warehouse/inbound/__tests__/useCutting.test.ts:504:7 - ❯ TestComponent node_modules/.pnpm/@testing-library+react@16.3.2_@testing-library+dom@10.4.1_@types+react-dom@19.2.3_@types+reac_wek2qea7xkhq5fgdzagsmrzoju/node_modules/@testing-library/react/dist/pure.js:330:27 - ❯ Object.react_stack_bottom_frame node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:25904:20 - ❯ renderWithHooks node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:7662:22 - ❯ updateFunctionComponent node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:10166:19 - ❯ beginWork node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:11778:18 - ❯ runWithFiberInDEV node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:874:13 - ❯ performUnitOfWork node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:17641:22 - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[126/151]⎯ - - FAIL  src/app/[locale]/warehouse/inbound/__tests__/useCutting.test.ts > useCutting > newLabels 缺少 id 时回退为 cut-${idx} -Error: Failed to call `useTranslations` because the context from `NextIntlClientProvider` was not found. - -This can happen because: -1) You intended to render this component as a Server Component, the render - failed, and therefore React attempted to render the component on the client - instead. If this is the case, check the console for server errors. -2) You intended to render this component on the client side, but no context was found. - Learn more about this error here: https://next-intl.dev/docs/environments/server-client-components#missing-context - ❯ node_modules/.pnpm/next-intl@4.13.0_@swc+helpers@0.5.15_next@16.1.1_@babel+core@7.29.0_@playwright+test@1.59.1_r_bduexwmuzix7ovejuqbqnee2bm/node_modules/next-intl/dist/esm/development/react-client/index.js:20:13 - ❯ Module.useCutting src/app/[locale]/warehouse/inbound/hooks/useCutting.ts:22:13 -  20| -  21| export function useCutting(deps: UseCuttingDeps) { -  22| const t = useTranslations('Warehouse'); -  | ^ -  23| const { -  24| currentLabel, - ❯ src/app/[locale]/warehouse/inbound/__tests__/useCutting.test.ts:535:7 - ❯ TestComponent node_modules/.pnpm/@testing-library+react@16.3.2_@testing-library+dom@10.4.1_@types+react-dom@19.2.3_@types+reac_wek2qea7xkhq5fgdzagsmrzoju/node_modules/@testing-library/react/dist/pure.js:330:27 - ❯ Object.react_stack_bottom_frame node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:25904:20 - ❯ renderWithHooks node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:7662:22 - ❯ updateFunctionComponent node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:10166:19 - ❯ beginWork node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:11778:18 - ❯ runWithFiberInDEV node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:874:13 - ❯ performUnitOfWork node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:17641:22 - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[127/151]⎯ - - FAIL  src/app/[locale]/warehouse/inbound/__tests__/useCutting.test.ts > useCutting > API 返回失败且无 message 时回退到 cutFailed -Error: Failed to call `useTranslations` because the context from `NextIntlClientProvider` was not found. - -This can happen because: -1) You intended to render this component as a Server Component, the render - failed, and therefore React attempted to render the component on the client - instead. If this is the case, check the console for server errors. -2) You intended to render this component on the client side, but no context was found. - Learn more about this error here: https://next-intl.dev/docs/environments/server-client-components#missing-context - ❯ node_modules/.pnpm/next-intl@4.13.0_@swc+helpers@0.5.15_next@16.1.1_@babel+core@7.29.0_@playwright+test@1.59.1_r_bduexwmuzix7ovejuqbqnee2bm/node_modules/next-intl/dist/esm/development/react-client/index.js:20:13 - ❯ Module.useCutting src/app/[locale]/warehouse/inbound/hooks/useCutting.ts:22:13 -  20| -  21| export function useCutting(deps: UseCuttingDeps) { -  22| const t = useTranslations('Warehouse'); -  | ^ -  23| const { -  24| currentLabel, - ❯ src/app/[locale]/warehouse/inbound/__tests__/useCutting.test.ts:558:7 - ❯ TestComponent node_modules/.pnpm/@testing-library+react@16.3.2_@testing-library+dom@10.4.1_@types+react-dom@19.2.3_@types+reac_wek2qea7xkhq5fgdzagsmrzoju/node_modules/@testing-library/react/dist/pure.js:330:27 - ❯ Object.react_stack_bottom_frame node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:25904:20 - ❯ renderWithHooks node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:7662:22 - ❯ updateFunctionComponent node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:10166:19 - ❯ beginWork node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:11778:18 - ❯ runWithFiberInDEV node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:874:13 - ❯ performUnitOfWork node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:17641:22 - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[128/151]⎯ - - FAIL  src/app/[locale]/warehouse/inbound/__tests__/useCutting.test.ts > useCutting > user 为 null 且表单为空时请求体 operatorId/operatorName 回退到默认值 -Error: Failed to call `useTranslations` because the context from `NextIntlClientProvider` was not found. - -This can happen because: -1) You intended to render this component as a Server Component, the render - failed, and therefore React attempted to render the component on the client - instead. If this is the case, check the console for server errors. -2) You intended to render this component on the client side, but no context was found. - Learn more about this error here: https://next-intl.dev/docs/environments/server-client-components#missing-context - ❯ node_modules/.pnpm/next-intl@4.13.0_@swc+helpers@0.5.15_next@16.1.1_@babel+core@7.29.0_@playwright+test@1.59.1_r_bduexwmuzix7ovejuqbqnee2bm/node_modules/next-intl/dist/esm/development/react-client/index.js:20:13 - ❯ Module.useCutting src/app/[locale]/warehouse/inbound/hooks/useCutting.ts:22:13 -  20| -  21| export function useCutting(deps: UseCuttingDeps) { -  22| const t = useTranslations('Warehouse'); -  | ^ -  23| const { -  24| currentLabel, - ❯ src/app/[locale]/warehouse/inbound/__tests__/useCutting.test.ts:579:7 - ❯ TestComponent node_modules/.pnpm/@testing-library+react@16.3.2_@testing-library+dom@10.4.1_@types+react-dom@19.2.3_@types+reac_wek2qea7xkhq5fgdzagsmrzoju/node_modules/@testing-library/react/dist/pure.js:330:27 - ❯ Object.react_stack_bottom_frame node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:25904:20 - ❯ renderWithHooks node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:7662:22 - ❯ updateFunctionComponent node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:10166:19 - ❯ beginWork node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:11778:18 - ❯ runWithFiberInDEV node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:874:13 - ❯ performUnitOfWork node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:17641:22 - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[129/151]⎯ - - FAIL  src/app/[locale]/warehouse/inbound/__tests__/useInboundData.test.ts > useInboundData > 初始状态为空 -Error: Failed to call `useTranslations` because the context from `NextIntlClientProvider` was not found. - -This can happen because: -1) You intended to render this component as a Server Component, the render - failed, and therefore React attempted to render the component on the client - instead. If this is the case, check the console for server errors. -2) You intended to render this component on the client side, but no context was found. - Learn more about this error here: https://next-intl.dev/docs/environments/server-client-components#missing-context - ❯ node_modules/.pnpm/next-intl@4.13.0_@swc+helpers@0.5.15_next@16.1.1_@babel+core@7.29.0_@playwright+test@1.59.1_r_bduexwmuzix7ovejuqbqnee2bm/node_modules/next-intl/dist/esm/development/react-client/index.js:20:13 - ❯ Module.useInboundData src/app/[locale]/warehouse/inbound/hooks/useInboundData.ts:17:13 -  15| -  16| export function useInboundData() { -  17| const t = useTranslations('Warehouse'); -  | ^ -  18| -  19| const [searchQuery, setSearchQuery] = useState(''); - ❯ src/app/[locale]/warehouse/inbound/__tests__/useInboundData.test.ts:63:41 - ❯ TestComponent node_modules/.pnpm/@testing-library+react@16.3.2_@testing-library+dom@10.4.1_@types+react-dom@19.2.3_@types+reac_wek2qea7xkhq5fgdzagsmrzoju/node_modules/@testing-library/react/dist/pure.js:330:27 - ❯ Object.react_stack_bottom_frame node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:25904:20 - ❯ renderWithHooks node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:7662:22 - ❯ updateFunctionComponent node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:10166:19 - ❯ beginWork node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:11778:18 - ❯ runWithFiberInDEV node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:874:13 - ❯ performUnitOfWork node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:17641:22 - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[130/151]⎯ - - FAIL  src/app/[locale]/warehouse/inbound/__tests__/useInboundData.test.ts > useInboundData > 挂载后并行拉取入库单、仓库、分类、供应商、标签 -Error: Failed to call `useTranslations` because the context from `NextIntlClientProvider` was not found. - -This can happen because: -1) You intended to render this component as a Server Component, the render - failed, and therefore React attempted to render the component on the client - instead. If this is the case, check the console for server errors. -2) You intended to render this component on the client side, but no context was found. - Learn more about this error here: https://next-intl.dev/docs/environments/server-client-components#missing-context - ❯ node_modules/.pnpm/next-intl@4.13.0_@swc+helpers@0.5.15_next@16.1.1_@babel+core@7.29.0_@playwright+test@1.59.1_r_bduexwmuzix7ovejuqbqnee2bm/node_modules/next-intl/dist/esm/development/react-client/index.js:20:13 - ❯ Module.useInboundData src/app/[locale]/warehouse/inbound/hooks/useInboundData.ts:17:13 -  15| -  16| export function useInboundData() { -  17| const t = useTranslations('Warehouse'); -  | ^ -  18| -  19| const [searchQuery, setSearchQuery] = useState(''); - ❯ src/app/[locale]/warehouse/inbound/__tests__/useInboundData.test.ts:90:41 - ❯ TestComponent node_modules/.pnpm/@testing-library+react@16.3.2_@testing-library+dom@10.4.1_@types+react-dom@19.2.3_@types+reac_wek2qea7xkhq5fgdzagsmrzoju/node_modules/@testing-library/react/dist/pure.js:330:27 - ❯ Object.react_stack_bottom_frame node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:25904:20 - ❯ renderWithHooks node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:7662:22 - ❯ updateFunctionComponent node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:10166:19 - ❯ beginWork node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:11778:18 - ❯ runWithFiberInDEV node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:874:13 - ❯ performUnitOfWork node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:17641:22 - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[131/151]⎯ - - FAIL  src/app/[locale]/warehouse/inbound/__tests__/useInboundData.test.ts > useInboundData > fetchInboundRecords 处理 data 为数组的响应 -Error: Failed to call `useTranslations` because the context from `NextIntlClientProvider` was not found. - -This can happen because: -1) You intended to render this component as a Server Component, the render - failed, and therefore React attempted to render the component on the client - instead. If this is the case, check the console for server errors. -2) You intended to render this component on the client side, but no context was found. - Learn more about this error here: https://next-intl.dev/docs/environments/server-client-components#missing-context - ❯ node_modules/.pnpm/next-intl@4.13.0_@swc+helpers@0.5.15_next@16.1.1_@babel+core@7.29.0_@playwright+test@1.59.1_r_bduexwmuzix7ovejuqbqnee2bm/node_modules/next-intl/dist/esm/development/react-client/index.js:20:13 - ❯ Module.useInboundData src/app/[locale]/warehouse/inbound/hooks/useInboundData.ts:17:13 -  15| -  16| export function useInboundData() { -  17| const t = useTranslations('Warehouse'); -  | ^ -  18| -  19| const [searchQuery, setSearchQuery] = useState(''); - ❯ src/app/[locale]/warehouse/inbound/__tests__/useInboundData.test.ts:103:41 - ❯ TestComponent node_modules/.pnpm/@testing-library+react@16.3.2_@testing-library+dom@10.4.1_@types+react-dom@19.2.3_@types+reac_wek2qea7xkhq5fgdzagsmrzoju/node_modules/@testing-library/react/dist/pure.js:330:27 - ❯ Object.react_stack_bottom_frame node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:25904:20 - ❯ renderWithHooks node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:7662:22 - ❯ updateFunctionComponent node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:10166:19 - ❯ beginWork node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:11778:18 - ❯ runWithFiberInDEV node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:874:13 - ❯ performUnitOfWork node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:17641:22 - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[132/151]⎯ - - FAIL  src/app/[locale]/warehouse/inbound/__tests__/useInboundData.test.ts > useInboundData > fetchInboundRecords 处理 API 返回失败 -Error: Failed to call `useTranslations` because the context from `NextIntlClientProvider` was not found. - -This can happen because: -1) You intended to render this component as a Server Component, the render - failed, and therefore React attempted to render the component on the client - instead. If this is the case, check the console for server errors. -2) You intended to render this component on the client side, but no context was found. - Learn more about this error here: https://next-intl.dev/docs/environments/server-client-components#missing-context - ❯ node_modules/.pnpm/next-intl@4.13.0_@swc+helpers@0.5.15_next@16.1.1_@babel+core@7.29.0_@playwright+test@1.59.1_r_bduexwmuzix7ovejuqbqnee2bm/node_modules/next-intl/dist/esm/development/react-client/index.js:20:13 - ❯ Module.useInboundData src/app/[locale]/warehouse/inbound/hooks/useInboundData.ts:17:13 -  15| -  16| export function useInboundData() { -  17| const t = useTranslations('Warehouse'); -  | ^ -  18| -  19| const [searchQuery, setSearchQuery] = useState(''); - ❯ src/app/[locale]/warehouse/inbound/__tests__/useInboundData.test.ts:111:41 - ❯ TestComponent node_modules/.pnpm/@testing-library+react@16.3.2_@testing-library+dom@10.4.1_@types+react-dom@19.2.3_@types+reac_wek2qea7xkhq5fgdzagsmrzoju/node_modules/@testing-library/react/dist/pure.js:330:27 - ❯ Object.react_stack_bottom_frame node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:25904:20 - ❯ renderWithHooks node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:7662:22 - ❯ updateFunctionComponent node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:10166:19 - ❯ beginWork node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:11778:18 - ❯ runWithFiberInDEV node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:874:13 - ❯ performUnitOfWork node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:17641:22 - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[133/151]⎯ - - FAIL  src/app/[locale]/warehouse/inbound/__tests__/useInboundData.test.ts > useInboundData > fetchInboundRecords 处理网络异常 -Error: Failed to call `useTranslations` because the context from `NextIntlClientProvider` was not found. - -This can happen because: -1) You intended to render this component as a Server Component, the render - failed, and therefore React attempted to render the component on the client - instead. If this is the case, check the console for server errors. -2) You intended to render this component on the client side, but no context was found. - Learn more about this error here: https://next-intl.dev/docs/environments/server-client-components#missing-context - ❯ node_modules/.pnpm/next-intl@4.13.0_@swc+helpers@0.5.15_next@16.1.1_@babel+core@7.29.0_@playwright+test@1.59.1_r_bduexwmuzix7ovejuqbqnee2bm/node_modules/next-intl/dist/esm/development/react-client/index.js:20:13 - ❯ Module.useInboundData src/app/[locale]/warehouse/inbound/hooks/useInboundData.ts:17:13 -  15| -  16| export function useInboundData() { -  17| const t = useTranslations('Warehouse'); -  | ^ -  18| -  19| const [searchQuery, setSearchQuery] = useState(''); - ❯ src/app/[locale]/warehouse/inbound/__tests__/useInboundData.test.ts:118:41 - ❯ TestComponent node_modules/.pnpm/@testing-library+react@16.3.2_@testing-library+dom@10.4.1_@types+react-dom@19.2.3_@types+reac_wek2qea7xkhq5fgdzagsmrzoju/node_modules/@testing-library/react/dist/pure.js:330:27 - ❯ Object.react_stack_bottom_frame node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:25904:20 - ❯ renderWithHooks node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:7662:22 - ❯ updateFunctionComponent node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:10166:19 - ❯ beginWork node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:11778:18 - ❯ runWithFiberInDEV node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:874:13 - ❯ performUnitOfWork node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:17641:22 - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[134/151]⎯ - - FAIL  src/app/[locale]/warehouse/inbound/__tests__/useInboundData.test.ts > useInboundData > handleRefresh 刷新数据并显示成功提示 -Error: Failed to call `useTranslations` because the context from `NextIntlClientProvider` was not found. - -This can happen because: -1) You intended to render this component as a Server Component, the render - failed, and therefore React attempted to render the component on the client - instead. If this is the case, check the console for server errors. -2) You intended to render this component on the client side, but no context was found. - Learn more about this error here: https://next-intl.dev/docs/environments/server-client-components#missing-context - ❯ node_modules/.pnpm/next-intl@4.13.0_@swc+helpers@0.5.15_next@16.1.1_@babel+core@7.29.0_@playwright+test@1.59.1_r_bduexwmuzix7ovejuqbqnee2bm/node_modules/next-intl/dist/esm/development/react-client/index.js:20:13 - ❯ Module.useInboundData src/app/[locale]/warehouse/inbound/hooks/useInboundData.ts:17:13 -  15| -  16| export function useInboundData() { -  17| const t = useTranslations('Warehouse'); -  | ^ -  18| -  19| const [searchQuery, setSearchQuery] = useState(''); - ❯ src/app/[locale]/warehouse/inbound/__tests__/useInboundData.test.ts:127:41 - ❯ TestComponent node_modules/.pnpm/@testing-library+react@16.3.2_@testing-library+dom@10.4.1_@types+react-dom@19.2.3_@types+reac_wek2qea7xkhq5fgdzagsmrzoju/node_modules/@testing-library/react/dist/pure.js:330:27 - ❯ Object.react_stack_bottom_frame node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:25904:20 - ❯ renderWithHooks node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:7662:22 - ❯ updateFunctionComponent node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:10166:19 - ❯ beginWork node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:11778:18 - ❯ runWithFiberInDEV node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:874:13 - ❯ performUnitOfWork node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:17641:22 - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[135/151]⎯ - - FAIL  src/app/[locale]/warehouse/inbound/__tests__/useInboundData.test.ts > useInboundData > setSearchQuery 更新搜索关键词 -Error: Failed to call `useTranslations` because the context from `NextIntlClientProvider` was not found. - -This can happen because: -1) You intended to render this component as a Server Component, the render - failed, and therefore React attempted to render the component on the client - instead. If this is the case, check the console for server errors. -2) You intended to render this component on the client side, but no context was found. - Learn more about this error here: https://next-intl.dev/docs/environments/server-client-components#missing-context - ❯ node_modules/.pnpm/next-intl@4.13.0_@swc+helpers@0.5.15_next@16.1.1_@babel+core@7.29.0_@playwright+test@1.59.1_r_bduexwmuzix7ovejuqbqnee2bm/node_modules/next-intl/dist/esm/development/react-client/index.js:20:13 - ❯ Module.useInboundData src/app/[locale]/warehouse/inbound/hooks/useInboundData.ts:17:13 -  15| -  16| export function useInboundData() { -  17| const t = useTranslations('Warehouse'); -  | ^ -  18| -  19| const [searchQuery, setSearchQuery] = useState(''); - ❯ src/app/[locale]/warehouse/inbound/__tests__/useInboundData.test.ts:147:41 - ❯ TestComponent node_modules/.pnpm/@testing-library+react@16.3.2_@testing-library+dom@10.4.1_@types+react-dom@19.2.3_@types+reac_wek2qea7xkhq5fgdzagsmrzoju/node_modules/@testing-library/react/dist/pure.js:330:27 - ❯ Object.react_stack_bottom_frame node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:25904:20 - ❯ renderWithHooks node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:7662:22 - ❯ updateFunctionComponent node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:10166:19 - ❯ beginWork node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:11778:18 - ❯ runWithFiberInDEV node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:874:13 - ❯ performUnitOfWork node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:17641:22 - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[136/151]⎯ - - FAIL  src/app/[locale]/warehouse/inbound/__tests__/useInboundData.test.ts > useInboundData > setSelectedRecords 更新选中记录 -Error: Failed to call `useTranslations` because the context from `NextIntlClientProvider` was not found. - -This can happen because: -1) You intended to render this component as a Server Component, the render - failed, and therefore React attempted to render the component on the client - instead. If this is the case, check the console for server errors. -2) You intended to render this component on the client side, but no context was found. - Learn more about this error here: https://next-intl.dev/docs/environments/server-client-components#missing-context - ❯ node_modules/.pnpm/next-intl@4.13.0_@swc+helpers@0.5.15_next@16.1.1_@babel+core@7.29.0_@playwright+test@1.59.1_r_bduexwmuzix7ovejuqbqnee2bm/node_modules/next-intl/dist/esm/development/react-client/index.js:20:13 - ❯ Module.useInboundData src/app/[locale]/warehouse/inbound/hooks/useInboundData.ts:17:13 -  15| -  16| export function useInboundData() { -  17| const t = useTranslations('Warehouse'); -  | ^ -  18| -  19| const [searchQuery, setSearchQuery] = useState(''); - ❯ src/app/[locale]/warehouse/inbound/__tests__/useInboundData.test.ts:154:41 - ❯ TestComponent node_modules/.pnpm/@testing-library+react@16.3.2_@testing-library+dom@10.4.1_@types+react-dom@19.2.3_@types+reac_wek2qea7xkhq5fgdzagsmrzoju/node_modules/@testing-library/react/dist/pure.js:330:27 - ❯ Object.react_stack_bottom_frame node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:25904:20 - ❯ renderWithHooks node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:7662:22 - ❯ updateFunctionComponent node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:10166:19 - ❯ beginWork node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:11778:18 - ❯ runWithFiberInDEV node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:874:13 - ❯ performUnitOfWork node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:17641:22 - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[137/151]⎯ - - FAIL  src/app/[locale]/warehouse/inbound/__tests__/useInboundData.test.ts > useInboundData > totalInboundToday 统计今日入库数量 -Error: Failed to call `useTranslations` because the context from `NextIntlClientProvider` was not found. - -This can happen because: -1) You intended to render this component as a Server Component, the render - failed, and therefore React attempted to render the component on the client - instead. If this is the case, check the console for server errors. -2) You intended to render this component on the client side, but no context was found. - Learn more about this error here: https://next-intl.dev/docs/environments/server-client-components#missing-context - ❯ node_modules/.pnpm/next-intl@4.13.0_@swc+helpers@0.5.15_next@16.1.1_@babel+core@7.29.0_@playwright+test@1.59.1_r_bduexwmuzix7ovejuqbqnee2bm/node_modules/next-intl/dist/esm/development/react-client/index.js:20:13 - ❯ Module.useInboundData src/app/[locale]/warehouse/inbound/hooks/useInboundData.ts:17:13 -  15| -  16| export function useInboundData() { -  17| const t = useTranslations('Warehouse'); -  | ^ -  18| -  19| const [searchQuery, setSearchQuery] = useState(''); - ❯ src/app/[locale]/warehouse/inbound/__tests__/useInboundData.test.ts:165:41 - ❯ TestComponent node_modules/.pnpm/@testing-library+react@16.3.2_@testing-library+dom@10.4.1_@types+react-dom@19.2.3_@types+reac_wek2qea7xkhq5fgdzagsmrzoju/node_modules/@testing-library/react/dist/pure.js:330:27 - ❯ Object.react_stack_bottom_frame node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:25904:20 - ❯ renderWithHooks node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:7662:22 - ❯ updateFunctionComponent node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:10166:19 - ❯ beginWork node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:11778:18 - ❯ runWithFiberInDEV node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:874:13 - ❯ performUnitOfWork node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:17641:22 - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[138/151]⎯ - - FAIL  src/app/[locale]/warehouse/inbound/__tests__/useInboundData.test.ts > useInboundData > totalInboundMonth 统计本月入库数量 -Error: Failed to call `useTranslations` because the context from `NextIntlClientProvider` was not found. - -This can happen because: -1) You intended to render this component as a Server Component, the render - failed, and therefore React attempted to render the component on the client - instead. If this is the case, check the console for server errors. -2) You intended to render this component on the client side, but no context was found. - Learn more about this error here: https://next-intl.dev/docs/environments/server-client-components#missing-context - ❯ node_modules/.pnpm/next-intl@4.13.0_@swc+helpers@0.5.15_next@16.1.1_@babel+core@7.29.0_@playwright+test@1.59.1_r_bduexwmuzix7ovejuqbqnee2bm/node_modules/next-intl/dist/esm/development/react-client/index.js:20:13 - ❯ Module.useInboundData src/app/[locale]/warehouse/inbound/hooks/useInboundData.ts:17:13 -  15| -  16| export function useInboundData() { -  17| const t = useTranslations('Warehouse'); -  | ^ -  18| -  19| const [searchQuery, setSearchQuery] = useState(''); - ❯ src/app/[locale]/warehouse/inbound/__tests__/useInboundData.test.ts:181:41 - ❯ TestComponent node_modules/.pnpm/@testing-library+react@16.3.2_@testing-library+dom@10.4.1_@types+react-dom@19.2.3_@types+reac_wek2qea7xkhq5fgdzagsmrzoju/node_modules/@testing-library/react/dist/pure.js:330:27 - ❯ Object.react_stack_bottom_frame node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:25904:20 - ❯ renderWithHooks node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:7662:22 - ❯ updateFunctionComponent node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:10166:19 - ❯ beginWork node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:11778:18 - ❯ runWithFiberInDEV node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:874:13 - ❯ performUnitOfWork node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:17641:22 - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[139/151]⎯ - - FAIL  src/app/[locale]/warehouse/inbound/__tests__/useInboundData.test.ts > useInboundData > data.data 为 null 时记录列表为空 -Error: Failed to call `useTranslations` because the context from `NextIntlClientProvider` was not found. - -This can happen because: -1) You intended to render this component as a Server Component, the render - failed, and therefore React attempted to render the component on the client - instead. If this is the case, check the console for server errors. -2) You intended to render this component on the client side, but no context was found. - Learn more about this error here: https://next-intl.dev/docs/environments/server-client-components#missing-context - ❯ node_modules/.pnpm/next-intl@4.13.0_@swc+helpers@0.5.15_next@16.1.1_@babel+core@7.29.0_@playwright+test@1.59.1_r_bduexwmuzix7ovejuqbqnee2bm/node_modules/next-intl/dist/esm/development/react-client/index.js:20:13 - ❯ Module.useInboundData src/app/[locale]/warehouse/inbound/hooks/useInboundData.ts:17:13 -  15| -  16| export function useInboundData() { -  17| const t = useTranslations('Warehouse'); -  | ^ -  18| -  19| const [searchQuery, setSearchQuery] = useState(''); - ❯ src/app/[locale]/warehouse/inbound/__tests__/useInboundData.test.ts:190:41 - ❯ TestComponent node_modules/.pnpm/@testing-library+react@16.3.2_@testing-library+dom@10.4.1_@types+react-dom@19.2.3_@types+reac_wek2qea7xkhq5fgdzagsmrzoju/node_modules/@testing-library/react/dist/pure.js:330:27 - ❯ Object.react_stack_bottom_frame node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:25904:20 - ❯ renderWithHooks node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:7662:22 - ❯ updateFunctionComponent node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:10166:19 - ❯ beginWork node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:11778:18 - ❯ runWithFiberInDEV node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:874:13 - ❯ performUnitOfWork node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:17641:22 - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[140/151]⎯ - - FAIL  src/app/[locale]/warehouse/inbound/__tests__/useInboundData.test.ts > useInboundData > 使用 createTime(驼峰)字段也能正确统计今日入库 -Error: Failed to call `useTranslations` because the context from `NextIntlClientProvider` was not found. - -This can happen because: -1) You intended to render this component as a Server Component, the render - failed, and therefore React attempted to render the component on the client - instead. If this is the case, check the console for server errors. -2) You intended to render this component on the client side, but no context was found. - Learn more about this error here: https://next-intl.dev/docs/environments/server-client-components#missing-context - ❯ node_modules/.pnpm/next-intl@4.13.0_@swc+helpers@0.5.15_next@16.1.1_@babel+core@7.29.0_@playwright+test@1.59.1_r_bduexwmuzix7ovejuqbqnee2bm/node_modules/next-intl/dist/esm/development/react-client/index.js:20:13 - ❯ Module.useInboundData src/app/[locale]/warehouse/inbound/hooks/useInboundData.ts:17:13 -  15| -  16| export function useInboundData() { -  17| const t = useTranslations('Warehouse'); -  | ^ -  18| -  19| const [searchQuery, setSearchQuery] = useState(''); - ❯ src/app/[locale]/warehouse/inbound/__tests__/useInboundData.test.ts:200:41 - ❯ TestComponent node_modules/.pnpm/@testing-library+react@16.3.2_@testing-library+dom@10.4.1_@types+react-dom@19.2.3_@types+reac_wek2qea7xkhq5fgdzagsmrzoju/node_modules/@testing-library/react/dist/pure.js:330:27 - ❯ Object.react_stack_bottom_frame node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:25904:20 - ❯ renderWithHooks node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:7662:22 - ❯ updateFunctionComponent node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:10166:19 - ❯ beginWork node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:11778:18 - ❯ runWithFiberInDEV node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:874:13 - ❯ performUnitOfWork node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:17641:22 - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[141/151]⎯ - - FAIL  src/app/[locale]/warehouse/inbound/__tests__/useInboundData.test.ts > useInboundData > create_time 为空字符串的记录不计入今日/本月统计 -Error: Failed to call `useTranslations` because the context from `NextIntlClientProvider` was not found. - -This can happen because: -1) You intended to render this component as a Server Component, the render - failed, and therefore React attempted to render the component on the client - instead. If this is the case, check the console for server errors. -2) You intended to render this component on the client side, but no context was found. - Learn more about this error here: https://next-intl.dev/docs/environments/server-client-components#missing-context - ❯ node_modules/.pnpm/next-intl@4.13.0_@swc+helpers@0.5.15_next@16.1.1_@babel+core@7.29.0_@playwright+test@1.59.1_r_bduexwmuzix7ovejuqbqnee2bm/node_modules/next-intl/dist/esm/development/react-client/index.js:20:13 - ❯ Module.useInboundData src/app/[locale]/warehouse/inbound/hooks/useInboundData.ts:17:13 -  15| -  16| export function useInboundData() { -  17| const t = useTranslations('Warehouse'); -  | ^ -  18| -  19| const [searchQuery, setSearchQuery] = useState(''); - ❯ src/app/[locale]/warehouse/inbound/__tests__/useInboundData.test.ts:210:41 - ❯ TestComponent node_modules/.pnpm/@testing-library+react@16.3.2_@testing-library+dom@10.4.1_@types+react-dom@19.2.3_@types+reac_wek2qea7xkhq5fgdzagsmrzoju/node_modules/@testing-library/react/dist/pure.js:330:27 - ❯ Object.react_stack_bottom_frame node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:25904:20 - ❯ renderWithHooks node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:7662:22 - ❯ updateFunctionComponent node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:10166:19 - ❯ beginWork node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:11778:18 - ❯ runWithFiberInDEV node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:874:13 - ❯ performUnitOfWork node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:17641:22 - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[142/151]⎯ - - FAIL  src/app/[locale]/warehouse/inbound/__tests__/useInboundData.test.ts > useInboundData > statusFilter 变更时触发带 status 参数的重新拉取 -Error: Failed to call `useTranslations` because the context from `NextIntlClientProvider` was not found. - -This can happen because: -1) You intended to render this component as a Server Component, the render - failed, and therefore React attempted to render the component on the client - instead. If this is the case, check the console for server errors. -2) You intended to render this component on the client side, but no context was found. - Learn more about this error here: https://next-intl.dev/docs/environments/server-client-components#missing-context - ❯ node_modules/.pnpm/next-intl@4.13.0_@swc+helpers@0.5.15_next@16.1.1_@babel+core@7.29.0_@playwright+test@1.59.1_r_bduexwmuzix7ovejuqbqnee2bm/node_modules/next-intl/dist/esm/development/react-client/index.js:20:13 - ❯ Module.useInboundData src/app/[locale]/warehouse/inbound/hooks/useInboundData.ts:17:13 -  15| -  16| export function useInboundData() { -  17| const t = useTranslations('Warehouse'); -  | ^ -  18| -  19| const [searchQuery, setSearchQuery] = useState(''); - ❯ src/app/[locale]/warehouse/inbound/__tests__/useInboundData.test.ts:224:41 - ❯ TestComponent node_modules/.pnpm/@testing-library+react@16.3.2_@testing-library+dom@10.4.1_@types+react-dom@19.2.3_@types+reac_wek2qea7xkhq5fgdzagsmrzoju/node_modules/@testing-library/react/dist/pure.js:330:27 - ❯ Object.react_stack_bottom_frame node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:25904:20 - ❯ renderWithHooks node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:7662:22 - ❯ updateFunctionComponent node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:10166:19 - ❯ beginWork node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:11778:18 - ❯ runWithFiberInDEV node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:874:13 - ❯ performUnitOfWork node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:17641:22 - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[143/151]⎯ - - FAIL  src/app/[locale]/warehouse/inbound/__tests__/useInboundData.test.ts > useInboundData > fetchWarehouses API 返回失败时仓库列表保持为空 -Error: Failed to call `useTranslations` because the context from `NextIntlClientProvider` was not found. - -This can happen because: -1) You intended to render this component as a Server Component, the render - failed, and therefore React attempted to render the component on the client - instead. If this is the case, check the console for server errors. -2) You intended to render this component on the client side, but no context was found. - Learn more about this error here: https://next-intl.dev/docs/environments/server-client-components#missing-context - ❯ node_modules/.pnpm/next-intl@4.13.0_@swc+helpers@0.5.15_next@16.1.1_@babel+core@7.29.0_@playwright+test@1.59.1_r_bduexwmuzix7ovejuqbqnee2bm/node_modules/next-intl/dist/esm/development/react-client/index.js:20:13 - ❯ Module.useInboundData src/app/[locale]/warehouse/inbound/hooks/useInboundData.ts:17:13 -  15| -  16| export function useInboundData() { -  17| const t = useTranslations('Warehouse'); -  | ^ -  18| -  19| const [searchQuery, setSearchQuery] = useState(''); - ❯ src/app/[locale]/warehouse/inbound/__tests__/useInboundData.test.ts:248:41 - ❯ TestComponent node_modules/.pnpm/@testing-library+react@16.3.2_@testing-library+dom@10.4.1_@types+react-dom@19.2.3_@types+reac_wek2qea7xkhq5fgdzagsmrzoju/node_modules/@testing-library/react/dist/pure.js:330:27 - ❯ Object.react_stack_bottom_frame node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:25904:20 - ❯ renderWithHooks node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:7662:22 - ❯ updateFunctionComponent node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:10166:19 - ❯ beginWork node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:11778:18 - ❯ runWithFiberInDEV node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:874:13 - ❯ performUnitOfWork node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:17641:22 - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[144/151]⎯ - - FAIL  src/app/[locale]/warehouse/inbound/__tests__/useInboundData.test.ts > useInboundData > fetchSuppliers API 返回失败时供应商列表保持为空 -Error: Failed to call `useTranslations` because the context from `NextIntlClientProvider` was not found. - -This can happen because: -1) You intended to render this component as a Server Component, the render - failed, and therefore React attempted to render the component on the client - instead. If this is the case, check the console for server errors. -2) You intended to render this component on the client side, but no context was found. - Learn more about this error here: https://next-intl.dev/docs/environments/server-client-components#missing-context - ❯ node_modules/.pnpm/next-intl@4.13.0_@swc+helpers@0.5.15_next@16.1.1_@babel+core@7.29.0_@playwright+test@1.59.1_r_bduexwmuzix7ovejuqbqnee2bm/node_modules/next-intl/dist/esm/development/react-client/index.js:20:13 - ❯ Module.useInboundData src/app/[locale]/warehouse/inbound/hooks/useInboundData.ts:17:13 -  15| -  16| export function useInboundData() { -  17| const t = useTranslations('Warehouse'); -  | ^ -  18| -  19| const [searchQuery, setSearchQuery] = useState(''); - ❯ src/app/[locale]/warehouse/inbound/__tests__/useInboundData.test.ts:264:41 - ❯ TestComponent node_modules/.pnpm/@testing-library+react@16.3.2_@testing-library+dom@10.4.1_@types+react-dom@19.2.3_@types+reac_wek2qea7xkhq5fgdzagsmrzoju/node_modules/@testing-library/react/dist/pure.js:330:27 - ❯ Object.react_stack_bottom_frame node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:25904:20 - ❯ renderWithHooks node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:7662:22 - ❯ updateFunctionComponent node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:10166:19 - ❯ beginWork node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:11778:18 - ❯ runWithFiberInDEV node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:874:13 - ❯ performUnitOfWork node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:17641:22 - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[145/151]⎯ - - FAIL  src/app/[locale]/warehouse/inbound/__tests__/usePrintLabels.test.ts > usePrintLabels > window.open 被阻止时显示错误提示 -Error: Failed to call `useTranslations` because the context from `NextIntlClientProvider` was not found. - -This can happen because: -1) You intended to render this component as a Server Component, the render - failed, and therefore React attempted to render the component on the client - instead. If this is the case, check the console for server errors. -2) You intended to render this component on the client side, but no context was found. - Learn more about this error here: https://next-intl.dev/docs/environments/server-client-components#missing-context - ❯ node_modules/.pnpm/next-intl@4.13.0_@swc+helpers@0.5.15_next@16.1.1_@babel+core@7.29.0_@playwright+test@1.59.1_r_bduexwmuzix7ovejuqbqnee2bm/node_modules/next-intl/dist/esm/development/react-client/index.js:20:13 - ❯ Module.usePrintLabels src/app/[locale]/warehouse/inbound/hooks/usePrintLabels.ts:10:13 -  8| -  9| export function usePrintLabels(inboundRecords: InboundRecord[]) { -  10| const t = useTranslations('Warehouse'); -  | ^ -  11| -  12| const handlePrintLabels = useCallback(async () => { - ❯ src/app/[locale]/warehouse/inbound/__tests__/usePrintLabels.test.ts:81:41 - ❯ TestComponent node_modules/.pnpm/@testing-library+react@16.3.2_@testing-library+dom@10.4.1_@types+react-dom@19.2.3_@types+reac_wek2qea7xkhq5fgdzagsmrzoju/node_modules/@testing-library/react/dist/pure.js:330:27 - ❯ Object.react_stack_bottom_frame node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:25904:20 - ❯ renderWithHooks node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:7662:22 - ❯ updateFunctionComponent node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:10166:19 - ❯ beginWork node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:11778:18 - ❯ runWithFiberInDEV node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:874:13 - ❯ performUnitOfWork node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:17641:22 - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[146/151]⎯ - - FAIL  src/app/[locale]/warehouse/inbound/__tests__/usePrintLabels.test.ts > usePrintLabels > 成功打开打印窗口并写入内容 -Error: Failed to call `useTranslations` because the context from `NextIntlClientProvider` was not found. - -This can happen because: -1) You intended to render this component as a Server Component, the render - failed, and therefore React attempted to render the component on the client - instead. If this is the case, check the console for server errors. -2) You intended to render this component on the client side, but no context was found. - Learn more about this error here: https://next-intl.dev/docs/environments/server-client-components#missing-context - ❯ node_modules/.pnpm/next-intl@4.13.0_@swc+helpers@0.5.15_next@16.1.1_@babel+core@7.29.0_@playwright+test@1.59.1_r_bduexwmuzix7ovejuqbqnee2bm/node_modules/next-intl/dist/esm/development/react-client/index.js:20:13 - ❯ Module.usePrintLabels src/app/[locale]/warehouse/inbound/hooks/usePrintLabels.ts:10:13 -  8| -  9| export function usePrintLabels(inboundRecords: InboundRecord[]) { -  10| const t = useTranslations('Warehouse'); -  | ^ -  11| -  12| const handlePrintLabels = useCallback(async () => { - ❯ src/app/[locale]/warehouse/inbound/__tests__/usePrintLabels.test.ts:105:41 - ❯ TestComponent node_modules/.pnpm/@testing-library+react@16.3.2_@testing-library+dom@10.4.1_@types+react-dom@19.2.3_@types+reac_wek2qea7xkhq5fgdzagsmrzoju/node_modules/@testing-library/react/dist/pure.js:330:27 - ❯ Object.react_stack_bottom_frame node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:25904:20 - ❯ renderWithHooks node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:7662:22 - ❯ updateFunctionComponent node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:10166:19 - ❯ beginWork node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:11778:18 - ❯ runWithFiberInDEV node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:874:13 - ❯ performUnitOfWork node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:17641:22 - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[147/151]⎯ - - FAIL  src/app/[locale]/warehouse/inbound/__tests__/usePrintLabels.test.ts > usePrintLabels > generatePrintContent 抛出异常时显示 printFailed -Error: Failed to call `useTranslations` because the context from `NextIntlClientProvider` was not found. - -This can happen because: -1) You intended to render this component as a Server Component, the render - failed, and therefore React attempted to render the component on the client - instead. If this is the case, check the console for server errors. -2) You intended to render this component on the client side, but no context was found. - Learn more about this error here: https://next-intl.dev/docs/environments/server-client-components#missing-context - ❯ node_modules/.pnpm/next-intl@4.13.0_@swc+helpers@0.5.15_next@16.1.1_@babel+core@7.29.0_@playwright+test@1.59.1_r_bduexwmuzix7ovejuqbqnee2bm/node_modules/next-intl/dist/esm/development/react-client/index.js:20:13 - ❯ Module.usePrintLabels src/app/[locale]/warehouse/inbound/hooks/usePrintLabels.ts:10:13 -  8| -  9| export function usePrintLabels(inboundRecords: InboundRecord[]) { -  10| const t = useTranslations('Warehouse'); -  | ^ -  11| -  12| const handlePrintLabels = useCallback(async () => { - ❯ src/app/[locale]/warehouse/inbound/__tests__/usePrintLabels.test.ts:132:41 - ❯ TestComponent node_modules/.pnpm/@testing-library+react@16.3.2_@testing-library+dom@10.4.1_@types+react-dom@19.2.3_@types+reac_wek2qea7xkhq5fgdzagsmrzoju/node_modules/@testing-library/react/dist/pure.js:330:27 - ❯ Object.react_stack_bottom_frame node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:25904:20 - ❯ renderWithHooks node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:7662:22 - ❯ updateFunctionComponent node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:10166:19 - ❯ beginWork node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:11778:18 - ❯ runWithFiberInDEV node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:874:13 - ❯ performUnitOfWork node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:17641:22 - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[148/151]⎯ - - FAIL  src/app/[locale]/warehouse/inbound/__tests__/usePrintLabels.test.ts > usePrintLabels > 空记录列表也能正常调用(不报错) -Error: Failed to call `useTranslations` because the context from `NextIntlClientProvider` was not found. - -This can happen because: -1) You intended to render this component as a Server Component, the render - failed, and therefore React attempted to render the component on the client - instead. If this is the case, check the console for server errors. -2) You intended to render this component on the client side, but no context was found. - Learn more about this error here: https://next-intl.dev/docs/environments/server-client-components#missing-context - ❯ node_modules/.pnpm/next-intl@4.13.0_@swc+helpers@0.5.15_next@16.1.1_@babel+core@7.29.0_@playwright+test@1.59.1_r_bduexwmuzix7ovejuqbqnee2bm/node_modules/next-intl/dist/esm/development/react-client/index.js:20:13 - ❯ Module.usePrintLabels src/app/[locale]/warehouse/inbound/hooks/usePrintLabels.ts:10:13 -  8| -  9| export function usePrintLabels(inboundRecords: InboundRecord[]) { -  10| const t = useTranslations('Warehouse'); -  | ^ -  11| -  12| const handlePrintLabels = useCallback(async () => { - ❯ src/app/[locale]/warehouse/inbound/__tests__/usePrintLabels.test.ts:155:41 - ❯ TestComponent node_modules/.pnpm/@testing-library+react@16.3.2_@testing-library+dom@10.4.1_@types+react-dom@19.2.3_@types+reac_wek2qea7xkhq5fgdzagsmrzoju/node_modules/@testing-library/react/dist/pure.js:330:27 - ❯ Object.react_stack_bottom_frame node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:25904:20 - ❯ renderWithHooks node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:7662:22 - ❯ updateFunctionComponent node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:10166:19 - ❯ beginWork node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:11778:18 - ❯ runWithFiberInDEV node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:874:13 - ❯ performUnitOfWork node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom-client.development.js:17641:22 - -⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[149/151]⎯ - - - Test Files  22 failed | 120 passed (142) - Tests  151 failed | 2125 passed (2276) - Start at  11:30:57 - Duration  95.96s (transform 46.56s, setup 6.05s, import 184.98s, tests 32.94s, environment 1421.73s) - diff --git a/_vt_check.txt b/_vt_check.txt deleted file mode 100644 index a5e69b93..00000000 --- a/_vt_check.txt +++ /dev/null @@ -1,119 +0,0 @@ - - RUN  v4.1.5 D:/dcprint/erp-project - -stderr | tests/integration/inbound-api.test.ts -[RepoRegistry] active impl = mysql (default; set REPOSITORY_IMPL=drizzle to switch) - -stderr | tests/integration/inbound-api.test.ts > 入库 API 集成测试 > GET /api/warehouse/inbound - 查询入库单列表 > 正常流程:返回分页列表数据 -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) - -stderr | tests/integration/inbound-api.test.ts > 入库 API 集成测试 > GET /api/warehouse/inbound - 查询入库单列表 > 支持按状态筛选 -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) - -stderr | tests/integration/inbound-api.test.ts > 入库 API 集成测试 > GET /api/warehouse/inbound - 查询入库单列表 > 使用默认分页参数(page=1, pageSize=10) -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) - -stderr | tests/integration/inbound-api.test.ts > 入库 API 集成测试 > GET /api/warehouse/inbound - 查询入库单列表 > service 抛错时返回 500 -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) - -stderr | tests/integration/inbound-api.test.ts > 入库 API 集成测试 > POST /api/warehouse/inbound - 创建入库单 > 正常流程:创建成功返回 order_id 和 order_no -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) - -stderr | tests/integration/inbound-api.test.ts > 入库 API 集成测试 > POST /api/warehouse/inbound - 创建入库单 > service 抛 DomainError 时返回 500(未被路由捕获) -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) - -stderr | tests/integration/inbound-api.test.ts > 入库 API 集成测试 > PUT /api/warehouse/inbound - 更新入库单 > 正常流程:submit 动作调用 submitOrder -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) - -stderr | tests/integration/inbound-api.test.ts > 入库 API 集成测试 > PUT /api/warehouse/inbound - 更新入库单 > 正常流程:approve 动作调用 approveOrder -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) - -stderr | tests/integration/inbound-api.test.ts > 入库 API 集成测试 > PUT /api/warehouse/inbound - 更新入库单 > 正常流程:cancel 动作调用 cancelOrder -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) - -stderr | tests/integration/inbound-api.test.ts > 入库 API 集成测试 > PUT /api/warehouse/inbound - 更新入库单 > 正常流程:unapprove 动作调用 unapproveOrder -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) - -stderr | tests/integration/inbound-api.test.ts > 入库 API 集成测试 > PUT /api/warehouse/inbound - 更新入库单 > 使用 status=pending 等价于 submit 动作 -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) - -stderr | tests/integration/inbound-api.test.ts > 入库 API 集成测试 > PUT /api/warehouse/inbound - 更新入库单 > 使用 status=approved 等价于 approve 动作 -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) - -stderr | tests/integration/inbound-api.test.ts > 入库 API 集成测试 > PUT /api/warehouse/inbound - 更新入库单 > 领域异常:NotFoundError 返回 404 -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) - -stderr | tests/integration/inbound-api.test.ts > 入库 API 集成测试 > PUT /api/warehouse/inbound - 更新入库单 > 领域异常:VersionConflictError 返回 409 -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) - -stderr | tests/integration/inbound-api.test.ts > 入库 API 集成测试 > PUT /api/warehouse/inbound - 更新入库单 > 领域异常:DomainError 返回 400 -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) - -stderr | tests/integration/inbound-api.test.ts > 入库 API 集成测试 > PUT /api/warehouse/inbound - 更新入库单 > 仅更新 remark 字段时走直接 SQL 更新 -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) - -stderr | tests/integration/inbound-api.test.ts > 入库 API 集成测试 > DELETE /api/warehouse/inbound - 删除入库单 > 正常流程:删除草稿状态入库单 -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) - -stderr | tests/integration/inbound-api.test.ts > 入库 API 集成测试 > DELETE /api/warehouse/inbound - 删除入库单 > 领域异常:NotFoundError 返回 404 -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) - -stderr | tests/integration/inbound-api.test.ts > 入库 API 集成测试 > DELETE /api/warehouse/inbound - 删除入库单 > 领域异常:DomainError(已审核不能删除)返回 400 -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) - -stderr | tests/integration/inbound-api.test.ts > 入库 API 集成测试 > DELETE /api/warehouse/inbound - 删除入库单 > 参数异常:id 非数字时正常处理为 parseInt -[RepoRegistry] getInboundOrderRepository() → MysqlInboundOrderRepository (mysql) -[RepoRegistry] getPurchaseOrderRepository() → MysqlPurchaseOrderRepository (mysql) - - ✓ tests/integration/inbound-api.test.ts (34 tests) 94ms -[2026-08-27 10:32:17.933 +0800] INFO: [warehouse/inbound] 开始物料分类校验 - itemCount: 1 - materialIds: [ - 101 - ] - - Test Files  1 passed (1) - Tests  34 passed (34) - Start at  10:32:12 - Duration  5.26s (transform 1.06s, setup 35ms, import 2.47s, tests 94ms, environment 2.03s) - -[2026-08-27 10:32:17.934 +0800] INFO: [warehouse/inbound] 物料分类校验完成 - blocked: false - uncategorizedCount: 0 -[2026-08-27 10:32:17.948 +0800] INFO: [warehouse/inbound] 开始物料分类校验 - itemCount: 1 - materialIds: [ - 101 - ] -[2026-08-27 10:32:17.949 +0800] INFO: [warehouse/inbound] 物料分类校验完成 - blocked: false - uncategorizedCount: 0 -[2026-08-27 10:32:17.950 +0800] INFO: [warehouse/inbound] 开始物料分类校验 - itemCount: 1 - materialIds: [ - 101 - ] -[2026-08-27 10:32:17.950 +0800] INFO: [warehouse/inbound] 物料分类校验完成 - blocked: true - uncategorizedCount: 1 -[2026-08-27 10:32:17.950 +0800] WARN: [warehouse/inbound] 物料分类校验阻断提交 - message: "以下物料尚未设置物料分类:M001(材料1)。请先在「基础数据 → 物料分类」中归类。" diff --git a/agency-agents b/agency-agents new file mode 160000 index 00000000..783f6a72 --- /dev/null +++ b/agency-agents @@ -0,0 +1 @@ +Subproject commit 783f6a72bfd7f3135700ac273c619d92821b419a diff --git a/alertmanager/alertmanager.yml b/alertmanager/alertmanager.yml deleted file mode 100644 index 2ed37f9d..00000000 --- a/alertmanager/alertmanager.yml +++ /dev/null @@ -1,54 +0,0 @@ -global: - resolve_timeout: 5m - -route: - group_by: ['alertname', 'severity'] - group_wait: 30s - group_interval: 5m - repeat_interval: 4h - receiver: 'default' - routes: - - match: - severity: critical - receiver: 'critical' - group_wait: 10s - repeat_interval: 1h - - match: - severity: warning - receiver: 'warning' - repeat_interval: 4h - -receivers: - - name: 'default' - email_configs: - - to: 'ops-team@your-domain.com' - from: 'alertmanager@your-domain.com' - smarthost: 'smtp.your-domain.com:587' - auth_username: '${SMTP_USER}' - auth_password: '${SMTP_PASSWORD}' - require_tls: true - send_resolved: true - - - name: 'critical' - email_configs: - - to: 'ops-team@your-domain.com' - from: 'alertmanager@your-domain.com' - smarthost: 'smtp.your-domain.com:587' - auth_username: '${SMTP_USER}' - auth_password: '${SMTP_PASSWORD}' - require_tls: true - send_resolved: true - # 钉钉通知(需部署 dingtalk-webhook 适配器) - # webhook_configs: - # - url: 'http://dingtalk-webhook:8060/dingtalk/webhook1/send' - # send_resolved: true - - - name: 'warning' - email_configs: - - to: 'ops-team@your-domain.com' - from: 'alertmanager@your-domain.com' - smarthost: 'smtp.your-domain.com:587' - auth_username: '${SMTP_USER}' - auth_password: '${SMTP_PASSWORD}' - require_tls: true - send_resolved: true diff --git a/colcheck.mjs b/colcheck.mjs deleted file mode 100644 index 28af6045..00000000 --- a/colcheck.mjs +++ /dev/null @@ -1,16 +0,0 @@ -import mysql from 'mysql2/promise'; -import { readFileSync } from 'fs'; -const env = readFileSync('.env','utf8'); -const get = (k)=>{ const m = env.match(new RegExp('^'+k+'=(.*)$','m')); return m?m[1].trim():undefined; }; -const cfg = { host:get('DB_HOST')||'127.0.0.1', port:parseInt(get('DB_PORT')||'3306'), user:get('DB_USER')||'root', password:get('DB_PASSWORD')||'', database:get('DB_NAME')||'vnerpdacahng' }; -const conn = await mysql.createConnection(cfg); -const tables = ['sal_delivery','sal_return','sal_reconciliation','pur_purchase_return','pur_purchase_return_line','pur_request','pur_request_item','pur_supplier']; -for (const t of tables) { - try { - const [rows] = await conn.query("SELECT COLUMN_NAME FROM information_schema.columns WHERE table_schema=? AND table_name=? ORDER BY ORDINAL_POSITION",[cfg.database,t]); - const cols = rows.map(r=>r.COLUMN_NAME); - console.log(`\n=== ${t} (${cols.length} cols) ===`); - console.log(cols.join(', ')); - } catch(e){ console.log(`\n=== ${t} ERROR: ${e.message}`); } -} -await conn.end(); diff --git a/database/_archive/_fix_part4.js b/database/_archive/_fix_part4.js new file mode 100644 index 00000000..864a5d06 --- /dev/null +++ b/database/_archive/_fix_part4.js @@ -0,0 +1,38 @@ +const fs = require('fs'); +const path = require('path'); +const outFile = path.join(__dirname, 'seed_part4_dcprint_qc_eqp.sql'); + +let content = fs.readFileSync(outFile, 'utf8'); + +const standardCardSQL = ` +-- ----------------------------------------------------------- +-- 9. prd_standard_card 标准卡 (20条) +-- ----------------------------------------------------------- +INSERT IGNORE INTO prd_standard_card +(card_no, customer_name, customer_code, product_name, version, date, document_code, finished_size, tolerance, material_name, material_type, layout_type, spacing, spacing_value, sheet_width, sheet_length, core_type, paper_direction, roll_width, paper_edge, standard_usage, jump_distance, process_flow1, process_flow2, print_type, first_jump_distance, sequences, film_manufacturer, film_code, film_size, process_method, stamping_method, mold_code, layout_method, layout_way, jump_distance2, mylar_material, mylar_specs, mylar_layout, mylar_jump, adhesive_type, adhesive_manufacturer, adhesive_code, adhesive_size, dashed_knife, slice_per_row, slice_per_roll, slice_per_bundle, slice_per_bag, slice_per_box, back_knife_mold, back_mylar_mold, release_paper_code, release_paper_type, release_paper_specs, padding_material, packing_material, glue_type, packing_type, special_color, color_formula, file_path, sample_info, notes, creator, reviewer, factory_manager, quality_manager, sales, approver, status, creator_id, reviewer_id, create_time, update_time, deleted) +VALUES +('SC-20250512-001', '美的集团', 'CUST-001', '空调面板标签', 'V1.0', '2025-04-01', 'DOC-MD-001', '150mm×80mm', '±0.3mm', 'PET白膜0.125mm', 'PET', '单排', '3mm', '3mm', '300mm', '500mm', '3寸', '纵向', '310mm', '5mm', '1.2m²/千张', '5mm', '印刷→模切→品检→包装', 'UV印刷→模切', 'UV柔印', '5mm', '["C","M","Y","K"]', '东莞宏达', 'FM-MD-001', '300mm×500mm', 'UV固化', '冷烫', 'DIE-20250512-002', '单排居中', '左进右出', '5mm', 'PET离型膜', '300mm×0.075mm', '满版', '3mm', '水性胶', '3M', 'ADH-001', '300mm×500m', 0, '10', '5000', '500', '100', '50', 'DK-001', 'BM-001', 'RP-001', '格拉辛', '300mm×500m', '珍珠棉', '瓦楞纸箱', '水性胶', '箱装', 'Pantone 185C', 'C:0 M:100 Y:85 K:0', '/files/SC-001.pdf', '已确认样品', '美的空调面板标签标准工艺卡', '张伟', '王强', '刘洋', '陈明', '赵磊', '孙丽', 1, 2, 4, '2025-04-01 08:00:00', '2025-04-01 08:00:00', 0), +('SC-20250512-002', '格力电器', 'CUST-002', '洗衣机面板标签', 'V1.0', '2025-04-02', 'DOC-GL-001', '200mm×120mm', '±0.3mm', 'PVC白膜0.2mm', 'PVC', '双排', '4mm', '4mm', '450mm', '600mm', '3寸', '纵向', '460mm', '5mm', '1.5m²/千张', '6mm', '印刷→模切→品检→包装', '溶剂印刷→模切', '溶剂柔印', '6mm', '["K","专色灰"]', '东莞宏达', 'FM-GL-001', '450mm×600mm', '溶剂固化', '无', 'DIE-20250512-004', '双排对称', '左进右出', '6mm', 'PET离型膜', '450mm×0.075mm', '双排', '4mm', '溶剂胶', '汉高', 'ADH-002', '450mm×500m', 0, '8', '4000', '400', '80', '40', 'DK-002', 'BM-002', 'RP-002', '格拉辛', '450mm×500m', '珍珠棉', '瓦楞纸箱', '溶剂胶', '箱装', 'Pantone Cool Gray 6C', 'K:50 专色灰', '/files/SC-002.pdf', '已确认样品', '格力洗衣机面板标签标准工艺卡', '李娜', '王强', '刘洋', '陈明', '赵磊', '孙丽', 1, 3, 4, '2025-04-02 09:00:00', '2025-04-02 09:00:00', 0), +('SC-20250512-003', '华为技术', 'CUST-003', '手机后盖标签', 'V2.0', '2025-04-03', 'DOC-HW-001', '80mm×45mm', '±0.2mm', 'PET透明膜0.1mm', 'PET', '四排', '2mm', '2mm', '350mm', '400mm', '3寸', '纵向', '360mm', '3mm', '0.8m²/千张', '3mm', '印刷→模切→品检→包装', 'UV印刷→模切→UV光油', 'UV柔印', '3mm', '["C","M","Y","K","专色蓝"]', '东莞宏达', 'FM-HW-001', '350mm×400mm', 'UV固化', '无', 'DIE-20250512-003', '四排矩阵', '左进右出', '3mm', 'PET离型膜', '350mm×0.05mm', '四排', '2mm', 'UV胶', '3M', 'ADH-003', '350mm×500m', 0, '20', '10000', '1000', '200', '100', 'DK-003', 'BM-003', 'RP-003', 'PET离型', '350mm×500m', '防静电袋', '瓦楞纸箱', 'UV胶', '箱装', 'Pantone 2945C', 'C:100 M:56 Y:0 K:18', '/files/SC-003.pdf', '已确认样品', '华为手机后盖标签高精度工艺卡', '张伟', '刘洋', '陈明', '赵磊', '孙丽', '周杰', 1, 2, 5, '2025-04-03 08:00:00', '2025-04-03 08:00:00', 0), +('SC-20250512-004', '比亚迪', 'CUST-004', '汽车内饰标签', 'V1.0', '2025-04-05', 'DOC-BYD-001', '250mm×180mm', '±0.5mm', 'PVC透明膜0.15mm', 'PVC', '单排', '5mm', '5mm', '500mm', '400mm', '6寸', '横向', '510mm', '8mm', '2.0m²/千张', '8mm', '印刷→模切→品检→包装', '溶剂印刷→模切', '溶剂凹印', '8mm', '["K","专色绿"]', '东莞宏达', 'FM-BYD-001', '500mm×400mm', '溶剂固化', '无', 'DIE-20250512-008', '单排居中', '左进右出', '8mm', 'PET离型膜', '500mm×0.1mm', '满版', '5mm', '耐高温胶', '德莎', 'ADH-004', '500mm×300m', 0, '5', '2500', '250', '50', '25', 'DK-004', 'BM-004', 'RP-004', '格拉辛', '500mm×300m', '泡沫板', '木箱', '耐高温胶', '木箱装', 'Pantone 356C', 'C:100 Y:100 K:50', '/files/SC-004.pdf', '已确认样品', '比亚迪汽车内饰标签耐高温工艺卡', '李娜', '刘洋', '陈明', '赵磊', '孙丽', '周杰', 1, 3, 5, '2025-04-05 09:00:00', '2025-04-05 09:00:00', 0), +('SC-20250512-005', 'TCL科技', 'CUST-005', '家电面板标签', 'V1.0', '2025-04-07', 'DOC-TCL-001', '180mm×100mm', '±0.3mm', 'PET白膜0.125mm', 'PET', '双排', '3mm', '3mm', '400mm', '500mm', '3寸', '纵向', '410mm', '5mm', '1.3m²/千张', '5mm', '印刷→模切→品检→包装', 'UV印刷→模切', 'UV柔印', '5mm', '["C","M","Y","K","专色橙"]', '东莞宏达', 'FM-TCL-001', '400mm×500mm', 'UV固化', '冷烫', 'DIE-20250512-006', '双排对称', '左进右出', '5mm', 'PET离型膜', '400mm×0.075mm', '双排', '3mm', '水性胶', '3M', 'ADH-005', '400mm×500m', 0, '12', '6000', '600', '120', '60', 'DK-005', 'BM-005', 'RP-005', '格拉辛', '400mm×500m', '珍珠棉', '瓦楞纸箱', '水性胶', '箱装', 'Pantone 165C', 'C:0 M:65 Y:100 K:0', '/files/SC-005.pdf', '已确认样品', 'TCL家电面板标签标准工艺卡', '王强', '陈明', '赵磊', '孙丽', '周杰', '吴芳', 1, 4, 6, '2025-04-07 08:00:00', '2025-04-07 08:00:00', 0), +('SC-20250512-006', '中兴通讯', 'CUST-006', '5G设备标签', 'V1.0', '2025-04-08', 'DOC-ZX-001', '60mm×30mm', '±0.2mm', 'PET透明膜0.1mm', 'PET', '六排', '2mm', '2mm', '400mm', '350mm', '3寸', '纵向', '410mm', '3mm', '0.6m²/千张', '3mm', '印刷→模切→品检→包装', 'UV印刷→模切', 'UV柔印', '3mm', '["K","专色蓝"]', '东莞宏达', 'FM-ZX-001', '400mm×350mm', 'UV固化', '无', 'DIE-20250512-007', '六排矩阵', '左进右出', '3mm', 'PET离型膜', '400mm×0.05mm', '六排', '2mm', 'UV胶', '3M', 'ADH-006', '400mm×500m', 0, '30', '15000', '1500', '300', '150', 'DK-006', 'BM-006', 'RP-006', 'PET离型', '400mm×500m', '防静电袋', '瓦楞纸箱', 'UV胶', '箱装', 'Pantone 3005C', 'C:100 M:30 Y:0 K:12', '/files/SC-006.pdf', '已确认样品', '中兴5G设备标签高密度工艺卡', '张伟', '陈明', '赵磊', '孙丽', '周杰', '吴芳', 1, 2, 6, '2025-04-08 09:00:00', '2025-04-08 09:00:00', 0), +('SC-20250512-007', '广汽集团', 'CUST-007', '汽车内饰标签', 'V1.0', '2025-04-10', 'DOC-GQ-001', '300mm×200mm', '±0.5mm', 'PVC透明膜0.15mm', 'PVC', '单排', '6mm', '6mm', '550mm', '450mm', '6寸', '横向', '560mm', '8mm', '2.5m²/千张', '8mm', '印刷→模切→品检→包装', '溶剂印刷→模切→覆膜', '溶剂凹印', '8mm', '["K","专色银"]', '东莞宏达', 'FM-GQ-001', '550mm×450mm', '溶剂固化', '热烫', 'DIE-20250512-008', '单排居中', '左进右出', '8mm', 'PET离型膜', '550mm×0.1mm', '满版', '6mm', '耐高温胶', '德莎', 'ADH-007', '550mm×300m', 0, '4', '2000', '200', '40', '20', 'DK-007', 'BM-007', 'RP-007', '格拉辛', '550mm×300m', '泡沫板', '木箱', '耐高温胶', '木箱装', 'Pantone 877C', '银色金属墨', '/files/SC-007.pdf', '已确认样品', '广汽汽车内饰标签大尺寸工艺卡', '李娜', '赵磊', '孙丽', '周杰', '吴芳', '张伟', 1, 3, 7, '2025-04-10 08:00:00', '2025-04-10 08:00:00', 0), +('SC-20250512-008', '海信集团', 'CUST-008', '空调面板标签', 'V1.0', '2025-04-12', 'DOC-HX-001', '160mm×90mm', '±0.3mm', 'PET白膜0.125mm', 'PET', '双排', '3mm', '3mm', '380mm', '480mm', '3寸', '纵向', '390mm', '5mm', '1.1m²/千张', '5mm', '印刷→模切→品检→包装', 'UV印刷→模切→UV光油', 'UV柔印', '5mm', '["C","M","Y","K","专色金"]', '东莞宏达', 'FM-HX-001', '380mm×480mm', 'UV固化', '冷烫', 'DIE-20250512-009', '双排对称', '左进右出', '5mm', 'PET离型膜', '380mm×0.075mm', '双排', '3mm', '水性胶', '3M', 'ADH-008', '380mm×500m', 0, '10', '5000', '500', '100', '50', 'DK-008', 'BM-008', 'RP-008', '格拉辛', '380mm×500m', '珍珠棉', '瓦楞纸箱', '水性胶', '箱装', 'Pantone 871C', '金色金属墨', '/files/SC-008.pdf', '已确认样品', '海信空调面板标签金标工艺卡', '王强', '赵磊', '孙丽', '周杰', '吴芳', '张伟', 1, 4, 7, '2025-04-12 09:00:00', '2025-04-12 09:00:00', 0), +('SC-20250512-009', '小米科技', 'CUST-009', '手机标签', 'V1.0', '2025-04-15', 'DOC-XM-001', '75mm×40mm', '±0.2mm', 'PET透明膜0.1mm', 'PET', '四排', '2mm', '2mm', '350mm', '380mm', '3寸', '纵向', '360mm', '3mm', '0.7m²/千张', '3mm', '印刷→模切→品检→包装', 'UV印刷→模切→UV光油', 'UV柔印', '3mm', '["C","M","Y","K","专色橙"]', '东莞宏达', 'FM-XM-001', '350mm×380mm', 'UV固化', '无', 'DIE-20250512-010', '四排矩阵', '左进右出', '3mm', 'PET离型膜', '350mm×0.05mm', '四排', '2mm', 'UV胶', '3M', 'ADH-009', '350mm×500m', 0, '25', '12000', '1200', '240', '120', 'DK-009', 'BM-009', 'RP-009', 'PET离型', '350mm×500m', '防静电袋', '瓦楞纸箱', 'UV胶', '箱装', 'Pantone 151C', 'C:0 M:70 Y:100 K:0', '/files/SC-009.pdf', '已确认样品', '小米手机标签高密度工艺卡', '张伟', '孙丽', '周杰', '吴芳', '张伟', '李娜', 1, 2, 8, '2025-04-15 08:00:00', '2025-04-15 08:00:00', 0), +('SC-20250512-010', 'OPPO', 'CUST-010', '手机标签', 'V1.0', '2025-04-17', 'DOC-OPPO-001', '70mm×35mm', '±0.2mm', 'PET透明膜0.1mm', 'PET', '四排', '2mm', '2mm', '320mm', '350mm', '3寸', '纵向', '330mm', '3mm', '0.6m²/千张', '3mm', '印刷→模切→品检→包装', 'UV印刷→模切', 'UV柔印', '3mm', '["K","专色绿"]', '东莞宏达', 'FM-OPPO-001', '320mm×350mm', 'UV固化', '无', 'DIE-20250512-011', '四排矩阵', '左进右出', '3mm', 'PET离型膜', '320mm×0.05mm', '四排', '2mm', 'UV胶', '3M', 'ADH-010', '320mm×500m', 0, '28', '14000', '1400', '280', '140', 'DK-010', 'BM-010', 'RP-010', 'PET离型', '320mm×500m', '防静电袋', '瓦楞纸箱', 'UV胶', '箱装', 'Pantone 375C', 'C:70 M:0 Y:100 K:0', '/files/SC-010.pdf', '已确认样品', 'OPPO手机标签高密度工艺卡', '李娜', '孙丽', '周杰', '吴芳', '张伟', '李娜', 1, 3, 8, '2025-04-17 09:00:00', '2025-04-17 09:00:00', 0), +('SC-20250512-011', '长虹', 'CUST-011', '烟包标签', 'V1.0', '2025-04-18', 'DOC-CH-001', '220mm×140mm', '±0.3mm', 'PET白膜0.125mm', 'PET', '单排', '4mm', '4mm', '280mm', '200mm', '3寸', '纵向', '290mm', '5mm', '1.4m²/千张', '5mm', '印刷→烫金→模切→品检→包装', 'UV印刷→烫金→模切', 'UV柔印+烫金', '5mm', '["K","专色金"]', '东莞宏达', 'FM-CH-001', '280mm×200mm', 'UV固化', '热烫', 'DIE-20250512-012', '单排居中', '左进右出', '5mm', 'PET离型膜', '280mm×0.075mm', '满版', '4mm', '水性胶', '3M', 'ADH-011', '280mm×500m', 0, '8', '4000', '400', '80', '40', 'DK-011', 'BM-011', 'RP-011', '格拉辛', '280mm×500m', '珍珠棉', '瓦楞纸箱', '水性胶', '箱装', 'Pantone 871C', '金色金属墨', '/files/SC-011.pdf', '已确认样品', '长虹烟包标签烫金工艺卡', '王强', '周杰', '吴芳', '张伟', '李娜', '王强', 1, 4, 9, '2025-04-18 08:00:00', '2025-04-18 08:00:00', 0), +('SC-20250512-012', '创维', 'CUST-012', '日化标签', 'V1.0', '2025-04-20', 'DOC-CW-001', '190mm×110mm', '±0.3mm', 'PVC白膜0.2mm', 'PVC', '双排', '3mm', '3mm', '420mm', '500mm', '3寸', '纵向', '430mm', '5mm', '1.2m²/千张', '5mm', '印刷→模切→品检→包装', '溶剂印刷→模切', '溶剂柔印', '5mm', '["K","专色"]', '东莞宏达', 'FM-CW-001', '420mm×500mm', '溶剂固化', '无', 'DIE-20250512-013', '双排对称', '左进右出', '5mm', 'PET离型膜', '420mm×0.075mm', '双排', '3mm', '溶剂胶', '汉高', 'ADH-012', '420mm×500m', 0, '10', '5000', '500', '100', '50', 'DK-012', 'BM-012', 'RP-012', '格拉辛', '420mm×500m', '珍珠棉', '瓦楞纸箱', '溶剂胶', '箱装', '专色蓝', 'C:100 M:50', '/files/SC-012.pdf', '已确认样品', '创维日化标签标准工艺卡', '张伟', '周杰', '吴芳', '张伟', '李娜', '王强', 1, 2, 9, '2025-04-20 09:00:00', '2025-04-20 09:00:00', 0), +('SC-20250512-013', '联想', 'CUST-013', '不干胶标签', 'V1.0', '2025-04-22', 'DOC-LX-001', '100mm×60mm', '±0.2mm', 'PET透明膜0.1mm', 'PET', '三排', '2mm', '2mm', '350mm', '400mm', '3寸', '纵向', '360mm', '3mm', '0.9m²/千张', '3mm', '印刷→模切→品检→包装', 'UV印刷→模切', 'UV柔印', '3mm', '["C","M","Y","K"]', '东莞宏达', 'FM-LX-001', '350mm×400mm', 'UV固化', '无', 'DIE-20250512-014', '三排矩阵', '左进右出', '3mm', 'PET离型膜', '350mm×0.05mm', '三排', '2mm', 'UV胶', '3M', 'ADH-013', '350mm×500m', 0, '15', '7500', '750', '150', '75', 'DK-013', 'BM-013', 'RP-013', 'PET离型', '350mm×500m', '防静电袋', '瓦楞纸箱', 'UV胶', '箱装', '无', 'CMYK四色', '/files/SC-013.pdf', '已确认样品', '联想不干胶标签标准工艺卡', '李娜', '吴芳', '张伟', '李娜', '王强', '赵磊', 1, 3, 10, '2025-04-22 08:00:00', '2025-04-22 08:00:00', 0), +('SC-20250512-014', '海尔', 'CUST-014', '礼品包装标签', 'V1.0', '2025-04-24', 'DOC-HE-001', '280mm×200mm', '±0.5mm', 'PVC透明膜0.15mm', 'PVC', '单排', '5mm', '5mm', '500mm', '400mm', '6寸', '横向', '510mm', '8mm', '2.2m²/千张', '8mm', '印刷→压纹→模切→品检→包装', 'UV印刷→压纹→模切', 'UV柔印+压纹', '8mm', '["K","专色"]', '东莞宏达', 'FM-HE-001', '500mm×400mm', 'UV固化', '无', 'DIE-20250512-015', '单排居中', '左进右出', '8mm', 'PET离型膜', '500mm×0.1mm', '满版', '5mm', '水性胶', '3M', 'ADH-014', '500mm×300m', 0, '6', '3000', '300', '60', '30', 'DK-014', 'BM-014', 'RP-014', '格拉辛', '500mm×300m', '泡沫板', '木箱', '水性胶', '木箱装', '专色红', 'M:100 Y:100', '/files/SC-014.pdf', '已确认样品', '海尔礼品包装标签压纹工艺卡', '王强', '吴芳', '张伟', '李娜', '王强', '赵磊', 1, 4, 10, '2025-04-24 09:00:00', '2025-04-24 09:00:00', 0), +('SC-20250512-015', '方太', 'CUST-015', '食品标签', 'V1.0', '2025-04-25', 'DOC-FT-001', '170mm×100mm', '±0.3mm', 'PET白膜0.125mm', 'PET', '双排', '3mm', '3mm', '400mm', '450mm', '3寸', '纵向', '410mm', '5mm', '1.0m²/千张', '5mm', '印刷→模切→品检→包装', 'UV印刷→模切', 'UV柔印', '5mm', '["C","M","Y","K"]', '东莞宏达', 'FM-FT-001', '400mm×450mm', 'UV固化', '无', 'DIE-20250512-016', '双排对称', '左进右出', '5mm', 'PET离型膜', '400mm×0.075mm', '双排', '3mm', '食品级胶', '3M', 'ADH-015', '400mm×500m', 0, '12', '6000', '600', '120', '60', 'DK-015', 'BM-015', 'RP-015', '格拉辛', '400mm×500m', '食品级纸', '瓦楞纸箱', '食品级胶', '箱装', '无', 'CMYK四色', '/files/SC-015.pdf', '已确认样品-食品级', '方太食品标签食品级工艺卡', '张伟', '吴芳', '李娜', '王强', '赵磊', '孙丽', 1, 2, 10, '2025-04-25 08:00:00', '2025-04-25 08:00:00', 0), +('SC-20250512-016', '志高', 'CUST-016', '电池标签', 'V1.0', '2025-04-27', 'DOC-ZG-001', '90mm×50mm', '±0.2mm', 'PET透明膜0.1mm', 'PET', '五排', '2mm', '2mm', '500mm', '400mm', '3寸', '纵向', '510mm', '3mm', '0.5m²/千张', '3mm', '印刷→模切→品检→包装', 'UV印刷→模切', 'UV柔印', '3mm', '["K","专色绿"]', '东莞宏达', 'FM-ZG-001', '500mm×400mm', 'UV固化', '无', 'DIE-20250512-017', '五排矩阵', '左进右出', '3mm', 'PET离型膜', '500mm×0.05mm', '五排', '2mm', 'UV胶', '3M', 'ADH-016', '500mm×500m', 0, '20', '10000', '1000', '200', '100', 'DK-016', 'BM-016', 'RP-016', 'PET离型', '500mm×500m', '防静电袋', '瓦楞纸箱', 'UV胶', '箱装', 'Pantone 356C', 'C:100 Y:100 K:50', '/files/SC-016.pdf', '已确认样品', '志高电池标签高密度工艺卡', '李娜', '吴芳', '王强', '赵磊', '孙丽', '周杰', 1, 3, 10, '2025-04-27 09:00:00', '2025-04-27 09:00:00', 0), +('SC-20250512-017', '奥克斯', 'CUST-017', '酒类标签', 'V1.0', '2025-04-28', 'DOC-AKS-001', '130mm×80mm', '±0.3mm', 'PET白膜0.125mm', 'PET', '双排', '3mm', '3mm', '320mm', '400mm', '3寸', '纵向', '330mm', '5mm', '0.9m²/千张', '5mm', '印刷→模切→品检→包装', 'UV印刷→模切', 'UV柔印', '5mm', '["K","专色"]', '东莞宏达', 'FM-AKS-001', '320mm×400mm', 'UV固化', '无', 'DIE-20250512-018', '双排对称', '左进右出', '5mm', 'PET离型膜', '320mm×0.075mm', '双排', '3mm', '水性胶', '3M', 'ADH-017', '320mm×500m', 0, '12', '6000', '600', '120', '60', 'DK-017', 'BM-017', 'RP-017', '格拉辛', '320mm×500m', '珍珠棉', '瓦楞纸箱', '水性胶', '箱装', '专色棕', 'K:60 Y:40', '/files/SC-017.pdf', '已确认样品', '奥克斯酒类标签标准工艺卡', '王强', '张伟', '李娜', '王强', '赵磊', '孙丽', 1, 4, 2, '2025-04-28 08:00:00', '2025-04-28 08:00:00', 0), +('SC-20250512-018', '康佳', 'CUST-018', '贺卡印刷', 'V1.0', '2025-05-01', 'DOC-KJ-001', '200mm×150mm', '±0.3mm', 'PET白膜0.125mm', 'PET', '单排', '4mm', '4mm', '280mm', '250mm', '3寸', '纵向', '290mm', '5mm', '1.0m²/千张', '5mm', '印刷→烫金→模切→品检→包装', 'UV印刷→烫金→模切', 'UV柔印+烫金', '5mm', '["K","专色金"]', '东莞宏达', 'FM-KJ-001', '280mm×250mm', 'UV固化', '热烫', 'DIE-20250512-019', '单排居中', '左进右出', '5mm', 'PET离型膜', '280mm×0.075mm', '满版', '4mm', '水性胶', '3M', 'ADH-018', '280mm×500m', 0, '8', '4000', '400', '80', '40', 'DK-018', 'BM-018', 'RP-018', '格拉辛', '280mm×500m', '珍珠棉', '瓦楞纸箱', '水性胶', '箱装', 'Pantone 871C', '金色金属墨', '/files/SC-018.pdf', '已确认样品', '康佳贺卡印刷烫金工艺卡', '张伟', '张伟', '李娜', '王强', '赵磊', '孙丽', 1, 2, 2, '2025-05-01 08:00:00', '2025-05-01 08:00:00', 0), +('SC-20250512-019', '熊猫电子', 'CUST-019', '饮料标签', 'V1.0', '2025-05-03', 'DOC-XM-002', '180mm×120mm', '±0.3mm', 'PET白膜0.125mm', 'PET', '双排', '3mm', '3mm', '400mm', '500mm', '3寸', '纵向', '410mm', '5mm', '1.1m²/千张', '5mm', '印刷→模切→品检→包装', 'UV印刷→模切', 'UV柔印', '5mm', '["C","M","Y","K"]', '东莞宏达', 'FM-XM-002', '400mm×500mm', 'UV固化', '无', 'DIE-20250512-020', '双排对称', '左进右出', '5mm', 'PET离型膜', '400mm×0.075mm', '双排', '3mm', '水性胶', '3M', 'ADH-019', '400mm×500m', 0, '10', '5000', '500', '100', '50', 'DK-019', 'BM-019', 'RP-019', '格拉辛', '400mm×500m', '珍珠棉', '瓦楞纸箱', '水性胶', '箱装', '无', 'CMYK四色', '/files/SC-019.pdf', '已确认样品', '熊猫饮料标签标准工艺卡', '李娜', '李娜', '王强', '赵磊', '孙丽', '周杰', 1, 3, 3, '2025-05-03 08:00:00', '2025-05-03 08:00:00', 0), +('SC-20250512-020', '苏泊尔', 'CUST-020', '化妆品标签', 'V1.0', '2025-05-05', 'DOC-SBR-001', '160mm×100mm', '±0.3mm', 'PET白膜0.125mm', 'PET', '双排', '3mm', '3mm', '380mm', '450mm', '3寸', '纵向', '390mm', '5mm', '1.0m²/千张', '5mm', '印刷→模切→品检→包装', 'UV印刷→模切', 'UV柔印', '5mm', '["C","M","Y","K","专色"]', '东莞宏达', 'FM-SBR-001', '380mm×450mm', 'UV固化', '无', 'DIE-20250512-001', '双排对称', '左进右出', '5mm', 'PET离型膜', '380mm×0.075mm', '双排', '3mm', '水性胶', '3M', 'ADH-020', '380mm×500m', 0, '12', '6000', '600', '120', '60', 'DK-020', 'BM-020', 'RP-020', '格拉辛', '380mm×500m', '珍珠棉', '瓦楞纸箱', '水性胶', '箱装', '专色粉', 'M:50 Y:10', '/files/SC-020.pdf', '已确认样品', '苏泊尔化妆品标签标准工艺卡', '王强', '王强', '赵磊', '孙丽', '周杰', '吴芳', 1, 4, 4, '2025-05-05 09:00:00', '2025-05-05 09:00:00', 0); +`; + +content = content.replace('SET FOREIGN_KEY_CHECKS=1;', standardCardSQL + '\nSET FOREIGN_KEY_CHECKS=1;\n'); +fs.writeFileSync(outFile, content, 'utf8'); +console.log('prd_standard_card added successfully'); diff --git a/database/_archive/_gen_part4_remaining.js b/database/_archive/_gen_part4_remaining.js new file mode 100644 index 00000000..360f61f5 --- /dev/null +++ b/database/_archive/_gen_part4_remaining.js @@ -0,0 +1,476 @@ +const fs = require('fs'); +const path = require('path'); + +const outFile = path.join(__dirname, 'seed_part4_dcprint_qc_eqp.sql'); +let sql = ''; + +function append(comment, table, cols, rows) { + sql += `\n-- -----------------------------------------------------------\n-- ${comment}\n-- -----------------------------------------------------------\n`; + sql += `INSERT IGNORE INTO ${table}\n(${cols})\nVALUES\n`; + sql += rows.join(',\n') + ';\n\n'; +} + +// 9. ink_opening_record +append('9. ink_opening_record 油墨开罐记录 (20条)', 'ink_opening_record', + 'record_no, material_id, material_code, material_name, batch_no, label_id, ink_type, open_time, expire_hours, expire_time, remaining_qty, unit, status, operator_id, operator_name, remark, create_time, update_time, deleted', + Array.from({length:20}, (_,i) => { + const n = String(i+1).padStart(3,'0'); + const d = `2025-04-${String(Math.min(i*2+1,30)).padStart(2,'0')}`; + const inkTypes = ['UV','UV','UV','UV','UV','solvent','solvent','solvent','solvent','water','water','UV','UV','UV','UV','solvent','UV','water','UV','solvent']; + const names = ['UV油墨-黑色','UV油墨-白色','UV油墨-红色','UV油墨-蓝色','UV油墨-黄色','溶剂油墨-黑色','溶剂油墨-白色','溶剂油墨-金色','溶剂油墨-银色','水性油墨-黑色','水性油墨-红色','UV油墨-专色红','UV油墨-专色蓝','UV光油-高光','UV光油-哑光','溶剂油墨-绿色','UV油墨-紫色','水性油墨-蓝色','UV油墨-橙色','溶剂油墨-棕色']; + const codes = ['INK-20250512-001','INK-20250512-002','INK-20250512-003','INK-20250512-004','INK-20250512-005','INK-20250512-006','INK-20250512-007','INK-20250512-008','INK-20250512-009','INK-20250512-010','INK-20250512-011','INK-20250512-012','INK-20250512-013','INK-20250512-014','INK-20250512-015','INK-20250512-016','INK-20250512-017','INK-20250512-018','INK-20250512-019','INK-20250512-020']; + const expH = inkTypes[i]==='UV'?168:inkTypes[i]==='solvent'?120:144; + const qty = [5.2000,3.8000,2.5000,1.8000,1.5000,4.2000,3.0000,1.2000,0.8000,2.0000,1.5000,0.6000,0.5000,3.5000,2.8000,1.6000,0.4000,1.0000,0.5000,0.8000][i]; + const opId = (i%8)+2; + const opNames = ['张伟','李娜','王强','刘洋','陈明','赵磊','孙丽','周杰']; + return `('IOR-20250512-${n}', ${i+1}, '${codes[i]}', '${names[i]}', 'BH202504${String(i*2+1).padStart(4,'0')}01', ${i+1}, '${inkTypes[i]}', '${d} 08:30:00', ${expH}, DATE_ADD('${d} 08:30:00', INTERVAL ${expH} HOUR), ${qty.toFixed(4)}, 'kg', 1, ${opId}, '${opNames[i%8]}', '${names[i]}开罐使用', '${d} 08:30:00', '${d} 08:30:00', 0)`; + }) +); + +// 10. ink_usage +append('10. ink_usage 油墨使用记录 (20条)', 'ink_usage', + 'work_order_id, screen_plate_id, ink_id, ink_code, ink_name, usage_qty, unit, usage_date, operator_id, operator_name, remark, create_time, update_time, deleted', + Array.from({length:20}, (_,i) => { + const d = `2025-04-${String(Math.min(i+1,30)).padStart(2,'0')}`; + const names = ['UV油墨-黑色','UV油墨-白色','UV油墨-红色','UV油墨-蓝色','UV油墨-黄色','溶剂油墨-黑色','溶剂油墨-白色','溶剂油墨-金色','溶剂油墨-银色','水性油墨-黑色','水性油墨-红色','UV油墨-专色红','UV油墨-专色蓝','UV光油-高光','UV光油-哑光','溶剂油墨-绿色','UV油墨-紫色','水性油墨-蓝色','UV油墨-橙色','溶剂油墨-棕色']; + const codes = ['INK-20250512-001','INK-20250512-002','INK-20250512-003','INK-20250512-004','INK-20250512-005','INK-20250512-006','INK-20250512-007','INK-20250512-008','INK-20250512-009','INK-20250512-010','INK-20250512-011','INK-20250512-012','INK-20250512-013','INK-20250512-014','INK-20250512-015','INK-20250512-016','INK-20250512-017','INK-20250512-018','INK-20250512-019','INK-20250512-020']; + const qtys = [2.5000,1.8000,1.2000,0.8000,0.6000,2.0000,1.5000,0.5000,0.4000,1.0000,0.8000,0.3000,0.2500,1.8000,1.2000,0.6000,0.2000,0.5000,0.3000,0.4000]; + const opId = (i%8)+2; + const opNames = ['张伟','李娜','王强','刘洋','陈明','赵磊','孙丽','周杰']; + return `(${i+1}, ${i+1}, ${i+1}, '${codes[i]}', '${names[i]}', ${qtys[i].toFixed(4)}, 'kg', '${d} 10:00:00', ${opId}, '${opNames[i%8]}', '工单${String(i+1).padStart(3,'0')}油墨使用', '${d} 10:00:00', '${d} 10:00:00', 0)`; + }) +); + +// 11. ink_mixed_record +append('11. ink_mixed_record 油墨调配记录 (20条)', 'ink_mixed_record', + 'record_no, base_ink_id, base_ink_code, base_ink_name, mix_ratio, color_name, color_code, company_id, company_name, mix_time, operator_id, operator_name, quantity, unit, warehouse_id, location_id, status, expire_time, remark, create_time, update_time, deleted', + Array.from({length:20}, (_,i) => { + const n = String(i+1).padStart(3,'0'); + const d = `2025-04-${String(Math.min(i+1,30)).padStart(2,'0')}`; + const colors = ['美的红','华为蓝','格力灰','比亚迪绿','TCL橙','中兴蓝','广汽银','海信金','小米橙','OPPO绿','咖啡棕','浅灰','深紫','草绿','玫红','天蓝','军绿','珊瑚','藏青','米黄']; + const colorCodes = ['#EF3340','#003DA5','#A7A9AC','#007A33','#F37021','#0072CE','#8A8D8F','#9B7E46','#FF6900','#00A651','#5C4033','#C8C9C7','#4A2060','#7AB648','#DA1884','#7EC8E3','#4B5320','#F88379','#0C2340','#D4C5A9']; + const ratios = ['65:35','70:30','25:75','60:40','55:45','65:35','50:50','45:55','70:30','55:45','60:40','15:85','55:45','40:60','50:50','30:70','35:65','45:55','55:45','25:75']; + const baseIds = [11,12,10,14,13,12,18,17,13,14,19,4,15,14,1,2,14,1,10,3]; + const baseCodes = ['BI-20250512-011','BI-20250512-012','BI-20250512-010','BI-20250512-014','BI-20250512-013','BI-20250512-012','BI-20250512-018','BI-20250512-017','BI-20250512-013','BI-20250512-014','BI-20250512-019','BI-20250512-004','BI-20250512-015','BI-20250512-014','BI-20250512-001','BI-20250512-002','BI-20250512-014','BI-20250512-001','BI-20250512-010','BI-20250512-003']; + const baseNames = ['基础油墨-红色','基础油墨-蓝色','基础油墨-黑色','基础油墨-绿色','基础油墨-橙色','基础油墨-蓝色','基础油墨-银色','基础油墨-金色','基础油墨-橙色','基础油墨-绿色','基础油墨-棕色','基础油墨-黑色','基础油墨-紫色','基础油墨-绿色','基础油墨-品红','基础油墨-青色','基础油墨-绿色','基础油墨-品红','基础油墨-黑色','基础油墨-黄色']; + const qtys = [5.000,4.000,3.500,3.000,4.500,3.800,4.200,3.200,5.500,4.000,2.800,3.500,2.500,3.000,3.200,4.000,2.800,3.500,3.000,4.500]; + const cids = [1,3,2,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]; + const cnames = ['美的集团','华为技术','格力电器','比亚迪','TCL科技','中兴通讯','广汽集团','海信集团','小米科技','OPPO','长虹','创维','联想','海尔','方太','志高','奥克斯','康佳','熊猫电子','苏泊尔']; + const opId = (i%8)+2; + const opNames = ['张伟','李娜','王强','刘洋','陈明','赵磊','孙丽','周杰']; + const wh = i%2===0?9:10; + return `('IMR-20250512-${n}', ${baseIds[i]}, '${baseCodes[i]}', '${baseNames[i]}', '${ratios[i]}', '${colors[i]}', '${colorCodes[i]}', ${cids[i]}, '${cnames[i]}', '${d} 09:00:00', ${opId}, '${opNames[i%8]}', ${qtys[i].toFixed(4)}, 'kg', ${wh}, ${i+1}, 1, DATE_ADD('${d} 09:00:00', INTERVAL 168 HOUR), '${colors[i]}调配-${cnames[i]}项目', '${d} 09:00:00', '${d} 09:00:00', 0)`; + }) +); + +// 12. prd_die_usage_log +append('12. prd_die_usage_log 刀模使用日志 (20条)', 'prd_die_usage_log', + 'die_id, die_code, work_report_id, work_order_id, work_order_no, process_name, impressions, cumulative_after, operator_id, operator_name, equipment_id, usage_date, remark, create_time', + Array.from({length:20}, (_,i) => { + const d = `2025-04-${String(Math.min(i+1,30)).padStart(2,'0')}`; + const dieCodes = ['DIE-20250512-001','DIE-20250512-002','DIE-20250512-003','DIE-20250512-004','DIE-20250512-005','DIE-20250512-006','DIE-20250512-007','DIE-20250512-008','DIE-20250512-009','DIE-20250512-010','DIE-20250512-011','DIE-20250512-012','DIE-20250512-013','DIE-20250512-014','DIE-20250512-015','DIE-20250512-016','DIE-20250512-017','DIE-20250512-018','DIE-20250512-019','DIE-20250512-020']; + const imprs = [5000,6000,8000,3000,2000,1500,10000,5000,8000,15000,6000,3000,4000,8000,1000,8000,12000,2500,1500,10000]; + const cumuls = [35000,42000,68000,25000,15000,8000,95000,38000,55000,120000,45000,22000,30000,78000,5000,60000,88000,18000,10000,72000]; + const procs = ['模切','模切','精密模切','模切','烫金模切','压纹','高速模切','模切','模切','高速模切','模切','烫金模切','模切','模切','压纹','模切','高速模切','模切','烫金模切','模切']; + const opId = (i%8)+2; + const opNames = ['张伟','李娜','王强','刘洋','陈明','赵磊','孙丽','周杰']; + return `(${i+1}, '${dieCodes[i]}', ${i+1}, ${i+1}, 'WO-202504${String(i+1).padStart(2,'0')}-${String(i+1).padStart(3,'0')}', '${procs[i]}', ${imprs[i]}, ${cumuls[i]}, ${opId}, '${opNames[i%8]}', ${i+1}, '${d}', '${dieCodes[i]}使用${imprs[i]}次', '${d} 14:00:00')`; + }) +); + +// 13. prd_die_maintenance +append('13. prd_die_maintenance 刀模维护 (20条)', 'prd_die_maintenance', + 'maintenance_no, die_id, die_code, maintenance_type, impressions_before, impressions_after, maintenance_date, next_maintenance_date, cost, technician_id, technician_name, status, remark, create_time, update_time, deleted', + Array.from({length:20}, (_,i) => { + const n = String(i+1).padStart(3,'0'); + const d = `2025-04-${String(Math.min(i*2+1,30)).padStart(2,'0')}`; + const dieCodes = ['DIE-20250512-001','DIE-20250512-002','DIE-20250512-003','DIE-20250512-004','DIE-20250512-005','DIE-20250512-006','DIE-20250512-007','DIE-20250512-008','DIE-20250512-009','DIE-20250512-010','DIE-20250512-011','DIE-20250512-012','DIE-20250512-013','DIE-20250512-014','DIE-20250512-015','DIE-20250512-016','DIE-20250512-017','DIE-20250512-018','DIE-20250512-019','DIE-20250512-020']; + const types = ['routine','routine','routine','routine','repair','routine','routine','routine','repair','routine','routine','repair','routine','routine','routine','repair','routine','routine','repair','routine']; + const imprB = [30000,36000,60000,20000,12000,6000,80000,32000,50000,100000,38000,18000,25000,65000,3000,52000,75000,15000,8000,62000]; + const imprA = [35000,42000,68000,25000,15000,8000,95000,38000,55000,120000,45000,22000,30000,78000,5000,60000,88000,18000,10000,72000]; + const costs = [800.00,950.00,1200.00,750.00,2500.00,600.00,1500.00,1100.00,3200.00,1800.00,900.00,2800.00,700.00,1300.00,500.00,2600.00,1600.00,850.00,2100.00,1400.00]; + const techId = (i%5)+6; + const techNames = ['陈明','赵磊','孙丽','周杰','吴芳']; + const nextD = `2025-${String(9+(i%3)).padStart(2,'0')}-${String(Math.min(i*3+1,28)).padStart(2,'0')}`; + return `('DM-20250512-${n}', ${i+1}, '${dieCodes[i]}', '${types[i]}', ${imprB[i]}, ${imprA[i]}, '${d}', '${nextD}', ${costs[i].toFixed(2)}, ${techId}, '${techNames[i%5]}', 1, '${dieCodes[i]}${types[i]==='repair'?'修复维护':'例行维护'}', '${d} 16:00:00', '${d} 16:00:00', 0)`; + }) +); + +// 14. prd_die_template +append('14. prd_die_template 刀模模板 (20条)', 'prd_die_template', + 'template_code, template_name, asset_type, layout_type, pieces_per_impression, cumulative_impressions, max_impressions, maintenance_interval, maintenance_count, last_maintenance_impressions, last_maintenance_date, last_used_date, unit_price, die_status, qr_code, template_type, specification, material, max_usage, current_usage, remaining_usage, status, storage_location, purchase_date, supplier_id, remark, create_time, update_time, create_by, deleted', + Array.from({length:20}, (_,i) => { + const n = String(i+1).padStart(3,'0'); + const names = ['圆刀模模板-电池标贴','平刀模模板-空调面板','模切刀模板-手机后盖','圆刀模模板-洗衣机面板','烫金模模板-高端酒标','压纹模模板-化妆品盒','模切刀模板-电子标签','圆刀模模板-汽车内饰','平刀模模板-热收缩膜','模切刀模板-物流标签','圆刀模模板-药品标签','烫金模模板-烟包','平刀模模板-日化标签','模切刀模板-不干胶','压纹模模板-礼品盒','圆刀模模板-食品标签','模切刀模板-电池标签','平刀模模板-酒标','烫金模模板-贺卡','圆刀模模板-饮料标']; + const types = [1,2,3,1,4,5,3,1,2,3,1,4,2,3,5,1,3,2,4,1]; + const specs = ['300mm×200mm','450mm×350mm','180mm×90mm','380mm×280mm','200mm×150mm','250mm×200mm','120mm×60mm','500mm×400mm','600mm×500mm','100mm×50mm','150mm×80mm','300mm×250mm','280mm×180mm','200mm×120mm','350mm×300mm','220mm×150mm','160mm×70mm','250mm×200mm','180mm×130mm','320mm×220mm']; + const mats = ['高速钢','合金钢','硬质合金','高速钢','铜合金','合金钢','硬质合金','高速钢','合金钢','硬质合金','高速钢','铜合金','合金钢','硬质合金','合金钢','高速钢','硬质合金','合金钢','铜合金','高速钢']; + const maxImp = [100000,80000,120000,90000,60000,50000,150000,70000,80000,200000,110000,50000,85000,130000,40000,95000,140000,75000,45000,105000]; + const cumImp = [35000,42000,68000,25000,15000,8000,95000,38000,55000,120000,45000,22000,30000,78000,5000,60000,88000,18000,10000,72000]; + const prices = [3500.00,4200.00,5800.00,3800.00,6500.00,5200.00,4500.00,4800.00,5500.00,3200.00,3600.00,7200.00,4000.00,4300.00,5800.00,3700.00,4600.00,4100.00,6800.00,3900.00]; + const d = `2025-04-${String(Math.min(i+1,30)).padStart(2,'0')}`; + const purD = `2024-${String(6+(i%6)).padStart(2,'0')}-${String(Math.min(i*2+1,28)).padStart(2,'0')}`; + return `('DT-20250512-${n}', '${names[i]}', 'die', 'single_row', 1, ${cumImp[i]}, ${maxImp[i]}, 30000, ${Math.floor(cumImp[i]/30000)}, ${Math.floor(cumImp[i]/30000)*30000}, '${d}', '${d}', ${prices[i].toFixed(2)}, 'available', 'QR-DT-${n}', ${types[i]}, '${specs[i]}', '${mats[i]}', ${maxImp[i]}, ${cumImp[i]}, ${maxImp[i]-cumImp[i]}, 1, '刀模仓-${String.fromCharCode(65+Math.floor(i/2))}区', '${purD}', 14, '${names[i]}标准模板', '${d} 08:00:00', '${d} 08:00:00', 2, 0)`; + }) +); + +// 15. screen_plate_history +append('15. screen_plate_history 网版历史 (20条)', 'screen_plate_history', + 'screen_plate_id, action, tension_value, life_increment, remark, operator_id, operator_name, created_at', + Array.from({length:20}, (_,i) => { + const actions = ['exposure','use','clean','reclaim','use','exposure','use','clean','use','exposure','use','reclaim','use','clean','exposure','use','clean','use','reclaim','use']; + const tensions = [25.50,24.80,24.20,0,23.50,26.80,26.20,25.50,24.80,21.80,21.20,0,20.50,19.80,28.80,28.20,27.50,26.30,0,25.10]; + const increments = [0,500,0,0,800,0,600,0,700,0,400,0,500,0,0,600,0,800,0,500]; + const d = `2025-04-${String(Math.min(i+1,30)).padStart(2,'0')}`; + const opId = (i%8)+2; + const opNames = ['张伟','李娜','王强','刘洋','陈明','赵磊','孙丽','周杰']; + const remarks = ['网版曝光制版','网版上机使用','网版清洗保养','网版回收翻新','网版上机使用','网版曝光制版','网版上机使用','网版清洗保养','网版上机使用','网版曝光制版','网版上机使用','网版回收翻新','网版上机使用','网版清洗保养','网版曝光制版','网版上机使用','网版清洗保养','网版上机使用','网版回收翻新','网版上机使用']; + return `(${i+1}, '${actions[i]}', ${tensions[i].toFixed(2)}, ${increments[i]}, '${remarks[i]}', ${opId}, '${opNames[i%8]}', '${d} ${String(8+i).padStart(2,'0')}:00:00')`; + }) +); + +// ============================================================ +// 二、质量管理模块 +// ============================================================ +sql += '\n\n-- ============================================================\n-- 二、质量管理模块\n-- ============================================================\n'; + +// 16. qc_incoming_inspection +append('16. qc_incoming_inspection 来料检验 (20条)', 'qc_incoming_inspection', + 'inspection_no, inspection_date, supplier_id, supplier_name, material_id, material_code, material_name, specification, batch_no, quantity, unit, inspection_type, inspection_result, qualified_qty, unqualified_qty, inspector_id, inspector_name, remark, create_time, update_time, create_by, deleted', + Array.from({length:20}, (_,i) => { + const n = String(i+1).padStart(3,'0'); + const d = `2025-04-${String(Math.min(i+1,30)).padStart(2,'0')}`; + const sids = [1,2,3,4,5,6,3,4,7,8,9,10,1,2,3,4,5,6,3,4]; + const snames = ['东莞鑫达薄膜材料','佛山顺达PVC材料','深圳精诚油墨化工','广州永盛化工','东莞宏达丝网材料','上海紫江特种油墨','深圳精诚油墨化工','广州永盛化工','苏州雅特不干胶材料','深圳金太阳保护膜','东莞鑫达薄膜材料','佛山顺达PVC材料','东莞鑫达薄膜材料','佛山顺达PVC材料','深圳精诚油墨化工','广州永盛化工','东莞宏达丝网材料','上海紫江特种油墨','深圳精诚油墨化工','广州永盛化工']; + const mids = [1,3,8,10,14,9,16,17,5,20,2,4,6,7,11,12,15,13,18,19]; + const mcodes = ['PET-001','PVC-001','UV-BK','SOL-BK','SCREEN-300','UV-WH','THINNER','UV-VARNISH','BOPP-001','FILM-SHRINK','PET-002','PVC-002','PE-001','PAPER-001','AG-PASTE','DIE-001','LABEL-ELEC','LABEL-AC','LABEL-WASH','LABEL-PHONE']; + const mnames = ['PET透明膜0.1mm','PVC透明膜0.15mm','UV油墨-黑色','溶剂型油墨-黑色','丝印网版-300目','UV油墨-白色','稀释剂-标准型','UV光油-高光','BOPP透明膜0.08mm','热收缩膜','PET白膜0.125mm','PVC白膜0.2mm','PE膜0.05mm','铜版纸80g','导电银浆-高导电','模切刀模-平板','电子标签','空调面板标签','洗衣机面板标签','手机后盖标签']; + const specs = ['1000mm×500m/卷','1000mm×400m/卷','UV固化型 粘度25-30s','溶剂型 粘度30-35s','300目 铝合金框架','UV固化型 粘度20-25s','标准型 沸点80-120°C','UV光油 固含量≥95%','800mm×600m/卷','通用型','1200mm×500m/卷','1200mm×400m/卷','600mm×400m/卷','大度889mm×1194mm','导电型 固含70%','平板式 精度±0.05mm','60mm×30mm','150mm×80mm','200mm×120mm','80mm×45mm']; + const qtys = [1000.0000,600.0000,120.0000,80.0000,15.0000,100.0000,300.0000,50.0000,600.0000,200.0000,800.0000,750.0000,400.0000,500.0000,40.0000,10.0000,50000.0000,30000.0000,20000.0000,100000.0000]; + const results = i===7||i===15?0:1; + const qQty = results===1?qtys[i]:qtys[i]*0.85; + const uQty = qtys[i]-qQty; + const inspId = (i%5)+4; + const inspNames = ['王强','刘洋','陈明','赵磊','孙丽']; + return `('QCI-20250512-${n}', '${d}', ${sids[i]}, '${snames[i]}', ${mids[i]}, '${mcodes[i]}', '${mnames[i]}', '${specs[i]}', 'BH202504${String(i+1).padStart(4,'0')}01', ${qtys[i].toFixed(4)}, '${i<8?"kg":"件"}', 1, ${results}, ${qQty.toFixed(4)}, ${uQty.toFixed(4)}, ${inspId}, '${inspNames[i%5]}', '${results===1?"来料检验合格":"来料检验部分不合格"}', '${d} 14:00:00', '${d} 14:00:00', ${inspId}, 0)`; + }) +); + +// 17. qc_incoming_inspection_item +append('17. qc_incoming_inspection_item 来料检验明细 (40条)', 'qc_incoming_inspection_item', + 'inspection_id, item_name, standard, actual_value, result, remark, create_time, deleted', + Array.from({length:40}, (_,i) => { + const inspId = Math.floor(i/2)+1; + const items = ['外观检查','尺寸测量','粘度测试','固含量检测','色差检测','pH值检测','包装完整性','标签核对']; + const standards = ['无破损、无污染','公差±0.5mm','25-30s','≥60%','ΔE≤2.0','6.0-8.0','包装完好无破损','标签信息完整准确']; + const actuals = ['合格','合格','27s','62%','ΔE=1.2','7.2','完好','完整','合格','合格','32s','58%','ΔE=0.8','6.8','完好','完整','合格','合格','28s','61%','ΔE=1.5','7.0','完好','完整','合格','合格','33s','59%','ΔE=1.8','7.5','完好','完整','合格','合格','26s','63%','ΔE=0.5','6.5','完好','完整','合格','合格','29s','60%','ΔE=2.5','8.2','轻微破损','信息模糊','合格','合格','31s','57%','ΔE=1.0','7.1','完好','完整','合格','合格','27s','64%','ΔE=0.3','6.9','完好','完整','合格','合格','30s','61%','ΔE=1.3','7.3','完好','完整','合格','合格','28s','62%','ΔE=0.9','7.0','完好','完整','合格','合格','26s','65%','ΔE=0.7','6.8','完好','完整','合格','合格','29s','60%','ΔE=1.1','7.2','完好','完整','合格','合格','32s','58%','ΔE=1.6','7.4','完好','完整','合格','合格','27s','63%','ΔE=0.4','6.7','完好','完整','合格','合格','25s','66%','ΔE=0.6','6.6','完好','完整','合格','合格','30s','61%','ΔE=1.4','7.1','完好','完整','合格','合格','28s','59%','ΔE=2.8','8.5','破损','信息缺失']; + const idx = i % 8; + const d = `2025-04-${String(Math.min(Math.floor(i/2)+1,30)).padStart(2,'0')}`; + const result = (i===14||i===15||i===30||i===31)?0:1; + return `(${inspId}, '${items[idx]}', '${standards[idx]}', '${actuals[i]}', ${result}, '${result===1?"符合标准":"不符合标准"}', '${d} 14:30:00', 0)`; + }) +); + +// 18. qc_process_inspection +append('18. qc_process_inspection 过程检验 (20条)', 'qc_process_inspection', + 'inspection_no, inspection_date, work_order_id, work_order_no, process_name, product_id, product_code, product_name, inspection_qty, qualified_qty, unqualified_qty, inspection_result, inspector_name, remark, create_time, update_time, create_by, deleted', + Array.from({length:20}, (_,i) => { + const n = String(i+1).padStart(3,'0'); + const d = `2025-04-${String(Math.min(i+1,30)).padStart(2,'0')}`; + const procs = ['印刷','印刷','模切','印刷','烫金','印刷','模切','印刷','印刷','模切','印刷','烫金','印刷','模切','压纹','印刷','模切','印刷','烫金','模切']; + const pids = [22,23,24,25,19,20,22,23,24,25,19,20,22,23,24,25,19,20,22,23]; + const pcodes = ['LABEL-AC','LABEL-WASH','LABEL-PHONE','LABEL-AUTO','LABEL-ELEC','FILM-SHRINK','LABEL-AC','LABEL-WASH','LABEL-PHONE','LABEL-AUTO','LABEL-ELEC','FILM-SHRINK','LABEL-AC','LABEL-WASH','LABEL-PHONE','LABEL-AUTO','LABEL-ELEC','FILM-SHRINK','LABEL-AC','LABEL-WASH']; + const pnames = ['空调面板标签','洗衣机面板标签','手机后盖标签','汽车内饰标签','电子标签','热收缩膜','空调面板标签','洗衣机面板标签','手机后盖标签','汽车内饰标签','电子标签','热收缩膜','空调面板标签','洗衣机面板标签','手机后盖标签','汽车内饰标签','电子标签','热收缩膜','空调面板标签','洗衣机面板标签']; + const iq = [5000,3000,8000,4000,2000,5000,6000,2500,7000,3000,4000,2000,5500,3500,9000,4500,2500,6000,5000,3000]; + const results = i===5||i===12?0:1; + const qq = results===1?iq[i]:Math.floor(iq[i]*0.92); + const uq = iq[i]-qq; + const inspNames = ['王强','刘洋','陈明','赵磊','孙丽']; + return `('QPI-20250512-${n}', '${d}', ${i+1}, 'WO-202504${String(i+1).padStart(2,'0')}-${String(i+1).padStart(3,'0')}', '${procs[i]}', ${pids[i]}, '${pcodes[i]}', '${pnames[i]}', ${iq[i].toFixed(4)}, ${qq.toFixed(4)}, ${uq.toFixed(4)}, ${results}, '${inspNames[i%5]}', '${results===1?"过程检验合格":"过程检验发现不良"}', '${d} 15:00:00', '${d} 15:00:00', ${i%5+4}, 0)`; + }) +); + +// 19. qc_final_inspection +append('19. qc_final_inspection 终检 (20条)', 'qc_final_inspection', + 'inspection_no, inspection_date, work_order_id, work_order_no, product_id, product_code, product_name, batch_no, inspection_qty, qualified_qty, unqualified_qty, inspection_result, inspector_name, remark, create_time, update_time, create_by, deleted', + Array.from({length:20}, (_,i) => { + const n = String(i+1).padStart(3,'0'); + const d = `2025-04-${String(Math.min(i+2,30)).padStart(2,'0')}`; + const pids = [22,23,24,25,19,20,22,23,24,25,19,20,22,23,24,25,19,20,22,23]; + const pcodes = ['LABEL-AC','LABEL-WASH','LABEL-PHONE','LABEL-AUTO','LABEL-ELEC','FILM-SHRINK','LABEL-AC','LABEL-WASH','LABEL-PHONE','LABEL-AUTO','LABEL-ELEC','FILM-SHRINK','LABEL-AC','LABEL-WASH','LABEL-PHONE','LABEL-AUTO','LABEL-ELEC','FILM-SHRINK','LABEL-AC','LABEL-WASH']; + const pnames = ['空调面板标签','洗衣机面板标签','手机后盖标签','汽车内饰标签','电子标签','热收缩膜','空调面板标签','洗衣机面板标签','手机后盖标签','汽车内饰标签','电子标签','热收缩膜','空调面板标签','洗衣机面板标签','手机后盖标签','汽车内饰标签','电子标签','热收缩膜','空调面板标签','洗衣机面板标签']; + const iq = [5000,3000,8000,4000,2000,5000,6000,2500,7000,3000,4000,2000,5500,3500,9000,4500,2500,6000,5000,3000]; + const results = i===8?0:1; + const qq = results===1?iq[i]:Math.floor(iq[i]*0.95); + const uq = iq[i]-qq; + const inspNames = ['刘洋','陈明','赵磊','孙丽','周杰']; + return `('QFI-20250512-${n}', '${d}', ${i+1}, 'WO-202504${String(i+1).padStart(2,'0')}-${String(i+1).padStart(3,'0')}', ${pids[i]}, '${pcodes[i]}', '${pnames[i]}', 'FB202504${String(i+1).padStart(4,'0')}01', ${iq[i].toFixed(4)}, ${qq.toFixed(4)}, ${uq.toFixed(4)}, ${results}, '${inspNames[i%5]}', '${results===1?"终检合格放行":"终检部分不合格需返工"}', '${d} 16:00:00', '${d} 16:00:00', ${i%5+4}, 0)`; + }) +); + +// 20. qc_inspection +append('20. qc_inspection 通用检验 (20条)', 'qc_inspection', + 'inspection_no, inspection_type, source_type, source_no, material_id, batch_no, inspection_qty, qualified_qty, unqualified_qty, inspection_result, inspector, inspection_date, remark, create_time, update_time, deleted', + Array.from({length:20}, (_,i) => { + const n = String(i+1).padStart(3,'0'); + const d = `2025-04-${String(Math.min(i+1,30)).padStart(2,'0')}`; + const types = [1,1,2,1,3,1,2,1,1,3,1,2,1,1,3,2,1,1,2,3]; + const srcTypes = ['incoming','incoming','process','incoming','final','incoming','process','incoming','incoming','final','incoming','process','incoming','incoming','final','process','incoming','incoming','process','final']; + const srcNos = [`QCI-20250512-${String(Math.min(i+1,20)).padStart(3,'0')}`,`QCI-20250512-${String(Math.min(i+1,20)).padStart(3,'0')}`,`QPI-20250512-${String(Math.min(i+1,20)).padStart(3,'0')}`,`QCI-20250512-${String(Math.min(i+1,20)).padStart(3,'0')}`,`QFI-20250512-${String(Math.min(i+1,20)).padStart(3,'0')}`,`QCI-20250512-${String(Math.min(i+1,20)).padStart(3,'0')}`,`QPI-20250512-${String(Math.min(i+1,20)).padStart(3,'0')}`,`QCI-20250512-${String(Math.min(i+1,20)).padStart(3,'0')}`,`QCI-20250512-${String(Math.min(i+1,20)).padStart(3,'0')}`,`QFI-20250512-${String(Math.min(i+1,20)).padStart(3,'0')}`,`QCI-20250512-${String(Math.min(i+1,20)).padStart(3,'0')}`,`QPI-20250512-${String(Math.min(i+1,20)).padStart(3,'0')}`,`QCI-20250512-${String(Math.min(i+1,20)).padStart(3,'0')}`,`QCI-20250512-${String(Math.min(i+1,20)).padStart(3,'0')}`,`QFI-20250512-${String(Math.min(i+1,20)).padStart(3,'0')}`,`QPI-20250512-${String(Math.min(i+1,20)).padStart(3,'0')}`,`QCI-20250512-${String(Math.min(i+1,20)).padStart(3,'0')}`,`QCI-20250512-${String(Math.min(i+1,20)).padStart(3,'0')}`,`QPI-20250512-${String(Math.min(i+1,20)).padStart(3,'0')}`,`QFI-20250512-${String(Math.min(i+1,20)).padStart(3,'0')}`]; + const mids = [1,3,8,10,14,9,16,17,5,20,2,4,6,7,11,12,15,13,18,19]; + const iq = [1000,600,120,80,15,100,300,50,600,200,800,750,400,500,40,10,50000,30000,20000,100000]; + const results = i===7||i===15?0:1; + const qq = results===1?iq[i]:Math.floor(iq[i]*0.85); + const inspNames = ['王强','刘洋','陈明','赵磊','孙丽']; + return `('QI-20250512-${n}', ${types[i]}, '${srcTypes[i]}', '${srcNos[i]}', ${mids[i]}, 'BH202504${String(i+1).padStart(4,'0')}01', ${iq[i].toFixed(4)}, ${qq.toFixed(4)}, ${(iq[i]-qq).toFixed(4)}, ${results}, '${inspNames[i%5]}', '${d}', '${results===1?"检验合格":"检验不合格"}', '${d} 14:00:00', '${d} 14:00:00', 0)`; + }) +); + +// 21. qc_unqualified +append('21. qc_unqualified 不合格记录 (20条)', 'qc_unqualified', + 'unqualified_no, inspection_id, source_type, source_no, material_id, material_name, quantity, defect_type, defect_desc, handle_type, handle_result, handler, handle_date, remark, create_time, update_time, deleted', + Array.from({length:20}, (_,i) => { + const n = String(i+1).padStart(3,'0'); + const d = `2025-04-${String(Math.min(i+3,30)).padStart(2,'0')}`; + const srcTypes = ['incoming','incoming','process','incoming','final','incoming','process','incoming','incoming','final','incoming','process','incoming','incoming','final','process','incoming','incoming','process','final']; + const srcNos = [`QCI-20250512-008`,`QCI-20250512-016`,`QPI-20250512-006`,`QCI-20250512-003`,`QFI-20250512-009`,`QCI-20250512-005`,`QPI-20250512-013`,`QCI-20250512-002`,`QCI-20250512-007`,`QFI-20250512-010`,`QCI-20250512-001`,`QPI-20250512-004`,`QCI-20250512-011`,`QCI-20250512-014`,`QFI-20250512-015`,`QPI-20250512-017`,`QCI-20250512-019`,`QCI-20250512-020`,`QPI-20250512-018`,`QFI-20250512-012`]; + const mids = [8,10,8,8,14,9,16,17,5,20,1,4,6,7,11,12,15,13,18,19]; + const mnames = ['UV油墨-黑色','溶剂型油墨-黑色','UV油墨-黑色','UV油墨-黑色','丝印网版-300目','UV油墨-白色','稀释剂-标准型','UV光油-高光','BOPP透明膜0.08mm','热收缩膜','PET透明膜0.1mm','PVC白膜0.2mm','PE膜0.05mm','铜版纸80g','导电银浆-高导电','模切刀模-平板','电子标签','空调面板标签','洗衣机面板标签','手机后盖标签']; + const qtys = [18.0000,12.0000,9.6000,5.4000,2.2500,15.0000,45.0000,7.5000,90.0000,30.0000,120.0000,112.5000,60.0000,75.0000,6.0000,1.5000,4000.0000,4500.0000,1600.0000,5000.0000]; + const defectTypes = ['外观不良','尺寸偏差','色差超标','粘度异常','包装破损','性能不达标','杂质超标','固含量不足','厚度偏差','收缩率异常','表面划伤','厚度不均','密封不良','克重偏差','导电率偏低','精度不足','印刷偏位','色差超标','套印不准','模切偏差']; + const handleTypes = [2,1,3,2,1,2,1,2,1,3,1,2,1,2,3,1,2,1,3,2]; + const handleResults = [1,1,2,1,1,1,1,2,1,2,1,1,1,1,2,1,1,1,2,1]; + const handlers = ['王强','刘洋','陈明','赵磊','孙丽']; + return `('UNQ-20250512-${n}', ${i+1}, '${srcTypes[i]}', '${srcNos[i]}', ${mids[i]}, '${mnames[i]}', ${qtys[i].toFixed(4)}, '${defectTypes[i]}', '${mnames[i]}${defectTypes[i]}不合格', ${handleTypes[i]}, ${handleResults[i]}, '${handlers[i%5]}', '${d}', '${defectTypes[i]}处理', '${d} 16:00:00', '${d} 16:00:00', 0)`; + }) +); + +// 22. qc_unqualified_handle +append('22. qc_unqualified_handle 不合格处理 (20条)', 'qc_unqualified_handle', + 'handle_no, inspection_id, material_id, material_code, material_name, unqualified_qty, handle_type, handle_status, responsible_dept, responsible_person, handle_result, cost_amount, remark, create_time, update_time, create_by, deleted', + Array.from({length:20}, (_,i) => { + const n = String(i+1).padStart(3,'0'); + const d = `2025-04-${String(Math.min(i+4,30)).padStart(2,'0')}`; + const mids = [8,10,8,8,14,9,16,17,5,20,1,4,6,7,11,12,15,13,18,19]; + const mcodes = ['UV-BK','SOL-BK','UV-BK','UV-BK','SCREEN-300','UV-WH','THINNER','UV-VARNISH','BOPP-001','FILM-SHRINK','PET-001','PVC-002','PE-001','PAPER-001','AG-PASTE','DIE-001','LABEL-ELEC','LABEL-AC','LABEL-WASH','LABEL-PHONE']; + const mnames = ['UV油墨-黑色','溶剂型油墨-黑色','UV油墨-黑色','UV油墨-黑色','丝印网版-300目','UV油墨-白色','稀释剂-标准型','UV光油-高光','BOPP透明膜0.08mm','热收缩膜','PET透明膜0.1mm','PVC白膜0.2mm','PE膜0.05mm','铜版纸80g','导电银浆-高导电','模切刀模-平板','电子标签','空调面板标签','洗衣机面板标签','手机后盖标签']; + const qtys = [18.0000,12.0000,9.6000,5.4000,2.2500,15.0000,45.0000,7.5000,90.0000,30.0000,120.0000,112.5000,60.0000,75.0000,6.0000,1.5000,4000.0000,4500.0000,1600.0000,5000.0000]; + const hTypes = [2,1,3,2,1,2,1,2,1,3,1,2,1,2,3,1,2,1,3,2]; + const depts = ['采购部','品质部','生产部','采购部','品质部','采购部','品质部','采购部','品质部','生产部','采购部','品质部','采购部','品质部','生产部','品质部','生产部','品质部','生产部','品质部']; + const persons = ['张伟','李娜','王强','刘洋','陈明','赵磊','孙丽','周杰','吴芳','张伟','李娜','王强','刘洋','陈明','赵磊','孙丽','周杰','吴芳','张伟','李娜']; + const costs = [2520.0000,1440.0000,1344.0000,648.0000,1012.5000,2025.0000,1350.0000,975.0000,1800.0000,690.0000,3000.0000,2531.2500,1200.0000,1500.0000,4200.0000,750.0000,6000.0000,5850.0000,1920.0000,3250.0000]; + const results = ['退货处理-供应商承担','让步接收-降级使用','返工处理-重新印刷','退货处理-供应商承担','让步接收-降级使用','退货处理-供应商承担','让步接收-降级使用','退货处理-供应商承担','让步接收-降级使用','返工处理-重新加工','让步接收-降级使用','退货处理-供应商承担','让步接收-降级使用','退货处理-供应商承担','返工处理-重新加工','让步接收-降级使用','退货处理-供应商承担','让步接收-降级使用','返工处理-重新加工','退货处理-供应商承担']; + return `('UH-20250512-${n}', ${i+1}, ${mids[i]}, '${mcodes[i]}', '${mnames[i]}', ${qtys[i].toFixed(4)}, ${hTypes[i]}, 1, '${depts[i]}', '${persons[i]}', '${results[i]}', ${costs[i].toFixed(4)}, '${mnames[i]}不合格处理完成', '${d} 17:00:00', '${d} 17:00:00', ${i%5+4}, 0)`; + }) +); + +// 23. qms_complaint +append('23. qms_complaint 客户投诉 (20条)', 'qms_complaint', + 'complaint_no, customer_id, customer_name, order_no, product_code, product_name, complaint_type, complaint_level, defect_desc, defect_qty, total_qty, defect_rate, reporter, report_time, handler, contain_action, root_cause, corrective_action, preventive_action, verify_result, verifier, verify_time, status, close_time, remark, create_time, update_time, create_by, deleted', + Array.from({length:20}, (_,i) => { + const n = String(i+1).padStart(3,'0'); + const d = `2025-04-${String(Math.min(i+5,30)).padStart(2,'0')}`; + const cids = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]; + const cnames = ['美的集团','格力电器','华为技术','比亚迪','TCL科技','中兴通讯','广汽集团','海信集团','小米科技','OPPO','长虹','创维','联想','海尔','方太','志高','奥克斯','康佳','熊猫电子','苏泊尔']; + const orders = [`SO-20250401-001`,`SO-20250402-002`,`SO-20250403-003`,`SO-20250405-004`,`SO-20250407-005`,`SO-20250408-006`,`SO-20250410-007`,`SO-20250411-008`,`SO-20250414-009`,`SO-20250415-010`,`SO-20250417-011`,`SO-20250418-012`,`SO-20250421-013`,`SO-20250422-014`,`SO-20250424-015`,`SO-20250425-016`,`SO-20250428-017`,`SO-20250502-018`,`SO-20250505-019`,`SO-20250508-020`]; + const pcodes = ['LABEL-AC','LABEL-WASH','LABEL-PHONE','LABEL-AUTO','LABEL-ELEC','LABEL-ELEC','LABEL-AUTO','LABEL-AC','LABEL-PHONE','LABEL-PHONE','LABEL-ELEC','LABEL-WASH','LABEL-ELEC','LABEL-AC','LABEL-ELEC','LABEL-PHONE','LABEL-WASH','LABEL-AC','LABEL-AUTO','LABEL-WASH']; + const pnames = ['空调面板标签','洗衣机面板标签','手机后盖标签','汽车内饰标签','电子标签','电子标签','汽车内饰标签','空调面板标签','手机后盖标签','手机后盖标签','电子标签','洗衣机面板标签','电子标签','空调面板标签','电子标签','手机后盖标签','洗衣机面板标签','空调面板标签','汽车内饰标签','洗衣机面板标签']; + const types = ['quality','quality','quality','delivery','quality','quality','quality','quality','quality','quality','quality','quality','quality','quality','quality','quality','quality','quality','quality','quality']; + const levels = ['B','B','A','C','B','B','C','B','A','B','C','B','C','B','B','A','B','C','B','B']; + const descs = ['色差偏大','套印不准','模切偏位','交期延迟1天','印刷模糊','色差超标','烫金脱落','UV光油不均匀','印刷偏位','色差偏大','套印不准','模切毛边','印刷模糊','色差偏大','粘性不足','模切偏位','套印不准','UV光油脱落','色差偏大','模切毛边']; + const dqs = [200,150,500,0,100,80,50,120,300,100,60,80,40,100,50,200,80,60,100,80]; + const tqs = [50000,30000,100000,0,15000,20000,45000,55000,120000,90000,18000,25000,5000,40000,5000,80000,20000,60000,45000,25000]; + const rates = dqs.map((d,j)=>tqs[j]===0?0:((d/tqs[j])*100).toFixed(2)); + const handlers = ['王强','刘洋','陈明','赵磊','孙丽']; + const statuses = i<15?3:i<18?2:1; + return `('CMP-20250512-${n}', ${cids[i]}, '${cnames[i]}', '${orders[i]}', '${pcodes[i]}', '${pnames[i]}', '${types[i]}', '${levels[i]}', '${descs[i]}', ${dqs[i]}, ${tqs[i]}, ${rates[i]}, '${handlers[i%5]}', '${d} 09:00:00', '${handlers[(i+2)%5]}', '隔离库存+暂停发货', '油墨配比偏差/设备参数漂移', '调整油墨配方+校准设备', '增加首件确认频次+加强巡检', ${statuses===3?`'通过'`:`'待验证'`}, '${handlers[(i+3)%5]}', ${statuses===3?`'${d} 16:00:00'`:'NULL'}, ${statuses}, ${statuses===3?`'${d} 17:00:00'`:'NULL'}, '${cnames[i]}${descs[i]}投诉', '${d} 09:00:00', '${d} 09:00:00', ${i%5+4}, 0)`; + }) +); + +// 24. qms_lab_test +append('24. qms_lab_test 实验室测试 (20条)', 'qms_lab_test', + 'test_no, test_type, product_id, product_code, product_name, batch_no, sample_source, test_items, test_result, overall_result, tester, test_time, reviewer, review_time, equipment_used, test_env, remark, create_time, update_time, create_by, deleted', + Array.from({length:20}, (_,i) => { + const n = String(i+1).padStart(3,'0'); + const d = `2025-04-${String(Math.min(i+1,30)).padStart(2,'0')}`; + const types = ['adhesion','abrasion','color','environmental','adhesion','abrasion','color','environmental','adhesion','abrasion','color','environmental','adhesion','abrasion','color','environmental','adhesion','abrasion','color','environmental']; + const pids = [22,23,24,25,19,20,22,23,24,25,19,20,22,23,24,25,19,20,22,23]; + const pcodes = ['LABEL-AC','LABEL-WASH','LABEL-PHONE','LABEL-AUTO','LABEL-ELEC','FILM-SHRINK','LABEL-AC','LABEL-WASH','LABEL-PHONE','LABEL-AUTO','LABEL-ELEC','FILM-SHRINK','LABEL-AC','LABEL-WASH','LABEL-PHONE','LABEL-AUTO','LABEL-ELEC','FILM-SHRINK','LABEL-AC','LABEL-WASH']; + const pnames = ['空调面板标签','洗衣机面板标签','手机后盖标签','汽车内饰标签','电子标签','热收缩膜','空调面板标签','洗衣机面板标签','手机后盖标签','汽车内饰标签','电子标签','热收缩膜','空调面板标签','洗衣机面板标签','手机后盖标签','汽车内饰标签','电子标签','热收缩膜','空调面板标签','洗衣机面板标签']; + const sources = ['production','production','production','supplier','production','production','production','supplier','production','production','production','supplier','production','production','production','supplier','production','production','production','supplier']; + const items = ['附着力测试(百格法),耐候性测试','耐磨测试(Taber),耐刮擦测试','色差检测(分光仪),光泽度测试','RoHS检测,REACH检测','附着力测试(百格法),耐高温测试','收缩率测试,热封强度测试','附着力测试(百格法),耐候性测试','耐磨测试(Taber),耐刮擦测试','色差检测(分光仪),光泽度测试','RoHS检测,REACH检测','附着力测试(百格法),耐高温测试','收缩率测试,热封强度测试','附着力测试(百格法),耐候性测试','耐磨测试(Taber),耐刮擦测试','色差检测(分光仪),光泽度测试','RoHS检测,REACH检测','附着力测试(百格法),耐高温测试','收缩率测试,热封强度测试','附着力测试(百格法),耐候性测试','耐磨测试(Taber),耐刮擦测试']; + const results = ['附着力4B,耐候500h合格','耐磨500次合格,耐刮擦4级','ΔE=0.8,光泽85°','RoHS合格,REACH合格','附着力5B,耐高温120°C合格','收缩率65%,热封强度2.5N','附着力4B,耐候500h合格','耐磨500次合格,耐刮擦4级','ΔE=1.2,光泽82°','RoHS合格,REACH合格','附着力5B,耐高温120°C合格','收缩率68%,热封强度2.8N','附着力4B,耐候500h合格','耐磨500次合格,耐刮擦4级','ΔE=0.5,光泽88°','RoHS合格,REACH合格','附着力5B,耐高温120°C合格','收缩率70%,热封强度3.0N','附着力4B,耐候500h合格','耐磨500次合格,耐刮擦4级']; + const overalls = i===8?'conditional':'pass'; + const testers = ['王强','刘洋','陈明','赵磊','孙丽']; + return `('LT-20250512-${n}', '${types[i]}', ${pids[i]}, '${pcodes[i]}', '${pnames[i]}', 'FB202504${String(i+1).padStart(4,'0')}01', '${sources[i]}', '${items[i]}', '${results[i]}', '${overalls}', '${testers[i%5]}', '${d} 10:00:00', '${testers[(i+1)%5]}', '${d} 15:00:00', '百格刀,Taber磨耗仪,分光仪,X荧光仪', '温度23±2°C,湿度50±5%RH', '${pnames[i]}${types[i]}测试${overalls==='pass'?'合格':'有条件合格'}', '${d} 10:00:00', '${d} 15:00:00', ${i%5+4}, 0)`; + }) +); + +// 25. qms_sgs_cert +append('25. qms_sgs_cert SGS认证 (20条)', 'qms_sgs_cert', + 'cert_no, material_id, material_code, material_name, supplier_id, supplier_name, cert_type, test_items, test_result, test_report_no, test_org, issue_date, expire_date, status, file_url, remark, create_time, update_time, create_by, deleted', + Array.from({length:20}, (_,i) => { + const n = String(i+1).padStart(3,'0'); + const d = `2025-04-${String(Math.min(i+1,30)).padStart(2,'0')}`; + const mids = [1,3,5,8,10,14,16,17,2,4,6,9,11,12,13,15,18,19,20,7]; + const mcodes = ['PET-001','PVC-001','BOPP-001','UV-BK','SOL-BK','SCREEN-300','THINNER','UV-VARNISH','PET-002','PVC-002','PE-001','UV-WH','AG-PASTE','DIE-001','LABEL-ELEC','LABEL-AC','LABEL-WASH','LABEL-PHONE','FILM-SHRINK','PAPER-001']; + const mnames = ['PET透明膜0.1mm','PVC透明膜0.15mm','BOPP透明膜0.08mm','UV油墨-黑色','溶剂型油墨-黑色','丝印网版-300目','稀释剂-标准型','UV光油-高光','PET白膜0.125mm','PVC白膜0.2mm','PE膜0.05mm','UV油墨-白色','导电银浆-高导电','模切刀模-平板','电子标签','空调面板标签','洗衣机面板标签','手机后盖标签','热收缩膜','铜版纸80g']; + const sids = [1,2,5,3,4,5,4,3,1,2,6,6,7,5,8,1,2,3,4,9]; + const snames = ['东莞鑫达薄膜材料','佛山顺达PVC材料','东莞宏达丝网材料','深圳精诚油墨化工','广州永盛化工','上海紫江特种油墨','苏州雅特不干胶材料','深圳金太阳保护膜','东莞鑫达薄膜材料']; + const cTypes = ['RoHS','RoHS','RoHS','RoHS','RoHS','RoHS','MSDS','RoHS','REACH','REACH','RoHS','RoHS','RoHS','RoHS','REACH','REACH','REACH','REACH','RoHS','FSC']; + const tItems = ['铅,汞,镉,六价铬,多溴联苯,多溴二苯醚','铅,汞,镉,六价铬,多溴联苯,多溴二苯醚','铅,汞,镉,六价铬,多溴联苯,多溴二苯醚','铅,汞,镉,六价铬,多溴联苯,多溴二苯醚,VOC含量','铅,汞,镉,六价铬,多溴联苯,多溴二苯醚,VOC含量','铅,汞,镉,六价铬','闪点,燃点,爆炸极限,VOC含量','铅,汞,镉,六价铬,多溴联苯,多溴二苯醚','SVHC 211项筛查','SVHC 211项筛查','铅,汞,镉,六价铬','铅,汞,镉,六价铬,多溴联苯,多溴二苯醚','铅,汞,镉,六价铬,导电率','铅,汞,镉,六价铬','SVHC 211项筛查','SVHC 211项筛查','SVHC 211项筛查','SVHC 211项筛查','铅,汞,镉,六价铬','可持续性认证']; + return `('SGS-20250512-${n}', ${mids[i]}, '${mcodes[i]}', '${mnames[i]}', ${sids[i]}, '${snames[Math.min(i,snames.length-1)]}', '${cTypes[i]}', '${tItems[i]}', 'pass', 'SGS-RPT-2025-${String(i+1).padStart(4,'0')}', 'SGS通标标准技术服务有限公司', '${d}', '2026-${String(Math.min(i+1,12)).padStart(2,'0')}-${String(Math.min(i+1,28)).padStart(2,'0')}', 1, '/files/SGS-${n}.pdf', '${mnames[i]}${cTypes[i]}认证合格', '${d} 10:00:00', '${d} 10:00:00', 2, 0)`; + }) +); + +// 26. qms_sgs_cert_item +append('26. qms_sgs_cert_item SGS认证明细 (40条)', 'qms_sgs_cert_item', + 'cert_id, test_item_name, test_standard, limit_value, test_value, unit, result, sort_order, create_time', + Array.from({length:40}, (_,i) => { + const certId = Math.floor(i/2)+1; + const items6 = ['铅(Pb)','镉(Cd)','汞(Hg)','六价铬(Cr6+)','多溴联苯(PBB)','多溴二苯醚(PBDE)']; + const idx = i % 6; + const limits = ['1000','100','1000','1000','1000','1000']; + const values = ['ND','ND','ND','ND','ND','ND']; + const d = `2025-04-${String(Math.min(Math.floor(i/2)+1,30)).padStart(2,'0')}`; + return `(${certId}, '${items6[idx]}', 'RoHS 2.0', '${limits[idx]}', '${values[idx]}', 'mg/kg', 'pass', ${idx+1}, '${d} 10:00:00')`; + }) +); + +// 27. qms_supplier_audit +append('27. qms_supplier_audit 供应商审核 (20条)', 'qms_supplier_audit', + 'audit_no, supplier_id, supplier_name, audit_type, audit_scope, audit_date, auditor, audit_items, audit_scores, total_score, conclusion, nonconformities, corrective_request, deadline, status, remark, create_time, update_time, create_by, deleted', + Array.from({length:20}, (_,i) => { + const n = String(i+1).padStart(3,'0'); + const d = `2025-04-${String(Math.min(i+1,30)).padStart(2,'0')}`; + const sids = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]; + const snames = ['东莞鑫达薄膜材料','佛山顺达PVC材料','深圳精诚油墨化工','广州永盛化工','东莞宏达丝网材料','上海紫江特种油墨','苏州雅特不干胶材料','深圳金太阳保护膜','东莞鑫达薄膜材料','佛山顺达PVC材料','深圳精诚油墨化工','广州永盛化工','东莞宏达丝网材料','上海紫江特种油墨','苏州雅特不干胶材料','深圳金太阳保护膜','东莞鑫达薄膜材料','佛山顺达PVC材料','深圳精诚油墨化工','广州永盛化工']; + const types = ['annual','annual','follow','annual','annual','follow','annual','annual','follow','annual','annual','follow','annual','annual','follow','annual','annual','follow','annual','annual']; + const scopes = ['质量管理体系审核','质量管理体系审核','不合格品跟踪审核','质量管理体系审核','质量管理体系审核','不合格品跟踪审核','质量管理体系审核','质量管理体系审核','不合格品跟踪审核','质量管理体系审核','质量管理体系审核','不合格品跟踪审核','质量管理体系审核','质量管理体系审核','不合格品跟踪审核','质量管理体系审核','质量管理体系审核','不合格品跟踪审核','质量管理体系审核','质量管理体系审核']; + const scores = [88.5,85.2,82.0,90.1,78.5,80.5,86.3,83.7,79.5,87.2,84.8,81.3,89.6,82.1,77.8,85.9,88.0,83.5,86.7,84.2]; + const conclusions = scores.map(s=>s>=85?'qualified':'conditional'); + const auditors = ['王强','刘洋','陈明','赵磊','孙丽']; + const deadlines = `2025-05-${String(Math.min(i+15,30)).padStart(2,'0')}`; + return `('SA-20250512-${n}', ${sids[i]}, '${snames[i]}', '${types[i]}', '${scopes[i]}', '${d}', '${auditors[i%5]}', '质量体系,生产过程,来料控制,出货检验,人员培训', '体系:${Math.floor(scores[i])},过程:${Math.floor(scores[i]-2)},来料:${Math.floor(scores[i]+1)},出货:${Math.floor(scores[i]-1)},培训:${Math.floor(scores[i]+2)}', ${scores[i].toFixed(1)}, '${conclusions[i]}', ${scores[i]<85?`'文件管控不完善,记录追溯性不足'`:'NULL'}, ${scores[i]<85?`'完善文件管控体系,加强记录追溯管理'`:'NULL'}, ${scores[i]<85?`'${deadlines}'`:'NULL'}, ${scores[i]<85?2:1}, '${snames[i]}${types[i]}审核${conclusions[i]==='qualified'?'合格':'有条件合格'}', '${d} 10:00:00', '${d} 10:00:00', ${i%5+4}, 0)`; + }) +); + +// ============================================================ +// 三、设备管理模块 +// ============================================================ +sql += '\n\n-- ============================================================\n-- 三、设备管理模块\n-- ============================================================\n'; + +// 28. eqp_equipment +append('28. eqp_equipment 设备 (20条)', 'eqp_equipment', + 'equipment_code, equipment_name, equipment_type, brand, model, serial_no, workshop_id, location, purchase_date, manufacturer, supplier_id, warranty_expire, rated_capacity, current_status, oee, availability, performance, quality_rate, total_run_hours, last_maintenance_date, next_maintenance_date, status, remark, create_time, update_time, create_by, deleted', + Array.from({length:20}, (_,i) => { + const codes = ['EQP-20250512-001','EQP-20250512-002','EQP-20250512-003','EQP-20250512-004','EQP-20250512-005','EQP-20250512-006','EQP-20250512-007','EQP-20250512-008','EQP-20250512-009','EQP-20250512-010','EQP-20250512-011','EQP-20250512-012','EQP-20250512-013','EQP-20250512-014','EQP-20250512-015','EQP-20250512-016','EQP-20250512-017','EQP-20250512-018','EQP-20250512-019','EQP-20250512-020']; + const names = ['1号柔印机','2号柔印机','3号凹印机','4号丝印机','5号凹印机','1号模切机','2号模切机','1号复卷机','2号复卷机','1号分条机','2号分条机','1号检品机','2号检品机','1号涂布机','2号涂布机','1号复合机','2号复合机','1号烫金机','2号烫金机','1号数字印刷机']; + const types = [1,1,2,3,2,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11]; + const brands = ['博斯特','博斯特','西江','湄洲','西江','博远','博远','德信','德信','博远','博远','德信','德信','博斯特','博斯特','西江','西江','博远','博远','惠普']; + const models = ['L-350','L-350','R-850','S-600','R-1200','D-500','D-500','W-800','W-800','S-300','S-300','I-600','I-600','C-1000','C-1000','L-800','L-800','H-400','H-400','HP-Indigo']; + const serials = ['SN-2023-001','SN-2023-002','SN-2022-003','SN-2024-004','SN-2022-005','SN-2023-006','SN-2023-007','SN-2024-008','SN-2024-009','SN-2023-010','SN-2023-011','SN-2024-012','SN-2024-013','SN-2022-014','SN-2022-015','SN-2023-016','SN-2023-017','SN-2024-018','SN-2024-019','SN-2025-020']; + const wids = [1,1,2,3,2,4,4,5,5,6,6,7,7,8,8,9,9,10,10,3]; + const locs = ['A栋1楼','A栋1楼','A栋2楼','B栋1楼','A栋2楼','B栋2楼','B栋2楼','C栋1楼','C栋1楼','C栋2楼','C栋2楼','D栋1楼','D栋1楼','A栋3楼','A栋3楼','B栋3楼','B栋3楼','D栋2楼','D栋2楼','B栋1楼']; + const purDs = ['2023-03-15','2023-06-20','2022-09-10','2024-01-15','2022-11-20','2023-08-05','2023-09-12','2024-02-28','2024-03-15','2023-04-20','2023-07-10','2024-04-01','2024-05-15','2022-06-10','2022-08-25','2023-05-15','2023-10-20','2024-06-01','2024-07-15','2025-01-10']; + const mans = ['瑞士博斯特','瑞士博斯特','日本西江','中国湄洲','日本西江','深圳博远','深圳博远','珠海德信','珠海德信','深圳博远','深圳博远','珠海德信','珠海德信','瑞士博斯特','瑞士博斯特','日本西江','日本西江','深圳博远','深圳博远','美国惠普']; + const supIds = [15,15,15,14,15,14,14,14,14,15,15,14,14,15,15,15,15,14,14,15]; + const warExps = ['2025-03-15','2025-06-20','2024-09-10','2026-01-15','2024-11-20','2025-08-05','2025-09-12','2026-02-28','2026-03-15','2025-04-20','2025-07-10','2026-04-01','2026-05-15','2024-06-10','2024-08-25','2025-05-15','2025-10-20','2026-06-01','2026-07-15','2027-01-10']; + const caps = [150.0000,150.0000,200.0000,80.0000,250.0000,120.0000,120.0000,180.0000,180.0000,100.0000,100.0000,160.0000,160.0000,120.0000,120.0000,140.0000,140.0000,90.0000,90.0000,60.0000]; + const stats = i===2?2:i===13?3:1; + const oees = [85.50,83.20,0,78.60,88.10,82.30,80.50,86.70,84.90,79.80,81.20,90.10,88.50,0,76.30,84.60,82.80,87.20,85.40,75.60]; + const avs = [92.30,90.50,0,85.20,94.10,88.60,87.30,93.50,91.80,86.40,88.10,95.20,93.80,0,83.50,91.20,89.60,94.10,92.30,82.40]; + const perfs = [93.50,92.80,0,91.20,94.80,93.50,92.10,93.80,92.60,91.50,92.30,95.30,94.60,0,90.80,93.20,92.50,93.80,92.90,91.20]; + const qrs = [98.80,99.10,0,99.50,98.50,98.20,99.00,98.60,99.20,99.30,98.90,99.10,99.00,0,99.40,99.00,99.20,98.50,99.10,99.50]; + const runHs = [8500.00,7200.00,0,3200.00,9800.00,6500.00,5800.00,4100.00,3800.00,5600.00,4900.00,2800.00,2200.00,0,12000.00,7200.00,6800.00,3500.00,3100.00,1500.00]; + const lastM = `2025-04-${String(Math.min(i+1,30)).padStart(2,'0')}`; + const nextM = `2025-07-${String(Math.min(i+1,30)).padStart(2,'0')}`; + return `('${codes[i]}', '${names[i]}', ${types[i]}, '${brands[i]}', '${models[i]}', '${serials[i]}', ${wids[i]}, '${locs[i]}', '${purDs[i]}', '${mans[i]}', ${supIds[i]}, '${warExps[i]}', ${caps[i].toFixed(4)}, ${stats}, ${oees[i].toFixed(2)}, ${avs[i].toFixed(2)}, ${perfs[i].toFixed(2)}, ${qrs[i].toFixed(2)}, ${runHs[i].toFixed(2)}, '${lastM}', '${nextM}', 1, '${names[i]}${stats===1?'正常运行':stats===2?'维修中':'保养中'}', '${lastM} 08:00:00', '${lastM} 08:00:00', 2, 0)`; + }) +); + +// 29. eqp_calibration +append('29. eqp_calibration 设备校准 (20条)', 'eqp_calibration', + 'calibration_no, equipment_id, equipment_code, equipment_name, calibration_date, next_calibration_date, calibration_org, calibration_result, certificate_no, calibration_cost, status, remark, create_time, update_time, create_by, deleted', + Array.from({length:20}, (_,i) => { + const n = String(i+1).padStart(3,'0'); + const d = `2025-04-${String(Math.min(i+1,30)).padStart(2,'0')}`; + const codes = ['EQP-20250512-001','EQP-20250512-002','EQP-20250512-003','EQP-20250512-004','EQP-20250512-005','EQP-20250512-006','EQP-20250512-007','EQP-20250512-008','EQP-20250512-009','EQP-20250512-010','EQP-20250512-011','EQP-20250512-012','EQP-20250512-013','EQP-20250512-014','EQP-20250512-015','EQP-20250512-016','EQP-20250512-017','EQP-20250512-018','EQP-20250512-019','EQP-20250512-020']; + const names = ['1号柔印机','2号柔印机','3号凹印机','4号丝印机','5号凹印机','1号模切机','2号模切机','1号复卷机','2号复卷机','1号分条机','2号分条机','1号检品机','2号检品机','1号涂布机','2号涂布机','1号复合机','2号复合机','1号烫金机','2号烫金机','1号数字印刷机']; + const nextD = `2025-10-${String(Math.min(i+1,30)).padStart(2,'0')}`; + const orgs = ['广东省计量科学研究院','深圳市计量质量检测研究院','广东省计量科学研究院','深圳市计量质量检测研究院','广东省计量科学研究院','深圳市计量质量检测研究院','广东省计量科学研究院','深圳市计量质量检测研究院','广东省计量科学研究院','深圳市计量质量检测研究院','广东省计量科学研究院','深圳市计量质量检测研究院','广东省计量科学研究院','深圳市计量质量检测研究院','广东省计量科学研究院','深圳市计量质量检测研究院','广东省计量科学研究院','深圳市计量质量检测研究院','广东省计量科学研究院','深圳市计量质量检测研究院']; + const costs = [3500.0000,3500.0000,4200.0000,2800.0000,4200.0000,3000.0000,3000.0000,2500.0000,2500.0000,2200.0000,2200.0000,2800.0000,2800.0000,3800.0000,3800.0000,3200.0000,3200.0000,2600.0000,2600.0000,4500.0000]; + return `('CAL-20250512-${n}', ${i+1}, '${codes[i]}', '${names[i]}', '${d}', '${nextD}', '${orgs[i]}', 1, 'CAL-CERT-${n}', ${costs[i].toFixed(4)}, 1, '${names[i]}年度校准合格', '${d} 09:00:00', '${d} 09:00:00', 2, 0)`; + }) +); + +// 30. eqp_maintenance_plan +append('30. eqp_maintenance_plan 维护计划 (20条)', 'eqp_maintenance_plan', + 'plan_no, equipment_id, maintenance_type, cycle_type, cycle_value, plan_date, responsible_id, content, status, complete_date, remark, create_time, update_time, deleted', + Array.from({length:20}, (_,i) => { + const n = String(i+1).padStart(3,'0'); + const d = `2025-04-${String(Math.min(i+1,30)).padStart(2,'0')}`; + const mTypes = [1,1,2,1,1,1,2,1,1,1,1,2,1,1,2,1,1,1,2,1]; + const cTypes = [2,2,1,2,2,2,1,2,2,3,2,1,2,2,1,3,2,2,1,2]; + const cVals = [30,30,90,30,30,30,90,30,30,15,30,90,30,30,90,15,30,30,90,30]; + const contents = ['日常保养:清洁润滑检查','日常保养:清洁润滑检查','深度保养:更换易损件校准','日常保养:清洁润滑检查','日常保养:清洁润滑检查','日常保养:清洁润滑检查','深度保养:更换刀片校准','日常保养:清洁润滑检查','日常保养:清洁润滑检查','日常保养:清洁检查张力','日常保养:清洁润滑检查','深度保养:更换光源校准','日常保养:清洁润滑检查','日常保养:清洁润滑检查','深度保养:更换涂布头','日常保养:清洁检查张力','日常保养:清洁润滑检查','日常保养:清洁润滑检查','深度保养:更换烫金版','日常保养:清洁校准喷头']; + const statuses = i<10?2:1; + const compD = statuses===2?`'${d}'`:'NULL'; + return `('MP-20250512-${n}', ${i+1}, ${mTypes[i]}, ${cTypes[i]}, ${cVals[i]}, '${d}', ${(i%5)+6}, '${contents[i]}', ${statuses}, ${compD}, '${contents[i]}${statuses===2?'已完成':'待执行'}', '${d} 08:00:00', '${d} 08:00:00', 0)`; + }) +); + +// 31. eqp_maintenance_record +append('31. eqp_maintenance_record 维护记录 (20条)', 'eqp_maintenance_record', + 'record_no, plan_id, equipment_id, maintenance_type, fault_desc, maintenance_content, start_time, end_time, downtime_hours, cost, responsible_id, result, remark, create_time, update_time, deleted', + Array.from({length:20}, (_,i) => { + const n = String(i+1).padStart(3,'0'); + const d = `2025-04-${String(Math.min(i+1,30)).padStart(2,'0')}`; + const mTypes = [1,1,2,1,1,1,2,1,1,1,1,2,1,1,2,1,1,1,2,1]; + const faultDescs = ['无故障-例行保养','无故障-例行保养','印刷套准偏移','无故障-例行保养','无故障-例行保养','无故障-例行保养','模切精度下降','无故障-例行保养','无故障-例行保养','无故障-例行保养','无故障-例行保养','检测光源衰减','无故障-例行保养','无故障-例行保养','涂布均匀度偏差','无故障-例行保养','无故障-例行保养','无故障-例行保养','烫金压力不均','无故障-例行保养']; + const contents = ['清洁润滑更换耗材','清洁润滑更换耗材','更换轴承校准套准系统','清洁润滑更换耗材','清洁润滑更换耗材','清洁润滑更换耗材','更换刀片校准模切精度','清洁润滑更换耗材','清洁润滑更换耗材','清洁检查更换张力弹簧','清洁润滑更换耗材','更换UV光源校准检测参数','清洁润滑更换耗材','清洁润滑更换耗材','更换涂布头校准涂布参数','清洁检查更换张力辊','清洁润滑更换耗材','清洁润滑更换耗材','更换烫金版调整压力','清洁校准喷头更换墨盒']; + const dhs = [2.00,2.50,8.00,1.50,2.00,2.50,6.00,1.50,2.00,1.00,2.50,5.00,1.50,2.00,7.00,1.00,2.50,2.00,6.50,3.00]; + const costs = [500.0000,600.0000,3500.0000,450.0000,550.0000,600.0000,2800.0000,450.0000,500.0000,300.0000,550.0000,2200.0000,450.0000,500.0000,3200.0000,350.0000,600.0000,500.0000,2500.0000,1800.0000]; + return `('MR-20250512-${n}', ${i+1}, ${i+1}, ${mTypes[i]}, '${faultDescs[i]}', '${contents[i]}', '${d} 08:00:00', '${d} ${String(8+Math.ceil(dhs[i])).padStart(2,'0')}:00:00', ${dhs[i].toFixed(2)}, ${costs[i].toFixed(4)}, ${(i%5)+6}, 1, '${contents[i]}完成', '${d} 16:00:00', '${d} 16:00:00', 0)`; + }) +); + +// 32. eqp_repair +append('32. eqp_repair 设备维修 (20条)', 'eqp_repair', + 'repair_no, equipment_id, equipment_code, equipment_name, fault_date, fault_desc, repair_type, repair_person, repair_start_time, repair_end_time, repair_cost, repair_result, status, remark, create_time, update_time, create_by, deleted', + Array.from({length:20}, (_,i) => { + const n = String(i+1).padStart(3,'0'); + const d = `2025-04-${String(Math.min(i*2+1,30)).padStart(2,'0')}`; + const codes = ['EQP-20250512-001','EQP-20250512-002','EQP-20250512-003','EQP-20250512-004','EQP-20250512-005','EQP-20250512-006','EQP-20250512-007','EQP-20250512-008','EQP-20250512-009','EQP-20250512-010','EQP-20250512-011','EQP-20250512-012','EQP-20250512-013','EQP-20250512-014','EQP-20250512-015','EQP-20250512-016','EQP-20250512-017','EQP-20250512-018','EQP-20250512-019','EQP-20250512-020']; + const names = ['1号柔印机','2号柔印机','3号凹印机','4号丝印机','5号凹印机','1号模切机','2号模切机','1号复卷机','2号复卷机','1号分条机','2号分条机','1号检品机','2号检品机','1号涂布机','2号涂布机','1号复合机','2号复合机','1号烫金机','2号烫金机','1号数字印刷机']; + const faultDescs = ['套准系统异常','墨泵压力不稳','主电机异响','刮刀磨损过快','张力控制器失灵','模切刀片断裂','气压系统漏气','收卷张力不均','导辊轴承磨损','分切刀口毛刺','纠偏系统偏差','CCD相机故障','传送带打滑','涂布头堵塞','烘箱温控异常','复合辊压力不均','胶水供给系统故障','烫金版磨损','温控系统报警','喷头堵塞']; + const rTypes = [1,2,1,2,1,1,2,2,1,2,1,1,2,1,1,2,1,2,1,2]; + const persons = ['李师傅','王师傅','张师傅','赵师傅','刘师傅','李师傅','王师傅','张师傅','赵师傅','刘师傅','李师傅','王师傅','张师傅','赵师傅','刘师傅','李师傅','王师傅','张师傅','赵师傅','刘师傅']; + const endD = `2025-04-${String(Math.min(i*2+2,30)).padStart(2,'0')}`; + const costs = [2500.0000,800.0000,5500.0000,600.0000,3200.0000,1800.0000,500.0000,700.0000,1200.0000,400.0000,2000.0000,3500.0000,300.0000,2800.0000,1500.0000,600.0000,2200.0000,900.0000,1800.0000,1200.0000]; + const statuses = i<16?2:1; + return `('REP-20250512-${n}', ${i+1}, '${codes[i]}', '${names[i]}', '${d}', '${faultDescs[i]}', ${rTypes[i]}, '${persons[i]}', '${d} 09:00:00', '${endD} 17:00:00', ${costs[i].toFixed(4)}, '${faultDescs[i]}已修复设备恢复正常', ${statuses}, '${names[i]}${faultDescs[i]}维修${statuses===2?'已完成':'进行中'}', '${d} 09:00:00', '${d} 17:00:00', 2, 0)`; + }) +); + +// 33. eqp_scrap +append('33. eqp_scrap 设备报废 (20条)', 'eqp_scrap', + 'scrap_no, equipment_id, equipment_code, equipment_name, scrap_date, scrap_reason, original_value, net_value, approval_person, status, remark, create_time, update_time, create_by, deleted', + Array.from({length:20}, (_,i) => { + const n = String(i+1).padStart(3,'0'); + const d = `2025-04-${String(Math.min(i+5,30)).padStart(2,'0')}`; + const codes = ['EQP-20250512-001','EQP-20250512-002','EQP-20250512-003','EQP-20250512-004','EQP-20250512-005','EQP-20250512-006','EQP-20250512-007','EQP-20250512-008','EQP-20250512-009','EQP-20250512-010','EQP-20250512-011','EQP-20250512-012','EQP-20250512-013','EQP-20250512-014','EQP-20250512-015','EQP-20250512-016','EQP-20250512-017','EQP-20250512-018','EQP-20250512-019','EQP-20250512-020']; + const names = ['1号柔印机','2号柔印机','3号凹印机','4号丝印机','5号凹印机','1号模切机','2号模切机','1号复卷机','2号复卷机','1号分条机','2号分条机','1号检品机','2号检品机','1号涂布机','2号涂布机','1号复合机','2号复合机','1号烫金机','2号烫金机','1号数字印刷机']; + const reasons = ['使用年限到期-已超10年','使用年限到期-已超10年','核心部件损坏-维修成本过高','技术淘汰-产能不足','使用年限到期-已超10年','多次维修仍存在精度问题','使用年限到期-已超8年','技术升级-新设备替代','使用年限到期-已超8年','技术淘汰-精度不达标','多次维修仍存在故障','使用年限到期-已超8年','技术升级-新设备替代','核心部件损坏-维修成本过高','使用年限到期-已超10年','技术淘汰-速度不达标','使用年限到期-已超8年','多次维修仍存在压力问题','技术升级-新设备替代','喷头老化-更换成本过高']; + const origVals = [850000.0000,850000.0000,1200000.0000,350000.0000,1500000.0000,450000.0000,450000.0000,380000.0000,380000.0000,280000.0000,280000.0000,520000.0000,520000.0000,980000.0000,980000.0000,650000.0000,650000.0000,420000.0000,420000.0000,1800000.0000]; + const netVals = [50000.0000,45000.0000,80000.0000,20000.0000,100000.0000,30000.0000,25000.0000,22000.0000,20000.0000,15000.0000,18000.0000,35000.0000,30000.0000,65000.0000,60000.0000,40000.0000,35000.0000,25000.0000,22000.0000,120000.0000]; + const approvals = ['总经理-张总','总经理-张总','总经理-张总','总经理-张总','总经理-张总','总经理-张总','总经理-张总','总经理-张总','总经理-张总','总经理-张总','总经理-张总','总经理-张总','总经理-张总','总经理-张总','总经理-张总','总经理-张总','总经理-张总','总经理-张总','总经理-张总','总经理-张总']; + const statuses = i<15?2:1; + return `('SCR-20250512-${n}', ${i+1}, '${codes[i]}', '${names[i]}', '${d}', '${reasons[i]}', ${origVals[i].toFixed(4)}, ${netVals[i].toFixed(4)}, '${approvals[i]}', ${statuses}, '${names[i]}报废${statuses===2?'已审批':'待审批'}', '${d} 10:00:00', '${d} 10:00:00', 2, 0)`; + }) +); + +sql += '\nSET FOREIGN_KEY_CHECKS=1;\n'; + +fs.appendFileSync(outFile, sql, 'utf8'); +console.log('SQL appended successfully. Total length: ' + sql.length); diff --git a/database/_archive/_temp_part2.sql b/database/_archive/_temp_part2.sql new file mode 100644 index 00000000..de71c890 --- /dev/null +++ b/database/_archive/_temp_part2.sql @@ -0,0 +1,34 @@ + + +-- ============================================================ +-- 16. 报工单 (prd_work_report) - 22条 +-- 已完成工单1-7有完整报工, 生产中工单8-14部分报工, +-- 待生产/已取消工单无报工 +-- ============================================================ +INSERT IGNORE INTO prd_work_report +(report_no, work_order_id, work_order_no, process_name, process_seq, equipment_id, operator_id, operator_name, plan_qty, completed_qty, qualified_qty, defective_qty, scrap_qty, start_time, end_time, work_hours, is_first_piece, first_piece_status, first_piece_inspector, remark, create_by, create_time, deleted) +VALUES +('WR-20250402-001', 1, 'WO-20250401-001', '柔性版印刷', 1, 1, 6, '陈明', 10000.00, 10000.00, 9950.00, 40.00, 10.00, '2025-04-01 08:30:00', '2025-04-03 17:00:00', 24.0, 1, 'qualified', '刘洋', '美的空调面板标签柔印报工', 6, '2025-04-02 17:30:00', 0), +('WR-20250404-002', 2, 'WO-20250403-002', '丝网印刷', 1, 2, 7, '赵磊', 8000.00, 8000.00, 7970.00, 25.00, 5.00, '2025-04-03 08:30:00', '2025-04-05 17:00:00', 20.0, 1, 'qualified', '刘洋', '格力洗衣机面板标签丝印报工', 7, '2025-04-04 17:30:00', 0), +('WR-20250406-003', 3, 'WO-20250405-003', '柔性版印刷', 1, 1, 6, '陈明', 15000.00, 15000.00, 14920.00, 60.00, 20.00, '2025-04-05 08:00:00', '2025-04-08 17:00:00', 32.0, 1, 'qualified', '刘洋', '华为手机后盖标签柔印报工', 6, '2025-04-06 17:30:00', 0), +('WR-20250409-004', 4, 'WO-20250408-004', '溶剂型印刷', 1, 3, 7, '赵磊', 12000.00, 12000.00, 11940.00, 45.00, 15.00, '2025-04-08 08:30:00', '2025-04-11 17:00:00', 28.0, 1, 'qualified', '孙丽', '比亚迪汽车内饰标签溶剂印刷报工', 7, '2025-04-09 17:30:00', 0), +('WR-20250411-005', 5, 'WO-20250410-005', '柔性版印刷', 1, 1, 6, '陈明', 20000.00, 20000.00, 19900.00, 75.00, 25.00, '2025-04-10 08:30:00', '2025-04-14 17:00:00', 40.0, 1, 'qualified', '刘洋', 'TCL电子标签柔印报工', 6, '2025-04-11 17:30:00', 0), +('WR-20250413-006', 6, 'WO-20250412-006', 'UV上光', 1, 4, 7, '赵磊', 5000.00, 5000.00, 4980.00, 15.00, 5.00, '2025-04-12 09:00:00', '2025-04-14 17:00:00', 16.0, 1, 'qualified', '孙丽', '中兴热收缩膜UV上光报工', 7, '2025-04-13 17:30:00', 0), +('WR-20250416-007', 7, 'WO-20250415-007', '柔性版印刷', 1, 1, 6, '陈明', 8000.00, 8000.00, 7960.00, 30.00, 10.00, '2025-04-15 08:30:00', '2025-04-18 17:00:00', 20.0, 1, 'qualified', '刘洋', '美的空调面板标签v2.0柔印报工', 6, '2025-04-16 17:30:00', 0), +('WR-20250420-008', 8, 'WO-20250418-008', '丝网印刷', 1, 2, 7, '赵磊', 10000.00, 6000.00, 5980.00, 15.00, 5.00, '2025-04-18 08:30:00', '2025-04-22 17:00:00', 16.0, 1, 'qualified', '孙丽', '格力洗衣机面板标签高端版丝印报工-进行中', 7, '2025-04-20 17:30:00', 0), +('WR-20250422-009', 9, 'WO-20250420-009', '柔性版印刷', 1, 1, 6, '陈明', 12000.00, 7000.00, 6970.00, 20.00, 10.00, '2025-04-20 08:00:00', '2025-04-24 17:00:00', 18.0, 1, 'qualified', '刘洋', '华为手机后盖标签防伪版柔印报工-进行中', 6, '2025-04-22 17:30:00', 0), +('WR-20250424-010', 10, 'WO-20250422-010', '溶剂型印刷', 1, 3, 7, '赵磊', 6000.00, 3000.00, 2985.00, 10.00, 5.00, '2025-04-22 08:30:00', '2025-04-25 17:00:00', 8.0, 1, 'qualified', '孙丽', '比亚迪汽车内饰标签v2.0溶剂印刷报工-进行中', 7, '2025-04-24 17:30:00', 0), +('WR-20250427-011', 11, 'WO-20250425-011', '柔性版印刷', 1, 1, 6, '陈明', 15000.00, 5000.00, 4980.00, 15.00, 5.00, '2025-04-25 08:30:00', '2025-04-28 17:00:00', 12.0, 1, 'qualified', '刘洋', '广汽电子标签防水版柔印报工-进行中', 6, '2025-04-27 17:30:00', 0), +('WR-20250430-012', 12, 'WO-20250428-012', 'UV上光', 1, 4, 7, '赵磊', 8000.00, 2000.00, 1990.00, 8.00, 2.00, '2025-04-28 09:00:00', '2025-04-30 17:00:00', 6.0, 1, 'qualified', '孙丽', '海信热收缩膜v2.0 UV上光报工-进行中', 7, '2025-04-30 17:30:00', 0), +('WR-20250503-013', 13, 'WO-20250501-013', '柔性版印刷', 1, 1, 6, '陈明', 10000.00, 3000.00, 2985.00, 10.00, 5.00, '2025-05-01 08:30:00', '2025-05-03 17:00:00', 8.0, 1, 'qualified', '刘洋', '小米空调面板标签经济版柔印报工-进行中', 6, '2025-05-03 17:30:00', 0), +('WR-20250505-014', 14, 'WO-20250503-014', '丝网印刷', 1, 2, 7, '赵磊', 9000.00, 2000.00, 1990.00, 8.00, 2.00, '2025-05-03 08:30:00', '2025-05-05 17:00:00', 6.0, 1, 'pending', NULL, 'OPPO洗衣机面板标签高端版丝印报工-首件待检', 7, '2025-05-05 17:30:00', 0), +('WR-20250506-015', 15, 'WO-20250505-015', '柔性版印刷', 1, 1, 6, '陈明', 11000.00, 0.00, 0.00, 0.00, 0.00, NULL, NULL, 0.0, 0, NULL, NULL, '美的手机后盖标签防伪版-待报工', 6, '2025-05-06 09:00:00', 0), +('WR-20250508-016', 16, 'WO-20250507-016', '溶剂型印刷', 1, 3, 7, '赵磊', 7000.00, 0.00, 0.00, 0.00, 0.00, NULL, NULL, 0.0, 0, NULL, NULL, '格力汽车内饰标签耐高温版-待报工', 7, '2025-05-08 09:00:00', 0), +('WR-20250509-017', 17, 'WO-20250508-017', '柔性版印刷', 1, 1, 6, '陈明', 18000.00, 0.00, 0.00, 0.00, 0.00, NULL, NULL, 0.0, 0, NULL, NULL, '华为电子标签防水版-待报工', 6, '2025-05-09 09:00:00', 0), +('WR-20250510-018', 18, 'WO-20250509-018', 'UV上光', 1, 4, 7, '赵磊', 6000.00, 0.00, 0.00, 0.00, 0.00, NULL, NULL, 0.0, 0, NULL, NULL, '比亚迪热收缩膜高收缩版-待报工', 7, '2025-05-10 09:00:00', 0), +('WR-20250511-019', 19, 'WO-20250510-019', '柔性版印刷', 1, 1, 6, '陈明', 9000.00, 0.00, 0.00, 0.00, 0.00, NULL, NULL, 0.0, 0, NULL, NULL, 'TCL空调面板标签v3.0-待报工', 6, '2025-05-11 09:00:00', 0), +('WR-20250512-020', 20, 'WO-20250511-020', '丝网印刷', 1, 2, 7, '赵磊', 7500.00, 0.00, 0.00, 0.00, 0.00, NULL, NULL, 0.0, 0, NULL, NULL, '中兴洗衣机面板标签v3.0-待报工', 7, '2025-05-12 09:00:00', 0), +('WR-20250411-021', 21, 'WO-20250410-021', '柔性版印刷', 1, 1, 6, '陈明', 5000.00, 0.00, 0.00, 0.00, 0.00, NULL, NULL, 0.0, 0, NULL, NULL, '广汽手机后盖标签-已取消-无报工', 6, '2025-04-11 10:00:00', 0), +('WR-20250416-022', 22, 'WO-20250415-022', '丝网印刷', 1, 2, 7, '赵磊', 4000.00, 0.00, 0.00, 0.00, 0.00, NULL, NULL, 0.0, 0, NULL, NULL, '海信汽车内饰标签-已取消-无报工', 7, '2025-04-16 10:00:00', 0); + +SET FOREIGN_KEY_CHECKS=1; diff --git a/database/_archive/package-lock.json b/database/_archive/package-lock.json new file mode 100644 index 00000000..bd838170 --- /dev/null +++ b/database/_archive/package-lock.json @@ -0,0 +1,158 @@ +{ + "name": "database", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "database", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "mysql2": "^3.20.0" + } + }, + "node_modules/@types/node": { + "version": "25.5.0", + "resolved": "https://registry.npmmirror.com/@types/node/-/node-25.5.0.tgz", + "integrity": "sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw==", + "license": "MIT", + "peer": true, + "dependencies": { + "undici-types": "~7.18.0" + } + }, + "node_modules/aws-ssl-profiles": { + "version": "1.1.2", + "resolved": "https://registry.npmmirror.com/aws-ssl-profiles/-/aws-ssl-profiles-1.1.2.tgz", + "integrity": "sha512-NZKeq9AfyQvEeNlN0zSYAaWrmBffJh3IELMZfRpJVWgrpEbtEpnjvzqBPf+mxoI287JohRDoa+/nsfqqiZmF6g==", + "license": "MIT", + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/denque": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/denque/-/denque-2.1.0.tgz", + "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/generate-function": { + "version": "2.3.1", + "resolved": "https://registry.npmmirror.com/generate-function/-/generate-function-2.3.1.tgz", + "integrity": "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==", + "license": "MIT", + "dependencies": { + "is-property": "^1.0.2" + } + }, + "node_modules/iconv-lite": { + "version": "0.7.2", + "resolved": "https://registry.npmmirror.com/iconv-lite/-/iconv-lite-0.7.2.tgz", + "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/is-property": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/is-property/-/is-property-1.0.2.tgz", + "integrity": "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==", + "license": "MIT" + }, + "node_modules/long": { + "version": "5.3.2", + "resolved": "https://registry.npmmirror.com/long/-/long-5.3.2.tgz", + "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==", + "license": "Apache-2.0" + }, + "node_modules/lru.min": { + "version": "1.1.4", + "resolved": "https://registry.npmmirror.com/lru.min/-/lru.min-1.1.4.tgz", + "integrity": "sha512-DqC6n3QQ77zdFpCMASA1a3Jlb64Hv2N2DciFGkO/4L9+q/IpIAuRlKOvCXabtRW6cQf8usbmM6BE/TOPysCdIA==", + "license": "MIT", + "engines": { + "bun": ">=1.0.0", + "deno": ">=1.30.0", + "node": ">=8.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wellwelwel" + } + }, + "node_modules/mysql2": { + "version": "3.20.0", + "resolved": "https://registry.npmmirror.com/mysql2/-/mysql2-3.20.0.tgz", + "integrity": "sha512-eCLUs7BNbgA6nf/MZXsaBO1SfGs0LtLVrJD3WeWq+jPLDWkSufTD+aGMwykfUVPdZnblaUK1a8G/P63cl9FkKg==", + "license": "MIT", + "dependencies": { + "aws-ssl-profiles": "^1.1.2", + "denque": "^2.1.0", + "generate-function": "^2.3.1", + "iconv-lite": "^0.7.2", + "long": "^5.3.2", + "lru.min": "^1.1.4", + "named-placeholders": "^1.1.6", + "sql-escaper": "^1.3.3" + }, + "engines": { + "node": ">= 8.0" + }, + "peerDependencies": { + "@types/node": ">= 8" + } + }, + "node_modules/named-placeholders": { + "version": "1.1.6", + "resolved": "https://registry.npmmirror.com/named-placeholders/-/named-placeholders-1.1.6.tgz", + "integrity": "sha512-Tz09sEL2EEuv5fFowm419c1+a/jSMiBjI9gHxVLrVdbUkkNUUfjsVYs9pVZu5oCon/kmRh9TfLEObFtkVxmY0w==", + "license": "MIT", + "dependencies": { + "lru.min": "^1.1.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmmirror.com/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/sql-escaper": { + "version": "1.3.3", + "resolved": "https://registry.npmmirror.com/sql-escaper/-/sql-escaper-1.3.3.tgz", + "integrity": "sha512-BsTCV265VpTp8tm1wyIm1xqQCS+Q9NHx2Sr+WcnUrgLrQ6yiDIvHYJV5gHxsj1lMBy2zm5twLaZao8Jd+S8JJw==", + "license": "MIT", + "engines": { + "bun": ">=1.0.0", + "deno": ">=2.0.0", + "node": ">=12.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/mysqljs/sql-escaper?sponsor=1" + } + }, + "node_modules/undici-types": { + "version": "7.18.2", + "resolved": "https://registry.npmmirror.com/undici-types/-/undici-types-7.18.2.tgz", + "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==", + "license": "MIT", + "peer": true + } + } +} diff --git a/database/_archive/package.json b/database/_archive/package.json new file mode 100644 index 00000000..04ce46b0 --- /dev/null +++ b/database/_archive/package.json @@ -0,0 +1,15 @@ +{ + "name": "database", + "version": "1.0.0", + "main": "init-db.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC", + "description": "", + "dependencies": { + "mysql2": "^3.20.0" + } +} diff --git a/database/_archive/pnpm-lock.yaml b/database/_archive/pnpm-lock.yaml new file mode 100644 index 00000000..432adf6a --- /dev/null +++ b/database/_archive/pnpm-lock.yaml @@ -0,0 +1,109 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + mysql2: + specifier: ^3.20.0 + version: 3.22.2(@types/node@25.6.0) + +packages: + + '@types/node@25.6.0': + resolution: {integrity: sha512-+qIYRKdNYJwY3vRCZMdJbPLJAtGjQBudzZzdzwQYkEPQd+PJGixUL5QfvCLDaULoLv+RhT3LDkwEfKaAkgSmNQ==} + + aws-ssl-profiles@1.1.2: + resolution: {integrity: sha512-NZKeq9AfyQvEeNlN0zSYAaWrmBffJh3IELMZfRpJVWgrpEbtEpnjvzqBPf+mxoI287JohRDoa+/nsfqqiZmF6g==} + engines: {node: '>= 6.0.0'} + + denque@2.1.0: + resolution: {integrity: sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==} + engines: {node: '>=0.10'} + + generate-function@2.3.1: + resolution: {integrity: sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==} + + iconv-lite@0.7.2: + resolution: {integrity: sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==} + engines: {node: '>=0.10.0'} + + is-property@1.0.2: + resolution: {integrity: sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==} + + long@5.3.2: + resolution: {integrity: sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==} + + lru.min@1.1.4: + resolution: {integrity: sha512-DqC6n3QQ77zdFpCMASA1a3Jlb64Hv2N2DciFGkO/4L9+q/IpIAuRlKOvCXabtRW6cQf8usbmM6BE/TOPysCdIA==} + engines: {bun: '>=1.0.0', deno: '>=1.30.0', node: '>=8.0.0'} + + mysql2@3.22.2: + resolution: {integrity: sha512-snC/L6YoCJPFpozZo3p3hiOlt9ItQ7sCnLSziFLlIttEzsPhrdcPT8g21BiQ7Oqif25W4Xq1IFuBzBvoFYDf0Q==} + engines: {node: '>= 8.0'} + peerDependencies: + '@types/node': '>= 8' + + named-placeholders@1.1.6: + resolution: {integrity: sha512-Tz09sEL2EEuv5fFowm419c1+a/jSMiBjI9gHxVLrVdbUkkNUUfjsVYs9pVZu5oCon/kmRh9TfLEObFtkVxmY0w==} + engines: {node: '>=8.0.0'} + + safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + + sql-escaper@1.3.3: + resolution: {integrity: sha512-BsTCV265VpTp8tm1wyIm1xqQCS+Q9NHx2Sr+WcnUrgLrQ6yiDIvHYJV5gHxsj1lMBy2zm5twLaZao8Jd+S8JJw==} + engines: {bun: '>=1.0.0', deno: '>=2.0.0', node: '>=12.0.0'} + + undici-types@7.19.2: + resolution: {integrity: sha512-qYVnV5OEm2AW8cJMCpdV20CDyaN3g0AjDlOGf1OW4iaDEx8MwdtChUp4zu4H0VP3nDRF/8RKWH+IPp9uW0YGZg==} + +snapshots: + + '@types/node@25.6.0': + dependencies: + undici-types: 7.19.2 + + aws-ssl-profiles@1.1.2: {} + + denque@2.1.0: {} + + generate-function@2.3.1: + dependencies: + is-property: 1.0.2 + + iconv-lite@0.7.2: + dependencies: + safer-buffer: 2.1.2 + + is-property@1.0.2: {} + + long@5.3.2: {} + + lru.min@1.1.4: {} + + mysql2@3.22.2(@types/node@25.6.0): + dependencies: + '@types/node': 25.6.0 + aws-ssl-profiles: 1.1.2 + denque: 2.1.0 + generate-function: 2.3.1 + iconv-lite: 0.7.2 + long: 5.3.2 + lru.min: 1.1.4 + named-placeholders: 1.1.6 + sql-escaper: 1.3.3 + + named-placeholders@1.1.6: + dependencies: + lru.min: 1.1.4 + + safer-buffer@2.1.2: {} + + sql-escaper@1.3.3: {} + + undici-types@7.19.2: {} diff --git a/database/_archive/run-purchase-sql.js b/database/_archive/run-purchase-sql.js new file mode 100644 index 00000000..cc518dde --- /dev/null +++ b/database/_archive/run-purchase-sql.js @@ -0,0 +1,49 @@ +const mysql = require('mysql2/promise'); +const fs = require('fs'); +const path = require('path'); + +async function runSQL() { + const connection = await mysql.createConnection({ + host: '127.0.0.1', + port: 3306, + user: 'root', + password: 'Snqig521223', + database: 'vnerpdacahng', + multipleStatements: true + }); + + try { + const sqlFile = path.join(__dirname, 'purchase_request.sql'); + const sql = fs.readFileSync(sqlFile, 'utf8'); + + console.log('开始执行采购申请数据库脚本...'); + await connection.query(sql); + console.log('✅ 数据库表创建成功!'); + + // 验证表是否创建成功 + const [tables] = await connection.query(` + SELECT TABLE_NAME + FROM information_schema.TABLES + WHERE TABLE_SCHEMA = 'vnerpdacahng' + AND TABLE_NAME LIKE 'pur_%' + `); + + console.log('\n已创建的表:'); + tables.forEach(t => console.log(` - ${t.TABLE_NAME}`)); + + // 验证数据 + const [requests] = await connection.query('SELECT COUNT(*) as count FROM pur_request'); + const [items] = await connection.query('SELECT COUNT(*) as count FROM pur_request_item'); + + console.log(`\n测试数据:`); + console.log(` - 采购申请: ${requests[0].count} 条`); + console.log(` - 申请明细: ${items[0].count} 条`); + + } catch (error) { + console.error('❌ 执行失败:', error.message); + } finally { + await connection.end(); + } +} + +runSQL(); diff --git a/database/_archive/run-sample-sql.js b/database/_archive/run-sample-sql.js new file mode 100644 index 00000000..6049dcbe --- /dev/null +++ b/database/_archive/run-sample-sql.js @@ -0,0 +1,46 @@ +const mysql = require('mysql2/promise'); +const fs = require('fs'); +const path = require('path'); + +async function runSQL() { + const connection = await mysql.createConnection({ + host: '127.0.0.1', + port: 3306, + user: 'root', + password: 'Snqig521223', + database: 'vnerpdacahng', + multipleStatements: true + }); + + try { + const sqlFile = path.join(__dirname, 'sample_orders.sql'); + const sql = fs.readFileSync(sqlFile, 'utf8'); + + console.log('开始执行打样单数据库脚本...'); + await connection.query(sql); + console.log('✅ 数据库表创建成功!'); + + // 验证表是否创建成功 + const [tables] = await connection.query(` + SELECT TABLE_NAME + FROM information_schema.TABLES + WHERE TABLE_SCHEMA = 'vnerpdacahng' + AND TABLE_NAME = 'sample_order' + `); + + if (tables.length > 0) { + console.log('\n✅ 表 sample_order 创建成功'); + } + + // 验证数据 + const [count] = await connection.query('SELECT COUNT(*) as count FROM sample_order'); + console.log(`✅ 测试数据: ${count[0].count} 条打样单记录`); + + } catch (error) { + console.error('❌ 执行失败:', error.message); + } finally { + await connection.end(); + } +} + +runSQL(); diff --git a/database/_archive/run-sql.js b/database/_archive/run-sql.js new file mode 100644 index 00000000..b3be048f --- /dev/null +++ b/database/_archive/run-sql.js @@ -0,0 +1,29 @@ +const mysql = require('mysql2/promise'); +const fs = require('fs'); +const path = require('path'); + +async function runSQL() { + const connection = await mysql.createConnection({ + host: '127.0.0.1', + user: 'root', + password: 'Snqig521223', + database: 'vnerpdacahng', + multipleStatements: true + }); + + try { + const sqlFile = path.join(__dirname, 'update_standard_card.sql'); + const sql = fs.readFileSync(sqlFile, 'utf8'); + + console.log('正在执行 SQL 脚本...'); + await connection.query(sql); + console.log('SQL 脚本执行成功!'); + + } catch (error) { + console.error('执行失败:', error); + } finally { + await connection.end(); + } +} + +runSQL(); diff --git a/database/_archive/run-vehicle-sql.js b/database/_archive/run-vehicle-sql.js new file mode 100644 index 00000000..45c72180 --- /dev/null +++ b/database/_archive/run-vehicle-sql.js @@ -0,0 +1,49 @@ +const mysql = require('mysql2/promise'); +const fs = require('fs'); +const path = require('path'); + +async function runSQL() { + const connection = await mysql.createConnection({ + host: '127.0.0.1', + port: 3306, + user: 'root', + password: 'Snqig521223', + database: 'vnerpdacahng', + multipleStatements: true + }); + + try { + const sqlFile = path.join(__dirname, 'delivery_vehicles.sql'); + const sql = fs.readFileSync(sqlFile, 'utf8'); + + console.log('开始执行车辆管理数据库脚本...'); + await connection.query(sql); + console.log('✅ 数据库表创建成功!'); + + // 验证表是否创建成功 + const [tables] = await connection.query(` + SELECT TABLE_NAME + FROM information_schema.TABLES + WHERE TABLE_SCHEMA = 'vnerpdacahng' + AND TABLE_NAME LIKE 'delivery_%' + `); + + console.log('\n已创建的表:'); + tables.forEach(t => console.log(` - ${t.TABLE_NAME}`)); + + // 验证数据 + const [vehicles] = await connection.query('SELECT COUNT(*) as count FROM delivery_vehicle'); + const [drivers] = await connection.query('SELECT COUNT(*) as count FROM delivery_driver'); + + console.log(`\n测试数据:`); + console.log(` - 车辆: ${vehicles[0].count} 条`); + console.log(` - 司机: ${drivers[0].count} 条`); + + } catch (error) { + console.error('❌ 执行失败:', error.message); + } finally { + await connection.end(); + } +} + +runSQL(); diff --git a/database/actual_schema.sql b/database/actual_schema.sql index 0f3f1925..5e5f0aa6 100644 --- a/database/actual_schema.sql +++ b/database/actual_schema.sql @@ -1,2 +1 @@ --- This file is deprecated. --- The authoritative schema is database/vnerpdacahng_schema.sql +DROP TABLE IF EXISTS `v_purchase_request_full`; diff --git a/database/audit_system_tables.sql b/database/audit_system_tables.sql index dee7f3e0..69e46bfe 100644 --- a/database/audit_system_tables.sql +++ b/database/audit_system_tables.sql @@ -1,5 +1,5 @@ -- ============================================================ --- 印刷生产经营信息管理系统 Print MIS 系统可审计性核心表结构 +-- VNERP 系统可审计性核心表结构 -- 文件名: audit_system_tables.sql -- 说明: 包含操作日志、登录日志、库存流水、财务流水四张核心审计表 -- 原则: 所有操作留痕,所有修改留底,所有单据不可删,所有变动有据可查 @@ -398,7 +398,7 @@ ALTER TABLE `fin_payable` -- ============================================================ -- 完成提示 -- ============================================================ -SELECT '印刷生产经营信息管理系统 Print MIS 审计系统表创建完成' AS result; +SELECT 'VNERP 审计系统表创建完成' AS result; SELECT CONCAT( '共创建 ', (SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = 'vnerpdacahng' AND table_name LIKE 'sys_%log%'), diff --git a/database/department_structure_new.sql b/database/department_structure_new.sql index 915ede77..94559e27 100644 --- a/database/department_structure_new.sql +++ b/database/department_structure_new.sql @@ -1,4 +1,4 @@ --- VNERP 系统 - 新部门架构设计 +-- 越南达昌科技有限公司系统 - 新部门架构设计 -- 生成日期: 2026-03-26 -- 部门结构: -- 管理部 diff --git a/database/hr_all_seed.sql b/database/hr_all_seed.sql deleted file mode 100644 index f7b9c0f5..00000000 --- a/database/hr_all_seed.sql +++ /dev/null @@ -1,75 +0,0 @@ --- ============================================================ --- HR 模块种子数据 (Phase 8 — Integration & Deployment) --- 依赖: hr_all_tables.sql 执行后 --- 注意: 员工 ID 引用 seed_part5 数据 (sys_employee id 1~20) --- ============================================================ - -SET NAMES utf8mb4; -SET FOREIGN_KEY_CHECKS=0; - --- ============================================================ --- 1. 薪资标准 (hr_salary_standard) --- 3 个印刷岗位, 基本工资 5000~8000 --- ============================================================ -INSERT IGNORE INTO hr_salary_standard (position_code, skill_level, base_salary, piece_rate_type, performance_base, allowance_night, allowance_high_temp, effective_date, factory_id, status, remark) VALUES -('CUTTER_OP', 3, 6500.00, 'piece', 1500.00, 30.00, 200.00, '2026-01-01', 1, 1, '切纸机长-中级-含夜班高温津贴'), -('CUTTER_OP', 4, 8000.00, 'piece', 2000.00, 30.00, 200.00, '2026-01-01', 1, 1, '切纸机长-高级'), -('OFFSET_OP', 3, 7000.00, 'piece', 1800.00, 35.00, 200.00, '2026-01-01', 1, 1, '胶印机长-中级'), -('OFFSET_OP', 4, 8500.00, 'piece', 2200.00, 35.00, 200.00, '2026-01-01', 1, 1, '胶印机长-高级'), -('BINDING_LEADER', 2, 5000.00, 'piece', 1200.00, 20.00, 150.00, '2026-01-01', 1, 1, '装订组长-初级'), -('BINDING_LEADER', 3, 6200.00, 'piece', 1500.00, 20.00, 150.00, '2026-01-01', 1, 1, '装订组长-中级'), -('BINDING_LEADER', 4, 7500.00, 'piece', 1800.00, 20.00, 150.00, '2026-01-01', 1, 1, '装订组长-高级'); - --- ============================================================ --- 2. 工序单价 (hr_piece_rate) --- 5 个工序编码, 单价 0.02~0.50 --- ============================================================ -INSERT IGNORE INTO hr_piece_rate (process_code, product_type, unit_price, unit, quality_threshold, effective_date, factory_id, status, remark) VALUES -('CUTTING', '通用', 0.0500, '件', 98.00, '2026-01-01', 1, 1, '切割工序-标准品'), -('PRINTING', '通用', 0.2000, '件', 99.00, '2026-01-01', 1, 1, '印刷工序-标准品'), -('FOLDING', '通用', 0.0200, '件', 97.00, '2026-01-01', 1, 1, '折页工序-标准品'), -('BINDING', '通用', 0.0800, '件', 98.50, '2026-01-01', 1, 1, '装订工序-标准品'), -('QC_INSPECT', '通用', 0.0300, '件', 100.00, '2026-01-01', 1, 1, '质检工序-标准品'), -('CUTTING', '精密', 0.1000, '件', 99.50, '2026-01-01', 1, 1, '切割工序-精密品'), -('PRINTING', '彩色', 0.5000, '件', 99.50, '2026-01-01', 1, 1, '印刷工序-彩色品'); - --- ============================================================ --- 3. 员工技能矩阵 (hr_skill_matrix) --- 3 名员工, 多种技能 --- ============================================================ -INSERT IGNORE INTO hr_skill_matrix (employee_id, skill_code, skill_name, skill_category, skill_level, certified, certificate_id, assessor, assess_date, next_assess_date, remark) VALUES --- 员工 ID 3 (王建国-技术主管) -(3, 'OFFSET-PRINT', '胶印印刷', 'printing', 5, 1, NULL, '陈志强', '2025-06-15', '2026-06-15', '高级胶印技能-可带徒'), -(3, 'CUT-GUIDE', '切纸指导', 'finishing', 4, 1, NULL, '陈志强', '2025-06-15', '2026-06-15', '切纸工序指导能力'), -(3, 'MNT-PRESS', '印刷机维护', 'maintenance', 4, 0, NULL, '刘志远', '2025-08-20', '2026-08-20', '印刷机中级维护-待认证'), --- 员工 ID 5 (刘志远-模切组长) -(5, 'DIE-CUT', '模切操作', 'finishing', 5, 1, NULL, '王建国', '2025-04-10', '2026-04-10', '高级模切-核心技能'), -(5, 'CUTTING', '切割', 'finishing', 4, 1, NULL, '王建国', '2025-04-10', '2026-04-10', '切割技能-熟练'), -(5, 'MNT-DIE', '模切机维护', 'maintenance', 3, 0, NULL, '张丽华', '2025-09-05', '2026-09-05', '中级维护-待认证'), --- 员工 ID 6 (陈美玲-商标组长) -(6, 'SCREEN-PRINT', '丝网印刷', 'printing', 5, 1, NULL, '王建国', '2025-05-20', '2026-05-20', '高级丝印-核心技能'), -(6, 'COLOR-MATCH', '配色', 'printing', 4, 1, NULL, '王建国', '2025-05-20', '2026-05-20', '配色技能-熟练'), -(6, 'QC-PRINT', '印刷品检', 'quality', 3, 0, NULL, '孙浩然', '2025-10-12', '2026-10-12', '印刷品检初级-待认证'); - --- ============================================================ --- 4. 员工证书 (hr_certificate) --- 2 名员工, 其中 1 张即将到期 (用于到期预警测试) --- ============================================================ -INSERT IGNORE INTO hr_certificate (employee_id, cert_name, cert_code, cert_type, issue_authority, issue_date, expiry_date, remind_days, status, file_url, remark) VALUES --- 员工 ID 5 (刘志远) - 有效期证书 -(5, '模切设备操作证', 'MC-2024-00128', 'operation', '佛山市应急管理局', '2024-03-15', '2027-03-14', 60, 1, '/uploads/cert/mc_00128.pdf', '模切设备持证上岗'), -(5, '高处作业证', 'GW-2023-00563', 'safety', '广东省应急管理厅', '2023-06-20', '2026-06-19', 45, 1, '/uploads/cert/gw_00563.pdf', '涉及设备高处检修'), --- 员工 ID 9 (孙浩然-品质工程师) - 即将到期证书 (30 天内到期, 用于预警测试) -(9, '质量管理体系内审员', 'QMS-2024-00315', 'quality', 'SGS认证中心', '2024-02-01', '2026-07-25', 30, 1, '/uploads/cert/qms_00315.pdf', 'ISO9001内审资格-即将到期'), -(9, '色彩管理认证', 'CM-2024-00892', 'skill', '爱色丽色彩学院', '2024-05-10', '2027-05-09', 60, 1, '/uploads/cert/cm_00892.pdf', '色彩管理专业认证'); - --- ============================================================ --- 5. 班次规则 (hr_shift) --- 3 个班次: 白班/中班/夜班 --- ============================================================ -INSERT IGNORE INTO hr_shift (shift_name, start_time, end_time, allow_overtime, overtime_rate, night_allowance, late_threshold, early_leave_threshold, working_hours, sort_order, status, remark) VALUES -('白班', '08:00', '17:00', 1, 1.5, 0.00, 15, 15, 8.0, 1, 1, '标准白班-午休1小时'), -('中班', '16:00', '00:00', 1, 1.5, 15.00, 10, 10, 8.0, 2, 1, '中班-含夜班津贴'), -('夜班', '00:00', '08:00', 0, 2.0, 30.00, 10, 10, 8.0, 3, 1, '夜班-双倍加班倍率+津贴'); - -SET FOREIGN_KEY_CHECKS=1; diff --git a/database/hr_all_tables.sql b/database/hr_all_tables.sql deleted file mode 100644 index 7c643f1f..00000000 --- a/database/hr_all_tables.sql +++ /dev/null @@ -1,247 +0,0 @@ --- ============================================================ --- HR 模块完整数据表 DDL 汇总 --- Phase 8 — Integration & Deployment --- 依赖顺序: 无依赖表 → 员工级引用 → 跨表引用 --- ============================================================ - --- ----------------------------------------------------------- --- 1. 薪资标准表 (按岗位/技能等级) --- 无表依赖 --- ----------------------------------------------------------- -CREATE TABLE IF NOT EXISTS hr_salary_standard ( - id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, - position_code VARCHAR(50) NOT NULL COMMENT '岗位编码', - skill_level INT NOT NULL DEFAULT 1 COMMENT '技能等级 1-5', - base_salary DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT '基本工资', - piece_rate_type VARCHAR(20) COMMENT '计件类型: piece/qty/weight', - performance_base DECIMAL(10,2) DEFAULT 0.00 COMMENT '绩效基数', - allowance_night DECIMAL(10,2) DEFAULT 0.00 COMMENT '夜班津贴(元/天)', - allowance_high_temp DECIMAL(10,2) DEFAULT 0.00 COMMENT '高温津贴(元/月)', - effective_date DATE NOT NULL COMMENT '生效日期', - factory_id BIGINT UNSIGNED COMMENT '所属工厂', - status TINYINT DEFAULT 1 COMMENT '1=启用 0=停用', - remark TEXT, - create_time DATETIME DEFAULT CURRENT_TIMESTAMP, - update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - deleted TINYINT DEFAULT 0, - INDEX idx_position_code (position_code), - INDEX idx_effective_date (effective_date), - INDEX idx_factory (factory_id) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='薪资标准表'; - --- ----------------------------------------------------------- --- 2. 工序单价表 (计件工资核心) --- 无表依赖 --- ----------------------------------------------------------- -CREATE TABLE IF NOT EXISTS hr_piece_rate ( - id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, - process_code VARCHAR(50) NOT NULL COMMENT '工序编码 (印刷/裁切/包装等)', - product_type VARCHAR(50) COMMENT '产品类型', - unit_price DECIMAL(10,4) NOT NULL DEFAULT 0.0000 COMMENT '计件单价', - unit VARCHAR(20) NOT NULL DEFAULT '件' COMMENT '计量单位: 件/米/公斤', - quality_threshold DECIMAL(5,2) DEFAULT 0.00 COMMENT '质量达标率门槛 %', - effective_date DATE NOT NULL COMMENT '生效日期', - factory_id BIGINT UNSIGNED COMMENT '所属工厂', - status TINYINT DEFAULT 1 COMMENT '1=启用 0=停用', - remark TEXT, - create_time DATETIME DEFAULT CURRENT_TIMESTAMP, - update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - deleted TINYINT DEFAULT 0, - INDEX idx_process_code (process_code), - INDEX idx_product_type (product_type), - INDEX idx_effective_date (effective_date), - INDEX idx_factory (factory_id) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='工序单价表'; - --- ----------------------------------------------------------- --- 3. 班次规则表 & 排班表 --- 无表依赖 (排班表逻辑引用班次ID) --- ----------------------------------------------------------- -CREATE TABLE IF NOT EXISTS hr_shift ( - id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, - shift_name VARCHAR(50) NOT NULL COMMENT '班次名称 (早班/中班/夜班)', - start_time VARCHAR(5) NOT NULL COMMENT '开始时间 HH:mm', - end_time VARCHAR(5) NOT NULL COMMENT '结束时间 HH:mm', - allow_overtime TINYINT DEFAULT 1 COMMENT '允许加班', - overtime_rate DECIMAL(3,1) DEFAULT 1.5 COMMENT '加班倍率', - night_allowance DECIMAL(10,2) DEFAULT 0.00 COMMENT '夜班津贴', - late_threshold INT DEFAULT 15 COMMENT '迟到阈值(分钟)', - early_leave_threshold INT DEFAULT 15 COMMENT '早退阈值(分钟)', - working_hours DECIMAL(4,1) COMMENT '标准工时', - sort_order INT DEFAULT 0, - status TINYINT DEFAULT 1, - remark TEXT, - create_time DATETIME DEFAULT CURRENT_TIMESTAMP, - update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - deleted TINYINT DEFAULT 0 -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='班次规则'; - -CREATE TABLE IF NOT EXISTS hr_schedule ( - id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, - employee_id BIGINT UNSIGNED NOT NULL COMMENT '员工ID', - schedule_date DATE NOT NULL COMMENT '排班日期', - shift_id BIGINT UNSIGNED COMMENT '班次ID', - schedule_type VARCHAR(20) DEFAULT 'normal' COMMENT 'normal(正常)/overtime(加班)/leave(请假)', - source VARCHAR(20) DEFAULT 'manual' COMMENT 'manual(手动)/auto(自动生成)', - status TINYINT DEFAULT 1, - create_time DATETIME DEFAULT CURRENT_TIMESTAMP, - update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - UNIQUE KEY uk_emp_date (employee_id, schedule_date), - INDEX idx_date (schedule_date), - INDEX idx_shift (shift_id) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='排班表'; - --- ----------------------------------------------------------- --- 4. 考勤异常表 --- 逻辑依赖: employee_id (引用 sys_employee) --- ----------------------------------------------------------- -CREATE TABLE IF NOT EXISTS hr_attendance_exception ( - id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, - employee_id BIGINT UNSIGNED NOT NULL COMMENT '员工ID', - exception_date DATE NOT NULL COMMENT '异常日期', - exception_type VARCHAR(20) NOT NULL COMMENT 'late(迟到)/early_leave(早退)/absence(旷工)/overtime(加班超时)', - minutes INT DEFAULT 0 COMMENT '迟到/早退分钟数', - deduction_amount DECIMAL(10,2) DEFAULT 0.00 COMMENT '扣款金额', - status VARCHAR(20) DEFAULT 'pending' COMMENT 'pending(待处理)/approved(已批准)/rejected(已驳回)', - handler_id BIGINT UNSIGNED COMMENT '处理人ID', - handle_time DATETIME COMMENT '处理时间', - remark TEXT, - create_time DATETIME DEFAULT CURRENT_TIMESTAMP, - update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - INDEX idx_employee_date (employee_id, exception_date), - INDEX idx_type (exception_type), - INDEX idx_status (status) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='考勤异常表'; - --- ----------------------------------------------------------- --- 5. 员工薪资档案表 --- 逻辑依赖: employee_id (引用 sys_employee) --- ----------------------------------------------------------- -CREATE TABLE IF NOT EXISTS hr_salary_profile ( - id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, - employee_id BIGINT UNSIGNED NOT NULL COMMENT '员工ID', - salary_type VARCHAR(20) NOT NULL DEFAULT 'mixed' COMMENT '薪资类型: piece(计件)/time(计时)/mixed(混合)', - base_salary DECIMAL(10,2) DEFAULT 0.00 COMMENT '基本工资', - social_insurance_base DECIMAL(10,2) DEFAULT 0.00 COMMENT '社保基数', - housing_fund_rate DECIMAL(5,2) DEFAULT 0.00 COMMENT '公积金比例 %', - tax_deduction DECIMAL(10,2) DEFAULT 0.00 COMMENT '专项附加扣除(月)', - bank_account VARCHAR(50) COMMENT '银行卡号', - bank_name VARCHAR(100) COMMENT '开户行', - effective_date DATE NOT NULL COMMENT '生效日期', - status TINYINT DEFAULT 1 COMMENT '1=启用 0=停用', - remark TEXT, - create_time DATETIME DEFAULT CURRENT_TIMESTAMP, - update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - INDEX idx_employee (employee_id), - INDEX idx_effective_date (effective_date) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='员工薪资档案表'; - --- ----------------------------------------------------------- --- 6. 月度薪资计算结果表 --- 逻辑依赖: employee_id (引用 sys_employee) --- ----------------------------------------------------------- -CREATE TABLE IF NOT EXISTS hr_salary_calculation ( - id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, - employee_id BIGINT UNSIGNED NOT NULL COMMENT '员工ID', - calc_month VARCHAR(7) NOT NULL COMMENT '计算月份 (YYYY-MM)', - base_salary DECIMAL(10,2) DEFAULT 0.00 COMMENT '基本工资', - piece_salary DECIMAL(10,2) DEFAULT 0.00 COMMENT '计件工资', - overtime_salary DECIMAL(10,2) DEFAULT 0.00 COMMENT '加班工资', - performance_salary DECIMAL(10,2) DEFAULT 0.00 COMMENT '绩效奖金', - allowances DECIMAL(10,2) DEFAULT 0.00 COMMENT '津贴补贴合计', - social_insurance_personal DECIMAL(10,2) DEFAULT 0.00 COMMENT '个人社保', - housing_fund_personal DECIMAL(10,2) DEFAULT 0.00 COMMENT '个人公积金', - individual_tax DECIMAL(10,2) DEFAULT 0.00 COMMENT '个人所得税', - attendance_deduction DECIMAL(10,2) DEFAULT 0.00 COMMENT '考勤扣款', - other_deduction DECIMAL(10,2) DEFAULT 0.00 COMMENT '其他扣款', - gross_pay DECIMAL(10,2) DEFAULT 0.00 COMMENT '应发合计', - total_deduction DECIMAL(10,2) DEFAULT 0.00 COMMENT '应扣合计', - net_pay DECIMAL(10,2) DEFAULT 0.00 COMMENT '实发工资', - status VARCHAR(20) DEFAULT 'draft' COMMENT 'draft(草稿)/confirmed(确认)/paid(已发)', - calc_log JSON COMMENT '计算日志(JSON)', - create_time DATETIME DEFAULT CURRENT_TIMESTAMP, - update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - UNIQUE KEY uk_employee_month (employee_id, calc_month), - INDEX idx_month (calc_month), - INDEX idx_status (status) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='月度薪资计算结果表'; - --- ----------------------------------------------------------- --- 7. 计件产量明细表 (与MES对接) --- 逻辑依赖: employee_id (引用 sys_employee) --- ----------------------------------------------------------- -CREATE TABLE IF NOT EXISTS hr_piece_work_detail ( - id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, - employee_id BIGINT UNSIGNED NOT NULL COMMENT '员工ID', - work_date DATE NOT NULL COMMENT '工作日期', - process_code VARCHAR(50) NOT NULL COMMENT '工序编码', - product_code VARCHAR(50) COMMENT '产品编码', - quantity INT NOT NULL DEFAULT 0 COMMENT '产量', - defective_quantity INT DEFAULT 0 COMMENT '次品数量', - unit_price DECIMAL(10,4) NOT NULL DEFAULT 0.0000 COMMENT '当时单价', - amount DECIMAL(10,2) GENERATED ALWAYS AS (quantity * unit_price * (1 - defective_quantity/NULLIF(quantity,0))) STORED COMMENT '金额(自动计算)', - machine_id VARCHAR(50) COMMENT '设备ID', - mes_sync_id VARCHAR(50) COMMENT 'MES同步ID', - sync_status TINYINT DEFAULT 0 COMMENT '0=待同步 1=已同步', - remark TEXT, - create_time DATETIME DEFAULT CURRENT_TIMESTAMP, - update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - INDEX idx_employee (employee_id), - INDEX idx_work_date (work_date), - INDEX idx_process (process_code), - INDEX idx_mes_sync (mes_sync_id), - INDEX idx_employee_date (employee_id, work_date) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='计件产量明细表'; - --- ----------------------------------------------------------- --- 8. 员工证书表 --- 逻辑依赖: employee_id (引用 sys_employee) --- ----------------------------------------------------------- -CREATE TABLE IF NOT EXISTS hr_certificate ( - id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, - employee_id BIGINT UNSIGNED NOT NULL COMMENT '员工ID', - cert_name VARCHAR(200) NOT NULL COMMENT '证书名称', - cert_code VARCHAR(100) DEFAULT NULL COMMENT '证书编号', - cert_type VARCHAR(50) DEFAULT NULL COMMENT '证书类型(operation/safety/quality/skill)', - issue_authority VARCHAR(200) DEFAULT NULL COMMENT '发证机关', - issue_date DATE DEFAULT NULL COMMENT '发证日期', - expiry_date DATE DEFAULT NULL COMMENT '到期日期', - remind_days INT DEFAULT 30 COMMENT '提前提醒天数', - status TINYINT DEFAULT 1 COMMENT '状态 1有效 0过期', - file_url VARCHAR(500) DEFAULT NULL COMMENT '证书扫描件', - remark TEXT COMMENT '备注', - create_time DATETIME DEFAULT CURRENT_TIMESTAMP, - update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - deleted TINYINT DEFAULT 0, - PRIMARY KEY (id), - KEY idx_ce_employee (employee_id), - KEY idx_ce_type (cert_type), - KEY idx_ce_expiry (expiry_date), - KEY idx_ce_status (status) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='员工证书'; - --- ----------------------------------------------------------- --- 9. 员工技能矩阵表 --- 逻辑依赖: employee_id (引用 sys_employee), certificate_id (引用 hr_certificate) --- ----------------------------------------------------------- -CREATE TABLE IF NOT EXISTS hr_skill_matrix ( - id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, - employee_id BIGINT UNSIGNED NOT NULL COMMENT '员工ID', - skill_code VARCHAR(50) NOT NULL COMMENT '技能编码', - skill_name VARCHAR(100) NOT NULL COMMENT '技能名称', - skill_category VARCHAR(50) DEFAULT NULL COMMENT '技能分类(printing/binding/finishing/maintenance)', - skill_level TINYINT DEFAULT 1 COMMENT '技能等级 1-5', - certified TINYINT DEFAULT 0 COMMENT '是否认证 0/1', - certificate_id BIGINT UNSIGNED DEFAULT NULL COMMENT '关联证书ID', - assessor VARCHAR(50) DEFAULT NULL COMMENT '评估人', - assess_date DATE DEFAULT NULL COMMENT '评估日期', - next_assess_date DATE DEFAULT NULL COMMENT '下次评估日期', - remark TEXT COMMENT '备注', - create_time DATETIME DEFAULT CURRENT_TIMESTAMP, - update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - deleted TINYINT DEFAULT 0, - PRIMARY KEY (id), - KEY idx_sk_employee (employee_id), - KEY idx_sk_category (skill_category), - KEY idx_sk_level (skill_level) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='员工技能矩阵'; diff --git a/database/hr_attendance_exception.sql b/database/hr_attendance_exception.sql deleted file mode 100644 index acbde748..00000000 --- a/database/hr_attendance_exception.sql +++ /dev/null @@ -1,18 +0,0 @@ --- 考勤异常表 -CREATE TABLE IF NOT EXISTS hr_attendance_exception ( - id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, - employee_id BIGINT UNSIGNED NOT NULL COMMENT '员工ID', - exception_date DATE NOT NULL COMMENT '异常日期', - exception_type VARCHAR(20) NOT NULL COMMENT 'late(迟到)/early_leave(早退)/absence(旷工)/overtime(加班超时)', - minutes INT DEFAULT 0 COMMENT '迟到/早退分钟数', - deduction_amount DECIMAL(10,2) DEFAULT 0.00 COMMENT '扣款金额', - status VARCHAR(20) DEFAULT 'pending' COMMENT 'pending(待处理)/approved(已批准)/rejected(已驳回)', - handler_id BIGINT UNSIGNED COMMENT '处理人ID', - handle_time DATETIME COMMENT '处理时间', - remark TEXT, - create_time DATETIME DEFAULT CURRENT_TIMESTAMP, - update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - INDEX idx_employee_date (employee_id, exception_date), - INDEX idx_type (exception_type), - INDEX idx_status (status) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='考勤异常表'; diff --git a/database/hr_certificate.sql b/database/hr_certificate.sql deleted file mode 100644 index 268e98e1..00000000 --- a/database/hr_certificate.sql +++ /dev/null @@ -1,23 +0,0 @@ --- 证书表 -CREATE TABLE IF NOT EXISTS `hr_certificate` ( - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, - `employee_id` BIGINT UNSIGNED NOT NULL COMMENT '员工ID', - `cert_name` VARCHAR(200) NOT NULL COMMENT '证书名称', - `cert_code` VARCHAR(100) DEFAULT NULL COMMENT '证书编号', - `cert_type` VARCHAR(50) DEFAULT NULL COMMENT '证书类型(operation/safety/quality/skill)', - `issue_authority` VARCHAR(200) DEFAULT NULL COMMENT '发证机关', - `issue_date` DATE DEFAULT NULL COMMENT '发证日期', - `expiry_date` DATE DEFAULT NULL COMMENT '到期日期', - `remind_days` INT DEFAULT 30 COMMENT '提前提醒天数', - `status` TINYINT DEFAULT 1 COMMENT '状态 1有效 0过期', - `file_url` VARCHAR(500) DEFAULT NULL COMMENT '证书扫描件', - `remark` TEXT COMMENT '备注', - `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP, - `update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - `deleted` TINYINT DEFAULT 0, - PRIMARY KEY (`id`), - KEY `idx_ce_employee` (`employee_id`), - KEY `idx_ce_type` (`cert_type`), - KEY `idx_ce_expiry` (`expiry_date`), - KEY `idx_ce_status` (`status`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='员工证书'; diff --git a/database/hr_menu_registration.sql b/database/hr_menu_registration.sql deleted file mode 100644 index b5b58a63..00000000 --- a/database/hr_menu_registration.sql +++ /dev/null @@ -1,130 +0,0 @@ --- HR 模块菜单注册(适配 vnerpdacahng sys_menu 表结构) --- 列: menu_name, menu_code, menu_type(1=目录 2=菜单 3=按钮), icon, path, component, permission, sort_order, status, visible, is_visible, deleted --- 注意:menu_code 有 UNIQUE 约束,不能重复 - --- ============================================================ --- 1. 创建父目录(如果不存在) --- ============================================================ -INSERT INTO `sys_menu` (`menu_name`, `menu_code`, `menu_type`, `icon`, `path`, `permission`, `sort_order`, `status`, `visible`, `is_visible`, `deleted`) -SELECT '人力资源', 'hr', 1, 'Users', '/hr', 'hr:*', 12, 1, 1, 1, 0 -WHERE NOT EXISTS (SELECT 1 FROM `sys_menu` WHERE `menu_code` = 'hr' AND `deleted` = 0); - --- 获取父目录 ID -SET @hr_parent = (SELECT `id` FROM `sys_menu` WHERE `menu_code` = 'hr' AND `deleted` = 0 LIMIT 1); - --- ============================================================ --- 2. 子菜单(menu_type=2) --- ============================================================ --- 使用临时表批量插入,避免重复 -CREATE TEMPORARY TABLE IF NOT EXISTS `tmp_hr_menus` ( - `menu_name` VARCHAR(50), `menu_code` VARCHAR(50), `icon` VARCHAR(50), - `path` VARCHAR(200), `component` VARCHAR(255), `permission` VARCHAR(100), `sort_order` INT -); - -DELETE FROM `tmp_hr_menus`; -INSERT INTO `tmp_hr_menus` VALUES -('员工管理', 'hr_employee', 'UserCircle', '/hr/employee', '/hr/employee', 'hr:employee:*', 1), -('组织架构', 'hr_organization', 'Building2', '/hr/organization', '/hr/organization', 'hr:organization:*', 2), -('班次管理', 'hr_shift', 'Clock', '/hr/shifts', '/hr/shifts', 'hr:shift:*', 3), -('排班管理', 'hr_schedule', 'CalendarDays', '/hr/schedules', '/hr/schedules', 'hr:schedule:*', 4), -('考勤管理', 'hr_attendance', 'ClipboardCheck', '/hr/attendance', '/hr/attendance', 'hr:attendance:*', 5), -('工序单价', 'hr_piece_rate', 'DollarSign', '/hr/salary/piece-rate', '/hr/salary/piece-rate', 'hr:piece-rate:*', 6), -('计件产量', 'hr_piece_work', 'Hammer', '/hr/salary/piece-work', '/hr/salary/piece-work', 'hr:piece-work:*', 7), -('薪资计算', 'hr_salary_calc', 'Calculator', '/hr/salary/calculate', '/hr/salary/calculate', 'hr:salary:calc', 8), -('薪资管理', 'hr_salary', 'Wallet', '/hr/salary', '/hr/salary', 'hr:salary:*', 9), -('银行报盘', 'hr_bank_report', 'Landmark', '/hr/salary/bank-report', '/hr/salary/bank-report', 'hr:bank-report:*', 10), -('电子工资条', 'hr_payslip', 'FileText', '/hr/salary/payslips', '/hr/salary/payslips', 'hr:payslip:*', 11), -('绩效评分', 'hr_performance', 'Star', '/hr/performance', '/hr/performance', 'hr:performance:*', 12), -('培训管理', 'hr_training', 'BookOpen', '/hr/training', '/hr/training', 'hr:training:*', 13), -('技能认证', 'hr_skill', 'Award', '/hr/skills', '/hr/skills', 'hr:skill:*', 14), -('证书管理', 'hr_certificate', 'ScrollText', '/hr/certificates', '/hr/certificates', 'hr:certificate:*', 15), -('证书到期预警','hr_cert_expiry', 'BellRing', '/hr/certificates/expiring','/hr/certificates/expiring','hr:certificate:expiry',16), -('人力报表', 'hr_report', 'BarChart3', '/hr/reports', '/hr/reports', 'hr:report:*', 17), -('MES同步日志','hr_mes_sync', 'RefreshCw', '/hr/mes-sync', '/hr/mes-sync', 'hr:mes-sync:*', 18); - --- 插入不重复的菜单 -INSERT INTO `sys_menu` (`menu_name`, `menu_code`, `menu_type`, `icon`, `path`, `component`, `permission`, `sort_order`, `parent_id`, `status`, `visible`, `is_visible`, `deleted`) -SELECT t.*, @hr_parent, 1, 1, 1, 1, 0 -FROM `tmp_hr_menus` t -WHERE NOT EXISTS (SELECT 1 FROM `sys_menu` WHERE `menu_code` = t.`menu_code` AND `deleted` = 0); - --- ============================================================ --- 3. 按钮权限(menu_type=3,挂载在对应子菜单下) --- ============================================================ -CREATE TEMPORARY TABLE IF NOT EXISTS `tmp_hr_buttons` ( - `menu_code` VARCHAR(50), `btn_name` VARCHAR(50), `btn_code` VARCHAR(50), `sort` INT -); - -DELETE FROM `tmp_hr_buttons`; - --- 员工管理按钮 -INSERT INTO `tmp_hr_buttons` VALUES ('hr_employee', '新增员工', 'hr:employee:create', 1); -INSERT INTO `tmp_hr_buttons` VALUES ('hr_employee', '编辑员工', 'hr:employee:edit', 2); -INSERT INTO `tmp_hr_buttons` VALUES ('hr_employee', '删除员工', 'hr:employee:delete', 3); -INSERT INTO `tmp_hr_buttons` VALUES ('hr_employee', '导出员工', 'hr:employee:export', 4); - --- 考勤管理按钮 -INSERT INTO `tmp_hr_buttons` VALUES ('hr_attendance', '新增考勤', 'hr:attendance:create', 1); -INSERT INTO `tmp_hr_buttons` VALUES ('hr_attendance', '编辑考勤', 'hr:attendance:edit', 2); -INSERT INTO `tmp_hr_buttons` VALUES ('hr_attendance', '删除考勤', 'hr:attendance:delete', 3); -INSERT INTO `tmp_hr_buttons` VALUES ('hr_attendance', '导入考勤', 'hr:attendance:import', 4); - --- 薪资管理按钮 -INSERT INTO `tmp_hr_buttons` VALUES ('hr_salary', '编辑薪资', 'hr:salary:edit', 1); -INSERT INTO `tmp_hr_buttons` VALUES ('hr_salary', '确认发放', 'hr:salary:confirm', 2); -INSERT INTO `tmp_hr_buttons` VALUES ('hr_salary', '导出薪资', 'hr:salary:export', 3); - --- 薪资计算按钮 -INSERT INTO `tmp_hr_buttons` VALUES ('hr_salary_calc', '执行计算', 'hr:salary:calc-execute', 1); -INSERT INTO `tmp_hr_buttons` VALUES ('hr_salary_calc', '批量确认', 'hr:salary:calc-confirm', 2); - --- 培训管理按钮 -INSERT INTO `tmp_hr_buttons` VALUES ('hr_training', '新增培训', 'hr:training:create', 1); -INSERT INTO `tmp_hr_buttons` VALUES ('hr_training', '编辑培训', 'hr:training:edit', 2); -INSERT INTO `tmp_hr_buttons` VALUES ('hr_training', '删除培训', 'hr:training:delete', 3); - --- 绩效评分按钮 -INSERT INTO `tmp_hr_buttons` VALUES ('hr_performance', '录入评分', 'hr:performance:create', 1); -INSERT INTO `tmp_hr_buttons` VALUES ('hr_performance', '编辑评分', 'hr:performance:edit', 2); - --- 证书管理按钮 -INSERT INTO `tmp_hr_buttons` VALUES ('hr_certificate', '新增证书', 'hr:certificate:create', 1); -INSERT INTO `tmp_hr_buttons` VALUES ('hr_certificate', '编辑证书', 'hr:certificate:edit', 2); -INSERT INTO `tmp_hr_buttons` VALUES ('hr_certificate', '删除证书', 'hr:certificate:delete', 3); - --- MES同步按钮 -INSERT INTO `tmp_hr_buttons` VALUES ('hr_mes_sync', '手动同步', 'hr:mes-sync:manual', 1); -INSERT INTO `tmp_hr_buttons` VALUES ('hr_mes_sync', '重试同步', 'hr:mes-sync:retry', 2); - --- 插入按钮权限(挂载到父菜单) -INSERT INTO `sys_menu` (`menu_name`, `menu_code`, `menu_type`, `permission`, `sort_order`, `parent_id`, `status`, `visible`, `is_visible`, `deleted`) -SELECT b.`btn_name`, b.`btn_code`, 3, b.`btn_code`, b.`sort`, m.`id`, 1, 1, 1, 0 -FROM `tmp_hr_buttons` b -JOIN `sys_menu` m ON m.`menu_code` = b.`menu_code` AND m.`deleted` = 0 -WHERE NOT EXISTS (SELECT 1 FROM `sys_menu` WHERE `menu_code` = b.`btn_code` AND `deleted` = 0); - --- ============================================================ --- 4. 角色-菜单关联(admin role_id=1) --- ============================================================ -INSERT INTO `sys_role_menu` (`role_id`, `menu_id`) -SELECT 1, m.`id` FROM `sys_menu` m -WHERE m.`menu_code` IN ( - 'hr', 'hr_employee', 'hr_organization', 'hr_shift', 'hr_schedule', - 'hr_attendance', 'hr_piece_rate', 'hr_piece_work', 'hr_salary_calc', - 'hr_salary', 'hr_bank_report', 'hr_payslip', 'hr_performance', - 'hr_training', 'hr_skill', 'hr_certificate', 'hr_cert_expiry', - 'hr_report', 'hr_mes_sync' -) AND m.`deleted` = 0 -AND NOT EXISTS (SELECT 1 FROM `sys_role_menu` WHERE `role_id` = 1 AND `menu_id` = m.`id`); - --- 按钮权限关联(admin 角色) -INSERT INTO `sys_role_menu` (`role_id`, `menu_id`) -SELECT 1, m.`id` FROM `sys_menu` m -WHERE m.`menu_type` = 3 AND m.`menu_code` LIKE 'hr:%' AND m.`deleted` = 0 -AND NOT EXISTS (SELECT 1 FROM `sys_role_menu` WHERE `role_id` = 1 AND `menu_id` = m.`id`); - --- ============================================================ --- 5. 清理临时表 --- ============================================================ -DROP TEMPORARY TABLE IF EXISTS `tmp_hr_menus`; -DROP TEMPORARY TABLE IF EXISTS `tmp_hr_buttons`; diff --git a/database/hr_organization.sql b/database/hr_organization.sql deleted file mode 100644 index fc2ef782..00000000 --- a/database/hr_organization.sql +++ /dev/null @@ -1,118 +0,0 @@ --- 六级组织架构: 集团 -> 法人 -> 工厂 -> 车间 -> 班组 -> 岗位 - --- 1. 集团 -CREATE TABLE IF NOT EXISTS org_group ( - id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, - code VARCHAR(50) NOT NULL COMMENT '集团编码', - name VARCHAR(100) NOT NULL COMMENT '集团名称', - sort_order INT DEFAULT 0 COMMENT '排序', - status TINYINT DEFAULT 1 COMMENT '1=启用 0=停用', - remark TEXT, - create_time DATETIME DEFAULT CURRENT_TIMESTAMP, - update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - deleted TINYINT DEFAULT 0, - UNIQUE KEY uk_group_code (code) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='集团'; - --- 2. 法人 -CREATE TABLE IF NOT EXISTS org_legal_entity ( - id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, - group_id BIGINT UNSIGNED NOT NULL COMMENT '所属集团', - code VARCHAR(50) NOT NULL COMMENT '法人编码', - name VARCHAR(100) NOT NULL COMMENT '法人名称', - tax_id VARCHAR(50) COMMENT '税号', - legal_person VARCHAR(50) COMMENT '法定代表人', - sort_order INT DEFAULT 0, - status TINYINT DEFAULT 1, - remark TEXT, - create_time DATETIME DEFAULT CURRENT_TIMESTAMP, - update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - deleted TINYINT DEFAULT 0, - UNIQUE KEY uk_le_code (code), - INDEX idx_group (group_id) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='法人'; - --- 3. 工厂 -CREATE TABLE IF NOT EXISTS org_factory ( - id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, - legal_entity_id BIGINT UNSIGNED NOT NULL COMMENT '所属法人', - code VARCHAR(50) NOT NULL COMMENT '工厂编码', - name VARCHAR(100) NOT NULL COMMENT '工厂名称', - address VARCHAR(255) COMMENT '地址', - contact_person VARCHAR(50) COMMENT '联系人', - contact_phone VARCHAR(20) COMMENT '联系电话', - sort_order INT DEFAULT 0, - status TINYINT DEFAULT 1, - remark TEXT, - create_time DATETIME DEFAULT CURRENT_TIMESTAMP, - update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - deleted TINYINT DEFAULT 0, - UNIQUE KEY uk_factory_code (code), - INDEX idx_legal_entity (legal_entity_id) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='工厂'; - --- 4. 车间 -CREATE TABLE IF NOT EXISTS org_workshop ( - id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, - factory_id BIGINT UNSIGNED NOT NULL COMMENT '所属工厂', - code VARCHAR(50) NOT NULL COMMENT '车间编码', - name VARCHAR(100) NOT NULL COMMENT '车间名称', - manager_name VARCHAR(50) COMMENT '车间主任', - sort_order INT DEFAULT 0, - status TINYINT DEFAULT 1, - remark TEXT, - create_time DATETIME DEFAULT CURRENT_TIMESTAMP, - update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - deleted TINYINT DEFAULT 0, - UNIQUE KEY uk_workshop_code (code), - INDEX idx_factory (factory_id) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='车间'; - --- 5. 班组 -CREATE TABLE IF NOT EXISTS org_team ( - id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, - workshop_id BIGINT UNSIGNED NOT NULL COMMENT '所属车间', - code VARCHAR(50) NOT NULL COMMENT '班组编码', - name VARCHAR(100) NOT NULL COMMENT '班组名称', - team_leader VARCHAR(50) COMMENT '班组长', - sort_order INT DEFAULT 0, - status TINYINT DEFAULT 1, - remark TEXT, - create_time DATETIME DEFAULT CURRENT_TIMESTAMP, - update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - deleted TINYINT DEFAULT 0, - UNIQUE KEY uk_team_code (code), - INDEX idx_workshop (workshop_id) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='班组'; - --- 6. 岗位 -CREATE TABLE IF NOT EXISTS org_position ( - id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, - team_id BIGINT UNSIGNED COMMENT '所属班组', - code VARCHAR(50) NOT NULL COMMENT '岗位编码', - name VARCHAR(100) NOT NULL COMMENT '岗位名称', - skill_level INT DEFAULT 1 COMMENT '技能等级 1-5', - base_salary_range VARCHAR(50) COMMENT '基本工资范围', - sort_order INT DEFAULT 0, - status TINYINT DEFAULT 1, - remark TEXT, - create_time DATETIME DEFAULT CURRENT_TIMESTAMP, - update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - deleted TINYINT DEFAULT 0, - UNIQUE KEY uk_position_code (code), - INDEX idx_team (team_id) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='岗位'; - --- 员工-岗位关联表 (支持员工多岗位) -CREATE TABLE IF NOT EXISTS hr_employee_position ( - id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, - employee_id BIGINT UNSIGNED NOT NULL, - position_id BIGINT UNSIGNED NOT NULL, - is_primary TINYINT DEFAULT 0 COMMENT '1=主岗', - start_date DATE COMMENT '任职日期', - end_date DATE COMMENT '离职日期', - status TINYINT DEFAULT 1, - create_time DATETIME DEFAULT CURRENT_TIMESTAMP, - UNIQUE KEY uk_emp_pos (employee_id, position_id), - INDEX idx_position (position_id) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='员工岗位关联'; diff --git a/database/hr_piece_rate.sql b/database/hr_piece_rate.sql deleted file mode 100644 index da1ca3e4..00000000 --- a/database/hr_piece_rate.sql +++ /dev/null @@ -1,20 +0,0 @@ --- 工序单价表 (计件工资核心) -CREATE TABLE IF NOT EXISTS hr_piece_rate ( - id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, - process_code VARCHAR(50) NOT NULL COMMENT '工序编码 (印刷/裁切/包装等)', - product_type VARCHAR(50) COMMENT '产品类型', - unit_price DECIMAL(10,4) NOT NULL DEFAULT 0.0000 COMMENT '计件单价', - unit VARCHAR(20) NOT NULL DEFAULT '件' COMMENT '计量单位: 件/米/公斤', - quality_threshold DECIMAL(5,2) DEFAULT 0.00 COMMENT '质量达标率门槛 %', - effective_date DATE NOT NULL COMMENT '生效日期', - factory_id BIGINT UNSIGNED COMMENT '所属工厂', - status TINYINT DEFAULT 1 COMMENT '1=启用 0=停用', - remark TEXT, - create_time DATETIME DEFAULT CURRENT_TIMESTAMP, - update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - deleted TINYINT DEFAULT 0, - INDEX idx_process_code (process_code), - INDEX idx_product_type (product_type), - INDEX idx_effective_date (effective_date), - INDEX idx_factory (factory_id) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='工序单价表'; diff --git a/database/hr_piece_work_detail.sql b/database/hr_piece_work_detail.sql deleted file mode 100644 index 632aaf55..00000000 --- a/database/hr_piece_work_detail.sql +++ /dev/null @@ -1,23 +0,0 @@ --- 计件产量明细表 (与MES对接) -CREATE TABLE IF NOT EXISTS hr_piece_work_detail ( - id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, - employee_id BIGINT UNSIGNED NOT NULL COMMENT '员工ID', - work_date DATE NOT NULL COMMENT '工作日期', - process_code VARCHAR(50) NOT NULL COMMENT '工序编码', - product_code VARCHAR(50) COMMENT '产品编码', - quantity INT NOT NULL DEFAULT 0 COMMENT '产量', - defective_quantity INT DEFAULT 0 COMMENT '次品数量', - unit_price DECIMAL(10,4) NOT NULL DEFAULT 0.0000 COMMENT '当时单价', - amount DECIMAL(10,2) GENERATED ALWAYS AS (quantity * unit_price * (1 - defective_quantity/NULLIF(quantity,0))) STORED COMMENT '金额(自动计算)', - machine_id VARCHAR(50) COMMENT '设备ID', - mes_sync_id VARCHAR(50) COMMENT 'MES同步ID', - sync_status TINYINT DEFAULT 0 COMMENT '0=待同步 1=已同步', - remark TEXT, - create_time DATETIME DEFAULT CURRENT_TIMESTAMP, - update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - INDEX idx_employee (employee_id), - INDEX idx_work_date (work_date), - INDEX idx_process (process_code), - INDEX idx_mes_sync (mes_sync_id), - INDEX idx_employee_date (employee_id, work_date) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='计件产量明细表'; diff --git a/database/hr_salary_calculation.sql b/database/hr_salary_calculation.sql deleted file mode 100644 index 67d1fd03..00000000 --- a/database/hr_salary_calculation.sql +++ /dev/null @@ -1,33 +0,0 @@ --- 月度薪资计算结果表 -CREATE TABLE IF NOT EXISTS hr_salary_calculation ( - id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, - employee_id BIGINT UNSIGNED NOT NULL COMMENT '员工ID', - calc_month VARCHAR(7) NOT NULL COMMENT '计算月份 (YYYY-MM)', - - -- 应发项 - base_salary DECIMAL(10,2) DEFAULT 0.00 COMMENT '基本工资', - piece_salary DECIMAL(10,2) DEFAULT 0.00 COMMENT '计件工资', - overtime_salary DECIMAL(10,2) DEFAULT 0.00 COMMENT '加班工资', - performance_salary DECIMAL(10,2) DEFAULT 0.00 COMMENT '绩效奖金', - allowances DECIMAL(10,2) DEFAULT 0.00 COMMENT '津贴补贴合计', - - -- 应扣项 - social_insurance_personal DECIMAL(10,2) DEFAULT 0.00 COMMENT '个人社保', - housing_fund_personal DECIMAL(10,2) DEFAULT 0.00 COMMENT '个人公积金', - individual_tax DECIMAL(10,2) DEFAULT 0.00 COMMENT '个人所得税', - attendance_deduction DECIMAL(10,2) DEFAULT 0.00 COMMENT '考勤扣款', - other_deduction DECIMAL(10,2) DEFAULT 0.00 COMMENT '其他扣款', - - -- 合计 - gross_pay DECIMAL(10,2) DEFAULT 0.00 COMMENT '应发合计', - total_deduction DECIMAL(10,2) DEFAULT 0.00 COMMENT '应扣合计', - net_pay DECIMAL(10,2) DEFAULT 0.00 COMMENT '实发工资', - - status VARCHAR(20) DEFAULT 'draft' COMMENT 'draft(草稿)/confirmed(确认)/paid(已发)', - calc_log JSON COMMENT '计算日志(JSON)', - create_time DATETIME DEFAULT CURRENT_TIMESTAMP, - update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - UNIQUE KEY uk_employee_month (employee_id, calc_month), - INDEX idx_month (calc_month), - INDEX idx_status (status) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='月度薪资计算结果表'; diff --git a/database/hr_salary_profile.sql b/database/hr_salary_profile.sql deleted file mode 100644 index d9516e3f..00000000 --- a/database/hr_salary_profile.sql +++ /dev/null @@ -1,19 +0,0 @@ --- 员工薪资档案表 -CREATE TABLE IF NOT EXISTS hr_salary_profile ( - id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, - employee_id BIGINT UNSIGNED NOT NULL COMMENT '员工ID', - salary_type VARCHAR(20) NOT NULL DEFAULT 'mixed' COMMENT '薪资类型: piece(计件)/time(计时)/mixed(混合)', - base_salary DECIMAL(10,2) DEFAULT 0.00 COMMENT '基本工资', - social_insurance_base DECIMAL(10,2) DEFAULT 0.00 COMMENT '社保基数', - housing_fund_rate DECIMAL(5,2) DEFAULT 0.00 COMMENT '公积金比例 %', - tax_deduction DECIMAL(10,2) DEFAULT 0.00 COMMENT '专项附加扣除(月)', - bank_account VARCHAR(50) COMMENT '银行卡号', - bank_name VARCHAR(100) COMMENT '开户行', - effective_date DATE NOT NULL COMMENT '生效日期', - status TINYINT DEFAULT 1 COMMENT '1=启用 0=停用', - remark TEXT, - create_time DATETIME DEFAULT CURRENT_TIMESTAMP, - update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - INDEX idx_employee (employee_id), - INDEX idx_effective_date (effective_date) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='员工薪资档案表'; diff --git a/database/hr_salary_standard.sql b/database/hr_salary_standard.sql deleted file mode 100644 index c407aa92..00000000 --- a/database/hr_salary_standard.sql +++ /dev/null @@ -1,21 +0,0 @@ --- 薪资标准表 (按岗位/技能等级) -CREATE TABLE IF NOT EXISTS hr_salary_standard ( - id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, - position_code VARCHAR(50) NOT NULL COMMENT '岗位编码', - skill_level INT NOT NULL DEFAULT 1 COMMENT '技能等级 1-5', - base_salary DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT '基本工资', - piece_rate_type VARCHAR(20) COMMENT '计件类型: piece/qty/weight', - performance_base DECIMAL(10,2) DEFAULT 0.00 COMMENT '绩效基数', - allowance_night DECIMAL(10,2) DEFAULT 0.00 COMMENT '夜班津贴(元/天)', - allowance_high_temp DECIMAL(10,2) DEFAULT 0.00 COMMENT '高温津贴(元/月)', - effective_date DATE NOT NULL COMMENT '生效日期', - factory_id BIGINT UNSIGNED COMMENT '所属工厂', - status TINYINT DEFAULT 1 COMMENT '1=启用 0=停用', - remark TEXT, - create_time DATETIME DEFAULT CURRENT_TIMESTAMP, - update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - deleted TINYINT DEFAULT 0, - INDEX idx_position_code (position_code), - INDEX idx_effective_date (effective_date), - INDEX idx_factory (factory_id) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='薪资标准表'; diff --git a/database/hr_shift.sql b/database/hr_shift.sql deleted file mode 100644 index 3f3b26ae..00000000 --- a/database/hr_shift.sql +++ /dev/null @@ -1,35 +0,0 @@ --- 班次规则表 -CREATE TABLE IF NOT EXISTS hr_shift ( - id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, - shift_name VARCHAR(50) NOT NULL COMMENT '班次名称 (早班/中班/夜班)', - start_time VARCHAR(5) NOT NULL COMMENT '开始时间 HH:mm', - end_time VARCHAR(5) NOT NULL COMMENT '结束时间 HH:mm', - allow_overtime TINYINT DEFAULT 1 COMMENT '允许加班', - overtime_rate DECIMAL(3,1) DEFAULT 1.5 COMMENT '加班倍率', - night_allowance DECIMAL(10,2) DEFAULT 0.00 COMMENT '夜班津贴', - late_threshold INT DEFAULT 15 COMMENT '迟到阈值(分钟)', - early_leave_threshold INT DEFAULT 15 COMMENT '早退阈值(分钟)', - working_hours DECIMAL(4,1) COMMENT '标准工时', - sort_order INT DEFAULT 0, - status TINYINT DEFAULT 1, - remark TEXT, - create_time DATETIME DEFAULT CURRENT_TIMESTAMP, - update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - deleted TINYINT DEFAULT 0 -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='班次规则'; - --- 排班表 -CREATE TABLE IF NOT EXISTS hr_schedule ( - id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, - employee_id BIGINT UNSIGNED NOT NULL COMMENT '员工ID', - schedule_date DATE NOT NULL COMMENT '排班日期', - shift_id BIGINT UNSIGNED COMMENT '班次ID', - schedule_type VARCHAR(20) DEFAULT 'normal' COMMENT 'normal(正常)/overtime(加班)/leave(请假)', - source VARCHAR(20) DEFAULT 'manual' COMMENT 'manual(手动)/auto(自动生成)', - status TINYINT DEFAULT 1, - create_time DATETIME DEFAULT CURRENT_TIMESTAMP, - update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - UNIQUE KEY uk_emp_date (employee_id, schedule_date), - INDEX idx_date (schedule_date), - INDEX idx_shift (shift_id) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='排班表'; diff --git a/database/hr_skill_matrix.sql b/database/hr_skill_matrix.sql deleted file mode 100644 index d8609557..00000000 --- a/database/hr_skill_matrix.sql +++ /dev/null @@ -1,46 +0,0 @@ --- 技能矩阵表:岗位 × 技能 × 等级 -CREATE TABLE IF NOT EXISTS `hr_skill_matrix` ( - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, - `employee_id` BIGINT UNSIGNED NOT NULL COMMENT '员工ID', - `skill_code` VARCHAR(50) NOT NULL COMMENT '技能编码', - `skill_name` VARCHAR(100) NOT NULL COMMENT '技能名称', - `skill_category` VARCHAR(50) DEFAULT NULL COMMENT '技能分类(printing/binding/finishing/maintenance)', - `skill_level` TINYINT DEFAULT 1 COMMENT '技能等级 1-5', - `certified` TINYINT DEFAULT 0 COMMENT '是否认证 0/1', - `certificate_id` BIGINT UNSIGNED DEFAULT NULL COMMENT '关联证书ID', - `assessor` VARCHAR(50) DEFAULT NULL COMMENT '评估人', - `assess_date` DATE DEFAULT NULL COMMENT '评估日期', - `next_assess_date` DATE DEFAULT NULL COMMENT '下次评估日期', - `remark` TEXT COMMENT '备注', - `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP, - `update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - `deleted` TINYINT DEFAULT 0, - PRIMARY KEY (`id`), - KEY `idx_sk_employee` (`employee_id`), - KEY `idx_sk_category` (`skill_category`), - KEY `idx_sk_level` (`skill_level`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='员工技能矩阵'; - --- 证书表 -CREATE TABLE IF NOT EXISTS `hr_certificate` ( - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, - `employee_id` BIGINT UNSIGNED NOT NULL COMMENT '员工ID', - `cert_name` VARCHAR(200) NOT NULL COMMENT '证书名称', - `cert_code` VARCHAR(100) DEFAULT NULL COMMENT '证书编号', - `cert_type` VARCHAR(50) DEFAULT NULL COMMENT '证书类型(operation/safety/quality/skill)', - `issue_authority` VARCHAR(200) DEFAULT NULL COMMENT '发证机关', - `issue_date` DATE DEFAULT NULL COMMENT '发证日期', - `expiry_date` DATE DEFAULT NULL COMMENT '到期日期', - `remind_days` INT DEFAULT 30 COMMENT '提前提醒天数', - `status` TINYINT DEFAULT 1 COMMENT '状态 1有效 0过期', - `file_url` VARCHAR(500) DEFAULT NULL COMMENT '证书扫描件', - `remark` TEXT COMMENT '备注', - `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP, - `update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - `deleted` TINYINT DEFAULT 0, - PRIMARY KEY (`id`), - KEY `idx_ce_employee` (`employee_id`), - KEY `idx_ce_type` (`cert_type`), - KEY `idx_ce_expiry` (`expiry_date`), - KEY `idx_ce_status` (`status`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='员工证书'; diff --git a/database/init-db.js b/database/init-db.js index 35db13cc..1a313c51 100644 --- a/database/init-db.js +++ b/database/init-db.js @@ -2,13 +2,12 @@ const mysql = require('../erp-project/node_modules/mysql2/promise'); const fs = require('fs'); const path = require('path'); -// 数据库配置(从环境变量读取,禁止硬编码) -require('dotenv').config(); +// 数据库配置 const config = { - host: process.env.DB_HOST || '127.0.0.1', - user: process.env.DB_USER || 'root', - password: process.env.DB_PASSWORD || '', - database: process.env.DB_NAME || 'vnerpdacahng', + host: '127.0.0.1', + user: 'root', + password: 'Snqig521223', + database: 'vnerpdacahng', charset: 'utf8mb4', multipleStatements: true // 允许多条语句 }; diff --git a/database/migrations/002_add_outbox_dispatching.sql b/database/migrations/002_add_outbox_dispatching.sql deleted file mode 100644 index bffa1440..00000000 --- a/database/migrations/002_add_outbox_dispatching.sql +++ /dev/null @@ -1,12 +0,0 @@ --- ========================================== --- 迁移: 002_add_outbox_dispatching --- 用途: 支持 OutboxPoller 原子 claim(SELECT FOR UPDATE SKIP LOCKED) --- 关联代码: src/infrastructure/repositories/MysqlDomainEventOutboxRepository.ts --- 说明: 新增 dispatching 中间状态与 claim/dispatch 时间戳, --- 多实例并发时通过 SKIP LOCKED 避免重复消费 --- 执行: mysql -u root -p vnerpdacahng < 002_add_outbox_dispatching.sql --- ========================================== - -ALTER TABLE `domain_event_outbox` - ADD COLUMN `claimed_at` DATETIME DEFAULT NULL COMMENT '最近一次被 claim 的时间(status=dispatching 时写入)' AFTER `processed_at`, - ADD COLUMN `dispatched_at` DATETIME DEFAULT NULL COMMENT '成功分发到 Stream 的时间(status=processed 前置)' AFTER `claimed_at`; diff --git a/database/migrations/003_create_event_processed.sql b/database/migrations/003_create_event_processed.sql deleted file mode 100644 index 7ac8f7e8..00000000 --- a/database/migrations/003_create_event_processed.sql +++ /dev/null @@ -1,16 +0,0 @@ --- ========================================== --- 迁移: 003_create_event_processed --- 用途: 事件处理器幂等性保障(防止 OutboxPoller 重试或 Stream 消费者重启时重复执行) --- 关联代码: src/infrastructure/event-bus/IdempotencyGuard.ts --- 说明: event_id + handler_name 联合唯一,同一事件同一处理器只成功记录一次 --- 执行: mysql -u root -p vnerpdacahng < 003_create_event_processed.sql --- ========================================== - -CREATE TABLE IF NOT EXISTS `sys_event_processed` ( - `id` BIGINT AUTO_INCREMENT PRIMARY KEY, - `event_id` BIGINT NOT NULL COMMENT 'domain_event_outbox.id', - `handler_name` VARCHAR(255) NOT NULL COMMENT '处理器类名', - `processed_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - UNIQUE KEY `uk_event_handler` (`event_id`, `handler_name`), - INDEX `idx_processed_at` (`processed_at`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='事件幂等表'; diff --git a/database/migrations/004_add_status_to_event_processed.sql b/database/migrations/004_add_status_to_event_processed.sql deleted file mode 100644 index a49e57a5..00000000 --- a/database/migrations/004_add_status_to_event_processed.sql +++ /dev/null @@ -1,19 +0,0 @@ --- ========================================== --- 迁移: 004_add_status_to_event_processed --- 用途: 两阶段幂等标记(processing → processed),解决 mark-before-execute 崩溃窗口 --- 关联代码: src/infrastructure/event-bus/IdempotencyGuard.ts --- 说明: --- - status='processing': checkAndMark 时插入,表示 handler 正在执行 --- - status='processed': handler 成功后由 markAsProcessed 更新 --- - 崩溃恢复: reclaimStaleProcessing 清理超过 5 分钟的 processing 记录 --- - 旧数据默认 'processed'(向后兼容) --- 执行: mysql -u root -p vnerpdacahng < 004_add_status_to_event_processed.sql --- ========================================== - -ALTER TABLE `sys_event_processed` - ADD COLUMN `status` VARCHAR(20) NOT NULL DEFAULT 'processed' - COMMENT 'processing-处理中, processed-已处理' - AFTER `handler_name`; - -ALTER TABLE `sys_event_processed` - ADD INDEX `idx_status_processed_at` (`status`, `processed_at`); diff --git a/database/migrations/005_align_warehouse_schema.sql b/database/migrations/005_align_warehouse_schema.sql deleted file mode 100644 index 22a813eb..00000000 --- a/database/migrations/005_align_warehouse_schema.sql +++ /dev/null @@ -1,434 +0,0 @@ --- ========================================== --- 迁移: 005_align_warehouse_schema --- 用途: 仓储模块 schema 补全 + 列对齐 --- 关联代码: src/app/api/warehouse/**, src/application/handlers/InventorySyncHandler.ts --- 说明: --- 1. 补齐 11 张代码已引用但 SQL 中缺失的表(inv_outbound_order/item, inv_transfer_order/item, --- inv_stocktaking/item, inv_stock_adjust/item, inv_sales_outbound/item, --- inv_production_inbound/item, inv_unit_conversion, inv_warehouse_log) --- 2. 为 inv_inventory_batch 追加代码引用的列(available_qty, material_code, material_name, --- unit_price, locked_qty, produce_date, deleted, version, update_time) --- 3. 为 inv_inventory 追加 stocktaking_flag 列(盘点冻结出库用) --- 4. 将 inv_inventory_batch.status 注释更新为 1-normal 2-frozen 3-expired(语义不变,仅文档对齐代码) --- 执行: mysql -u root -p vnerpdacahng < 005_align_warehouse_schema.sql --- 注意: 本迁移幂等,可重复执行(使用 IF NOT EXISTS / 检查列存在) --- ========================================== - --- ========================================== --- 第 1 部分:修改已有表(inv_inventory_batch + inv_inventory) --- ========================================== - --- 1.1 inv_inventory_batch 追加列(按代码引用对齐) --- 注意:使用 PROCEDURE 检查列是否存在,避免 ALTER 重复执行报错 -DROP PROCEDURE IF EXISTS add_column_if_not_exists; -DELIMITER // -CREATE PROCEDURE add_column_if_not_exists( - IN tbl VARCHAR(64), - IN col VARCHAR(64), - IN col_def TEXT -) -BEGIN - IF NOT EXISTS ( - SELECT 1 FROM information_schema.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = tbl AND COLUMN_NAME = col - ) THEN - SET @ddl = CONCAT('ALTER TABLE `', tbl, '` ADD COLUMN `', col, '` ', col_def); - PREPARE stmt FROM @ddl; - EXECUTE stmt; - DEALLOCATE PREPARE stmt; - END IF; -END// -DELIMITER ; - -CALL add_column_if_not_exists('inv_inventory_batch', 'material_code', "VARCHAR(50) NULL COMMENT '物料编码(冗余,便于查询)' AFTER `material_id`"); -CALL add_column_if_not_exists('inv_inventory_batch', 'material_name', "VARCHAR(200) NULL COMMENT '物料名称(冗余)' AFTER `material_code`"); -CALL add_column_if_not_exists('inv_inventory_batch', 'available_qty', "DECIMAL(18,4) DEFAULT 0 COMMENT '可用数量(= quantity - locked_qty - 已出库)' AFTER `quantity`"); -CALL add_column_if_not_exists('inv_inventory_batch', 'locked_qty', "DECIMAL(18,4) DEFAULT 0 COMMENT '锁定数量' AFTER `available_qty`"); -CALL add_column_if_not_exists('inv_inventory_batch', 'unit_price', "DECIMAL(18,4) DEFAULT 0 COMMENT '入库单价(冗余,用于成本核算)' AFTER `unit_cost`"); -CALL add_column_if_not_exists('inv_inventory_batch', 'produce_date', "DATE NULL COMMENT '生产日期' AFTER `inbound_date`"); -CALL add_column_if_not_exists('inv_inventory_batch', 'deleted', "TINYINT NOT NULL DEFAULT 0 COMMENT '软删除: 0-正常, 1-已删除' AFTER `status`"); -CALL add_column_if_not_exists('inv_inventory_batch', 'version', "INT NOT NULL DEFAULT 0 COMMENT '乐观锁版本号' AFTER `deleted`"); -CALL add_column_if_not_exists('inv_inventory_batch', 'update_time', "DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间' AFTER `create_time`"); - --- 1.2 inv_inventory 追加 stocktaking_flag 列 -CALL add_column_if_not_exists('inv_inventory', 'stocktaking_flag', "TINYINT NOT NULL DEFAULT 0 COMMENT '0-正常 1-盘点中(冻结出库)' AFTER `available_qty`"); - --- 1.3 更新 inv_inventory_batch.status 注释(语义不变,仅文档对齐代码) -ALTER TABLE `inv_inventory_batch` - MODIFY COLUMN `status` TINYINT NOT NULL DEFAULT 1 COMMENT '1-normal 可用, 2-frozen 冻结, 3-expired 过期'; - --- 1.4 追加索引(IF NOT EXISTS 在 MySQL 8.0+ 支持) -ALTER TABLE `inv_inventory_batch` - ADD INDEX IF NOT EXISTS `idx_warehouse` (`warehouse_id`); -ALTER TABLE `inv_inventory_batch` - ADD INDEX IF NOT EXISTS `idx_status` (`status`); -ALTER TABLE `inv_inventory_batch` - ADD INDEX IF NOT EXISTS `idx_material_warehouse_status` (`material_id`, `warehouse_id`, `status`, `deleted`); -ALTER TABLE `inv_inventory` - ADD INDEX IF NOT EXISTS `idx_stocktaking_flag` (`stocktaking_flag`); - -DROP PROCEDURE IF EXISTS add_column_if_not_exists; - --- ========================================== --- 第 2 部分:创建 11 张缺失表(CREATE TABLE IF NOT EXISTS 保证幂等) --- ========================================== - --- 出库单主表 -CREATE TABLE IF NOT EXISTS `inv_outbound_order` ( - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '出库单ID', - `order_no` VARCHAR(30) NOT NULL COMMENT '出库单号', - `order_date` DATE NOT NULL COMMENT '出库日期', - `outbound_type` VARCHAR(20) NOT NULL DEFAULT 'production' COMMENT 'production-生产出库, sales-销售出库, return-退货出库, other-其他', - `warehouse_id` BIGINT UNSIGNED NOT NULL COMMENT '仓库ID', - `warehouse_code` VARCHAR(50) COMMENT '仓库编码(冗余)', - `warehouse_name` VARCHAR(100) COMMENT '仓库名称(冗余)', - `customer_id` BIGINT UNSIGNED COMMENT '客户ID(销售出库用)', - `customer_name` VARCHAR(100) COMMENT '客户名称(冗余)', - `work_order_id` BIGINT UNSIGNED COMMENT '工单ID(生产出库用)', - `work_order_no` VARCHAR(50) COMMENT '工单编号(冗余)', - `total_qty` DECIMAL(18,4) DEFAULT 0 COMMENT '总数量', - `total_amount` DECIMAL(18,4) DEFAULT 0 COMMENT '总金额', - `currency` VARCHAR(10) DEFAULT 'CNY' COMMENT '币种', - `status` VARCHAR(20) NOT NULL DEFAULT 'pending' COMMENT 'pending-待审, approved-已审, completed-已完成, cancelled-已取消', - `audit_status` TINYINT DEFAULT 0 COMMENT '0-待审, 1-通过, 2-驳回', - `auditor_id` BIGINT UNSIGNED COMMENT '审核人ID', - `auditor_name` VARCHAR(50) COMMENT '审核人姓名', - `audit_time` DATETIME COMMENT '审核时间', - `audit_remark` VARCHAR(500) COMMENT '审核备注', - `operator_id` BIGINT UNSIGNED COMMENT '操作人ID', - `operator_name` VARCHAR(50) COMMENT '操作人姓名', - `finance_posted` TINYINT DEFAULT 0 COMMENT '是否已生成财务凭证: 0-否, 1-是', - `version` INT DEFAULT 0 COMMENT '乐观锁版本号', - `remark` VARCHAR(500) COMMENT '备注', - `deleted` TINYINT DEFAULT 0 COMMENT '删除标记', - `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - `update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', - PRIMARY KEY (`id`), - UNIQUE KEY `uk_order_no` (`order_no`), - KEY `idx_status` (`status`), - KEY `idx_warehouse` (`warehouse_id`), - KEY `idx_order_date` (`order_date`), - KEY `idx_outbound_type` (`outbound_type`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='出库单主表'; - --- 出库单明细表 -CREATE TABLE IF NOT EXISTS `inv_outbound_item` ( - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '明细ID', - `order_id` BIGINT UNSIGNED NOT NULL COMMENT '出库单ID', - `material_id` BIGINT UNSIGNED NOT NULL COMMENT '物料ID', - `material_code` VARCHAR(50) COMMENT '物料编码(冗余)', - `material_name` VARCHAR(200) COMMENT '物料名称(冗余)', - `material_spec` VARCHAR(200) COMMENT '物料规格', - `batch_no` VARCHAR(50) COMMENT '批次号', - `batch_id` BIGINT UNSIGNED COMMENT '批次ID', - `quantity` DECIMAL(18,4) NOT NULL COMMENT '数量', - `unit` VARCHAR(20) COMMENT '单位', - `unit_price` DECIMAL(18,4) DEFAULT 0 COMMENT '单价', - `amount` DECIMAL(18,4) DEFAULT 0 COMMENT '金额', - `warehouse_location` VARCHAR(50) COMMENT '库位', - `remark` VARCHAR(255) COMMENT '备注', - `deleted` TINYINT DEFAULT 0 COMMENT '删除标记', - `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - PRIMARY KEY (`id`), - KEY `idx_order` (`order_id`), - KEY `idx_material` (`material_id`), - KEY `idx_batch` (`batch_no`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='出库单明细表'; - --- 调拨单主表 -CREATE TABLE IF NOT EXISTS `inv_transfer_order` ( - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '调拨单ID', - `transfer_no` VARCHAR(30) NOT NULL COMMENT '调拨单号', - `type` TINYINT NOT NULL COMMENT '1-库位调拨, 2-仓库调拨', - `from_warehouse_id` BIGINT UNSIGNED NOT NULL COMMENT '源仓库ID', - `to_warehouse_id` BIGINT UNSIGNED NOT NULL COMMENT '目标仓库ID', - `from_location` VARCHAR(50) COMMENT '源库位', - `to_location` VARCHAR(50) COMMENT '目标库位', - `status` TINYINT NOT NULL DEFAULT 0 COMMENT '0-草稿, 1-待审批, 2-已出库, 3-已入库, 4-已取消', - `applicant_id` BIGINT UNSIGNED COMMENT '申请人ID', - `applicant_name` VARCHAR(50) COMMENT '申请人姓名', - `approver_id` BIGINT UNSIGNED COMMENT '审批人ID', - `approver_name` VARCHAR(50) COMMENT '审批人姓名', - `operator_id` BIGINT UNSIGNED COMMENT '操作人ID', - `operator_name` VARCHAR(50) COMMENT '操作人姓名', - `out_time` DATETIME COMMENT '出库时间', - `in_time` DATETIME COMMENT '入库时间', - `total_qty` DECIMAL(18,4) DEFAULT 0 COMMENT '总数量', - `total_amount` DECIMAL(18,4) DEFAULT 0 COMMENT '总金额', - `version` INT DEFAULT 0 COMMENT '乐观锁版本号', - `remark` VARCHAR(500) COMMENT '备注', - `deleted` TINYINT DEFAULT 0 COMMENT '删除标记', - `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - `update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', - PRIMARY KEY (`id`), - UNIQUE KEY `uk_transfer_no` (`transfer_no`), - KEY `idx_status` (`status`), - KEY `idx_from_warehouse` (`from_warehouse_id`), - KEY `idx_to_warehouse` (`to_warehouse_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='调拨单主表'; - --- 调拨单明细表 -CREATE TABLE IF NOT EXISTS `inv_transfer_item` ( - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '明细ID', - `transfer_id` BIGINT UNSIGNED NOT NULL COMMENT '调拨单ID', - `material_id` BIGINT UNSIGNED NOT NULL COMMENT '物料ID', - `material_code` VARCHAR(50) COMMENT '物料编码(冗余)', - `material_name` VARCHAR(200) COMMENT '物料名称(冗余)', - `qr_code` VARCHAR(50) COMMENT '二维码', - `batch_no` VARCHAR(50) COMMENT '批次号', - `quantity` DECIMAL(18,4) NOT NULL COMMENT '申请数量', - `out_quantity` DECIMAL(18,4) DEFAULT 0 COMMENT '实际出库数量', - `in_quantity` DECIMAL(18,4) DEFAULT 0 COMMENT '实际入库数量', - `unit` VARCHAR(20) COMMENT '单位', - `unit_price` DECIMAL(18,4) DEFAULT 0 COMMENT '单价', - `amount` DECIMAL(18,4) DEFAULT 0 COMMENT '金额', - `remark` VARCHAR(255) COMMENT '备注', - `deleted` TINYINT DEFAULT 0 COMMENT '删除标记', - `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - PRIMARY KEY (`id`), - KEY `idx_transfer` (`transfer_id`), - KEY `idx_material` (`material_id`), - KEY `idx_batch` (`batch_no`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='调拨单明细表'; - --- 盘点单主表 -CREATE TABLE IF NOT EXISTS `inv_stocktaking` ( - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '盘点单ID', - `check_no` VARCHAR(30) NOT NULL COMMENT '盘点单号', - `type` TINYINT NOT NULL COMMENT '1-全仓, 2-部分, 3-抽样', - `warehouse_id` BIGINT UNSIGNED NOT NULL COMMENT '仓库ID', - `warehouse_name` VARCHAR(100) COMMENT '仓库名称(冗余)', - `scope` VARCHAR(500) COMMENT '盘点范围(物料ID列表或分类ID列表 JSON)', - `status` TINYINT NOT NULL DEFAULT 0 COMMENT '0-草稿, 1-进行中, 2-待审批, 3-已审批, 4-已取消', - `applicant_id` BIGINT UNSIGNED COMMENT '申请人ID', - `applicant_name` VARCHAR(50) COMMENT '申请人姓名', - `approver_id` BIGINT UNSIGNED COMMENT '审批人ID', - `approver_name` VARCHAR(50) COMMENT '审批人姓名', - `approve_time` DATETIME COMMENT '审批时间', - `approve_remark` VARCHAR(500) COMMENT '审批备注', - `total_items` INT DEFAULT 0 COMMENT '盘点项总数', - `diff_items` INT DEFAULT 0 COMMENT '差异数', - `total_diff_amount` DECIMAL(18,4) DEFAULT 0 COMMENT '差异总金额', - `version` INT DEFAULT 0 COMMENT '乐观锁版本号', - `remark` VARCHAR(500) COMMENT '备注', - `deleted` TINYINT DEFAULT 0 COMMENT '删除标记', - `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - `update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', - PRIMARY KEY (`id`), - UNIQUE KEY `uk_check_no` (`check_no`), - KEY `idx_status` (`status`), - KEY `idx_warehouse` (`warehouse_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='盘点单主表'; - --- 盘点单明细表 -CREATE TABLE IF NOT EXISTS `inv_stocktaking_item` ( - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '明细ID', - `taking_id` BIGINT UNSIGNED NOT NULL COMMENT '盘点单ID', - `material_id` BIGINT UNSIGNED NOT NULL COMMENT '物料ID', - `material_code` VARCHAR(50) COMMENT '物料编码(冗余)', - `material_name` VARCHAR(200) COMMENT '物料名称(冗余)', - `batch_no` VARCHAR(50) COMMENT '批次号', - `warehouse_id` BIGINT UNSIGNED COMMENT '仓库ID', - `location` VARCHAR(50) COMMENT '库位', - `book_qty` DECIMAL(18,4) DEFAULT 0 COMMENT '账面数量', - `actual_qty` DECIMAL(18,4) COMMENT '实盘数量', - `diff_qty` DECIMAL(18,4) DEFAULT 0 COMMENT '差异=实盘-账面', - `unit` VARCHAR(20) COMMENT '单位', - `unit_price` DECIMAL(18,4) DEFAULT 0 COMMENT '单价', - `diff_amount` DECIMAL(18,4) DEFAULT 0 COMMENT '差异金额', - `scan_time` DATETIME COMMENT '扫码时间', - `scan_operator` VARCHAR(50) COMMENT '扫码操作员', - `remark` VARCHAR(255) COMMENT '备注', - `status` TINYINT DEFAULT 0 COMMENT '0-待盘, 1-已盘, 2-差异已处理', - `deleted` TINYINT DEFAULT 0 COMMENT '删除标记', - `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - `update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', - PRIMARY KEY (`id`), - KEY `idx_taking` (`taking_id`), - KEY `idx_material` (`material_id`), - KEY `idx_status` (`status`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='盘点单明细表'; - --- 库存调整单主表 -CREATE TABLE IF NOT EXISTS `inv_stock_adjust` ( - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '调整单ID', - `adjust_no` VARCHAR(30) NOT NULL COMMENT '调整单号', - `warehouse_id` BIGINT UNSIGNED NOT NULL COMMENT '仓库ID', - `adjust_date` DATE NOT NULL COMMENT '调整日期', - `adjust_type` VARCHAR(20) NOT NULL COMMENT 'damage-损坏, loss-盘亏, gain-盘盈, other-其他', - `operator_id` BIGINT UNSIGNED COMMENT '操作人ID', - `operator_name` VARCHAR(50) COMMENT '操作人姓名', - `approver_id` BIGINT UNSIGNED COMMENT '审批人ID', - `approver_name` VARCHAR(50) COMMENT '审批人姓名', - `approve_time` DATETIME COMMENT '审批时间', - `status` TINYINT DEFAULT 0 COMMENT '0-待审, 1-已审, 2-已驳回', - `total_qty` DECIMAL(18,4) DEFAULT 0 COMMENT '总数量', - `total_amount` DECIMAL(18,4) DEFAULT 0 COMMENT '总金额', - `version` INT DEFAULT 0 COMMENT '乐观锁版本号', - `remark` VARCHAR(500) COMMENT '备注', - `deleted` TINYINT DEFAULT 0 COMMENT '删除标记', - `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - `update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', - PRIMARY KEY (`id`), - UNIQUE KEY `uk_adjust_no` (`adjust_no`), - KEY `idx_status` (`status`), - KEY `idx_warehouse` (`warehouse_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='库存调整单主表'; - --- 库存调整单明细表 -CREATE TABLE IF NOT EXISTS `inv_stock_adjust_item` ( - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '明细ID', - `adjust_id` BIGINT UNSIGNED NOT NULL COMMENT '调整单ID', - `material_id` BIGINT UNSIGNED NOT NULL COMMENT '物料ID', - `material_code` VARCHAR(50) COMMENT '物料编码(冗余)', - `material_name` VARCHAR(200) COMMENT '物料名称(冗余)', - `batch_no` VARCHAR(50) COMMENT '批次号', - `before_qty` DECIMAL(18,4) DEFAULT 0 COMMENT '调整前数量', - `adjust_qty` DECIMAL(18,4) NOT NULL COMMENT '调整数量(正/负)', - `after_qty` DECIMAL(18,4) DEFAULT 0 COMMENT '调整后数量', - `unit` VARCHAR(20) COMMENT '单位', - `unit_price` DECIMAL(18,4) DEFAULT 0 COMMENT '单价', - `amount` DECIMAL(18,4) DEFAULT 0 COMMENT '金额', - `reason` VARCHAR(255) COMMENT '调整原因', - `deleted` TINYINT DEFAULT 0 COMMENT '删除标记', - `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - PRIMARY KEY (`id`), - KEY `idx_adjust` (`adjust_id`), - KEY `idx_material` (`material_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='库存调整单明细表'; - --- 销售出库单主表 -CREATE TABLE IF NOT EXISTS `inv_sales_outbound` ( - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '销售出库单ID', - `outbound_no` VARCHAR(30) NOT NULL COMMENT '销售出库单号', - `order_id` BIGINT UNSIGNED COMMENT '销售订单ID', - `order_no` VARCHAR(50) COMMENT '销售订单号(冗余)', - `customer_id` BIGINT UNSIGNED COMMENT '客户ID', - `customer_name` VARCHAR(100) COMMENT '客户名称(冗余)', - `warehouse_id` BIGINT UNSIGNED NOT NULL COMMENT '仓库ID', - `warehouse_name` VARCHAR(100) COMMENT '仓库名称(冗余)', - `outbound_date` DATE NOT NULL COMMENT '出库日期', - `delivery_person` VARCHAR(50) COMMENT '配送人', - `logistics_no` VARCHAR(100) COMMENT '物流单号', - `status` TINYINT DEFAULT 1 COMMENT '1-待出库, 2-已出库, 3-已完成, 4-已取消', - `finance_posted` TINYINT DEFAULT 0 COMMENT '是否已生成财务凭证: 0-否, 1-是', - `operator_id` BIGINT UNSIGNED COMMENT '操作人ID', - `operator_name` VARCHAR(50) COMMENT '操作人姓名', - `total_qty` DECIMAL(18,4) DEFAULT 0 COMMENT '总数量', - `total_amount` DECIMAL(18,4) DEFAULT 0 COMMENT '总金额', - `version` INT DEFAULT 0 COMMENT '乐观锁版本号', - `remark` VARCHAR(500) COMMENT '备注', - `deleted` TINYINT DEFAULT 0 COMMENT '删除标记', - `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - `update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', - PRIMARY KEY (`id`), - UNIQUE KEY `uk_outbound_no` (`outbound_no`), - KEY `idx_status` (`status`), - KEY `idx_order` (`order_id`), - KEY `idx_warehouse` (`warehouse_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='销售出库单主表'; - --- 销售出库单明细表 -CREATE TABLE IF NOT EXISTS `inv_sales_outbound_item` ( - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '明细ID', - `outbound_id` BIGINT UNSIGNED NOT NULL COMMENT '销售出库单ID', - `material_id` BIGINT UNSIGNED NOT NULL COMMENT '物料ID', - `material_code` VARCHAR(50) COMMENT '物料编码(冗余)', - `material_name` VARCHAR(200) COMMENT '物料名称(冗余)', - `batch_no` VARCHAR(50) COMMENT '批次号', - `quantity` DECIMAL(18,4) NOT NULL COMMENT '数量', - `unit` VARCHAR(20) COMMENT '单位', - `unit_price` DECIMAL(18,4) DEFAULT 0 COMMENT '单价', - `amount` DECIMAL(18,4) DEFAULT 0 COMMENT '金额', - `remark` VARCHAR(255) COMMENT '备注', - `deleted` TINYINT DEFAULT 0 COMMENT '删除标记', - `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - PRIMARY KEY (`id`), - KEY `idx_outbound` (`outbound_id`), - KEY `idx_material` (`material_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='销售出库单明细表'; - --- 生产入库单主表 -CREATE TABLE IF NOT EXISTS `inv_production_inbound` ( - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '生产入库单ID', - `inbound_no` VARCHAR(30) NOT NULL COMMENT '生产入库单号', - `work_order_id` BIGINT UNSIGNED COMMENT '工单ID', - `work_order_no` VARCHAR(50) COMMENT '工单编号(冗余)', - `warehouse_id` BIGINT UNSIGNED NOT NULL COMMENT '仓库ID', - `warehouse_name` VARCHAR(100) COMMENT '仓库名称(冗余)', - `inbound_date` DATE NOT NULL COMMENT '入库日期', - `operator_id` BIGINT UNSIGNED COMMENT '操作人ID', - `operator_name` VARCHAR(50) COMMENT '操作人姓名', - `qc_status` VARCHAR(20) DEFAULT 'pending' COMMENT 'pending-待检, pass-合格, fail-不合格', - `status` TINYINT DEFAULT 1 COMMENT '1-待入库, 2-已入库, 3-已完成, 4-已取消', - `finance_posted` TINYINT DEFAULT 0 COMMENT '是否已生成财务凭证: 0-否, 1-是', - `total_qty` DECIMAL(18,4) DEFAULT 0 COMMENT '总数量', - `total_amount` DECIMAL(18,4) DEFAULT 0 COMMENT '总金额', - `version` INT DEFAULT 0 COMMENT '乐观锁版本号', - `remark` VARCHAR(500) COMMENT '备注', - `deleted` TINYINT DEFAULT 0 COMMENT '删除标记', - `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - `update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', - PRIMARY KEY (`id`), - UNIQUE KEY `uk_inbound_no` (`inbound_no`), - KEY `idx_status` (`status`), - KEY `idx_work_order` (`work_order_id`), - KEY `idx_warehouse` (`warehouse_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='生产入库单主表'; - --- 生产入库单明细表 -CREATE TABLE IF NOT EXISTS `inv_production_inbound_item` ( - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '明细ID', - `inbound_id` BIGINT UNSIGNED NOT NULL COMMENT '生产入库单ID', - `material_id` BIGINT UNSIGNED NOT NULL COMMENT '物料ID', - `material_code` VARCHAR(50) COMMENT '物料编码(冗余)', - `material_name` VARCHAR(200) COMMENT '物料名称(冗余)', - `batch_no` VARCHAR(50) COMMENT '批次号', - `quantity` DECIMAL(18,4) NOT NULL COMMENT '数量', - `unit` VARCHAR(20) COMMENT '单位', - `unit_price` DECIMAL(18,4) DEFAULT 0 COMMENT '单价', - `amount` DECIMAL(18,4) DEFAULT 0 COMMENT '金额', - `remark` VARCHAR(255) COMMENT '备注', - `deleted` TINYINT DEFAULT 0 COMMENT '删除标记', - `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - PRIMARY KEY (`id`), - KEY `idx_inbound` (`inbound_id`), - KEY `idx_material` (`material_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='生产入库单明细表'; - --- 单位换算表 -CREATE TABLE IF NOT EXISTS `inv_unit_conversion` ( - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '换算ID', - `material_id` BIGINT UNSIGNED NOT NULL COMMENT '物料ID', - `from_unit` VARCHAR(20) NOT NULL COMMENT '源单位', - `to_unit` VARCHAR(20) NOT NULL COMMENT '目标单位', - `ratio` DECIMAL(18,6) NOT NULL COMMENT '换算比率: from_unit * ratio = to_unit', - `is_default` TINYINT DEFAULT 0 COMMENT '是否默认换算: 0-否, 1-是', - `remark` VARCHAR(255) COMMENT '备注', - `deleted` TINYINT DEFAULT 0 COMMENT '删除标记', - `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - `update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', - PRIMARY KEY (`id`), - UNIQUE KEY `uk_material_units` (`material_id`, `from_unit`, `to_unit`), - KEY `idx_material` (`material_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='单位换算表'; - --- 仓库操作日志表 -CREATE TABLE IF NOT EXISTS `inv_warehouse_log` ( - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '日志ID', - `warehouse_id` BIGINT UNSIGNED NOT NULL COMMENT '仓库ID', - `operation_type` VARCHAR(30) NOT NULL COMMENT 'create-创建, update-更新, delete-删除, freeze-冻结, unfreeze-解冻', - `operation_content` TEXT COMMENT '操作内容(JSON 或文本)', - `operator_id` BIGINT UNSIGNED COMMENT '操作人ID', - `operator_name` VARCHAR(50) COMMENT '操作人姓名', - `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - PRIMARY KEY (`id`), - KEY `idx_warehouse` (`warehouse_id`), - KEY `idx_create_time` (`create_time`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='仓库操作日志表'; - --- ========================================== --- 迁移完成 --- 验证: SELECT TABLE_NAME FROM information_schema.TABLES WHERE TABLE_SCHEMA='vnerpdacahng' AND TABLE_NAME LIKE 'inv_%'; --- 应返回 22 张表(原 11 张 + 新增 11 张) --- ========================================== diff --git a/database/migrations/006_add_login_security_fields.sql b/database/migrations/006_add_login_security_fields.sql deleted file mode 100644 index eac810c1..00000000 --- a/database/migrations/006_add_login_security_fields.sql +++ /dev/null @@ -1,42 +0,0 @@ --- ======================================================== --- 006: 登录安全字段(登录失败次数、锁定时间、密码修改时间) --- 来源:migrations/0003_add_login_security_fields.sql --- 修正:MySQL 不支持 ADD COLUMN IF NOT EXISTS,改用 INFORMATION_SCHEMA 守卫 --- 注意:last_login_time 已存在于 schema.sql,不再重复添加 --- ======================================================== - -SET @dbname = DATABASE(); -SET @tablename = 'sys_user'; - --- login_fail_count -SET @colname = 'login_fail_count'; -SET @preparedStatement = (SELECT IF( - (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = @dbname AND TABLE_NAME = @tablename AND COLUMN_NAME = @colname) > 0, - 'SELECT 1', - 'ALTER TABLE sys_user ADD COLUMN login_fail_count INT DEFAULT 0 COMMENT ''登录失败次数''' -)); -PREPARE alterIfNotExists FROM @preparedStatement; -EXECUTE alterIfNotExists; -DEALLOCATE PREPARE alterIfNotExists; - --- lock_time -SET @colname = 'lock_time'; -SET @preparedStatement = (SELECT IF( - (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = @dbname AND TABLE_NAME = @tablename AND COLUMN_NAME = @colname) > 0, - 'SELECT 1', - 'ALTER TABLE sys_user ADD COLUMN lock_time DATETIME DEFAULT NULL COMMENT ''账号锁定时间''' -)); -PREPARE alterIfNotExists FROM @preparedStatement; -EXECUTE alterIfNotExists; -DEALLOCATE PREPARE alterIfNotExists; - --- pwd_update_time -SET @colname = 'pwd_update_time'; -SET @preparedStatement = (SELECT IF( - (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = @dbname AND TABLE_NAME = @tablename AND COLUMN_NAME = @colname) > 0, - 'SELECT 1', - 'ALTER TABLE sys_user ADD COLUMN pwd_update_time DATETIME DEFAULT NULL COMMENT ''密码修改时间''' -)); -PREPARE alterIfNotExists FROM @preparedStatement; -EXECUTE alterIfNotExists; -DEALLOCATE PREPARE alterIfNotExists; diff --git a/database/migrations/008_add_inventory_alerts.sql b/database/migrations/008_add_inventory_alerts.sql deleted file mode 100644 index a633e3ef..00000000 --- a/database/migrations/008_add_inventory_alerts.sql +++ /dev/null @@ -1,66 +0,0 @@ --- ======================================================== --- 008: 库存批次告警与检验状态字段 --- 来源:migrations/0001_add_inventory_alerts.sql --- 修正: --- 1. 表名 inventory_batches → inv_inventory_batch(与 schema.sql 对齐) --- 2. 列名 expiry_date → expire_date(与 schema.sql 对齐) --- 3. 移除 PostgreSQL 专有语法 COMMENT ON COLUMN(MySQL 用 ALTER TABLE 内联 COMMENT) --- ======================================================== - -SET @dbname = DATABASE(); -SET @tablename = 'inv_inventory_batch'; - --- alert_level -SET @colname = 'alert_level'; -SET @preparedStatement = (SELECT IF( - (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = @dbname AND TABLE_NAME = @tablename AND COLUMN_NAME = @colname) > 0, - 'SELECT 1', - 'ALTER TABLE inv_inventory_batch ADD COLUMN alert_level VARCHAR(20) DEFAULT ''normal'' COMMENT ''库存告警级别: normal/warning/critical''' -)); -PREPARE alterIfNotExists FROM @preparedStatement; -EXECUTE alterIfNotExists; -DEALLOCATE PREPARE alterIfNotExists; - --- last_alert_time -SET @colname = 'last_alert_time'; -SET @preparedStatement = (SELECT IF( - (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = @dbname AND TABLE_NAME = @tablename AND COLUMN_NAME = @colname) > 0, - 'SELECT 1', - 'ALTER TABLE inv_inventory_batch ADD COLUMN last_alert_time TIMESTAMP NULL COMMENT ''最后告警时间''' -)); -PREPARE alterIfNotExists FROM @preparedStatement; -EXECUTE alterIfNotExists; -DEALLOCATE PREPARE alterIfNotExists; - --- inspection_status -SET @colname = 'inspection_status'; -SET @preparedStatement = (SELECT IF( - (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = @dbname AND TABLE_NAME = @tablename AND COLUMN_NAME = @colname) > 0, - 'SELECT 1', - 'ALTER TABLE inv_inventory_batch ADD COLUMN inspection_status VARCHAR(20) DEFAULT ''pending'' COMMENT ''检验状态: pending/pass/fail''' -)); -PREPARE alterIfNotExists FROM @preparedStatement; -EXECUTE alterIfNotExists; -DEALLOCATE PREPARE alterIfNotExists; - --- quarantine_status -SET @colname = 'quarantine_status'; -SET @preparedStatement = (SELECT IF( - (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = @dbname AND TABLE_NAME = @tablename AND COLUMN_NAME = @colname) > 0, - 'SELECT 1', - 'ALTER TABLE inv_inventory_batch ADD COLUMN quarantine_status VARCHAR(20) DEFAULT ''none'' COMMENT ''隔离状态: none/quarantined/released''' -)); -PREPARE alterIfNotExists FROM @preparedStatement; -EXECUTE alterIfNotExists; -DEALLOCATE PREPARE alterIfNotExists; - --- 告警级别索引 -SET @indexname = 'idx_alert_level'; -SET @preparedStatement = (SELECT IF( - (SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_SCHEMA = @dbname AND TABLE_NAME = @tablename AND INDEX_NAME = @indexname) > 0, - 'SELECT 1', - 'CREATE INDEX idx_alert_level ON inv_inventory_batch(alert_level)' -)); -PREPARE alterIfNotExists FROM @preparedStatement; -EXECUTE alterIfNotExists; -DEALLOCATE PREPARE alterIfNotExists; diff --git a/database/migrations/009_add_indexes_and_fks.sql b/database/migrations/009_add_indexes_and_fks.sql deleted file mode 100644 index eea64352..00000000 --- a/database/migrations/009_add_indexes_and_fks.sql +++ /dev/null @@ -1,321 +0,0 @@ --- ======================================================== --- 009: 索引与外键规范化 --- 来源:Phase 1-4 — 审计 schema.sql 发现 37 张表共 ~85 个 *_id 列缺少索引 --- 策略: --- 1. 补充高频查询字段缺失索引(operator_id, warehouse_id, customer_id, work_order_id 等) --- 2. 为核心父子关系添加外键约束(幂等守卫) --- 3. 所有操作使用 INFORMATION_SCHEMA 守卫,确保幂等 --- 注意:MySQL 不支持 CREATE INDEX IF NOT EXISTS,使用 SET/PREPARE/EXECUTE 模式 --- ======================================================== - --- 辅助:为单个索引执行幂等添加 --- 用法:CALL add_index_if_not_exists('table', 'idx_name', 'column_expr'); --- 因 migrate.ts 按分号分割,无法使用存储过程,改用内联守卫模式 - --- ======================================================== --- 一、高优先级缺失索引 --- ======================================================== - --- fin_voucher_line: 辅助核算维度(财务报表高频过滤/分组) -SET @cnt = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'fin_voucher_line' AND INDEX_NAME = 'idx_customer'); -SET @sql = IF(@cnt = 0, 'ALTER TABLE fin_voucher_line ADD INDEX idx_customer (customer_id)', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @cnt = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'fin_voucher_line' AND INDEX_NAME = 'idx_supplier'); -SET @sql = IF(@cnt = 0, 'ALTER TABLE fin_voucher_line ADD INDEX idx_supplier (supplier_id)', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @cnt = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'fin_voucher_line' AND INDEX_NAME = 'idx_department'); -SET @sql = IF(@cnt = 0, 'ALTER TABLE fin_voucher_line ADD INDEX idx_department (department_id)', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @cnt = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'fin_voucher_line' AND INDEX_NAME = 'idx_project'); -SET @sql = IF(@cnt = 0, 'ALTER TABLE fin_voucher_line ADD INDEX idx_project (project_id)', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- qc_inspection: 质检查询高频字段 -SET @cnt = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'qc_inspection' AND INDEX_NAME = 'idx_material'); -SET @sql = IF(@cnt = 0, 'ALTER TABLE qc_inspection ADD INDEX idx_material (material_id)', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @cnt = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'qc_inspection' AND INDEX_NAME = 'idx_source'); -SET @sql = IF(@cnt = 0, 'ALTER TABLE qc_inspection ADD INDEX idx_source (source_id)', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @cnt = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'qc_inspection' AND INDEX_NAME = 'idx_inspector'); -SET @sql = IF(@cnt = 0, 'ALTER TABLE qc_inspection ADD INDEX idx_inspector (inspector_id)', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- qc_unqualified: 不合格品处理 -SET @cnt = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'qc_unqualified' AND INDEX_NAME = 'idx_inspection'); -SET @sql = IF(@cnt = 0, 'ALTER TABLE qc_unqualified ADD INDEX idx_inspection (inspection_id)', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @cnt = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'qc_unqualified' AND INDEX_NAME = 'idx_material'); -SET @sql = IF(@cnt = 0, 'ALTER TABLE qc_unqualified ADD INDEX idx_material (material_id)', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @cnt = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'qc_unqualified' AND INDEX_NAME = 'idx_handler'); -SET @sql = IF(@cnt = 0, 'ALTER TABLE qc_unqualified ADD INDEX idx_handler (handler_id)', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- inv_outbound_order: 出库单高频查询 -SET @cnt = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_outbound_order' AND INDEX_NAME = 'idx_customer'); -SET @sql = IF(@cnt = 0, 'ALTER TABLE inv_outbound_order ADD INDEX idx_customer (customer_id)', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @cnt = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_outbound_order' AND INDEX_NAME = 'idx_work_order'); -SET @sql = IF(@cnt = 0, 'ALTER TABLE inv_outbound_order ADD INDEX idx_work_order (work_order_id)', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @cnt = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_outbound_order' AND INDEX_NAME = 'idx_auditor'); -SET @sql = IF(@cnt = 0, 'ALTER TABLE inv_outbound_order ADD INDEX idx_auditor (auditor_id)', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @cnt = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_outbound_order' AND INDEX_NAME = 'idx_operator'); -SET @sql = IF(@cnt = 0, 'ALTER TABLE inv_outbound_order ADD INDEX idx_operator (operator_id)', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- inv_outbound_item: batch_id 缺索引(idx_batch 是 batch_no VARCHAR) -SET @cnt = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_outbound_item' AND INDEX_NAME = 'idx_batch_id'); -SET @sql = IF(@cnt = 0, 'ALTER TABLE inv_outbound_item ADD INDEX idx_batch_id (batch_id)', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- prd_work_order: 生产工单关联查询 -SET @cnt = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'prd_work_order' AND INDEX_NAME = 'idx_sales_order'); -SET @sql = IF(@cnt = 0, 'ALTER TABLE prd_work_order ADD INDEX idx_sales_order (sales_order_id)', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @cnt = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'prd_work_order' AND INDEX_NAME = 'idx_workshop'); -SET @sql = IF(@cnt = 0, 'ALTER TABLE prd_work_order ADD INDEX idx_workshop (workshop_id)', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @cnt = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'prd_work_order' AND INDEX_NAME = 'idx_workcenter'); -SET @sql = IF(@cnt = 0, 'ALTER TABLE prd_work_order ADD INDEX idx_workcenter (workcenter_id)', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- inv_material: 默认仓库 FK 缺索引 -SET @cnt = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_material' AND INDEX_NAME = 'idx_warehouse'); -SET @sql = IF(@cnt = 0, 'ALTER TABLE inv_material ADD INDEX idx_warehouse (warehouse_id)', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- ======================================================== --- 二、operator_id / handler_id 批量补索引(9 张表) --- ======================================================== - -SET @cnt = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_inventory_log' AND INDEX_NAME = 'idx_operator'); -SET @sql = IF(@cnt = 0, 'ALTER TABLE inv_inventory_log ADD INDEX idx_operator (operator_id)', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @cnt = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_fifo_override_log' AND INDEX_NAME = 'idx_operator'); -SET @sql = IF(@cnt = 0, 'ALTER TABLE inv_fifo_override_log ADD INDEX idx_operator (operator_id)', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @cnt = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_transfer_order' AND INDEX_NAME = 'idx_operator'); -SET @sql = IF(@cnt = 0, 'ALTER TABLE inv_transfer_order ADD INDEX idx_operator (operator_id)', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @cnt = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_stock_adjust' AND INDEX_NAME = 'idx_operator'); -SET @sql = IF(@cnt = 0, 'ALTER TABLE inv_stock_adjust ADD INDEX idx_operator (operator_id)', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @cnt = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_sales_outbound' AND INDEX_NAME = 'idx_operator'); -SET @sql = IF(@cnt = 0, 'ALTER TABLE inv_sales_outbound ADD INDEX idx_operator (operator_id)', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @cnt = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_production_inbound' AND INDEX_NAME = 'idx_operator'); -SET @sql = IF(@cnt = 0, 'ALTER TABLE inv_production_inbound ADD INDEX idx_operator (operator_id)', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @cnt = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_warehouse_log' AND INDEX_NAME = 'idx_operator'); -SET @sql = IF(@cnt = 0, 'ALTER TABLE inv_warehouse_log ADD INDEX idx_operator (operator_id)', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @cnt = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'fin_receipt_record' AND INDEX_NAME = 'idx_handler'); -SET @sql = IF(@cnt = 0, 'ALTER TABLE fin_receipt_record ADD INDEX idx_handler (handler_id)', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @cnt = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'fin_payment_record' AND INDEX_NAME = 'idx_handler'); -SET @sql = IF(@cnt = 0, 'ALTER TABLE fin_payment_record ADD INDEX idx_handler (handler_id)', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- ======================================================== --- 三、warehouse_id 批量补索引(6 张表) --- ======================================================== - -SET @cnt = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'pur_receipt' AND INDEX_NAME = 'idx_warehouse'); -SET @sql = IF(@cnt = 0, 'ALTER TABLE pur_receipt ADD INDEX idx_warehouse (warehouse_id)', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @cnt = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'sal_delivery' AND INDEX_NAME = 'idx_warehouse'); -SET @sql = IF(@cnt = 0, 'ALTER TABLE sal_delivery ADD INDEX idx_warehouse (warehouse_id)', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @cnt = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'material_requisitions' AND INDEX_NAME = 'idx_warehouse'); -SET @sql = IF(@cnt = 0, 'ALTER TABLE material_requisitions ADD INDEX idx_warehouse (warehouse_id)', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @cnt = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'material_returns' AND INDEX_NAME = 'idx_warehouse'); -SET @sql = IF(@cnt = 0, 'ALTER TABLE material_returns ADD INDEX idx_warehouse (warehouse_id)', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @cnt = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'material_batch_costs' AND INDEX_NAME = 'idx_warehouse'); -SET @sql = IF(@cnt = 0, 'ALTER TABLE material_batch_costs ADD INDEX idx_warehouse (warehouse_id)', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @cnt = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_stocktaking_item' AND INDEX_NAME = 'idx_warehouse'); -SET @sql = IF(@cnt = 0, 'ALTER TABLE inv_stocktaking_item ADD INDEX idx_warehouse (warehouse_id)', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- ======================================================== --- 四、approver_id / applicant_id 批量补索引 --- ======================================================== - -SET @cnt = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'material_requisitions' AND INDEX_NAME = 'idx_approver'); -SET @sql = IF(@cnt = 0, 'ALTER TABLE material_requisitions ADD INDEX idx_approver (approver_id)', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @cnt = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'material_returns' AND INDEX_NAME = 'idx_applicant'); -SET @sql = IF(@cnt = 0, 'ALTER TABLE material_returns ADD INDEX idx_applicant (applicant_id)', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @cnt = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'material_returns' AND INDEX_NAME = 'idx_confirm'); -SET @sql = IF(@cnt = 0, 'ALTER TABLE material_returns ADD INDEX idx_confirm (confirm_id)', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @cnt = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_transfer_order' AND INDEX_NAME = 'idx_applicant'); -SET @sql = IF(@cnt = 0, 'ALTER TABLE inv_transfer_order ADD INDEX idx_applicant (applicant_id)', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @cnt = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_transfer_order' AND INDEX_NAME = 'idx_approver'); -SET @sql = IF(@cnt = 0, 'ALTER TABLE inv_transfer_order ADD INDEX idx_approver (approver_id)', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @cnt = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_stocktaking' AND INDEX_NAME = 'idx_applicant'); -SET @sql = IF(@cnt = 0, 'ALTER TABLE inv_stocktaking ADD INDEX idx_applicant (applicant_id)', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @cnt = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_stocktaking' AND INDEX_NAME = 'idx_approver'); -SET @sql = IF(@cnt = 0, 'ALTER TABLE inv_stocktaking ADD INDEX idx_approver (approver_id)', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @cnt = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_stock_adjust' AND INDEX_NAME = 'idx_approver'); -SET @sql = IF(@cnt = 0, 'ALTER TABLE inv_stock_adjust ADD INDEX idx_approver (approver_id)', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @cnt = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_fifo_override_log' AND INDEX_NAME = 'idx_approve'); -SET @sql = IF(@cnt = 0, 'ALTER TABLE inv_fifo_override_log ADD INDEX idx_approve (approve_id)', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- ======================================================== --- 五、其他高频缺失索引 --- ======================================================== - --- pur_request: 申请部门/申请人 -SET @cnt = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'pur_request' AND INDEX_NAME = 'idx_request_dept'); -SET @sql = IF(@cnt = 0, 'ALTER TABLE pur_request ADD INDEX idx_request_dept (request_dept_id)', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @cnt = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'pur_request' AND INDEX_NAME = 'idx_requester'); -SET @sql = IF(@cnt = 0, 'ALTER TABLE pur_request ADD INDEX idx_requester (requester_id)', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- pur_receipt: 仓库/检验员 -SET @cnt = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'pur_receipt' AND INDEX_NAME = 'idx_inspector'); -SET @sql = IF(@cnt = 0, 'ALTER TABLE pur_receipt ADD INDEX idx_inspector (inspector_id)', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- pur_receipt_detail: 关联订单明细 -SET @cnt = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'pur_receipt_detail' AND INDEX_NAME = 'idx_order_detail'); -SET @sql = IF(@cnt = 0, 'ALTER TABLE pur_receipt_detail ADD INDEX idx_order_detail (order_detail_id)', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- sal_delivery: 仓库 --- (已在第三节添加 idx_warehouse) - --- sal_delivery_detail: 关联订单明细 -SET @cnt = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'sal_delivery_detail' AND INDEX_NAME = 'idx_order_detail'); -SET @sql = IF(@cnt = 0, 'ALTER TABLE sal_delivery_detail ADD INDEX idx_order_detail (order_detail_id)', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- prd_standard_card: 创建人/审核人 -SET @cnt = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'prd_standard_card' AND INDEX_NAME = 'idx_creator'); -SET @sql = IF(@cnt = 0, 'ALTER TABLE prd_standard_card ADD INDEX idx_creator (creator_id)', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @cnt = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'prd_standard_card' AND INDEX_NAME = 'idx_reviewer'); -SET @sql = IF(@cnt = 0, 'ALTER TABLE prd_standard_card ADD INDEX idx_reviewer (reviewer_id)', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- prd_work_order_color_seq: 网版/油墨配方 -SET @cnt = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'prd_work_order_color_seq' AND INDEX_NAME = 'idx_screen_plate'); -SET @sql = IF(@cnt = 0, 'ALTER TABLE prd_work_order_color_seq ADD INDEX idx_screen_plate (screen_plate_id)', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @cnt = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'prd_work_order_color_seq' AND INDEX_NAME = 'idx_ink_formula'); -SET @sql = IF(@cnt = 0, 'ALTER TABLE prd_work_order_color_seq ADD INDEX idx_ink_formula (ink_formula_id)', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- prd_schedule: 关联订单 -SET @cnt = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'prd_schedule' AND INDEX_NAME = 'idx_order'); -SET @sql = IF(@cnt = 0, 'ALTER TABLE prd_schedule ADD INDEX idx_order (order_id)', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- fin_receivable / fin_payable: 来源单据 -SET @cnt = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'fin_receivable' AND INDEX_NAME = 'idx_source'); -SET @sql = IF(@cnt = 0, 'ALTER TABLE fin_receivable ADD INDEX idx_source (source_id)', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @cnt = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'fin_payable' AND INDEX_NAME = 'idx_source'); -SET @sql = IF(@cnt = 0, 'ALTER TABLE fin_payable ADD INDEX idx_source (source_id)', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- sys_department: 负责人 -SET @cnt = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'sys_department' AND INDEX_NAME = 'idx_leader'); -SET @sql = IF(@cnt = 0, 'ALTER TABLE sys_department ADD INDEX idx_leader (leader_id)', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- sys_role_menu: menu_id 单独索引(复合唯一键 uk_role_menu 中 menu_id 非最左前缀) -SET @cnt = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'sys_role_menu' AND INDEX_NAME = 'idx_menu'); -SET @sql = IF(@cnt = 0, 'ALTER TABLE sys_role_menu ADD INDEX idx_menu (menu_id)', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- material_requisitions: 原领料单 -SET @cnt = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'material_requisitions' AND INDEX_NAME = 'idx_original_requisition'); -SET @sql = IF(@cnt = 0, 'ALTER TABLE material_requisitions ADD INDEX idx_original_requisition (original_requisition_id)', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- inv_sales_outbound: customer_id -SET @cnt = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_sales_outbound' AND INDEX_NAME = 'idx_customer'); -SET @sql = IF(@cnt = 0, 'ALTER TABLE inv_sales_outbound ADD INDEX idx_customer (customer_id)', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- ======================================================== --- 六、核心外键约束(幂等添加) --- 仅对强关联的父子表添加 FK,使用 INFORMATION_SCHEMA 守卫 --- ======================================================== - --- inv_outbound_item.order_id → inv_outbound_order.id -SET @cnt = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_outbound_item' AND CONSTRAINT_NAME = 'fk_outbound_item_order'); -SET @sql = IF(@cnt = 0, 'ALTER TABLE inv_outbound_item ADD CONSTRAINT fk_outbound_item_order FOREIGN KEY (order_id) REFERENCES inv_outbound_order(id) ON DELETE CASCADE ON UPDATE CASCADE', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- inv_inbound_item.order_id → inv_inbound_order.id -SET @cnt = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_inbound_item' AND CONSTRAINT_NAME = 'fk_inbound_item_order'); -SET @sql = IF(@cnt = 0, 'ALTER TABLE inv_inbound_item ADD CONSTRAINT fk_inbound_item_order FOREIGN KEY (order_id) REFERENCES inv_inbound_order(id) ON DELETE CASCADE ON UPDATE CASCADE', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- pur_order_detail.order_id → pur_order.id -SET @cnt = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'pur_order_detail' AND CONSTRAINT_NAME = 'fk_pur_order_detail_order'); -SET @sql = IF(@cnt = 0, 'ALTER TABLE pur_order_detail ADD CONSTRAINT fk_pur_order_detail_order FOREIGN KEY (order_id) REFERENCES pur_order(id) ON DELETE CASCADE ON UPDATE CASCADE', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- sal_order_detail.order_id → sal_order.id -SET @cnt = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'sal_order_detail' AND CONSTRAINT_NAME = 'fk_sal_order_detail_order'); -SET @sql = IF(@cnt = 0, 'ALTER TABLE sal_order_detail ADD CONSTRAINT fk_sal_order_detail_order FOREIGN KEY (order_id) REFERENCES sal_order(id) ON DELETE CASCADE ON UPDATE CASCADE', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- pur_request_detail.request_id → pur_request.id -SET @cnt = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'pur_request_detail' AND CONSTRAINT_NAME = 'fk_pur_request_detail_request'); -SET @sql = IF(@cnt = 0, 'ALTER TABLE pur_request_detail ADD CONSTRAINT fk_pur_request_detail_request FOREIGN KEY (request_id) REFERENCES pur_request(id) ON DELETE CASCADE ON UPDATE CASCADE', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; diff --git a/database/migrations/010_add_first_login_column.sql b/database/migrations/010_add_first_login_column.sql deleted file mode 100644 index f52e269b..00000000 --- a/database/migrations/010_add_first_login_column.sql +++ /dev/null @@ -1,22 +0,0 @@ --- ======================================================== --- 010: sys_user.first_login 列 --- 用途:首次登录强制改密标记(0-否, 1-是) --- 来源:原先仅由 src/app/api/init/migrate/route.ts 运行时动态添加, --- 未同步到权威 schema.sql,导致 schema 不一致。 --- 本迁移将列正式纳入迁移链,新环境由 schema.sql 直接创建, --- 旧环境通过本迁移补齐。 --- 兼容:MySQL 不支持 ADD COLUMN IF NOT EXISTS,使用 INFORMATION_SCHEMA 守卫 --- ======================================================== - -SET @dbname = DATABASE(); -SET @tablename = 'sys_user'; - -SET @colname = 'first_login'; -SET @preparedStatement = (SELECT IF( - (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = @dbname AND TABLE_NAME = @tablename AND COLUMN_NAME = @colname) > 0, - 'SELECT 1', - 'ALTER TABLE sys_user ADD COLUMN first_login TINYINT DEFAULT 1 COMMENT ''是否首次登录: 0-否, 1-是'' AFTER pwd_update_time' -)); -PREPARE alterIfNotExists FROM @preparedStatement; -EXECUTE alterIfNotExists; -DEALLOCATE PREPARE alterIfNotExists; diff --git a/database/migrations/011_add_missing_warehouse_tables.sql b/database/migrations/011_add_missing_warehouse_tables.sql deleted file mode 100644 index f04a1119..00000000 --- a/database/migrations/011_add_missing_warehouse_tables.sql +++ /dev/null @@ -1,114 +0,0 @@ --- ======================================================== --- 011: 补建缺失的仓储管理表 --- 问题:schema.sql 已定义但实际数据库未创建的表 --- - inv_transfer_order(调拨单主表) --- - inv_transfer_item(调拨单明细表) --- - inv_stock_adjust(库存调整单主表) --- - inv_stock_adjust_item(库存调整单明细表) --- 原因:旧版 schema.sql 未包含这些表,升级后未同步 --- 兼容:使用 CREATE TABLE IF NOT EXISTS 实现幂等 --- ======================================================== - --- 调拨单主表 -CREATE TABLE IF NOT EXISTS `inv_transfer_order` ( - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '调拨单ID', - `transfer_no` VARCHAR(30) NOT NULL COMMENT '调拨单号', - `type` TINYINT NOT NULL COMMENT '1-库位调拨, 2-仓库调拨', - `from_warehouse_id` BIGINT UNSIGNED NOT NULL COMMENT '源仓库ID', - `to_warehouse_id` BIGINT UNSIGNED NOT NULL COMMENT '目标仓库ID', - `from_location` VARCHAR(50) COMMENT '源库位', - `to_location` VARCHAR(50) COMMENT '目标库位', - `status` TINYINT NOT NULL DEFAULT 0 COMMENT '0-草稿, 1-待审批, 2-已出库, 3-已入库, 4-已取消', - `applicant_id` BIGINT UNSIGNED COMMENT '申请人ID', - `applicant_name` VARCHAR(50) COMMENT '申请人姓名', - `approver_id` BIGINT UNSIGNED COMMENT '审批人ID', - `approver_name` VARCHAR(50) COMMENT '审批人姓名', - `operator_id` BIGINT UNSIGNED COMMENT '操作人ID', - `operator_name` VARCHAR(50) COMMENT '操作人姓名', - `out_time` DATETIME COMMENT '出库时间', - `in_time` DATETIME COMMENT '入库时间', - `total_qty` DECIMAL(18,4) DEFAULT 0 COMMENT '总数量', - `total_amount` DECIMAL(18,4) DEFAULT 0 COMMENT '总金额', - `version` INT DEFAULT 0 COMMENT '乐观锁版本号', - `remark` VARCHAR(500) COMMENT '备注', - `deleted` TINYINT DEFAULT 0 COMMENT '删除标记', - `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - `update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', - PRIMARY KEY (`id`), - UNIQUE KEY `uk_transfer_no` (`transfer_no`), - KEY `idx_status` (`status`), - KEY `idx_from_warehouse` (`from_warehouse_id`), - KEY `idx_to_warehouse` (`to_warehouse_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='调拨单主表'; - --- 调拨单明细表 -CREATE TABLE IF NOT EXISTS `inv_transfer_item` ( - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '明细ID', - `transfer_id` BIGINT UNSIGNED NOT NULL COMMENT '调拨单ID', - `material_id` BIGINT UNSIGNED NOT NULL COMMENT '物料ID', - `material_code` VARCHAR(50) COMMENT '物料编码(冗余)', - `material_name` VARCHAR(200) COMMENT '物料名称(冗余)', - `qr_code` VARCHAR(50) COMMENT '二维码', - `batch_no` VARCHAR(50) COMMENT '批次号', - `quantity` DECIMAL(18,4) NOT NULL COMMENT '申请数量', - `out_quantity` DECIMAL(18,4) DEFAULT 0 COMMENT '实际出库数量', - `in_quantity` DECIMAL(18,4) DEFAULT 0 COMMENT '实际入库数量', - `unit` VARCHAR(20) COMMENT '单位', - `unit_price` DECIMAL(18,4) DEFAULT 0 COMMENT '单价', - `amount` DECIMAL(18,4) DEFAULT 0 COMMENT '金额', - `remark` VARCHAR(255) COMMENT '备注', - `deleted` TINYINT DEFAULT 0 COMMENT '删除标记', - `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - PRIMARY KEY (`id`), - KEY `idx_transfer` (`transfer_id`), - KEY `idx_material` (`material_id`), - KEY `idx_batch` (`batch_no`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='调拨单明细表'; - --- 库存调整单主表 -CREATE TABLE IF NOT EXISTS `inv_stock_adjust` ( - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '调整单ID', - `adjust_no` VARCHAR(30) NOT NULL COMMENT '调整单号', - `warehouse_id` BIGINT UNSIGNED NOT NULL COMMENT '仓库ID', - `adjust_date` DATE NOT NULL COMMENT '调整日期', - `adjust_type` TINYINT NOT NULL DEFAULT 1 COMMENT '调整类型: 1-盘盈, 2-盘亏, 3-其他', - `operator_id` BIGINT UNSIGNED COMMENT '操作人ID', - `operator_name` VARCHAR(50) COMMENT '操作人姓名', - `approver_id` BIGINT UNSIGNED COMMENT '审批人ID', - `approver_name` VARCHAR(50) COMMENT '审批人姓名', - `approve_time` DATETIME COMMENT '审批时间', - `status` TINYINT DEFAULT 0 COMMENT '0-待审, 1-已审, 2-已驳回', - `total_qty` DECIMAL(18,4) DEFAULT 0 COMMENT '总数量', - `total_amount` DECIMAL(18,4) DEFAULT 0 COMMENT '总金额', - `version` INT DEFAULT 0 COMMENT '乐观锁版本号', - `remark` VARCHAR(500) COMMENT '备注', - `deleted` TINYINT DEFAULT 0 COMMENT '删除标记', - `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - `update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', - PRIMARY KEY (`id`), - UNIQUE KEY `uk_adjust_no` (`adjust_no`), - KEY `idx_status` (`status`), - KEY `idx_warehouse` (`warehouse_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='库存调整单主表'; - --- 库存调整单明细表 -CREATE TABLE IF NOT EXISTS `inv_stock_adjust_item` ( - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '明细ID', - `adjust_id` BIGINT UNSIGNED NOT NULL COMMENT '调整单ID', - `material_id` BIGINT UNSIGNED NOT NULL COMMENT '物料ID', - `material_code` VARCHAR(50) COMMENT '物料编码(冗余)', - `material_name` VARCHAR(200) COMMENT '物料名称(冗余)', - `batch_no` VARCHAR(50) COMMENT '批次号', - `before_qty` DECIMAL(18,4) DEFAULT 0 COMMENT '调整前数量', - `adjust_qty` DECIMAL(18,4) NOT NULL COMMENT '调整数量(正/负)', - `after_qty` DECIMAL(18,4) DEFAULT 0 COMMENT '调整后数量', - `unit` VARCHAR(20) COMMENT '单位', - `unit_price` DECIMAL(18,4) DEFAULT 0 COMMENT '单价', - `amount` DECIMAL(18,4) DEFAULT 0 COMMENT '金额', - `reason` VARCHAR(255) COMMENT '调整原因', - `deleted` TINYINT DEFAULT 0 COMMENT '删除标记', - `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - PRIMARY KEY (`id`), - KEY `idx_adjust` (`adjust_id`), - KEY `idx_material` (`material_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='库存调整单明细表'; diff --git a/database/migrations/012_add_sales_return_and_reconciliation.sql b/database/migrations/012_add_sales_return_and_reconciliation.sql deleted file mode 100644 index 24b69806..00000000 --- a/database/migrations/012_add_sales_return_and_reconciliation.sql +++ /dev/null @@ -1,172 +0,0 @@ --- ======================================================== --- 012: 销售管理模块补全 — 退货单与对账单表 --- 目的:支撑 Phase 3-3 销售管理领域层落地 --- - sal_return 退货单主表(ReturnOrder 聚合) --- - sal_return_detail 退货单明细表(ReturnOrderLine) --- - sal_reconciliation 对账单主表(Reconciliation 聚合) --- - sal_reconciliation_line 对账单明细表(发货/退货单关联) --- - sal_reconciliation_writeoff 对账核销记录表(WriteOffRecord) --- 说明: --- 1. 退货完成时通过回调复用仓储模块 inv_inbound_order 入库, --- 通过回调在财务模块 fin_receivable 创建红字应收单, --- 本表仅记录关联单号(inbound_order_id/no, receivable_id/no), --- 不复制入库/应收明细,保持单据单一数据源。 --- 2. 对账单 lines 记录对账期间内的发货/退货单(source_type=1 发货/2 退货), --- writeoff 表记录对账单对应收单的核销记录,支持一对多核销。 --- 3. 使用 CREATE TABLE IF NOT EXISTS 实现幂等。 --- 4. 跨模块引用(customer_id, order_id, warehouse_id, receivable_id 等) --- 仅建索引不加外键,避免跨模块迁移顺序依赖。 --- ======================================================== - --- -------------------------------------------------------- --- 1. 退货单主表 sal_return --- 对应 ReturnOrder 聚合根,状态:1-待审核, 2-已审核, 3-已完成, 9-已取消 --- -------------------------------------------------------- -CREATE TABLE IF NOT EXISTS `sal_return` ( - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '退货单ID', - `return_no` VARCHAR(50) NOT NULL COMMENT '退货单号', - `status` TINYINT NOT NULL DEFAULT 1 COMMENT '状态: 1-待审核, 2-已审核, 3-已完成, 9-已取消', - `order_id` BIGINT UNSIGNED NOT NULL COMMENT '销售订单ID', - `order_no` VARCHAR(50) COMMENT '销售订单号(冗余)', - `customer_id` BIGINT UNSIGNED NOT NULL COMMENT '客户ID', - `customer_name` VARCHAR(100) COMMENT '客户名称(冗余)', - `warehouse_id` BIGINT UNSIGNED NOT NULL COMMENT '入库仓库ID', - `delivery_id` BIGINT UNSIGNED COMMENT '关联发货单ID', - `delivery_no` VARCHAR(50) COMMENT '关联发货单号(冗余)', - `reason` VARCHAR(500) NOT NULL COMMENT '退货原因', - `return_date` DATE NOT NULL COMMENT '退货日期', - `total_amount` DECIMAL(18,4) DEFAULT 0 COMMENT '退货总金额', - `approve_by` BIGINT UNSIGNED COMMENT '审核人ID', - `approve_time` DATETIME COMMENT '审核时间', - `complete_by` BIGINT UNSIGNED COMMENT '完成人ID', - `complete_time` DATETIME COMMENT '完成时间', - `inbound_order_id` BIGINT UNSIGNED COMMENT '关联入库单ID(退货入库)', - `inbound_order_no` VARCHAR(50) COMMENT '关联入库单号(冗余)', - `receivable_id` BIGINT UNSIGNED COMMENT '关联红字应收单ID', - `receivable_no` VARCHAR(50) COMMENT '关联红字应收单号(冗余)', - `remark` VARCHAR(500) COMMENT '备注', - `version` INT DEFAULT 0 COMMENT '乐观锁版本号', - `deleted` TINYINT DEFAULT 0 COMMENT '删除标记', - `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - `update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', - `create_by` BIGINT UNSIGNED COMMENT '创建人ID', - `update_by` BIGINT UNSIGNED COMMENT '更新人ID', - PRIMARY KEY (`id`), - UNIQUE KEY `uk_return_no` (`return_no`), - KEY `idx_status` (`status`), - KEY `idx_order` (`order_id`), - KEY `idx_customer` (`customer_id`), - KEY `idx_warehouse` (`warehouse_id`), - KEY `idx_delivery` (`delivery_id`), - KEY `idx_return_date` (`return_date`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='退货单主表'; - --- -------------------------------------------------------- --- 2. 退货单明细表 sal_return_detail --- 对应 ReturnOrderLine 实体,记录退货物料明细 --- -------------------------------------------------------- -CREATE TABLE IF NOT EXISTS `sal_return_detail` ( - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '明细ID', - `return_id` BIGINT UNSIGNED NOT NULL COMMENT '退货单ID', - `line_no` INT NOT NULL COMMENT '行号', - `delivery_detail_id` BIGINT UNSIGNED COMMENT '关联发货明细ID', - `order_detail_id` BIGINT UNSIGNED COMMENT '关联订单明细ID', - `material_id` BIGINT UNSIGNED NOT NULL COMMENT '物料ID', - `material_code` VARCHAR(50) COMMENT '物料编码(冗余)', - `material_name` VARCHAR(200) COMMENT '物料名称(冗余)', - `material_spec` VARCHAR(100) COMMENT '物料规格(冗余)', - `unit` VARCHAR(20) COMMENT '单位', - `quantity` DECIMAL(18,4) NOT NULL COMMENT '退货数量', - `unit_price` DECIMAL(18,4) DEFAULT 0 COMMENT '单价', - `amount` DECIMAL(18,4) DEFAULT 0 COMMENT '金额', - `batch_no` VARCHAR(50) COMMENT '批次号', - `remark` VARCHAR(255) COMMENT '备注', - `deleted` TINYINT DEFAULT 0 COMMENT '删除标记', - `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - PRIMARY KEY (`id`), - KEY `idx_return` (`return_id`), - KEY `idx_material` (`material_id`), - KEY `idx_order_detail` (`order_detail_id`), - KEY `idx_batch` (`batch_no`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='退货单明细表'; - --- -------------------------------------------------------- --- 3. 对账单主表 sal_reconciliation --- 对应 Reconciliation 聚合根 --- 状态:1-草稿, 2-已确认, 3-部分核销, 4-已核销, 9-已关闭 --- -------------------------------------------------------- -CREATE TABLE IF NOT EXISTS `sal_reconciliation` ( - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '对账单ID', - `reconciliation_no` VARCHAR(50) NOT NULL COMMENT '对账单号', - `status` TINYINT NOT NULL DEFAULT 1 COMMENT '状态: 1-草稿, 2-已确认, 3-部分核销, 4-已核销, 9-已关闭', - `customer_id` BIGINT UNSIGNED NOT NULL COMMENT '客户ID', - `customer_name` VARCHAR(100) COMMENT '客户名称(冗余)', - `period_start` DATE NOT NULL COMMENT '对账开始日期', - `period_end` DATE NOT NULL COMMENT '对账结束日期', - `delivery_amount` DECIMAL(18,4) DEFAULT 0 COMMENT '发货总金额', - `return_amount` DECIMAL(18,4) DEFAULT 0 COMMENT '退货总金额', - `net_amount` DECIMAL(18,4) DEFAULT 0 COMMENT '净额(发货-退货)', - `discount_amount` DECIMAL(18,4) DEFAULT 0 COMMENT '折扣金额', - `received_amount` DECIMAL(18,4) DEFAULT 0 COMMENT '已核销金额', - `balance_amount` DECIMAL(18,4) DEFAULT 0 COMMENT '未核销余额', - `confirm_by` BIGINT UNSIGNED COMMENT '确认人ID', - `confirm_time` DATETIME COMMENT '确认时间', - `close_by` BIGINT UNSIGNED COMMENT '关闭人ID', - `close_time` DATETIME COMMENT '关闭时间', - `remark` VARCHAR(500) COMMENT '备注', - `version` INT DEFAULT 0 COMMENT '乐观锁版本号', - `deleted` TINYINT DEFAULT 0 COMMENT '删除标记', - `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - `update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', - `create_by` BIGINT UNSIGNED COMMENT '创建人ID', - `update_by` BIGINT UNSIGNED COMMENT '更新人ID', - PRIMARY KEY (`id`), - UNIQUE KEY `uk_reconciliation_no` (`reconciliation_no`), - KEY `idx_status` (`status`), - KEY `idx_customer` (`customer_id`), - KEY `idx_period` (`period_start`, `period_end`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='对账单主表'; - --- -------------------------------------------------------- --- 4. 对账单明细表 sal_reconciliation_line --- 对应 ReconciliationLineProps,记录对账期间内的发货/退货单 --- source_type: 1=发货单, 2=退货单 --- -------------------------------------------------------- -CREATE TABLE IF NOT EXISTS `sal_reconciliation_line` ( - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '明细ID', - `reconciliation_id` BIGINT UNSIGNED NOT NULL COMMENT '对账单ID', - `source_type` TINYINT NOT NULL COMMENT '来源类型: 1-发货单, 2-退货单', - `source_id` BIGINT UNSIGNED NOT NULL COMMENT '来源单据ID', - `source_no` VARCHAR(50) NOT NULL COMMENT '来源单据号', - `source_date` DATE NOT NULL COMMENT '来源单据日期', - `amount` DECIMAL(18,4) NOT NULL COMMENT '单据金额', - `remark` VARCHAR(255) COMMENT '备注', - `deleted` TINYINT DEFAULT 0 COMMENT '删除标记', - `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - PRIMARY KEY (`id`), - KEY `idx_reconciliation` (`reconciliation_id`), - KEY `idx_source` (`source_type`, `source_id`), - KEY `idx_source_no` (`source_no`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='对账单明细表'; - --- -------------------------------------------------------- --- 5. 对账核销记录表 sal_reconciliation_writeoff --- 对应 WriteOffRecord 实体,记录对账单对应收单的核销记录 --- 支持一对多核销:一张对账单可核销多张应收单 --- 支持多次部分核销:同一 receivable_id 可有多条记录 --- -------------------------------------------------------- -CREATE TABLE IF NOT EXISTS `sal_reconciliation_writeoff` ( - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '核销记录ID', - `reconciliation_id` BIGINT UNSIGNED NOT NULL COMMENT '对账单ID', - `receivable_id` BIGINT UNSIGNED NOT NULL COMMENT '应收单ID', - `amount` DECIMAL(18,4) NOT NULL COMMENT '核销金额', - `write_off_date` DATE NOT NULL COMMENT '核销日期', - `remark` VARCHAR(255) COMMENT '备注', - `deleted` TINYINT DEFAULT 0 COMMENT '删除标记', - `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - `create_by` BIGINT UNSIGNED COMMENT '创建人ID', - PRIMARY KEY (`id`), - KEY `idx_reconciliation` (`reconciliation_id`), - KEY `idx_receivable` (`receivable_id`), - KEY `idx_write_off_date` (`write_off_date`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='对账核销记录表'; diff --git a/database/migrations/013_alter_sal_delivery_add_fields.sql b/database/migrations/013_alter_sal_delivery_add_fields.sql deleted file mode 100644 index cec661c6..00000000 --- a/database/migrations/013_alter_sal_delivery_add_fields.sql +++ /dev/null @@ -1,152 +0,0 @@ --- ======================================================== --- 013: 补全 sal_delivery / sal_delivery_detail 缺失字段 --- 目的:使现有 sal_delivery 表能完整持久化 Delivery 聚合根 --- 缺失字段: --- sal_delivery: --- - order_no 销售订单号(冗余) --- - customer_name 客户名称(冗余) --- - ship_by 发货人ID --- - ship_time 发货时间 --- - sign_by 签收人ID --- - sign_time 签收时间 --- - version 乐观锁版本号 --- - update_time 更新时间(已有 create_time) --- - update_by 更新人ID --- - deleted 软删除标记 --- sal_delivery_detail: --- - line_no 行号 --- - material_code 物料编码(冗余) --- - material_name 物料名称(冗余) --- - material_spec 物料规格(冗余) --- - deleted 软删除标记 --- 兼容:使用 ALTER TABLE ADD COLUMN IF NOT EXISTS(MySQL 8.0.29+) --- 低版本用存储过程幂等检测,此处采用 INFORMATION_SCHEMA 检测 --- ======================================================== - --- -------------------------------------------------------- --- 1. sal_delivery 补字段 --- -------------------------------------------------------- - --- order_no -SET @col_exists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'sal_delivery' AND COLUMN_NAME = 'order_no'); -SET @sql = IF(@col_exists = 0, - 'ALTER TABLE `sal_delivery` ADD COLUMN `order_no` VARCHAR(50) COMMENT ''销售订单号(冗余)'' AFTER `order_id`', - 'SELECT ''column order_no already exists'' AS msg'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- customer_name -SET @col_exists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'sal_delivery' AND COLUMN_NAME = 'customer_name'); -SET @sql = IF(@col_exists = 0, - 'ALTER TABLE `sal_delivery` ADD COLUMN `customer_name` VARCHAR(100) COMMENT ''客户名称(冗余)'' AFTER `customer_id`', - 'SELECT ''column customer_name already exists'' AS msg'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- ship_by -SET @col_exists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'sal_delivery' AND COLUMN_NAME = 'ship_by'); -SET @sql = IF(@col_exists = 0, - 'ALTER TABLE `sal_delivery` ADD COLUMN `ship_by` BIGINT UNSIGNED COMMENT ''发货人ID'' AFTER `create_by`', - 'SELECT ''column ship_by already exists'' AS msg'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- ship_time -SET @col_exists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'sal_delivery' AND COLUMN_NAME = 'ship_time'); -SET @sql = IF(@col_exists = 0, - 'ALTER TABLE `sal_delivery` ADD COLUMN `ship_time` DATETIME COMMENT ''发货时间'' AFTER `ship_by`', - 'SELECT ''column ship_time already exists'' AS msg'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- sign_by -SET @col_exists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'sal_delivery' AND COLUMN_NAME = 'sign_by'); -SET @sql = IF(@col_exists = 0, - 'ALTER TABLE `sal_delivery` ADD COLUMN `sign_by` BIGINT UNSIGNED COMMENT ''签收人ID'' AFTER `ship_time`', - 'SELECT ''column sign_by already exists'' AS msg'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- sign_time -SET @col_exists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'sal_delivery' AND COLUMN_NAME = 'sign_time'); -SET @sql = IF(@col_exists = 0, - 'ALTER TABLE `sal_delivery` ADD COLUMN `sign_time` DATETIME COMMENT ''签收时间'' AFTER `sign_by`', - 'SELECT ''column sign_time already exists'' AS msg'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- version -SET @col_exists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'sal_delivery' AND COLUMN_NAME = 'version'); -SET @sql = IF(@col_exists = 0, - 'ALTER TABLE `sal_delivery` ADD COLUMN `version` INT DEFAULT 0 COMMENT ''乐观锁版本号''', - 'SELECT ''column version already exists'' AS msg'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- update_time -SET @col_exists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'sal_delivery' AND COLUMN_NAME = 'update_time'); -SET @sql = IF(@col_exists = 0, - 'ALTER TABLE `sal_delivery` ADD COLUMN `update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT ''更新时间''', - 'SELECT ''column update_time already exists'' AS msg'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- update_by -SET @col_exists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'sal_delivery' AND COLUMN_NAME = 'update_by'); -SET @sql = IF(@col_exists = 0, - 'ALTER TABLE `sal_delivery` ADD COLUMN `update_by` BIGINT UNSIGNED COMMENT ''更新人ID''', - 'SELECT ''column update_by already exists'' AS msg'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- deleted -SET @col_exists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'sal_delivery' AND COLUMN_NAME = 'deleted'); -SET @sql = IF(@col_exists = 0, - 'ALTER TABLE `sal_delivery` ADD COLUMN `deleted` TINYINT DEFAULT 0 COMMENT ''删除标记''', - 'SELECT ''column deleted already exists'' AS msg'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- -------------------------------------------------------- --- 2. sal_delivery_detail 补字段 --- -------------------------------------------------------- - --- line_no -SET @col_exists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'sal_delivery_detail' AND COLUMN_NAME = 'line_no'); -SET @sql = IF(@col_exists = 0, - 'ALTER TABLE `sal_delivery_detail` ADD COLUMN `line_no` INT COMMENT ''行号'' AFTER `delivery_id`', - 'SELECT ''column line_no already exists'' AS msg'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- material_code -SET @col_exists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'sal_delivery_detail' AND COLUMN_NAME = 'material_code'); -SET @sql = IF(@col_exists = 0, - 'ALTER TABLE `sal_delivery_detail` ADD COLUMN `material_code` VARCHAR(50) COMMENT ''物料编码(冗余)'' AFTER `material_id`', - 'SELECT ''column material_code already exists'' AS msg'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- material_name -SET @col_exists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'sal_delivery_detail' AND COLUMN_NAME = 'material_name'); -SET @sql = IF(@col_exists = 0, - 'ALTER TABLE `sal_delivery_detail` ADD COLUMN `material_name` VARCHAR(200) COMMENT ''物料名称(冗余)'' AFTER `material_code`', - 'SELECT ''column material_name already exists'' AS msg'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- material_spec -SET @col_exists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'sal_delivery_detail' AND COLUMN_NAME = 'material_spec'); -SET @sql = IF(@col_exists = 0, - 'ALTER TABLE `sal_delivery_detail` ADD COLUMN `material_spec` VARCHAR(100) COMMENT ''物料规格(冗余)'' AFTER `material_name`', - 'SELECT ''column material_spec already exists'' AS msg'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- deleted -SET @col_exists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'sal_delivery_detail' AND COLUMN_NAME = 'deleted'); -SET @sql = IF(@col_exists = 0, - 'ALTER TABLE `sal_delivery_detail` ADD COLUMN `deleted` TINYINT DEFAULT 0 COMMENT ''删除标记''', - 'SELECT ''column deleted already exists'' AS msg'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; diff --git a/database/migrations/014_alter_sal_delivery_add_logistics_fields.sql b/database/migrations/014_alter_sal_delivery_add_logistics_fields.sql deleted file mode 100644 index c4a3a764..00000000 --- a/database/migrations/014_alter_sal_delivery_add_logistics_fields.sql +++ /dev/null @@ -1,52 +0,0 @@ --- ======================================================== --- 014: 补全 sal_delivery 物流联系人字段 --- 目的:支持发货单的物流配送信息(收货人、联系方式、配送地址) --- 缺失字段: --- sal_delivery: --- - total_qty 总数量(明细数量合计,冗余便于查询) --- - contact_name 收货人姓名 --- - contact_phone 收货人电话 --- - delivery_address 配送地址 --- - sign_status 签收状态(0-未签收, 1-已签收) --- 兼容:INFORMATION_SCHEMA 检测 + PREPARE/EXECUTE 实现幂等 --- ======================================================== - --- total_qty -SET @col_exists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'sal_delivery' AND COLUMN_NAME = 'total_qty'); -SET @sql = IF(@col_exists = 0, - 'ALTER TABLE `sal_delivery` ADD COLUMN `total_qty` DECIMAL(18,4) DEFAULT 0 COMMENT ''总数量(明细数量合计)'' AFTER `total_amount`', - 'SELECT ''column total_qty already exists'' AS msg'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- contact_name -SET @col_exists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'sal_delivery' AND COLUMN_NAME = 'contact_name'); -SET @sql = IF(@col_exists = 0, - 'ALTER TABLE `sal_delivery` ADD COLUMN `contact_name` VARCHAR(50) COMMENT ''收货人姓名'' AFTER `tracking_no`', - 'SELECT ''column contact_name already exists'' AS msg'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- contact_phone -SET @col_exists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'sal_delivery' AND COLUMN_NAME = 'contact_phone'); -SET @sql = IF(@col_exists = 0, - 'ALTER TABLE `sal_delivery` ADD COLUMN `contact_phone` VARCHAR(30) COMMENT ''收货人电话'' AFTER `contact_name`', - 'SELECT ''column contact_phone already exists'' AS msg'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- delivery_address -SET @col_exists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'sal_delivery' AND COLUMN_NAME = 'delivery_address'); -SET @sql = IF(@col_exists = 0, - 'ALTER TABLE `sal_delivery` ADD COLUMN `delivery_address` VARCHAR(500) COMMENT ''配送地址'' AFTER `contact_phone`', - 'SELECT ''column delivery_address already exists'' AS msg'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- sign_status -SET @col_exists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'sal_delivery' AND COLUMN_NAME = 'sign_status'); -SET @sql = IF(@col_exists = 0, - 'ALTER TABLE `sal_delivery` ADD COLUMN `sign_status` TINYINT DEFAULT 0 COMMENT ''签收状态: 0-未签收, 1-已签收'' AFTER `sign_time`', - 'SELECT ''column sign_status already exists'' AS msg'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; diff --git a/database/migrations/015_align_purchase_schema.sql b/database/migrations/015_align_purchase_schema.sql deleted file mode 100644 index f806032b..00000000 --- a/database/migrations/015_align_purchase_schema.sql +++ /dev/null @@ -1,267 +0,0 @@ --- Migration 015: 对齐采购模块 Schema 基线 --- 目标:统一 vnerpdacahng_schema.sql 与运行时代码的表名/字段/状态码 --- 决策依据:.trae/documents/P0-高危问题修复方案.md 第99行 --- "pur_order(SQL 旧名) vs pur_purchase_order(运行时) → 以运行时为准, --- 删除 SQL 中未用的 pur_order/pur_order_detail,标注废弃" --- --- 变更内容: --- 1. 废弃旧表 pur_order / pur_order_detail / pur_receipt / pur_receipt_detail --- (运行时未使用;收货复用 inv_inbound_order,见 create_po_grn_tables.sql) --- 2. 建立/对齐权威表 pur_purchase_order / pur_purchase_order_line --- (对齐 scripts/create_po_grn_tables.sql 的运行时表结构) --- 3. 状态码统一为 10/20/30/40/50/90(与 PurchaseOrderStatus.fromDbCode 一致) --- 旧码 1-待确认/2-已确认/3-部分到货/4-已完成/5-已取消 → 新码 10/20/30/40/50/90 --- --- 安全性:所有 CREATE 使用 IF NOT EXISTS,ADD COLUMN 使用 IF NOT EXISTS(MySQL 8.0+) --- 旧表采用 RENAME 而非 DROP,保留数据以便回滚 - --- ========================================== --- 1. 废弃旧表(重命名而非删除,保留数据) --- ========================================== - --- 旧表 pur_order → pur_order_deprecated -SET @sql = IF( - EXISTS(SELECT 1 FROM information_schema.tables WHERE table_schema = DATABASE() AND table_name = 'pur_order'), - 'RENAME TABLE pur_order TO pur_order_deprecated', - 'SELECT "pur_order not exists, skip" AS msg' -); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 旧表 pur_order_detail → pur_order_detail_deprecated -SET @sql = IF( - EXISTS(SELECT 1 FROM information_schema.tables WHERE table_schema = DATABASE() AND table_name = 'pur_order_detail'), - 'RENAME TABLE pur_order_detail TO pur_order_detail_deprecated', - 'SELECT "pur_order_detail not exists, skip" AS msg' -); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 旧表 pur_receipt → pur_receipt_deprecated --- 注意:运行时收货复用 inv_inbound_order(见 create_po_grn_tables.sql 第84行 ALTER TABLE inv_inbound_order) -SET @sql = IF( - EXISTS(SELECT 1 FROM information_schema.tables WHERE table_schema = DATABASE() AND table_name = 'pur_receipt'), - 'RENAME TABLE pur_receipt TO pur_receipt_deprecated', - 'SELECT "pur_receipt not exists, skip" AS msg' -); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 旧表 pur_receipt_detail → pur_receipt_detail_deprecated -SET @sql = IF( - EXISTS(SELECT 1 FROM information_schema.tables WHERE table_schema = DATABASE() AND table_name = 'pur_receipt_detail'), - 'RENAME TABLE pur_receipt_detail TO pur_receipt_detail_deprecated', - 'SELECT "pur_receipt_detail not exists, skip" AS msg' -); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- ========================================== --- 2. 建立权威表 pur_purchase_order(采购单主表) --- ========================================== -CREATE TABLE IF NOT EXISTS pur_purchase_order ( - id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY COMMENT '主键ID', - po_no VARCHAR(50) NOT NULL COMMENT '采购单号', - supplier_id INT UNSIGNED DEFAULT NULL COMMENT '供应商ID', - supplier_name VARCHAR(100) NOT NULL COMMENT '供应商名称', - supplier_code VARCHAR(50) DEFAULT NULL COMMENT '供应商编码', - order_date DATE NOT NULL COMMENT '订单日期', - delivery_date DATE DEFAULT NULL COMMENT '预计交货日期', - currency VARCHAR(10) DEFAULT 'CNY' COMMENT '币种', - exchange_rate DECIMAL(10,4) DEFAULT 1.0000 COMMENT '汇率', - total_amount DECIMAL(14,2) DEFAULT 0 COMMENT '订单总金额', - total_quantity DECIMAL(14,3) DEFAULT 0 COMMENT '订单总数量', - tax_rate DECIMAL(5,2) DEFAULT 13.00 COMMENT '税率%', - tax_amount DECIMAL(14,2) DEFAULT 0 COMMENT '税额', - grand_total DECIMAL(14,2) DEFAULT 0 COMMENT '含税总金额', - -- 状态: 10-草稿, 20-待审批, 30-已审批, 40-部分收货, 50-已完成, 90-已关闭 - -- 与 PurchaseOrderStatus.fromDbCode 对齐 - status TINYINT UNSIGNED DEFAULT 10 COMMENT '状态: 10-草稿, 20-待审批, 30-已审批, 40-部分收货, 50-已完成, 90-已关闭', - over_receipt_tolerance DECIMAL(5,2) DEFAULT 5.00 COMMENT '超收容差率%', - payment_terms VARCHAR(100) DEFAULT NULL COMMENT '付款条款', - delivery_address TEXT DEFAULT NULL COMMENT '送货地址', - contact_person VARCHAR(50) DEFAULT NULL COMMENT '联系人', - contact_phone VARCHAR(50) DEFAULT NULL COMMENT '联系电话', - remark TEXT DEFAULT NULL COMMENT '备注', - create_by INT UNSIGNED DEFAULT NULL COMMENT '创建人ID', - create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - update_by INT UNSIGNED DEFAULT NULL COMMENT '更新人ID', - update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', - audit_by INT UNSIGNED DEFAULT NULL COMMENT '审批人ID', - audit_time DATETIME DEFAULT NULL COMMENT '审批时间', - close_by INT UNSIGNED DEFAULT NULL COMMENT '关闭人ID', - close_time DATETIME DEFAULT NULL COMMENT '关闭时间', - close_reason VARCHAR(200) DEFAULT NULL COMMENT '关闭原因', - deleted TINYINT(1) DEFAULT 0 COMMENT '是否删除', - UNIQUE KEY uk_po_no (po_no), - INDEX idx_supplier (supplier_id), - INDEX idx_status (status), - INDEX idx_order_date (order_date), - INDEX idx_delivery_date (delivery_date), - INDEX idx_create_time (create_time) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='采购单主表'; - --- 补全字段(如果表已存在但缺字段,对齐 create_po_grn_tables.sql) --- MySQL 8.0.29+ 支持 ADD COLUMN IF NOT EXISTS;旧版本需用存储过程 --- 此处使用存储过程保证幂等 -DELIMITER $$ -CREATE PROCEDURE IF NOT EXISTS p_align_purchase_order_columns() -BEGIN - IF NOT EXISTS (SELECT 1 FROM information_schema.columns - WHERE table_schema = DATABASE() AND table_name = 'pur_purchase_order' AND column_name = 'audit_by') THEN - ALTER TABLE pur_purchase_order ADD COLUMN audit_by INT UNSIGNED DEFAULT NULL COMMENT '审批人ID' AFTER update_time; - END IF; - IF NOT EXISTS (SELECT 1 FROM information_schema.columns - WHERE table_schema = DATABASE() AND table_name = 'pur_purchase_order' AND column_name = 'audit_time') THEN - ALTER TABLE pur_purchase_order ADD COLUMN audit_time DATETIME DEFAULT NULL COMMENT '审批时间' AFTER audit_by; - END IF; - IF NOT EXISTS (SELECT 1 FROM information_schema.columns - WHERE table_schema = DATABASE() AND table_name = 'pur_purchase_order' AND column_name = 'close_by') THEN - ALTER TABLE pur_purchase_order ADD COLUMN close_by INT UNSIGNED DEFAULT NULL COMMENT '关闭人ID' AFTER audit_time; - END IF; - IF NOT EXISTS (SELECT 1 FROM information_schema.columns - WHERE table_schema = DATABASE() AND table_name = 'pur_purchase_order' AND column_name = 'close_time') THEN - ALTER TABLE pur_purchase_order ADD COLUMN close_time DATETIME DEFAULT NULL COMMENT '关闭时间' AFTER close_by; - END IF; - IF NOT EXISTS (SELECT 1 FROM information_schema.columns - WHERE table_schema = DATABASE() AND table_name = 'pur_purchase_order' AND column_name = 'close_reason') THEN - ALTER TABLE pur_purchase_order ADD COLUMN close_reason VARCHAR(200) DEFAULT NULL COMMENT '关闭原因' AFTER close_time; - END IF; - IF NOT EXISTS (SELECT 1 FROM information_schema.columns - WHERE table_schema = DATABASE() AND table_name = 'pur_purchase_order' AND column_name = 'over_receipt_tolerance') THEN - ALTER TABLE pur_purchase_order ADD COLUMN over_receipt_tolerance DECIMAL(5,2) DEFAULT 5.00 COMMENT '超收容差率%' AFTER status; - END IF; - IF NOT EXISTS (SELECT 1 FROM information_schema.columns - WHERE table_schema = DATABASE() AND table_name = 'pur_purchase_order' AND column_name = 'grand_total') THEN - ALTER TABLE pur_purchase_order ADD COLUMN grand_total DECIMAL(14,2) DEFAULT 0 COMMENT '含税总金额' AFTER tax_amount; - END IF; - IF NOT EXISTS (SELECT 1 FROM information_schema.columns - WHERE table_schema = DATABASE() AND table_name = 'pur_purchase_order' AND column_name = 'tax_rate') THEN - ALTER TABLE pur_purchase_order ADD COLUMN tax_rate DECIMAL(5,2) DEFAULT 13.00 COMMENT '税率%' AFTER total_quantity; - END IF; - IF NOT EXISTS (SELECT 1 FROM information_schema.columns - WHERE table_schema = DATABASE() AND table_name = 'pur_purchase_order' AND column_name = 'tax_amount') THEN - ALTER TABLE pur_purchase_order ADD COLUMN tax_amount DECIMAL(14,2) DEFAULT 0 COMMENT '税额' AFTER tax_rate; - END IF; - IF NOT EXISTS (SELECT 1 FROM information_schema.columns - WHERE table_schema = DATABASE() AND table_name = 'pur_purchase_order' AND column_name = 'total_quantity') THEN - ALTER TABLE pur_purchase_order ADD COLUMN total_quantity DECIMAL(14,3) DEFAULT 0 COMMENT '订单总数量' AFTER total_amount; - END IF; - IF NOT EXISTS (SELECT 1 FROM information_schema.columns - WHERE table_schema = DATABASE() AND table_name = 'pur_purchase_order' AND column_name = 'supplier_code') THEN - ALTER TABLE pur_purchase_order ADD COLUMN supplier_code VARCHAR(50) DEFAULT NULL COMMENT '供应商编码' AFTER supplier_name; - END IF; - IF NOT EXISTS (SELECT 1 FROM information_schema.columns - WHERE table_schema = DATABASE() AND table_name = 'pur_purchase_order' AND column_name = 'exchange_rate') THEN - ALTER TABLE pur_purchase_order ADD COLUMN exchange_rate DECIMAL(10,4) DEFAULT 1.0000 COMMENT '汇率' AFTER currency; - END IF; - IF NOT EXISTS (SELECT 1 FROM information_schema.columns - WHERE table_schema = DATABASE() AND table_name = 'pur_purchase_order' AND column_name = 'deleted') THEN - ALTER TABLE pur_purchase_order ADD COLUMN deleted TINYINT(1) DEFAULT 0 COMMENT '是否删除' AFTER close_reason; - END IF; -END$$ -DELIMITER ; -CALL p_align_purchase_order_columns(); -DROP PROCEDURE IF EXISTS p_align_purchase_order_columns; - --- ========================================== --- 3. 建立权威表 pur_purchase_order_line(采购单行表) --- ========================================== -CREATE TABLE IF NOT EXISTS pur_purchase_order_line ( - id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY COMMENT '主键ID', - po_id INT UNSIGNED NOT NULL COMMENT '采购单ID', - line_no INT UNSIGNED NOT NULL COMMENT '行号', - material_id INT UNSIGNED DEFAULT NULL COMMENT '物料ID', - material_code VARCHAR(50) NOT NULL COMMENT '物料编码', - material_name VARCHAR(200) NOT NULL COMMENT '物料名称', - material_spec VARCHAR(500) DEFAULT NULL COMMENT '物料规格', - unit VARCHAR(20) DEFAULT '件' COMMENT '单位', - order_qty DECIMAL(14,3) NOT NULL DEFAULT 0 COMMENT '订购数量', - received_qty DECIMAL(14,3) DEFAULT 0 COMMENT '累计入库数量', - returned_qty DECIMAL(14,3) DEFAULT 0 COMMENT '累计退货数量', - unit_price DECIMAL(14,4) NOT NULL DEFAULT 0 COMMENT '单价', - amount DECIMAL(14,2) DEFAULT 0 COMMENT '金额', - tax_rate DECIMAL(5,2) DEFAULT 13.00 COMMENT '税率%', - tax_amount DECIMAL(14,2) DEFAULT 0 COMMENT '税额', - line_total DECIMAL(14,2) DEFAULT 0 COMMENT '行合计', - require_date DATE DEFAULT NULL COMMENT '需求日期', - closed_flag TINYINT(1) DEFAULT 0 COMMENT '行关闭标志', - closed_reason VARCHAR(200) DEFAULT NULL COMMENT '关闭原因', - remark TEXT DEFAULT NULL COMMENT '备注', - create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', - UNIQUE KEY uk_po_line (po_id, line_no), - INDEX idx_material (material_id), - INDEX idx_material_code (material_code), - INDEX idx_require_date (require_date), - CONSTRAINT fk_pur_line_po FOREIGN KEY (po_id) REFERENCES pur_purchase_order(id) ON DELETE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='采购单行表'; - --- 补全 pur_purchase_order_line 字段(幂等) -DELIMITER $$ -CREATE PROCEDURE IF NOT EXISTS p_align_purchase_line_columns() -BEGIN - IF NOT EXISTS (SELECT 1 FROM information_schema.columns - WHERE table_schema = DATABASE() AND table_name = 'pur_purchase_order_line' AND column_name = 'returned_qty') THEN - ALTER TABLE pur_purchase_order_line ADD COLUMN returned_qty DECIMAL(14,3) DEFAULT 0 COMMENT '累计退货数量' AFTER received_qty; - END IF; - IF NOT EXISTS (SELECT 1 FROM information_schema.columns - WHERE table_schema = DATABASE() AND table_name = 'pur_purchase_order_line' AND column_name = 'tax_rate') THEN - ALTER TABLE pur_purchase_order_line ADD COLUMN tax_rate DECIMAL(5,2) DEFAULT 13.00 COMMENT '税率%' AFTER amount; - END IF; - IF NOT EXISTS (SELECT 1 FROM information_schema.columns - WHERE table_schema = DATABASE() AND table_name = 'pur_purchase_order_line' AND column_name = 'tax_amount') THEN - ALTER TABLE pur_purchase_order_line ADD COLUMN tax_amount DECIMAL(14,2) DEFAULT 0 COMMENT '税额' AFTER tax_rate; - END IF; - IF NOT EXISTS (SELECT 1 FROM information_schema.columns - WHERE table_schema = DATABASE() AND table_name = 'pur_purchase_order_line' AND column_name = 'line_total') THEN - ALTER TABLE pur_purchase_order_line ADD COLUMN line_total DECIMAL(14,2) DEFAULT 0 COMMENT '行合计' AFTER tax_amount; - END IF; - IF NOT EXISTS (SELECT 1 FROM information_schema.columns - WHERE table_schema = DATABASE() AND table_name = 'pur_purchase_order_line' AND column_name = 'closed_flag') THEN - ALTER TABLE pur_purchase_order_line ADD COLUMN closed_flag TINYINT(1) DEFAULT 0 COMMENT '行关闭标志' AFTER require_date; - END IF; - IF NOT EXISTS (SELECT 1 FROM information_schema.columns - WHERE table_schema = DATABASE() AND table_name = 'pur_purchase_order_line' AND column_name = 'closed_reason') THEN - ALTER TABLE pur_purchase_order_line ADD COLUMN closed_reason VARCHAR(200) DEFAULT NULL COMMENT '关闭原因' AFTER closed_flag; - END IF; -END$$ -DELIMITER ; -CALL p_align_purchase_line_columns(); -DROP PROCEDURE IF EXISTS p_align_purchase_line_columns; - --- ========================================== --- 4. 状态码迁移(如果旧表 pur_order_deprecated 有数据且需迁移) --- 旧码: 1-待确认, 2-已确认, 3-部分到货, 4-已完成, 5-已取消 --- 新码: 10-草稿, 20-待审批, 30-已审批, 40-部分收货, 50-已完成, 90-已关闭 --- 映射: 1→10, 2→20, 3→40, 4→50, 5→90 --- 注:如需数据迁移,请手动执行以下语句(注释状态) --- ========================================== --- INSERT INTO pur_purchase_order (po_no, supplier_id, supplier_name, order_date, total_amount, status, create_time) --- SELECT order_no, supplier_id, '', order_date, total_amount, --- CASE status --- WHEN 1 THEN 10 -- 待确认 → 草稿 --- WHEN 2 THEN 20 -- 已确认 → 待审批 --- WHEN 3 THEN 40 -- 部分到货 → 部分收货 --- WHEN 4 THEN 50 -- 已完成 → 已完成 --- WHEN 5 THEN 90 -- 已取消 → 已关闭 --- ELSE 10 --- END, --- create_time --- FROM pur_order_deprecated --- WHERE po_no NOT IN (SELECT po_no FROM pur_purchase_order); - --- ========================================== --- 5. 验证 --- ========================================== --- 验证新表存在且字段完整 -SELECT 'pur_purchase_order' AS table_name, - (SELECT COUNT(*) FROM information_schema.columns - WHERE table_schema = DATABASE() AND table_name = 'pur_purchase_order') AS column_count, - (SELECT COUNT(*) FROM information_schema.columns - WHERE table_schema = DATABASE() AND table_name = 'pur_purchase_order' AND column_name IN - ('id','po_no','supplier_id','supplier_name','order_date','total_amount','status', - 'audit_by','audit_time','close_by','close_time','close_reason','deleted')) AS key_columns_present -UNION ALL -SELECT 'pur_purchase_order_line', - (SELECT COUNT(*) FROM information_schema.columns - WHERE table_schema = DATABASE() AND table_name = 'pur_purchase_order_line'), - (SELECT COUNT(*) FROM information_schema.columns - WHERE table_schema = DATABASE() AND table_name = 'pur_purchase_order_line' AND column_name IN - ('id','po_id','line_no','material_id','order_qty','received_qty','returned_qty','unit_price','amount')); diff --git a/database/migrations/016_create_purchase_return_reconciliation_tables.sql b/database/migrations/016_create_purchase_return_reconciliation_tables.sql deleted file mode 100644 index f3bc2668..00000000 --- a/database/migrations/016_create_purchase_return_reconciliation_tables.sql +++ /dev/null @@ -1,127 +0,0 @@ --- ============================================================ --- Migration 016: 采购退货 + 采购对账表结构 --- 表名: pur_purchase_return, pur_purchase_return_line, --- pur_purchase_reconciliation, pur_purchase_reconciliation_writeoff --- 字符集: utf8mb4 --- ============================================================ - --- -------------------------------------------------------- --- 1. 采购退货主表 --- -------------------------------------------------------- -CREATE TABLE IF NOT EXISTS `pur_purchase_return` ( - `id` BIGINT NOT NULL AUTO_INCREMENT, - `return_no` VARCHAR(32) NOT NULL COMMENT '退货单号', - `status` TINYINT NOT NULL DEFAULT 1 COMMENT '状态: 1=待审核, 2=已审核, 3=已完成, 9=已取消', - `order_id` BIGINT NOT NULL COMMENT '采购订单ID', - `order_no` VARCHAR(32) NOT NULL DEFAULT '' COMMENT '采购订单号', - `supplier_id` BIGINT NOT NULL COMMENT '供应商ID', - `supplier_name` VARCHAR(128) NOT NULL DEFAULT '' COMMENT '供应商名称', - `warehouse_id` BIGINT NOT NULL COMMENT '仓库ID', - `receipt_id` BIGINT NULL COMMENT '收货单ID', - `receipt_no` VARCHAR(32) NOT NULL DEFAULT '' COMMENT '收货单号', - `reason` VARCHAR(512) NOT NULL COMMENT '退货原因', - `return_date` DATE NOT NULL COMMENT '退货日期', - `total_amount` DECIMAL(14,2) NOT NULL DEFAULT 0.00 COMMENT '退货总金额', - `approve_by` BIGINT NULL COMMENT '审核人', - `approve_time` DATETIME NULL COMMENT '审核时间', - `complete_by` BIGINT NULL COMMENT '完成人', - `complete_time` DATETIME NULL COMMENT '完成时间', - `outbound_order_id` BIGINT NULL COMMENT '出库单ID', - `outbound_order_no` VARCHAR(32) NULL COMMENT '出库单号', - `payable_id` BIGINT NULL COMMENT '红字应付单ID', - `payable_no` VARCHAR(32) NULL COMMENT '红字应付单号', - `remark` VARCHAR(512) NOT NULL DEFAULT '' COMMENT '备注', - `create_by` BIGINT NULL COMMENT '创建人', - `create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - `update_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', - `deleted` TINYINT NOT NULL DEFAULT 0 COMMENT '软删除: 0=未删除, 1=已删除', - PRIMARY KEY (`id`), - UNIQUE KEY `uk_return_no` (`return_no`), - KEY `idx_order_id` (`order_id`), - KEY `idx_supplier_id` (`supplier_id`), - KEY `idx_status` (`status`), - KEY `idx_return_date` (`return_date`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='采购退货主表'; - --- -------------------------------------------------------- --- 2. 采购退货明细表 --- -------------------------------------------------------- -CREATE TABLE IF NOT EXISTS `pur_purchase_return_line` ( - `id` BIGINT NOT NULL AUTO_INCREMENT, - `return_id` BIGINT NOT NULL COMMENT '退货单ID', - `line_no` INT NOT NULL COMMENT '行号', - `order_line_id` BIGINT NULL COMMENT '采购订单行ID', - `material_id` BIGINT NOT NULL COMMENT '物料ID', - `material_code` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '物料编码', - `material_name` VARCHAR(256) NOT NULL DEFAULT '' COMMENT '物料名称', - `material_spec` VARCHAR(256) NOT NULL DEFAULT '' COMMENT '物料规格', - `unit` VARCHAR(32) NOT NULL DEFAULT '件' COMMENT '单位', - `quantity` DECIMAL(14,4) NOT NULL COMMENT '退货数量', - `unit_price` DECIMAL(14,2) NOT NULL COMMENT '单价', - `amount` DECIMAL(14,2) NOT NULL COMMENT '金额', - `batch_no` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '批次号', - `reason` VARCHAR(512) NOT NULL DEFAULT '' COMMENT '退货原因', - `remark` VARCHAR(512) NOT NULL DEFAULT '' COMMENT '备注', - PRIMARY KEY (`id`), - KEY `idx_return_id` (`return_id`), - KEY `idx_material_id` (`material_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='采购退货明细表'; - --- -------------------------------------------------------- --- 3. 采购对账主表 --- -------------------------------------------------------- -CREATE TABLE IF NOT EXISTS `pur_purchase_reconciliation` ( - `id` BIGINT NOT NULL AUTO_INCREMENT, - `reconciliation_no` VARCHAR(32) NOT NULL COMMENT '对账单号', - `status` TINYINT NOT NULL DEFAULT 1 COMMENT '状态: 1=草稿, 2=已确认, 3=部分核销, 4=已核销完成, 9=已关闭', - `supplier_id` BIGINT NOT NULL COMMENT '供应商ID', - `supplier_name` VARCHAR(128) NOT NULL DEFAULT '' COMMENT '供应商名称', - `period_start` DATE NOT NULL COMMENT '对账开始日期', - `period_end` DATE NOT NULL COMMENT '对账结束日期', - `receipt_amount` DECIMAL(14,2) NOT NULL DEFAULT 0.00 COMMENT '收货金额', - `return_amount` DECIMAL(14,2) NOT NULL DEFAULT 0.00 COMMENT '退货金额', - `net_amount` DECIMAL(14,2) NOT NULL DEFAULT 0.00 COMMENT '净额', - `discount_amount` DECIMAL(14,2) NOT NULL DEFAULT 0.00 COMMENT '折扣金额', - `paid_amount` DECIMAL(14,2) NOT NULL DEFAULT 0.00 COMMENT '已核销金额', - `balance_amount` DECIMAL(14,2) NOT NULL DEFAULT 0.00 COMMENT '余额', - `remark` VARCHAR(512) NOT NULL DEFAULT '' COMMENT '备注', - `create_by` BIGINT NULL COMMENT '创建人', - `confirm_by` BIGINT NULL COMMENT '确认人', - `confirm_time` DATETIME NULL COMMENT '确认时间', - `close_by` BIGINT NULL COMMENT '关闭人', - `close_time` DATETIME NULL COMMENT '关闭时间', - `create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - `update_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', - `deleted` TINYINT NOT NULL DEFAULT 0 COMMENT '软删除: 0=未删除, 1=已删除', - PRIMARY KEY (`id`), - UNIQUE KEY `uk_reconciliation_no` (`reconciliation_no`), - KEY `idx_supplier_id` (`supplier_id`), - KEY `idx_status` (`status`), - KEY `idx_period` (`period_start`, `period_end`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='采购对账主表'; - --- -------------------------------------------------------- --- 4. 采购对账核销记录表 --- -------------------------------------------------------- -CREATE TABLE IF NOT EXISTS `pur_purchase_reconciliation_writeoff` ( - `id` BIGINT NOT NULL AUTO_INCREMENT, - `reconciliation_id` BIGINT NOT NULL COMMENT '对账单ID', - `payable_id` BIGINT NOT NULL COMMENT '应付单ID', - `amount` DECIMAL(14,2) NOT NULL COMMENT '核销金额', - `write_off_date` DATE NOT NULL COMMENT '核销日期', - `remark` VARCHAR(512) NOT NULL DEFAULT '' COMMENT '备注', - `create_by` BIGINT NULL COMMENT '创建人', - `create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - PRIMARY KEY (`id`), - KEY `idx_reconciliation_id` (`reconciliation_id`), - KEY `idx_payable_id` (`payable_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='采购对账核销记录表'; - --- 验证 -SELECT 'pur_purchase_return' AS table_name, COUNT(*) AS row_count FROM pur_purchase_return -UNION ALL -SELECT 'pur_purchase_return_line', COUNT(*) FROM pur_purchase_return_line -UNION ALL -SELECT 'pur_purchase_reconciliation', COUNT(*) FROM pur_purchase_reconciliation -UNION ALL -SELECT 'pur_purchase_reconciliation_writeoff', COUNT(*) FROM pur_purchase_reconciliation_writeoff; diff --git a/database/migrations/017_add_core_foreign_keys.sql b/database/migrations/017_add_core_foreign_keys.sql deleted file mode 100644 index 1feea34d..00000000 --- a/database/migrations/017_add_core_foreign_keys.sql +++ /dev/null @@ -1,126 +0,0 @@ --- ============================================================ --- Migration 017: 补充核心外键约束 --- 日期: 2026-07-07 --- 目的: 修复 database-design-analysis.html 报告中识别的 P0 严重问题 --- "87 张表仅 3 个外键约束,依赖应用层维护父子关系" --- 策略: --- 1. 仅对类型完全匹配的列对添加 FK(BIGINT UNSIGNED → BIGINT UNSIGNED) --- 2. 强父子关系(主表-明细表)使用 ON DELETE CASCADE --- 3. 弱关联关系(如订单-客户)使用 ON DELETE RESTRICT --- 4. 可空外键使用 ON DELETE SET NULL --- 5. 使用 INFORMATION_SCHEMA 守卫保证幂等 --- 6. migrate.ts 按分号分割,禁止使用存储过程 --- ============================================================ - --- ======================================================== --- 一、销售模块外键(6 个) --- ======================================================== - --- sal_order.customer_id → crm_customer.id -SET @cnt = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'sal_order' AND CONSTRAINT_NAME = 'fk_sal_order_customer'); -SET @sql = IF(@cnt = 0, 'ALTER TABLE sal_order ADD CONSTRAINT fk_sal_order_customer FOREIGN KEY (customer_id) REFERENCES crm_customer(id) ON DELETE RESTRICT ON UPDATE CASCADE', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- sal_delivery.order_id → sal_order.id (可空,订单可删除时置空) -SET @cnt = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'sal_delivery' AND CONSTRAINT_NAME = 'fk_sal_delivery_order'); -SET @sql = IF(@cnt = 0, 'ALTER TABLE sal_delivery ADD CONSTRAINT fk_sal_delivery_order FOREIGN KEY (order_id) REFERENCES sal_order(id) ON DELETE SET NULL ON UPDATE CASCADE', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- sal_delivery_detail.delivery_id → sal_delivery.id (强父子,CASCADE) -SET @cnt = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'sal_delivery_detail' AND CONSTRAINT_NAME = 'fk_sal_delivery_detail_delivery'); -SET @sql = IF(@cnt = 0, 'ALTER TABLE sal_delivery_detail ADD CONSTRAINT fk_sal_delivery_detail_delivery FOREIGN KEY (delivery_id) REFERENCES sal_delivery(id) ON DELETE CASCADE ON UPDATE CASCADE', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- sal_delivery_detail.material_id → inv_material.id -SET @cnt = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'sal_delivery_detail' AND CONSTRAINT_NAME = 'fk_sal_delivery_detail_material'); -SET @sql = IF(@cnt = 0, 'ALTER TABLE sal_delivery_detail ADD CONSTRAINT fk_sal_delivery_detail_material FOREIGN KEY (material_id) REFERENCES inv_material(id) ON DELETE RESTRICT ON UPDATE CASCADE', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- sal_return.order_id → sal_order.id -SET @cnt = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'sal_return' AND CONSTRAINT_NAME = 'fk_sal_return_order'); -SET @sql = IF(@cnt = 0, 'ALTER TABLE sal_return ADD CONSTRAINT fk_sal_return_order FOREIGN KEY (order_id) REFERENCES sal_order(id) ON DELETE RESTRICT ON UPDATE CASCADE', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- sal_return_detail.return_id → sal_return.id (强父子,CASCADE) -SET @cnt = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'sal_return_detail' AND CONSTRAINT_NAME = 'fk_sal_return_detail_return'); -SET @sql = IF(@cnt = 0, 'ALTER TABLE sal_return_detail ADD CONSTRAINT fk_sal_return_detail_return FOREIGN KEY (return_id) REFERENCES sal_return(id) ON DELETE CASCADE ON UPDATE CASCADE', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- ======================================================== --- 二、库存模块外键(7 个) --- ======================================================== - --- inv_inventory.material_id → inv_material.id -SET @cnt = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_inventory' AND CONSTRAINT_NAME = 'fk_inv_inventory_material'); -SET @sql = IF(@cnt = 0, 'ALTER TABLE inv_inventory ADD CONSTRAINT fk_inv_inventory_material FOREIGN KEY (material_id) REFERENCES inv_material(id) ON DELETE RESTRICT ON UPDATE CASCADE', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- inv_inventory.warehouse_id → inv_warehouse.id -SET @cnt = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_inventory' AND CONSTRAINT_NAME = 'fk_inv_inventory_warehouse'); -SET @sql = IF(@cnt = 0, 'ALTER TABLE inv_inventory ADD CONSTRAINT fk_inv_inventory_warehouse FOREIGN KEY (warehouse_id) REFERENCES inv_warehouse(id) ON DELETE RESTRICT ON UPDATE CASCADE', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- inv_inventory_batch.material_id → inv_material.id -SET @cnt = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_inventory_batch' AND CONSTRAINT_NAME = 'fk_inv_inventory_batch_material'); -SET @sql = IF(@cnt = 0, 'ALTER TABLE inv_inventory_batch ADD CONSTRAINT fk_inv_inventory_batch_material FOREIGN KEY (material_id) REFERENCES inv_material(id) ON DELETE RESTRICT ON UPDATE CASCADE', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- inv_inventory_batch.warehouse_id → inv_warehouse.id (可空) -SET @cnt = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_inventory_batch' AND CONSTRAINT_NAME = 'fk_inv_inventory_batch_warehouse'); -SET @sql = IF(@cnt = 0, 'ALTER TABLE inv_inventory_batch ADD CONSTRAINT fk_inv_inventory_batch_warehouse FOREIGN KEY (warehouse_id) REFERENCES inv_warehouse(id) ON DELETE SET NULL ON UPDATE CASCADE', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- inv_outbound_order.warehouse_id → inv_warehouse.id -SET @cnt = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_outbound_order' AND CONSTRAINT_NAME = 'fk_inv_outbound_order_warehouse'); -SET @sql = IF(@cnt = 0, 'ALTER TABLE inv_outbound_order ADD CONSTRAINT fk_inv_outbound_order_warehouse FOREIGN KEY (warehouse_id) REFERENCES inv_warehouse(id) ON DELETE RESTRICT ON UPDATE CASCADE', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- inv_outbound_order.customer_id → crm_customer.id (可空,仅销售出库用) -SET @cnt = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_outbound_order' AND CONSTRAINT_NAME = 'fk_inv_outbound_order_customer'); -SET @sql = IF(@cnt = 0, 'ALTER TABLE inv_outbound_order ADD CONSTRAINT fk_inv_outbound_order_customer FOREIGN KEY (customer_id) REFERENCES crm_customer(id) ON DELETE SET NULL ON UPDATE CASCADE', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- inv_outbound_order.work_order_id → prd_work_order.id (可空,仅生产出库用) -SET @cnt = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_outbound_order' AND CONSTRAINT_NAME = 'fk_inv_outbound_order_workorder'); -SET @sql = IF(@cnt = 0, 'ALTER TABLE inv_outbound_order ADD CONSTRAINT fk_inv_outbound_order_workorder FOREIGN KEY (work_order_id) REFERENCES prd_work_order(id) ON DELETE SET NULL ON UPDATE CASCADE', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- ======================================================== --- 三、生产模块外键(3 个) --- ======================================================== - --- prd_work_order.sales_order_id → sal_order.id (可空) -SET @cnt = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'prd_work_order' AND CONSTRAINT_NAME = 'fk_prd_work_order_sales_order'); -SET @sql = IF(@cnt = 0, 'ALTER TABLE prd_work_order ADD CONSTRAINT fk_prd_work_order_sales_order FOREIGN KEY (sales_order_id) REFERENCES sal_order(id) ON DELETE SET NULL ON UPDATE CASCADE', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- prd_work_order.material_id → inv_material.id -SET @cnt = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'prd_work_order' AND CONSTRAINT_NAME = 'fk_prd_work_order_material'); -SET @sql = IF(@cnt = 0, 'ALTER TABLE prd_work_order ADD CONSTRAINT fk_prd_work_order_material FOREIGN KEY (material_id) REFERENCES inv_material(id) ON DELETE RESTRICT ON UPDATE CASCADE', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- prd_work_order_color_seq.work_order_id → prd_work_order.id (强父子,CASCADE) -SET @cnt = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'prd_work_order_color_seq' AND CONSTRAINT_NAME = 'fk_prd_wo_color_seq_workorder'); -SET @sql = IF(@cnt = 0, 'ALTER TABLE prd_work_order_color_seq ADD CONSTRAINT fk_prd_wo_color_seq_workorder FOREIGN KEY (work_order_id) REFERENCES prd_work_order(id) ON DELETE CASCADE ON UPDATE CASCADE', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- ======================================================== --- 四、财务模块外键(2 个) --- ======================================================== - --- fin_receivable.customer_id → crm_customer.id -SET @cnt = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'fin_receivable' AND CONSTRAINT_NAME = 'fk_fin_receivable_customer'); -SET @sql = IF(@cnt = 0, 'ALTER TABLE fin_receivable ADD CONSTRAINT fk_fin_receivable_customer FOREIGN KEY (customer_id) REFERENCES crm_customer(id) ON DELETE RESTRICT ON UPDATE CASCADE', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- fin_payable.supplier_id → pur_supplier.id -SET @cnt = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'fin_payable' AND CONSTRAINT_NAME = 'fk_fin_payable_supplier'); -SET @sql = IF(@cnt = 0, 'ALTER TABLE fin_payable ADD CONSTRAINT fk_fin_payable_supplier FOREIGN KEY (supplier_id) REFERENCES pur_supplier(id) ON DELETE RESTRICT ON UPDATE CASCADE', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- ======================================================== --- 五、外键添加后校验(输出当前 FK 总数) --- ======================================================== -SELECT CONCAT('外键总数: ', COUNT(*)) AS fk_summary -FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS -WHERE TABLE_SCHEMA = DATABASE() AND CONSTRAINT_TYPE = 'FOREIGN KEY'; diff --git a/database/migrations/018_fix_remaining_fks.sql b/database/migrations/018_fix_remaining_fks.sql deleted file mode 100644 index d5e308e6..00000000 --- a/database/migrations/018_fix_remaining_fks.sql +++ /dev/null @@ -1,77 +0,0 @@ --- ============================================================ --- Migration 018: 修复 Migration 017 遗留的 4 个 FK 问题 --- 日期: 2026-07-07 --- 背景: Migration 017 添加 12 个 FK 成功,6 个失败 --- 原因: --- 1. inv_inventory_batch.material_id 类型 INT UNSIGNED != inv_material.id BIGINT UNSIGNED --- 2. inv_inventory_batch.warehouse_id NOT NULL 不能用 SET NULL --- 3. sal_return / sal_return_detail 表实际名为 sal_return_order / sal_return_order_item --- 4. inv_outbound_order 缺少 customer_id 和 work_order_id 列 --- 策略: --- - 修复 inv_inventory_batch 列类型为 BIGINT UNSIGNED(与父表对齐) --- - 用 RESTRICT 替代 SET NULL 添加 inv_inventory_batch FK --- - 对 sal_return_order 和 sal_return_order_item 添加 FK --- - 跳过 inv_outbound_order 的 customer_id/work_order_id(列不存在,需先补列) --- ============================================================ - --- ======================================================== --- 一、修复 inv_inventory_batch 列类型不匹配 --- ======================================================== - --- 检查并修改 material_id 类型为 BIGINT UNSIGNED -SET @col_type = (SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_inventory_batch' AND COLUMN_NAME = 'material_id'); -SET @sql = IF(@col_type = 'int unsigned', 'ALTER TABLE inv_inventory_batch MODIFY COLUMN material_id BIGINT UNSIGNED NOT NULL COMMENT ''物料ID''', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 检查并修改 warehouse_id 类型为 BIGINT UNSIGNED(保持 NOT NULL) -SET @col_type = (SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_inventory_batch' AND COLUMN_NAME = 'warehouse_id'); -SET @sql = IF(@col_type = 'int unsigned', 'ALTER TABLE inv_inventory_batch MODIFY COLUMN warehouse_id BIGINT UNSIGNED NOT NULL COMMENT ''仓库ID''', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- ======================================================== --- 二、补加 inv_inventory_batch 的 FK(用 RESTRICT 因列为 NOT NULL) --- ======================================================== - --- inv_inventory_batch.material_id → inv_material.id -SET @cnt = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_inventory_batch' AND CONSTRAINT_NAME = 'fk_inv_inventory_batch_material'); -SET @sql = IF(@cnt = 0, 'ALTER TABLE inv_inventory_batch ADD CONSTRAINT fk_inv_inventory_batch_material FOREIGN KEY (material_id) REFERENCES inv_material(id) ON DELETE RESTRICT ON UPDATE CASCADE', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- inv_inventory_batch.warehouse_id → inv_warehouse.id (NOT NULL,用 RESTRICT) -SET @cnt = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_inventory_batch' AND CONSTRAINT_NAME = 'fk_inv_inventory_batch_warehouse'); -SET @sql = IF(@cnt = 0, 'ALTER TABLE inv_inventory_batch ADD CONSTRAINT fk_inv_inventory_batch_warehouse FOREIGN KEY (warehouse_id) REFERENCES inv_warehouse(id) ON DELETE RESTRICT ON UPDATE CASCADE', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- ======================================================== --- 三、对实际表 sal_return_order / sal_return_order_item 添加 FK --- 注意: schema.sql 写的是 sal_return / sal_return_detail, --- 但实际 DB 表名为 sal_return_order / sal_return_order_item --- 这是 P0-2 基线重置需解决的 schema 不一致问题 --- ======================================================== - --- 先检查 sal_return_order 表是否存在 -SET @tbl_exists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'sal_return_order'); -SET @sql = IF(@tbl_exists > 0, 'SELECT 1', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- sal_return_order.order_id → sal_order.id (如果表存在) -SET @cnt = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'sal_return_order' AND CONSTRAINT_NAME = 'fk_sal_return_order_order'); -SET @tbl_exists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'sal_return_order'); -SET @has_order_id = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'sal_return_order' AND COLUMN_NAME = 'order_id'); -SET @sql = IF(@cnt = 0 AND @tbl_exists > 0 AND @has_order_id > 0, 'ALTER TABLE sal_return_order ADD CONSTRAINT fk_sal_return_order_order FOREIGN KEY (order_id) REFERENCES sal_order(id) ON DELETE RESTRICT ON UPDATE CASCADE', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- sal_return_order_item.return_order_id → sal_return_order.id (如果表和列存在) --- 先探测正确的列名(return_id 或 return_order_id) -SET @col_name = (SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'sal_return_order_item' AND COLUMN_NAME IN ('return_order_id','return_id','order_id') LIMIT 1); -SET @cnt = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'sal_return_order_item' AND CONSTRAINT_NAME = 'fk_sal_return_order_item_return'); -SET @tbl_exists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'sal_return_order_item'); -SET @sql = IF(@cnt = 0 AND @tbl_exists > 0 AND @col_name IS NOT NULL, CONCAT('ALTER TABLE sal_return_order_item ADD CONSTRAINT fk_sal_return_order_item_return FOREIGN KEY (', @col_name, ') REFERENCES sal_return_order(id) ON DELETE CASCADE ON UPDATE CASCADE'), 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- ======================================================== --- 四、校验最终 FK 总数 --- ======================================================== -SELECT CONCAT('外键总数: ', COUNT(*)) AS fk_summary -FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS -WHERE TABLE_SCHEMA = DATABASE() AND CONSTRAINT_TYPE = 'FOREIGN KEY'; diff --git a/database/migrations/019_align_schema_for_concurrency_tests.sql b/database/migrations/019_align_schema_for_concurrency_tests.sql deleted file mode 100644 index 3f20b4a5..00000000 --- a/database/migrations/019_align_schema_for_concurrency_tests.sql +++ /dev/null @@ -1,134 +0,0 @@ --- ============================================================ --- Migration 019: 为并发测试补齐缺失的表和列 --- 日期: 2026-07-07 --- 目的: P0-2 基线重置的一部分,恢复 8 个被 skip 的 concurrency 测试 --- 内容: --- 1. 创建 inv_stocktaking 和 inv_stocktaking_item 表 --- 2. 为 inv_inventory 添加 batch_no 列 --- 3. 为 inv_outbound_order 添加 operator_id, version 列 --- 4. 为 inv_outbound_item 添加 deleted 列 --- 5. 为 inv_inbound_order 添加 operator_id, operator_name, warehouse_code, warehouse_name 列 --- 6. 为 inv_inbound_item 添加 deleted 列 --- 7. 为 prd_material_issue 添加 operator_id 列 --- 8. 为 prd_material_issue_item 添加 create_time 列 --- 幂等性: 所有 ALTER 使用 INFORMATION_SCHEMA 守卫 --- ======================================================== - --- ======================================================== --- 一、创建 inv_stocktaking 盘点单主表 --- ======================================================== -SET @tbl_exists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_stocktaking'); -SET @sql = IF(@tbl_exists = 0, 'CREATE TABLE inv_stocktaking ( - id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, - taking_no VARCHAR(50) NOT NULL COMMENT ''盘点单号'', - taking_type TINYINT DEFAULT 1 COMMENT ''盘点类型: 1-全盘, 2-部分盘'', - warehouse_id BIGINT UNSIGNED NOT NULL COMMENT ''仓库ID'', - status TINYINT DEFAULT 1 COMMENT ''状态: 1-待盘点, 2-盘点中, 3-待审批, 4-已审批, 9-已取消'', - taking_date DATE COMMENT ''盘点日期'', - operator_id BIGINT UNSIGNED COMMENT ''操作员ID'', - operator_name VARCHAR(50) COMMENT ''操作员姓名'', - remark VARCHAR(500) COMMENT ''备注'', - create_time DATETIME DEFAULT CURRENT_TIMESTAMP, - update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - deleted TINYINT DEFAULT 0, - PRIMARY KEY (id), - UNIQUE KEY uk_taking_no (taking_no), - KEY idx_warehouse (warehouse_id), - KEY idx_status (status) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT=''盘点单主表''', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- ======================================================== --- 二、创建 inv_stocktaking_item 盘点单明细表 --- ======================================================== -SET @tbl_exists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_stocktaking_item'); -SET @sql = IF(@tbl_exists = 0, 'CREATE TABLE inv_stocktaking_item ( - id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, - taking_id BIGINT UNSIGNED NOT NULL COMMENT ''盘点单ID'', - material_id BIGINT UNSIGNED NOT NULL COMMENT ''物料ID'', - material_code VARCHAR(50) COMMENT ''物料编码'', - material_name VARCHAR(100) COMMENT ''物料名称'', - system_qty DECIMAL(18,4) DEFAULT 0 COMMENT ''系统数量'', - actual_qty DECIMAL(18,4) DEFAULT 0 COMMENT ''实盘数量'', - diff_qty DECIMAL(18,4) DEFAULT 0 COMMENT ''差异数量'', - unit VARCHAR(20) COMMENT ''单位'', - batch_no VARCHAR(50) COMMENT ''批次号'', - remark VARCHAR(255) COMMENT ''备注'', - create_time DATETIME DEFAULT CURRENT_TIMESTAMP, - update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - PRIMARY KEY (id), - KEY idx_taking (taking_id), - KEY idx_material (material_id) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT=''盘点单明细表''', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- ======================================================== --- 三、为 inv_inventory 添加 batch_no 列 --- ======================================================== -SET @col_exists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_inventory' AND COLUMN_NAME = 'batch_no'); -SET @sql = IF(@col_exists = 0, 'ALTER TABLE inv_inventory ADD COLUMN batch_no VARCHAR(50) COMMENT ''批次号'' AFTER available_qty', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- ======================================================== --- 四、为 inv_outbound_order 添加缺失列 --- ======================================================== -SET @col_exists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_outbound_order' AND COLUMN_NAME = 'operator_id'); -SET @sql = IF(@col_exists = 0, 'ALTER TABLE inv_outbound_order ADD COLUMN operator_id BIGINT UNSIGNED COMMENT ''操作员ID'' AFTER operator_name', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @col_exists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_outbound_order' AND COLUMN_NAME = 'version'); -SET @sql = IF(@col_exists = 0, 'ALTER TABLE inv_outbound_order ADD COLUMN version INT DEFAULT 0 COMMENT ''乐观锁版本号'' AFTER deleted', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- ======================================================== --- 五、为 inv_outbound_item 添加 deleted 列 --- ======================================================== -SET @col_exists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_outbound_item' AND COLUMN_NAME = 'deleted'); -SET @sql = IF(@col_exists = 0, 'ALTER TABLE inv_outbound_item ADD COLUMN deleted TINYINT DEFAULT 0 COMMENT ''软删除'' AFTER create_time', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- ======================================================== --- 六、为 inv_inbound_order 添加缺失列 --- ======================================================== -SET @col_exists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_inbound_order' AND COLUMN_NAME = 'operator_id'); -SET @sql = IF(@col_exists = 0, 'ALTER TABLE inv_inbound_order ADD COLUMN operator_id BIGINT UNSIGNED COMMENT ''操作员ID'' AFTER supplier_name', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @col_exists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_inbound_order' AND COLUMN_NAME = 'operator_name'); -SET @sql = IF(@col_exists = 0, 'ALTER TABLE inv_inbound_order ADD COLUMN operator_name VARCHAR(50) COMMENT ''操作员姓名'' AFTER operator_id', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @col_exists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_inbound_order' AND COLUMN_NAME = 'warehouse_code'); -SET @sql = IF(@col_exists = 0, 'ALTER TABLE inv_inbound_order ADD COLUMN warehouse_code VARCHAR(50) COMMENT ''仓库编码'' AFTER warehouse_id', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @col_exists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_inbound_order' AND COLUMN_NAME = 'warehouse_name'); -SET @sql = IF(@col_exists = 0, 'ALTER TABLE inv_inbound_order ADD COLUMN warehouse_name VARCHAR(100) COMMENT ''仓库名称'' AFTER warehouse_code', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- ======================================================== --- 七、为 inv_inbound_item 添加 deleted 列 --- ======================================================== -SET @col_exists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_inbound_item' AND COLUMN_NAME = 'deleted'); -SET @sql = IF(@col_exists = 0, 'ALTER TABLE inv_inbound_item ADD COLUMN deleted TINYINT DEFAULT 0 COMMENT ''软删除'' AFTER create_time', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- ======================================================== --- 八、为 prd_material_issue 添加 operator_id 列 --- ======================================================== -SET @col_exists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'prd_material_issue' AND COLUMN_NAME = 'operator_id'); -SET @sql = IF(@col_exists = 0, 'ALTER TABLE prd_material_issue ADD COLUMN operator_id BIGINT UNSIGNED COMMENT ''操作员ID'' AFTER operator_name', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- ======================================================== --- 九、为 prd_material_issue_item 添加 create_time 列 --- ======================================================== -SET @col_exists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'prd_material_issue_item' AND COLUMN_NAME = 'create_time'); -SET @sql = IF(@col_exists = 0, 'ALTER TABLE prd_material_issue_item ADD COLUMN create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT ''创建时间''', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- ======================================================== --- 十、校验 --- ======================================================== -SELECT CONCAT('inv_stocktaking: ', COUNT(*)) AS summary -FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_stocktaking'; diff --git a/database/migrations/020_align_qc_unqualified_schema.sql b/database/migrations/020_align_qc_unqualified_schema.sql deleted file mode 100644 index fbfff673..00000000 --- a/database/migrations/020_align_qc_unqualified_schema.sql +++ /dev/null @@ -1,128 +0,0 @@ --- ============================================================ --- Migration 020: 对齐 qc_unqualified 表结构(吸收 qc_unqualified_handle 字段) --- 日期: 2026-07-07 --- 背景: --- schema.sql 定义了 qc_unqualified 表(status + handle_method) --- 但 init/supplement-tables/route.ts 运行时创建了 qc_unqualified_handle 表 --- (handle_status + handle_type + 7 个额外字段) --- API 路由使用 qc_unqualified_handle,spc-analysis.ts 使用 qc_unqualified --- 导致两表数据不互通 --- 策略: --- 1. 为 qc_unqualified 补齐 qc_unqualified_handle 的 7 个字段 --- 2. 重命名 status -> handle_status 对齐前端字段名 --- 3. 删除 handle_method,新增 handle_type(扩展为 4 种处置方式) --- 4. 合并 qc_unqualified_handle 数据到 qc_unqualified --- 5. DROP qc_unqualified_handle 消除幽灵表 --- ============================================================ - --- ======================================================== --- 一、为 qc_unqualified 补齐字段(幂等) --- ======================================================== - --- handle_no 处理单号 -SET @cnt = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'qc_unqualified' AND COLUMN_NAME = 'handle_no'); -SET @sql = IF(@cnt = 0, 'ALTER TABLE qc_unqualified ADD COLUMN handle_no VARCHAR(50) NULL COMMENT ''处理单号'' AFTER unqualified_no', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- material_code 物料编码 -SET @cnt = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'qc_unqualified' AND COLUMN_NAME = 'material_code'); -SET @sql = IF(@cnt = 0, 'ALTER TABLE qc_unqualified ADD COLUMN material_code VARCHAR(50) NULL COMMENT ''物料编码'' AFTER material_id', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- material_name 物料名称 -SET @cnt = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'qc_unqualified' AND COLUMN_NAME = 'material_name'); -SET @sql = IF(@cnt = 0, 'ALTER TABLE qc_unqualified ADD COLUMN material_name VARCHAR(100) NULL COMMENT ''物料名称'' AFTER material_code', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- handle_type 处置方式: 1-返工/2-报废/3-让步接收/4-退货 -SET @cnt = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'qc_unqualified' AND COLUMN_NAME = 'handle_type'); -SET @sql = IF(@cnt = 0, 'ALTER TABLE qc_unqualified ADD COLUMN handle_type TINYINT NULL COMMENT ''处置方式: 1-返工, 2-报废, 3-让步接收, 4-退货'' AFTER unqualified_reason', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- responsible_dept 责任部门 -SET @cnt = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'qc_unqualified' AND COLUMN_NAME = 'responsible_dept'); -SET @sql = IF(@cnt = 0, 'ALTER TABLE qc_unqualified ADD COLUMN responsible_dept VARCHAR(100) NULL COMMENT ''责任部门'' AFTER handle_type', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- responsible_person 责任人 -SET @cnt = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'qc_unqualified' AND COLUMN_NAME = 'responsible_person'); -SET @sql = IF(@cnt = 0, 'ALTER TABLE qc_unqualified ADD COLUMN responsible_person VARCHAR(50) NULL COMMENT ''责任人'' AFTER responsible_dept', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- cost_amount 损失金额 -SET @cnt = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'qc_unqualified' AND COLUMN_NAME = 'cost_amount'); -SET @sql = IF(@cnt = 0, 'ALTER TABLE qc_unqualified ADD COLUMN cost_amount DECIMAL(18,4) DEFAULT 0 COMMENT ''损失金额'' AFTER handle_result', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- remark 备注(qc_unqualified 原本只有 unqualified_reason,无 remark) -SET @cnt = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'qc_unqualified' AND COLUMN_NAME = 'remark'); -SET @sql = IF(@cnt = 0, 'ALTER TABLE qc_unqualified ADD COLUMN remark TEXT NULL COMMENT ''备注'' AFTER cost_amount', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- update_time -SET @cnt = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'qc_unqualified' AND COLUMN_NAME = 'update_time'); -SET @sql = IF(@cnt = 0, 'ALTER TABLE qc_unqualified ADD COLUMN update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT ''更新时间'' AFTER create_time', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- create_by -SET @cnt = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'qc_unqualified' AND COLUMN_NAME = 'create_by'); -SET @sql = IF(@cnt = 0, 'ALTER TABLE qc_unqualified ADD COLUMN create_by BIGINT UNSIGNED NULL COMMENT ''创建人ID'' AFTER update_time', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- deleted 软删除 -SET @cnt = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'qc_unqualified' AND COLUMN_NAME = 'deleted'); -SET @sql = IF(@cnt = 0, 'ALTER TABLE qc_unqualified ADD COLUMN deleted TINYINT NOT NULL DEFAULT 0 COMMENT ''软删除: 0-正常, 1-已删除'' AFTER create_by', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- ======================================================== --- 二、添加 handle_status 列(实际 DB 无 status 列,需直接 ADD) --- ======================================================== - -SET @cnt = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'qc_unqualified' AND COLUMN_NAME = 'handle_status'); -SET @sql = IF(@cnt = 0, 'ALTER TABLE qc_unqualified ADD COLUMN handle_status TINYINT NOT NULL DEFAULT 1 COMMENT ''处理状态: 1-待处理, 2-处理中, 3-已完成'' AFTER handle_type', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 同步更新 handle_type 注释(扩展为 4 种处置方式) -SET @col_exists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'qc_unqualified' AND COLUMN_NAME = 'handle_type'); -SET @sql = IF(@col_exists > 0, 'ALTER TABLE qc_unqualified MODIFY COLUMN handle_type TINYINT NULL COMMENT ''处置方式: 1-返工, 2-报废, 3-让步接收, 4-退货''', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- ======================================================== --- 三、删除废弃的 handle_method 列(被 handle_type 替代) --- ======================================================== - -SET @cnt = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'qc_unqualified' AND COLUMN_NAME = 'handle_method'); -SET @sql = IF(@cnt > 0, 'ALTER TABLE qc_unqualified DROP COLUMN handle_method', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- ======================================================== --- 四、添加 handle_no 唯一键(如果不存在) --- ======================================================== - -SET @cnt = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'qc_unqualified' AND INDEX_NAME = 'uk_handle_no'); -SET @sql = IF(@cnt = 0, 'ALTER TABLE qc_unqualified ADD UNIQUE KEY uk_handle_no (handle_no)', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- ======================================================== --- 五、合并 qc_unqualified_handle 数据到 qc_unqualified(如果表存在) --- ======================================================== - -SET @tbl_exists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'qc_unqualified_handle'); -SET @sql = IF(@tbl_exists > 0, 'INSERT IGNORE INTO qc_unqualified (handle_no, inspection_id, material_id, material_code, material_name, batch_no, unqualified_qty, unqualified_type, unqualified_reason, handle_type, responsible_dept, responsible_person, handle_result, cost_amount, remark, handle_status, handler_id, handle_date, create_time, update_time, create_by, deleted) SELECT handle_no, inspection_id, material_id, material_code, material_name, NULL, unqualified_qty, NULL, NULL, handle_type, responsible_dept, responsible_person, handle_result, cost_amount, remark, handle_status, NULL, NULL, create_time, update_time, create_by, deleted FROM qc_unqualified_handle WHERE handle_no IS NOT NULL', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- ======================================================== --- 六、删除幽灵表 qc_unqualified_handle --- ======================================================== - -SET @tbl_exists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'qc_unqualified_handle'); -SET @sql = IF(@tbl_exists > 0, 'DROP TABLE qc_unqualified_handle', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- ======================================================== --- 七、为 handle_status 添加索引(如果不存在) --- ======================================================== - -SET @cnt = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'qc_unqualified' AND INDEX_NAME = 'idx_handle_status'); -SET @sql = IF(@cnt = 0, 'ALTER TABLE qc_unqualified ADD KEY idx_handle_status (handle_status)', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; diff --git a/database/migrations/021_qc_unqualified_add_update_by_and_fk.sql b/database/migrations/021_qc_unqualified_add_update_by_and_fk.sql deleted file mode 100644 index 4f29aecb..00000000 --- a/database/migrations/021_qc_unqualified_add_update_by_and_fk.sql +++ /dev/null @@ -1,95 +0,0 @@ --- ============================================================ --- Migration 021: qc_unqualified 补齐 update_by 列 + 外键约束 --- --- 背景:根据《项目整体分析报告》P0 #2(外键约束缺失)和 P2 #7(审计字段命名不统一) --- 为 qc_unqualified 表补齐: --- 1. update_by BIGINT UNSIGNED 列(统一审计字段) --- 2. 修改 deleted 列为 NOT NULL DEFAULT 0(统一软删除 P2 #9) --- 3. 添加 material_id → inv_material.id 外键 --- 4. 添加 create_by → sys_user.id 外键 --- 5. 添加 update_by → sys_user.id 外键 --- 6. 添加 idx_material_id 和 idx_source 索引(高频查询字段,参见报告"高频查询字段必须索引"硬约束) --- --- 幂等模式:所有 ALTER 用 INFORMATION_SCHEMA 守卫 --- ============================================================ - --- 1. 补齐 update_by 列 -SET @col_exists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'qc_unqualified' AND COLUMN_NAME = 'update_by'); -SET @sql = IF(@col_exists = 0, - 'ALTER TABLE qc_unqualified ADD COLUMN update_by BIGINT UNSIGNED COMMENT ''更新人ID'' AFTER create_by', - 'SELECT ''update_by column already exists'' AS info'); -PREPARE stmt FROM @sql; -EXECUTE stmt; -DEALLOCATE PREPARE stmt; - --- 2. 统一 deleted 列定义为 NOT NULL DEFAULT 0(P2 #9 软删除字段统一) -SET @col_def = (SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'qc_unqualified' AND COLUMN_NAME = 'deleted'); -SET @sql = IF(@col_def IS NOT NULL AND @col_def NOT LIKE '%NOT NULL%', - 'ALTER TABLE qc_unqualified MODIFY COLUMN deleted TINYINT NOT NULL DEFAULT 0 COMMENT ''软删除: 0-正常, 1-已删除''', - 'SELECT ''deleted column already NOT NULL DEFAULT 0'' AS info'); -PREPARE stmt FROM @sql; -EXECUTE stmt; -DEALLOCATE PREPARE stmt; - --- 3. 添加 idx_material_id 索引(高频查询字段) -SET @idx_exists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'qc_unqualified' AND INDEX_NAME = 'idx_material_id'); -SET @sql = IF(@idx_exists = 0, - 'ALTER TABLE qc_unqualified ADD INDEX idx_material_id (material_id)', - 'SELECT ''idx_material_id already exists'' AS info'); -PREPARE stmt FROM @sql; -EXECUTE stmt; -DEALLOCATE PREPARE stmt; - --- 4. 添加 idx_source 复合索引(source_type + source_no 高频查询) -SET @idx_exists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'qc_unqualified' AND INDEX_NAME = 'idx_source'); -SET @sql = IF(@idx_exists = 0, - 'ALTER TABLE qc_unqualified ADD INDEX idx_source (source_type, source_no)', - 'SELECT ''idx_source already exists'' AS info'); -PREPARE stmt FROM @sql; -EXECUTE stmt; -DEALLOCATE PREPARE stmt; - --- 5. 添加 fk_qc_unqualified_material 外键(material_id → inv_material.id) -SET @fk_exists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'qc_unqualified' AND CONSTRAINT_NAME = 'fk_qc_unqualified_material'); -SET @sql = IF(@fk_exists = 0, - 'ALTER TABLE qc_unqualified ADD CONSTRAINT fk_qc_unqualified_material FOREIGN KEY (material_id) REFERENCES inv_material (id) ON DELETE SET NULL ON UPDATE CASCADE', - 'SELECT ''fk_qc_unqualified_material already exists'' AS info'); -PREPARE stmt FROM @sql; -EXECUTE stmt; -DEALLOCATE PREPARE stmt; - --- 6. 添加 fk_qc_unqualified_create_by 外键(create_by → sys_user.id) -SET @fk_exists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'qc_unqualified' AND CONSTRAINT_NAME = 'fk_qc_unqualified_create_by'); -SET @sql = IF(@fk_exists = 0, - 'ALTER TABLE qc_unqualified ADD CONSTRAINT fk_qc_unqualified_create_by FOREIGN KEY (create_by) REFERENCES sys_user (id) ON DELETE SET NULL ON UPDATE CASCADE', - 'SELECT ''fk_qc_unqualified_create_by already exists'' AS info'); -PREPARE stmt FROM @sql; -EXECUTE stmt; -DEALLOCATE PREPARE stmt; - --- 7. 添加 fk_qc_unqualified_update_by 外键(update_by → sys_user.id) -SET @fk_exists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'qc_unqualified' AND CONSTRAINT_NAME = 'fk_qc_unqualified_update_by'); -SET @sql = IF(@fk_exists = 0, - 'ALTER TABLE qc_unqualified ADD CONSTRAINT fk_qc_unqualified_update_by FOREIGN KEY (update_by) REFERENCES sys_user (id) ON DELETE SET NULL ON UPDATE CASCADE', - 'SELECT ''fk_qc_unqualified_update_by already exists'' AS info'); -PREPARE stmt FROM @sql; -EXECUTE stmt; -DEALLOCATE PREPARE stmt; - --- 8. 验证最终表结构 -SELECT - COLUMN_NAME, - COLUMN_TYPE, - IS_NULLABLE, - COLUMN_DEFAULT, - COLUMN_COMMENT -FROM INFORMATION_SCHEMA.COLUMNS -WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'qc_unqualified' -ORDER BY ORDINAL_POSITION; diff --git a/database/migrations/022_unify_collation.sql b/database/migrations/022_unify_collation.sql deleted file mode 100644 index 7683fbbc..00000000 --- a/database/migrations/022_unify_collation.sql +++ /dev/null @@ -1,187 +0,0 @@ --- ============================================================ --- Migration 022: 统一所有表 Collation 为 utf8mb4_0900_ai_ci --- --- 背景:根据《项目整体分析报告》P1 #2(Collation 不统一) --- 21 张表使用非标准 Collation(utf8mb4_unicode_ci 或 utf8mb4_ai_ci) --- 统一为 utf8mb4_0900_ai_ci 以支持越南语等特殊字符排序 --- --- 幂等模式:所有 ALTER 用 INFORMATION_SCHEMA 守卫 --- ============================================================ - --- 工具宏:对单张表执行 Collation 统一(幂等) --- 用法:每次设置 @tbl_name 后调用此块 - --- 1. inv_inventory_log (utf8mb4_ai_ci → utf8mb4_0900_ai_ci) -SET @curr = (SELECT TABLE_COLLATION FROM INFORMATION_SCHEMA.TABLES - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_inventory_log'); -SET @sql = IF(@curr IS NOT NULL AND @curr != 'utf8mb4_0900_ai_ci', - 'ALTER TABLE inv_inventory_log CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci', - 'SELECT ''inv_inventory_log collation already utf8mb4_0900_ai_ci'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 2. eqp_equipment -SET @curr = (SELECT TABLE_COLLATION FROM INFORMATION_SCHEMA.TABLES - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'eqp_equipment'); -SET @sql = IF(@curr IS NOT NULL AND @curr != 'utf8mb4_0900_ai_ci', - 'ALTER TABLE eqp_equipment CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci', - 'SELECT ''eqp_equipment collation already utf8mb4_0900_ai_ci'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 3. prd_schedule -SET @curr = (SELECT TABLE_COLLATION FROM INFORMATION_SCHEMA.TABLES - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'prd_schedule'); -SET @sql = IF(@curr IS NOT NULL AND @curr != 'utf8mb4_0900_ai_ci', - 'ALTER TABLE prd_schedule CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci', - 'SELECT ''prd_schedule collation already utf8mb4_0900_ai_ci'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 4. prd_schedule_detail -SET @curr = (SELECT TABLE_COLLATION FROM INFORMATION_SCHEMA.TABLES - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'prd_schedule_detail'); -SET @sql = IF(@curr IS NOT NULL AND @curr != 'utf8mb4_0900_ai_ci', - 'ALTER TABLE prd_schedule_detail CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci', - 'SELECT ''prd_schedule_detail collation already utf8mb4_0900_ai_ci'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 5. prd_work_order_color_seq -SET @curr = (SELECT TABLE_COLLATION FROM INFORMATION_SCHEMA.TABLES - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'prd_work_order_color_seq'); -SET @sql = IF(@curr IS NOT NULL AND @curr != 'utf8mb4_0900_ai_ci', - 'ALTER TABLE prd_work_order_color_seq CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci', - 'SELECT ''prd_work_order_color_seq collation already utf8mb4_0900_ai_ci'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 6. material_requisitions -SET @curr = (SELECT TABLE_COLLATION FROM INFORMATION_SCHEMA.TABLES - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'material_requisitions'); -SET @sql = IF(@curr IS NOT NULL AND @curr != 'utf8mb4_0900_ai_ci', - 'ALTER TABLE material_requisitions CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci', - 'SELECT ''material_requisitions collation already utf8mb4_0900_ai_ci'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 7. material_requisition_items -SET @curr = (SELECT TABLE_COLLATION FROM INFORMATION_SCHEMA.TABLES - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'material_requisition_items'); -SET @sql = IF(@curr IS NOT NULL AND @curr != 'utf8mb4_0900_ai_ci', - 'ALTER TABLE material_requisition_items CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci', - 'SELECT ''material_requisition_items collation already utf8mb4_0900_ai_ci'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 8. material_returns -SET @curr = (SELECT TABLE_COLLATION FROM INFORMATION_SCHEMA.TABLES - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'material_returns'); -SET @sql = IF(@curr IS NOT NULL AND @curr != 'utf8mb4_0900_ai_ci', - 'ALTER TABLE material_returns CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci', - 'SELECT ''material_returns collation already utf8mb4_0900_ai_ci'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 9. material_return_items -SET @curr = (SELECT TABLE_COLLATION FROM INFORMATION_SCHEMA.TABLES - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'material_return_items'); -SET @sql = IF(@curr IS NOT NULL AND @curr != 'utf8mb4_0900_ai_ci', - 'ALTER TABLE material_return_items CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci', - 'SELECT ''material_return_items collation already utf8mb4_0900_ai_ci'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 10. inv_fifo_override_log -SET @curr = (SELECT TABLE_COLLATION FROM INFORMATION_SCHEMA.TABLES - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_fifo_override_log'); -SET @sql = IF(@curr IS NOT NULL AND @curr != 'utf8mb4_0900_ai_ci', - 'ALTER TABLE inv_fifo_override_log CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci', - 'SELECT ''inv_fifo_override_log collation already utf8mb4_0900_ai_ci'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 11. work_order_costs -SET @curr = (SELECT TABLE_COLLATION FROM INFORMATION_SCHEMA.TABLES - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'work_order_costs'); -SET @sql = IF(@curr IS NOT NULL AND @curr != 'utf8mb4_0900_ai_ci', - 'ALTER TABLE work_order_costs CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci', - 'SELECT ''work_order_costs collation already utf8mb4_0900_ai_ci'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 12. material_batch_costs -SET @curr = (SELECT TABLE_COLLATION FROM INFORMATION_SCHEMA.TABLES - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'material_batch_costs'); -SET @sql = IF(@curr IS NOT NULL AND @curr != 'utf8mb4_0900_ai_ci', - 'ALTER TABLE material_batch_costs CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci', - 'SELECT ''material_batch_costs collation already utf8mb4_0900_ai_ci'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 13. inv_inventory_batch -SET @curr = (SELECT TABLE_COLLATION FROM INFORMATION_SCHEMA.TABLES - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_inventory_batch'); -SET @sql = IF(@curr IS NOT NULL AND @curr != 'utf8mb4_0900_ai_ci', - 'ALTER TABLE inv_inventory_batch CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci', - 'SELECT ''inv_inventory_batch collation already utf8mb4_0900_ai_ci'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 14. inv_material_std -SET @curr = (SELECT TABLE_COLLATION FROM INFORMATION_SCHEMA.TABLES - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_material_std'); -SET @sql = IF(@curr IS NOT NULL AND @curr != 'utf8mb4_0900_ai_ci', - 'ALTER TABLE inv_material_std CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci', - 'SELECT ''inv_material_std collation already utf8mb4_0900_ai_ci'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 15. prd_bom_std -SET @curr = (SELECT TABLE_COLLATION FROM INFORMATION_SCHEMA.TABLES - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'prd_bom_std'); -SET @sql = IF(@curr IS NOT NULL AND @curr != 'utf8mb4_0900_ai_ci', - 'ALTER TABLE prd_bom_std CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci', - 'SELECT ''prd_bom_std collation already utf8mb4_0900_ai_ci'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 16. prd_bom_line_std -SET @curr = (SELECT TABLE_COLLATION FROM INFORMATION_SCHEMA.TABLES - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'prd_bom_line_std'); -SET @sql = IF(@curr IS NOT NULL AND @curr != 'utf8mb4_0900_ai_ci', - 'ALTER TABLE prd_bom_line_std CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci', - 'SELECT ''prd_bom_line_std collation already utf8mb4_0900_ai_ci'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 17. fin_account -SET @curr = (SELECT TABLE_COLLATION FROM INFORMATION_SCHEMA.TABLES - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'fin_account'); -SET @sql = IF(@curr IS NOT NULL AND @curr != 'utf8mb4_0900_ai_ci', - 'ALTER TABLE fin_account CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci', - 'SELECT ''fin_account collation already utf8mb4_0900_ai_ci'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 18. fin_period -SET @curr = (SELECT TABLE_COLLATION FROM INFORMATION_SCHEMA.TABLES - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'fin_period'); -SET @sql = IF(@curr IS NOT NULL AND @curr != 'utf8mb4_0900_ai_ci', - 'ALTER TABLE fin_period CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci', - 'SELECT ''fin_period collation already utf8mb4_0900_ai_ci'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 19. fin_voucher -SET @curr = (SELECT TABLE_COLLATION FROM INFORMATION_SCHEMA.TABLES - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'fin_voucher'); -SET @sql = IF(@curr IS NOT NULL AND @curr != 'utf8mb4_0900_ai_ci', - 'ALTER TABLE fin_voucher CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci', - 'SELECT ''fin_voucher collation already utf8mb4_0900_ai_ci'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 20. fin_voucher_line -SET @curr = (SELECT TABLE_COLLATION FROM INFORMATION_SCHEMA.TABLES - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'fin_voucher_line'); -SET @sql = IF(@curr IS NOT NULL AND @curr != 'utf8mb4_0900_ai_ci', - 'ALTER TABLE fin_voucher_line CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci', - 'SELECT ''fin_voucher_line collation already utf8mb4_0900_ai_ci'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 21. fin_account_balance -SET @curr = (SELECT TABLE_COLLATION FROM INFORMATION_SCHEMA.TABLES - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'fin_account_balance'); -SET @sql = IF(@curr IS NOT NULL AND @curr != 'utf8mb4_0900_ai_ci', - 'ALTER TABLE fin_account_balance CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci', - 'SELECT ''fin_account_balance collation already utf8mb4_0900_ai_ci'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 验证:检查是否仍有非 utf8mb4_0900_ai_ci 的表 -SELECT TABLE_NAME, TABLE_COLLATION -FROM INFORMATION_SCHEMA.TABLES -WHERE TABLE_SCHEMA = DATABASE() - AND TABLE_COLLATION != 'utf8mb4_0900_ai_ci' -ORDER BY TABLE_NAME; diff --git a/database/migrations/023_unify_soft_delete.sql b/database/migrations/023_unify_soft_delete.sql deleted file mode 100644 index 4ce8daea..00000000 --- a/database/migrations/023_unify_soft_delete.sql +++ /dev/null @@ -1,254 +0,0 @@ --- ============================================================ --- Migration 023: 统一软删除字段为 deleted TINYINT NOT NULL DEFAULT 0 --- --- 背景:根据《项目整体分析报告》P2 #9(软删除字段不统一) --- 约 43 张表的 deleted 字段定义不一致: --- - TINYINT(1) 变体(2 张) --- - 缺 NOT NULL(约 35 张) --- - COMMENT 不标准(7 张) --- 统一为:deleted TINYINT NOT NULL DEFAULT 0 COMMENT '软删除: 0-正常, 1-已删除' --- --- 幂等模式:所有 ALTER 用 INFORMATION_SCHEMA 守卫 --- 守卫条件:IS_NULLABLE='NO' AND COLUMN_TYPE='tinyint' AND COLUMN_DEFAULT='0' --- ============================================================ - --- A. TINYINT(1) 变体 + B. 缺 NOT NULL + C. COMMENT 不标准 --- 所有变体用相同 MODIFY COLUMN 语句处理 - --- 1. sys_user -SET @curr = (SELECT CONCAT(IS_NULLABLE,'|',COLUMN_TYPE,'|',IFNULL(COLUMN_DEFAULT,'')) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='sys_user' AND COLUMN_NAME='deleted'); -SET @sql = IF(@curr != 'NO|tinyint|0', 'ALTER TABLE sys_user MODIFY COLUMN deleted TINYINT NOT NULL DEFAULT 0 COMMENT ''软删除: 0-正常, 1-已删除''', 'SELECT ''sys_user.deleted already standard'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 2. sys_department -SET @curr = (SELECT CONCAT(IS_NULLABLE,'|',COLUMN_TYPE,'|',IFNULL(COLUMN_DEFAULT,'')) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='sys_department' AND COLUMN_NAME='deleted'); -SET @sql = IF(@curr != 'NO|tinyint|0', 'ALTER TABLE sys_department MODIFY COLUMN deleted TINYINT NOT NULL DEFAULT 0 COMMENT ''软删除: 0-正常, 1-已删除''', 'SELECT ''sys_department.deleted already standard'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 3. sys_role -SET @curr = (SELECT CONCAT(IS_NULLABLE,'|',COLUMN_TYPE,'|',IFNULL(COLUMN_DEFAULT,'')) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='sys_role' AND COLUMN_NAME='deleted'); -SET @sql = IF(@curr != 'NO|tinyint|0', 'ALTER TABLE sys_role MODIFY COLUMN deleted TINYINT NOT NULL DEFAULT 0 COMMENT ''软删除: 0-正常, 1-已删除''', 'SELECT ''sys_role.deleted already standard'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 4. crm_customer -SET @curr = (SELECT CONCAT(IS_NULLABLE,'|',COLUMN_TYPE,'|',IFNULL(COLUMN_DEFAULT,'')) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='crm_customer' AND COLUMN_NAME='deleted'); -SET @sql = IF(@curr != 'NO|tinyint|0', 'ALTER TABLE crm_customer MODIFY COLUMN deleted TINYINT NOT NULL DEFAULT 0 COMMENT ''软删除: 0-正常, 1-已删除''', 'SELECT ''crm_customer.deleted already standard'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 5. crm_customer_contact -SET @curr = (SELECT CONCAT(IS_NULLABLE,'|',COLUMN_TYPE,'|',IFNULL(COLUMN_DEFAULT,'')) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='crm_customer_contact' AND COLUMN_NAME='deleted'); -SET @sql = IF(@curr != 'NO|tinyint|0', 'ALTER TABLE crm_customer_contact MODIFY COLUMN deleted TINYINT NOT NULL DEFAULT 0 COMMENT ''软删除: 0-正常, 1-已删除''', 'SELECT ''crm_customer_contact.deleted already standard'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 6. pur_supplier -SET @curr = (SELECT CONCAT(IS_NULLABLE,'|',COLUMN_TYPE,'|',IFNULL(COLUMN_DEFAULT,'')) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='pur_supplier' AND COLUMN_NAME='deleted'); -SET @sql = IF(@curr != 'NO|tinyint|0', 'ALTER TABLE pur_supplier MODIFY COLUMN deleted TINYINT NOT NULL DEFAULT 0 COMMENT ''软删除: 0-正常, 1-已删除''', 'SELECT ''pur_supplier.deleted already standard'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 7. inv_material_category -SET @curr = (SELECT CONCAT(IS_NULLABLE,'|',COLUMN_TYPE,'|',IFNULL(COLUMN_DEFAULT,'')) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='inv_material_category' AND COLUMN_NAME='deleted'); -SET @sql = IF(@curr != 'NO|tinyint|0', 'ALTER TABLE inv_material_category MODIFY COLUMN deleted TINYINT NOT NULL DEFAULT 0 COMMENT ''软删除: 0-正常, 1-已删除''', 'SELECT ''inv_material_category.deleted already standard'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 8. inv_material -SET @curr = (SELECT CONCAT(IS_NULLABLE,'|',COLUMN_TYPE,'|',IFNULL(COLUMN_DEFAULT,'')) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='inv_material' AND COLUMN_NAME='deleted'); -SET @sql = IF(@curr != 'NO|tinyint|0', 'ALTER TABLE inv_material MODIFY COLUMN deleted TINYINT NOT NULL DEFAULT 0 COMMENT ''软删除: 0-正常, 1-已删除''', 'SELECT ''inv_material.deleted already standard'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 9. inv_warehouse -SET @curr = (SELECT CONCAT(IS_NULLABLE,'|',COLUMN_TYPE,'|',IFNULL(COLUMN_DEFAULT,'')) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='inv_warehouse' AND COLUMN_NAME='deleted'); -SET @sql = IF(@curr != 'NO|tinyint|0', 'ALTER TABLE inv_warehouse MODIFY COLUMN deleted TINYINT NOT NULL DEFAULT 0 COMMENT ''软删除: 0-正常, 1-已删除''', 'SELECT ''inv_warehouse.deleted already standard'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 10. inv_inventory -SET @curr = (SELECT CONCAT(IS_NULLABLE,'|',COLUMN_TYPE,'|',IFNULL(COLUMN_DEFAULT,'')) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='inv_inventory' AND COLUMN_NAME='deleted'); -SET @sql = IF(@curr != 'NO|tinyint|0', 'ALTER TABLE inv_inventory MODIFY COLUMN deleted TINYINT NOT NULL DEFAULT 0 COMMENT ''软删除: 0-正常, 1-已删除''', 'SELECT ''inv_inventory.deleted already standard'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 11. pur_request -SET @curr = (SELECT CONCAT(IS_NULLABLE,'|',COLUMN_TYPE,'|',IFNULL(COLUMN_DEFAULT,'')) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='pur_request' AND COLUMN_NAME='deleted'); -SET @sql = IF(@curr != 'NO|tinyint|0', 'ALTER TABLE pur_request MODIFY COLUMN deleted TINYINT NOT NULL DEFAULT 0 COMMENT ''软删除: 0-正常, 1-已删除''', 'SELECT ''pur_request.deleted already standard'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 12. pur_purchase_order (TINYINT(1) 变体) -SET @curr = (SELECT CONCAT(IS_NULLABLE,'|',COLUMN_TYPE,'|',IFNULL(COLUMN_DEFAULT,'')) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='pur_purchase_order' AND COLUMN_NAME='deleted'); -SET @sql = IF(@curr != 'NO|tinyint|0', 'ALTER TABLE pur_purchase_order MODIFY COLUMN deleted TINYINT NOT NULL DEFAULT 0 COMMENT ''软删除: 0-正常, 1-已删除''', 'SELECT ''pur_purchase_order.deleted already standard'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 13. sal_order -SET @curr = (SELECT CONCAT(IS_NULLABLE,'|',COLUMN_TYPE,'|',IFNULL(COLUMN_DEFAULT,'')) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='sal_order' AND COLUMN_NAME='deleted'); -SET @sql = IF(@curr != 'NO|tinyint|0', 'ALTER TABLE sal_order MODIFY COLUMN deleted TINYINT NOT NULL DEFAULT 0 COMMENT ''软删除: 0-正常, 1-已删除''', 'SELECT ''sal_order.deleted already standard'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 14. sal_order_detail -SET @curr = (SELECT CONCAT(IS_NULLABLE,'|',COLUMN_TYPE,'|',IFNULL(COLUMN_DEFAULT,'')) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='sal_order_detail' AND COLUMN_NAME='deleted'); -SET @sql = IF(@curr != 'NO|tinyint|0', 'ALTER TABLE sal_order_detail MODIFY COLUMN deleted TINYINT NOT NULL DEFAULT 0 COMMENT ''软删除: 0-正常, 1-已删除''', 'SELECT ''sal_order_detail.deleted already standard'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 15. sal_delivery -SET @curr = (SELECT CONCAT(IS_NULLABLE,'|',COLUMN_TYPE,'|',IFNULL(COLUMN_DEFAULT,'')) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='sal_delivery' AND COLUMN_NAME='deleted'); -SET @sql = IF(@curr != 'NO|tinyint|0', 'ALTER TABLE sal_delivery MODIFY COLUMN deleted TINYINT NOT NULL DEFAULT 0 COMMENT ''软删除: 0-正常, 1-已删除''', 'SELECT ''sal_delivery.deleted already standard'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 16. sal_delivery_detail -SET @curr = (SELECT CONCAT(IS_NULLABLE,'|',COLUMN_TYPE,'|',IFNULL(COLUMN_DEFAULT,'')) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='sal_delivery_detail' AND COLUMN_NAME='deleted'); -SET @sql = IF(@curr != 'NO|tinyint|0', 'ALTER TABLE sal_delivery_detail MODIFY COLUMN deleted TINYINT NOT NULL DEFAULT 0 COMMENT ''软删除: 0-正常, 1-已删除''', 'SELECT ''sal_delivery_detail.deleted already standard'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 17. sal_return -SET @curr = (SELECT CONCAT(IS_NULLABLE,'|',COLUMN_TYPE,'|',IFNULL(COLUMN_DEFAULT,'')) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='sal_return' AND COLUMN_NAME='deleted'); -SET @sql = IF(@curr != 'NO|tinyint|0', 'ALTER TABLE sal_return MODIFY COLUMN deleted TINYINT NOT NULL DEFAULT 0 COMMENT ''软删除: 0-正常, 1-已删除''', 'SELECT ''sal_return.deleted already standard'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 18. sal_return_detail -SET @curr = (SELECT CONCAT(IS_NULLABLE,'|',COLUMN_TYPE,'|',IFNULL(COLUMN_DEFAULT,'')) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='sal_return_detail' AND COLUMN_NAME='deleted'); -SET @sql = IF(@curr != 'NO|tinyint|0', 'ALTER TABLE sal_return_detail MODIFY COLUMN deleted TINYINT NOT NULL DEFAULT 0 COMMENT ''软删除: 0-正常, 1-已删除''', 'SELECT ''sal_return_detail.deleted already standard'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 19. sal_reconciliation -SET @curr = (SELECT CONCAT(IS_NULLABLE,'|',COLUMN_TYPE,'|',IFNULL(COLUMN_DEFAULT,'')) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='sal_reconciliation' AND COLUMN_NAME='deleted'); -SET @sql = IF(@curr != 'NO|tinyint|0', 'ALTER TABLE sal_reconciliation MODIFY COLUMN deleted TINYINT NOT NULL DEFAULT 0 COMMENT ''软删除: 0-正常, 1-已删除''', 'SELECT ''sal_reconciliation.deleted already standard'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 20. sal_reconciliation_line -SET @curr = (SELECT CONCAT(IS_NULLABLE,'|',COLUMN_TYPE,'|',IFNULL(COLUMN_DEFAULT,'')) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='sal_reconciliation_line' AND COLUMN_NAME='deleted'); -SET @sql = IF(@curr != 'NO|tinyint|0', 'ALTER TABLE sal_reconciliation_line MODIFY COLUMN deleted TINYINT NOT NULL DEFAULT 0 COMMENT ''软删除: 0-正常, 1-已删除''', 'SELECT ''sal_reconciliation_line.deleted already standard'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 21. sal_reconciliation_writeoff -SET @curr = (SELECT CONCAT(IS_NULLABLE,'|',COLUMN_TYPE,'|',IFNULL(COLUMN_DEFAULT,'')) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='sal_reconciliation_writeoff' AND COLUMN_NAME='deleted'); -SET @sql = IF(@curr != 'NO|tinyint|0', 'ALTER TABLE sal_reconciliation_writeoff MODIFY COLUMN deleted TINYINT NOT NULL DEFAULT 0 COMMENT ''软删除: 0-正常, 1-已删除''', 'SELECT ''sal_reconciliation_writeoff.deleted already standard'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 22. prd_standard_card -SET @curr = (SELECT CONCAT(IS_NULLABLE,'|',COLUMN_TYPE,'|',IFNULL(COLUMN_DEFAULT,'')) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='prd_standard_card' AND COLUMN_NAME='deleted'); -SET @sql = IF(@curr != 'NO|tinyint|0', 'ALTER TABLE prd_standard_card MODIFY COLUMN deleted TINYINT NOT NULL DEFAULT 0 COMMENT ''软删除: 0-正常, 1-已删除''', 'SELECT ''prd_standard_card.deleted already standard'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 23. qc_inspection -SET @curr = (SELECT CONCAT(IS_NULLABLE,'|',COLUMN_TYPE,'|',IFNULL(COLUMN_DEFAULT,'')) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='qc_inspection' AND COLUMN_NAME='deleted'); -SET @sql = IF(@curr != 'NO|tinyint|0', 'ALTER TABLE qc_inspection MODIFY COLUMN deleted TINYINT NOT NULL DEFAULT 0 COMMENT ''软删除: 0-正常, 1-已删除''', 'SELECT ''qc_inspection.deleted already standard'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 24. eqp_equipment -SET @curr = (SELECT CONCAT(IS_NULLABLE,'|',COLUMN_TYPE,'|',IFNULL(COLUMN_DEFAULT,'')) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='eqp_equipment' AND COLUMN_NAME='deleted'); -SET @sql = IF(@curr != 'NO|tinyint|0', 'ALTER TABLE eqp_equipment MODIFY COLUMN deleted TINYINT NOT NULL DEFAULT 0 COMMENT ''软删除: 0-正常, 1-已删除''', 'SELECT ''eqp_equipment.deleted already standard'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 25. prd_schedule -SET @curr = (SELECT CONCAT(IS_NULLABLE,'|',COLUMN_TYPE,'|',IFNULL(COLUMN_DEFAULT,'')) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='prd_schedule' AND COLUMN_NAME='deleted'); -SET @sql = IF(@curr != 'NO|tinyint|0', 'ALTER TABLE prd_schedule MODIFY COLUMN deleted TINYINT NOT NULL DEFAULT 0 COMMENT ''软删除: 0-正常, 1-已删除''', 'SELECT ''prd_schedule.deleted already standard'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 26. material_requisitions -SET @curr = (SELECT CONCAT(IS_NULLABLE,'|',COLUMN_TYPE,'|',IFNULL(COLUMN_DEFAULT,'')) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='material_requisitions' AND COLUMN_NAME='deleted'); -SET @sql = IF(@curr != 'NO|tinyint|0', 'ALTER TABLE material_requisitions MODIFY COLUMN deleted TINYINT NOT NULL DEFAULT 0 COMMENT ''软删除: 0-正常, 1-已删除''', 'SELECT ''material_requisitions.deleted already standard'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 27. material_returns -SET @curr = (SELECT CONCAT(IS_NULLABLE,'|',COLUMN_TYPE,'|',IFNULL(COLUMN_DEFAULT,'')) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='material_returns' AND COLUMN_NAME='deleted'); -SET @sql = IF(@curr != 'NO|tinyint|0', 'ALTER TABLE material_returns MODIFY COLUMN deleted TINYINT NOT NULL DEFAULT 0 COMMENT ''软删除: 0-正常, 1-已删除''', 'SELECT ''material_returns.deleted already standard'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 28. inv_inventory_batch -SET @curr = (SELECT CONCAT(IS_NULLABLE,'|',COLUMN_TYPE,'|',IFNULL(COLUMN_DEFAULT,'')) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='inv_inventory_batch' AND COLUMN_NAME='deleted'); -SET @sql = IF(@curr != 'NO|tinyint|0', 'ALTER TABLE inv_inventory_batch MODIFY COLUMN deleted TINYINT NOT NULL DEFAULT 0 COMMENT ''软删除: 0-正常, 1-已删除''', 'SELECT ''inv_inventory_batch.deleted already standard'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 29. fin_voucher -SET @curr = (SELECT CONCAT(IS_NULLABLE,'|',COLUMN_TYPE,'|',IFNULL(COLUMN_DEFAULT,'')) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='fin_voucher' AND COLUMN_NAME='deleted'); -SET @sql = IF(@curr != 'NO|tinyint|0', 'ALTER TABLE fin_voucher MODIFY COLUMN deleted TINYINT NOT NULL DEFAULT 0 COMMENT ''软删除: 0-正常, 1-已删除''', 'SELECT ''fin_voucher.deleted already standard'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 30. inv_material_std -SET @curr = (SELECT CONCAT(IS_NULLABLE,'|',COLUMN_TYPE,'|',IFNULL(COLUMN_DEFAULT,'')) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='inv_material_std' AND COLUMN_NAME='deleted'); -SET @sql = IF(@curr != 'NO|tinyint|0', 'ALTER TABLE inv_material_std MODIFY COLUMN deleted TINYINT NOT NULL DEFAULT 0 COMMENT ''软删除: 0-正常, 1-已删除''', 'SELECT ''inv_material_std.deleted already standard'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 31. prd_bom_std -SET @curr = (SELECT CONCAT(IS_NULLABLE,'|',COLUMN_TYPE,'|',IFNULL(COLUMN_DEFAULT,'')) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='prd_bom_std' AND COLUMN_NAME='deleted'); -SET @sql = IF(@curr != 'NO|tinyint|0', 'ALTER TABLE prd_bom_std MODIFY COLUMN deleted TINYINT NOT NULL DEFAULT 0 COMMENT ''软删除: 0-正常, 1-已删除''', 'SELECT ''prd_bom_std.deleted already standard'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 32. prd_bom_line_std -SET @curr = (SELECT CONCAT(IS_NULLABLE,'|',COLUMN_TYPE,'|',IFNULL(COLUMN_DEFAULT,'')) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='prd_bom_line_std' AND COLUMN_NAME='deleted'); -SET @sql = IF(@curr != 'NO|tinyint|0', 'ALTER TABLE prd_bom_line_std MODIFY COLUMN deleted TINYINT NOT NULL DEFAULT 0 COMMENT ''软删除: 0-正常, 1-已删除''', 'SELECT ''prd_bom_line_std.deleted already standard'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 33. inv_inbound_order (TINYINT(1) 变体) -SET @curr = (SELECT CONCAT(IS_NULLABLE,'|',COLUMN_TYPE,'|',IFNULL(COLUMN_DEFAULT,'')) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='inv_inbound_order' AND COLUMN_NAME='deleted'); -SET @sql = IF(@curr != 'NO|tinyint|0', 'ALTER TABLE inv_inbound_order MODIFY COLUMN deleted TINYINT NOT NULL DEFAULT 0 COMMENT ''软删除: 0-正常, 1-已删除''', 'SELECT ''inv_inbound_order.deleted already standard'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 34. inv_outbound_order -SET @curr = (SELECT CONCAT(IS_NULLABLE,'|',COLUMN_TYPE,'|',IFNULL(COLUMN_DEFAULT,'')) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='inv_outbound_order' AND COLUMN_NAME='deleted'); -SET @sql = IF(@curr != 'NO|tinyint|0', 'ALTER TABLE inv_outbound_order MODIFY COLUMN deleted TINYINT NOT NULL DEFAULT 0 COMMENT ''软删除: 0-正常, 1-已删除''', 'SELECT ''inv_outbound_order.deleted already standard'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 35. inv_outbound_item -SET @curr = (SELECT CONCAT(IS_NULLABLE,'|',COLUMN_TYPE,'|',IFNULL(COLUMN_DEFAULT,'')) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='inv_outbound_item' AND COLUMN_NAME='deleted'); -SET @sql = IF(@curr != 'NO|tinyint|0', 'ALTER TABLE inv_outbound_item MODIFY COLUMN deleted TINYINT NOT NULL DEFAULT 0 COMMENT ''软删除: 0-正常, 1-已删除''', 'SELECT ''inv_outbound_item.deleted already standard'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 36. inv_transfer_order -SET @curr = (SELECT CONCAT(IS_NULLABLE,'|',COLUMN_TYPE,'|',IFNULL(COLUMN_DEFAULT,'')) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='inv_transfer_order' AND COLUMN_NAME='deleted'); -SET @sql = IF(@curr != 'NO|tinyint|0', 'ALTER TABLE inv_transfer_order MODIFY COLUMN deleted TINYINT NOT NULL DEFAULT 0 COMMENT ''软删除: 0-正常, 1-已删除''', 'SELECT ''inv_transfer_order.deleted already standard'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 37. inv_transfer_item -SET @curr = (SELECT CONCAT(IS_NULLABLE,'|',COLUMN_TYPE,'|',IFNULL(COLUMN_DEFAULT,'')) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='inv_transfer_item' AND COLUMN_NAME='deleted'); -SET @sql = IF(@curr != 'NO|tinyint|0', 'ALTER TABLE inv_transfer_item MODIFY COLUMN deleted TINYINT NOT NULL DEFAULT 0 COMMENT ''软删除: 0-正常, 1-已删除''', 'SELECT ''inv_transfer_item.deleted already standard'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 38. inv_stocktaking -SET @curr = (SELECT CONCAT(IS_NULLABLE,'|',COLUMN_TYPE,'|',IFNULL(COLUMN_DEFAULT,'')) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='inv_stocktaking' AND COLUMN_NAME='deleted'); -SET @sql = IF(@curr != 'NO|tinyint|0', 'ALTER TABLE inv_stocktaking MODIFY COLUMN deleted TINYINT NOT NULL DEFAULT 0 COMMENT ''软删除: 0-正常, 1-已删除''', 'SELECT ''inv_stocktaking.deleted already standard'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 39. inv_stocktaking_item -SET @curr = (SELECT CONCAT(IS_NULLABLE,'|',COLUMN_TYPE,'|',IFNULL(COLUMN_DEFAULT,'')) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='inv_stocktaking_item' AND COLUMN_NAME='deleted'); -SET @sql = IF(@curr != 'NO|tinyint|0', 'ALTER TABLE inv_stocktaking_item MODIFY COLUMN deleted TINYINT NOT NULL DEFAULT 0 COMMENT ''软删除: 0-正常, 1-已删除''', 'SELECT ''inv_stocktaking_item.deleted already standard'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 40. inv_stock_adjust -SET @curr = (SELECT CONCAT(IS_NULLABLE,'|',COLUMN_TYPE,'|',IFNULL(COLUMN_DEFAULT,'')) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='inv_stock_adjust' AND COLUMN_NAME='deleted'); -SET @sql = IF(@curr != 'NO|tinyint|0', 'ALTER TABLE inv_stock_adjust MODIFY COLUMN deleted TINYINT NOT NULL DEFAULT 0 COMMENT ''软删除: 0-正常, 1-已删除''', 'SELECT ''inv_stock_adjust.deleted already standard'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 41. inv_stock_adjust_item -SET @curr = (SELECT CONCAT(IS_NULLABLE,'|',COLUMN_TYPE,'|',IFNULL(COLUMN_DEFAULT,'')) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='inv_stock_adjust_item' AND COLUMN_NAME='deleted'); -SET @sql = IF(@curr != 'NO|tinyint|0', 'ALTER TABLE inv_stock_adjust_item MODIFY COLUMN deleted TINYINT NOT NULL DEFAULT 0 COMMENT ''软删除: 0-正常, 1-已删除''', 'SELECT ''inv_stock_adjust_item.deleted already standard'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 42. inv_sales_outbound -SET @curr = (SELECT CONCAT(IS_NULLABLE,'|',COLUMN_TYPE,'|',IFNULL(COLUMN_DEFAULT,'')) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='inv_sales_outbound' AND COLUMN_NAME='deleted'); -SET @sql = IF(@curr != 'NO|tinyint|0', 'ALTER TABLE inv_sales_outbound MODIFY COLUMN deleted TINYINT NOT NULL DEFAULT 0 COMMENT ''软删除: 0-正常, 1-已删除''', 'SELECT ''inv_sales_outbound.deleted already standard'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 43. inv_sales_outbound_item -SET @curr = (SELECT CONCAT(IS_NULLABLE,'|',COLUMN_TYPE,'|',IFNULL(COLUMN_DEFAULT,'')) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='inv_sales_outbound_item' AND COLUMN_NAME='deleted'); -SET @sql = IF(@curr != 'NO|tinyint|0', 'ALTER TABLE inv_sales_outbound_item MODIFY COLUMN deleted TINYINT NOT NULL DEFAULT 0 COMMENT ''软删除: 0-正常, 1-已删除''', 'SELECT ''inv_sales_outbound_item.deleted already standard'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 44. inv_production_inbound -SET @curr = (SELECT CONCAT(IS_NULLABLE,'|',COLUMN_TYPE,'|',IFNULL(COLUMN_DEFAULT,'')) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='inv_production_inbound' AND COLUMN_NAME='deleted'); -SET @sql = IF(@curr != 'NO|tinyint|0', 'ALTER TABLE inv_production_inbound MODIFY COLUMN deleted TINYINT NOT NULL DEFAULT 0 COMMENT ''软删除: 0-正常, 1-已删除''', 'SELECT ''inv_production_inbound.deleted already standard'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 45. inv_production_inbound_item -SET @curr = (SELECT CONCAT(IS_NULLABLE,'|',COLUMN_TYPE,'|',IFNULL(COLUMN_DEFAULT,'')) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='inv_production_inbound_item' AND COLUMN_NAME='deleted'); -SET @sql = IF(@curr != 'NO|tinyint|0', 'ALTER TABLE inv_production_inbound_item MODIFY COLUMN deleted TINYINT NOT NULL DEFAULT 0 COMMENT ''软删除: 0-正常, 1-已删除''', 'SELECT ''inv_production_inbound_item.deleted already standard'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 46. inv_unit_conversion -SET @curr = (SELECT CONCAT(IS_NULLABLE,'|',COLUMN_TYPE,'|',IFNULL(COLUMN_DEFAULT,'')) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='inv_unit_conversion' AND COLUMN_NAME='deleted'); -SET @sql = IF(@curr != 'NO|tinyint|0', 'ALTER TABLE inv_unit_conversion MODIFY COLUMN deleted TINYINT NOT NULL DEFAULT 0 COMMENT ''软删除: 0-正常, 1-已删除''', 'SELECT ''inv_unit_conversion.deleted already standard'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 验证:检查是否仍有非标准 deleted 字段 -SELECT TABLE_NAME, IS_NULLABLE, COLUMN_TYPE, COLUMN_DEFAULT -FROM INFORMATION_SCHEMA.COLUMNS -WHERE TABLE_SCHEMA = DATABASE() - AND COLUMN_NAME = 'deleted' - AND (IS_NULLABLE = 'YES' OR COLUMN_TYPE != 'tinyint' OR IFNULL(COLUMN_DEFAULT,'') != '0') -ORDER BY TABLE_NAME; diff --git a/database/migrations/024_unify_decimal_precision.sql b/database/migrations/024_unify_decimal_precision.sql deleted file mode 100644 index b6f98d3b..00000000 --- a/database/migrations/024_unify_decimal_precision.sql +++ /dev/null @@ -1,213 +0,0 @@ --- ============================================================ --- Migration 024: 统一所有 DECIMAL 字段精度为 DECIMAL(18,4) --- --- 背景:根据《项目整体分析报告》P2 #5(DECIMAL 精度不一致) --- 14 张表共 39 个 DECIMAL 字段精度非 (18,4) --- 涉及金额、数量、税率、汇率、比率等字段 --- 统一为 DECIMAL(18,4) 以保证计算精度一致 --- --- 注意:inv_unit_conversion.ratio 从 (18,6) 收敛到 (18,4),可能丢失 2 位小数精度 --- 但为统一规范,按报告要求处理 --- --- 幂等模式:所有 ALTER 用 INFORMATION_SCHEMA 守卫 --- ============================================================ - --- ===== pur_purchase_order (7 字段) ===== - -SET @curr = (SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='pur_purchase_order' AND COLUMN_NAME='exchange_rate'); -SET @sql = IF(@curr != 'decimal(18,4)', 'ALTER TABLE pur_purchase_order MODIFY COLUMN exchange_rate DECIMAL(18,4) DEFAULT 1.0000 COMMENT ''汇率''', 'SELECT ''pur_purchase_order.exchange_rate already (18,4)'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @curr = (SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='pur_purchase_order' AND COLUMN_NAME='total_amount'); -SET @sql = IF(@curr != 'decimal(18,4)', 'ALTER TABLE pur_purchase_order MODIFY COLUMN total_amount DECIMAL(18,4) DEFAULT 0 COMMENT ''订单总金额''', 'SELECT ''pur_purchase_order.total_amount already (18,4)'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @curr = (SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='pur_purchase_order' AND COLUMN_NAME='total_quantity'); -SET @sql = IF(@curr != 'decimal(18,4)', 'ALTER TABLE pur_purchase_order MODIFY COLUMN total_quantity DECIMAL(18,4) DEFAULT 0 COMMENT ''订单总数量''', 'SELECT ''pur_purchase_order.total_quantity already (18,4)'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @curr = (SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='pur_purchase_order' AND COLUMN_NAME='tax_rate'); -SET @sql = IF(@curr != 'decimal(18,4)', 'ALTER TABLE pur_purchase_order MODIFY COLUMN tax_rate DECIMAL(18,4) DEFAULT 13.00 COMMENT ''税率%''', 'SELECT ''pur_purchase_order.tax_rate already (18,4)'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @curr = (SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='pur_purchase_order' AND COLUMN_NAME='tax_amount'); -SET @sql = IF(@curr != 'decimal(18,4)', 'ALTER TABLE pur_purchase_order MODIFY COLUMN tax_amount DECIMAL(18,4) DEFAULT 0 COMMENT ''税额''', 'SELECT ''pur_purchase_order.tax_amount already (18,4)'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @curr = (SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='pur_purchase_order' AND COLUMN_NAME='grand_total'); -SET @sql = IF(@curr != 'decimal(18,4)', 'ALTER TABLE pur_purchase_order MODIFY COLUMN grand_total DECIMAL(18,4) DEFAULT 0 COMMENT ''含税总金额''', 'SELECT ''pur_purchase_order.grand_total already (18,4)'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @curr = (SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='pur_purchase_order' AND COLUMN_NAME='over_receipt_tolerance'); -SET @sql = IF(@curr != 'decimal(18,4)', 'ALTER TABLE pur_purchase_order MODIFY COLUMN over_receipt_tolerance DECIMAL(18,4) DEFAULT 5.00 COMMENT ''超收容差率%''', 'SELECT ''pur_purchase_order.over_receipt_tolerance already (18,4)'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- ===== pur_purchase_order_line (8 字段) ===== - -SET @curr = (SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='pur_purchase_order_line' AND COLUMN_NAME='order_qty'); -SET @sql = IF(@curr != 'decimal(18,4)', 'ALTER TABLE pur_purchase_order_line MODIFY COLUMN order_qty DECIMAL(18,4) NOT NULL DEFAULT 0 COMMENT ''订购数量''', 'SELECT ''pur_purchase_order_line.order_qty already (18,4)'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @curr = (SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='pur_purchase_order_line' AND COLUMN_NAME='received_qty'); -SET @sql = IF(@curr != 'decimal(18,4)', 'ALTER TABLE pur_purchase_order_line MODIFY COLUMN received_qty DECIMAL(18,4) DEFAULT 0 COMMENT ''累计入库数量''', 'SELECT ''pur_purchase_order_line.received_qty already (18,4)'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @curr = (SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='pur_purchase_order_line' AND COLUMN_NAME='returned_qty'); -SET @sql = IF(@curr != 'decimal(18,4)', 'ALTER TABLE pur_purchase_order_line MODIFY COLUMN returned_qty DECIMAL(18,4) DEFAULT 0 COMMENT ''累计退货数量''', 'SELECT ''pur_purchase_order_line.returned_qty already (18,4)'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @curr = (SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='pur_purchase_order_line' AND COLUMN_NAME='unit_price'); -SET @sql = IF(@curr != 'decimal(18,4)', 'ALTER TABLE pur_purchase_order_line MODIFY COLUMN unit_price DECIMAL(18,4) NOT NULL DEFAULT 0 COMMENT ''单价''', 'SELECT ''pur_purchase_order_line.unit_price already (18,4)'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @curr = (SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='pur_purchase_order_line' AND COLUMN_NAME='amount'); -SET @sql = IF(@curr != 'decimal(18,4)', 'ALTER TABLE pur_purchase_order_line MODIFY COLUMN amount DECIMAL(18,4) DEFAULT 0 COMMENT ''金额''', 'SELECT ''pur_purchase_order_line.amount already (18,4)'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @curr = (SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='pur_purchase_order_line' AND COLUMN_NAME='tax_rate'); -SET @sql = IF(@curr != 'decimal(18,4)', 'ALTER TABLE pur_purchase_order_line MODIFY COLUMN tax_rate DECIMAL(18,4) DEFAULT 13.00 COMMENT ''税率%''', 'SELECT ''pur_purchase_order_line.tax_rate already (18,4)'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @curr = (SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='pur_purchase_order_line' AND COLUMN_NAME='tax_amount'); -SET @sql = IF(@curr != 'decimal(18,4)', 'ALTER TABLE pur_purchase_order_line MODIFY COLUMN tax_amount DECIMAL(18,4) DEFAULT 0 COMMENT ''税额''', 'SELECT ''pur_purchase_order_line.tax_amount already (18,4)'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @curr = (SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='pur_purchase_order_line' AND COLUMN_NAME='line_total'); -SET @sql = IF(@curr != 'decimal(18,4)', 'ALTER TABLE pur_purchase_order_line MODIFY COLUMN line_total DECIMAL(18,4) DEFAULT 0 COMMENT ''行合计''', 'SELECT ''pur_purchase_order_line.line_total already (18,4)'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- ===== sal_order (1 字段) ===== - -SET @curr = (SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='sal_order' AND COLUMN_NAME='exchange_rate'); -SET @sql = IF(@curr != 'decimal(18,4)', 'ALTER TABLE sal_order MODIFY COLUMN exchange_rate DECIMAL(18,4) DEFAULT 1 COMMENT ''汇率''', 'SELECT ''sal_order.exchange_rate already (18,4)'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- ===== sal_order_detail (1 字段) ===== - -SET @curr = (SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='sal_order_detail' AND COLUMN_NAME='tax_rate'); -SET @sql = IF(@curr != 'decimal(18,4)', 'ALTER TABLE sal_order_detail MODIFY COLUMN tax_rate DECIMAL(18,4) DEFAULT 0 COMMENT ''税率(%)''', 'SELECT ''sal_order_detail.tax_rate already (18,4)'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- ===== prd_bom_detail (1 字段) ===== - -SET @curr = (SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='prd_bom_detail' AND COLUMN_NAME='loss_rate'); -SET @sql = IF(@curr != 'decimal(18,4)', 'ALTER TABLE prd_bom_detail MODIFY COLUMN loss_rate DECIMAL(18,4) DEFAULT 0 COMMENT ''损耗率(%)''', 'SELECT ''prd_bom_detail.loss_rate already (18,4)'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- ===== eqp_equipment (1 字段) ===== - -SET @curr = (SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='eqp_equipment' AND COLUMN_NAME='capacity_per_hour'); -SET @sql = IF(@curr != 'decimal(18,4)', 'ALTER TABLE eqp_equipment MODIFY COLUMN capacity_per_hour DECIMAL(18,4) DEFAULT 100 COMMENT ''每小时产能(件/小时)''', 'SELECT ''eqp_equipment.capacity_per_hour already (18,4)'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- ===== prd_schedule_detail (1 字段) ===== - -SET @curr = (SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='prd_schedule_detail' AND COLUMN_NAME='duration_hours'); -SET @sql = IF(@curr != 'decimal(18,4)', 'ALTER TABLE prd_schedule_detail MODIFY COLUMN duration_hours DECIMAL(18,4) COMMENT ''预计耗时(小时)''', 'SELECT ''prd_schedule_detail.duration_hours already (18,4)'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- ===== prd_work_order_color_seq (1 字段) ===== - -SET @curr = (SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='prd_work_order_color_seq' AND COLUMN_NAME='estimated_duration_hours'); -SET @sql = IF(@curr != 'decimal(18,4)', 'ALTER TABLE prd_work_order_color_seq MODIFY COLUMN estimated_duration_hours DECIMAL(18,4) DEFAULT 4 COMMENT ''预计耗时(小时)''', 'SELECT ''prd_work_order_color_seq.estimated_duration_hours already (18,4)'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- ===== inv_inbound_order (2 字段) ===== - -SET @curr = (SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='inv_inbound_order' AND COLUMN_NAME='total_quantity'); -SET @sql = IF(@curr != 'decimal(18,4)', 'ALTER TABLE inv_inbound_order MODIFY COLUMN total_quantity DECIMAL(18,4) DEFAULT 0 COMMENT ''总数量''', 'SELECT ''inv_inbound_order.total_quantity already (18,4)'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @curr = (SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='inv_inbound_order' AND COLUMN_NAME='total_amount'); -SET @sql = IF(@curr != 'decimal(18,4)', 'ALTER TABLE inv_inbound_order MODIFY COLUMN total_amount DECIMAL(18,4) COMMENT ''总金额''', 'SELECT ''inv_inbound_order.total_amount already (18,4)'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- ===== inv_inbound_item (3 字段) ===== - -SET @curr = (SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='inv_inbound_item' AND COLUMN_NAME='quantity'); -SET @sql = IF(@curr != 'decimal(18,4)', 'ALTER TABLE inv_inbound_item MODIFY COLUMN quantity DECIMAL(18,4) COMMENT ''数量''', 'SELECT ''inv_inbound_item.quantity already (18,4)'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @curr = (SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='inv_inbound_item' AND COLUMN_NAME='unit_price'); -SET @sql = IF(@curr != 'decimal(18,4)', 'ALTER TABLE inv_inbound_item MODIFY COLUMN unit_price DECIMAL(18,4) COMMENT ''单价''', 'SELECT ''inv_inbound_item.unit_price already (18,4)'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @curr = (SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='inv_inbound_item' AND COLUMN_NAME='total_price'); -SET @sql = IF(@curr != 'decimal(18,4)', 'ALTER TABLE inv_inbound_item MODIFY COLUMN total_price DECIMAL(18,4) COMMENT ''总价''', 'SELECT ''inv_inbound_item.total_price already (18,4)'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- ===== fin_voucher (3 字段) ===== - -SET @curr = (SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='fin_voucher' AND COLUMN_NAME='total_debit'); -SET @sql = IF(@curr != 'decimal(18,4)', 'ALTER TABLE fin_voucher MODIFY COLUMN total_debit DECIMAL(18,4) DEFAULT 0 COMMENT ''借方合计''', 'SELECT ''fin_voucher.total_debit already (18,4)'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @curr = (SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='fin_voucher' AND COLUMN_NAME='total_credit'); -SET @sql = IF(@curr != 'decimal(18,4)', 'ALTER TABLE fin_voucher MODIFY COLUMN total_credit DECIMAL(18,4) DEFAULT 0 COMMENT ''贷方合计''', 'SELECT ''fin_voucher.total_credit already (18,4)'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @curr = (SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='fin_voucher' AND COLUMN_NAME='total_amount'); -SET @sql = IF(@curr != 'decimal(18,4)', 'ALTER TABLE fin_voucher MODIFY COLUMN total_amount DECIMAL(18,4) DEFAULT 0 COMMENT ''凭证总额(旧版兼容)''', 'SELECT ''fin_voucher.total_amount already (18,4)'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- ===== fin_voucher_line (3 字段) ===== - -SET @curr = (SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='fin_voucher_line' AND COLUMN_NAME='debit_amount'); -SET @sql = IF(@curr != 'decimal(18,4)', 'ALTER TABLE fin_voucher_line MODIFY COLUMN debit_amount DECIMAL(18,4) DEFAULT 0 COMMENT ''借方金额''', 'SELECT ''fin_voucher_line.debit_amount already (18,4)'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @curr = (SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='fin_voucher_line' AND COLUMN_NAME='credit_amount'); -SET @sql = IF(@curr != 'decimal(18,4)', 'ALTER TABLE fin_voucher_line MODIFY COLUMN credit_amount DECIMAL(18,4) DEFAULT 0 COMMENT ''贷方金额''', 'SELECT ''fin_voucher_line.credit_amount already (18,4)'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @curr = (SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='fin_voucher_line' AND COLUMN_NAME='amount'); -SET @sql = IF(@curr != 'decimal(18,4)', 'ALTER TABLE fin_voucher_line MODIFY COLUMN amount DECIMAL(18,4) DEFAULT 0 COMMENT ''金额(旧版兼容)''', 'SELECT ''fin_voucher_line.amount already (18,4)'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- ===== fin_account_balance (8 字段) ===== - -SET @curr = (SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='fin_account_balance' AND COLUMN_NAME='begin_debit'); -SET @sql = IF(@curr != 'decimal(18,4)', 'ALTER TABLE fin_account_balance MODIFY COLUMN begin_debit DECIMAL(18,4) DEFAULT 0 COMMENT ''期初借方''', 'SELECT ''fin_account_balance.begin_debit already (18,4)'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @curr = (SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='fin_account_balance' AND COLUMN_NAME='begin_credit'); -SET @sql = IF(@curr != 'decimal(18,4)', 'ALTER TABLE fin_account_balance MODIFY COLUMN begin_credit DECIMAL(18,4) DEFAULT 0 COMMENT ''期初贷方''', 'SELECT ''fin_account_balance.begin_credit already (18,4)'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @curr = (SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='fin_account_balance' AND COLUMN_NAME='current_debit'); -SET @sql = IF(@curr != 'decimal(18,4)', 'ALTER TABLE fin_account_balance MODIFY COLUMN current_debit DECIMAL(18,4) DEFAULT 0 COMMENT ''本期借方发生''', 'SELECT ''fin_account_balance.current_debit already (18,4)'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @curr = (SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='fin_account_balance' AND COLUMN_NAME='current_credit'); -SET @sql = IF(@curr != 'decimal(18,4)', 'ALTER TABLE fin_account_balance MODIFY COLUMN current_credit DECIMAL(18,4) DEFAULT 0 COMMENT ''本期贷方发生''', 'SELECT ''fin_account_balance.current_credit already (18,4)'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @curr = (SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='fin_account_balance' AND COLUMN_NAME='year_debit'); -SET @sql = IF(@curr != 'decimal(18,4)', 'ALTER TABLE fin_account_balance MODIFY COLUMN year_debit DECIMAL(18,4) DEFAULT 0 COMMENT ''本年累计借方''', 'SELECT ''fin_account_balance.year_debit already (18,4)'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @curr = (SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='fin_account_balance' AND COLUMN_NAME='year_credit'); -SET @sql = IF(@curr != 'decimal(18,4)', 'ALTER TABLE fin_account_balance MODIFY COLUMN year_credit DECIMAL(18,4) DEFAULT 0 COMMENT ''本年累计贷方''', 'SELECT ''fin_account_balance.year_credit already (18,4)'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @curr = (SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='fin_account_balance' AND COLUMN_NAME='end_debit'); -SET @sql = IF(@curr != 'decimal(18,4)', 'ALTER TABLE fin_account_balance MODIFY COLUMN end_debit DECIMAL(18,4) DEFAULT 0 COMMENT ''期末借方''', 'SELECT ''fin_account_balance.end_debit already (18,4)'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @curr = (SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='fin_account_balance' AND COLUMN_NAME='end_credit'); -SET @sql = IF(@curr != 'decimal(18,4)', 'ALTER TABLE fin_account_balance MODIFY COLUMN end_credit DECIMAL(18,4) DEFAULT 0 COMMENT ''期末贷方''', 'SELECT ''fin_account_balance.end_credit already (18,4)'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- ===== inv_unit_conversion (1 字段) ===== - -SET @curr = (SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='inv_unit_conversion' AND COLUMN_NAME='ratio'); -SET @sql = IF(@curr != 'decimal(18,4)', 'ALTER TABLE inv_unit_conversion MODIFY COLUMN ratio DECIMAL(18,4) NOT NULL COMMENT ''换算比率: from_unit * ratio = to_unit''', 'SELECT ''inv_unit_conversion.ratio already (18,4)'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 验证:检查是否仍有非 decimal(18,4) 的 DECIMAL 字段 -SELECT TABLE_NAME, COLUMN_NAME, COLUMN_TYPE -FROM INFORMATION_SCHEMA.COLUMNS -WHERE TABLE_SCHEMA = DATABASE() - AND DATA_TYPE = 'decimal' - AND COLUMN_TYPE != 'decimal(18,4)' -ORDER BY TABLE_NAME, COLUMN_NAME; diff --git a/database/migrations/025_unify_audit_fields.sql b/database/migrations/025_unify_audit_fields.sql deleted file mode 100644 index b5b24245..00000000 --- a/database/migrations/025_unify_audit_fields.sql +++ /dev/null @@ -1,111 +0,0 @@ --- ============================================================ --- Migration 025: 统一审计字段命名为 create_time/update_time/create_by/update_by --- --- 背景:根据《项目整体分析报告》P2 #7(审计字段命名不统一) --- 5 张表使用非标准审计字段命名: --- - created_at → create_time --- - created_by/updated_by → create_by/update_by --- - creator_id → create_by --- --- 特殊处理: --- - domain_event_outbox: 需重建 idx_status_created 索引 --- - fin_voucher: created_at 与 create_time 冗余,删除 created_at --- - fin_voucher.created_by 为 VARCHAR(50)(存姓名),保留类型仅改名 --- - prd_standard_card.creator 是业务字段("制作"),保留不动;creator_id → create_by --- --- 幂等模式:所有 ALTER 用 INFORMATION_SCHEMA 守卫 --- ============================================================ - --- ===== 1. prd_standard_card: creator_id → create_by ===== - -SET @col_exists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'prd_standard_card' AND COLUMN_NAME = 'creator_id'); -SET @sql = IF(@col_exists > 0, - 'ALTER TABLE prd_standard_card CHANGE COLUMN creator_id create_by BIGINT UNSIGNED COMMENT ''创建人ID''', - 'SELECT ''prd_standard_card.creator_id already renamed to create_by'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- ===== 2. domain_event_outbox: created_at → create_time(需重建索引)===== - --- 2.1 先删除 idx_status_created 索引(如果存在) -SET @idx_exists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'domain_event_outbox' AND INDEX_NAME = 'idx_status_created'); -SET @sql = IF(@idx_exists > 0, - 'ALTER TABLE domain_event_outbox DROP INDEX idx_status_created', - 'SELECT ''idx_status_created already dropped'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 2.2 CHANGE COLUMN created_at → create_time -SET @col_exists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'domain_event_outbox' AND COLUMN_NAME = 'created_at'); -SET @sql = IF(@col_exists > 0, - 'ALTER TABLE domain_event_outbox CHANGE COLUMN created_at create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT ''事件创建时间''', - 'SELECT ''domain_event_outbox.created_at already renamed to create_time'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 2.3 重建 idx_status_created 索引(引用 create_time) -SET @idx_exists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'domain_event_outbox' AND INDEX_NAME = 'idx_status_created'); -SET @sql = IF(@idx_exists = 0, - 'ALTER TABLE domain_event_outbox ADD INDEX idx_status_created (status, create_time) COMMENT ''待处理事件查询索引''', - 'SELECT ''idx_status_created already exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- ===== 3. inv_material_std: created_by → create_by, updated_by → update_by ===== - -SET @col_exists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_material_std' AND COLUMN_NAME = 'created_by'); -SET @sql = IF(@col_exists > 0, - 'ALTER TABLE inv_material_std CHANGE COLUMN created_by create_by BIGINT UNSIGNED DEFAULT NULL COMMENT ''创建人ID''', - 'SELECT ''inv_material_std.created_by already renamed'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @col_exists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_material_std' AND COLUMN_NAME = 'updated_by'); -SET @sql = IF(@col_exists > 0, - 'ALTER TABLE inv_material_std CHANGE COLUMN updated_by update_by BIGINT UNSIGNED DEFAULT NULL COMMENT ''更新人ID''', - 'SELECT ''inv_material_std.updated_by already renamed'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- ===== 4. prd_bom_std: created_by → create_by, updated_by → update_by ===== - -SET @col_exists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'prd_bom_std' AND COLUMN_NAME = 'created_by'); -SET @sql = IF(@col_exists > 0, - 'ALTER TABLE prd_bom_std CHANGE COLUMN created_by create_by BIGINT UNSIGNED DEFAULT NULL COMMENT ''创建人ID''', - 'SELECT ''prd_bom_std.created_by already renamed'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @col_exists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'prd_bom_std' AND COLUMN_NAME = 'updated_by'); -SET @sql = IF(@col_exists > 0, - 'ALTER TABLE prd_bom_std CHANGE COLUMN updated_by update_by BIGINT UNSIGNED DEFAULT NULL COMMENT ''更新人ID''', - 'SELECT ''prd_bom_std.updated_by already renamed'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- ===== 5. fin_voucher: 删除冗余 created_at, created_by → create_by ===== - --- 5.1 删除冗余的 created_at 字段(create_time 已存在) -SET @col_exists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'fin_voucher' AND COLUMN_NAME = 'created_at'); -SET @sql = IF(@col_exists > 0, - 'ALTER TABLE fin_voucher DROP COLUMN created_at', - 'SELECT ''fin_voucher.created_at already dropped'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 5.2 created_by VARCHAR(50) → create_by VARCHAR(50)(保留类型,仅改名) --- 注意:fin_voucher.created_by 存制单人姓名,保留 VARCHAR(50) 类型避免数据丢失 -SET @col_exists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'fin_voucher' AND COLUMN_NAME = 'created_by'); -SET @sql = IF(@col_exists > 0, - 'ALTER TABLE fin_voucher CHANGE COLUMN created_by create_by VARCHAR(50) COMMENT ''制单人''', - 'SELECT ''fin_voucher.created_by already renamed'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 验证:检查是否仍有非标准审计字段 -SELECT TABLE_NAME, COLUMN_NAME, COLUMN_TYPE, COLUMN_COMMENT -FROM INFORMATION_SCHEMA.COLUMNS -WHERE TABLE_SCHEMA = DATABASE() - AND (COLUMN_NAME IN ('created_at', 'updated_at', 'created_by', 'updated_by', 'creator_id') - OR (TABLE_NAME = 'fin_voucher' AND COLUMN_NAME = 'created_at')) -ORDER BY TABLE_NAME, COLUMN_NAME; diff --git a/database/migrations/026_add_missing_audit_fields.sql b/database/migrations/026_add_missing_audit_fields.sql deleted file mode 100644 index fa42dfb1..00000000 --- a/database/migrations/026_add_missing_audit_fields.sql +++ /dev/null @@ -1,280 +0,0 @@ --- ============================================================ --- Migration 026: 补齐缺失的标准审计字段 --- --- 背景:根据《项目整体分析报告》P2 #7(审计字段命名不统一)+ P2 #9(软删除不统一) --- 35+ 张表缺失标准审计字段(create_time/update_time/create_by/update_by/deleted) --- --- 字段标准定义: --- create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间' --- update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间' --- create_by BIGINT UNSIGNED DEFAULT NULL COMMENT '创建人ID' --- update_by BIGINT UNSIGNED DEFAULT NULL COMMENT '更新人ID' --- deleted TINYINT NOT NULL DEFAULT 0 COMMENT '软删除: 0-正常, 1-已删除' --- --- 幂等模式:所有 ALTER 用 INFORMATION_SCHEMA 守卫 --- ============================================================ - --- ===== A. 补 create_time(4 表:inv_inventory, fin_account, fin_period, fin_account_balance)===== - -SET @col = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='inv_inventory' AND COLUMN_NAME='create_time'); -SET @sql = IF(@col=0, 'ALTER TABLE inv_inventory ADD COLUMN create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT ''创建时间''', 'SELECT ''inv_inventory.create_time exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @col = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='fin_account' AND COLUMN_NAME='create_time'); -SET @sql = IF(@col=0, 'ALTER TABLE fin_account ADD COLUMN create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT ''创建时间''', 'SELECT ''fin_account.create_time exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @col = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='fin_period' AND COLUMN_NAME='create_time'); -SET @sql = IF(@col=0, 'ALTER TABLE fin_period ADD COLUMN create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT ''创建时间''', 'SELECT ''fin_period.create_time exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @col = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='fin_account_balance' AND COLUMN_NAME='create_time'); -SET @sql = IF(@col=0, 'ALTER TABLE fin_account_balance ADD COLUMN create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT ''创建时间''', 'SELECT ''fin_account_balance.create_time exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- ===== B. 补 update_time(6 表)===== - -SET @col = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='qc_inspection' AND COLUMN_NAME='update_time'); -SET @sql = IF(@col=0, 'ALTER TABLE qc_inspection ADD COLUMN update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT ''更新时间''', 'SELECT ''qc_inspection.update_time exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @col = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='material_batch_costs' AND COLUMN_NAME='update_time'); -SET @sql = IF(@col=0, 'ALTER TABLE material_batch_costs ADD COLUMN update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT ''更新时间''', 'SELECT ''material_batch_costs.update_time exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @col = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='fin_voucher' AND COLUMN_NAME='update_time'); -SET @sql = IF(@col=0, 'ALTER TABLE fin_voucher ADD COLUMN update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT ''更新时间''', 'SELECT ''fin_voucher.update_time exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @col = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='fin_account' AND COLUMN_NAME='update_time'); -SET @sql = IF(@col=0, 'ALTER TABLE fin_account ADD COLUMN update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT ''更新时间''', 'SELECT ''fin_account.update_time exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @col = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='fin_period' AND COLUMN_NAME='update_time'); -SET @sql = IF(@col=0, 'ALTER TABLE fin_period ADD COLUMN update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT ''更新时间''', 'SELECT ''fin_period.update_time exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @col = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='fin_account_balance' AND COLUMN_NAME='update_time'); -SET @sql = IF(@col=0, 'ALTER TABLE fin_account_balance ADD COLUMN update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT ''更新时间''', 'SELECT ''fin_account_balance.update_time exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- ===== C. 补 create_by + update_by(35 表,每张表一个 ALTER ADD 多列)===== - --- 系统模块 -SET @col = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='sys_department' AND COLUMN_NAME='create_by'); -SET @sql = IF(@col=0, 'ALTER TABLE sys_department ADD COLUMN create_by BIGINT UNSIGNED DEFAULT NULL COMMENT ''创建人ID'', ADD COLUMN update_by BIGINT UNSIGNED DEFAULT NULL COMMENT ''更新人ID''', 'SELECT ''sys_department audit fields exist'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @col = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='sys_role' AND COLUMN_NAME='create_by'); -SET @sql = IF(@col=0, 'ALTER TABLE sys_role ADD COLUMN create_by BIGINT UNSIGNED DEFAULT NULL COMMENT ''创建人ID'', ADD COLUMN update_by BIGINT UNSIGNED DEFAULT NULL COMMENT ''更新人ID''', 'SELECT ''sys_role audit fields exist'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @col = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='sys_menu' AND COLUMN_NAME='create_by'); -SET @sql = IF(@col=0, 'ALTER TABLE sys_menu ADD COLUMN create_by BIGINT UNSIGNED DEFAULT NULL COMMENT ''创建人ID'', ADD COLUMN update_by BIGINT UNSIGNED DEFAULT NULL COMMENT ''更新人ID''', 'SELECT ''sys_menu audit fields exist'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @col = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='sys_dict_type' AND COLUMN_NAME='create_by'); -SET @sql = IF(@col=0, 'ALTER TABLE sys_dict_type ADD COLUMN create_by BIGINT UNSIGNED DEFAULT NULL COMMENT ''创建人ID'', ADD COLUMN update_by BIGINT UNSIGNED DEFAULT NULL COMMENT ''更新人ID''', 'SELECT ''sys_dict_type audit fields exist'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @col = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='sys_dict_data' AND COLUMN_NAME='create_by'); -SET @sql = IF(@col=0, 'ALTER TABLE sys_dict_data ADD COLUMN create_by BIGINT UNSIGNED DEFAULT NULL COMMENT ''创建人ID'', ADD COLUMN update_by BIGINT UNSIGNED DEFAULT NULL COMMENT ''更新人ID''', 'SELECT ''sys_dict_data audit fields exist'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @col = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='sys_config' AND COLUMN_NAME='create_by'); -SET @sql = IF(@col=0, 'ALTER TABLE sys_config ADD COLUMN create_by BIGINT UNSIGNED DEFAULT NULL COMMENT ''创建人ID'', ADD COLUMN update_by BIGINT UNSIGNED DEFAULT NULL COMMENT ''更新人ID''', 'SELECT ''sys_config audit fields exist'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 客户模块 -SET @col = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='crm_customer_contact' AND COLUMN_NAME='create_by'); -SET @sql = IF(@col=0, 'ALTER TABLE crm_customer_contact ADD COLUMN create_by BIGINT UNSIGNED DEFAULT NULL COMMENT ''创建人ID'', ADD COLUMN update_by BIGINT UNSIGNED DEFAULT NULL COMMENT ''更新人ID''', 'SELECT ''crm_customer_contact audit fields exist'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 仓储模块 -SET @col = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='inv_material_category' AND COLUMN_NAME='create_by'); -SET @sql = IF(@col=0, 'ALTER TABLE inv_material_category ADD COLUMN create_by BIGINT UNSIGNED DEFAULT NULL COMMENT ''创建人ID'', ADD COLUMN update_by BIGINT UNSIGNED DEFAULT NULL COMMENT ''更新人ID''', 'SELECT ''inv_material_category audit fields exist'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @col = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='inv_warehouse' AND COLUMN_NAME='create_by'); -SET @sql = IF(@col=0, 'ALTER TABLE inv_warehouse ADD COLUMN create_by BIGINT UNSIGNED DEFAULT NULL COMMENT ''创建人ID'', ADD COLUMN update_by BIGINT UNSIGNED DEFAULT NULL COMMENT ''更新人ID''', 'SELECT ''inv_warehouse audit fields exist'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @col = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='inv_inventory' AND COLUMN_NAME='create_by'); -SET @sql = IF(@col=0, 'ALTER TABLE inv_inventory ADD COLUMN create_by BIGINT UNSIGNED DEFAULT NULL COMMENT ''创建人ID'', ADD COLUMN update_by BIGINT UNSIGNED DEFAULT NULL COMMENT ''更新人ID''', 'SELECT ''inv_inventory audit fields exist'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @col = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='inv_inventory_batch' AND COLUMN_NAME='create_by'); -SET @sql = IF(@col=0, 'ALTER TABLE inv_inventory_batch ADD COLUMN create_by BIGINT UNSIGNED DEFAULT NULL COMMENT ''创建人ID'', ADD COLUMN update_by BIGINT UNSIGNED DEFAULT NULL COMMENT ''更新人ID''', 'SELECT ''inv_inventory_batch audit fields exist'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @col = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='inv_inbound_order' AND COLUMN_NAME='create_by'); -SET @sql = IF(@col=0, 'ALTER TABLE inv_inbound_order ADD COLUMN create_by BIGINT UNSIGNED DEFAULT NULL COMMENT ''创建人ID'', ADD COLUMN update_by BIGINT UNSIGNED DEFAULT NULL COMMENT ''更新人ID''', 'SELECT ''inv_inbound_order audit fields exist'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @col = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='inv_outbound_order' AND COLUMN_NAME='create_by'); -SET @sql = IF(@col=0, 'ALTER TABLE inv_outbound_order ADD COLUMN create_by BIGINT UNSIGNED DEFAULT NULL COMMENT ''创建人ID'', ADD COLUMN update_by BIGINT UNSIGNED DEFAULT NULL COMMENT ''更新人ID''', 'SELECT ''inv_outbound_order audit fields exist'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @col = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='inv_transfer_order' AND COLUMN_NAME='create_by'); -SET @sql = IF(@col=0, 'ALTER TABLE inv_transfer_order ADD COLUMN create_by BIGINT UNSIGNED DEFAULT NULL COMMENT ''创建人ID'', ADD COLUMN update_by BIGINT UNSIGNED DEFAULT NULL COMMENT ''更新人ID''', 'SELECT ''inv_transfer_order audit fields exist'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @col = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='inv_stocktaking' AND COLUMN_NAME='create_by'); -SET @sql = IF(@col=0, 'ALTER TABLE inv_stocktaking ADD COLUMN create_by BIGINT UNSIGNED DEFAULT NULL COMMENT ''创建人ID'', ADD COLUMN update_by BIGINT UNSIGNED DEFAULT NULL COMMENT ''更新人ID''', 'SELECT ''inv_stocktaking audit fields exist'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @col = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='inv_stock_adjust' AND COLUMN_NAME='create_by'); -SET @sql = IF(@col=0, 'ALTER TABLE inv_stock_adjust ADD COLUMN create_by BIGINT UNSIGNED DEFAULT NULL COMMENT ''创建人ID'', ADD COLUMN update_by BIGINT UNSIGNED DEFAULT NULL COMMENT ''更新人ID''', 'SELECT ''inv_stock_adjust audit fields exist'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @col = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='inv_sales_outbound' AND COLUMN_NAME='create_by'); -SET @sql = IF(@col=0, 'ALTER TABLE inv_sales_outbound ADD COLUMN create_by BIGINT UNSIGNED DEFAULT NULL COMMENT ''创建人ID'', ADD COLUMN update_by BIGINT UNSIGNED DEFAULT NULL COMMENT ''更新人ID''', 'SELECT ''inv_sales_outbound audit fields exist'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @col = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='inv_production_inbound' AND COLUMN_NAME='create_by'); -SET @sql = IF(@col=0, 'ALTER TABLE inv_production_inbound ADD COLUMN create_by BIGINT UNSIGNED DEFAULT NULL COMMENT ''创建人ID'', ADD COLUMN update_by BIGINT UNSIGNED DEFAULT NULL COMMENT ''更新人ID''', 'SELECT ''inv_production_inbound audit fields exist'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @col = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='inv_unit_conversion' AND COLUMN_NAME='create_by'); -SET @sql = IF(@col=0, 'ALTER TABLE inv_unit_conversion ADD COLUMN create_by BIGINT UNSIGNED DEFAULT NULL COMMENT ''创建人ID'', ADD COLUMN update_by BIGINT UNSIGNED DEFAULT NULL COMMENT ''更新人ID''', 'SELECT ''inv_unit_conversion audit fields exist'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 质量模块 -SET @col = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='qc_inspection' AND COLUMN_NAME='create_by'); -SET @sql = IF(@col=0, 'ALTER TABLE qc_inspection ADD COLUMN create_by BIGINT UNSIGNED DEFAULT NULL COMMENT ''创建人ID'', ADD COLUMN update_by BIGINT UNSIGNED DEFAULT NULL COMMENT ''更新人ID''', 'SELECT ''qc_inspection audit fields exist'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 财务模块 -SET @col = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='fin_receivable' AND COLUMN_NAME='create_by'); -SET @sql = IF(@col=0, 'ALTER TABLE fin_receivable ADD COLUMN create_by BIGINT UNSIGNED DEFAULT NULL COMMENT ''创建人ID'', ADD COLUMN update_by BIGINT UNSIGNED DEFAULT NULL COMMENT ''更新人ID''', 'SELECT ''fin_receivable audit fields exist'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @col = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='fin_payable' AND COLUMN_NAME='create_by'); -SET @sql = IF(@col=0, 'ALTER TABLE fin_payable ADD COLUMN create_by BIGINT UNSIGNED DEFAULT NULL COMMENT ''创建人ID'', ADD COLUMN update_by BIGINT UNSIGNED DEFAULT NULL COMMENT ''更新人ID''', 'SELECT ''fin_payable audit fields exist'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @col = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='fin_account' AND COLUMN_NAME='create_by'); -SET @sql = IF(@col=0, 'ALTER TABLE fin_account ADD COLUMN create_by BIGINT UNSIGNED DEFAULT NULL COMMENT ''创建人ID'', ADD COLUMN update_by BIGINT UNSIGNED DEFAULT NULL COMMENT ''更新人ID''', 'SELECT ''fin_account audit fields exist'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @col = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='fin_period' AND COLUMN_NAME='create_by'); -SET @sql = IF(@col=0, 'ALTER TABLE fin_period ADD COLUMN create_by BIGINT UNSIGNED DEFAULT NULL COMMENT ''创建人ID'', ADD COLUMN update_by BIGINT UNSIGNED DEFAULT NULL COMMENT ''更新人ID''', 'SELECT ''fin_period audit fields exist'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @col = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='fin_account_balance' AND COLUMN_NAME='create_by'); -SET @sql = IF(@col=0, 'ALTER TABLE fin_account_balance ADD COLUMN create_by BIGINT UNSIGNED DEFAULT NULL COMMENT ''创建人ID'', ADD COLUMN update_by BIGINT UNSIGNED DEFAULT NULL COMMENT ''更新人ID''', 'SELECT ''fin_account_balance audit fields exist'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 生产/设备模块 -SET @col = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='eqp_equipment' AND COLUMN_NAME='create_by'); -SET @sql = IF(@col=0, 'ALTER TABLE eqp_equipment ADD COLUMN create_by BIGINT UNSIGNED DEFAULT NULL COMMENT ''创建人ID'', ADD COLUMN update_by BIGINT UNSIGNED DEFAULT NULL COMMENT ''更新人ID''', 'SELECT ''eqp_equipment audit fields exist'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @col = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='prd_schedule' AND COLUMN_NAME='create_by'); -SET @sql = IF(@col=0, 'ALTER TABLE prd_schedule ADD COLUMN create_by BIGINT UNSIGNED DEFAULT NULL COMMENT ''创建人ID'', ADD COLUMN update_by BIGINT UNSIGNED DEFAULT NULL COMMENT ''更新人ID''', 'SELECT ''prd_schedule audit fields exist'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @col = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='work_order_costs' AND COLUMN_NAME='create_by'); -SET @sql = IF(@col=0, 'ALTER TABLE work_order_costs ADD COLUMN create_by BIGINT UNSIGNED DEFAULT NULL COMMENT ''创建人ID'', ADD COLUMN update_by BIGINT UNSIGNED DEFAULT NULL COMMENT ''更新人ID''', 'SELECT ''work_order_costs audit fields exist'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @col = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='material_batch_costs' AND COLUMN_NAME='create_by'); -SET @sql = IF(@col=0, 'ALTER TABLE material_batch_costs ADD COLUMN create_by BIGINT UNSIGNED DEFAULT NULL COMMENT ''创建人ID'', ADD COLUMN update_by BIGINT UNSIGNED DEFAULT NULL COMMENT ''更新人ID''', 'SELECT ''material_batch_costs audit fields exist'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 领退料模块 -SET @col = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='material_requisitions' AND COLUMN_NAME='create_by'); -SET @sql = IF(@col=0, 'ALTER TABLE material_requisitions ADD COLUMN create_by BIGINT UNSIGNED DEFAULT NULL COMMENT ''创建人ID'', ADD COLUMN update_by BIGINT UNSIGNED DEFAULT NULL COMMENT ''更新人ID''', 'SELECT ''material_requisitions audit fields exist'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @col = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='material_returns' AND COLUMN_NAME='create_by'); -SET @sql = IF(@col=0, 'ALTER TABLE material_returns ADD COLUMN create_by BIGINT UNSIGNED DEFAULT NULL COMMENT ''创建人ID'', ADD COLUMN update_by BIGINT UNSIGNED DEFAULT NULL COMMENT ''更新人ID''', 'SELECT ''material_returns audit fields exist'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 仅补 update_by 的表(已有 create_by) -SET @col = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='prd_standard_card' AND COLUMN_NAME='update_by'); -SET @sql = IF(@col=0, 'ALTER TABLE prd_standard_card ADD COLUMN update_by BIGINT UNSIGNED DEFAULT NULL COMMENT ''更新人ID''', 'SELECT ''prd_standard_card.update_by exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @col = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='prd_work_order' AND COLUMN_NAME='update_by'); -SET @sql = IF(@col=0, 'ALTER TABLE prd_work_order ADD COLUMN update_by BIGINT UNSIGNED DEFAULT NULL COMMENT ''更新人ID''', 'SELECT ''prd_work_order.update_by exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @col = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='prd_bom' AND COLUMN_NAME='update_by'); -SET @sql = IF(@col=0, 'ALTER TABLE prd_bom ADD COLUMN update_by BIGINT UNSIGNED DEFAULT NULL COMMENT ''更新人ID''', 'SELECT ''prd_bom.update_by exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @col = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='fin_voucher' AND COLUMN_NAME='update_by'); -SET @sql = IF(@col=0, 'ALTER TABLE fin_voucher ADD COLUMN update_by BIGINT UNSIGNED DEFAULT NULL COMMENT ''更新人ID''', 'SELECT ''fin_voucher.update_by exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- ===== D. 补 deleted(14 表,仅业务主表)===== - -SET @col = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='sys_menu' AND COLUMN_NAME='deleted'); -SET @sql = IF(@col=0, 'ALTER TABLE sys_menu ADD COLUMN deleted TINYINT NOT NULL DEFAULT 0 COMMENT ''软删除: 0-正常, 1-已删除''', 'SELECT ''sys_menu.deleted exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @col = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='sys_dict_type' AND COLUMN_NAME='deleted'); -SET @sql = IF(@col=0, 'ALTER TABLE sys_dict_type ADD COLUMN deleted TINYINT NOT NULL DEFAULT 0 COMMENT ''软删除: 0-正常, 1-已删除''', 'SELECT ''sys_dict_type.deleted exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @col = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='sys_dict_data' AND COLUMN_NAME='deleted'); -SET @sql = IF(@col=0, 'ALTER TABLE sys_dict_data ADD COLUMN deleted TINYINT NOT NULL DEFAULT 0 COMMENT ''软删除: 0-正常, 1-已删除''', 'SELECT ''sys_dict_data.deleted exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @col = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='sys_config' AND COLUMN_NAME='deleted'); -SET @sql = IF(@col=0, 'ALTER TABLE sys_config ADD COLUMN deleted TINYINT NOT NULL DEFAULT 0 COMMENT ''软删除: 0-正常, 1-已删除''', 'SELECT ''sys_config.deleted exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @col = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='prd_work_order' AND COLUMN_NAME='deleted'); -SET @sql = IF(@col=0, 'ALTER TABLE prd_work_order ADD COLUMN deleted TINYINT NOT NULL DEFAULT 0 COMMENT ''软删除: 0-正常, 1-已删除''', 'SELECT ''prd_work_order.deleted exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @col = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='prd_bom' AND COLUMN_NAME='deleted'); -SET @sql = IF(@col=0, 'ALTER TABLE prd_bom ADD COLUMN deleted TINYINT NOT NULL DEFAULT 0 COMMENT ''软删除: 0-正常, 1-已删除''', 'SELECT ''prd_bom.deleted exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @col = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='fin_receivable' AND COLUMN_NAME='deleted'); -SET @sql = IF(@col=0, 'ALTER TABLE fin_receivable ADD COLUMN deleted TINYINT NOT NULL DEFAULT 0 COMMENT ''软删除: 0-正常, 1-已删除''', 'SELECT ''fin_receivable.deleted exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @col = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='fin_payable' AND COLUMN_NAME='deleted'); -SET @sql = IF(@col=0, 'ALTER TABLE fin_payable ADD COLUMN deleted TINYINT NOT NULL DEFAULT 0 COMMENT ''软删除: 0-正常, 1-已删除''', 'SELECT ''fin_payable.deleted exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @col = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='work_order_costs' AND COLUMN_NAME='deleted'); -SET @sql = IF(@col=0, 'ALTER TABLE work_order_costs ADD COLUMN deleted TINYINT NOT NULL DEFAULT 0 COMMENT ''软删除: 0-正常, 1-已删除''', 'SELECT ''work_order_costs.deleted exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @col = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='material_batch_costs' AND COLUMN_NAME='deleted'); -SET @sql = IF(@col=0, 'ALTER TABLE material_batch_costs ADD COLUMN deleted TINYINT NOT NULL DEFAULT 0 COMMENT ''软删除: 0-正常, 1-已删除''', 'SELECT ''material_batch_costs.deleted exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @col = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='fin_account' AND COLUMN_NAME='deleted'); -SET @sql = IF(@col=0, 'ALTER TABLE fin_account ADD COLUMN deleted TINYINT NOT NULL DEFAULT 0 COMMENT ''软删除: 0-正常, 1-已删除''', 'SELECT ''fin_account.deleted exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @col = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='fin_period' AND COLUMN_NAME='deleted'); -SET @sql = IF(@col=0, 'ALTER TABLE fin_period ADD COLUMN deleted TINYINT NOT NULL DEFAULT 0 COMMENT ''软删除: 0-正常, 1-已删除''', 'SELECT ''fin_period.deleted exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @col = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='fin_account_balance' AND COLUMN_NAME='deleted'); -SET @sql = IF(@col=0, 'ALTER TABLE fin_account_balance ADD COLUMN deleted TINYINT NOT NULL DEFAULT 0 COMMENT ''软删除: 0-正常, 1-已删除''', 'SELECT ''fin_account_balance.deleted exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @col = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='qc_inspection' AND COLUMN_NAME='deleted'); -SET @sql = IF(@col=0, 'ALTER TABLE qc_inspection ADD COLUMN deleted TINYINT NOT NULL DEFAULT 0 COMMENT ''软删除: 0-正常, 1-已删除''', 'SELECT ''qc_inspection.deleted exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 验证:检查仍缺失标准审计字段的业务主表 -SELECT TABLE_NAME, - MAX(CASE WHEN COLUMN_NAME='create_time' THEN 1 ELSE 0 END) AS has_create_time, - MAX(CASE WHEN COLUMN_NAME='update_time' THEN 1 ELSE 0 END) AS has_update_time, - MAX(CASE WHEN COLUMN_NAME='create_by' THEN 1 ELSE 0 END) AS has_create_by, - MAX(CASE WHEN COLUMN_NAME='update_by' THEN 1 ELSE 0 END) AS has_update_by, - MAX(CASE WHEN COLUMN_NAME='deleted' THEN 1 ELSE 0 END) AS has_deleted -FROM INFORMATION_SCHEMA.COLUMNS -WHERE TABLE_SCHEMA = DATABASE() - AND TABLE_NAME IN ('inv_inventory','fin_account','fin_period','fin_account_balance','qc_inspection','prd_work_order','prd_bom','fin_receivable','fin_payable','sys_menu','sys_dict_type','sys_dict_data','sys_config') -GROUP BY TABLE_NAME -ORDER BY TABLE_NAME; diff --git a/database/migrations/027_align_pk_type_purchase.sql b/database/migrations/027_align_pk_type_purchase.sql deleted file mode 100644 index 799fc550..00000000 --- a/database/migrations/027_align_pk_type_purchase.sql +++ /dev/null @@ -1,71 +0,0 @@ --- ============================================================ --- Migration 027: 主键类型对齐 - 采购模块 --- --- 背景:根据《项目整体分析报告》P2 #6(主键类型不一致) --- 采购模块 2 张表主键为 INT UNSIGNED,需统一为 BIGINT UNSIGNED --- 以匹配 ERP 全局主键类型标准(id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT) --- --- 涉及表: --- pur_purchase_order.id INT UNSIGNED → BIGINT UNSIGNED --- pur_purchase_order_line.id INT UNSIGNED → BIGINT UNSIGNED --- pur_purchase_order_line.po_id INT UNSIGNED → BIGINT UNSIGNED(关联列同步) --- --- 操作顺序(避免 FK 冲突): --- 1. 删除 fk_pur_line_po --- 2. 修改 pur_purchase_order.id 类型 --- 3. 修改 pur_purchase_order_line.id 类型 --- 4. 修改 pur_purchase_order_line.po_id 类型 --- 5. 重建 fk_pur_line_po --- --- 幂等模式:所有 ALTER 用 INFORMATION_SCHEMA 守卫 --- ============================================================ - --- 1. 删除 fk_pur_line_po(如果存在) -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'pur_purchase_order_line' AND CONSTRAINT_NAME = 'fk_pur_line_po'); -SET @sql = IF(@fk > 0, - 'ALTER TABLE pur_purchase_order_line DROP FOREIGN KEY fk_pur_line_po', - 'SELECT ''fk_pur_line_po not exists (already dropped)'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 2. 修改 pur_purchase_order.id 为 BIGINT UNSIGNED --- 守卫:仅当当前类型不是 BIGINT UNSIGNED 时执行 -SET @curr = (SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'pur_purchase_order' AND COLUMN_NAME = 'id'); -SET @sql = IF(@curr IS NOT NULL AND @curr NOT LIKE 'bigint%unsigned%', - 'ALTER TABLE pur_purchase_order MODIFY COLUMN id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT ''主键ID''', - 'SELECT ''pur_purchase_order.id already bigint unsigned'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 3. 修改 pur_purchase_order_line.id 为 BIGINT UNSIGNED -SET @curr = (SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'pur_purchase_order_line' AND COLUMN_NAME = 'id'); -SET @sql = IF(@curr IS NOT NULL AND @curr NOT LIKE 'bigint%unsigned%', - 'ALTER TABLE pur_purchase_order_line MODIFY COLUMN id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT ''主键ID''', - 'SELECT ''pur_purchase_order_line.id already bigint unsigned'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 4. 修改 pur_purchase_order_line.po_id 为 BIGINT UNSIGNED(关联列必须与主键类型一致) -SET @curr = (SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'pur_purchase_order_line' AND COLUMN_NAME = 'po_id'); -SET @sql = IF(@curr IS NOT NULL AND @curr NOT LIKE 'bigint%unsigned%', - 'ALTER TABLE pur_purchase_order_line MODIFY COLUMN po_id BIGINT UNSIGNED NOT NULL COMMENT ''采购单ID''', - 'SELECT ''pur_purchase_order_line.po_id already bigint unsigned'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 5. 重建 fk_pur_line_po(如果不存在) -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'pur_purchase_order_line' AND CONSTRAINT_NAME = 'fk_pur_line_po'); -SET @sql = IF(@fk = 0, - 'ALTER TABLE pur_purchase_order_line ADD CONSTRAINT fk_pur_line_po FOREIGN KEY (po_id) REFERENCES pur_purchase_order (id) ON DELETE CASCADE ON UPDATE CASCADE', - 'SELECT ''fk_pur_line_po already exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 6. 验证:检查主键类型已对齐 -SELECT - TABLE_NAME, COLUMN_NAME, COLUMN_TYPE, COLUMN_KEY, EXTRA -FROM INFORMATION_SCHEMA.COLUMNS -WHERE TABLE_SCHEMA = DATABASE() - AND ((TABLE_NAME = 'pur_purchase_order' AND COLUMN_NAME = 'id') - OR (TABLE_NAME = 'pur_purchase_order_line' AND COLUMN_NAME IN ('id', 'po_id'))) -ORDER BY TABLE_NAME, ORDINAL_POSITION; diff --git a/database/migrations/028_align_pk_type_general_ledger_system.sql b/database/migrations/028_align_pk_type_general_ledger_system.sql deleted file mode 100644 index 462ed2a1..00000000 --- a/database/migrations/028_align_pk_type_general_ledger_system.sql +++ /dev/null @@ -1,102 +0,0 @@ --- ============================================================ --- Migration 028: 主键类型对齐 - 总账+系统模块 --- --- 背景:根据《项目整体分析报告》P2 #6(主键类型不一致) --- 4 张表主键类型不符合 BIGINT UNSIGNED 标准 --- --- 涉及表: --- fin_account.id INT UNSIGNED → BIGINT UNSIGNED --- fin_account.parent_id INT UNSIGNED → BIGINT UNSIGNED(自引用关联列) --- fin_period.id INT UNSIGNED → BIGINT UNSIGNED --- sys_event_processed.id BIGINT (缺 UNSIGNED) → BIGINT UNSIGNED --- sys_migration.id INT (缺 UNSIGNED) → BIGINT UNSIGNED --- --- 级联调整(引用 fin_account.id 的列): --- fin_voucher_line.account_id INT UNSIGNED → BIGINT UNSIGNED --- fin_account_balance.account_id INT UNSIGNED → BIGINT UNSIGNED --- (两表均无 FK 到 fin_account,仅 KEY idx_account,可直接 MODIFY) --- --- 注:fin_period 通过 period_code 冗余关联,无 period_id 引用列 --- 注:sys_event_processed / sys_migration 无 FK 引用 --- --- 幂等模式:所有 ALTER 用 INFORMATION_SCHEMA 守卫 --- ============================================================ - --- ===== A. fin_account 主键 + 自引用列 ===== - --- A1. fin_account.id -SET @curr = (SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'fin_account' AND COLUMN_NAME = 'id'); -SET @sql = IF(@curr IS NOT NULL AND @curr NOT LIKE 'bigint%unsigned%', - 'ALTER TABLE fin_account MODIFY COLUMN id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT ''科目ID''', - 'SELECT ''fin_account.id already bigint unsigned'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- A2. fin_account.parent_id(自引用关联列) -SET @curr = (SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'fin_account' AND COLUMN_NAME = 'parent_id'); -SET @sql = IF(@curr IS NOT NULL AND @curr NOT LIKE 'bigint%unsigned%', - 'ALTER TABLE fin_account MODIFY COLUMN parent_id BIGINT UNSIGNED COMMENT ''父级科目ID''', - 'SELECT ''fin_account.parent_id already bigint unsigned'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- ===== B. fin_period 主键 ===== - -SET @curr = (SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'fin_period' AND COLUMN_NAME = 'id'); -SET @sql = IF(@curr IS NOT NULL AND @curr NOT LIKE 'bigint%unsigned%', - 'ALTER TABLE fin_period MODIFY COLUMN id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT ''期间ID''', - 'SELECT ''fin_period.id already bigint unsigned'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- ===== C. fin_voucher_line.account_id(引用 fin_account.id)===== - -SET @curr = (SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'fin_voucher_line' AND COLUMN_NAME = 'account_id'); -SET @sql = IF(@curr IS NOT NULL AND @curr NOT LIKE 'bigint%unsigned%', - 'ALTER TABLE fin_voucher_line MODIFY COLUMN account_id BIGINT UNSIGNED NOT NULL COMMENT ''科目ID''', - 'SELECT ''fin_voucher_line.account_id already bigint unsigned'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- ===== D. fin_account_balance.account_id(引用 fin_account.id)===== --- 注:该列参与 UNIQUE KEY uk_period_account,MySQL MODIFY COLUMN 会自动保留索引 - -SET @curr = (SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'fin_account_balance' AND COLUMN_NAME = 'account_id'); -SET @sql = IF(@curr IS NOT NULL AND @curr NOT LIKE 'bigint%unsigned%', - 'ALTER TABLE fin_account_balance MODIFY COLUMN account_id BIGINT UNSIGNED NOT NULL COMMENT ''科目ID''', - 'SELECT ''fin_account_balance.account_id already bigint unsigned'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- ===== E. sys_event_processed.id(补 UNSIGNED)===== - -SET @curr = (SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'sys_event_processed' AND COLUMN_NAME = 'id'); -SET @sql = IF(@curr IS NOT NULL AND @curr NOT LIKE 'bigint%unsigned%', - 'ALTER TABLE sys_event_processed MODIFY COLUMN id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT', - 'SELECT ''sys_event_processed.id already bigint unsigned'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- ===== F. sys_migration.id(INT → BIGINT UNSIGNED)===== - -SET @curr = (SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'sys_migration' AND COLUMN_NAME = 'id'); -SET @sql = IF(@curr IS NOT NULL AND @curr NOT LIKE 'bigint%unsigned%', - 'ALTER TABLE sys_migration MODIFY COLUMN id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT', - 'SELECT ''sys_migration.id already bigint unsigned'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- ===== G. 验证:检查所有目标列类型已对齐 ===== -SELECT - TABLE_NAME, COLUMN_NAME, COLUMN_TYPE, COLUMN_KEY, EXTRA -FROM INFORMATION_SCHEMA.COLUMNS -WHERE TABLE_SCHEMA = DATABASE() - AND ( - (TABLE_NAME = 'fin_account' AND COLUMN_NAME IN ('id', 'parent_id')) - OR (TABLE_NAME = 'fin_period' AND COLUMN_NAME = 'id') - OR (TABLE_NAME = 'fin_voucher_line' AND COLUMN_NAME = 'account_id') - OR (TABLE_NAME = 'fin_account_balance' AND COLUMN_NAME = 'account_id') - OR (TABLE_NAME = 'sys_event_processed' AND COLUMN_NAME = 'id') - OR (TABLE_NAME = 'sys_migration' AND COLUMN_NAME = 'id') - ) -ORDER BY TABLE_NAME, ORDINAL_POSITION; diff --git a/database/migrations/029_add_p0_core_fk.sql b/database/migrations/029_add_p0_core_fk.sql deleted file mode 100644 index 416a0812..00000000 --- a/database/migrations/029_add_p0_core_fk.sql +++ /dev/null @@ -1,204 +0,0 @@ --- ============================================================ --- Migration 029: 批量 FK - P0 核心业务(15 个) --- --- 背景:根据《项目整体分析报告》P0 #2(外键约束缺失) --- 补全 P0 核心业务 FK,覆盖销售/仓储/采购/生产主从表关联 --- --- 前置条件:Migration 027/028 已统一采购和总账模块主键类型 --- --- 操作顺序: --- A. 类型对齐(3 处阻塞项:inv_inbound_item.order_id/material_id, --- pur_purchase_order.supplier_id, pur_purchase_order_line.material_id) --- B. 索引补建(prd_work_order.sales_order_id 缺索引) --- C. 15 个 P0 FK 添加 --- --- ON DELETE 策略: --- - 明细表(detail/item/line)→ CASCADE(主表删则明细同删) --- - 主数据引用(material/warehouse/customer/supplier)→ RESTRICT --- - 可空引用(order_id 可空的 delivery/return/work_order)→ SET NULL --- --- 幂等模式:所有 ALTER 用 INFORMATION_SCHEMA 守卫 --- ============================================================ - --- ===== A. 类型对齐 ===== - --- A1. inv_inbound_item.order_id: INT → BIGINT UNSIGNED -SET @curr = (SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_inbound_item' AND COLUMN_NAME = 'order_id'); -SET @sql = IF(@curr IS NOT NULL AND @curr NOT LIKE 'bigint%unsigned%', - 'ALTER TABLE inv_inbound_item MODIFY COLUMN order_id BIGINT UNSIGNED NOT NULL COMMENT ''入库单ID''', - 'SELECT ''inv_inbound_item.order_id already bigint unsigned'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- A2. inv_inbound_item.material_id: INT → BIGINT UNSIGNED -SET @curr = (SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_inbound_item' AND COLUMN_NAME = 'material_id'); -SET @sql = IF(@curr IS NOT NULL AND @curr NOT LIKE 'bigint%unsigned%', - 'ALTER TABLE inv_inbound_item MODIFY COLUMN material_id BIGINT UNSIGNED DEFAULT NULL COMMENT ''物料ID''', - 'SELECT ''inv_inbound_item.material_id already bigint unsigned'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- A3. pur_purchase_order.supplier_id: INT UNSIGNED → BIGINT UNSIGNED -SET @curr = (SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'pur_purchase_order' AND COLUMN_NAME = 'supplier_id'); -SET @sql = IF(@curr IS NOT NULL AND @curr NOT LIKE 'bigint%unsigned%', - 'ALTER TABLE pur_purchase_order MODIFY COLUMN supplier_id BIGINT UNSIGNED DEFAULT NULL COMMENT ''供应商ID''', - 'SELECT ''pur_purchase_order.supplier_id already bigint unsigned'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- A4. pur_purchase_order_line.material_id: INT UNSIGNED → BIGINT UNSIGNED -SET @curr = (SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'pur_purchase_order_line' AND COLUMN_NAME = 'material_id'); -SET @sql = IF(@curr IS NOT NULL AND @curr NOT LIKE 'bigint%unsigned%', - 'ALTER TABLE pur_purchase_order_line MODIFY COLUMN material_id BIGINT UNSIGNED DEFAULT NULL COMMENT ''物料ID''', - 'SELECT ''pur_purchase_order_line.material_id already bigint unsigned'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- ===== B. 索引补建 ===== - --- B1. prd_work_order.sales_order_id 缺索引(FK 前置条件) -SET @idx = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'prd_work_order' AND INDEX_NAME = 'idx_sales_order'); -SET @sql = IF(@idx = 0, - 'ALTER TABLE prd_work_order ADD INDEX idx_sales_order (sales_order_id)', - 'SELECT ''idx_sales_order already exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- ===== C. P0 核心 FK 添加(15 个)===== - --- C1. sal_order_detail.order_id → sal_order.id -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'sal_order_detail' AND CONSTRAINT_NAME = 'fk_sal_order_detail_order'); -SET @sql = IF(@fk = 0, - 'ALTER TABLE sal_order_detail ADD CONSTRAINT fk_sal_order_detail_order FOREIGN KEY (order_id) REFERENCES sal_order (id) ON DELETE CASCADE ON UPDATE CASCADE', - 'SELECT ''fk_sal_order_detail_order already exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- C2. sal_order.customer_id → crm_customer.id -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'sal_order' AND CONSTRAINT_NAME = 'fk_sal_order_customer'); -SET @sql = IF(@fk = 0, - 'ALTER TABLE sal_order ADD CONSTRAINT fk_sal_order_customer FOREIGN KEY (customer_id) REFERENCES crm_customer (id) ON DELETE RESTRICT ON UPDATE CASCADE', - 'SELECT ''fk_sal_order_customer already exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- C3. sal_order_detail.material_id → inv_material.id -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'sal_order_detail' AND CONSTRAINT_NAME = 'fk_sal_order_detail_material'); -SET @sql = IF(@fk = 0, - 'ALTER TABLE sal_order_detail ADD CONSTRAINT fk_sal_order_detail_material FOREIGN KEY (material_id) REFERENCES inv_material (id) ON DELETE RESTRICT ON UPDATE CASCADE', - 'SELECT ''fk_sal_order_detail_material already exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- C4. sal_delivery.order_id → sal_order.id(order_id 可空,SET NULL) -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'sal_delivery' AND CONSTRAINT_NAME = 'fk_sal_delivery_order'); -SET @sql = IF(@fk = 0, - 'ALTER TABLE sal_delivery ADD CONSTRAINT fk_sal_delivery_order FOREIGN KEY (order_id) REFERENCES sal_order (id) ON DELETE SET NULL ON UPDATE CASCADE', - 'SELECT ''fk_sal_delivery_order already exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- C5. sal_return.order_id → sal_order.id -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'sal_return' AND CONSTRAINT_NAME = 'fk_sal_return_order'); -SET @sql = IF(@fk = 0, - 'ALTER TABLE sal_return ADD CONSTRAINT fk_sal_return_order FOREIGN KEY (order_id) REFERENCES sal_order (id) ON DELETE RESTRICT ON UPDATE CASCADE', - 'SELECT ''fk_sal_return_order already exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- C6. inv_inventory.material_id → inv_material.id -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_inventory' AND CONSTRAINT_NAME = 'fk_inv_inventory_material'); -SET @sql = IF(@fk = 0, - 'ALTER TABLE inv_inventory ADD CONSTRAINT fk_inv_inventory_material FOREIGN KEY (material_id) REFERENCES inv_material (id) ON DELETE RESTRICT ON UPDATE CASCADE', - 'SELECT ''fk_inv_inventory_material already exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- C7. inv_inventory.warehouse_id → inv_warehouse.id -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_inventory' AND CONSTRAINT_NAME = 'fk_inv_inventory_warehouse'); -SET @sql = IF(@fk = 0, - 'ALTER TABLE inv_inventory ADD CONSTRAINT fk_inv_inventory_warehouse FOREIGN KEY (warehouse_id) REFERENCES inv_warehouse (id) ON DELETE RESTRICT ON UPDATE CASCADE', - 'SELECT ''fk_inv_inventory_warehouse already exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- C8. inv_inventory_batch.material_id → inv_material.id -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_inventory_batch' AND CONSTRAINT_NAME = 'fk_inv_inventory_batch_material'); -SET @sql = IF(@fk = 0, - 'ALTER TABLE inv_inventory_batch ADD CONSTRAINT fk_inv_inventory_batch_material FOREIGN KEY (material_id) REFERENCES inv_material (id) ON DELETE RESTRICT ON UPDATE CASCADE', - 'SELECT ''fk_inv_inventory_batch_material already exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- C9. inv_inventory_batch.warehouse_id → inv_warehouse.id(warehouse_id 可空,SET NULL) -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_inventory_batch' AND CONSTRAINT_NAME = 'fk_inv_inventory_batch_warehouse'); -SET @sql = IF(@fk = 0, - 'ALTER TABLE inv_inventory_batch ADD CONSTRAINT fk_inv_inventory_batch_warehouse FOREIGN KEY (warehouse_id) REFERENCES inv_warehouse (id) ON DELETE SET NULL ON UPDATE CASCADE', - 'SELECT ''fk_inv_inventory_batch_warehouse already exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- C10. inv_inbound_item.order_id → inv_inbound_order.id -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_inbound_item' AND CONSTRAINT_NAME = 'fk_inv_inbound_item_order'); -SET @sql = IF(@fk = 0, - 'ALTER TABLE inv_inbound_item ADD CONSTRAINT fk_inv_inbound_item_order FOREIGN KEY (order_id) REFERENCES inv_inbound_order (id) ON DELETE CASCADE ON UPDATE CASCADE', - 'SELECT ''fk_inv_inbound_item_order already exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- C11. inv_outbound_item.order_id → inv_outbound_order.id(修正:列名为 order_id 非 outbound_id) -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_outbound_item' AND CONSTRAINT_NAME = 'fk_inv_outbound_item_order'); -SET @sql = IF(@fk = 0, - 'ALTER TABLE inv_outbound_item ADD CONSTRAINT fk_inv_outbound_item_order FOREIGN KEY (order_id) REFERENCES inv_outbound_order (id) ON DELETE CASCADE ON UPDATE CASCADE', - 'SELECT ''fk_inv_outbound_item_order already exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- C12. pur_purchase_order.supplier_id → pur_supplier.id(可空,SET NULL) -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'pur_purchase_order' AND CONSTRAINT_NAME = 'fk_pur_po_supplier'); -SET @sql = IF(@fk = 0, - 'ALTER TABLE pur_purchase_order ADD CONSTRAINT fk_pur_po_supplier FOREIGN KEY (supplier_id) REFERENCES pur_supplier (id) ON DELETE SET NULL ON UPDATE CASCADE', - 'SELECT ''fk_pur_po_supplier already exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- C13. pur_purchase_order_line.material_id → inv_material.id(可空,SET NULL) -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'pur_purchase_order_line' AND CONSTRAINT_NAME = 'fk_pur_line_material'); -SET @sql = IF(@fk = 0, - 'ALTER TABLE pur_purchase_order_line ADD CONSTRAINT fk_pur_line_material FOREIGN KEY (material_id) REFERENCES inv_material (id) ON DELETE SET NULL ON UPDATE CASCADE', - 'SELECT ''fk_pur_line_material already exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- C14. prd_work_order.material_id → inv_material.id -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'prd_work_order' AND CONSTRAINT_NAME = 'fk_prd_wo_material'); -SET @sql = IF(@fk = 0, - 'ALTER TABLE prd_work_order ADD CONSTRAINT fk_prd_wo_material FOREIGN KEY (material_id) REFERENCES inv_material (id) ON DELETE RESTRICT ON UPDATE CASCADE', - 'SELECT ''fk_prd_wo_material already exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- C15. prd_work_order.sales_order_id → sal_order.id(可空,SET NULL) -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'prd_work_order' AND CONSTRAINT_NAME = 'fk_prd_wo_sales_order'); -SET @sql = IF(@fk = 0, - 'ALTER TABLE prd_work_order ADD CONSTRAINT fk_prd_wo_sales_order FOREIGN KEY (sales_order_id) REFERENCES sal_order (id) ON DELETE SET NULL ON UPDATE CASCADE', - 'SELECT ''fk_prd_wo_sales_order already exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- ===== D. 验证:检查 P0 FK 已添加 ===== -SELECT - TABLE_NAME, CONSTRAINT_NAME, REFERENCED_TABLE_NAME, REFERENCED_COLUMN_NAME -FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE -WHERE TABLE_SCHEMA = DATABASE() - AND REFERENCED_TABLE_NAME IS NOT NULL - AND CONSTRAINT_NAME IN ( - 'fk_sal_order_detail_order', 'fk_sal_order_customer', 'fk_sal_order_detail_material', - 'fk_sal_delivery_order', 'fk_sal_return_order', - 'fk_inv_inventory_material', 'fk_inv_inventory_warehouse', - 'fk_inv_inventory_batch_material', 'fk_inv_inventory_batch_warehouse', - 'fk_inv_inbound_item_order', 'fk_inv_outbound_item_order', - 'fk_pur_po_supplier', 'fk_pur_line_material', - 'fk_prd_wo_material', 'fk_prd_wo_sales_order' - ) -ORDER BY TABLE_NAME; diff --git a/database/migrations/030_add_p1_rbac_master_detail_fk.sql b/database/migrations/030_add_p1_rbac_master_detail_fk.sql deleted file mode 100644 index 1ba59ff3..00000000 --- a/database/migrations/030_add_p1_rbac_master_detail_fk.sql +++ /dev/null @@ -1,176 +0,0 @@ --- ============================================================ --- Migration 030: 批量 FK - P1 RBAC + 主从表(16 个) --- --- 背景:根据《项目整体分析报告》P0 #2(外键约束缺失) --- 补全 P1 级别 FK:RBAC 多对多关联 + 业务主从表 + 主数据从属关系 --- --- 涉及 FK(共 16 个): --- A. RBAC 关联(5 个):sys_user_role, sys_role_menu, sys_dict_data --- B. 业务主从表(8 个):sal_delivery_detail, sal_return_detail, prd_bom_detail, --- prd_schedule_detail, material_requisition_items, material_return_items, --- inv_transfer_item, inv_stocktaking_item --- C. 主数据从属(3 个):crm_customer_contact, inv_material.category_id, inv_warehouse.manager_id --- --- 自引用 FK(sys_department.parent_id, sys_menu.parent_id)留待 Migration 034 处理 --- 原因:DEFAULT 0 占位值需先数据清洗(改为 NULL),避免加 FK 时校验失败 --- --- 前置条件:Migration 026 已补齐审计字段;Migration 029 已补 P0 核心 FK --- --- 幂等模式:所有 ALTER 用 INFORMATION_SCHEMA 守卫 --- ============================================================ - --- ===== A. RBAC 关联表(5 个)===== - --- A1. sys_user_role.user_id → sys_user.id -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'sys_user_role' AND CONSTRAINT_NAME = 'fk_user_role_user'); -SET @sql = IF(@fk = 0, - 'ALTER TABLE sys_user_role ADD CONSTRAINT fk_user_role_user FOREIGN KEY (user_id) REFERENCES sys_user (id) ON DELETE CASCADE ON UPDATE CASCADE', - 'SELECT ''fk_user_role_user already exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- A2. sys_user_role.role_id → sys_role.id -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'sys_user_role' AND CONSTRAINT_NAME = 'fk_user_role_role'); -SET @sql = IF(@fk = 0, - 'ALTER TABLE sys_user_role ADD CONSTRAINT fk_user_role_role FOREIGN KEY (role_id) REFERENCES sys_role (id) ON DELETE CASCADE ON UPDATE CASCADE', - 'SELECT ''fk_user_role_role already exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- A3. sys_role_menu.role_id → sys_role.id -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'sys_role_menu' AND CONSTRAINT_NAME = 'fk_role_menu_role'); -SET @sql = IF(@fk = 0, - 'ALTER TABLE sys_role_menu ADD CONSTRAINT fk_role_menu_role FOREIGN KEY (role_id) REFERENCES sys_role (id) ON DELETE CASCADE ON UPDATE CASCADE', - 'SELECT ''fk_role_menu_role already exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- A4. sys_role_menu.menu_id → sys_menu.id -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'sys_role_menu' AND CONSTRAINT_NAME = 'fk_role_menu_menu'); -SET @sql = IF(@fk = 0, - 'ALTER TABLE sys_role_menu ADD CONSTRAINT fk_role_menu_menu FOREIGN KEY (menu_id) REFERENCES sys_menu (id) ON DELETE CASCADE ON UPDATE CASCADE', - 'SELECT ''fk_role_menu_menu already exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- A5. sys_dict_data.dict_type_id → sys_dict_type.id -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'sys_dict_data' AND CONSTRAINT_NAME = 'fk_dict_data_type'); -SET @sql = IF(@fk = 0, - 'ALTER TABLE sys_dict_data ADD CONSTRAINT fk_dict_data_type FOREIGN KEY (dict_type_id) REFERENCES sys_dict_type (id) ON DELETE RESTRICT ON UPDATE CASCADE', - 'SELECT ''fk_dict_data_type already exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- ===== B. 业务主从表(8 个)===== - --- B1. sal_delivery_detail.delivery_id → sal_delivery.id -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'sal_delivery_detail' AND CONSTRAINT_NAME = 'fk_delivery_detail_delivery'); -SET @sql = IF(@fk = 0, - 'ALTER TABLE sal_delivery_detail ADD CONSTRAINT fk_delivery_detail_delivery FOREIGN KEY (delivery_id) REFERENCES sal_delivery (id) ON DELETE CASCADE ON UPDATE CASCADE', - 'SELECT ''fk_delivery_detail_delivery already exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- B2. sal_return_detail.return_id → sal_return.id -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'sal_return_detail' AND CONSTRAINT_NAME = 'fk_return_detail_return'); -SET @sql = IF(@fk = 0, - 'ALTER TABLE sal_return_detail ADD CONSTRAINT fk_return_detail_return FOREIGN KEY (return_id) REFERENCES sal_return (id) ON DELETE CASCADE ON UPDATE CASCADE', - 'SELECT ''fk_return_detail_return already exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- B3. prd_bom_detail.bom_id → prd_bom.id -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'prd_bom_detail' AND CONSTRAINT_NAME = 'fk_bom_detail_bom'); -SET @sql = IF(@fk = 0, - 'ALTER TABLE prd_bom_detail ADD CONSTRAINT fk_bom_detail_bom FOREIGN KEY (bom_id) REFERENCES prd_bom (id) ON DELETE CASCADE ON UPDATE CASCADE', - 'SELECT ''fk_bom_detail_bom already exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- B4. prd_schedule_detail.schedule_id → prd_schedule.id -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'prd_schedule_detail' AND CONSTRAINT_NAME = 'fk_schedule_detail_schedule'); -SET @sql = IF(@fk = 0, - 'ALTER TABLE prd_schedule_detail ADD CONSTRAINT fk_schedule_detail_schedule FOREIGN KEY (schedule_id) REFERENCES prd_schedule (id) ON DELETE CASCADE ON UPDATE CASCADE', - 'SELECT ''fk_schedule_detail_schedule already exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- B5. material_requisition_items.requisition_id → material_requisitions.id -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'material_requisition_items' AND CONSTRAINT_NAME = 'fk_req_items_requisition'); -SET @sql = IF(@fk = 0, - 'ALTER TABLE material_requisition_items ADD CONSTRAINT fk_req_items_requisition FOREIGN KEY (requisition_id) REFERENCES material_requisitions (id) ON DELETE CASCADE ON UPDATE CASCADE', - 'SELECT ''fk_req_items_requisition already exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- B6. material_return_items.return_id → material_returns.id -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'material_return_items' AND CONSTRAINT_NAME = 'fk_ret_items_return'); -SET @sql = IF(@fk = 0, - 'ALTER TABLE material_return_items ADD CONSTRAINT fk_ret_items_return FOREIGN KEY (return_id) REFERENCES material_returns (id) ON DELETE CASCADE ON UPDATE CASCADE', - 'SELECT ''fk_ret_items_return already exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- B7. inv_transfer_item.transfer_id → inv_transfer_order.id -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_transfer_item' AND CONSTRAINT_NAME = 'fk_transfer_item_order'); -SET @sql = IF(@fk = 0, - 'ALTER TABLE inv_transfer_item ADD CONSTRAINT fk_transfer_item_order FOREIGN KEY (transfer_id) REFERENCES inv_transfer_order (id) ON DELETE CASCADE ON UPDATE CASCADE', - 'SELECT ''fk_transfer_item_order already exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- B8. inv_stocktaking_item.taking_id → inv_stocktaking.id -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_stocktaking_item' AND CONSTRAINT_NAME = 'fk_stocktaking_item_taking'); -SET @sql = IF(@fk = 0, - 'ALTER TABLE inv_stocktaking_item ADD CONSTRAINT fk_stocktaking_item_taking FOREIGN KEY (taking_id) REFERENCES inv_stocktaking (id) ON DELETE CASCADE ON UPDATE CASCADE', - 'SELECT ''fk_stocktaking_item_taking already exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- ===== C. 主数据从属(3 个)===== - --- C1. crm_customer_contact.customer_id → crm_customer.id -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'crm_customer_contact' AND CONSTRAINT_NAME = 'fk_customer_contact_customer'); -SET @sql = IF(@fk = 0, - 'ALTER TABLE crm_customer_contact ADD CONSTRAINT fk_customer_contact_customer FOREIGN KEY (customer_id) REFERENCES crm_customer (id) ON DELETE CASCADE ON UPDATE CASCADE', - 'SELECT ''fk_customer_contact_customer already exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- C2. inv_material.category_id → inv_material_category.id(可空,SET NULL) -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_material' AND CONSTRAINT_NAME = 'fk_material_category'); -SET @sql = IF(@fk = 0, - 'ALTER TABLE inv_material ADD CONSTRAINT fk_material_category FOREIGN KEY (category_id) REFERENCES inv_material_category (id) ON DELETE SET NULL ON UPDATE CASCADE', - 'SELECT ''fk_material_category already exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- C3. inv_warehouse.manager_id → sys_user.id(需先建索引) -SET @idx = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_warehouse' AND INDEX_NAME = 'idx_manager'); -SET @sql = IF(@idx = 0, - 'ALTER TABLE inv_warehouse ADD INDEX idx_manager (manager_id)', - 'SELECT ''idx_manager already exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_warehouse' AND CONSTRAINT_NAME = 'fk_warehouse_manager'); -SET @sql = IF(@fk = 0, - 'ALTER TABLE inv_warehouse ADD CONSTRAINT fk_warehouse_manager FOREIGN KEY (manager_id) REFERENCES sys_user (id) ON DELETE SET NULL ON UPDATE CASCADE', - 'SELECT ''fk_warehouse_manager already exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- ===== D. 验证:检查 P1 FK 已添加 ===== -SELECT - TABLE_NAME, CONSTRAINT_NAME, REFERENCED_TABLE_NAME -FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE -WHERE TABLE_SCHEMA = DATABASE() - AND REFERENCED_TABLE_NAME IS NOT NULL - AND CONSTRAINT_NAME IN ( - 'fk_user_role_user', 'fk_user_role_role', 'fk_role_menu_role', 'fk_role_menu_menu', 'fk_dict_data_type', - 'fk_delivery_detail_delivery', 'fk_return_detail_return', 'fk_bom_detail_bom', - 'fk_schedule_detail_schedule', 'fk_req_items_requisition', 'fk_ret_items_return', - 'fk_transfer_item_order', 'fk_stocktaking_item_taking', - 'fk_customer_contact_customer', 'fk_material_category', 'fk_warehouse_manager' - ) -ORDER BY TABLE_NAME; diff --git a/database/migrations/031_add_warehouse_module_fk.sql b/database/migrations/031_add_warehouse_module_fk.sql deleted file mode 100644 index 7f556aef..00000000 --- a/database/migrations/031_add_warehouse_module_fk.sql +++ /dev/null @@ -1,229 +0,0 @@ --- ============================================================ --- Migration 031: 批量 FK - 仓储模块(40 个) --- --- 背景:根据《项目整体分析报告》P0 #2(外键约束缺失) --- 仓储模块 11 张表 schema 中无任何 FK CONSTRAINT --- 迁移 009/017/018/029/030 已部分补齐,本迁移补全剩余 FK --- --- 前置条件:Migration 026/029/030 已完成 --- --- 涉及表: --- inv_inventory_log, inv_inbound_order, inv_inbound_item, --- inv_outbound_order, inv_outbound_item, --- inv_transfer_order, inv_transfer_item, --- inv_stocktaking, inv_stocktaking_item, --- inv_stock_adjust, inv_stock_adjust_item, --- inv_sales_outbound, inv_sales_outbound_item, --- inv_production_inbound, inv_production_inbound_item, --- inv_unit_conversion --- --- 跳过项: --- - batch_no 系列 FK:父表 inv_inventory_batch.batch_no 无 UNIQUE 约束,不可作 FK --- - from_unit/to_unit:inv_unit 表不存在,单位为 VARCHAR 字符串 --- - 多态外键:仓储表未发现 source_id+source_type 模式 --- --- 幂等模式:所有 ALTER 用 INFORMATION_SCHEMA 守卫(与 009/017/018 重叠的会自动跳过) --- ============================================================ - --- ===== A. 前置类型对齐 ===== - --- A1. inv_inbound_order.warehouse_id: INT → BIGINT UNSIGNED -SET @curr = (SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_inbound_order' AND COLUMN_NAME = 'warehouse_id'); -SET @sql = IF(@curr IS NOT NULL AND @curr NOT LIKE 'bigint%unsigned%', - 'ALTER TABLE inv_inbound_order MODIFY COLUMN warehouse_id BIGINT UNSIGNED NOT NULL COMMENT ''入库仓库ID''', - 'SELECT ''inv_inbound_order.warehouse_id already bigint unsigned'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- ===== B. inv_inventory_log(3 个 FK)===== - -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_inventory_log' AND CONSTRAINT_NAME = 'fk_inv_invlog_material'); -SET @sql = IF(@fk = 0, 'ALTER TABLE inv_inventory_log ADD CONSTRAINT fk_inv_invlog_material FOREIGN KEY (material_id) REFERENCES inv_material (id) ON DELETE RESTRICT ON UPDATE CASCADE', 'SELECT ''exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_inventory_log' AND CONSTRAINT_NAME = 'fk_inv_invlog_warehouse'); -SET @sql = IF(@fk = 0, 'ALTER TABLE inv_inventory_log ADD CONSTRAINT fk_inv_invlog_warehouse FOREIGN KEY (warehouse_id) REFERENCES inv_warehouse (id) ON DELETE RESTRICT ON UPDATE CASCADE', 'SELECT ''exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_inventory_log' AND CONSTRAINT_NAME = 'fk_inv_invlog_operator'); -SET @sql = IF(@fk = 0, 'ALTER TABLE inv_inventory_log ADD CONSTRAINT fk_inv_invlog_operator FOREIGN KEY (operator_id) REFERENCES sys_user (id) ON DELETE SET NULL ON UPDATE CASCADE', 'SELECT ''exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- ===== C. inv_inbound_order(1 个 FK,warehouse_id)===== - -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_inbound_order' AND CONSTRAINT_NAME = 'fk_inv_inbound_warehouse'); -SET @sql = IF(@fk = 0, 'ALTER TABLE inv_inbound_order ADD CONSTRAINT fk_inv_inbound_warehouse FOREIGN KEY (warehouse_id) REFERENCES inv_warehouse (id) ON DELETE RESTRICT ON UPDATE CASCADE', 'SELECT ''exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- ===== D. inv_outbound_order(5 个 FK)===== - -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_outbound_order' AND CONSTRAINT_NAME = 'fk_inv_outbound_warehouse'); -SET @sql = IF(@fk = 0, 'ALTER TABLE inv_outbound_order ADD CONSTRAINT fk_inv_outbound_warehouse FOREIGN KEY (warehouse_id) REFERENCES inv_warehouse (id) ON DELETE RESTRICT ON UPDATE CASCADE', 'SELECT ''exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_outbound_order' AND CONSTRAINT_NAME = 'fk_inv_outbound_customer'); -SET @sql = IF(@fk = 0, 'ALTER TABLE inv_outbound_order ADD CONSTRAINT fk_inv_outbound_customer FOREIGN KEY (customer_id) REFERENCES crm_customer (id) ON DELETE SET NULL ON UPDATE CASCADE', 'SELECT ''exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_outbound_order' AND CONSTRAINT_NAME = 'fk_inv_outbound_workorder'); -SET @sql = IF(@fk = 0, 'ALTER TABLE inv_outbound_order ADD CONSTRAINT fk_inv_outbound_workorder FOREIGN KEY (work_order_id) REFERENCES prd_work_order (id) ON DELETE SET NULL ON UPDATE CASCADE', 'SELECT ''exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_outbound_order' AND CONSTRAINT_NAME = 'fk_inv_outbound_auditor'); -SET @sql = IF(@fk = 0, 'ALTER TABLE inv_outbound_order ADD CONSTRAINT fk_inv_outbound_auditor FOREIGN KEY (auditor_id) REFERENCES sys_user (id) ON DELETE SET NULL ON UPDATE CASCADE', 'SELECT ''exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_outbound_order' AND CONSTRAINT_NAME = 'fk_inv_outbound_operator'); -SET @sql = IF(@fk = 0, 'ALTER TABLE inv_outbound_order ADD CONSTRAINT fk_inv_outbound_operator FOREIGN KEY (operator_id) REFERENCES sys_user (id) ON DELETE SET NULL ON UPDATE CASCADE', 'SELECT ''exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- ===== E. inv_outbound_item(3 个 FK:material_id, batch_id)===== - -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_outbound_item' AND CONSTRAINT_NAME = 'fk_inv_outbound_item_material'); -SET @sql = IF(@fk = 0, 'ALTER TABLE inv_outbound_item ADD CONSTRAINT fk_inv_outbound_item_material FOREIGN KEY (material_id) REFERENCES inv_material (id) ON DELETE RESTRICT ON UPDATE CASCADE', 'SELECT ''exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_outbound_item' AND CONSTRAINT_NAME = 'fk_inv_outbound_item_batch'); -SET @sql = IF(@fk = 0, 'ALTER TABLE inv_outbound_item ADD CONSTRAINT fk_inv_outbound_item_batch FOREIGN KEY (batch_id) REFERENCES inv_inventory_batch (id) ON DELETE SET NULL ON UPDATE CASCADE', 'SELECT ''exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- ===== F. inv_transfer_order(5 个 FK)===== - -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_transfer_order' AND CONSTRAINT_NAME = 'fk_inv_transfer_from_wh'); -SET @sql = IF(@fk = 0, 'ALTER TABLE inv_transfer_order ADD CONSTRAINT fk_inv_transfer_from_wh FOREIGN KEY (from_warehouse_id) REFERENCES inv_warehouse (id) ON DELETE RESTRICT ON UPDATE CASCADE', 'SELECT ''exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_transfer_order' AND CONSTRAINT_NAME = 'fk_inv_transfer_to_wh'); -SET @sql = IF(@fk = 0, 'ALTER TABLE inv_transfer_order ADD CONSTRAINT fk_inv_transfer_to_wh FOREIGN KEY (to_warehouse_id) REFERENCES inv_warehouse (id) ON DELETE RESTRICT ON UPDATE CASCADE', 'SELECT ''exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_transfer_order' AND CONSTRAINT_NAME = 'fk_inv_transfer_applicant'); -SET @sql = IF(@fk = 0, 'ALTER TABLE inv_transfer_order ADD CONSTRAINT fk_inv_transfer_applicant FOREIGN KEY (applicant_id) REFERENCES sys_user (id) ON DELETE SET NULL ON UPDATE CASCADE', 'SELECT ''exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_transfer_order' AND CONSTRAINT_NAME = 'fk_inv_transfer_approver'); -SET @sql = IF(@fk = 0, 'ALTER TABLE inv_transfer_order ADD CONSTRAINT fk_inv_transfer_approver FOREIGN KEY (approver_id) REFERENCES sys_user (id) ON DELETE SET NULL ON UPDATE CASCADE', 'SELECT ''exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_transfer_order' AND CONSTRAINT_NAME = 'fk_inv_transfer_operator'); -SET @sql = IF(@fk = 0, 'ALTER TABLE inv_transfer_order ADD CONSTRAINT fk_inv_transfer_operator FOREIGN KEY (operator_id) REFERENCES sys_user (id) ON DELETE SET NULL ON UPDATE CASCADE', 'SELECT ''exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- ===== G. inv_transfer_item(1 个 FK:material_id)===== - -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_transfer_item' AND CONSTRAINT_NAME = 'fk_inv_transfer_item_material'); -SET @sql = IF(@fk = 0, 'ALTER TABLE inv_transfer_item ADD CONSTRAINT fk_inv_transfer_item_material FOREIGN KEY (material_id) REFERENCES inv_material (id) ON DELETE RESTRICT ON UPDATE CASCADE', 'SELECT ''exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- ===== H. inv_stocktaking(3 个 FK)===== - -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_stocktaking' AND CONSTRAINT_NAME = 'fk_inv_stocktaking_warehouse'); -SET @sql = IF(@fk = 0, 'ALTER TABLE inv_stocktaking ADD CONSTRAINT fk_inv_stocktaking_warehouse FOREIGN KEY (warehouse_id) REFERENCES inv_warehouse (id) ON DELETE RESTRICT ON UPDATE CASCADE', 'SELECT ''exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_stocktaking' AND CONSTRAINT_NAME = 'fk_inv_stocktaking_applicant'); -SET @sql = IF(@fk = 0, 'ALTER TABLE inv_stocktaking ADD CONSTRAINT fk_inv_stocktaking_applicant FOREIGN KEY (applicant_id) REFERENCES sys_user (id) ON DELETE SET NULL ON UPDATE CASCADE', 'SELECT ''exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_stocktaking' AND CONSTRAINT_NAME = 'fk_inv_stocktaking_approver'); -SET @sql = IF(@fk = 0, 'ALTER TABLE inv_stocktaking ADD CONSTRAINT fk_inv_stocktaking_approver FOREIGN KEY (approver_id) REFERENCES sys_user (id) ON DELETE SET NULL ON UPDATE CASCADE', 'SELECT ''exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- ===== I. inv_stocktaking_item(2 个 FK:material_id, warehouse_id)===== - -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_stocktaking_item' AND CONSTRAINT_NAME = 'fk_inv_stocktaking_item_material'); -SET @sql = IF(@fk = 0, 'ALTER TABLE inv_stocktaking_item ADD CONSTRAINT fk_inv_stocktaking_item_material FOREIGN KEY (material_id) REFERENCES inv_material (id) ON DELETE RESTRICT ON UPDATE CASCADE', 'SELECT ''exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_stocktaking_item' AND CONSTRAINT_NAME = 'fk_inv_stocktaking_item_warehouse'); -SET @sql = IF(@fk = 0, 'ALTER TABLE inv_stocktaking_item ADD CONSTRAINT fk_inv_stocktaking_item_warehouse FOREIGN KEY (warehouse_id) REFERENCES inv_warehouse (id) ON DELETE RESTRICT ON UPDATE CASCADE', 'SELECT ''exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- ===== J. inv_stock_adjust(3 个 FK)===== - -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_stock_adjust' AND CONSTRAINT_NAME = 'fk_inv_adjust_warehouse'); -SET @sql = IF(@fk = 0, 'ALTER TABLE inv_stock_adjust ADD CONSTRAINT fk_inv_adjust_warehouse FOREIGN KEY (warehouse_id) REFERENCES inv_warehouse (id) ON DELETE RESTRICT ON UPDATE CASCADE', 'SELECT ''exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_stock_adjust' AND CONSTRAINT_NAME = 'fk_inv_adjust_operator'); -SET @sql = IF(@fk = 0, 'ALTER TABLE inv_stock_adjust ADD CONSTRAINT fk_inv_adjust_operator FOREIGN KEY (operator_id) REFERENCES sys_user (id) ON DELETE SET NULL ON UPDATE CASCADE', 'SELECT ''exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_stock_adjust' AND CONSTRAINT_NAME = 'fk_inv_adjust_approver'); -SET @sql = IF(@fk = 0, 'ALTER TABLE inv_stock_adjust ADD CONSTRAINT fk_inv_adjust_approver FOREIGN KEY (approver_id) REFERENCES sys_user (id) ON DELETE SET NULL ON UPDATE CASCADE', 'SELECT ''exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- ===== K. inv_stock_adjust_item(2 个 FK)===== - -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_stock_adjust_item' AND CONSTRAINT_NAME = 'fk_inv_adjust_item_adjust'); -SET @sql = IF(@fk = 0, 'ALTER TABLE inv_stock_adjust_item ADD CONSTRAINT fk_inv_adjust_item_adjust FOREIGN KEY (adjust_id) REFERENCES inv_stock_adjust (id) ON DELETE CASCADE ON UPDATE CASCADE', 'SELECT ''exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_stock_adjust_item' AND CONSTRAINT_NAME = 'fk_inv_adjust_item_material'); -SET @sql = IF(@fk = 0, 'ALTER TABLE inv_stock_adjust_item ADD CONSTRAINT fk_inv_adjust_item_material FOREIGN KEY (material_id) REFERENCES inv_material (id) ON DELETE RESTRICT ON UPDATE CASCADE', 'SELECT ''exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- ===== L. inv_sales_outbound(4 个 FK)===== - -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_sales_outbound' AND CONSTRAINT_NAME = 'fk_inv_sales_outbound_order'); -SET @sql = IF(@fk = 0, 'ALTER TABLE inv_sales_outbound ADD CONSTRAINT fk_inv_sales_outbound_order FOREIGN KEY (order_id) REFERENCES sal_order (id) ON DELETE SET NULL ON UPDATE CASCADE', 'SELECT ''exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_sales_outbound' AND CONSTRAINT_NAME = 'fk_inv_sales_outbound_customer'); -SET @sql = IF(@fk = 0, 'ALTER TABLE inv_sales_outbound ADD CONSTRAINT fk_inv_sales_outbound_customer FOREIGN KEY (customer_id) REFERENCES crm_customer (id) ON DELETE SET NULL ON UPDATE CASCADE', 'SELECT ''exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_sales_outbound' AND CONSTRAINT_NAME = 'fk_inv_sales_outbound_warehouse'); -SET @sql = IF(@fk = 0, 'ALTER TABLE inv_sales_outbound ADD CONSTRAINT fk_inv_sales_outbound_warehouse FOREIGN KEY (warehouse_id) REFERENCES inv_warehouse (id) ON DELETE RESTRICT ON UPDATE CASCADE', 'SELECT ''exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_sales_outbound' AND CONSTRAINT_NAME = 'fk_inv_sales_outbound_operator'); -SET @sql = IF(@fk = 0, 'ALTER TABLE inv_sales_outbound ADD CONSTRAINT fk_inv_sales_outbound_operator FOREIGN KEY (operator_id) REFERENCES sys_user (id) ON DELETE SET NULL ON UPDATE CASCADE', 'SELECT ''exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- ===== M. inv_sales_outbound_item(2 个 FK)===== - -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_sales_outbound_item' AND CONSTRAINT_NAME = 'fk_inv_sales_outbound_item_outbound'); -SET @sql = IF(@fk = 0, 'ALTER TABLE inv_sales_outbound_item ADD CONSTRAINT fk_inv_sales_outbound_item_outbound FOREIGN KEY (outbound_id) REFERENCES inv_sales_outbound (id) ON DELETE CASCADE ON UPDATE CASCADE', 'SELECT ''exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_sales_outbound_item' AND CONSTRAINT_NAME = 'fk_inv_sales_outbound_item_material'); -SET @sql = IF(@fk = 0, 'ALTER TABLE inv_sales_outbound_item ADD CONSTRAINT fk_inv_sales_outbound_item_material FOREIGN KEY (material_id) REFERENCES inv_material (id) ON DELETE RESTRICT ON UPDATE CASCADE', 'SELECT ''exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- ===== N. inv_production_inbound(3 个 FK)===== - -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_production_inbound' AND CONSTRAINT_NAME = 'fk_inv_prod_inbound_workorder'); -SET @sql = IF(@fk = 0, 'ALTER TABLE inv_production_inbound ADD CONSTRAINT fk_inv_prod_inbound_workorder FOREIGN KEY (work_order_id) REFERENCES prd_work_order (id) ON DELETE SET NULL ON UPDATE CASCADE', 'SELECT ''exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_production_inbound' AND CONSTRAINT_NAME = 'fk_inv_prod_inbound_warehouse'); -SET @sql = IF(@fk = 0, 'ALTER TABLE inv_production_inbound ADD CONSTRAINT fk_inv_prod_inbound_warehouse FOREIGN KEY (warehouse_id) REFERENCES inv_warehouse (id) ON DELETE RESTRICT ON UPDATE CASCADE', 'SELECT ''exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_production_inbound' AND CONSTRAINT_NAME = 'fk_inv_prod_inbound_operator'); -SET @sql = IF(@fk = 0, 'ALTER TABLE inv_production_inbound ADD CONSTRAINT fk_inv_prod_inbound_operator FOREIGN KEY (operator_id) REFERENCES sys_user (id) ON DELETE SET NULL ON UPDATE CASCADE', 'SELECT ''exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- ===== O. inv_production_inbound_item(2 个 FK)===== - -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_production_inbound_item' AND CONSTRAINT_NAME = 'fk_inv_prod_inbound_item_inbound'); -SET @sql = IF(@fk = 0, 'ALTER TABLE inv_production_inbound_item ADD CONSTRAINT fk_inv_prod_inbound_item_inbound FOREIGN KEY (inbound_id) REFERENCES inv_production_inbound (id) ON DELETE CASCADE ON UPDATE CASCADE', 'SELECT ''exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_production_inbound_item' AND CONSTRAINT_NAME = 'fk_inv_prod_inbound_item_material'); -SET @sql = IF(@fk = 0, 'ALTER TABLE inv_production_inbound_item ADD CONSTRAINT fk_inv_prod_inbound_item_material FOREIGN KEY (material_id) REFERENCES inv_material (id) ON DELETE RESTRICT ON UPDATE CASCADE', 'SELECT ''exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- ===== P. inv_unit_conversion(1 个 FK)===== - -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_unit_conversion' AND CONSTRAINT_NAME = 'fk_inv_unit_conv_material'); -SET @sql = IF(@fk = 0, 'ALTER TABLE inv_unit_conversion ADD CONSTRAINT fk_inv_unit_conv_material FOREIGN KEY (material_id) REFERENCES inv_material (id) ON DELETE RESTRICT ON UPDATE CASCADE', 'SELECT ''exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- ===== Q. 验证:仓储模块 FK 总数 ===== -SELECT COUNT(*) AS warehouse_fk_count -FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS -WHERE TABLE_SCHEMA = DATABASE() - AND CONSTRAINT_TYPE = 'FOREIGN KEY' - AND TABLE_NAME LIKE 'inv_%'; diff --git a/database/migrations/032_add_sales_production_material_cost_fk.sql b/database/migrations/032_add_sales_production_material_cost_fk.sql deleted file mode 100644 index 068ca252..00000000 --- a/database/migrations/032_add_sales_production_material_cost_fk.sql +++ /dev/null @@ -1,192 +0,0 @@ --- ============================================================ --- Migration 032: 批量 FK - 销售+生产+领退料+成本(33 个业务 FK) --- --- 背景:根据《项目整体分析报告》P0 #2(外键约束缺失) --- 补全 4 个模块的业务关联 FK(不含审计字段 FK,留待 034) --- --- 前置条件:Migration 029/030/031 已完成 --- --- 涉及模块: --- 销售(13 个):sal_order, sal_delivery, sal_delivery_detail, sal_return, --- sal_return_detail, sal_reconciliation --- 生产(8 个):prd_bom, prd_bom_detail, prd_schedule, prd_schedule_detail, --- prd_standard_card --- 领退料(9 个):material_requisitions, material_requisition_items, --- material_returns, material_return_items --- 成本(3 个):work_order_costs, material_batch_costs --- --- 审计字段 FK(create_by/update_by → sys_user.id 共 27 个)留待 Migration 034 --- 原因:需先补 18 个索引,且优先级低于业务 FK --- --- 幂等模式:所有 ALTER 用 INFORMATION_SCHEMA 守卫 --- ============================================================ - --- ===== A. 索引补建(3 个,FK 前置条件)===== - -SET @idx = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'sal_return' AND INDEX_NAME = 'idx_inbound_order'); -SET @sql = IF(@idx = 0, 'ALTER TABLE sal_return ADD INDEX idx_inbound_order (inbound_order_id)', 'SELECT ''exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @idx = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'sal_return' AND INDEX_NAME = 'idx_receivable'); -SET @sql = IF(@idx = 0, 'ALTER TABLE sal_return ADD INDEX idx_receivable (receivable_id)', 'SELECT ''exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @idx = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'sal_return_detail' AND INDEX_NAME = 'idx_delivery_detail'); -SET @sql = IF(@idx = 0, 'ALTER TABLE sal_return_detail ADD INDEX idx_delivery_detail (delivery_detail_id)', 'SELECT ''exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- ===== B. 销售模块(13 个业务 FK)===== - -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'sal_order' AND CONSTRAINT_NAME = 'fk_sal_order_salesman'); -SET @sql = IF(@fk = 0, 'ALTER TABLE sal_order ADD CONSTRAINT fk_sal_order_salesman FOREIGN KEY (salesman_id) REFERENCES sys_user (id) ON DELETE SET NULL ON UPDATE CASCADE', 'SELECT ''exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'sal_delivery' AND CONSTRAINT_NAME = 'fk_sal_delivery_customer'); -SET @sql = IF(@fk = 0, 'ALTER TABLE sal_delivery ADD CONSTRAINT fk_sal_delivery_customer FOREIGN KEY (customer_id) REFERENCES crm_customer (id) ON DELETE RESTRICT ON UPDATE CASCADE', 'SELECT ''exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'sal_delivery' AND CONSTRAINT_NAME = 'fk_sal_delivery_warehouse'); -SET @sql = IF(@fk = 0, 'ALTER TABLE sal_delivery ADD CONSTRAINT fk_sal_delivery_warehouse FOREIGN KEY (warehouse_id) REFERENCES inv_warehouse (id) ON DELETE RESTRICT ON UPDATE CASCADE', 'SELECT ''exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'sal_delivery_detail' AND CONSTRAINT_NAME = 'fk_sal_delivery_detail_order_detail'); -SET @sql = IF(@fk = 0, 'ALTER TABLE sal_delivery_detail ADD CONSTRAINT fk_sal_delivery_detail_order_detail FOREIGN KEY (order_detail_id) REFERENCES sal_order_detail (id) ON DELETE SET NULL ON UPDATE CASCADE', 'SELECT ''exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'sal_return' AND CONSTRAINT_NAME = 'fk_sal_return_customer'); -SET @sql = IF(@fk = 0, 'ALTER TABLE sal_return ADD CONSTRAINT fk_sal_return_customer FOREIGN KEY (customer_id) REFERENCES crm_customer (id) ON DELETE RESTRICT ON UPDATE CASCADE', 'SELECT ''exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'sal_return' AND CONSTRAINT_NAME = 'fk_sal_return_warehouse'); -SET @sql = IF(@fk = 0, 'ALTER TABLE sal_return ADD CONSTRAINT fk_sal_return_warehouse FOREIGN KEY (warehouse_id) REFERENCES inv_warehouse (id) ON DELETE RESTRICT ON UPDATE CASCADE', 'SELECT ''exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'sal_return' AND CONSTRAINT_NAME = 'fk_sal_return_delivery'); -SET @sql = IF(@fk = 0, 'ALTER TABLE sal_return ADD CONSTRAINT fk_sal_return_delivery FOREIGN KEY (delivery_id) REFERENCES sal_delivery (id) ON DELETE SET NULL ON UPDATE CASCADE', 'SELECT ''exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'sal_return' AND CONSTRAINT_NAME = 'fk_sal_return_inbound'); -SET @sql = IF(@fk = 0, 'ALTER TABLE sal_return ADD CONSTRAINT fk_sal_return_inbound FOREIGN KEY (inbound_order_id) REFERENCES inv_inbound_order (id) ON DELETE SET NULL ON UPDATE CASCADE', 'SELECT ''exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'sal_return' AND CONSTRAINT_NAME = 'fk_sal_return_receivable'); -SET @sql = IF(@fk = 0, 'ALTER TABLE sal_return ADD CONSTRAINT fk_sal_return_receivable FOREIGN KEY (receivable_id) REFERENCES fin_receivable (id) ON DELETE SET NULL ON UPDATE CASCADE', 'SELECT ''exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'sal_return_detail' AND CONSTRAINT_NAME = 'fk_sal_return_detail_delivery_detail'); -SET @sql = IF(@fk = 0, 'ALTER TABLE sal_return_detail ADD CONSTRAINT fk_sal_return_detail_delivery_detail FOREIGN KEY (delivery_detail_id) REFERENCES sal_delivery_detail (id) ON DELETE SET NULL ON UPDATE CASCADE', 'SELECT ''exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'sal_return_detail' AND CONSTRAINT_NAME = 'fk_sal_return_detail_order_detail'); -SET @sql = IF(@fk = 0, 'ALTER TABLE sal_return_detail ADD CONSTRAINT fk_sal_return_detail_order_detail FOREIGN KEY (order_detail_id) REFERENCES sal_order_detail (id) ON DELETE SET NULL ON UPDATE CASCADE', 'SELECT ''exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'sal_return_detail' AND CONSTRAINT_NAME = 'fk_sal_return_detail_material'); -SET @sql = IF(@fk = 0, 'ALTER TABLE sal_return_detail ADD CONSTRAINT fk_sal_return_detail_material FOREIGN KEY (material_id) REFERENCES inv_material (id) ON DELETE RESTRICT ON UPDATE CASCADE', 'SELECT ''exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'sal_reconciliation' AND CONSTRAINT_NAME = 'fk_sal_recon_customer'); -SET @sql = IF(@fk = 0, 'ALTER TABLE sal_reconciliation ADD CONSTRAINT fk_sal_recon_customer FOREIGN KEY (customer_id) REFERENCES crm_customer (id) ON DELETE RESTRICT ON UPDATE CASCADE', 'SELECT ''exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- ===== C. 生产模块(8 个业务 FK)===== - -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'prd_bom' AND CONSTRAINT_NAME = 'fk_prd_bom_material'); -SET @sql = IF(@fk = 0, 'ALTER TABLE prd_bom ADD CONSTRAINT fk_prd_bom_material FOREIGN KEY (material_id) REFERENCES inv_material (id) ON DELETE RESTRICT ON UPDATE CASCADE', 'SELECT ''exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'prd_bom_detail' AND CONSTRAINT_NAME = 'fk_prd_bom_detail_material'); -SET @sql = IF(@fk = 0, 'ALTER TABLE prd_bom_detail ADD CONSTRAINT fk_prd_bom_detail_material FOREIGN KEY (material_id) REFERENCES inv_material (id) ON DELETE RESTRICT ON UPDATE CASCADE', 'SELECT ''exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'prd_schedule' AND CONSTRAINT_NAME = 'fk_prd_schedule_order'); -SET @sql = IF(@fk = 0, 'ALTER TABLE prd_schedule ADD CONSTRAINT fk_prd_schedule_order FOREIGN KEY (order_id) REFERENCES sal_order (id) ON DELETE SET NULL ON UPDATE CASCADE', 'SELECT ''exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'prd_schedule' AND CONSTRAINT_NAME = 'fk_prd_schedule_work_order'); -SET @sql = IF(@fk = 0, 'ALTER TABLE prd_schedule ADD CONSTRAINT fk_prd_schedule_work_order FOREIGN KEY (work_order_id) REFERENCES prd_work_order (id) ON DELETE SET NULL ON UPDATE CASCADE', 'SELECT ''exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'prd_schedule' AND CONSTRAINT_NAME = 'fk_prd_schedule_product'); -SET @sql = IF(@fk = 0, 'ALTER TABLE prd_schedule ADD CONSTRAINT fk_prd_schedule_product FOREIGN KEY (product_id) REFERENCES inv_material (id) ON DELETE SET NULL ON UPDATE CASCADE', 'SELECT ''exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'prd_schedule_detail' AND CONSTRAINT_NAME = 'fk_prd_schedule_detail_work_order'); -SET @sql = IF(@fk = 0, 'ALTER TABLE prd_schedule_detail ADD CONSTRAINT fk_prd_schedule_detail_work_order FOREIGN KEY (work_order_id) REFERENCES prd_work_order (id) ON DELETE CASCADE ON UPDATE CASCADE', 'SELECT ''exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'prd_schedule_detail' AND CONSTRAINT_NAME = 'fk_prd_schedule_detail_equipment'); -SET @sql = IF(@fk = 0, 'ALTER TABLE prd_schedule_detail ADD CONSTRAINT fk_prd_schedule_detail_equipment FOREIGN KEY (equipment_id) REFERENCES eqp_equipment (id) ON DELETE SET NULL ON UPDATE CASCADE', 'SELECT ''exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'prd_standard_card' AND CONSTRAINT_NAME = 'fk_prd_std_card_customer'); -SET @sql = IF(@fk = 0, 'ALTER TABLE prd_standard_card ADD CONSTRAINT fk_prd_std_card_customer FOREIGN KEY (customer_id) REFERENCES crm_customer (id) ON DELETE SET NULL ON UPDATE CASCADE', 'SELECT ''exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- ===== D. 领退料模块(9 个业务 FK)===== - -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'material_requisitions' AND CONSTRAINT_NAME = 'fk_mat_req_work_order'); -SET @sql = IF(@fk = 0, 'ALTER TABLE material_requisitions ADD CONSTRAINT fk_mat_req_work_order FOREIGN KEY (work_order_id) REFERENCES prd_work_order (id) ON DELETE SET NULL ON UPDATE CASCADE', 'SELECT ''exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'material_requisitions' AND CONSTRAINT_NAME = 'fk_mat_req_warehouse'); -SET @sql = IF(@fk = 0, 'ALTER TABLE material_requisitions ADD CONSTRAINT fk_mat_req_warehouse FOREIGN KEY (warehouse_id) REFERENCES inv_warehouse (id) ON DELETE SET NULL ON UPDATE CASCADE', 'SELECT ''exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'material_requisitions' AND CONSTRAINT_NAME = 'fk_mat_req_original'); -SET @sql = IF(@fk = 0, 'ALTER TABLE material_requisitions ADD CONSTRAINT fk_mat_req_original FOREIGN KEY (original_requisition_id) REFERENCES material_requisitions (id) ON DELETE SET NULL ON UPDATE CASCADE', 'SELECT ''exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'material_requisition_items' AND CONSTRAINT_NAME = 'fk_mat_req_item_material'); -SET @sql = IF(@fk = 0, 'ALTER TABLE material_requisition_items ADD CONSTRAINT fk_mat_req_item_material FOREIGN KEY (material_id) REFERENCES inv_material (id) ON DELETE RESTRICT ON UPDATE CASCADE', 'SELECT ''exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'material_returns' AND CONSTRAINT_NAME = 'fk_mat_ret_work_order'); -SET @sql = IF(@fk = 0, 'ALTER TABLE material_returns ADD CONSTRAINT fk_mat_ret_work_order FOREIGN KEY (work_order_id) REFERENCES prd_work_order (id) ON DELETE SET NULL ON UPDATE CASCADE', 'SELECT ''exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'material_returns' AND CONSTRAINT_NAME = 'fk_mat_ret_requisition'); -SET @sql = IF(@fk = 0, 'ALTER TABLE material_returns ADD CONSTRAINT fk_mat_ret_requisition FOREIGN KEY (requisition_id) REFERENCES material_requisitions (id) ON DELETE SET NULL ON UPDATE CASCADE', 'SELECT ''exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'material_returns' AND CONSTRAINT_NAME = 'fk_mat_ret_warehouse'); -SET @sql = IF(@fk = 0, 'ALTER TABLE material_returns ADD CONSTRAINT fk_mat_ret_warehouse FOREIGN KEY (warehouse_id) REFERENCES inv_warehouse (id) ON DELETE SET NULL ON UPDATE CASCADE', 'SELECT ''exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'material_return_items' AND CONSTRAINT_NAME = 'fk_mat_ret_item_material'); -SET @sql = IF(@fk = 0, 'ALTER TABLE material_return_items ADD CONSTRAINT fk_mat_ret_item_material FOREIGN KEY (material_id) REFERENCES inv_material (id) ON DELETE RESTRICT ON UPDATE CASCADE', 'SELECT ''exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- ===== E. 成本模块(3 个业务 FK)===== - -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'work_order_costs' AND CONSTRAINT_NAME = 'fk_wo_cost_work_order'); -SET @sql = IF(@fk = 0, 'ALTER TABLE work_order_costs ADD CONSTRAINT fk_wo_cost_work_order FOREIGN KEY (work_order_id) REFERENCES prd_work_order (id) ON DELETE CASCADE ON UPDATE CASCADE', 'SELECT ''exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'material_batch_costs' AND CONSTRAINT_NAME = 'fk_mat_batch_cost_material'); -SET @sql = IF(@fk = 0, 'ALTER TABLE material_batch_costs ADD CONSTRAINT fk_mat_batch_cost_material FOREIGN KEY (material_id) REFERENCES inv_material (id) ON DELETE RESTRICT ON UPDATE CASCADE', 'SELECT ''exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'material_batch_costs' AND CONSTRAINT_NAME = 'fk_mat_batch_cost_warehouse'); -SET @sql = IF(@fk = 0, 'ALTER TABLE material_batch_costs ADD CONSTRAINT fk_mat_batch_cost_warehouse FOREIGN KEY (warehouse_id) REFERENCES inv_warehouse (id) ON DELETE SET NULL ON UPDATE CASCADE', 'SELECT ''exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- ===== F. 验证:4 模块业务 FK 总数 ===== -SELECT - CASE - WHEN TABLE_NAME LIKE 'sal_%' THEN '销售' - WHEN TABLE_NAME LIKE 'prd_%' THEN '生产' - WHEN TABLE_NAME LIKE 'material_%' THEN '领退料' - WHEN TABLE_NAME IN ('work_order_costs','material_batch_costs') THEN '成本' - END AS module, - COUNT(*) AS fk_count -FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS -WHERE TABLE_SCHEMA = DATABASE() - AND CONSTRAINT_TYPE = 'FOREIGN KEY' - AND ( - TABLE_NAME LIKE 'sal_%' OR TABLE_NAME LIKE 'prd_%' - OR TABLE_NAME LIKE 'material_%' - OR TABLE_NAME IN ('work_order_costs','material_batch_costs') - ) -GROUP BY module -ORDER BY module; diff --git a/database/migrations/033_add_system_customer_supplier_finance_quality_fk.sql b/database/migrations/033_add_system_customer_supplier_finance_quality_fk.sql deleted file mode 100644 index 2b5d602b..00000000 --- a/database/migrations/033_add_system_customer_supplier_finance_quality_fk.sql +++ /dev/null @@ -1,122 +0,0 @@ --- ============================================================ --- Migration 033: 批量 FK - 系统+客户+供应商+财务+质量(13 个) --- --- 背景:根据《项目整体分析报告》P0 #2(外键约束缺失) --- 补全 5 个模块的业务关联 FK --- --- 前置条件:Migration 021/028/029/030/031/032 已完成 --- --- 涉及模块: --- 系统(2 个):sys_user.department_id, sys_department.leader_id --- 客户(1 个):crm_customer.salesman_id --- 供应商(0 个):无业务 FK 缺失 --- 财务(8 个):fin_voucher, fin_voucher_line, fin_account_balance, --- fin_receivable, fin_payable --- 质量(2 个):qc_inspection.material_id, qc_inspection.inspector_id --- --- 排除项(留待 Migration 034): --- - 自引用 FK:fin_account.parent_id, sys_menu.parent_id, sys_department.parent_id --- - 审计字段 FK:所有 create_by/update_by → sys_user.id --- - 多态外键:source_id + source_type --- - 类型不一致:fin_voucher_line.department_id (INT UNSIGNED vs BIGINT UNSIGNED) --- - 无父表:fin_voucher_line.project_id --- --- 特殊说明: --- - fin_voucher.period_code 和 fin_account_balance.period_code 引用 fin_period.period_code (UNIQUE 键) --- - MySQL 支持引用 UNIQUE 键而非主键的 FK --- --- 幂等模式:所有 ALTER 用 INFORMATION_SCHEMA 守卫 --- ============================================================ - --- ===== A. 系统模块(2 个)===== - --- A1. sys_user.department_id → sys_department.id -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'sys_user' AND CONSTRAINT_NAME = 'fk_sys_user_department'); -SET @sql = IF(@fk = 0, 'ALTER TABLE sys_user ADD CONSTRAINT fk_sys_user_department FOREIGN KEY (department_id) REFERENCES sys_department (id) ON DELETE SET NULL ON UPDATE CASCADE', 'SELECT ''exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- A2. sys_department.leader_id → sys_user.id -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'sys_department' AND CONSTRAINT_NAME = 'fk_sys_department_leader'); -SET @sql = IF(@fk = 0, 'ALTER TABLE sys_department ADD CONSTRAINT fk_sys_department_leader FOREIGN KEY (leader_id) REFERENCES sys_user (id) ON DELETE SET NULL ON UPDATE CASCADE', 'SELECT ''exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- ===== B. 客户模块(1 个)===== - -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'crm_customer' AND CONSTRAINT_NAME = 'fk_crm_customer_salesman'); -SET @sql = IF(@fk = 0, 'ALTER TABLE crm_customer ADD CONSTRAINT fk_crm_customer_salesman FOREIGN KEY (salesman_id) REFERENCES sys_user (id) ON DELETE SET NULL ON UPDATE CASCADE', 'SELECT ''exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- ===== C. 财务模块(8 个)===== - --- C1. fin_voucher.period_code → fin_period.period_code(引用 UNIQUE 键) -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'fin_voucher' AND CONSTRAINT_NAME = 'fk_fin_voucher_period'); -SET @sql = IF(@fk = 0, 'ALTER TABLE fin_voucher ADD CONSTRAINT fk_fin_voucher_period FOREIGN KEY (period_code) REFERENCES fin_period (period_code) ON DELETE RESTRICT ON UPDATE CASCADE', 'SELECT ''exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- C2. fin_voucher_line.account_id → fin_account.id -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'fin_voucher_line' AND CONSTRAINT_NAME = 'fk_fin_voucher_line_account'); -SET @sql = IF(@fk = 0, 'ALTER TABLE fin_voucher_line ADD CONSTRAINT fk_fin_voucher_line_account FOREIGN KEY (account_id) REFERENCES fin_account (id) ON DELETE RESTRICT ON UPDATE CASCADE', 'SELECT ''exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- C3. fin_voucher_line.customer_id → crm_customer.id -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'fin_voucher_line' AND CONSTRAINT_NAME = 'fk_fin_voucher_line_customer'); -SET @sql = IF(@fk = 0, 'ALTER TABLE fin_voucher_line ADD CONSTRAINT fk_fin_voucher_line_customer FOREIGN KEY (customer_id) REFERENCES crm_customer (id) ON DELETE SET NULL ON UPDATE CASCADE', 'SELECT ''exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- C4. fin_voucher_line.supplier_id → pur_supplier.id -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'fin_voucher_line' AND CONSTRAINT_NAME = 'fk_fin_voucher_line_supplier'); -SET @sql = IF(@fk = 0, 'ALTER TABLE fin_voucher_line ADD CONSTRAINT fk_fin_voucher_line_supplier FOREIGN KEY (supplier_id) REFERENCES pur_supplier (id) ON DELETE SET NULL ON UPDATE CASCADE', 'SELECT ''exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- C5. fin_account_balance.account_id → fin_account.id -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'fin_account_balance' AND CONSTRAINT_NAME = 'fk_fin_account_balance_account'); -SET @sql = IF(@fk = 0, 'ALTER TABLE fin_account_balance ADD CONSTRAINT fk_fin_account_balance_account FOREIGN KEY (account_id) REFERENCES fin_account (id) ON DELETE RESTRICT ON UPDATE CASCADE', 'SELECT ''exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- C6. fin_account_balance.period_code → fin_period.period_code(引用 UNIQUE 键) -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'fin_account_balance' AND CONSTRAINT_NAME = 'fk_fin_account_balance_period'); -SET @sql = IF(@fk = 0, 'ALTER TABLE fin_account_balance ADD CONSTRAINT fk_fin_account_balance_period FOREIGN KEY (period_code) REFERENCES fin_period (period_code) ON DELETE RESTRICT ON UPDATE CASCADE', 'SELECT ''exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- C7. fin_receivable.customer_id → crm_customer.id -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'fin_receivable' AND CONSTRAINT_NAME = 'fk_fin_receivable_customer'); -SET @sql = IF(@fk = 0, 'ALTER TABLE fin_receivable ADD CONSTRAINT fk_fin_receivable_customer FOREIGN KEY (customer_id) REFERENCES crm_customer (id) ON DELETE RESTRICT ON UPDATE CASCADE', 'SELECT ''exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- C8. fin_payable.supplier_id → pur_supplier.id -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'fin_payable' AND CONSTRAINT_NAME = 'fk_fin_payable_supplier'); -SET @sql = IF(@fk = 0, 'ALTER TABLE fin_payable ADD CONSTRAINT fk_fin_payable_supplier FOREIGN KEY (supplier_id) REFERENCES pur_supplier (id) ON DELETE RESTRICT ON UPDATE CASCADE', 'SELECT ''exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- ===== D. 质量模块(2 个)===== - --- D1. qc_inspection.material_id → inv_material.id -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'qc_inspection' AND CONSTRAINT_NAME = 'fk_qc_inspection_material'); -SET @sql = IF(@fk = 0, 'ALTER TABLE qc_inspection ADD CONSTRAINT fk_qc_inspection_material FOREIGN KEY (material_id) REFERENCES inv_material (id) ON DELETE SET NULL ON UPDATE CASCADE', 'SELECT ''exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- D2. qc_inspection.inspector_id → sys_user.id -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'qc_inspection' AND CONSTRAINT_NAME = 'fk_qc_inspection_inspector'); -SET @sql = IF(@fk = 0, 'ALTER TABLE qc_inspection ADD CONSTRAINT fk_qc_inspection_inspector FOREIGN KEY (inspector_id) REFERENCES sys_user (id) ON DELETE SET NULL ON UPDATE CASCADE', 'SELECT ''exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- ===== E. 验证:5 模块 FK 总数 ===== -SELECT - CASE - WHEN TABLE_NAME LIKE 'sys_%' THEN '系统' - WHEN TABLE_NAME LIKE 'crm_%' THEN '客户' - WHEN TABLE_NAME LIKE 'pur_supplier' THEN '供应商' - WHEN TABLE_NAME LIKE 'fin_%' THEN '财务' - WHEN TABLE_NAME LIKE 'qc_%' THEN '质量' - END AS module, - COUNT(*) AS fk_count -FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS -WHERE TABLE_SCHEMA = DATABASE() - AND CONSTRAINT_TYPE = 'FOREIGN KEY' - AND ( - TABLE_NAME LIKE 'sys_%' OR TABLE_NAME LIKE 'crm_%' - OR TABLE_NAME = 'pur_supplier' - OR TABLE_NAME LIKE 'fin_%' OR TABLE_NAME LIKE 'qc_%' - ) -GROUP BY module -ORDER BY module; diff --git a/database/migrations/034_self_ref_fk_code_adapt_validation.sql b/database/migrations/034_self_ref_fk_code_adapt_validation.sql deleted file mode 100644 index 9ce05b51..00000000 --- a/database/migrations/034_self_ref_fk_code_adapt_validation.sql +++ /dev/null @@ -1,117 +0,0 @@ --- ============================================================ --- Migration 034: 自引用 FK + 代码适配 + 全量验证 --- --- 背景:根据《项目整体分析报告》P0 #2(外键约束缺失) --- 补全 3 个自引用 FK(sys_department, sys_menu, inv_material_category) --- 完成代码适配检查和全量验证 --- --- 前置条件:Migration 026-033 已完成 --- --- 操作步骤: --- A. 自引用 FK 数据清洗(parent_id = 0 改为 NULL) --- B. 自引用 FK 列定义修改(DEFAULT 0 → DEFAULT NULL) --- C. 添加 3 个自引用 FK(ON DELETE RESTRICT 防止误删父级) --- D. 代码适配验证(tsc + vitest) --- --- 审计字段 FK(27 个 create_by/update_by → sys_user.id)说明: --- 这些 FK 优先级较低,需先补 18 个索引,工作量大且风险低 --- 建议在后续 Sprint 中单独处理(Migration 035) --- --- 幂等模式:所有 ALTER 用 INFORMATION_SCHEMA 守卫 --- ============================================================ - --- ===== A. 数据清洗:parent_id = 0 改为 NULL ===== - --- A1. sys_department.parent_id -UPDATE sys_department SET parent_id = NULL WHERE parent_id = 0; - --- A2. sys_menu.parent_id -UPDATE sys_menu SET parent_id = NULL WHERE parent_id = 0; - --- A3. inv_material_category.parent_id -UPDATE inv_material_category SET parent_id = NULL WHERE parent_id = 0; - --- ===== B. 列定义修改:DEFAULT 0 → DEFAULT NULL ===== - --- B1. sys_department.parent_id -SET @curr = (SELECT COLUMN_DEFAULT FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'sys_department' AND COLUMN_NAME = 'parent_id'); -SET @sql = IF(@curr = '0', - 'ALTER TABLE sys_department MODIFY COLUMN parent_id BIGINT UNSIGNED DEFAULT NULL COMMENT ''父部门ID, NULL为顶级部门''', - 'SELECT ''sys_department.parent_id default already null'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- B2. sys_menu.parent_id -SET @curr = (SELECT COLUMN_DEFAULT FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'sys_menu' AND COLUMN_NAME = 'parent_id'); -SET @sql = IF(@curr = '0', - 'ALTER TABLE sys_menu MODIFY COLUMN parent_id BIGINT UNSIGNED DEFAULT NULL COMMENT ''父菜单ID, NULL为顶级菜单''', - 'SELECT ''sys_menu.parent_id default already null'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- B3. inv_material_category.parent_id -SET @curr = (SELECT COLUMN_DEFAULT FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_material_category' AND COLUMN_NAME = 'parent_id'); -SET @sql = IF(@curr = '0', - 'ALTER TABLE inv_material_category MODIFY COLUMN parent_id BIGINT UNSIGNED DEFAULT NULL COMMENT ''父分类ID, NULL为顶级分类''', - 'SELECT ''inv_material_category.parent_id default already null'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- ===== C. 添加自引用 FK(3 个,ON DELETE RESTRICT)===== - --- C1. sys_department.parent_id → sys_department.id -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'sys_department' AND CONSTRAINT_NAME = 'fk_sys_department_parent'); -SET @sql = IF(@fk = 0, - 'ALTER TABLE sys_department ADD CONSTRAINT fk_sys_department_parent FOREIGN KEY (parent_id) REFERENCES sys_department (id) ON DELETE RESTRICT ON UPDATE CASCADE', - 'SELECT ''fk_sys_department_parent already exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- C2. sys_menu.parent_id → sys_menu.id -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'sys_menu' AND CONSTRAINT_NAME = 'fk_sys_menu_parent'); -SET @sql = IF(@fk = 0, - 'ALTER TABLE sys_menu ADD CONSTRAINT fk_sys_menu_parent FOREIGN KEY (parent_id) REFERENCES sys_menu (id) ON DELETE RESTRICT ON UPDATE CASCADE', - 'SELECT ''fk_sys_menu_parent already exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- C3. inv_material_category.parent_id → inv_material_category.id -SET @idx = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_material_category' AND INDEX_NAME = 'idx_parent'); -SET @sql = IF(@idx = 0, 'ALTER TABLE inv_material_category ADD INDEX idx_parent (parent_id)', 'SELECT ''idx_parent exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_material_category' AND CONSTRAINT_NAME = 'fk_material_category_parent'); -SET @sql = IF(@fk = 0, - 'ALTER TABLE inv_material_category ADD CONSTRAINT fk_material_category_parent FOREIGN KEY (parent_id) REFERENCES inv_material_category (id) ON DELETE RESTRICT ON UPDATE CASCADE', - 'SELECT ''fk_material_category_parent already exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- ===== D. 验证:自引用 FK 已添加 ===== -SELECT - TABLE_NAME, CONSTRAINT_NAME, REFERENCED_TABLE_NAME -FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE -WHERE TABLE_SCHEMA = DATABASE() - AND REFERENCED_TABLE_NAME IS NOT NULL - AND CONSTRAINT_NAME IN ('fk_sys_department_parent', 'fk_sys_menu_parent', 'fk_material_category_parent') -ORDER BY TABLE_NAME; - --- ===== E. Sprint 3 完成:统计 FK 总数 ===== -SELECT - 'Sprint 3 FK 总数' AS metric, - COUNT(*) AS value -FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS -WHERE TABLE_SCHEMA = DATABASE() - AND CONSTRAINT_TYPE = 'FOREIGN KEY'; - --- ===== 代码适配验证说明 ===== --- 1. TypeScript 代码使用 number 类型接收所有 ID 字段,BIGINT UNSIGNED 在 JS 中仍为 number(安全范围 2^53) --- 2. Migration 027/028 修改的主键类型(INT → BIGINT UNSIGNED)不影响代码逻辑 --- 3. Migration 029 的类型对齐(inv_inbound_item.order_id/material_id, pur_purchase_order.supplier_id, pur_purchase_order_line.material_id) --- 仅影响 DB 层,代码中这些列已用 number 类型接收 --- 4. 全量验证:tsc --noEmit + vitest run 需在应用此 migration 后执行 - --- ===== 后续工作(Migration 035,留待 Sprint 4)===== --- 1. 审计字段 FK(27 个 create_by/update_by → sys_user.id) --- - 需先补 18 个索引(sal_order/sal_delivery/sal_return/sal_reconciliation/prd_work_order/prd_bom 等) --- - 优先级低,建议在 Sprint 4 处理 --- 2. vnerpdacahng_schema.sql 主 schema 文件同步 --- - 反映 Migration 022-034 的所有变更 --- - 建议通过 mysqldump 重新导出 schema 替代手动修改 diff --git a/database/migrations/035_create_sys_notification.sql b/database/migrations/035_create_sys_notification.sql deleted file mode 100644 index db3de2c2..00000000 --- a/database/migrations/035_create_sys_notification.sql +++ /dev/null @@ -1,20 +0,0 @@ --- ===================================================== --- Migration 035: Create sys_notification table --- 异地登录告警等系统通知需要此表,login 路由的 INSERT INTO sys_notification --- 因表不存在而静默失败。补建该表以恢复通知功能。 --- ===================================================== - -CREATE TABLE IF NOT EXISTS sys_notification ( - id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY COMMENT '通知ID', - type VARCHAR(30) NOT NULL COMMENT '通知类型:inventory_alert/system/task/security等', - title VARCHAR(200) NOT NULL COMMENT '通知标题', - content TEXT COMMENT '通知内容', - user_id BIGINT UNSIGNED COMMENT '接收用户ID(空表示广播)', - is_read TINYINT DEFAULT 0 COMMENT '是否已读:0未读 1已读', - read_time DATETIME COMMENT '阅读时间', - create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - INDEX idx_user (user_id), - INDEX idx_type (type), - INDEX idx_read (is_read), - INDEX idx_create_time (create_time) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='系统通知'; diff --git a/database/migrations/036_fix_org_role_inbound_label_schema.sql b/database/migrations/036_fix_org_role_inbound_label_schema.sql deleted file mode 100644 index cf74b749..00000000 --- a/database/migrations/036_fix_org_role_inbound_label_schema.sql +++ /dev/null @@ -1,93 +0,0 @@ --- Migration 036: Fix schema mismatches causing 500 errors on --- /api/organization?type=company (sys_company missing columns) --- /api/system/roles (sys_role missing parent_id, inherit_mode) --- /api/warehouse/inbound/labels (inv_inbound_label table missing) --- --- All tables use utf8mb4_0900_ai_ci per project convention. - --- ============================================================ --- 1. sys_company: add columns expected by organization route --- Existing columns: company_name, company_code, address, phone --- Code expects: full_name, short_name, code, legal_person, --- reg_address, contact_phone, tax_no, bank_name, --- bank_account, website, fax, postcode, description --- Strategy: add new columns, copy data from old ones, keep old columns --- ============================================================ - --- Add missing columns (IF NOT EXISTS not supported in MySQL 8 ADD COLUMN) -ALTER TABLE sys_company - ADD COLUMN full_name VARCHAR(200) NULL AFTER id, - ADD COLUMN short_name VARCHAR(100) NULL, - ADD COLUMN code VARCHAR(50) NULL, - ADD COLUMN legal_person VARCHAR(50) NULL, - ADD COLUMN reg_address VARCHAR(500) NULL, - ADD COLUMN contact_phone VARCHAR(50) NULL, - ADD COLUMN tax_no VARCHAR(50) NULL, - ADD COLUMN bank_name VARCHAR(100) NULL, - ADD COLUMN bank_account VARCHAR(100) NULL, - ADD COLUMN website VARCHAR(200) NULL, - ADD COLUMN fax VARCHAR(50) NULL, - ADD COLUMN postcode VARCHAR(20) NULL, - ADD COLUMN description TEXT NULL; - --- Migrate data from old columns to new ones (only where new is NULL) -UPDATE sys_company SET full_name = company_name WHERE full_name IS NULL AND company_name IS NOT NULL; -UPDATE sys_company SET code = company_code WHERE code IS NULL AND company_code IS NOT NULL; -UPDATE sys_company SET reg_address = address WHERE reg_address IS NULL AND address IS NOT NULL; -UPDATE sys_company SET contact_phone = phone WHERE contact_phone IS NULL AND phone IS NOT NULL; - --- ============================================================ --- 2. sys_role: add parent_id and inherit_mode for role hierarchy --- ============================================================ - -ALTER TABLE sys_role - ADD COLUMN parent_id INT NULL DEFAULT NULL COMMENT '父角色ID(角色继承)', - ADD COLUMN inherit_mode VARCHAR(20) NOT NULL DEFAULT 'merge' COMMENT '权限继承模式: merge(合并) | override(覆盖)'; - --- Add index for parent_id lookups -CREATE INDEX idx_sys_role_parent_id ON sys_role(parent_id); - --- ============================================================ --- 3. inv_inbound_label: create table for warehouse inbound labels --- ============================================================ - -CREATE TABLE IF NOT EXISTS inv_inbound_label ( - id BIGINT AUTO_INCREMENT PRIMARY KEY, - label_id VARCHAR(50) NOT NULL COMMENT '标签唯一编号', - order_id BIGINT NULL COMMENT '入库单ID', - order_no VARCHAR(50) NULL COMMENT '入库单号', - item_id BIGINT NULL COMMENT '入库明细ID', - purchase_order_no VARCHAR(50) NULL COMMENT '采购单号', - supplier_name VARCHAR(200) NULL COMMENT '供应商名称', - inbound_date DATE NULL COMMENT '入库日期', - warehouse_code VARCHAR(50) NULL COMMENT '仓库编码', - material_code VARCHAR(50) NOT NULL COMMENT '物料编码', - material_name VARCHAR(200) NOT NULL COMMENT '物料名称', - specification VARCHAR(200) NULL COMMENT '规格', - width DECIMAL(18,4) NULL DEFAULT 0 COMMENT '幅宽', - batch_no VARCHAR(50) NULL COMMENT '批次号', - qty DECIMAL(18,4) NOT NULL DEFAULT 0 COMMENT '数量', - unit VARCHAR(20) NOT NULL DEFAULT '件' COMMENT '单位', - is_raw_material TINYINT NOT NULL DEFAULT 0 COMMENT '是否原料: 0=否, 1=是', - package_qty INT NULL DEFAULT 0 COMMENT '包装数量', - label_qty INT NULL DEFAULT 1 COMMENT '标签数量', - label_status VARCHAR(20) NOT NULL DEFAULT 'generated' COMMENT '标签状态: generated|used|split|void', - audit_status TINYINT NULL DEFAULT 0 COMMENT '审核状态: 0=未审核, 1=已审核', - operator_id BIGINT NULL COMMENT '操作员ID', - operator_name VARCHAR(50) NULL COMMENT '操作员姓名', - auditor_id BIGINT NULL COMMENT '审核员ID', - auditor_name VARCHAR(50) NULL COMMENT '审核员姓名', - audit_time DATETIME NULL COMMENT '审核时间', - color_code VARCHAR(50) NULL COMMENT '色号', - mixed_material_remark VARCHAR(500) NULL COMMENT '混料备注', - machine_no VARCHAR(50) NULL COMMENT '机台号', - remark VARCHAR(500) NULL COMMENT '备注', - deleted TINYINT NOT NULL DEFAULT 0 COMMENT '软删除: 0=正常, 1=已删除', - create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - UNIQUE KEY uk_label_id (label_id), - KEY idx_order_no (order_no), - KEY idx_material_code (material_code), - KEY idx_batch_no (batch_no), - KEY idx_label_status (label_status) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='入库物料标签'; diff --git a/database/migrations/037_migrate_purchase_order_status.sql b/database/migrations/037_migrate_purchase_order_status.sql deleted file mode 100644 index 828c6765..00000000 --- a/database/migrations/037_migrate_purchase_order_status.sql +++ /dev/null @@ -1,36 +0,0 @@ --- Migration 037: Migrate historical purchase order status=2 to status=10 (draft) --- --- Background: pur_purchase_order contained 66 rows with legacy status=2 (test seed data --- from 2026-07-07). The domain value object PurchaseOrderStatus only recognizes codes --- 10/20/30/40/50/90. The repository now degrades unknown codes to 'draft' defensively, --- but the data should be corrected at the source. --- --- status=2 had no domain meaning (likely a legacy "pending" code from an older schema). --- Mapping to 10 (draft) is the safest choice: draft is editable and can transition to --- any other status, so no business logic is bypassed. --- --- This migration is idempotent: running it again affects 0 rows. - --- 1. Show counts before migration (for verification) -SELECT - status, - COUNT(*) AS cnt -FROM pur_purchase_order -WHERE deleted = 0 -GROUP BY status -ORDER BY status; - --- 2. Migrate legacy status=2 to status=10 (draft) -UPDATE pur_purchase_order -SET status = 10, - update_time = NOW() -WHERE status = 2 AND deleted = 0; - --- 3. Show counts after migration (for verification) -SELECT - status, - COUNT(*) AS cnt -FROM pur_purchase_order -WHERE deleted = 0 -GROUP BY status -ORDER BY status; diff --git a/database/migrations/038_create_sys_calc_param.sql b/database/migrations/038_create_sys_calc_param.sql deleted file mode 100644 index 2087b864..00000000 --- a/database/migrations/038_create_sys_calc_param.sql +++ /dev/null @@ -1,57 +0,0 @@ --- ===================================================== --- Migration 038: Create sys_calc_param table --- 统一管理 ERP 全模块硬编码计算参数(标准费率、材料占比、制造费用率、 --- MRP 提前期、排程工作时段、质量合格率阈值、网版磨损系数等)。 --- 消除"最大技术债务:硬编码参数散布在多处常量中"的风险。 --- ===================================================== - -CREATE TABLE IF NOT EXISTS `sys_calc_param` ( - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '参数ID', - `category` VARCHAR(50) NOT NULL COMMENT '分类: mrp/cost/schedule/qc/screen_plate/printing', - `param_key` VARCHAR(100) NOT NULL COMMENT '参数键 (如 mrp.default_lead_time_days)', - `param_value` VARCHAR(500) NOT NULL COMMENT '参数值 (字符串存储,由服务层转型)', - `value_type` ENUM('int','decimal','boolean','string') NOT NULL DEFAULT 'string' COMMENT '值类型', - `default_value` VARCHAR(500) COMMENT '代码内置默认值(用于 DB 不可用时兜底)', - `description` VARCHAR(500) COMMENT '参数说明', - `status` TINYINT NOT NULL DEFAULT 1 COMMENT '0-禁用 1-启用', - `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - `update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', - `create_by` BIGINT UNSIGNED DEFAULT NULL COMMENT '创建人ID', - `update_by` BIGINT UNSIGNED DEFAULT NULL COMMENT '更新人ID', - `deleted` TINYINT NOT NULL DEFAULT 0 COMMENT '软删除: 0-正常 1-已删除', - PRIMARY KEY (`id`), - UNIQUE KEY `uk_param_key` (`param_key`), - KEY `idx_category` (`category`), - KEY `idx_status` (`status`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='计算参数配置表'; - --- ===================================================== --- 种子数据:全量初始化 15 个核心计算参数 --- ===================================================== - -INSERT INTO `sys_calc_param` (`category`, `param_key`, `param_value`, `value_type`, `default_value`, `description`) VALUES --- MRP 模块 -('mrp', 'mrp.default_lead_time_days', '7', 'int', '7', 'MRP 默认采购提前期(天),当 inv_material 无 lead_time_days 时兜底'), --- 成本核算模块 -('cost', 'cost.standard_labor_rate', '50', 'decimal', '50', '标准人工费率(元/小时),用于成本差异分析'), -('cost', 'cost.standard_efficiency_per_hour', '500', 'decimal', '500', '标准生产效率(件/小时),用于标准工时计算'), -('cost', 'cost.material_cost_ratio', '0.45', 'decimal', '0.45', '材料成本占售价比例,用于估算标准材料成本'), -('cost', 'cost.overhead_rate', '0.15', 'decimal', '0.15', '制造费用率(占材料成本比例),用于制造费用估算'), -('cost', 'cost.manufacturing_cost_ratio', '0.5', 'decimal', '0.5', '简化制造费用系数(占人工成本比例),API 手动计算路径使用'), --- 生产排程模块 -('schedule', 'schedule.working_hours_per_day', '8', 'int', '8', '每日工作时长(小时),排程引擎使用'), -('schedule', 'schedule.work_start_hour', '8', 'int', '8', '工作开始时间(24小时制),排程引擎使用'), -('schedule', 'schedule.search_days_ahead', '30', 'int', '30', '排程搜索范围(天),向前搜索可用时间槽'), -('schedule', 'schedule.default_setup_time_minutes', '30', 'int', '30', '默认换模时间(分钟),排程间隔'), -('schedule', 'schedule.default_max_colors', '4', 'int', '4', '设备默认最大色数,排程匹配使用'), --- 质量管理模块 -('qc', 'qc.pass_rate_threshold', '98', 'decimal', '98', '来料检验合格率阈值(%),≥此值判定合格'), --- 网版成本模块 -('screen_plate','screen_plate.wear_cost_ratio', '0.1', 'decimal', '0.1', '网版磨损成本系数,磨损成本 = 采购价 × 寿命比 × 此系数'), --- 多色印刷模块 -('printing', 'printing.default_loss_rate', '0.15', 'decimal', '0.15', '油墨默认损耗率(15%),配方损耗兜底值'), --- 采购模块 -('pur', 'pur.default_tax_rate', '13', 'decimal', '13', '采购订单默认税率(%),行级税率兜底'), --- 销售模块 -('sales', 'sales.default_payment_days', '30', 'int', '30', '销售应收默认账期(天),应收单生成时使用') -ON DUPLICATE KEY UPDATE `update_time` = CURRENT_TIMESTAMP; diff --git a/database/migrations/039_add_sales_tax_discount_fields.sql b/database/migrations/039_add_sales_tax_discount_fields.sql deleted file mode 100644 index bf056ec4..00000000 --- a/database/migrations/039_add_sales_tax_discount_fields.sql +++ /dev/null @@ -1,15 +0,0 @@ --- ===================================================== --- Migration 039: Add tax and discount fields to sales order --- 补齐销售订单与采购订单对称的税额/折扣率字段。 --- sal_order 表已有 tax_amount/total_with_tax/discount_amount,缺 tax_rate 和 discount_rate --- sal_order_detail 表已有 tax_rate/tax_amount/total_amount,完整无需修改 --- ===================================================== - --- sal_order 主表添加 tax_rate 和 discount_rate -ALTER TABLE `sal_order` - ADD COLUMN IF NOT EXISTS `tax_rate` DECIMAL(18,4) DEFAULT 0 COMMENT '订单税率(%)' AFTER `tax_amount`, - ADD COLUMN IF NOT EXISTS `discount_rate` DECIMAL(18,4) DEFAULT 0 COMMENT '折扣率(%)' AFTER `discount_amount`; - --- sal_order_detail 表确认已有字段(无需修改,仅注释说明) --- 已有: tax_rate, tax_amount, total_amount(含税行合计) --- sal_order_detail.tax_rate 默认 0(由领域模型 SalesOrderLine.create 填充) diff --git a/database/migrations/040_add_aql_sampling_fields.sql b/database/migrations/040_add_aql_sampling_fields.sql deleted file mode 100644 index 3534fc69..00000000 --- a/database/migrations/040_add_aql_sampling_fields.sql +++ /dev/null @@ -1,91 +0,0 @@ --- ===================================================== --- Migration 040: Add AQL sampling fields to qc_incoming_inspection --- 补齐来料检验 AQL 抽样方案字段,支持 GB/T 2828.1 标准。 --- 当前为全检模式,本次新增抽样计算所需字段但不破坏现有逻辑。 --- ===================================================== - -ALTER TABLE `qc_incoming_inspection` - ADD COLUMN IF NOT EXISTS `sample_size` INT DEFAULT NULL COMMENT 'AQL抽样样本量' AFTER `quantity`, - ADD COLUMN IF NOT EXISTS `accept_qty` INT DEFAULT NULL COMMENT '合格判定数(Ac)' AFTER `sample_size`, - ADD COLUMN IF NOT EXISTS `reject_qty` INT DEFAULT NULL COMMENT '不合格判定数(Re)' AFTER `accept_qty`, - ADD COLUMN IF NOT EXISTS `aql_level` VARCHAR(10) DEFAULT NULL COMMENT 'AQL水平: 0.65/1.0/1.5/2.5/4.0' AFTER `reject_qty`, - ADD COLUMN IF NOT EXISTS `inspection_standard` VARCHAR(50) DEFAULT NULL COMMENT '检验标准: GB/T 2828.1' AFTER `aql_level`; - --- ===================================================== --- AQL 抽样方案参考表 (GB/T 2828.1 单次正常抽样) --- ===================================================== -CREATE TABLE IF NOT EXISTS `qc_aql_sampling_plan` ( - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'ID', - `lot_size_min` INT NOT NULL COMMENT '批量下限', - `lot_size_max` INT NOT NULL COMMENT '批量上限', - `sample_size_code` VARCHAR(5) NOT NULL COMMENT '样本量字码: A,B,C,D,E,F,G,H,J,K,L', - `sample_size` INT NOT NULL COMMENT '样本量 n', - `aql_level` VARCHAR(10) NOT NULL COMMENT 'AQL水平: 0.65/1.0/1.5/2.5/4.0', - `accept_qty` INT NOT NULL COMMENT '合格判定数 Ac', - `reject_qty` INT NOT NULL COMMENT '不合格判定数 Re', - `inspection_level` VARCHAR(10) NOT NULL DEFAULT 'II' COMMENT '检验水平: I/II/III', - `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - PRIMARY KEY (`id`), - UNIQUE KEY `uk_plan` (`lot_size_min`, `lot_size_max`, `aql_level`, `inspection_level`), - KEY `idx_aql_level` (`aql_level`), - KEY `idx_lot_size` (`lot_size_min`, `lot_size_max`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='AQL抽样方案表(GB/T 2828.1)'; - --- ===================================================== --- 种子数据:GB/T 2828.1 正常检验单次抽样方案 (检验水平 II) --- 常用 AQL 水平: 0.65, 1.0, 1.5, 2.5, 4.0 --- ===================================================== -INSERT INTO `qc_aql_sampling_plan` (`lot_size_min`, `lot_size_max`, `sample_size_code`, `sample_size`, `aql_level`, `accept_qty`, `reject_qty`, `inspection_level`) VALUES --- 批量 2~8 (字码 A, n=2) -(2, 8, 'A', 2, '4.0', 0, 1, 'II'), --- 批量 9~15 (字码 B, n=3) -(9, 15, 'B', 3, '4.0', 0, 1, 'II'), --- 批量 16~25 (字码 C, n=5) -(16, 25, 'C', 5, '4.0', 0, 1, 'II'), -(16, 25, 'C', 5, '2.5', 0, 1, 'II'), --- 批量 26~50 (字码 D, n=8) -(26, 50, 'D', 8, '4.0', 1, 2, 'II'), -(26, 50, 'D', 8, '2.5', 1, 2, 'II'), -(26, 50, 'D', 8, '1.5', 0, 1, 'II'), --- 批量 51~90 (字码 E, n=13) -(51, 90, 'E', 13, '4.0', 1, 2, 'II'), -(51, 90, 'E', 13, '2.5', 1, 2, 'II'), -(51, 90, 'E', 13, '1.5', 1, 2, 'II'), -(51, 90, 'E', 13, '1.0', 0, 1, 'II'), --- 批量 91~150 (字码 F, n=20) -(91, 150, 'F', 20, '4.0', 2, 3, 'II'), -(91, 150, 'F', 20, '2.5', 1, 2, 'II'), -(91, 150, 'F', 20, '1.5', 1, 2, 'II'), -(91, 150, 'F', 20, '1.0', 1, 2, 'II'), -(91, 150, 'F', 20, '0.65', 0, 1, 'II'), --- 批量 151~280 (字码 G, n=32) -(151, 280, 'G', 32, '4.0', 3, 4, 'II'), -(151, 280, 'G', 32, '2.5', 2, 3, 'II'), -(151, 280, 'G', 32, '1.5', 1, 2, 'II'), -(151, 280, 'G', 32, '1.0', 1, 2, 'II'), -(151, 280, 'G', 32, '0.65', 1, 2, 'II'), --- 批量 281~500 (字码 H, n=50) -(281, 500, 'H', 50, '4.0', 5, 6, 'II'), -(281, 500, 'H', 50, '2.5', 3, 4, 'II'), -(281, 500, 'H', 50, '1.5', 2, 3, 'II'), -(281, 500, 'H', 50, '1.0', 1, 2, 'II'), -(281, 500, 'H', 50, '0.65', 1, 2, 'II'), --- 批量 501~1200 (字码 J, n=80) -(501, 1200, 'J', 80, '4.0', 7, 8, 'II'), -(501, 1200, 'J', 80, '2.5', 5, 6, 'II'), -(501, 1200, 'J', 80, '1.5', 3, 4, 'II'), -(501, 1200, 'J', 80, '1.0', 2, 3, 'II'), -(501, 1200, 'J', 80, '0.65', 1, 2, 'II'), --- 批量 1201~3200 (字码 K, n=125) -(1201, 3200, 'K', 125, '4.0', 10, 11, 'II'), -(1201, 3200, 'K', 125, '2.5', 7, 8, 'II'), -(1201, 3200, 'K', 125, '1.5', 5, 6, 'II'), -(1201, 3200, 'K', 125, '1.0', 3, 4, 'II'), -(1201, 3200, 'K', 125, '0.65', 2, 3, 'II'), --- 批量 3201~10000 (字码 L, n=200) -(3201, 10000, 'L', 200, '4.0', 14, 15, 'II'), -(3201, 10000, 'L', 200, '2.5', 10, 11, 'II'), -(3201, 10000, 'L', 200, '1.5', 7, 8, 'II'), -(3201, 10000, 'L', 200, '1.0', 5, 6, 'II'), -(3201, 10000, 'L', 200, '0.65', 3, 4, 'II') -ON DUPLICATE KEY UPDATE `update_time` = CURRENT_TIMESTAMP; diff --git a/database/migrations/041_unify_bom_tables.sql b/database/migrations/041_unify_bom_tables.sql deleted file mode 100644 index 71ee1fbb..00000000 --- a/database/migrations/041_unify_bom_tables.sql +++ /dev/null @@ -1,150 +0,0 @@ --- ===================================================== --- Migration 041: Create unified BOM view + migrate V1 data to V2 --- 统一 BOM 表结构:创建兼容视图,将 V1 (prd_bom/prd_bom_detail) 数据迁移至 V2 (bom_header/bom_line) --- V2 表字段更完整(审核、层级、工序关联),作为权威实现 --- V1 数据迁移后保留只读,通过视图统一访问 --- ===================================================== - --- 1. 将 V1 数据迁移到 V2(INSERT IGNORE 避免重复) -INSERT IGNORE INTO `bom_header` ( - bom_no, product_id, product_code, product_name, - version, is_default, status, total_cost, remark, create_time -) -SELECT - CONCAT('V1-', pb.id) AS bom_no, - pb.product_id, - (SELECT material_code FROM inv_material WHERE id = pb.product_id LIMIT 1) AS product_code, - (SELECT material_name FROM inv_material WHERE id = pb.product_id LIMIT 1) AS product_name, - pb.version, - 1 AS is_default, - CASE WHEN pb.status = 1 THEN 20 ELSE 10 END AS status, -- V1 1=启用→V2 20=已审核 - pb.total_cost, - pb.remark, - pb.create_time -FROM `prd_bom` pb -WHERE pb.deleted = 0 - AND pb.id NOT IN (SELECT legacy_id FROM bom_header WHERE legacy_source = 'prd_bom'); - --- 添加 legacy 追踪列(如果不存在) -ALTER TABLE `bom_header` - ADD COLUMN IF NOT EXISTS `legacy_source` VARCHAR(30) DEFAULT NULL COMMENT '旧表来源: prd_bom/bom_header' AFTER `remark`, - ADD COLUMN IF NOT EXISTS `legacy_id` BIGINT UNSIGNED DEFAULT NULL COMMENT '旧表原始ID' AFTER `legacy_source`; - --- 更新已迁移记录的 legacy 信息 -UPDATE `bom_header` bh -INNER JOIN `prd_bom` pb ON bh.bom_no = CONCAT('V1-', pb.id) -SET bh.legacy_source = 'prd_bom', bh.legacy_id = pb.id -WHERE bh.legacy_source IS NULL; - --- 2. 迁移 V1 明细到 V2 明细 -INSERT IGNORE INTO `bom_line` ( - bom_id, line_no, material_id, material_code, material_name, - material_spec, material_unit, consumption_qty, loss_rate, - unit_cost, total_cost, remark, create_time -) -SELECT - bh.id AS bom_id, - pbd.id AS line_no, - pbd.material_id, - (SELECT material_code FROM inv_material WHERE id = pbd.material_id LIMIT 1) AS material_code, - pbd.material_name, - NULL AS material_spec, - pbd.unit AS material_unit, - pbd.quantity AS consumption_qty, - pbd.loss_rate, - pbd.unit_cost, - pbd.total_cost, - pbd.remark, - pbd.create_time -FROM `prd_bom_detail` pbd -INNER JOIN `prd_bom` pb ON pbd.bom_id = pb.id -INNER JOIN `bom_header` bh ON bh.bom_no = CONCAT('V1-', pb.id) -WHERE pb.deleted = 0 - AND pbd.id NOT IN (SELECT legacy_id FROM bom_line WHERE legacy_source = 'prd_bom_detail'); - --- 添加 legacy 追踪列到明细表 -ALTER TABLE `bom_line` - ADD COLUMN IF NOT EXISTS `legacy_source` VARCHAR(30) DEFAULT NULL COMMENT '旧表来源' AFTER `remark`, - ADD COLUMN IF NOT EXISTS `legacy_id` BIGINT UNSIGNED DEFAULT NULL COMMENT '旧表原始ID' AFTER `legacy_source`; - --- 3. 创建统一 BOM 视图(V1 + V2 合并查询) -CREATE OR REPLACE VIEW `v_bom_unified` AS -SELECT - bh.id AS bom_id, - bh.bom_no, - bh.product_id, - bh.product_code, - bh.product_name, - bh.version, - bh.status, - bh.is_default, - bh.total_cost, - bh.remark, - bh.create_time, - bh.legacy_source, - bh.legacy_id, - 'v2' AS source_table -FROM `bom_header` bh -WHERE bh.deleted = 0 - -UNION ALL - -SELECT - pb.id AS bom_id, - CONCAT('V1-', pb.id) AS bom_no, - pb.product_id, - (SELECT material_code FROM inv_material WHERE id = pb.product_id LIMIT 1) AS product_code, - (SELECT material_name FROM inv_material WHERE id = pb.product_id LIMIT 1) AS product_name, - pb.version, - CASE WHEN pb.status = 1 THEN 20 ELSE 10 END AS status, - 1 AS is_default, - pb.total_cost, - pb.remark, - pb.create_time, - 'prd_bom' AS legacy_source, - pb.id AS legacy_id, - 'v1' AS source_table -FROM `prd_bom` pb -WHERE pb.deleted = 0 - AND pb.id NOT IN (SELECT legacy_id FROM bom_header WHERE legacy_source = 'prd_bom' AND legacy_id IS NOT NULL); - --- 4. 创建统一 BOM 明细视图 -CREATE OR REPLACE VIEW `v_bom_detail_unified` AS -SELECT - bl.id AS line_id, - bl.bom_id, - bl.line_no, - bl.material_id, - bl.material_code, - bl.material_name, - bl.material_spec, - bl.material_unit, - bl.consumption_qty, - bl.loss_rate, - bl.unit_cost, - bl.total_cost, - bl.remark, - 'v2' AS source_table -FROM `bom_line` bl - -UNION ALL - -SELECT - pbd.id AS line_id, - pbd.bom_id, - pbd.id AS line_no, - pbd.material_id, - (SELECT material_code FROM inv_material WHERE id = pbd.material_id LIMIT 1) AS material_code, - pbd.material_name, - NULL AS material_spec, - pbd.unit AS material_unit, - pbd.quantity AS consumption_qty, - pbd.loss_rate, - pbd.unit_cost, - pbd.total_cost, - pbd.remark, - 'v1' AS source_table -FROM `prd_bom_detail` pbd -INNER JOIN `prd_bom` pb ON pbd.bom_id = pb.id -WHERE pb.deleted = 0 - AND pbd.bom_id NOT IN (SELECT legacy_id FROM bom_header WHERE legacy_source = 'prd_bom' AND legacy_id IS NOT NULL); diff --git a/database/migrations/042_fix_027_duplicate_fk_complete_purchase_pk.sql b/database/migrations/042_fix_027_duplicate_fk_complete_purchase_pk.sql deleted file mode 100644 index 24b9fac8..00000000 --- a/database/migrations/042_fix_027_duplicate_fk_complete_purchase_pk.sql +++ /dev/null @@ -1,71 +0,0 @@ --- ============================================================ --- Migration 042: 修复 027 残留的重复 FK,完成采购模块 PK 类型对齐 --- --- 背景: --- Migration 027 已在 sys_migration 记录为 applied,但其 ALTER 语句 --- 因 pur_purchase_order_line 上存在遗留的自动生成 FK 约束 --- `pur_purchase_order_line_ibfk_1`(与 027 显式创建的 `fk_pur_line_po` --- 并存,指向同一对 po_id → pur_purchase_order.id)而静默失败。 --- 结果: --- - pur_purchase_order.id 仍是 int unsigned(应为 bigint unsigned) --- - pur_purchase_order_line.po_id 仍是 int unsigned(应为 bigint unsigned) --- 而 pur_purchase_order_line.id 已成功转为 bigint unsigned。 --- --- 操作顺序(避免 FK 冲突): --- 1. 删除遗留 FK `pur_purchase_order_line_ibfk_1`(如存在) --- 2. 删除现代 FK `fk_pur_line_po`(如存在,避免重建时名字冲突) --- 3. 修改 pur_purchase_order.id 为 BIGINT UNSIGNED --- 4. 修改 pur_purchase_order_line.po_id 为 BIGINT UNSIGNED --- 5. 重建 fk_pur_line_po --- --- 幂等模式:所有 ALTER / DROP 用 INFORMATION_SCHEMA 守卫 --- ============================================================ - --- 1. 删除遗留 FK `pur_purchase_order_line_ibfk_1` -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'pur_purchase_order_line' AND CONSTRAINT_NAME = 'pur_purchase_order_line_ibfk_1'); -SET @sql = IF(@fk > 0, - 'ALTER TABLE pur_purchase_order_line DROP FOREIGN KEY pur_purchase_order_line_ibfk_1', - 'SELECT ''pur_purchase_order_line_ibfk_1 not exists (already dropped)'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 2. 删除现代 FK `fk_pur_line_po`(027 重建过,但需先确保删除以便类型变更) -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'pur_purchase_order_line' AND CONSTRAINT_NAME = 'fk_pur_line_po'); -SET @sql = IF(@fk > 0, - 'ALTER TABLE pur_purchase_order_line DROP FOREIGN KEY fk_pur_line_po', - 'SELECT ''fk_pur_line_po not exists (already dropped)'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 3. 修改 pur_purchase_order.id 为 BIGINT UNSIGNED -SET @curr = (SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'pur_purchase_order' AND COLUMN_NAME = 'id'); -SET @sql = IF(@curr IS NOT NULL AND @curr NOT LIKE 'bigint%unsigned%', - 'ALTER TABLE pur_purchase_order MODIFY COLUMN id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT ''主键ID''', - 'SELECT ''pur_purchase_order.id already bigint unsigned'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 4. 修改 pur_purchase_order_line.po_id 为 BIGINT UNSIGNED -SET @curr = (SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'pur_purchase_order_line' AND COLUMN_NAME = 'po_id'); -SET @sql = IF(@curr IS NOT NULL AND @curr NOT LIKE 'bigint%unsigned%', - 'ALTER TABLE pur_purchase_order_line MODIFY COLUMN po_id BIGINT UNSIGNED NOT NULL COMMENT ''采购单ID''', - 'SELECT ''pur_purchase_order_line.po_id already bigint unsigned'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 5. 重建 fk_pur_line_po(如果不存在) -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'pur_purchase_order_line' AND CONSTRAINT_NAME = 'fk_pur_line_po'); -SET @sql = IF(@fk = 0, - 'ALTER TABLE pur_purchase_order_line ADD CONSTRAINT fk_pur_line_po FOREIGN KEY (po_id) REFERENCES pur_purchase_order (id) ON DELETE CASCADE ON UPDATE CASCADE', - 'SELECT ''fk_pur_line_po already exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 6. 验证:FK 与列类型已对齐 -SELECT - TABLE_NAME, COLUMN_NAME, COLUMN_TYPE, COLUMN_KEY, EXTRA -FROM INFORMATION_SCHEMA.COLUMNS -WHERE TABLE_SCHEMA = DATABASE() - AND ((TABLE_NAME = 'pur_purchase_order' AND COLUMN_NAME = 'id') - OR (TABLE_NAME = 'pur_purchase_order_line' AND COLUMN_NAME IN ('id', 'po_id'))) -ORDER BY TABLE_NAME, ORDINAL_POSITION; diff --git a/database/migrations/043_align_pk_type_p0_gaps.sql b/database/migrations/043_align_pk_type_p0_gaps.sql deleted file mode 100644 index 7fdec61f..00000000 --- a/database/migrations/043_align_pk_type_p0_gaps.sql +++ /dev/null @@ -1,158 +0,0 @@ --- ============================================================ --- Migration 043: 主键类型对齐 - P0 缺口(Drizzle ORM 消费表 FK 不一致) --- --- 背景:根据《PK 类型统一分析报告》P0 缺口 --- inv_inbound_order.id 与 inv_inbound_item.id/order_id/material_id 类型不一致 --- pur_purchase_order 审计列(create_by/update_by/audit_by/close_by)引用 sys_user.id(BIGINT) 但本身为 INT --- pur_purchase_return.id / pur_purchase_reconciliation.id 缺 UNSIGNED --- --- 涉及列(10 列): --- A. inv_inbound_order.id INT UNSIGNED → BIGINT UNSIGNED(主键,Drizzle 已声明 serial) --- B. inv_inbound_item.id INT UNSIGNED → BIGINT UNSIGNED(主键) --- C. inv_inbound_item.order_id INT UNSIGNED → BIGINT UNSIGNED(FK → inv_inbound_order.id) --- D. inv_inbound_item.material_id INT UNSIGNED → BIGINT UNSIGNED(逻辑引用物料表) --- E. pur_purchase_order.create_by INT UNSIGNED → BIGINT UNSIGNED(FK → sys_user.id) --- F. pur_purchase_order.update_by INT UNSIGNED → BIGINT UNSIGNED --- G. pur_purchase_order.audit_by INT UNSIGNED → BIGINT UNSIGNED --- H. pur_purchase_order.close_by INT UNSIGNED → BIGINT UNSIGNED --- I. pur_purchase_return.id BIGINT → BIGINT UNSIGNED(补 UNSIGNED) --- J. pur_purchase_reconciliation.id BIGINT → BIGINT UNSIGNED(补 UNSIGNED) --- --- 操作顺序(避免 FK 冲突): --- 1. 删除 fk_inv_inbound_item_order(如果存在) --- 2. 修改 inv_inbound_order.id --- 3. 修改 inv_inbound_item.id / order_id / material_id --- 4. 重建 fk_inv_inbound_item_order --- 5. 修改 pur_purchase_order 审计列(无 FK,直接 MODIFY) --- 6. 修改 pur_purchase_return.id / pur_purchase_reconciliation.id(补 UNSIGNED) --- --- 幂等模式:所有 ALTER 用 INFORMATION_SCHEMA 守卫 --- ============================================================ - --- ===== 1. 删除 inv_inbound_item 上的 FK ===== - --- 1a. 删除 fk_inv_inbound_item_order(如果存在) -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_inbound_item' AND CONSTRAINT_NAME = 'fk_inv_inbound_item_order'); -SET @sql = IF(@fk > 0, - 'ALTER TABLE inv_inbound_item DROP FOREIGN KEY fk_inv_inbound_item_order', - 'SELECT ''fk_inv_inbound_item_order not exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 1b. 删除旧名 fk_inbound_item_order(如果存在,migration 009 遗留) -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_inbound_item' AND CONSTRAINT_NAME = 'fk_inbound_item_order'); -SET @sql = IF(@fk > 0, - 'ALTER TABLE inv_inbound_item DROP FOREIGN KEY fk_inbound_item_order', - 'SELECT ''fk_inbound_item_order not exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- ===== 2. 修改 inv_inbound_order.id 为 BIGINT UNSIGNED ===== - -SET @curr = (SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_inbound_order' AND COLUMN_NAME = 'id'); -SET @sql = IF(@curr IS NOT NULL AND @curr NOT LIKE 'bigint%unsigned%', - 'ALTER TABLE inv_inbound_order MODIFY COLUMN id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT ''主键ID''', - 'SELECT ''inv_inbound_order.id already bigint unsigned'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- ===== 3. 修改 inv_inbound_item 列 ===== - --- 3a. inv_inbound_item.id → BIGINT UNSIGNED -SET @curr = (SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_inbound_item' AND COLUMN_NAME = 'id'); -SET @sql = IF(@curr IS NOT NULL AND @curr NOT LIKE 'bigint%unsigned%', - 'ALTER TABLE inv_inbound_item MODIFY COLUMN id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT ''主键ID''', - 'SELECT ''inv_inbound_item.id already bigint unsigned'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 3b. inv_inbound_item.order_id → BIGINT UNSIGNED -SET @curr = (SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_inbound_item' AND COLUMN_NAME = 'order_id'); -SET @sql = IF(@curr IS NOT NULL AND @curr NOT LIKE 'bigint%unsigned%', - 'ALTER TABLE inv_inbound_item MODIFY COLUMN order_id BIGINT UNSIGNED NOT NULL COMMENT ''入库单ID''', - 'SELECT ''inv_inbound_item.order_id already bigint unsigned'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 3c. inv_inbound_item.material_id → BIGINT UNSIGNED -SET @curr = (SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_inbound_item' AND COLUMN_NAME = 'material_id'); -SET @sql = IF(@curr IS NOT NULL AND @curr NOT LIKE 'bigint%unsigned%', - 'ALTER TABLE inv_inbound_item MODIFY COLUMN material_id BIGINT UNSIGNED DEFAULT NULL COMMENT ''物料ID''', - 'SELECT ''inv_inbound_item.material_id already bigint unsigned'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- ===== 4. 重建 fk_inv_inbound_item_order ===== - -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_inbound_item' AND CONSTRAINT_NAME = 'fk_inv_inbound_item_order'); -SET @sql = IF(@fk = 0, - 'ALTER TABLE inv_inbound_item ADD CONSTRAINT fk_inv_inbound_item_order FOREIGN KEY (order_id) REFERENCES inv_inbound_order (id) ON DELETE CASCADE ON UPDATE CASCADE', - 'SELECT ''fk_inv_inbound_item_order already exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- ===== 5. 修改 pur_purchase_order 审计列(无 FK,直接 MODIFY)===== - --- 5a. create_by -SET @curr = (SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'pur_purchase_order' AND COLUMN_NAME = 'create_by'); -SET @sql = IF(@curr IS NOT NULL AND @curr NOT LIKE 'bigint%unsigned%', - 'ALTER TABLE pur_purchase_order MODIFY COLUMN create_by BIGINT UNSIGNED DEFAULT NULL COMMENT ''创建人ID''', - 'SELECT ''pur_purchase_order.create_by already bigint unsigned'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 5b. update_by -SET @curr = (SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'pur_purchase_order' AND COLUMN_NAME = 'update_by'); -SET @sql = IF(@curr IS NOT NULL AND @curr NOT LIKE 'bigint%unsigned%', - 'ALTER TABLE pur_purchase_order MODIFY COLUMN update_by BIGINT UNSIGNED DEFAULT NULL COMMENT ''更新人ID''', - 'SELECT ''pur_purchase_order.update_by already bigint unsigned'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 5c. audit_by -SET @curr = (SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'pur_purchase_order' AND COLUMN_NAME = 'audit_by'); -SET @sql = IF(@curr IS NOT NULL AND @curr NOT LIKE 'bigint%unsigned%', - 'ALTER TABLE pur_purchase_order MODIFY COLUMN audit_by BIGINT UNSIGNED DEFAULT NULL COMMENT ''审核人ID''', - 'SELECT ''pur_purchase_order.audit_by already bigint unsigned'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 5d. close_by -SET @curr = (SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'pur_purchase_order' AND COLUMN_NAME = 'close_by'); -SET @sql = IF(@curr IS NOT NULL AND @curr NOT LIKE 'bigint%unsigned%', - 'ALTER TABLE pur_purchase_order MODIFY COLUMN close_by BIGINT UNSIGNED DEFAULT NULL COMMENT ''关闭人ID''', - 'SELECT ''pur_purchase_order.close_by already bigint unsigned'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- ===== 6. 修改 pur_purchase_return.id / pur_purchase_reconciliation.id(补 UNSIGNED)===== - --- 6a. pur_purchase_return.id -SET @curr = (SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'pur_purchase_return' AND COLUMN_NAME = 'id'); -SET @sql = IF(@curr IS NOT NULL AND @curr NOT LIKE 'bigint%unsigned%', - 'ALTER TABLE pur_purchase_return MODIFY COLUMN id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT ''主键ID''', - 'SELECT ''pur_purchase_return.id already bigint unsigned'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 6b. pur_purchase_reconciliation.id -SET @curr = (SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'pur_purchase_reconciliation' AND COLUMN_NAME = 'id'); -SET @sql = IF(@curr IS NOT NULL AND @curr NOT LIKE 'bigint%unsigned%', - 'ALTER TABLE pur_purchase_reconciliation MODIFY COLUMN id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT ''主键ID''', - 'SELECT ''pur_purchase_reconciliation.id already bigint unsigned'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- ===== 7. 验证:检查所有目标列类型已对齐 ===== -SELECT - TABLE_NAME, COLUMN_NAME, COLUMN_TYPE, COLUMN_KEY, EXTRA -FROM INFORMATION_SCHEMA.COLUMNS -WHERE TABLE_SCHEMA = DATABASE() - AND ( - (TABLE_NAME = 'inv_inbound_order' AND COLUMN_NAME = 'id') - OR (TABLE_NAME = 'inv_inbound_item' AND COLUMN_NAME IN ('id', 'order_id', 'material_id')) - OR (TABLE_NAME = 'pur_purchase_order' AND COLUMN_NAME IN ('create_by', 'update_by', 'audit_by', 'close_by')) - OR (TABLE_NAME = 'pur_purchase_return' AND COLUMN_NAME = 'id') - OR (TABLE_NAME = 'pur_purchase_reconciliation' AND COLUMN_NAME = 'id') - ) -ORDER BY TABLE_NAME, ORDINAL_POSITION; diff --git a/database/migrations/044_align_pk_type_sys_user_cascade.sql b/database/migrations/044_align_pk_type_sys_user_cascade.sql deleted file mode 100644 index a177d4a9..00000000 --- a/database/migrations/044_align_pk_type_sys_user_cascade.sql +++ /dev/null @@ -1,577 +0,0 @@ --- ============================================================ --- Migration 044: 主键类型对齐 - sys_user.id 级联迁移 --- --- 背景:sys_user.id 为 INT,被全库 20 个 FK 约束引用 + 多个无 FK 审计列逻辑引用。 --- 必须迁移为 BIGINT UNSIGNED 以匹配全局标准,并级联修改所有引用列。 --- --- 涉及列: --- A. sys_user.id INT → BIGINT UNSIGNED(主键) --- B. 20 个 FK 引用列 INT/INT UNSIGNED → BIGINT UNSIGNED --- C. 6 个无 FK 审计列 INT UNSIGNED → BIGINT UNSIGNED --- --- FK 引用列清单(20 个): --- 1. crm_customer.salesman_id fk_crm_customer_salesman SET NULL / CASCADE --- 2. inv_outbound_order.operator_id fk_inv_outbound_operator SET NULL / CASCADE --- 3. inv_outbound_order.auditor_id fk_inv_outbound_auditor SET NULL / CASCADE --- 4. inv_stock_adjust.approver_id fk_inv_adjust_approver SET NULL / CASCADE --- 5. inv_stock_adjust.operator_id fk_inv_adjust_operator SET NULL / CASCADE --- 6. inv_transfer_order.applicant_id fk_inv_transfer_applicant SET NULL / CASCADE --- 7. inv_transfer_order.approver_id fk_inv_transfer_approver SET NULL / CASCADE --- 8. inv_transfer_order.operator_id fk_inv_transfer_operator SET NULL / CASCADE --- 9. inv_warehouse.manager_id fk_warehouse_manager SET NULL / CASCADE --- 10. inv_inventory_log.operator_id fk_inv_invlog_operator SET NULL / CASCADE --- 11. inv_stocktaking.applicant_id fk_inv_stocktaking_applicant SET NULL / CASCADE --- 12. inv_stocktaking.approver_id fk_inv_stocktaking_approver SET NULL / CASCADE --- 13. inv_sales_outbound.operator_id fk_inv_sales_outbound_operator SET NULL / CASCADE --- 14. inv_production_inbound.operator_id fk_inv_prod_inbound_operator SET NULL / CASCADE --- 15. sys_department.leader_id fk_sys_department_leader SET NULL / CASCADE --- 16. qc_unqualified.create_by fk_qc_unqualified_create_by SET NULL / CASCADE --- 17. qc_unqualified.update_by fk_qc_unqualified_update_by SET NULL / CASCADE --- 18. qc_inspection.inspector_id fk_qc_inspection_inspector SET NULL / CASCADE --- 19. sal_order.salesman_id fk_sal_order_salesman SET NULL / CASCADE --- 20. sys_user_role.user_id fk_user_role_user CASCADE / CASCADE --- --- 无 FK 审计列清单(6 个): --- 21. inv_inbound_order.create_by --- 22. inv_inbound_order.update_by --- 23. pur_request.requester_id --- 24. pur_request.approver_id --- 25. pur_request_item.create_by --- 26. pur_request_item.update_by --- --- 操作顺序:删 FK → 改 sys_user.id → 改引用列 → 重建 FK --- 幂等模式:所有 ALTER 用 INFORMATION_SCHEMA 守卫 --- ============================================================ - --- ===== 1. 删除所有引用 sys_user.id 的 FK 约束 ===== - --- 1.1 通用删除宏:存在则删,不存在则跳过 --- fk_crm_customer_salesman -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS - WHERE TABLE_SCHEMA = DATABASE() AND CONSTRAINT_NAME = 'fk_crm_customer_salesman'); -SET @sql = IF(@fk > 0, 'ALTER TABLE crm_customer DROP FOREIGN KEY fk_crm_customer_salesman', 'SELECT ''skip'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- fk_inv_outbound_operator -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS - WHERE TABLE_SCHEMA = DATABASE() AND CONSTRAINT_NAME = 'fk_inv_outbound_operator'); -SET @sql = IF(@fk > 0, 'ALTER TABLE inv_outbound_order DROP FOREIGN KEY fk_inv_outbound_operator', 'SELECT ''skip'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- fk_inv_outbound_auditor -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS - WHERE TABLE_SCHEMA = DATABASE() AND CONSTRAINT_NAME = 'fk_inv_outbound_auditor'); -SET @sql = IF(@fk > 0, 'ALTER TABLE inv_outbound_order DROP FOREIGN KEY fk_inv_outbound_auditor', 'SELECT ''skip'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- fk_inv_adjust_approver -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS - WHERE TABLE_SCHEMA = DATABASE() AND CONSTRAINT_NAME = 'fk_inv_adjust_approver'); -SET @sql = IF(@fk > 0, 'ALTER TABLE inv_stock_adjust DROP FOREIGN KEY fk_inv_adjust_approver', 'SELECT ''skip'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- fk_inv_adjust_operator -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS - WHERE TABLE_SCHEMA = DATABASE() AND CONSTRAINT_NAME = 'fk_inv_adjust_operator'); -SET @sql = IF(@fk > 0, 'ALTER TABLE inv_stock_adjust DROP FOREIGN KEY fk_inv_adjust_operator', 'SELECT ''skip'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- fk_inv_transfer_applicant -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS - WHERE TABLE_SCHEMA = DATABASE() AND CONSTRAINT_NAME = 'fk_inv_transfer_applicant'); -SET @sql = IF(@fk > 0, 'ALTER TABLE inv_transfer_order DROP FOREIGN KEY fk_inv_transfer_applicant', 'SELECT ''skip'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- fk_inv_transfer_approver -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS - WHERE TABLE_SCHEMA = DATABASE() AND CONSTRAINT_NAME = 'fk_inv_transfer_approver'); -SET @sql = IF(@fk > 0, 'ALTER TABLE inv_transfer_order DROP FOREIGN KEY fk_inv_transfer_approver', 'SELECT ''skip'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- fk_inv_transfer_operator -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS - WHERE TABLE_SCHEMA = DATABASE() AND CONSTRAINT_NAME = 'fk_inv_transfer_operator'); -SET @sql = IF(@fk > 0, 'ALTER TABLE inv_transfer_order DROP FOREIGN KEY fk_inv_transfer_operator', 'SELECT ''skip'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- fk_warehouse_manager -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS - WHERE TABLE_SCHEMA = DATABASE() AND CONSTRAINT_NAME = 'fk_warehouse_manager'); -SET @sql = IF(@fk > 0, 'ALTER TABLE inv_warehouse DROP FOREIGN KEY fk_warehouse_manager', 'SELECT ''skip'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- fk_inv_invlog_operator -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS - WHERE TABLE_SCHEMA = DATABASE() AND CONSTRAINT_NAME = 'fk_inv_invlog_operator'); -SET @sql = IF(@fk > 0, 'ALTER TABLE inv_inventory_log DROP FOREIGN KEY fk_inv_invlog_operator', 'SELECT ''skip'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- fk_inv_stocktaking_applicant -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS - WHERE TABLE_SCHEMA = DATABASE() AND CONSTRAINT_NAME = 'fk_inv_stocktaking_applicant'); -SET @sql = IF(@fk > 0, 'ALTER TABLE inv_stocktaking DROP FOREIGN KEY fk_inv_stocktaking_applicant', 'SELECT ''skip'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- fk_inv_stocktaking_approver -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS - WHERE TABLE_SCHEMA = DATABASE() AND CONSTRAINT_NAME = 'fk_inv_stocktaking_approver'); -SET @sql = IF(@fk > 0, 'ALTER TABLE inv_stocktaking DROP FOREIGN KEY fk_inv_stocktaking_approver', 'SELECT ''skip'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- fk_inv_sales_outbound_operator -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS - WHERE TABLE_SCHEMA = DATABASE() AND CONSTRAINT_NAME = 'fk_inv_sales_outbound_operator'); -SET @sql = IF(@fk > 0, 'ALTER TABLE inv_sales_outbound DROP FOREIGN KEY fk_inv_sales_outbound_operator', 'SELECT ''skip'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- fk_inv_prod_inbound_operator -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS - WHERE TABLE_SCHEMA = DATABASE() AND CONSTRAINT_NAME = 'fk_inv_prod_inbound_operator'); -SET @sql = IF(@fk > 0, 'ALTER TABLE inv_production_inbound DROP FOREIGN KEY fk_inv_prod_inbound_operator', 'SELECT ''skip'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- fk_sys_department_leader -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS - WHERE TABLE_SCHEMA = DATABASE() AND CONSTRAINT_NAME = 'fk_sys_department_leader'); -SET @sql = IF(@fk > 0, 'ALTER TABLE sys_department DROP FOREIGN KEY fk_sys_department_leader', 'SELECT ''skip'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- fk_qc_unqualified_create_by -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS - WHERE TABLE_SCHEMA = DATABASE() AND CONSTRAINT_NAME = 'fk_qc_unqualified_create_by'); -SET @sql = IF(@fk > 0, 'ALTER TABLE qc_unqualified DROP FOREIGN KEY fk_qc_unqualified_create_by', 'SELECT ''skip'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- fk_qc_unqualified_update_by -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS - WHERE TABLE_SCHEMA = DATABASE() AND CONSTRAINT_NAME = 'fk_qc_unqualified_update_by'); -SET @sql = IF(@fk > 0, 'ALTER TABLE qc_unqualified DROP FOREIGN KEY fk_qc_unqualified_update_by', 'SELECT ''skip'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- fk_qc_inspection_inspector -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS - WHERE TABLE_SCHEMA = DATABASE() AND CONSTRAINT_NAME = 'fk_qc_inspection_inspector'); -SET @sql = IF(@fk > 0, 'ALTER TABLE qc_inspection DROP FOREIGN KEY fk_qc_inspection_inspector', 'SELECT ''skip'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- fk_sal_order_salesman -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS - WHERE TABLE_SCHEMA = DATABASE() AND CONSTRAINT_NAME = 'fk_sal_order_salesman'); -SET @sql = IF(@fk > 0, 'ALTER TABLE sal_order DROP FOREIGN KEY fk_sal_order_salesman', 'SELECT ''skip'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- fk_user_role_user -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS - WHERE TABLE_SCHEMA = DATABASE() AND CONSTRAINT_NAME = 'fk_user_role_user'); -SET @sql = IF(@fk > 0, 'ALTER TABLE sys_user_role DROP FOREIGN KEY fk_user_role_user', 'SELECT ''skip'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- ===== 2. 修改 sys_user.id 为 BIGINT UNSIGNED ===== - -SET @curr = (SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'sys_user' AND COLUMN_NAME = 'id'); -SET @sql = IF(@curr IS NOT NULL AND @curr NOT LIKE 'bigint%unsigned%', - 'ALTER TABLE sys_user MODIFY COLUMN id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT ''用户ID''', - 'SELECT ''sys_user.id already bigint unsigned'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- ===== 3. 修改 20 个 FK 引用列为 BIGINT UNSIGNED ===== - --- 3.1 crm_customer.salesman_id -SET @curr = (SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'crm_customer' AND COLUMN_NAME = 'salesman_id'); -SET @sql = IF(@curr IS NOT NULL AND @curr NOT LIKE 'bigint%unsigned%', - 'ALTER TABLE crm_customer MODIFY COLUMN salesman_id BIGINT UNSIGNED DEFAULT NULL COMMENT ''销售员ID''', - 'SELECT ''skip'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 3.2 inv_outbound_order.operator_id -SET @curr = (SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_outbound_order' AND COLUMN_NAME = 'operator_id'); -SET @sql = IF(@curr IS NOT NULL AND @curr NOT LIKE 'bigint%unsigned%', - 'ALTER TABLE inv_outbound_order MODIFY COLUMN operator_id BIGINT UNSIGNED DEFAULT NULL COMMENT ''操作员ID''', - 'SELECT ''skip'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 3.3 inv_outbound_order.auditor_id -SET @curr = (SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_outbound_order' AND COLUMN_NAME = 'auditor_id'); -SET @sql = IF(@curr IS NOT NULL AND @curr NOT LIKE 'bigint%unsigned%', - 'ALTER TABLE inv_outbound_order MODIFY COLUMN auditor_id BIGINT UNSIGNED DEFAULT NULL COMMENT ''审核员ID''', - 'SELECT ''skip'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 3.4 inv_stock_adjust.approver_id -SET @curr = (SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_stock_adjust' AND COLUMN_NAME = 'approver_id'); -SET @sql = IF(@curr IS NOT NULL AND @curr NOT LIKE 'bigint%unsigned%', - 'ALTER TABLE inv_stock_adjust MODIFY COLUMN approver_id BIGINT UNSIGNED DEFAULT NULL COMMENT ''审批人ID''', - 'SELECT ''skip'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 3.5 inv_stock_adjust.operator_id -SET @curr = (SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_stock_adjust' AND COLUMN_NAME = 'operator_id'); -SET @sql = IF(@curr IS NOT NULL AND @curr NOT LIKE 'bigint%unsigned%', - 'ALTER TABLE inv_stock_adjust MODIFY COLUMN operator_id BIGINT UNSIGNED DEFAULT NULL COMMENT ''操作员ID''', - 'SELECT ''skip'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 3.6 inv_transfer_order.applicant_id -SET @curr = (SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_transfer_order' AND COLUMN_NAME = 'applicant_id'); -SET @sql = IF(@curr IS NOT NULL AND @curr NOT LIKE 'bigint%unsigned%', - 'ALTER TABLE inv_transfer_order MODIFY COLUMN applicant_id BIGINT UNSIGNED DEFAULT NULL COMMENT ''申请人ID''', - 'SELECT ''skip'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 3.7 inv_transfer_order.approver_id -SET @curr = (SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_transfer_order' AND COLUMN_NAME = 'approver_id'); -SET @sql = IF(@curr IS NOT NULL AND @curr NOT LIKE 'bigint%unsigned%', - 'ALTER TABLE inv_transfer_order MODIFY COLUMN approver_id BIGINT UNSIGNED DEFAULT NULL COMMENT ''审批人ID''', - 'SELECT ''skip'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 3.8 inv_transfer_order.operator_id -SET @curr = (SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_transfer_order' AND COLUMN_NAME = 'operator_id'); -SET @sql = IF(@curr IS NOT NULL AND @curr NOT LIKE 'bigint%unsigned%', - 'ALTER TABLE inv_transfer_order MODIFY COLUMN operator_id BIGINT UNSIGNED DEFAULT NULL COMMENT ''操作员ID''', - 'SELECT ''skip'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 3.9 inv_warehouse.manager_id -SET @curr = (SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_warehouse' AND COLUMN_NAME = 'manager_id'); -SET @sql = IF(@curr IS NOT NULL AND @curr NOT LIKE 'bigint%unsigned%', - 'ALTER TABLE inv_warehouse MODIFY COLUMN manager_id BIGINT UNSIGNED DEFAULT NULL COMMENT ''仓库管理员ID''', - 'SELECT ''skip'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 3.10 inv_inventory_log.operator_id -SET @curr = (SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_inventory_log' AND COLUMN_NAME = 'operator_id'); -SET @sql = IF(@curr IS NOT NULL AND @curr NOT LIKE 'bigint%unsigned%', - 'ALTER TABLE inv_inventory_log MODIFY COLUMN operator_id BIGINT UNSIGNED DEFAULT NULL COMMENT ''操作员ID''', - 'SELECT ''skip'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 3.11 inv_stocktaking.applicant_id -SET @curr = (SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_stocktaking' AND COLUMN_NAME = 'applicant_id'); -SET @sql = IF(@curr IS NOT NULL AND @curr NOT LIKE 'bigint%unsigned%', - 'ALTER TABLE inv_stocktaking MODIFY COLUMN applicant_id BIGINT UNSIGNED DEFAULT NULL COMMENT ''申请人ID''', - 'SELECT ''skip'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 3.12 inv_stocktaking.approver_id -SET @curr = (SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_stocktaking' AND COLUMN_NAME = 'approver_id'); -SET @sql = IF(@curr IS NOT NULL AND @curr NOT LIKE 'bigint%unsigned%', - 'ALTER TABLE inv_stocktaking MODIFY COLUMN approver_id BIGINT UNSIGNED DEFAULT NULL COMMENT ''审批人ID''', - 'SELECT ''skip'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 3.13 inv_sales_outbound.operator_id -SET @curr = (SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_sales_outbound' AND COLUMN_NAME = 'operator_id'); -SET @sql = IF(@curr IS NOT NULL AND @curr NOT LIKE 'bigint%unsigned%', - 'ALTER TABLE inv_sales_outbound MODIFY COLUMN operator_id BIGINT UNSIGNED DEFAULT NULL COMMENT ''操作员ID''', - 'SELECT ''skip'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 3.14 inv_production_inbound.operator_id -SET @curr = (SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_production_inbound' AND COLUMN_NAME = 'operator_id'); -SET @sql = IF(@curr IS NOT NULL AND @curr NOT LIKE 'bigint%unsigned%', - 'ALTER TABLE inv_production_inbound MODIFY COLUMN operator_id BIGINT UNSIGNED DEFAULT NULL COMMENT ''操作员ID''', - 'SELECT ''skip'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 3.15 sys_department.leader_id -SET @curr = (SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'sys_department' AND COLUMN_NAME = 'leader_id'); -SET @sql = IF(@curr IS NOT NULL AND @curr NOT LIKE 'bigint%unsigned%', - 'ALTER TABLE sys_department MODIFY COLUMN leader_id BIGINT UNSIGNED DEFAULT NULL COMMENT ''部门负责人ID''', - 'SELECT ''skip'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 3.16 qc_unqualified.create_by -SET @curr = (SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'qc_unqualified' AND COLUMN_NAME = 'create_by'); -SET @sql = IF(@curr IS NOT NULL AND @curr NOT LIKE 'bigint%unsigned%', - 'ALTER TABLE qc_unqualified MODIFY COLUMN create_by BIGINT UNSIGNED DEFAULT NULL COMMENT ''创建人ID''', - 'SELECT ''skip'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 3.17 qc_unqualified.update_by -SET @curr = (SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'qc_unqualified' AND COLUMN_NAME = 'update_by'); -SET @sql = IF(@curr IS NOT NULL AND @curr NOT LIKE 'bigint%unsigned%', - 'ALTER TABLE qc_unqualified MODIFY COLUMN update_by BIGINT UNSIGNED DEFAULT NULL COMMENT ''更新人ID''', - 'SELECT ''skip'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 3.18 qc_inspection.inspector_id -SET @curr = (SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'qc_inspection' AND COLUMN_NAME = 'inspector_id'); -SET @sql = IF(@curr IS NOT NULL AND @curr NOT LIKE 'bigint%unsigned%', - 'ALTER TABLE qc_inspection MODIFY COLUMN inspector_id BIGINT UNSIGNED DEFAULT NULL COMMENT ''检验员ID''', - 'SELECT ''skip'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 3.19 sal_order.salesman_id -SET @curr = (SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'sal_order' AND COLUMN_NAME = 'salesman_id'); -SET @sql = IF(@curr IS NOT NULL AND @curr NOT LIKE 'bigint%unsigned%', - 'ALTER TABLE sal_order MODIFY COLUMN salesman_id BIGINT UNSIGNED DEFAULT NULL COMMENT ''销售员ID''', - 'SELECT ''skip'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 3.20 sys_user_role.user_id -SET @curr = (SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'sys_user_role' AND COLUMN_NAME = 'user_id'); -SET @sql = IF(@curr IS NOT NULL AND @curr NOT LIKE 'bigint%unsigned%', - 'ALTER TABLE sys_user_role MODIFY COLUMN user_id BIGINT UNSIGNED NOT NULL COMMENT ''用户ID''', - 'SELECT ''skip'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- ===== 4. 修改无 FK 审计列(6 个)===== - --- 4.1 inv_inbound_order.create_by -SET @curr = (SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_inbound_order' AND COLUMN_NAME = 'create_by'); -SET @sql = IF(@curr IS NOT NULL AND @curr NOT LIKE 'bigint%unsigned%', - 'ALTER TABLE inv_inbound_order MODIFY COLUMN create_by BIGINT UNSIGNED DEFAULT NULL COMMENT ''创建人ID''', - 'SELECT ''skip'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 4.2 inv_inbound_order.update_by -SET @curr = (SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_inbound_order' AND COLUMN_NAME = 'update_by'); -SET @sql = IF(@curr IS NOT NULL AND @curr NOT LIKE 'bigint%unsigned%', - 'ALTER TABLE inv_inbound_order MODIFY COLUMN update_by BIGINT UNSIGNED DEFAULT NULL COMMENT ''更新人ID''', - 'SELECT ''skip'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 4.3 pur_request.requester_id -SET @curr = (SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'pur_request' AND COLUMN_NAME = 'requester_id'); -SET @sql = IF(@curr IS NOT NULL AND @curr NOT LIKE 'bigint%unsigned%', - 'ALTER TABLE pur_request MODIFY COLUMN requester_id BIGINT UNSIGNED DEFAULT NULL COMMENT ''申请人ID''', - 'SELECT ''skip'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 4.4 pur_request.approver_id -SET @curr = (SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'pur_request' AND COLUMN_NAME = 'approver_id'); -SET @sql = IF(@curr IS NOT NULL AND @curr NOT LIKE 'bigint%unsigned%', - 'ALTER TABLE pur_request MODIFY COLUMN approver_id BIGINT UNSIGNED DEFAULT NULL COMMENT ''审批人ID''', - 'SELECT ''skip'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 4.5 pur_request_item.create_by -SET @curr = (SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'pur_request_item' AND COLUMN_NAME = 'create_by'); -SET @sql = IF(@curr IS NOT NULL AND @curr NOT LIKE 'bigint%unsigned%', - 'ALTER TABLE pur_request_item MODIFY COLUMN create_by BIGINT UNSIGNED DEFAULT NULL COMMENT ''创建人ID''', - 'SELECT ''skip'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 4.6 pur_request_item.update_by -SET @curr = (SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'pur_request_item' AND COLUMN_NAME = 'update_by'); -SET @sql = IF(@curr IS NOT NULL AND @curr NOT LIKE 'bigint%unsigned%', - 'ALTER TABLE pur_request_item MODIFY COLUMN update_by BIGINT UNSIGNED DEFAULT NULL COMMENT ''更新人ID''', - 'SELECT ''skip'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- ===== 5. 重建所有 FK 约束 ===== - --- 5.1 fk_crm_customer_salesman -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS - WHERE TABLE_SCHEMA = DATABASE() AND CONSTRAINT_NAME = 'fk_crm_customer_salesman'); -SET @sql = IF(@fk = 0, - 'ALTER TABLE crm_customer ADD CONSTRAINT fk_crm_customer_salesman FOREIGN KEY (salesman_id) REFERENCES sys_user (id) ON DELETE SET NULL ON UPDATE CASCADE', - 'SELECT ''skip'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 5.2 fk_inv_outbound_operator -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS - WHERE TABLE_SCHEMA = DATABASE() AND CONSTRAINT_NAME = 'fk_inv_outbound_operator'); -SET @sql = IF(@fk = 0, - 'ALTER TABLE inv_outbound_order ADD CONSTRAINT fk_inv_outbound_operator FOREIGN KEY (operator_id) REFERENCES sys_user (id) ON DELETE SET NULL ON UPDATE CASCADE', - 'SELECT ''skip'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 5.3 fk_inv_outbound_auditor -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS - WHERE TABLE_SCHEMA = DATABASE() AND CONSTRAINT_NAME = 'fk_inv_outbound_auditor'); -SET @sql = IF(@fk = 0, - 'ALTER TABLE inv_outbound_order ADD CONSTRAINT fk_inv_outbound_auditor FOREIGN KEY (auditor_id) REFERENCES sys_user (id) ON DELETE SET NULL ON UPDATE CASCADE', - 'SELECT ''skip'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 5.4 fk_inv_adjust_approver -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS - WHERE TABLE_SCHEMA = DATABASE() AND CONSTRAINT_NAME = 'fk_inv_adjust_approver'); -SET @sql = IF(@fk = 0, - 'ALTER TABLE inv_stock_adjust ADD CONSTRAINT fk_inv_adjust_approver FOREIGN KEY (approver_id) REFERENCES sys_user (id) ON DELETE SET NULL ON UPDATE CASCADE', - 'SELECT ''skip'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 5.5 fk_inv_adjust_operator -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS - WHERE TABLE_SCHEMA = DATABASE() AND CONSTRAINT_NAME = 'fk_inv_adjust_operator'); -SET @sql = IF(@fk = 0, - 'ALTER TABLE inv_stock_adjust ADD CONSTRAINT fk_inv_adjust_operator FOREIGN KEY (operator_id) REFERENCES sys_user (id) ON DELETE SET NULL ON UPDATE CASCADE', - 'SELECT ''skip'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 5.6 fk_inv_transfer_applicant -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS - WHERE TABLE_SCHEMA = DATABASE() AND CONSTRAINT_NAME = 'fk_inv_transfer_applicant'); -SET @sql = IF(@fk = 0, - 'ALTER TABLE inv_transfer_order ADD CONSTRAINT fk_inv_transfer_applicant FOREIGN KEY (applicant_id) REFERENCES sys_user (id) ON DELETE SET NULL ON UPDATE CASCADE', - 'SELECT ''skip'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 5.7 fk_inv_transfer_approver -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS - WHERE TABLE_SCHEMA = DATABASE() AND CONSTRAINT_NAME = 'fk_inv_transfer_approver'); -SET @sql = IF(@fk = 0, - 'ALTER TABLE inv_transfer_order ADD CONSTRAINT fk_inv_transfer_approver FOREIGN KEY (approver_id) REFERENCES sys_user (id) ON DELETE SET NULL ON UPDATE CASCADE', - 'SELECT ''skip'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 5.8 fk_inv_transfer_operator -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS - WHERE TABLE_SCHEMA = DATABASE() AND CONSTRAINT_NAME = 'fk_inv_transfer_operator'); -SET @sql = IF(@fk = 0, - 'ALTER TABLE inv_transfer_order ADD CONSTRAINT fk_inv_transfer_operator FOREIGN KEY (operator_id) REFERENCES sys_user (id) ON DELETE SET NULL ON UPDATE CASCADE', - 'SELECT ''skip'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 5.9 fk_warehouse_manager -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS - WHERE TABLE_SCHEMA = DATABASE() AND CONSTRAINT_NAME = 'fk_warehouse_manager'); -SET @sql = IF(@fk = 0, - 'ALTER TABLE inv_warehouse ADD CONSTRAINT fk_warehouse_manager FOREIGN KEY (manager_id) REFERENCES sys_user (id) ON DELETE SET NULL ON UPDATE CASCADE', - 'SELECT ''skip'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 5.10 fk_inv_invlog_operator -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS - WHERE TABLE_SCHEMA = DATABASE() AND CONSTRAINT_NAME = 'fk_inv_invlog_operator'); -SET @sql = IF(@fk = 0, - 'ALTER TABLE inv_inventory_log ADD CONSTRAINT fk_inv_invlog_operator FOREIGN KEY (operator_id) REFERENCES sys_user (id) ON DELETE SET NULL ON UPDATE CASCADE', - 'SELECT ''skip'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 5.11 fk_inv_stocktaking_applicant -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS - WHERE TABLE_SCHEMA = DATABASE() AND CONSTRAINT_NAME = 'fk_inv_stocktaking_applicant'); -SET @sql = IF(@fk = 0, - 'ALTER TABLE inv_stocktaking ADD CONSTRAINT fk_inv_stocktaking_applicant FOREIGN KEY (applicant_id) REFERENCES sys_user (id) ON DELETE SET NULL ON UPDATE CASCADE', - 'SELECT ''skip'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 5.12 fk_inv_stocktaking_approver -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS - WHERE TABLE_SCHEMA = DATABASE() AND CONSTRAINT_NAME = 'fk_inv_stocktaking_approver'); -SET @sql = IF(@fk = 0, - 'ALTER TABLE inv_stocktaking ADD CONSTRAINT fk_inv_stocktaking_approver FOREIGN KEY (approver_id) REFERENCES sys_user (id) ON DELETE SET NULL ON UPDATE CASCADE', - 'SELECT ''skip'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 5.13 fk_inv_sales_outbound_operator -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS - WHERE TABLE_SCHEMA = DATABASE() AND CONSTRAINT_NAME = 'fk_inv_sales_outbound_operator'); -SET @sql = IF(@fk = 0, - 'ALTER TABLE inv_sales_outbound ADD CONSTRAINT fk_inv_sales_outbound_operator FOREIGN KEY (operator_id) REFERENCES sys_user (id) ON DELETE SET NULL ON UPDATE CASCADE', - 'SELECT ''skip'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 5.14 fk_inv_prod_inbound_operator -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS - WHERE TABLE_SCHEMA = DATABASE() AND CONSTRAINT_NAME = 'fk_inv_prod_inbound_operator'); -SET @sql = IF(@fk = 0, - 'ALTER TABLE inv_production_inbound ADD CONSTRAINT fk_inv_prod_inbound_operator FOREIGN KEY (operator_id) REFERENCES sys_user (id) ON DELETE SET NULL ON UPDATE CASCADE', - 'SELECT ''skip'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 5.15 fk_sys_department_leader -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS - WHERE TABLE_SCHEMA = DATABASE() AND CONSTRAINT_NAME = 'fk_sys_department_leader'); -SET @sql = IF(@fk = 0, - 'ALTER TABLE sys_department ADD CONSTRAINT fk_sys_department_leader FOREIGN KEY (leader_id) REFERENCES sys_user (id) ON DELETE SET NULL ON UPDATE CASCADE', - 'SELECT ''skip'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 5.16 fk_qc_unqualified_create_by -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS - WHERE TABLE_SCHEMA = DATABASE() AND CONSTRAINT_NAME = 'fk_qc_unqualified_create_by'); -SET @sql = IF(@fk = 0, - 'ALTER TABLE qc_unqualified ADD CONSTRAINT fk_qc_unqualified_create_by FOREIGN KEY (create_by) REFERENCES sys_user (id) ON DELETE SET NULL ON UPDATE CASCADE', - 'SELECT ''skip'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 5.17 fk_qc_unqualified_update_by -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS - WHERE TABLE_SCHEMA = DATABASE() AND CONSTRAINT_NAME = 'fk_qc_unqualified_update_by'); -SET @sql = IF(@fk = 0, - 'ALTER TABLE qc_unqualified ADD CONSTRAINT fk_qc_unqualified_update_by FOREIGN KEY (update_by) REFERENCES sys_user (id) ON DELETE SET NULL ON UPDATE CASCADE', - 'SELECT ''skip'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 5.18 fk_qc_inspection_inspector -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS - WHERE TABLE_SCHEMA = DATABASE() AND CONSTRAINT_NAME = 'fk_qc_inspection_inspector'); -SET @sql = IF(@fk = 0, - 'ALTER TABLE qc_inspection ADD CONSTRAINT fk_qc_inspection_inspector FOREIGN KEY (inspector_id) REFERENCES sys_user (id) ON DELETE SET NULL ON UPDATE CASCADE', - 'SELECT ''skip'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 5.19 fk_sal_order_salesman -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS - WHERE TABLE_SCHEMA = DATABASE() AND CONSTRAINT_NAME = 'fk_sal_order_salesman'); -SET @sql = IF(@fk = 0, - 'ALTER TABLE sal_order ADD CONSTRAINT fk_sal_order_salesman FOREIGN KEY (salesman_id) REFERENCES sys_user (id) ON DELETE SET NULL ON UPDATE CASCADE', - 'SELECT ''skip'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 5.20 fk_user_role_user -SET @fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS - WHERE TABLE_SCHEMA = DATABASE() AND CONSTRAINT_NAME = 'fk_user_role_user'); -SET @sql = IF(@fk = 0, - 'ALTER TABLE sys_user_role ADD CONSTRAINT fk_user_role_user FOREIGN KEY (user_id) REFERENCES sys_user (id) ON DELETE CASCADE ON UPDATE CASCADE', - 'SELECT ''skip'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- ===== 6. 验证:检查 sys_user.id 及所有引用列类型已对齐 ===== -SELECT - TABLE_NAME, COLUMN_NAME, COLUMN_TYPE -FROM INFORMATION_SCHEMA.COLUMNS -WHERE TABLE_SCHEMA = DATABASE() - AND ( - (TABLE_NAME = 'sys_user' AND COLUMN_NAME = 'id') - OR (TABLE_NAME = 'crm_customer' AND COLUMN_NAME = 'salesman_id') - OR (TABLE_NAME = 'inv_outbound_order' AND COLUMN_NAME IN ('operator_id', 'auditor_id')) - OR (TABLE_NAME = 'inv_stock_adjust' AND COLUMN_NAME IN ('approver_id', 'operator_id')) - OR (TABLE_NAME = 'inv_transfer_order' AND COLUMN_NAME IN ('applicant_id', 'approver_id', 'operator_id')) - OR (TABLE_NAME = 'inv_warehouse' AND COLUMN_NAME = 'manager_id') - OR (TABLE_NAME = 'inv_inventory_log' AND COLUMN_NAME = 'operator_id') - OR (TABLE_NAME = 'inv_stocktaking' AND COLUMN_NAME IN ('applicant_id', 'approver_id')) - OR (TABLE_NAME = 'inv_sales_outbound' AND COLUMN_NAME = 'operator_id') - OR (TABLE_NAME = 'inv_production_inbound' AND COLUMN_NAME = 'operator_id') - OR (TABLE_NAME = 'sys_department' AND COLUMN_NAME = 'leader_id') - OR (TABLE_NAME = 'qc_unqualified' AND COLUMN_NAME IN ('create_by', 'update_by')) - OR (TABLE_NAME = 'qc_inspection' AND COLUMN_NAME = 'inspector_id') - OR (TABLE_NAME = 'sal_order' AND COLUMN_NAME = 'salesman_id') - OR (TABLE_NAME = 'sys_user_role' AND COLUMN_NAME = 'user_id') - OR (TABLE_NAME = 'inv_inbound_order' AND COLUMN_NAME IN ('create_by', 'update_by')) - OR (TABLE_NAME = 'pur_request' AND COLUMN_NAME IN ('requester_id', 'approver_id')) - OR (TABLE_NAME = 'pur_request_item' AND COLUMN_NAME IN ('create_by', 'update_by')) - ) -ORDER BY TABLE_NAME, ORDINAL_POSITION; diff --git a/database/migrations/045_remove_demo_menus.sql b/database/migrations/045_remove_demo_menus.sql deleted file mode 100644 index dae83876..00000000 --- a/database/migrations/045_remove_demo_menus.sql +++ /dev/null @@ -1,16 +0,0 @@ --- ============================================ --- 迁移 045: 删除示范菜单(demo / demo_crud) --- 说明:示范菜单仅用于开发演示,生产环境不应保留 --- 幂等:菜单不存在时不会报错 --- ============================================ - --- 1. 删除角色-菜单关联 -DELETE FROM sys_role_menu WHERE menu_id IN ( - SELECT id FROM ( - SELECT id FROM sys_menu WHERE menu_code IN ('demo', 'demo_crud') - ) AS t -); - --- 2. 先删除子菜单(demo_crud),再删除父菜单(demo) -DELETE FROM sys_menu WHERE menu_code = 'demo_crud'; -DELETE FROM sys_menu WHERE menu_code = 'demo'; diff --git a/database/migrations/046_create_equipment_maintenance_tables.sql b/database/migrations/046_create_equipment_maintenance_tables.sql deleted file mode 100644 index 9e356d22..00000000 --- a/database/migrations/046_create_equipment_maintenance_tables.sql +++ /dev/null @@ -1,103 +0,0 @@ --- ========================================== --- Migration 046: 设备维保管理模块建表 --- 创建三张表:eq_equipment(设备台账)、eq_maintenance_plan(维保计划)、eq_maintenance_record(维保记录) --- 遵循项目规范:utf8mb4 字符集、BIGINT UNSIGNED 主键、软删除、审计字段 --- ========================================== - --- 1. 设备台账表 -CREATE TABLE IF NOT EXISTS `eq_equipment` ( - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '设备ID', - `equipment_code` VARCHAR(50) NOT NULL COMMENT '设备编号(唯一)', - `equipment_name` VARCHAR(100) NOT NULL COMMENT '设备名称', - `equipment_type` VARCHAR(50) NOT NULL DEFAULT 'other' COMMENT '设备类型:printing/die_cutting/laminating/slitting/other', - `model` VARCHAR(100) DEFAULT NULL COMMENT '型号规格', - `manufacturer` VARCHAR(100) DEFAULT NULL COMMENT '制造商', - `workshop` VARCHAR(50) DEFAULT NULL COMMENT '所属车间', - `location` VARCHAR(100) DEFAULT NULL COMMENT '安装位置', - `purchase_date` DATE DEFAULT NULL COMMENT '购置日期', - `install_date` DATE DEFAULT NULL COMMENT '安装日期', - `purchase_price` DECIMAL(12,2) DEFAULT 0.00 COMMENT '购置价格', - `expected_life_years` INT DEFAULT 10 COMMENT '预计使用年限(年)', - `status` TINYINT NOT NULL DEFAULT 1 COMMENT '状态:1=运行中 2=停机 3=维修中 4=报废', - `cumulative_run_hours` DECIMAL(10,2) DEFAULT 0.00 COMMENT '累计运行时长(小时)', - `cumulative_print_count` BIGINT DEFAULT 0 COMMENT '累计印刷次数', - `last_maintenance_date` DATE DEFAULT NULL COMMENT '上次维保日期', - `next_maintenance_date` DATE DEFAULT NULL COMMENT '下次维保日期', - `remark` TEXT DEFAULT NULL COMMENT '备注', - `deleted` TINYINT NOT NULL DEFAULT 0 COMMENT '软删除:0=正常 1=已删除', - `create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - `update_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', - `create_by` BIGINT UNSIGNED DEFAULT NULL COMMENT '创建人', - `update_by` BIGINT UNSIGNED DEFAULT NULL COMMENT '更新人', - PRIMARY KEY (`id`), - UNIQUE KEY `uk_equipment_code` (`equipment_code`), - KEY `idx_equipment_type` (`equipment_type`), - KEY `idx_equipment_status` (`status`), - KEY `idx_equipment_workshop` (`workshop`), - KEY `idx_equipment_next_maintenance` (`next_maintenance_date`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='设备台账表'; - --- 2. 维保计划表 -CREATE TABLE IF NOT EXISTS `eq_maintenance_plan` ( - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '计划ID', - `plan_no` VARCHAR(50) NOT NULL COMMENT '计划编号(唯一)', - `equipment_id` BIGINT UNSIGNED NOT NULL COMMENT '设备ID', - `plan_name` VARCHAR(100) NOT NULL COMMENT '计划名称', - `maintenance_type` VARCHAR(20) NOT NULL DEFAULT 'routine' COMMENT '维保类型:routine=日常保养/periodic=定期维保/major=大修', - `cycle_type` VARCHAR(20) NOT NULL DEFAULT 'monthly' COMMENT '周期类型:daily/weekly/monthly/quarterly/yearly/custom', - `cycle_days` INT DEFAULT 30 COMMENT '自定义周期天数(cycle_type=custom 时使用)', - `lead_days` INT DEFAULT 7 COMMENT '提前提醒天数', - `estimated_hours` DECIMAL(5,2) DEFAULT 4.00 COMMENT '预计耗时(小时)', - `estimated_cost` DECIMAL(10,2) DEFAULT 0.00 COMMENT '预计费用', - `checklist` JSON DEFAULT NULL COMMENT '检查项目清单(JSON 数组)', - `status` TINYINT NOT NULL DEFAULT 1 COMMENT '状态:1=启用 2=停用', - `last_executed_date` DATE DEFAULT NULL COMMENT '上次执行日期', - `next_execute_date` DATE DEFAULT NULL COMMENT '下次执行日期', - `remark` TEXT DEFAULT NULL COMMENT '备注', - `deleted` TINYINT NOT NULL DEFAULT 0 COMMENT '软删除:0=正常 1=已删除', - `create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - `update_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', - `create_by` BIGINT UNSIGNED DEFAULT NULL COMMENT '创建人', - `update_by` BIGINT UNSIGNED DEFAULT NULL COMMENT '更新人', - PRIMARY KEY (`id`), - UNIQUE KEY `uk_plan_no` (`plan_no`), - KEY `idx_plan_equipment` (`equipment_id`), - KEY `idx_plan_status` (`status`), - KEY `idx_plan_next_execute` (`next_execute_date`), - CONSTRAINT `fk_plan_equipment` FOREIGN KEY (`equipment_id`) REFERENCES `eq_equipment` (`id`) ON DELETE RESTRICT ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='维保计划表'; - --- 3. 维保记录表 -CREATE TABLE IF NOT EXISTS `eq_maintenance_record` ( - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '记录ID', - `record_no` VARCHAR(50) NOT NULL COMMENT '记录编号(唯一)', - `equipment_id` BIGINT UNSIGNED NOT NULL COMMENT '设备ID', - `plan_id` BIGINT UNSIGNED DEFAULT NULL COMMENT '关联计划ID(自主维保为 NULL)', - `maintenance_type` VARCHAR(20) NOT NULL DEFAULT 'routine' COMMENT '维保类型:routine=日常保养/periodic=定期维保/major=大修/emergency=紧急维修', - `maintenance_date` DATE NOT NULL COMMENT '维保日期', - `start_time` DATETIME DEFAULT NULL COMMENT '开始时间', - `end_time` DATETIME DEFAULT NULL COMMENT '结束时间', - `actual_hours` DECIMAL(5,2) DEFAULT 0.00 COMMENT '实际耗时(小时)', - `actual_cost` DECIMAL(10,2) DEFAULT 0.00 COMMENT '实际费用', - `technician_name` VARCHAR(50) DEFAULT NULL COMMENT '技术员', - `run_hours_before` DECIMAL(10,2) DEFAULT NULL COMMENT '维保前运行时长', - `run_hours_after` DECIMAL(10,2) DEFAULT NULL COMMENT '维保后运行时长', - `description` TEXT DEFAULT NULL COMMENT '维保内容描述', - `parts_replaced` JSON DEFAULT NULL COMMENT '更换配件清单(JSON 数组)', - `result` VARCHAR(20) NOT NULL DEFAULT 'completed' COMMENT '维保结果:completed=完成/partial=部分完成/failed=失败', - `status` TINYINT NOT NULL DEFAULT 1 COMMENT '状态:1=已完成 2=进行中 3=已取消', - `remark` TEXT DEFAULT NULL COMMENT '备注', - `deleted` TINYINT NOT NULL DEFAULT 0 COMMENT '软删除:0=正常 1=已删除', - `create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - `update_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', - `create_by` BIGINT UNSIGNED DEFAULT NULL COMMENT '创建人', - `update_by` BIGINT UNSIGNED DEFAULT NULL COMMENT '更新人', - PRIMARY KEY (`id`), - UNIQUE KEY `uk_record_no` (`record_no`), - KEY `idx_record_equipment` (`equipment_id`), - KEY `idx_record_plan` (`plan_id`), - KEY `idx_record_date` (`maintenance_date`), - KEY `idx_record_status` (`status`), - CONSTRAINT `fk_record_equipment` FOREIGN KEY (`equipment_id`) REFERENCES `eq_equipment` (`id`) ON DELETE RESTRICT ON UPDATE CASCADE, - CONSTRAINT `fk_record_plan` FOREIGN KEY (`plan_id`) REFERENCES `eq_maintenance_plan` (`id`) ON DELETE SET NULL ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='维保记录表'; diff --git a/database/migrations/047_create_ink_formula_version_tables.sql b/database/migrations/047_create_ink_formula_version_tables.sql deleted file mode 100644 index 04e26ed0..00000000 --- a/database/migrations/047_create_ink_formula_version_tables.sql +++ /dev/null @@ -1,85 +0,0 @@ --- 047: 油墨配方版本管理 — 创建色号、版本、明细三张表 --- 依据: docs/油墨配方版本管理完整落地方案.md --- 命名规范: dcprint_ 模块前缀 + 实体名,全量审计字段 + 软删除 - --- 1. 色号基础档案表 -CREATE TABLE IF NOT EXISTS `dcprint_ink_color` ( - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, - `color_code` VARCHAR(50) NOT NULL COMMENT '色号编码(唯一)', - `color_name` VARCHAR(100) NOT NULL COMMENT '色号名称', - `color_series` VARCHAR(50) COMMENT '色系(红/蓝/绿...)', - `base_ink_type` VARCHAR(50) COMMENT '基墨类型(UV/solvent/water)', - `pantone_code` VARCHAR(50) COMMENT 'Pantone 色号', - `remark` TEXT COMMENT '备注', - `status` TINYINT NOT NULL DEFAULT 1 COMMENT '1-启用 2-停用', - `create_by` BIGINT UNSIGNED COMMENT '创建人ID', - `create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - `update_by` BIGINT UNSIGNED COMMENT '更新人ID', - `update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', - `is_deleted` TINYINT NOT NULL DEFAULT 0 COMMENT '软删除', - PRIMARY KEY (`id`), - UNIQUE KEY `uk_color_code` (`color_code`), - KEY `idx_color_name` (`color_name`), - KEY `idx_status` (`status`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='油墨色号基础档案表'; - --- 2. 配方版本主表(含成本快照) -CREATE TABLE IF NOT EXISTS `dcprint_ink_formula_version` ( - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, - `color_id` BIGINT UNSIGNED NOT NULL COMMENT '色号ID', - `version_no` VARCHAR(20) NOT NULL COMMENT '版本号 V1.0', - `version_name` VARCHAR(100) COMMENT '版本名称', - `status` TINYINT NOT NULL DEFAULT 1 COMMENT '1-草稿 2-已生效 3-已作废', - `change_reason` TEXT COMMENT '变更原因', - `source_version_id` BIGINT UNSIGNED COMMENT '源版本ID(一键复用来源)', - `process_note` TEXT COMMENT '工艺说明', - `total_weight` DECIMAL(10,3) COMMENT '配方总重量', - `unit` VARCHAR(10) DEFAULT 'kg' COMMENT '单位', - `shelf_life_hours` INT DEFAULT 168 COMMENT '保质期(小时)', - - -- 成本快照 - `theoretical_cost` DECIMAL(12,4) COMMENT '理论成本', - `cost_snapshot_time` DATETIME COMMENT '成本快照时间', - `cost_calc_status` TINYINT NOT NULL DEFAULT 0 COMMENT '0-未计算 1-完成 2-部分缺失', - `cost_warning` VARCHAR(255) COMMENT '成本缺失警告', - - `activate_by` BIGINT UNSIGNED COMMENT '生效操作人ID', - `activate_time` DATETIME COMMENT '生效时间', - `cancel_by` BIGINT UNSIGNED COMMENT '作废操作人ID', - `cancel_reason` TEXT COMMENT '作废原因', - `cancel_time` DATETIME COMMENT '作废时间', - `create_by` BIGINT UNSIGNED COMMENT '创建人ID', - `create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - `update_by` BIGINT UNSIGNED COMMENT '更新人ID', - `update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', - `is_deleted` TINYINT NOT NULL DEFAULT 0 COMMENT '软删除', - PRIMARY KEY (`id`), - UNIQUE KEY `uk_color_version` (`color_id`, `version_no`), - KEY `idx_color_id` (`color_id`), - KEY `idx_status` (`status`), - KEY `idx_source_version` (`source_version_id`), - CONSTRAINT `fk_formula_version_color` FOREIGN KEY (`color_id`) REFERENCES `dcprint_ink_color` (`id`) ON DELETE RESTRICT ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='油墨配方版本主表'; - --- 3. 配方明细表(含成本快照) -CREATE TABLE IF NOT EXISTS `dcprint_ink_formula_item` ( - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, - `version_id` BIGINT UNSIGNED NOT NULL COMMENT '版本ID', - `material_id` BIGINT UNSIGNED COMMENT '物料ID(base_ink.id)', - `material_code` VARCHAR(50) NOT NULL COMMENT '物料编码', - `material_name` VARCHAR(100) NOT NULL COMMENT '物料名称', - `ink_type` VARCHAR(20) COMMENT '油墨类型', - `brand` VARCHAR(100) COMMENT '品牌', - `ratio` DECIMAL(8,4) NOT NULL DEFAULT 0 COMMENT '配比百分比', - `weight` DECIMAL(10,3) COMMENT '重量', - `unit` VARCHAR(10) DEFAULT 'kg' COMMENT '单位', - `add_order` INT NOT NULL DEFAULT 0 COMMENT '加料顺序', - `process_remark` VARCHAR(255) COMMENT '工艺备注', - `sort` INT NOT NULL DEFAULT 0 COMMENT '排序', - `is_base` TINYINT NOT NULL DEFAULT 0 COMMENT '是否基墨', - `snapshot_unit_cost` DECIMAL(12,4) COMMENT '快照单位成本', - `create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - PRIMARY KEY (`id`), - KEY `idx_version_id` (`version_id`), - CONSTRAINT `fk_formula_item_version` FOREIGN KEY (`version_id`) REFERENCES `dcprint_ink_formula_version` (`id`) ON DELETE CASCADE ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='油墨配方明细表'; diff --git a/database/migrations/048_create_tool_lifecycle_tables.sql b/database/migrations/048_create_tool_lifecycle_tables.sql deleted file mode 100644 index bb6be19c..00000000 --- a/database/migrations/048_create_tool_lifecycle_tables.sql +++ /dev/null @@ -1,79 +0,0 @@ --- Migration 048: Create die-cutting/screen-plate tooling lifecycle management tables --- Per docs/刀模 _ 网版工装全生命周期管理 完整落地方案.md.md --- Single-table inheritance: tool_type 1=die, 2=screen_plate --- 5-state lifecycle: 1=待用, 2=在用, 3=维修中, 4=预警, 5=已报废 - -CREATE TABLE IF NOT EXISTS `dcprint_tool` ( - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, - `tool_type` TINYINT NOT NULL COMMENT '1-刀模 2-网版', - `tool_code` VARCHAR(50) NOT NULL COMMENT '工装编码', - `tool_name` VARCHAR(100) NOT NULL COMMENT '工装名称', - `spec` VARCHAR(255) DEFAULT NULL COMMENT '规格', - `material_id` BIGINT UNSIGNED DEFAULT NULL COMMENT '关联物料ID', - `total_life` INT NOT NULL COMMENT '额定总寿命(次数)', - `warning_threshold` INT NOT NULL COMMENT '预警阈值(次数)', - `used_count` INT NOT NULL DEFAULT 0 COMMENT '已使用次数', - `remain_life` INT NOT NULL COMMENT '剩余寿命', - `original_cost` DECIMAL(10,2) NOT NULL COMMENT '原值', - `accumulated_cost` DECIMAL(10,2) NOT NULL DEFAULT 0 COMMENT '累计分摊成本', - `net_value` DECIMAL(10,2) NOT NULL COMMENT '账面净值', - `unit_cost` DECIMAL(10,4) NOT NULL COMMENT '单次分摊成本', - `status` TINYINT NOT NULL DEFAULT 1 COMMENT '1-待用 2-在用 3-维修中 4-预警 5-已报废', - `manufacture_date` DATE DEFAULT NULL COMMENT '制作日期', - `warehouse_location` VARCHAR(100) DEFAULT NULL COMMENT '存放位置', - `remark` TEXT DEFAULT NULL COMMENT '备注', - `scrap_reason` TEXT DEFAULT NULL COMMENT '报废原因', - `scrap_time` DATETIME DEFAULT NULL COMMENT '报废时间', - `scrap_by` BIGINT UNSIGNED DEFAULT NULL COMMENT '报废人', - `create_by` BIGINT UNSIGNED DEFAULT NULL, - `create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - `update_by` BIGINT UNSIGNED DEFAULT NULL, - `update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - `is_deleted` TINYINT NOT NULL DEFAULT 0, - PRIMARY KEY (`id`), - UNIQUE KEY `uk_tool_code` (`tool_code`), - KEY `type_status_idx` (`tool_type`, `status`), - KEY `idx_material` (`material_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='刀模/网版工装主档案表'; - -CREATE TABLE IF NOT EXISTS `dcprint_tool_usage` ( - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, - `tool_id` BIGINT UNSIGNED NOT NULL COMMENT '工装ID', - `work_order_id` BIGINT UNSIGNED DEFAULT NULL COMMENT '关联工单ID', - `work_order_no` VARCHAR(50) DEFAULT NULL COMMENT '关联工单号', - `process_id` BIGINT UNSIGNED DEFAULT NULL COMMENT '工序ID', - `process_name` VARCHAR(100) DEFAULT NULL COMMENT '工序名称', - `use_count` INT NOT NULL DEFAULT 1 COMMENT '本次使用次数(=报工数量)', - `operator_id` BIGINT UNSIGNED DEFAULT NULL COMMENT '操作人员ID', - `operator_name` VARCHAR(100) DEFAULT NULL COMMENT '操作人员姓名', - `amortized_cost` DECIMAL(10,4) NOT NULL DEFAULT 0 COMMENT '本次分摊成本快照', - `use_time` DATETIME NOT NULL COMMENT '使用时间', - `remark` VARCHAR(500) DEFAULT NULL, - `create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - PRIMARY KEY (`id`), - KEY `idx_tool` (`tool_id`), - KEY `idx_work_order` (`work_order_id`), - KEY `idx_use_time` (`use_time`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='工装使用记录表'; - -CREATE TABLE IF NOT EXISTS `dcprint_tool_maintenance` ( - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, - `tool_id` BIGINT UNSIGNED NOT NULL COMMENT '工装ID', - `maintenance_type` TINYINT NOT NULL DEFAULT 1 COMMENT '1-维修 2-保养', - `maintenance_cost` DECIMAL(10,2) NOT NULL DEFAULT 0 COMMENT '维修费用', - `description` TEXT COMMENT '维修内容', - `life_before` INT NOT NULL COMMENT '维修前剩余寿命', - `life_after` INT NOT NULL COMMENT '维修后剩余寿命(手动调整)', - `life_adjustment` INT NOT NULL DEFAULT 0 COMMENT '寿命调整量', - `status` TINYINT NOT NULL DEFAULT 1 COMMENT '1-进行中 2-已完成', - `start_time` DATETIME NOT NULL COMMENT '开始时间', - `end_time` DATETIME DEFAULT NULL COMMENT '完成时间', - `operator_id` BIGINT UNSIGNED DEFAULT NULL COMMENT '维修人ID', - `operator_name` VARCHAR(100) DEFAULT NULL COMMENT '维修人姓名', - `remark` VARCHAR(500) DEFAULT NULL, - `create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - `update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - PRIMARY KEY (`id`), - KEY `idx_tool` (`tool_id`), - KEY `idx_status` (`status`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='工装维修记录表'; diff --git a/database/migrations/049_create_sample_process_card_tables.sql b/database/migrations/049_create_sample_process_card_tables.sql deleted file mode 100644 index 225de05a..00000000 --- a/database/migrations/049_create_sample_process_card_tables.sql +++ /dev/null @@ -1,91 +0,0 @@ --- Migration 049: 打样工艺卡 — 创建主表、物料明细表、工序明细表 --- 依据: docs/打样工艺卡录入页统一完善方案.md --- 双录入页同源同库:经典版(input)和高效版(input-v2)共用此数据模型 --- 关联: crm_customer, inv_material, dcprint_ink_color, dcprint_tool, prd_process_route_step - --- 1. 打样工艺卡主表 -CREATE TABLE IF NOT EXISTS `dcprint_sample_process_card` ( - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, - `sample_no` VARCHAR(50) NOT NULL COMMENT '打样编号(唯一)', - `sample_name` VARCHAR(100) NOT NULL COMMENT '打样名称', - `customer_id` BIGINT UNSIGNED DEFAULT NULL COMMENT '客户ID', - `customer_name` VARCHAR(100) DEFAULT NULL COMMENT '客户名称(冗余)', - `product_id` BIGINT UNSIGNED DEFAULT NULL COMMENT '产品ID', - `product_name` VARCHAR(200) DEFAULT NULL COMMENT '产品名称', - `version_no` VARCHAR(20) NOT NULL DEFAULT 'V1.0' COMMENT '版本号', - `status` TINYINT NOT NULL DEFAULT 1 COMMENT '1-草稿 2-打样中 3-已确认 4-已作废', - `substrate_material_id` BIGINT UNSIGNED DEFAULT NULL COMMENT '基材物料ID', - `substrate_material_name` VARCHAR(100) DEFAULT NULL COMMENT '基材名称(冗余)', - `spec` VARCHAR(255) DEFAULT NULL COMMENT '规格', - `print_color` VARCHAR(100) DEFAULT NULL COMMENT '印刷颜色描述', - `ink_color_id` BIGINT UNSIGNED DEFAULT NULL COMMENT '油墨色号ID', - `screen_plate_id` BIGINT UNSIGNED DEFAULT NULL COMMENT '网版工装ID', - `die_tool_id` BIGINT UNSIGNED DEFAULT NULL COMMENT '刀模工装ID', - `material_loss_rate` DECIMAL(5,2) DEFAULT 5.00 COMMENT '物料损耗率(%)', - `estimated_hour` DECIMAL(6,2) DEFAULT NULL COMMENT '预估工时', - `sample_work_order_id` BIGINT UNSIGNED DEFAULT NULL COMMENT '打样工单ID', - `sample_work_order_no` VARCHAR(50) DEFAULT NULL COMMENT '打样工单号', - `quote_id` BIGINT UNSIGNED DEFAULT NULL COMMENT '报价单ID', - `formal_work_order_id` BIGINT UNSIGNED DEFAULT NULL COMMENT '正式工单ID', - `source_version_id` BIGINT UNSIGNED DEFAULT NULL COMMENT '源版本ID(复制来源)', - `confirm_by` BIGINT UNSIGNED DEFAULT NULL COMMENT '确认人ID', - `confirm_time` DATETIME DEFAULT NULL COMMENT '确认时间', - `total_material_cost` DECIMAL(12,4) DEFAULT 0.0000 COMMENT '物料总成本快照', - `total_labor_cost` DECIMAL(12,4) DEFAULT 0.0000 COMMENT '人工总成本快照', - `total_tool_cost` DECIMAL(12,4) DEFAULT 0.0000 COMMENT '工装总成本快照', - `total_cost` DECIMAL(12,4) DEFAULT 0.0000 COMMENT '总成本快照', - `remark` TEXT DEFAULT NULL COMMENT '备注', - `create_by` BIGINT UNSIGNED DEFAULT NULL COMMENT '创建人ID', - `create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - `update_by` BIGINT UNSIGNED DEFAULT NULL COMMENT '更新人ID', - `update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', - `deleted` TINYINT NOT NULL DEFAULT 0 COMMENT '软删除', - PRIMARY KEY (`id`), - UNIQUE KEY `uk_sample_no` (`sample_no`), - KEY `idx_customer` (`customer_id`), - KEY `idx_status` (`status`), - KEY `idx_ink_color` (`ink_color_id`), - KEY `idx_die_tool` (`die_tool_id`), - KEY `idx_screen_plate` (`screen_plate_id`), - KEY `idx_source_version` (`source_version_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='打样工艺卡主表'; - --- 2. 工艺卡物料明细表 -CREATE TABLE IF NOT EXISTS `dcprint_sample_process_item` ( - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, - `card_id` BIGINT UNSIGNED NOT NULL COMMENT '工艺卡ID', - `item_type` TINYINT NOT NULL DEFAULT 1 COMMENT '1-主料 2-油墨 3-辅料', - `material_id` BIGINT UNSIGNED DEFAULT NULL COMMENT '物料ID(inv_material.id)', - `material_code` VARCHAR(50) NOT NULL COMMENT '物料编码', - `material_name` VARCHAR(100) NOT NULL COMMENT '物料名称', - `specification` VARCHAR(255) DEFAULT NULL COMMENT '规格', - `unit_dosage` DECIMAL(10,4) NOT NULL COMMENT '单耗', - `unit` VARCHAR(20) DEFAULT NULL COMMENT '单位', - `unit_cost` DECIMAL(12,4) DEFAULT 0.0000 COMMENT '单价快照', - `line_cost` DECIMAL(12,4) DEFAULT 0.0000 COMMENT '行成本快照', - `remark` VARCHAR(255) DEFAULT NULL COMMENT '备注', - `sort` INT NOT NULL DEFAULT 0 COMMENT '排序', - `create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - PRIMARY KEY (`id`), - KEY `idx_card_id` (`card_id`), - KEY `idx_material_id` (`material_id`), - CONSTRAINT `fk_sample_item_card` FOREIGN KEY (`card_id`) REFERENCES `dcprint_sample_process_card` (`id`) ON DELETE CASCADE ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='打样工艺卡物料明细表'; - --- 3. 工艺卡工序明细表 -CREATE TABLE IF NOT EXISTS `dcprint_sample_process_step` ( - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, - `card_id` BIGINT UNSIGNED NOT NULL COMMENT '工艺卡ID', - `process_id` BIGINT UNSIGNED DEFAULT NULL COMMENT '标准工序ID(prd_process_route_step.id)', - `process_name` VARCHAR(100) NOT NULL COMMENT '工序名称', - `work_hour` DECIMAL(6,2) NOT NULL COMMENT '工时', - `hourly_rate` DECIMAL(10,2) DEFAULT 0.00 COMMENT '工时单价快照', - `line_cost` DECIMAL(12,4) DEFAULT 0.0000 COMMENT '行成本快照', - `process_param` TEXT DEFAULT NULL COMMENT '工艺参数', - `sort` INT NOT NULL DEFAULT 0 COMMENT '排序', - `create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - PRIMARY KEY (`id`), - KEY `idx_card_id` (`card_id`), - KEY `idx_process_id` (`process_id`), - CONSTRAINT `fk_sample_step_card` FOREIGN KEY (`card_id`) REFERENCES `dcprint_sample_process_card` (`id`) ON DELETE CASCADE ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='打样工艺卡工序明细表'; diff --git a/database/migrations/050_add_sample_card_diagram_url.sql b/database/migrations/050_add_sample_card_diagram_url.sql deleted file mode 100644 index 590a8085..00000000 --- a/database/migrations/050_add_sample_card_diagram_url.sql +++ /dev/null @@ -1,6 +0,0 @@ --- Migration 050: 打样工艺卡 — 新增工艺图示字段(图文混排) --- 依据: docs/打样工艺卡录入页统一完善方案.md --- 支持在工艺卡中插入工艺简图,使信息更直观 - -ALTER TABLE `dcprint_sample_process_card` - ADD COLUMN `diagram_url` VARCHAR(500) DEFAULT NULL COMMENT '工艺图示URL' AFTER `remark`; diff --git a/database/migrations/051_create_sample_process_template_tables.sql b/database/migrations/051_create_sample_process_template_tables.sql deleted file mode 100644 index 3bf48d8b..00000000 --- a/database/migrations/051_create_sample_process_template_tables.sql +++ /dev/null @@ -1,81 +0,0 @@ --- Migration 051: 标准工艺模板库 — 统一模板与录入即沉淀 --- 依据: docs/打样工艺卡录入页统一完善方案.md --- 镜像打样工艺卡结构,支持「存为模板」与「从模板导入」全链路 - --- 1. 标准工艺模板主表 -CREATE TABLE IF NOT EXISTS `dcprint_sample_process_template` ( - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, - `template_no` VARCHAR(50) NOT NULL COMMENT '模板编号', - `template_name` VARCHAR(100) NOT NULL COMMENT '模板名称', - `category` VARCHAR(50) DEFAULT NULL COMMENT '分类(如:标签/软包装/纸盒)', - `tags` VARCHAR(255) DEFAULT NULL COMMENT '标签(逗号分隔)', - `description` TEXT DEFAULT NULL COMMENT '模板描述', - `source_card_id` BIGINT UNSIGNED DEFAULT NULL COMMENT '来源工艺卡ID', - `customer_id` BIGINT UNSIGNED DEFAULT NULL, - `customer_name` VARCHAR(100) DEFAULT NULL, - `product_name` VARCHAR(200) DEFAULT NULL, - `substrate_material_id` BIGINT UNSIGNED DEFAULT NULL, - `substrate_material_name` VARCHAR(100) DEFAULT NULL, - `spec` VARCHAR(255) DEFAULT NULL, - `print_color` VARCHAR(100) DEFAULT NULL, - `ink_color_id` BIGINT UNSIGNED DEFAULT NULL, - `screen_plate_id` BIGINT UNSIGNED DEFAULT NULL, - `die_tool_id` BIGINT UNSIGNED DEFAULT NULL, - `material_loss_rate` DECIMAL(5,2) DEFAULT 5.00, - `estimated_hour` DECIMAL(6,2) DEFAULT NULL, - `diagram_url` VARCHAR(500) DEFAULT NULL COMMENT '工艺图示URL', - `total_material_cost` DECIMAL(12,4) DEFAULT 0.0000, - `total_labor_cost` DECIMAL(12,4) DEFAULT 0.0000, - `total_tool_cost` DECIMAL(12,4) DEFAULT 0.0000, - `total_cost` DECIMAL(12,4) DEFAULT 0.0000, - `remark` TEXT DEFAULT NULL, - `status` TINYINT NOT NULL DEFAULT 1 COMMENT '1-启用 2-停用', - `usage_count` INT NOT NULL DEFAULT 0 COMMENT '使用次数', - `create_by` BIGINT UNSIGNED DEFAULT NULL, - `create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - `update_by` BIGINT UNSIGNED DEFAULT NULL, - `update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - `deleted` TINYINT NOT NULL DEFAULT 0 COMMENT '软删除', - PRIMARY KEY (`id`), - UNIQUE KEY `uk_template_no` (`template_no`), - KEY `idx_category` (`category`), - KEY `idx_customer` (`customer_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='标准工艺模板主表'; - --- 2. 模板物料明细表 -CREATE TABLE IF NOT EXISTS `dcprint_sample_process_template_item` ( - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, - `template_id` BIGINT UNSIGNED NOT NULL COMMENT '模板ID', - `item_type` TINYINT NOT NULL DEFAULT 1 COMMENT '1-主料 2-油墨 3-辅料', - `material_id` BIGINT UNSIGNED DEFAULT NULL, - `material_code` VARCHAR(50) NOT NULL, - `material_name` VARCHAR(100) NOT NULL, - `specification` VARCHAR(255) DEFAULT NULL, - `unit_dosage` DECIMAL(10,4) NOT NULL, - `unit` VARCHAR(20) DEFAULT NULL, - `unit_cost` DECIMAL(12,4) DEFAULT 0.0000, - `line_cost` DECIMAL(12,4) DEFAULT 0.0000, - `remark` VARCHAR(255) DEFAULT NULL, - `sort` INT NOT NULL DEFAULT 0, - `create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - PRIMARY KEY (`id`), - KEY `idx_template_id` (`template_id`), - CONSTRAINT `fk_tpl_item_card` FOREIGN KEY (`template_id`) REFERENCES `dcprint_sample_process_template` (`id`) ON DELETE CASCADE ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='标准工艺模板物料明细表'; - --- 3. 模板工序明细表 -CREATE TABLE IF NOT EXISTS `dcprint_sample_process_template_step` ( - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, - `template_id` BIGINT UNSIGNED NOT NULL COMMENT '模板ID', - `process_id` BIGINT UNSIGNED DEFAULT NULL, - `process_name` VARCHAR(100) NOT NULL, - `work_hour` DECIMAL(6,2) NOT NULL, - `hourly_rate` DECIMAL(10,2) DEFAULT 0.00, - `line_cost` DECIMAL(12,4) DEFAULT 0.0000, - `process_param` TEXT DEFAULT NULL, - `sort` INT NOT NULL DEFAULT 0, - `create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - PRIMARY KEY (`id`), - KEY `idx_template_id` (`template_id`), - CONSTRAINT `fk_tpl_step_card` FOREIGN KEY (`template_id`) REFERENCES `dcprint_sample_process_template` (`id`) ON DELETE CASCADE ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='标准工艺模板工序明细表'; diff --git a/database/migrations/052_create_sample_quote_tables.sql b/database/migrations/052_create_sample_quote_tables.sql deleted file mode 100644 index 2f04b924..00000000 --- a/database/migrations/052_create_sample_quote_tables.sql +++ /dev/null @@ -1,54 +0,0 @@ --- Migration 052: 打样报价单 — 一键生成报价 --- 依据: docs/打样工艺卡录入页统一完善方案.md (阶段 3) --- 已确认工艺卡一键生成报价单,支持加价率、有效期、成本快照 - --- 1. 报价单主表 -CREATE TABLE IF NOT EXISTS `sal_quote` ( - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, - `quote_no` VARCHAR(50) NOT NULL COMMENT '报价单号', - `quote_date` DATE NOT NULL, - `customer_id` BIGINT UNSIGNED DEFAULT NULL, - `customer_name` VARCHAR(100) DEFAULT NULL, - `sample_card_id` BIGINT UNSIGNED DEFAULT NULL COMMENT '来源工艺卡ID', - `sample_no` VARCHAR(50) DEFAULT NULL, - `product_name` VARCHAR(200) DEFAULT NULL, - `quantity` INT NOT NULL DEFAULT 1, - `unit` VARCHAR(20) DEFAULT 'pcs', - `material_cost` DECIMAL(12,4) DEFAULT 0.0000 COMMENT '物料成本快照', - `labor_cost` DECIMAL(12,4) DEFAULT 0.0000 COMMENT '人工成本快照', - `tool_cost` DECIMAL(12,4) DEFAULT 0.0000 COMMENT '工装成本快照', - `total_cost` DECIMAL(12,4) DEFAULT 0.0000 COMMENT '成本合计快照', - `markup_rate` DECIMAL(5,2) DEFAULT 30.00 COMMENT '加价率(%)', - `quoted_price` DECIMAL(12,4) DEFAULT 0.0000 COMMENT '报价金额', - `currency` VARCHAR(10) DEFAULT 'CNY', - `status` TINYINT NOT NULL DEFAULT 1 COMMENT '1-草稿 2-已发送 3-已接受 4-已拒绝 5-已作废', - `valid_until` DATE DEFAULT NULL, - `remark` TEXT DEFAULT NULL, - `create_by` BIGINT UNSIGNED DEFAULT NULL, - `create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - `update_by` BIGINT UNSIGNED DEFAULT NULL, - `update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - `deleted` TINYINT NOT NULL DEFAULT 0, - PRIMARY KEY (`id`), - UNIQUE KEY `uk_quote_no` (`quote_no`), - KEY `idx_customer` (`customer_id`), - KEY `idx_sample_card` (`sample_card_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='报价单主表'; - --- 2. 报价明细表(支持多行报价,可选使用) -CREATE TABLE IF NOT EXISTS `sal_quote_item` ( - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, - `quote_id` BIGINT UNSIGNED NOT NULL, - `line_no` INT NOT NULL DEFAULT 1, - `item_name` VARCHAR(200) NOT NULL, - `quantity` DECIMAL(10,4) NOT NULL DEFAULT 1.0000, - `unit` VARCHAR(20) DEFAULT 'pcs', - `unit_cost` DECIMAL(12,4) DEFAULT 0.0000, - `unit_price` DECIMAL(12,4) DEFAULT 0.0000, - `total_price` DECIMAL(12,4) DEFAULT 0.0000, - `remark` VARCHAR(255) DEFAULT NULL, - `create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - PRIMARY KEY (`id`), - KEY `idx_quote_id` (`quote_id`), - CONSTRAINT `fk_quote_item_quote` FOREIGN KEY (`quote_id`) REFERENCES `sal_quote` (`id`) ON DELETE CASCADE ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='报价单明细表'; diff --git a/database/migrations/053_add_tool_columns_to_work_report.sql b/database/migrations/053_add_tool_columns_to_work_report.sql deleted file mode 100644 index 9ba1cfe2..00000000 --- a/database/migrations/053_add_tool_columns_to_work_report.sql +++ /dev/null @@ -1,12 +0,0 @@ --- Migration 053: 报工表新增工装关联列(刀模/网版) --- 依据: docs/刀模 _ 网版工装全生命周期管理 完整落地方案.md 第7.2节 --- 目的: 打通报工 → 工装寿命累计事件驱动联动 --- 关联: dcprint_tool.id(不加 FK 约束,与 dcprint_sample_process_card.die_tool_id 现有模式一致) - -ALTER TABLE `prd_work_report` - ADD COLUMN `tool_id` BIGINT UNSIGNED DEFAULT NULL COMMENT '关联刀模工装ID(dcprint_tool.id)' AFTER `remark`, - ADD COLUMN `screen_plate_id` BIGINT UNSIGNED DEFAULT NULL COMMENT '关联网版工装ID(dcprint_tool.id)' AFTER `tool_id`; - -ALTER TABLE `prd_work_report` - ADD INDEX `idx_tool_id` (`tool_id`), - ADD INDEX `idx_screen_plate_id` (`screen_plate_id`); diff --git a/database/migrations/054_add_work_order_cumulative_fields.sql b/database/migrations/054_add_work_order_cumulative_fields.sql deleted file mode 100644 index 14d45772..00000000 --- a/database/migrations/054_add_work_order_cumulative_fields.sql +++ /dev/null @@ -1,9 +0,0 @@ --- Migration 054: prod_work_order 新增累计统计字段 --- 依据: docs/生产工单 - 领料 - 库存 - 完工入库 全链路完善方案.md 第2.4节 --- 目的: 工单累计已领/完工/退料数量与材料成本,用于快速查询与状态判断 - -ALTER TABLE `prod_work_order` - ADD COLUMN `picked_qty` DECIMAL(10,2) DEFAULT 0.00 COMMENT '累计已领数量(按BOM折算)' AFTER `deleted`, - ADD COLUMN `finished_qty` DECIMAL(10,2) DEFAULT 0.00 COMMENT '累计完工合格数量' AFTER `picked_qty`, - ADD COLUMN `returned_qty` DECIMAL(10,2) DEFAULT 0.00 COMMENT '累计退料数量' AFTER `finished_qty`, - ADD COLUMN `total_material_cost` DECIMAL(12,2) DEFAULT 0.00 COMMENT '累计材料成本' AFTER `returned_qty`; diff --git a/database/migrations/055_add_prd_schedule_indexes.sql b/database/migrations/055_add_prd_schedule_indexes.sql deleted file mode 100644 index 98555c09..00000000 --- a/database/migrations/055_add_prd_schedule_indexes.sql +++ /dev/null @@ -1,66 +0,0 @@ --- 055: 为 prd_schedule 和 prd_schedule_detail 表补充索引 --- 背景:这两张表已存在于数据库,但 ORM Schema 补全后需要确保索引完整 - --- 1. 确保 prd_schedule.work_order_id 有索引 -SET @exists = ( - SELECT COUNT(*) FROM information_schema.statistics - WHERE table_schema = DATABASE() - AND table_name = 'prd_schedule' - AND index_name = 'idx_schedule_work_order' -); - -SET @sql = IF(@exists = 0, - 'ALTER TABLE prd_schedule ADD INDEX idx_schedule_work_order (work_order_id)', - 'SELECT 1' -); -PREPARE stmt FROM @sql; -EXECUTE stmt; -DEALLOCATE PREPARE stmt; - --- 2. 确保 prd_schedule_detail.schedule_id 有索引 -SET @exists = ( - SELECT COUNT(*) FROM information_schema.statistics - WHERE table_schema = DATABASE() - AND table_name = 'prd_schedule_detail' - AND index_name = 'idx_detail_schedule' -); - -SET @sql = IF(@exists = 0, - 'ALTER TABLE prd_schedule_detail ADD INDEX idx_detail_schedule (schedule_id)', - 'SELECT 1' -); -PREPARE stmt FROM @sql; -EXECUTE stmt; -DEALLOCATE PREPARE stmt; - --- 3. 确保 prd_schedule_detail.work_order_id 有索引 -SET @exists = ( - SELECT COUNT(*) FROM information_schema.statistics - WHERE table_schema = DATABASE() - AND table_name = 'prd_schedule_detail' - AND index_name = 'idx_detail_work_order' -); - -SET @sql = IF(@exists = 0, - 'ALTER TABLE prd_schedule_detail ADD INDEX idx_detail_work_order (work_order_id)', - 'SELECT 1' -); -PREPARE stmt FROM @sql; -EXECUTE stmt; -DEALLOCATE PREPARE stmt; - --- 4. 为 prd_schedule 添加计划开始时间索引(如缺失) -SET @exists = ( - SELECT COUNT(*) FROM information_schema.statistics - WHERE table_schema = DATABASE() - AND table_name = 'prd_schedule' - AND index_name = 'idx_schedule_planned_start' -); - -SET @sql = IF(@exists = 0, - 'ALTER TABLE prd_schedule ADD INDEX idx_schedule_planned_start (planned_start)', - 'SELECT 1' -); -PREPARE stmt FROM @sql; -EXECUTE stmt; -DEALLOCATE PREPARE stmt; diff --git a/database/migrations/056_expand_tool_unified_fields.sql b/database/migrations/056_expand_tool_unified_fields.sql deleted file mode 100644 index 72963d16..00000000 --- a/database/migrations/056_expand_tool_unified_fields.sql +++ /dev/null @@ -1,28 +0,0 @@ --- 056: 扩展 dcprint_tool 表,统一刀模/网版/工装三套体系字段 --- 来源: 体系B (dcprint_die_template) + 体系C (dcprint_screen_plate) - -ALTER TABLE dcprint_tool - -- 体系B 字段 (刀模) - ADD COLUMN asset_type VARCHAR(50) DEFAULT NULL COMMENT '资产类型' AFTER warehouse_location, - ADD COLUMN layout_type VARCHAR(50) DEFAULT NULL COMMENT '版面类型' AFTER asset_type, - ADD COLUMN pieces_per_impression INT DEFAULT NULL COMMENT '每版印张数' AFTER layout_type, - ADD COLUMN material VARCHAR(100) DEFAULT NULL COMMENT '材质' AFTER pieces_per_impression, - ADD COLUMN qr_code VARCHAR(255) DEFAULT NULL COMMENT '二维码' AFTER material, - ADD COLUMN supplier_id BIGINT UNSIGNED DEFAULT NULL COMMENT '供应商ID' AFTER qr_code, - ADD COLUMN maintenance_interval INT DEFAULT NULL COMMENT '保养间隔(印数)' AFTER supplier_id, - ADD COLUMN maintenance_count INT DEFAULT 0 COMMENT '保养次数' AFTER maintenance_interval, - ADD COLUMN last_maintenance_date DATE DEFAULT NULL COMMENT '上次保养日期' AFTER maintenance_count, - ADD COLUMN last_maintenance_impressions INT DEFAULT NULL COMMENT '上次保养印数' AFTER last_maintenance_date, - ADD COLUMN last_used_date DATE DEFAULT NULL COMMENT '上次使用日期' AFTER last_maintenance_impressions, - -- 体系C 字段 (网版) - ADD COLUMN mesh_count VARCHAR(20) DEFAULT NULL COMMENT '目数' AFTER last_used_date, - ADD COLUMN mesh_material VARCHAR(50) DEFAULT NULL COMMENT '丝网材质' AFTER mesh_count, - ADD COLUMN size VARCHAR(50) DEFAULT NULL COMMENT '尺寸' AFTER mesh_material, - ADD COLUMN tension_value DECIMAL(5,1) DEFAULT NULL COMMENT '张力值' AFTER size, - ADD COLUMN frame_type VARCHAR(50) DEFAULT NULL COMMENT '网框类型' AFTER tension_value, - ADD COLUMN customer_id BIGINT UNSIGNED DEFAULT NULL COMMENT '客户ID' AFTER frame_type, - ADD COLUMN reclaim_count INT DEFAULT 0 COMMENT '回用次数' AFTER customer_id, - ADD COLUMN exposure_date DATE DEFAULT NULL COMMENT '曝光日期' AFTER reclaim_count, - ADD COLUMN last_clean_date DATE DEFAULT NULL COMMENT '上次清洗日期' AFTER exposure_date, - ADD COLUMN last_reclaim_date DATE DEFAULT NULL COMMENT '上次回用日期' AFTER last_clean_date, - ADD COLUMN tension_date DATE DEFAULT NULL COMMENT '张力检测日期' AFTER last_reclaim_date; diff --git a/database/migrations/057_unify_pk_to_bigint.sql b/database/migrations/057_unify_pk_to_bigint.sql deleted file mode 100644 index edff2f94..00000000 --- a/database/migrations/057_unify_pk_to_bigint.sql +++ /dev/null @@ -1,54 +0,0 @@ --- 057: 统一主键类型为 BIGINT UNSIGNED --- 将 25 张使用 INT PK 的表改为 BIGINT UNSIGNED --- 注意: 需先修改外键引用,再修改主键 - --- ======================================== --- 阶段 1: 禁用外键检查 --- ======================================== -SET FOREIGN_KEY_CHECKS = 0; - --- ======================================== --- 阶段 2: 修改主键列类型 --- ======================================== - --- 仓库模块 -ALTER TABLE inv_inbound_order MODIFY COLUMN id INT UNSIGNED NOT NULL AUTO_INCREMENT; -ALTER TABLE inv_inbound_item MODIFY COLUMN id INT UNSIGNED NOT NULL AUTO_INCREMENT; -ALTER TABLE inv_warehouse MODIFY COLUMN id INT UNSIGNED NOT NULL AUTO_INCREMENT; -ALTER TABLE inv_inventory MODIFY COLUMN id INT UNSIGNED NOT NULL AUTO_INCREMENT; -ALTER TABLE inv_outbound_order MODIFY COLUMN id INT UNSIGNED NOT NULL AUTO_INCREMENT; -ALTER TABLE inv_outbound_item MODIFY COLUMN id INT UNSIGNED NOT NULL AUTO_INCREMENT; -ALTER TABLE inv_transfer_order MODIFY COLUMN id INT UNSIGNED NOT NULL AUTO_INCREMENT; -ALTER TABLE inv_stocktaking MODIFY COLUMN id INT UNSIGNED NOT NULL AUTO_INCREMENT; - --- 销售模块 -ALTER TABLE sal_order MODIFY COLUMN id INT UNSIGNED NOT NULL AUTO_INCREMENT; -ALTER TABLE sal_order_detail MODIFY COLUMN id INT UNSIGNED NOT NULL AUTO_INCREMENT; -ALTER TABLE sal_delivery MODIFY COLUMN id INT UNSIGNED NOT NULL AUTO_INCREMENT; -ALTER TABLE sal_return_order MODIFY COLUMN id INT UNSIGNED NOT NULL AUTO_INCREMENT; -ALTER TABLE sal_reconciliation MODIFY COLUMN id INT UNSIGNED NOT NULL AUTO_INCREMENT; -ALTER TABLE sal_quote MODIFY COLUMN id INT UNSIGNED NOT NULL AUTO_INCREMENT; -ALTER TABLE sal_quote_item MODIFY COLUMN id INT UNSIGNED NOT NULL AUTO_INCREMENT; - --- 采购模块 -ALTER TABLE pur_purchase_order_line MODIFY COLUMN id INT UNSIGNED NOT NULL AUTO_INCREMENT; - --- 财务模块 -ALTER TABLE fin_receivable MODIFY COLUMN id INT UNSIGNED NOT NULL AUTO_INCREMENT; -ALTER TABLE fin_payable MODIFY COLUMN id INT UNSIGNED NOT NULL AUTO_INCREMENT; - --- 生产模块 -ALTER TABLE prd_work_order MODIFY COLUMN id INT UNSIGNED NOT NULL AUTO_INCREMENT; -ALTER TABLE prod_work_order MODIFY COLUMN id INT UNSIGNED NOT NULL AUTO_INCREMENT; -ALTER TABLE prod_work_order_item MODIFY COLUMN id INT UNSIGNED NOT NULL AUTO_INCREMENT; -ALTER TABLE prod_work_order_material_req MODIFY COLUMN id INT UNSIGNED NOT NULL AUTO_INCREMENT; - --- 打样模块 -ALTER TABLE dcprint_sample_process_template MODIFY COLUMN id INT UNSIGNED NOT NULL AUTO_INCREMENT; -ALTER TABLE dcprint_sample_process_template_item MODIFY COLUMN id INT UNSIGNED NOT NULL AUTO_INCREMENT; -ALTER TABLE dcprint_sample_process_template_step MODIFY COLUMN id INT UNSIGNED NOT NULL AUTO_INCREMENT; - --- ======================================== --- 阶段 3: 重新启用外键检查 --- ======================================== -SET FOREIGN_KEY_CHECKS = 1; diff --git a/database/migrations/058_unify_collation.sql b/database/migrations/058_unify_collation.sql deleted file mode 100644 index 28c2d088..00000000 --- a/database/migrations/058_unify_collation.sql +++ /dev/null @@ -1,23 +0,0 @@ --- 058: 统一排序规则为 utf8mb4_0900_ai_ci --- 将所有使用 utf8mb4_unicode_ci 的列统一为 utf8mb4_0900_ai_ci - --- ======================================== --- 查找并修改所有使用 unicode_ci 的列 --- 以下为已知使用 utf8mb4_unicode_ci 的表(基于调研结果) --- ======================================== - --- 以下 SQL 会遍历所有使用 utf8mb4_unicode_ci 排序规则的列并修改 --- 注意: 在大表上执行可能需要较长时间 - --- 方法: 使用存储过程批量修改(如果可用) --- 或逐表修改已知的 28 处 unicode_ci 列 - --- 示例修改语句(按实际表结构调整): --- ALTER TABLE CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; - --- 注意: 此迁移需要根据实际数据库中使用 unicode_ci 的表来执行 --- 建议先运行以下查询确认需要修改的表: --- SELECT TABLE_NAME, COLUMN_NAME, COLLATION_NAME --- FROM INFORMATION_SCHEMA.COLUMNS --- WHERE TABLE_SCHEMA = DATABASE() --- AND COLLATION_NAME = 'utf8mb4_unicode_ci'; diff --git a/database/migrations/059_add_missing_indexes.sql b/database/migrations/059_add_missing_indexes.sql deleted file mode 100644 index c6a6d9e5..00000000 --- a/database/migrations/059_add_missing_indexes.sql +++ /dev/null @@ -1,44 +0,0 @@ --- 059: 补齐高基数列索引 --- 为外键、状态、日期等高查询频率列添加索引 - --- 仓库模块 -ALTER TABLE inv_inbound_order ADD INDEX idx_supplier_id (supplier_id); -ALTER TABLE inv_inbound_order ADD INDEX idx_po_id (po_id); - --- 销售模块 -ALTER TABLE sal_order ADD INDEX idx_status (status); -ALTER TABLE sal_order ADD INDEX idx_order_date (order_date); -ALTER TABLE sal_delivery ADD INDEX idx_status (status); -ALTER TABLE sal_delivery ADD INDEX idx_delivery_date (delivery_date); -ALTER TABLE sal_return_order ADD INDEX idx_delivery_id (delivery_id); -ALTER TABLE sal_return_order ADD INDEX idx_warehouse_id (warehouse_id); -ALTER TABLE sal_quote ADD INDEX idx_status (status); -ALTER TABLE sal_quote ADD INDEX idx_quote_date (quote_date); - --- 采购模块 -ALTER TABLE pur_purchase_order ADD INDEX idx_create_by (create_by); - --- 财务模块 -ALTER TABLE fin_receivable ADD INDEX idx_due_date (due_date); -ALTER TABLE fin_payable ADD INDEX idx_due_date (due_date); - --- 生产模块 -ALTER TABLE prd_work_order ADD INDEX idx_work_order_date (work_order_date); -ALTER TABLE prod_work_order ADD INDEX idx_status (status); -ALTER TABLE prod_work_order_item ADD INDEX idx_material_id (material_id); - --- 打样模块 -ALTER TABLE dcprint_sample_process_template ADD INDEX idx_status (status); -ALTER TABLE dcprint_sample_process_template_item ADD INDEX idx_material_id (material_id); - --- 工装模块 -ALTER TABLE dcprint_tool ADD INDEX idx_customer_id (customer_id); -ALTER TABLE dcprint_tool_usage ADD INDEX idx_operator_id (operator_id); -ALTER TABLE dcprint_tool_maintenance ADD INDEX idx_operator_id (operator_id); - --- 油墨模块 -ALTER TABLE dcprint_ink_formula_item ADD INDEX idx_material_id (material_id); - --- 盘点模块 -ALTER TABLE inv_stocktaking ADD INDEX idx_operator_id (operator_id); -ALTER TABLE inv_stocktaking ADD INDEX idx_taking_date (taking_date); diff --git a/database/migrations/060_unify_status_codes.sql b/database/migrations/060_unify_status_codes.sql deleted file mode 100644 index 3cad3e70..00000000 --- a/database/migrations/060_unify_status_codes.sql +++ /dev/null @@ -1,76 +0,0 @@ --- 060: 状态码统一 (varchar→tinyint) --- 将 inv_inbound_order, inv_outbound_order, prod_work_order 的 varchar 状态列改为 tinyint - --- ======================================== --- 状态码映射表 --- ======================================== --- inv_inbound_order.status: 'pending'=1, 'approved'=2, 'cancelled'=3 --- inv_inbound_order.qc_status: 'pending'=0, 'passed'=1, 'failed'=2 --- inv_outbound_order.status: 'draft'=1, 'pending'=2, 'approved'=3, 'shipped'=4, 'cancelled'=5 --- inv_outbound_order.audit_status: 'pending'=0, 'approved'=1, 'rejected'=2 --- prod_work_order.status: 'pending'=1, 'scheduled'=2, 'in_progress'=3, 'completed'=4, 'cancelled'=5 --- prod_work_order.priority: 'low'=1, 'normal'=2, 'high'=3, 'urgent'=4 - --- ======================================== --- 阶段 1: 添加新列 --- ======================================== -ALTER TABLE inv_inbound_order - ADD COLUMN status_new TINYINT NOT NULL DEFAULT 1 COMMENT '状态(新)' AFTER status, - ADD COLUMN qc_status_new TINYINT NOT NULL DEFAULT 0 COMMENT '质检状态(新)' AFTER qc_status; - -ALTER TABLE inv_outbound_order - ADD COLUMN status_new TINYINT NOT NULL DEFAULT 1 COMMENT '状态(新)' AFTER status, - ADD COLUMN audit_status_new TINYINT NOT NULL DEFAULT 0 COMMENT '审核状态(新)' AFTER audit_status; - -ALTER TABLE prod_work_order - ADD COLUMN status_new TINYINT NOT NULL DEFAULT 1 COMMENT '状态(新)' AFTER status, - ADD COLUMN priority_new TINYINT NOT NULL DEFAULT 2 COMMENT '优先级(新)' AFTER priority; - --- ======================================== --- 阶段 2: 数据迁移 --- ======================================== --- inv_inbound_order.status -UPDATE inv_inbound_order SET status_new = 1 WHERE status = 'pending'; -UPDATE inv_inbound_order SET status_new = 2 WHERE status = 'approved'; -UPDATE inv_inbound_order SET status_new = 3 WHERE status = 'cancelled'; - --- inv_inbound_order.qc_status -UPDATE inv_inbound_order SET qc_status_new = 0 WHERE qc_status = 'pending'; -UPDATE inv_inbound_order SET qc_status_new = 1 WHERE qc_status = 'passed'; -UPDATE inv_inbound_order SET qc_status_new = 2 WHERE qc_status = 'failed'; - --- inv_outbound_order.status -UPDATE inv_outbound_order SET status_new = 1 WHERE status = 'draft'; -UPDATE inv_outbound_order SET status_new = 2 WHERE status = 'pending'; -UPDATE inv_outbound_order SET status_new = 3 WHERE status = 'approved'; -UPDATE inv_outbound_order SET status_new = 4 WHERE status = 'shipped'; -UPDATE inv_outbound_order SET status_new = 5 WHERE status = 'cancelled'; - --- inv_outbound_order.audit_status -UPDATE inv_outbound_order SET audit_status_new = 0 WHERE audit_status = 'pending'; -UPDATE inv_outbound_order SET audit_status_new = 1 WHERE audit_status = 'approved'; -UPDATE inv_outbound_order SET audit_status_new = 2 WHERE audit_status = 'rejected'; - --- prod_work_order.status -UPDATE prod_work_order SET status_new = 1 WHERE status = 'pending'; -UPDATE prod_work_order SET status_new = 2 WHERE status = 'scheduled'; -UPDATE prod_work_order SET status_new = 3 WHERE status = 'in_progress'; -UPDATE prod_work_order SET status_new = 4 WHERE status = 'completed'; -UPDATE prod_work_order SET status_new = 5 WHERE status = 'cancelled'; - --- prod_work_order.priority -UPDATE prod_work_order SET priority_new = 1 WHERE priority = 'low'; -UPDATE prod_work_order SET priority_new = 2 WHERE priority = 'normal'; -UPDATE prod_work_order SET priority_new = 3 WHERE priority = 'high'; -UPDATE prod_work_order SET priority_new = 4 WHERE priority = 'urgent'; - --- ======================================== --- 阶段 3: 删除旧列,重命名新列 --- ======================================== --- 注意: 执行此阶段前确保应用代码已更新为使用新列名 --- ALTER TABLE inv_inbound_order DROP COLUMN status, RENAME COLUMN status_new TO status; --- ALTER TABLE inv_inbound_order DROP COLUMN qc_status, RENAME COLUMN qc_status_new TO qc_status; --- ALTER TABLE inv_outbound_order DROP COLUMN status, RENAME COLUMN status_new TO status; --- ALTER TABLE inv_outbound_order DROP COLUMN audit_status, RENAME COLUMN audit_status_new TO audit_status; --- ALTER TABLE prod_work_order DROP COLUMN status, RENAME COLUMN status_new TO status; --- ALTER TABLE prod_work_order DROP COLUMN priority, RENAME COLUMN priority_new TO priority; diff --git a/database/migrations/061_add_unique_index_inv_inventory_transaction.sql b/database/migrations/061_add_unique_index_inv_inventory_transaction.sql deleted file mode 100644 index ba6d80bb..00000000 --- a/database/migrations/061_add_unique_index_inv_inventory_transaction.sql +++ /dev/null @@ -1,34 +0,0 @@ --- ============================================================ --- Migration 061: 为 inv_inventory_transaction 添加 (source_type, source_id) 唯一索引 --- --- 背景: --- FinishOrderInventoryHandler 使用"先 SELECT 后 INSERT"模式做幂等检查, --- 当 StreamConsumer 的 XAUTOCLAIM 回收机制将同一事件重新投递给另一个消费者时, --- 两个消费者可能同时通过 SELECT 检查(均读到 0 行),随后各自执行 INSERT, --- 造成库存双计数(TOCTOU 竞态条件)。 --- --- Handler 侧已改为 INSERT IGNORE + 唯一索引做幂等保护,本 migration 提供底层约束。 --- --- 索引:uk_inv_txn_source (source_type, source_id) --- - source_type: 流水来源类型(如 'prod_finish') --- - source_id: 来源单据 ID(如 finishOrderId) --- --- 幂等模式:使用 INFORMATION_SCHEMA.STATISTICS 检查索引是否存在再添加 --- ============================================================ - --- 检查唯一索引 uk_inv_txn_source 是否已存在,不存在则添加 -SET @idx = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_inventory_transaction' AND INDEX_NAME = 'uk_inv_txn_source'); -SET @sql = IF(@idx = 0, - 'ALTER TABLE inv_inventory_transaction ADD UNIQUE INDEX uk_inv_txn_source (source_type, source_id)', - 'SELECT ''uk_inv_txn_source already exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 验证:确认索引已创建 -SELECT - INDEX_NAME, COLUMN_NAME, NON_UNIQUE, SEQ_IN_INDEX -FROM INFORMATION_SCHEMA.STATISTICS -WHERE TABLE_SCHEMA = DATABASE() - AND TABLE_NAME = 'inv_inventory_transaction' - AND INDEX_NAME = 'uk_inv_txn_source' -ORDER BY SEQ_IN_INDEX; diff --git a/database/migrations/062_add_unique_index_sal_order_no.sql b/database/migrations/062_add_unique_index_sal_order_no.sql deleted file mode 100644 index 8b117dea..00000000 --- a/database/migrations/062_add_unique_index_sal_order_no.sql +++ /dev/null @@ -1,35 +0,0 @@ --- ============================================================ --- Migration 062: 为 sal_order.order_no 添加 UNIQUE INDEX --- --- 背景: --- SampleOrderApplicationService.createSalesOrderFromSample() 使用 --- SELECT COUNT(*) + 1 生成销售订单号(SO + YYYYMMDD + 4位序号)。 --- 当多个请求并发执行时,可能读到相同的 COUNT 值从而生成重复的订单号, --- 导致 INSERT 冲突或数据不一致。 --- --- 本 migration 为 order_no 列添加唯一索引,作为并发保护的底层约束。 --- 应用层已改为捕获 ER_DUP_ENTRY 并重试(序号递增),本索引提供最终防线。 --- --- 索引:uk_order_no (order_no) --- - 与 vnerpdacahng_schema.sql 中的 UNIQUE KEY uk_order_no 一致 --- - 确保已运行的旧数据库也拥有此约束 --- --- 幂等模式:使用 INFORMATION_SCHEMA.STATISTICS 检查索引是否存在再添加 --- ============================================================ - --- 检查唯一索引 uk_order_no 是否已存在,不存在则添加 -SET @idx = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'sal_order' AND INDEX_NAME = 'uk_order_no'); -SET @sql = IF(@idx = 0, - 'ALTER TABLE sal_order ADD UNIQUE INDEX uk_order_no (order_no)', - 'SELECT ''uk_order_no already exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 验证:确认索引已创建 -SELECT - INDEX_NAME, COLUMN_NAME, NON_UNIQUE, SEQ_IN_INDEX -FROM INFORMATION_SCHEMA.STATISTICS -WHERE TABLE_SCHEMA = DATABASE() - AND TABLE_NAME = 'sal_order' - AND INDEX_NAME = 'uk_order_no' -ORDER BY SEQ_IN_INDEX; diff --git a/database/migrations/063_create_currency_tables.sql b/database/migrations/063_create_currency_tables.sql deleted file mode 100644 index a1ca37f8..00000000 --- a/database/migrations/063_create_currency_tables.sql +++ /dev/null @@ -1,49 +0,0 @@ --- T063: 多币种基础设施 — 币种主数据 + 汇率表 + 公司本位币字段 - --- 1. 币种主数据表 -CREATE TABLE IF NOT EXISTS `sys_currency` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT, - `code` varchar(10) NOT NULL COMMENT '币种代码 ISO 4217 (CNY/USD/VND)', - `name` varchar(50) NOT NULL COMMENT '币种名称', - `symbol` varchar(10) DEFAULT NULL COMMENT '符号 (¥/$/₫)', - `decimal_places` tinyint DEFAULT 2 COMMENT '小数位 (CNY=2, USD=2, VND=0)', - `status` tinyint DEFAULT 1 COMMENT '1启用 0停用', - `sort` int DEFAULT 0, - `create_time` datetime DEFAULT CURRENT_TIMESTAMP, - `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - `create_by` bigint unsigned DEFAULT NULL, - `update_by` bigint unsigned DEFAULT NULL, - `deleted` tinyint DEFAULT 0, - PRIMARY KEY (`id`), - UNIQUE KEY `uk_currency_code` (`code`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='币种主数据'; - --- 预置数据 -INSERT IGNORE INTO `sys_currency` (`code`, `name`, `symbol`, `decimal_places`, `sort`) VALUES - ('CNY', '人民币', '¥', 2, 1), - ('USD', '美元', '$', 2, 2), - ('VND', '越南盾', '₫', 0, 3); - --- 2. 汇率表 -CREATE TABLE IF NOT EXISTS `sys_exchange_rate` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT, - `from_currency` varchar(10) NOT NULL COMMENT '源币种', - `to_currency` varchar(10) NOT NULL COMMENT '目标币种', - `rate` decimal(18,6) NOT NULL COMMENT '汇率', - `rate_date` date NOT NULL COMMENT '汇率日期', - `source` varchar(50) DEFAULT 'manual' COMMENT '汇率来源 manual/api', - `remark` varchar(200) DEFAULT NULL, - `create_time` datetime DEFAULT CURRENT_TIMESTAMP, - `create_by` bigint unsigned DEFAULT NULL, - PRIMARY KEY (`id`), - KEY `idx_from_to_date` (`from_currency`,`to_currency`,`rate_date`), - KEY `idx_date` (`rate_date`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='汇率表'; - --- 3. 公司表加本位币字段(幂等) -SET @col_exists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'sys_company' AND COLUMN_NAME = 'base_currency'); -SET @sql = IF(@col_exists = 0, - 'ALTER TABLE `sys_company` ADD COLUMN `base_currency` varchar(10) DEFAULT ''CNY'' COMMENT ''本位币'' AFTER `tax_no`', - 'SELECT ''base_currency already exists'''); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; diff --git a/database/migrations/064_purchase_multi_currency.sql b/database/migrations/064_purchase_multi_currency.sql deleted file mode 100644 index 01e9b7dd..00000000 --- a/database/migrations/064_purchase_multi_currency.sql +++ /dev/null @@ -1,107 +0,0 @@ --- T064: 采购模块多币种支持 — 6 张采购业务表新增本位币金额字段 --- 依赖: T063 sys_currency / sys_exchange_rate 已存在 --- 幂等: 逐列检查 INFORMATION_SCHEMA.COLUMNS,已存在则跳过 - --- ========================================== --- 1. pur_supplier — 供应商默认币种 --- ========================================== -SET @col_exists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'pur_supplier' AND COLUMN_NAME = 'default_currency'); -SET @sql = IF(@col_exists = 0, - 'ALTER TABLE pur_supplier ADD COLUMN `default_currency` VARCHAR(10) DEFAULT ''CNY'' NULL COMMENT ''供应商默认币种代码'' AFTER `supplier_code`', - 'SELECT ''pur_supplier.default_currency already exists'' AS msg'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- ========================================== --- 2. pur_purchase_order — 采购单本位币金额 --- ========================================== -SET @col_exists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'pur_purchase_order' AND COLUMN_NAME = 'base_total_amount'); -SET @sql = IF(@col_exists = 0, - 'ALTER TABLE pur_purchase_order - ADD COLUMN `base_total_amount` DECIMAL(18,4) DEFAULT 0.0000 NOT NULL COMMENT ''本位币订单总金额'' AFTER `grand_total`, - ADD COLUMN `base_tax_amount` DECIMAL(18,4) DEFAULT 0.0000 NOT NULL COMMENT ''本位币税额'' AFTER `base_total_amount`, - ADD COLUMN `base_grand_total` DECIMAL(18,4) DEFAULT 0.0000 NOT NULL COMMENT ''本位币含税总金额'' AFTER `base_tax_amount`', - 'SELECT ''pur_purchase_order.base_total_amount already exists'' AS msg'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- ========================================== --- 3. pur_purchase_order_line — 采购单行本位币金额 --- ========================================== -SET @col_exists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'pur_purchase_order_line' AND COLUMN_NAME = 'base_unit_price'); -SET @sql = IF(@col_exists = 0, - 'ALTER TABLE pur_purchase_order_line - ADD COLUMN `base_unit_price` DECIMAL(18,4) DEFAULT 0.0000 NOT NULL COMMENT ''本位币单价'' AFTER `line_total`, - ADD COLUMN `base_amount` DECIMAL(18,4) DEFAULT 0.0000 NOT NULL COMMENT ''本位币金额'' AFTER `base_unit_price`, - ADD COLUMN `base_tax_amount` DECIMAL(18,4) DEFAULT 0.0000 NOT NULL COMMENT ''本位币税额'' AFTER `base_amount`, - ADD COLUMN `base_line_total` DECIMAL(18,4) DEFAULT 0.0000 NOT NULL COMMENT ''本位币行合计'' AFTER `base_tax_amount`', - 'SELECT ''pur_purchase_order_line.base_unit_price already exists'' AS msg'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- ========================================== --- 4. pur_purchase_return — 采购退货单币种/汇率/本位币 --- ========================================== -SET @col_exists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'pur_purchase_return' AND COLUMN_NAME = 'currency'); -SET @sql = IF(@col_exists = 0, - 'ALTER TABLE pur_purchase_return - ADD COLUMN `currency` VARCHAR(10) DEFAULT ''CNY'' NOT NULL COMMENT ''币种'' AFTER `total_amount`, - ADD COLUMN `exchange_rate` DECIMAL(18,4) DEFAULT 1.0000 NOT NULL COMMENT ''汇率'' AFTER `currency`, - ADD COLUMN `base_total_amount` DECIMAL(18,4) DEFAULT 0.0000 NOT NULL COMMENT ''本位币退货总金额'' AFTER `exchange_rate`', - 'SELECT ''pur_purchase_return.currency already exists'' AS msg'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- ========================================== --- 5. pur_purchase_return_line — 采购退货单行本位币金额 --- ========================================== -SET @col_exists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'pur_purchase_return_line' AND COLUMN_NAME = 'base_unit_price'); -SET @sql = IF(@col_exists = 0, - 'ALTER TABLE pur_purchase_return_line - ADD COLUMN `base_unit_price` DECIMAL(18,4) DEFAULT 0.0000 NOT NULL COMMENT ''本位币单价'' AFTER `amount`, - ADD COLUMN `base_amount` DECIMAL(18,4) DEFAULT 0.0000 NOT NULL COMMENT ''本位币金额'' AFTER `base_unit_price`', - 'SELECT ''pur_purchase_return_line.base_unit_price already exists'' AS msg'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- ========================================== --- 6. pur_purchase_reconciliation — 采购对账单币种/汇率/本位币 --- ========================================== -SET @col_exists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'pur_purchase_reconciliation' AND COLUMN_NAME = 'currency'); -SET @sql = IF(@col_exists = 0, - 'ALTER TABLE pur_purchase_reconciliation - ADD COLUMN `currency` VARCHAR(10) DEFAULT ''CNY'' NOT NULL COMMENT ''币种'' AFTER `period_end`, - ADD COLUMN `exchange_rate` DECIMAL(18,4) DEFAULT 1.0000 NOT NULL COMMENT ''汇率'' AFTER `currency`, - ADD COLUMN `base_receipt_amount` DECIMAL(18,4) DEFAULT 0.0000 NOT NULL COMMENT ''本位币收货金额'' AFTER `receipt_amount`, - ADD COLUMN `base_return_amount` DECIMAL(18,4) DEFAULT 0.0000 NOT NULL COMMENT ''本位币退货金额'' AFTER `return_amount`, - ADD COLUMN `base_net_amount` DECIMAL(18,4) DEFAULT 0.0000 NOT NULL COMMENT ''本位币净额'' AFTER `net_amount`, - ADD COLUMN `base_discount_amount` DECIMAL(18,4) DEFAULT 0.0000 NOT NULL COMMENT ''本位币折扣金额'' AFTER `discount_amount`, - ADD COLUMN `base_paid_amount` DECIMAL(18,4) DEFAULT 0.0000 NOT NULL COMMENT ''本位币已核销金额'' AFTER `paid_amount`, - ADD COLUMN `base_balance_amount` DECIMAL(18,4) DEFAULT 0.0000 NOT NULL COMMENT ''本位币余额'' AFTER `balance_amount`', - 'SELECT ''pur_purchase_reconciliation.currency already exists'' AS msg'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- ========================================== --- 验证: 输出每个表新增的字段数 --- ========================================== -SELECT 'pur_supplier' AS table_name, COUNT(*) AS multi_currency_columns - FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'pur_supplier' AND (COLUMN_NAME LIKE 'base_%' OR COLUMN_NAME = 'default_currency') -UNION ALL -SELECT 'pur_purchase_order', COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'pur_purchase_order' AND COLUMN_NAME LIKE 'base_%' -UNION ALL -SELECT 'pur_purchase_order_line', COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'pur_purchase_order_line' AND COLUMN_NAME LIKE 'base_%' -UNION ALL -SELECT 'pur_purchase_return', COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'pur_purchase_return' - AND COLUMN_NAME IN ('currency','exchange_rate','base_total_amount') -UNION ALL -SELECT 'pur_purchase_return_line', COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'pur_purchase_return_line' AND COLUMN_NAME LIKE 'base_%' -UNION ALL -SELECT 'pur_purchase_reconciliation', COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'pur_purchase_reconciliation' - AND COLUMN_NAME IN ('currency','exchange_rate','base_receipt_amount','base_return_amount','base_net_amount','base_discount_amount','base_paid_amount','base_balance_amount'); diff --git a/database/migrations/065_backfill_purchase_currency.sql b/database/migrations/065_backfill_purchase_currency.sql deleted file mode 100644 index d1d95490..00000000 --- a/database/migrations/065_backfill_purchase_currency.sql +++ /dev/null @@ -1,58 +0,0 @@ --- 065_backfill_purchase_currency.sql --- Phase 2a: 历史数据回填 — 按订单日期查汇率,查不到 fallback 1.0 - --- 1. 回填供应商默认币种 -UPDATE pur_supplier SET default_currency = 'CNY' WHERE default_currency IS NULL; - --- 2. 回填采购订单 currency + exchange_rate(若为空) -UPDATE pur_purchase_order SET currency = 'CNY' WHERE currency IS NULL OR currency = ''; -UPDATE pur_purchase_order SET exchange_rate = 1.0000 WHERE exchange_rate IS NULL OR exchange_rate = 0; - --- 3. 回填采购订单 base_* 字段(仅处理 base_grand_total = 0 且 grand_total > 0 的记录) --- 使用子查询按 order_date 查历史汇率,查不到则用 1.0 -UPDATE pur_purchase_order po -LEFT JOIN ( - SELECT po2.id, COALESCE( - (SELECT rate FROM sys_exchange_rate er - WHERE er.from_currency = po2.currency AND er.to_currency = 'CNY' - AND er.rate_date <= po2.order_date - ORDER BY er.rate_date DESC LIMIT 1), - 1.0000 - ) AS historical_rate - FROM pur_purchase_order po2 - WHERE po2.base_grand_total = 0 AND po2.grand_total > 0 -) rates ON po.id = rates.id -SET po.exchange_rate = rates.historical_rate, - po.base_total_amount = ROUND(po.total_amount * rates.historical_rate, 4), - po.base_tax_amount = ROUND(po.tax_amount * rates.historical_rate, 4), - po.base_grand_total = ROUND(po.grand_total * rates.historical_rate, 4) -WHERE rates.id IS NOT NULL; - --- 4. 回填采购订单明细 base_* 字段 -UPDATE pur_purchase_order_line pol -INNER JOIN pur_purchase_order po ON pol.po_id = po.id -SET pol.base_unit_price = ROUND(pol.unit_price * po.exchange_rate, 4), - pol.base_amount = ROUND(pol.amount * po.exchange_rate, 4), - pol.base_tax_amount = ROUND(pol.tax_amount * po.exchange_rate, 4), - pol.base_line_total = ROUND(pol.line_total * po.exchange_rate, 4) -WHERE pol.base_line_total = 0 AND pol.line_total > 0; - --- 5. 回填退货单 currency + exchange_rate + base_total_amount -UPDATE pur_purchase_return pr -INNER JOIN pur_purchase_order po ON pr.order_id = po.id -SET pr.currency = po.currency, - pr.exchange_rate = po.exchange_rate, - pr.base_total_amount = ROUND(pr.total_amount * po.exchange_rate, 4) -WHERE pr.base_total_amount = 0 AND pr.total_amount > 0; - --- 6. 回填对账单(旧记录按 1.0 回填) -UPDATE pur_purchase_reconciliation SET currency = 'CNY' WHERE currency IS NULL OR currency = ''; -UPDATE pur_purchase_reconciliation SET exchange_rate = 1.0000 WHERE exchange_rate IS NULL OR exchange_rate = 0; -UPDATE pur_purchase_reconciliation -SET base_receipt_amount = ROUND(receipt_amount * exchange_rate, 4), - base_return_amount = ROUND(return_amount * exchange_rate, 4), - base_net_amount = ROUND(net_amount * exchange_rate, 4), - base_discount_amount = ROUND(discount_amount * exchange_rate, 4), - base_paid_amount = ROUND(paid_amount * exchange_rate, 4), - base_balance_amount = ROUND(balance_amount * exchange_rate, 4) -WHERE base_balance_amount = 0 AND balance_amount > 0; diff --git a/database/migrations/066_inventory_multi_currency.sql b/database/migrations/066_inventory_multi_currency.sql deleted file mode 100644 index bd433d7c..00000000 --- a/database/migrations/066_inventory_multi_currency.sql +++ /dev/null @@ -1,27 +0,0 @@ --- 库存模块多币种字段 --- Phase 2b: 入库/出库表补 currency + exchange_rate + base_amount 字段 - --- inv_inbound_order -ALTER TABLE `inv_inbound_order` - ADD COLUMN `currency` varchar(10) DEFAULT 'CNY' COMMENT '币种' AFTER `total_amount`, - ADD COLUMN `exchange_rate` decimal(18,6) DEFAULT 1.000000 COMMENT '汇率' AFTER `currency`, - ADD COLUMN `base_total_amount` decimal(18,4) DEFAULT 0.0000 COMMENT '本位币总金额' AFTER `exchange_rate`; - --- inv_inbound_item -ALTER TABLE `inv_inbound_item` - ADD COLUMN `base_unit_price` decimal(18,4) DEFAULT 0.0000 COMMENT '本位币单价' AFTER `unit_price`, - ADD COLUMN `base_amount` decimal(18,4) DEFAULT 0.0000 COMMENT '本位币金额' AFTER `amount`; - --- inv_outbound_order -ALTER TABLE `inv_outbound_order` - ADD COLUMN `exchange_rate` decimal(18,6) DEFAULT 1.000000 COMMENT '汇率' AFTER `currency`, - ADD COLUMN `base_total_amount` decimal(18,4) DEFAULT 0.0000 COMMENT '本位币总金额' AFTER `exchange_rate`; - --- inv_outbound_item -ALTER TABLE `inv_outbound_item` - ADD COLUMN `base_unit_price` decimal(18,4) DEFAULT 0.0000 COMMENT '本位币单价' AFTER `unit_price`, - ADD COLUMN `base_amount` decimal(18,4) DEFAULT 0.0000 COMMENT '本位币金额' AFTER `amount`; - --- 旧数据回填 -UPDATE `inv_inbound_order` SET `base_total_amount` = `total_amount` WHERE `base_total_amount` = 0; -UPDATE `inv_outbound_order` SET `base_total_amount` = `total_amount` WHERE `base_total_amount` = 0; diff --git a/database/migrations/067_sales_multi_currency.sql b/database/migrations/067_sales_multi_currency.sql deleted file mode 100644 index f0334eb6..00000000 --- a/database/migrations/067_sales_multi_currency.sql +++ /dev/null @@ -1,54 +0,0 @@ --- 销售模块多币种字段 --- Phase 2b: 销售订单/出库/退货/对账表补 currency + exchange_rate + base_* 字段 - --- sal_order(已有 currency + exchange_rate) -ALTER TABLE `sal_order` - ADD COLUMN `base_total_amount` decimal(18,4) DEFAULT 0.0000 COMMENT '本位币金额' AFTER `total_amount`, - ADD COLUMN `base_tax_amount` decimal(18,4) DEFAULT 0.0000 COMMENT '本位币税额' AFTER `tax_amount`, - ADD COLUMN `base_grand_total` decimal(18,4) DEFAULT 0.0000 COMMENT '本位币含税总额' AFTER `total_with_tax`; - --- sal_order_detail -ALTER TABLE `sal_order_detail` - ADD COLUMN `currency` varchar(10) DEFAULT 'CNY' COMMENT '币种' AFTER `material_spec`, - ADD COLUMN `exchange_rate` decimal(18,6) DEFAULT 1.000000 COMMENT '汇率' AFTER `currency`, - ADD COLUMN `base_unit_price` decimal(18,4) DEFAULT 0.0000 COMMENT '本位币单价' AFTER `unit_price`, - ADD COLUMN `base_amount` decimal(18,4) DEFAULT 0.0000 COMMENT '本位币金额' AFTER `amount`, - ADD COLUMN `base_tax_amount` decimal(18,4) DEFAULT 0.0000 COMMENT '本位币税额' AFTER `tax_amount`; - --- sal_delivery -ALTER TABLE `sal_delivery` - ADD COLUMN `currency` varchar(10) DEFAULT 'CNY' COMMENT '币种' AFTER `total_amount`, - ADD COLUMN `exchange_rate` decimal(18,6) DEFAULT 1.000000 COMMENT '汇率' AFTER `currency`, - ADD COLUMN `base_total_amount` decimal(18,4) DEFAULT 0.0000 COMMENT '本位币总金额' AFTER `exchange_rate`; - --- sal_delivery_detail -ALTER TABLE `sal_delivery_detail` - ADD COLUMN `base_unit_price` decimal(18,4) DEFAULT 0.0000 COMMENT '本位币单价' AFTER `unit_price`, - ADD COLUMN `base_amount` decimal(18,4) DEFAULT 0.0000 COMMENT '本位币金额' AFTER `amount`; - --- sal_return -ALTER TABLE `sal_return` - ADD COLUMN `currency` varchar(10) DEFAULT 'CNY' COMMENT '币种' AFTER `total_amount`, - ADD COLUMN `exchange_rate` decimal(18,6) DEFAULT 1.000000 COMMENT '汇率' AFTER `currency`, - ADD COLUMN `base_total_amount` decimal(18,4) DEFAULT 0.0000 COMMENT '本位币总金额' AFTER `exchange_rate`; - --- sal_return_detail -ALTER TABLE `sal_return_detail` - ADD COLUMN `base_unit_price` decimal(18,4) DEFAULT 0.0000 COMMENT '本位币单价' AFTER `unit_price`, - ADD COLUMN `base_amount` decimal(18,4) DEFAULT 0.0000 COMMENT '本位币金额' AFTER `amount`; - --- sal_reconciliation -ALTER TABLE `sal_reconciliation` - ADD COLUMN `currency` varchar(10) DEFAULT 'CNY' COMMENT '币种' AFTER `total_amount`, - ADD COLUMN `exchange_rate` decimal(18,6) DEFAULT 1.000000 COMMENT '汇率' AFTER `currency`, - ADD COLUMN `base_delivery_amount` decimal(18,4) DEFAULT 0.0000 COMMENT '本位币出库金额' AFTER `delivery_amount`, - ADD COLUMN `base_return_amount` decimal(18,4) DEFAULT 0.0000 COMMENT '本位币退货金额' AFTER `return_amount`, - ADD COLUMN `base_net_amount` decimal(18,4) DEFAULT 0.0000 COMMENT '本位币净额' AFTER `net_amount`, - ADD COLUMN `base_discount_amount` decimal(18,4) DEFAULT 0.0000 COMMENT '本位币折扣金额' AFTER `discount_amount`, - ADD COLUMN `base_received_amount` decimal(18,4) DEFAULT 0.0000 COMMENT '本位币实收金额' AFTER `received_amount`, - ADD COLUMN `base_balance_amount` decimal(18,4) DEFAULT 0.0000 COMMENT '本位币余额' AFTER `balance_amount`; - --- 旧数据回填 -UPDATE `sal_order` SET `base_total_amount` = `total_amount`, `base_tax_amount` = `tax_amount`, `base_grand_total` = `total_with_tax` WHERE `base_total_amount` = 0; -UPDATE `sal_delivery` SET `base_total_amount` = `total_amount` WHERE `base_total_amount` = 0; -UPDATE `sal_return` SET `base_total_amount` = `total_amount` WHERE `base_total_amount` = 0; diff --git a/database/migrations/068_finance_multi_currency.sql b/database/migrations/068_finance_multi_currency.sql deleted file mode 100644 index 3e6ea55f..00000000 --- a/database/migrations/068_finance_multi_currency.sql +++ /dev/null @@ -1,28 +0,0 @@ --- 财务模块多币种字段 --- Phase 2b: 应付/应收/付款/收款记录补 currency + exchange_rate + base_amount 字段 - -ALTER TABLE `fin_payable` - ADD COLUMN `currency` varchar(10) DEFAULT 'CNY' COMMENT '币种' AFTER `amount`, - ADD COLUMN `exchange_rate` decimal(18,6) DEFAULT 1.000000 COMMENT '汇率' AFTER `currency`, - ADD COLUMN `base_amount` decimal(18,4) DEFAULT 0.0000 COMMENT '本位币金额' AFTER `exchange_rate`; - -ALTER TABLE `fin_receivable` - ADD COLUMN `currency` varchar(10) DEFAULT 'CNY' COMMENT '币种' AFTER `amount`, - ADD COLUMN `exchange_rate` decimal(18,6) DEFAULT 1.000000 COMMENT '汇率' AFTER `currency`, - ADD COLUMN `base_amount` decimal(18,4) DEFAULT 0.0000 COMMENT '本位币金额' AFTER `exchange_rate`; - -ALTER TABLE `fin_payment_record` - ADD COLUMN `currency` varchar(10) DEFAULT 'CNY' COMMENT '币种' AFTER `amount`, - ADD COLUMN `exchange_rate` decimal(18,6) DEFAULT 1.000000 COMMENT '汇率' AFTER `currency`, - ADD COLUMN `base_amount` decimal(18,4) DEFAULT 0.0000 COMMENT '本位币金额' AFTER `exchange_rate`; - -ALTER TABLE `fin_receipt_record` - ADD COLUMN `currency` varchar(10) DEFAULT 'CNY' COMMENT '币种' AFTER `amount`, - ADD COLUMN `exchange_rate` decimal(18,6) DEFAULT 1.000000 COMMENT '汇率' AFTER `currency`, - ADD COLUMN `base_amount` decimal(18,4) DEFAULT 0.0000 COMMENT '本位币金额' AFTER `exchange_rate`; - --- 旧数据回填 -UPDATE `fin_payable` SET `base_amount` = `amount`, `currency` = 'CNY', `exchange_rate` = 1.0 WHERE `base_amount` = 0; -UPDATE `fin_receivable` SET `base_amount` = `amount`, `currency` = 'CNY', `exchange_rate` = 1.0 WHERE `base_amount` = 0; -UPDATE `fin_payment_record` SET `base_amount` = `amount`, `currency` = 'CNY', `exchange_rate` = 1.0 WHERE `base_amount` = 0; -UPDATE `fin_receipt_record` SET `base_amount` = `amount`, `currency` = 'CNY', `exchange_rate` = 1.0 WHERE `base_amount` = 0; diff --git a/database/migrations/069_enhance_sample_order.sql b/database/migrations/069_enhance_sample_order.sql deleted file mode 100644 index 60821173..00000000 --- a/database/migrations/069_enhance_sample_order.sql +++ /dev/null @@ -1,56 +0,0 @@ --- ============================================================================ --- 069: 打样订单模块增强(原编号 053,因与 053_add_tool_columns_to_work_report 重复而顺延) --- 功能: 增加工艺卡/生产工单/销售订单关联、打样费用管理、版本控制、 --- 反馈管理和样品库存管理 --- ============================================================================ - --- 1. 扩展 sal_sample_order 表,增加关联字段和业务字段(使用 IF NOT EXISTS 保证可重复执行) -ALTER TABLE `sal_sample_order` - ADD COLUMN IF NOT EXISTS `process_card_id` bigint unsigned DEFAULT NULL COMMENT '关联印前工艺卡ID' AFTER `create_by`, - ADD COLUMN IF NOT EXISTS `work_order_id` bigint unsigned DEFAULT NULL COMMENT '关联生产工单ID' AFTER `process_card_id`, - ADD COLUMN IF NOT EXISTS `sales_order_id` bigint unsigned DEFAULT NULL COMMENT '关联销售订单ID' AFTER `work_order_id`, - ADD COLUMN IF NOT EXISTS `sample_fee` decimal(18,4) DEFAULT 0 COMMENT '打样费用' AFTER `sales_order_id`, - ADD COLUMN IF NOT EXISTS `fee_charged` tinyint DEFAULT 0 COMMENT '是否收取打样费: 0-否 1-是' AFTER `sample_fee`, - ADD COLUMN IF NOT EXISTS `fee_deductible` tinyint DEFAULT 0 COMMENT '打样费是否可抵扣大货: 0-否 1-是' AFTER `fee_charged`, - ADD COLUMN IF NOT EXISTS `fee_deducted` tinyint DEFAULT 0 COMMENT '打样费是否已抵扣: 0-否 1-是' AFTER `fee_deductible`, - ADD COLUMN IF NOT EXISTS `sample_version` int DEFAULT 1 COMMENT '打样版本号(支持多轮改样)' AFTER `fee_deducted`, - ADD COLUMN IF NOT EXISTS `parent_version_id` bigint unsigned DEFAULT NULL COMMENT '父版本打样单ID' AFTER `sample_version`, - ADD COLUMN IF NOT EXISTS `converted_at` datetime DEFAULT NULL COMMENT '转大货时间' AFTER `parent_version_id`, - ADD COLUMN IF NOT EXISTS `converted_by` bigint unsigned DEFAULT NULL COMMENT '转大货操作人' AFTER `converted_at`; - --- 2. 创建打样反馈表 -CREATE TABLE IF NOT EXISTS `sal_sample_feedback` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT, - `sample_order_id` bigint unsigned NOT NULL COMMENT '关联打样单ID', - `round` int NOT NULL DEFAULT 1 COMMENT '反馈轮次', - `feedback_content` text COMMENT '客户反馈意见', - `modification_requirements` text COMMENT '修改要求', - `confirmation_status` varchar(20) DEFAULT 'pending' COMMENT '确认状态: pending/approved/rejected', - `feedback_by` varchar(100) COMMENT '反馈人', - `feedback_time` datetime COMMENT '反馈时间', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP, - `deleted` tinyint DEFAULT 0, - PRIMARY KEY (`id`), - KEY `idx_sample_order` (`sample_order_id`), - KEY `idx_round` (`sample_order_id`, `round`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='打样反馈表'; - --- 3. 创建样品库存表 -CREATE TABLE IF NOT EXISTS `sal_sample_inventory` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT, - `sample_order_id` bigint unsigned COMMENT '关联打样单ID', - `product_name` varchar(200) COMMENT '产品名称', - `material_no` varchar(50) COMMENT '物料编号', - `quantity` int DEFAULT 0 COMMENT '样品数量', - `unit` varchar(20) DEFAULT 'pcs' COMMENT '单位', - `warehouse_id` bigint unsigned COMMENT '仓库ID', - `status` varchar(20) DEFAULT 'available' COMMENT '状态: available/used/scrapped/sent', - `sent_to` varchar(200) COMMENT '寄送给谁', - `sent_date` date COMMENT '寄送日期', - `remark` text COMMENT '备注', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP, - `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - `deleted` tinyint DEFAULT 0, - PRIMARY KEY (`id`), - KEY `idx_sample_order` (`sample_order_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='样品库存表'; diff --git a/database/migrations/070_enhance_production_tables.sql b/database/migrations/070_enhance_production_tables.sql deleted file mode 100644 index e33e158c..00000000 --- a/database/migrations/070_enhance_production_tables.sql +++ /dev/null @@ -1,144 +0,0 @@ --- ============================================================================ --- 070: 生产管理模块增强 — 核心流程表(原编号 054,因与 054_add_work_order_cumulative_fields 重复而顺延) --- 功能: 工单状态增强、领料/退料/报工/完工入库表 --- ============================================================================ - --- 1. 扩展 prod_work_order 表,增加工序和成本字段(使用 IF NOT EXISTS 保证可重复执行) -ALTER TABLE `prod_work_order` - ADD COLUMN IF NOT EXISTS `order_type` tinyint DEFAULT 0 COMMENT '工单类型: 0-正常 1-打样工单 2-返工' AFTER `product_name`, - ADD COLUMN IF NOT EXISTS `approved_at` datetime DEFAULT NULL COMMENT '审核时间' AFTER `total_material_cost`, - ADD COLUMN IF NOT EXISTS `approved_by` bigint unsigned DEFAULT NULL COMMENT '审核人ID' AFTER `approved_at`, - ADD COLUMN IF NOT EXISTS `cancelled_at` datetime DEFAULT NULL COMMENT '作废时间' AFTER `approved_by`, - ADD COLUMN IF NOT EXISTS `cancelled_by` bigint unsigned DEFAULT NULL COMMENT '作废人ID' AFTER `cancelled_at`, - ADD COLUMN IF NOT EXISTS `cancelled_reason` varchar(500) DEFAULT NULL COMMENT '作废原因' AFTER `cancelled_by`, - ADD COLUMN IF NOT EXISTS `total_labor_cost` decimal(18,4) DEFAULT 0 COMMENT '人工成本合计' AFTER `total_material_cost`, - ADD COLUMN IF NOT EXISTS `total_tool_cost` decimal(18,4) DEFAULT 0 COMMENT '工装分摊成本' AFTER `total_labor_cost`, - ADD COLUMN IF NOT EXISTS `total_overhead_cost` decimal(18,4) DEFAULT 0 COMMENT '制造费用' AFTER `total_tool_cost`, - ADD COLUMN IF NOT EXISTS `unit_cost` decimal(18,4) DEFAULT 0 COMMENT '单位成本' AFTER `total_overhead_cost`; - --- 2. 生产领料单主表 -CREATE TABLE IF NOT EXISTS `prd_pick_order` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT, - `pick_no` varchar(50) NOT NULL COMMENT '领料单号', - `work_order_id` bigint unsigned NOT NULL COMMENT '关联工单ID', - `warehouse_id` bigint unsigned DEFAULT NULL COMMENT '仓库ID', - `picker_name` varchar(100) DEFAULT NULL COMMENT '领料人', - `total_qty` decimal(18,4) DEFAULT 0 COMMENT '总数量', - `status` tinyint DEFAULT 1 COMMENT '状态: 1-草稿 2-已审核 3-已作废', - `remark` text COMMENT '备注', - `create_by` bigint unsigned DEFAULT NULL, - `create_time` datetime DEFAULT CURRENT_TIMESTAMP, - `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - `deleted` tinyint DEFAULT 0, - PRIMARY KEY (`id`), - UNIQUE KEY `uk_pick_no` (`pick_no`), - KEY `idx_work_order` (`work_order_id`), - KEY `idx_warehouse` (`warehouse_id`), - KEY `idx_status` (`status`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='生产领料单主表'; - --- 3. 生产领料单明细 -CREATE TABLE IF NOT EXISTS `prd_pick_order_item` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT, - `pick_order_id` bigint unsigned NOT NULL COMMENT '关联领料单ID', - `material_id` bigint unsigned DEFAULT NULL COMMENT '物料ID', - `material_name` varchar(200) DEFAULT NULL COMMENT '物料名称', - `material_spec` varchar(200) DEFAULT NULL COMMENT '物料规格', - `required_qty` decimal(18,4) DEFAULT 0 COMMENT '需求数量', - `actual_qty` decimal(18,4) DEFAULT 0 COMMENT '实领数量', - `batch_no` varchar(50) DEFAULT NULL COMMENT '批次号', - `unit_cost` decimal(18,4) DEFAULT 0 COMMENT '单位成本', - `line_amount` decimal(18,4) DEFAULT 0 COMMENT '行金额', - `unit` varchar(20) DEFAULT 'pcs' COMMENT '单位', - `remark` text COMMENT '备注', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP, - PRIMARY KEY (`id`), - KEY `idx_pick_order` (`pick_order_id`), - KEY `idx_material` (`material_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='生产领料单明细'; - --- 4. 生产退料单主表 -CREATE TABLE IF NOT EXISTS `prd_return_order` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT, - `return_no` varchar(50) NOT NULL COMMENT '退料单号', - `work_order_id` bigint unsigned NOT NULL COMMENT '关联工单ID', - `pick_order_id` bigint unsigned DEFAULT NULL COMMENT '原领料单ID', - `warehouse_id` bigint unsigned DEFAULT NULL COMMENT '退料仓库ID', - `return_reason` varchar(500) DEFAULT NULL COMMENT '退料原因', - `total_qty` decimal(18,4) DEFAULT 0 COMMENT '总数量', - `status` tinyint DEFAULT 1 COMMENT '状态: 1-草稿 2-已审核 3-已作废', - `create_by` bigint unsigned DEFAULT NULL, - `create_time` datetime DEFAULT CURRENT_TIMESTAMP, - `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - `deleted` tinyint DEFAULT 0, - PRIMARY KEY (`id`), - UNIQUE KEY `uk_return_no` (`return_no`), - KEY `idx_work_order` (`work_order_id`), - KEY `idx_pick_order` (`pick_order_id`), - KEY `idx_status` (`status`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='生产退料单主表'; - --- 5. 生产退料单明细 -CREATE TABLE IF NOT EXISTS `prd_return_order_item` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT, - `return_order_id` bigint unsigned NOT NULL COMMENT '关联退料单ID', - `pick_order_item_id` bigint unsigned DEFAULT NULL COMMENT '原领料明细ID', - `material_id` bigint unsigned DEFAULT NULL COMMENT '物料ID', - `material_name` varchar(200) DEFAULT NULL COMMENT '物料名称', - `quantity` decimal(18,4) DEFAULT 0 COMMENT '退料数量', - `batch_no` varchar(50) DEFAULT NULL COMMENT '批次号', - `unit_cost` decimal(18,4) DEFAULT 0 COMMENT '单位成本', - `line_amount` decimal(18,4) DEFAULT 0 COMMENT '行金额', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP, - PRIMARY KEY (`id`), - KEY `idx_return_order` (`return_order_id`), - KEY `idx_material` (`material_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='生产退料单明细'; - --- 6. 工序报工单 -CREATE TABLE IF NOT EXISTS `prd_work_report` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT, - `report_no` varchar(50) NOT NULL COMMENT '报工单号', - `work_order_id` bigint unsigned NOT NULL COMMENT '关联工单ID', - `process_name` varchar(100) DEFAULT NULL COMMENT '工序名称', - `equipment_id` bigint unsigned DEFAULT NULL COMMENT '设备ID', - `equipment_name` varchar(100) DEFAULT NULL COMMENT '设备名称', - `shift` varchar(20) DEFAULT NULL COMMENT '班次', - `operator_name` varchar(100) DEFAULT NULL COMMENT '操作人员', - `qualified_qty` decimal(18,4) DEFAULT 0 COMMENT '合格数量', - `defective_qty` decimal(18,4) DEFAULT 0 COMMENT '不良数量', - `defect_reason` varchar(500) DEFAULT NULL COMMENT '不良原因', - `work_hours` decimal(10,2) DEFAULT 0 COMMENT '工时(小时)', - `report_date` date DEFAULT NULL COMMENT '报工日期', - `status` tinyint DEFAULT 1 COMMENT '状态: 1-草稿 2-已审核 3-已作废', - `create_by` bigint unsigned DEFAULT NULL, - `create_time` datetime DEFAULT CURRENT_TIMESTAMP, - `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - `deleted` tinyint DEFAULT 0, - PRIMARY KEY (`id`), - UNIQUE KEY `uk_report_no` (`report_no`), - KEY `idx_work_order` (`work_order_id`), - KEY `idx_equipment` (`equipment_id`), - KEY `idx_report_date` (`report_date`), - KEY `idx_status` (`status`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='工序报工单'; - --- 7. 完工入库单 -CREATE TABLE IF NOT EXISTS `prd_finish_order` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT, - `finish_no` varchar(50) NOT NULL COMMENT '完工单号', - `work_order_id` bigint unsigned NOT NULL COMMENT '关联工单ID', - `warehouse_id` bigint unsigned DEFAULT NULL COMMENT '入库仓库ID', - `qualified_qty` decimal(18,4) DEFAULT 0 COMMENT '合格数量', - `defective_qty` decimal(18,4) DEFAULT 0 COMMENT '不合格数量', - `status` tinyint DEFAULT 1 COMMENT '状态: 1-草稿 2-已审核 3-已作废', - `create_by` bigint unsigned DEFAULT NULL, - `create_time` datetime DEFAULT CURRENT_TIMESTAMP, - `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - `deleted` tinyint DEFAULT 0, - PRIMARY KEY (`id`), - UNIQUE KEY `uk_finish_no` (`finish_no`), - KEY `idx_work_order` (`work_order_id`), - KEY `idx_warehouse` (`warehouse_id`), - KEY `idx_status` (`status`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='完工入库单'; diff --git a/database/migrations/071_create_work_order_bom_and_sample_quotation.sql b/database/migrations/071_create_work_order_bom_and_sample_quotation.sql deleted file mode 100644 index 7b2a5aae..00000000 --- a/database/migrations/071_create_work_order_bom_and_sample_quotation.sql +++ /dev/null @@ -1,69 +0,0 @@ --- T001: 工单 BOM 表 + 打样报价表(原编号 055,因与 055_add_prd_schedule_indexes 重复而顺延) --- 前置:migration 070 已创建 prd_pick_order 等核心流程表 - --- 1. 工单 BOM 表 -CREATE TABLE IF NOT EXISTS `prd_work_order_bom` ( - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, - `work_order_id` BIGINT UNSIGNED NOT NULL COMMENT '工单ID(prd_work_order.id)', - `work_order_no` VARCHAR(50) DEFAULT NULL COMMENT '工单号(冗余)', - `material_id` BIGINT UNSIGNED NOT NULL COMMENT '物料ID(inv_material.id)', - `material_code` VARCHAR(50) NOT NULL COMMENT '物料编码', - `material_name` VARCHAR(100) NOT NULL COMMENT '物料名称', - `specification` VARCHAR(255) DEFAULT NULL COMMENT '规格', - `unit` VARCHAR(20) DEFAULT NULL COMMENT '单位', - `required_qty` DECIMAL(18,4) NOT NULL COMMENT '需求数量', - `picked_qty` DECIMAL(18,4) DEFAULT 0.0000 COMMENT '已领数量', - `returned_qty` DECIMAL(18,4) DEFAULT 0.0000 COMMENT '已退数量', - `unit_cost` DECIMAL(18,4) DEFAULT 0.0000 COMMENT '单价', - `line_cost` DECIMAL(18,4) DEFAULT 0.0000 COMMENT '行成本', - `item_type` TINYINT DEFAULT 1 COMMENT '1-主料 2-油墨 3-辅料', - `sort` INT DEFAULT 0 COMMENT '排序', - `remark` VARCHAR(255) DEFAULT NULL COMMENT '备注', - `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP, - `update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - `deleted` TINYINT DEFAULT 0 COMMENT '软删除', - PRIMARY KEY (`id`), - KEY `idx_work_order` (`work_order_id`), - KEY `idx_material` (`material_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='工单 BOM 表'; - --- 2. 打样报价表 -CREATE TABLE IF NOT EXISTS `sal_sample_quotation` ( - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, - `sample_order_id` BIGINT UNSIGNED NOT NULL COMMENT '关联打样单ID', - `quotation_no` VARCHAR(50) NOT NULL COMMENT '报价单号(唯一)', - `version` INT NOT NULL DEFAULT 1 COMMENT '版本号', - `material_cost` DECIMAL(14,4) DEFAULT 0.0000 COMMENT '物料成本', - `labor_cost` DECIMAL(14,4) DEFAULT 0.0000 COMMENT '人工成本', - `tool_cost` DECIMAL(14,4) DEFAULT 0.0000 COMMENT '工装成本', - `overhead_cost` DECIMAL(14,4) DEFAULT 0.0000 COMMENT '制造费用', - `total_cost` DECIMAL(14,4) DEFAULT 0.0000 COMMENT '总成本', - `currency` VARCHAR(10) DEFAULT 'CNY' COMMENT '币种', - `exchange_rate` DECIMAL(18,4) DEFAULT 1.0000 COMMENT '汇率', - `profit_rate` DECIMAL(6,2) DEFAULT 20.00 COMMENT '利润率(%)', - `quoted_price` DECIMAL(14,4) DEFAULT 0.0000 COMMENT '报价金额', - `status` TINYINT DEFAULT 1 COMMENT '1-草稿 2-已确认 3-已作废', - `valid_until` DATE DEFAULT NULL COMMENT '报价有效期', - `remark` TEXT DEFAULT NULL COMMENT '备注', - `create_by` BIGINT UNSIGNED DEFAULT NULL, - `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP, - `update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - `deleted` TINYINT DEFAULT 0 COMMENT '软删除', - PRIMARY KEY (`id`), - UNIQUE KEY `uk_quotation_no` (`quotation_no`), - KEY `idx_sample_order` (`sample_order_id`), - KEY `idx_quotation_status` (`status`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='打样报价表'; - --- 3. 扩展 sample_order 表(添加工艺卡、工单、销售订单关联字段) -ALTER TABLE `sample_order` - ADD COLUMN IF NOT EXISTS `customer_id` BIGINT UNSIGNED DEFAULT NULL COMMENT '客户ID' AFTER `customer_name`, - ADD COLUMN IF NOT EXISTS `process_card_id` BIGINT UNSIGNED DEFAULT NULL COMMENT '关联工艺卡ID' AFTER `customer_confirm`, - ADD COLUMN IF NOT EXISTS `work_order_id` BIGINT UNSIGNED DEFAULT NULL COMMENT '打样工单ID' AFTER `process_card_id`, - ADD COLUMN IF NOT EXISTS `sales_order_id` BIGINT UNSIGNED DEFAULT NULL COMMENT '转大货后销售订单ID' AFTER `work_order_id`, - ADD COLUMN IF NOT EXISTS `create_by` BIGINT UNSIGNED DEFAULT NULL COMMENT '创建人ID' AFTER `remark`, - ADD COLUMN IF NOT EXISTS `update_by` BIGINT UNSIGNED DEFAULT NULL COMMENT '更新人ID' AFTER `create_by`; - -ALTER TABLE `sample_order` - ADD KEY IF NOT EXISTS `idx_process_card` (`process_card_id`), - ADD KEY IF NOT EXISTS `idx_work_order` (`work_order_id`); diff --git a/database/migrations/072_add_standard_card_obsolete_fields.sql b/database/migrations/072_add_standard_card_obsolete_fields.sql deleted file mode 100644 index ac70dee3..00000000 --- a/database/migrations/072_add_standard_card_obsolete_fields.sql +++ /dev/null @@ -1,8 +0,0 @@ --- 072 为标准卡 print 表补充「作废」相关字段 --- 依赖:update_standard_card.sql 已创建 prd_standard_card(print 取向单表) --- 说明:/api/standard-card/action?action=obsolete 流转到 status=5(已作废), --- 需记录作废原因/操作人/时间。使用 ADD COLUMN IF NOT EXISTS 保证幂等、可重复执行。 -ALTER TABLE `prd_standard_card` - ADD COLUMN IF NOT EXISTS `obsolete_reason` VARCHAR(500) DEFAULT NULL COMMENT '作废原因' AFTER `status`, - ADD COLUMN IF NOT EXISTS `obsolete_by` BIGINT UNSIGNED DEFAULT NULL COMMENT '作废人ID' AFTER `obsolete_reason`, - ADD COLUMN IF NOT EXISTS `obsolete_at` DATETIME DEFAULT NULL COMMENT '作废时间' AFTER `obsolete_by`; diff --git a/database/migrations/073_drop_wh_inventory.sql b/database/migrations/073_drop_wh_inventory.sql deleted file mode 100644 index 5706f058..00000000 --- a/database/migrations/073_drop_wh_inventory.sql +++ /dev/null @@ -1,22 +0,0 @@ --- 迁移 073:废弃遗留幽灵表 wh_inventory,统一到 inv_inventory --- --- 背景: --- wh_inventory 在权威 schema(vnerpdacahng_schema.sql)与全部迁移文件中均无 CREATE TABLE 定义, --- 但旧代码(cost-engine / 发货扣库存 / 盘点扫码)与旧 seed 仍在引用,属于遗留幽灵表。 --- 库存权威表为 inv_inventory(按 material_id + warehouse_id 聚合,含 available_qty), --- 本迁移同期已将三处代码与 seed 重定向到 inv_inventory。 --- --- ⚠️ 部署前请 DBA 确认: --- 若生产库仍存在 wh_inventory 且其中有 inv_inventory 未覆盖的库存数据,请先执行下方 --- “合并片段”(取消注释)将数据并入 inv_inventory,再执行 DROP。 --- 若 wh_inventory 已无数据或不存在,直接 DROP 即可(IF EXISTS 保证幂等)。 --- --- 合并片段(仅当 wh_inventory 存在且有数据时需要;执行后请删除本段): --- INSERT INTO inv_inventory (material_id, warehouse_id, quantity, available_qty, batch_no, create_time, update_time, deleted) --- SELECT material_id, warehouse_id, quantity, quantity, batch_no, create_time, NOW(), 0 --- FROM wh_inventory --- ON DUPLICATE KEY UPDATE --- quantity = inv_inventory.quantity + VALUES(quantity), --- available_qty = inv_inventory.available_qty + VALUES(available_qty); - -DROP TABLE IF EXISTS `wh_inventory`; diff --git a/database/migrations/074_category_rules_config.sql b/database/migrations/074_category_rules_config.sql deleted file mode 100644 index 8e2d863d..00000000 --- a/database/migrations/074_category_rules_config.sql +++ /dev/null @@ -1,205 +0,0 @@ --- ===================================================== --- 074: 分类规则配置化 + sys_warehouse_category 列补齐 --- --- 背景: --- 系统设置里的"分类编码规则"(^MAT-CAT-\d{3,}$ / ^WH-CAT-\d{3,}$)此前 --- 硬编码在 src/app/api/settings/category-rules/route.ts 的常量里, --- 既不可配置,也从未被任何录入端执行过(校验器本身查错列名,见下)。 --- --- 本迁移把规则落到 sys_calc_param,使其成为真正的"系统设置", --- 由 src/lib/category-validation.ts 统一读取,前后端共用同一份规则。 --- --- 幂等:ON DUPLICATE KEY UPDATE + information_schema 探测 + 动态 SQL --- (MySQL 8.0 不支持 ADD COLUMN IF NOT EXISTS,那是 MariaDB 语法) --- ===================================================== - --- ----------------------------------------------------- --- 1. 分类规则参数(category 分类) --- ----------------------------------------------------- -INSERT INTO `sys_calc_param` (`category`, `param_key`, `param_value`, `value_type`, `default_value`, `description`) VALUES --- 物料分类 -('category', 'category.material.code_pattern', '^MAT-CAT-\\d{3,}$', 'string', '^MAT-CAT-\\d{3,}$', '物料分类编码正则。新增分类时强制校验,不符合则拒绝保存'), -('category', 'category.material.code_pattern_desc', 'MAT-CAT-XXX(3位以上数字)', 'string', 'MAT-CAT-XXX(3位以上数字)', '物料分类编码规则的中文描述,用于错误提示与前端占位符'), -('category', 'category.material.max_depth', '4', 'int', '4', '物料分类最大层级深度,超过则拒绝新增子分类'), --- 仓库分类 -('category', 'category.warehouse.code_pattern', '^WH-CAT-\\d{3,}$', 'string', '^WH-CAT-\\d{3,}$', '仓库分类编码正则。新增分类时强制校验,不符合则拒绝保存'), -('category', 'category.warehouse.code_pattern_desc', 'WH-CAT-XXX(3位以上数字)', 'string', 'WH-CAT-XXX(3位以上数字)', '仓库分类编码规则的中文描述,用于错误提示与前端占位符'), -('category', 'category.warehouse.max_depth', '3', 'int', '3', '仓库分类最大层级深度(当前仓库分类为单层,预留)'), --- 拦截策略:新增强制、编辑放行(存量不合规数据不被锁死) -('category', 'category.enforce_on_create', 'true', 'boolean','true', '新增分类时是否强制校验编码规则(true=不符合直接拒绝)'), -('category', 'category.enforce_on_update', 'false', 'boolean','false', '编辑分类时是否强制校验编码规则(false=仅提示,避免锁死存量不合规数据)'), --- 业务单据侧:采购/领料等使用物料时是否要求物料已归类 -('category', 'category.require_on_business', 'false', 'boolean','false', '采购等业务单据是否强制要求物料已归类(false=仅提示不拦截,true=未归类物料直接拒绝提交)') -ON DUPLICATE KEY UPDATE - `param_value` = VALUES(`param_value`), - `value_type` = VALUES(`value_type`), - `default_value` = VALUES(`default_value`), - `description` = VALUES(`description`), - `status` = 1, - `deleted` = 0; - --- ----------------------------------------------------- --- 2. sys_warehouse_category 补齐 deleted 列 --- --- 权威快照 database/vnerpdacahng_schema.sql 里该表【没有】deleted 列, --- 但 database/warehouse_category.sql 与全部业务代码都在 WHERE deleted = 0。 --- 这是 category-rules / category-linkage 校验器失效的原因之一。 --- --- 注意:MySQL 8.0 不支持 ALTER TABLE ... ADD COLUMN IF NOT EXISTS --- (那是 MariaDB 语法),因此用 information_schema 探测 + 动态 SQL。 --- 迁移运行器按 ';' 切分后在同一连接上逐条执行,会话变量与 --- prepared statement 均可跨语句保持。 --- ----------------------------------------------------- -SET @col_exists := ( - SELECT COUNT(*) FROM information_schema.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() - AND TABLE_NAME = 'sys_warehouse_category' - AND COLUMN_NAME = 'deleted' -); - -SET @ddl := IF(@col_exists = 0, - 'ALTER TABLE `sys_warehouse_category` ADD COLUMN `deleted` TINYINT NOT NULL DEFAULT 0 COMMENT ''软删除: 0-正常, 1-已删除''', - 'SELECT 1' -); - -PREPARE stmt FROM @ddl; - -EXECUTE stmt; - -DEALLOCATE PREPARE stmt; - -SET @idx_exists := ( - SELECT COUNT(*) FROM information_schema.STATISTICS - WHERE TABLE_SCHEMA = DATABASE() - AND TABLE_NAME = 'sys_warehouse_category' - AND INDEX_NAME = 'idx_deleted' -); - -SET @ddl := IF(@idx_exists = 0, - 'ALTER TABLE `sys_warehouse_category` ADD INDEX `idx_deleted` (`deleted`)', - 'SELECT 1' -); - -PREPARE stmt FROM @ddl; - -EXECUTE stmt; - -DEALLOCATE PREPARE stmt; - --- ----------------------------------------------------- --- 3. inv_warehouse 补齐 category_id 列 --- --- 权威快照里 inv_warehouse 【没有】category_id,唯一添加它的脚本 --- database/alter_warehouse_add_category.sql 引用了 w.code / w.name / w.type --- 这三个不存在的列(真实列名是 warehouse_code / warehouse_name / warehouse_type), --- 脚本本身跑不通 → 该列在 live 库大概率缺失。 --- --- 后果: --- - DELETE /api/organization/warehouse-category 的"分类下是否有仓库"守卫 --- 会抛 Unknown column 'category_id' → 删除分类恒定 500; --- - /api/init/warehouse-category 的 LEFT JOIN 同样报错。 --- ----------------------------------------------------- -SET @col_exists := ( - SELECT COUNT(*) FROM information_schema.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() - AND TABLE_NAME = 'inv_warehouse' - AND COLUMN_NAME = 'category_id' -); - -SET @ddl := IF(@col_exists = 0, - 'ALTER TABLE `inv_warehouse` ADD COLUMN `category_id` BIGINT UNSIGNED DEFAULT NULL COMMENT ''仓库分类ID(sys_warehouse_category.id)''', - 'SELECT 1' -); - -PREPARE stmt FROM @ddl; - -EXECUTE stmt; - -DEALLOCATE PREPARE stmt; - -SET @idx_exists := ( - SELECT COUNT(*) FROM information_schema.STATISTICS - WHERE TABLE_SCHEMA = DATABASE() - AND TABLE_NAME = 'inv_warehouse' - AND INDEX_NAME = 'idx_warehouse_category_id' -); - -SET @ddl := IF(@idx_exists = 0, - 'ALTER TABLE `inv_warehouse` ADD INDEX `idx_warehouse_category_id` (`category_id`)', - 'SELECT 1' -); - -PREPARE stmt FROM @ddl; - -EXECUTE stmt; - -DEALLOCATE PREPARE stmt; - --- 说明:此处刻意【不加】外键约束。sys_warehouse_category.id 是 --- bigint unsigned(权威快照)而 warehouse_category.sql 建表用 INT UNSIGNED, --- 两套定义并存时加 FK 会在部分环境直接失败;先补列保证功能可用, --- FK 留待 DBA 确认 live 库真实类型后单独处理。 - --- ----------------------------------------------------- --- 4. inv_material_category 补齐 category_type / remark 列 --- --- 权威快照里这两列【不存在】,但以下位置都在读写它们: --- - 前端 base-data/material-category 页有"分类类型"下拉(选了会被静默丢弃) --- - src/app/api/init/supplement-tables/route.ts 的建表语句含这两列 --- - init/settings-seed、init/core-flow-seed、init/warehouse-category-seed --- 的 INSERT / SELECT 都引用 category_type --- 即:live 库缺列时,这些初始化按钮一律 Unknown column 报错。 --- ----------------------------------------------------- -SET @col_exists := ( - SELECT COUNT(*) FROM information_schema.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() - AND TABLE_NAME = 'inv_material_category' - AND COLUMN_NAME = 'category_type' -); - -SET @ddl := IF(@col_exists = 0, - 'ALTER TABLE `inv_material_category` ADD COLUMN `category_type` TINYINT DEFAULT NULL COMMENT ''分类类型: 1-原材料,2-半成品,3-成品,4-辅料,5-包材,6-油墨,7-溶剂,8-网版,9-刀具,10-设备配件''', - 'SELECT 1' -); - -PREPARE stmt FROM @ddl; - -EXECUTE stmt; - -DEALLOCATE PREPARE stmt; - -SET @col_exists := ( - SELECT COUNT(*) FROM information_schema.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() - AND TABLE_NAME = 'inv_material_category' - AND COLUMN_NAME = 'remark' -); - -SET @ddl := IF(@col_exists = 0, - 'ALTER TABLE `inv_material_category` ADD COLUMN `remark` VARCHAR(255) DEFAULT NULL COMMENT ''备注''', - 'SELECT 1' -); - -PREPARE stmt FROM @ddl; - -EXECUTE stmt; - -DEALLOCATE PREPARE stmt; - --- ----------------------------------------------------- --- 5. 存量不合规数据自查(只读,不自动改数据) --- --- 按"新增强制 + 编辑放行"策略,存量不合规编码不做自动改写 --- (改编码会波及 inv_material.category_id 之外的人工记忆与外部对账)。 --- 运维可执行以下语句列出待整改清单: --- --- SELECT id, category_code, category_name --- FROM inv_material_category --- WHERE deleted = 0 AND category_code NOT REGEXP '^MAT-CAT-[0-9]{3,}$'; --- --- SELECT id, code, name --- FROM sys_warehouse_category --- WHERE deleted = 0 AND code NOT REGEXP '^WH-CAT-[0-9]{3,}$'; --- --- 也可在「系统设置 → 分类规则校验」页面直接查看(本次已修复该页)。 --- ----------------------------------------------------- diff --git a/database/migrations/075_add_material_fields_to_sal_order_item.sql b/database/migrations/075_add_material_fields_to_sal_order_item.sql deleted file mode 100644 index a50980bd..00000000 --- a/database/migrations/075_add_material_fields_to_sal_order_item.sql +++ /dev/null @@ -1,14 +0,0 @@ --- Migration 075: Add material_id, material_code, deleted to sal_order_item --- 销售订单明细表补齐物料关联字段,用于前端物料选择后直接存储物料ID和编码 - -ALTER TABLE `sal_order_item` - ADD COLUMN IF NOT EXISTS `material_id` BIGINT UNSIGNED NULL COMMENT '物料ID' AFTER `order_id`, - ADD COLUMN IF NOT EXISTS `material_code` VARCHAR(50) NULL COMMENT '物料编码' AFTER `material_id`, - ADD COLUMN IF NOT EXISTS `deleted` TINYINT(1) DEFAULT 0 COMMENT '软删除' AFTER `remark`; - --- 回填历史数据:根据 material_name 匹配 inv_material 获取 material_id -UPDATE sal_order_item soi -JOIN inv_material m ON m.material_name = soi.material_name AND m.deleted = 0 -SET soi.material_id = m.id, - soi.material_code = m.material_code -WHERE soi.material_id IS NULL AND soi.deleted = 0; \ No newline at end of file diff --git a/database/migrations/076_normalize_sample_order_printing_status.sql b/database/migrations/076_normalize_sample_order_printing_status.sql deleted file mode 100644 index 86531342..00000000 --- a/database/migrations/076_normalize_sample_order_printing_status.sql +++ /dev/null @@ -1,7 +0,0 @@ --- 归一化打样订单历史脏数据:status='printing' -> 'pending' --- 背景:sal_sample_order.status 合法枚举仅为 pending/producing/completed/cancelled, --- 'printing' 是旧版遗留脏数据,导致 /sample/orders 列表直接显示英文。 --- 前端已补 printing->待打样 映射,但根因在库内,本迁移将其统一为 pending, --- 避免与正常 pending 记录重复显示「待打样」。 --- 幂等:若已无 printing 行,UPDATE 影响 0 行,可重复执行。 -UPDATE sal_sample_order SET status = 'pending', update_time = NOW() WHERE status = 'printing' AND deleted = 0; diff --git a/database/migrations/077_qrcode_unique.sql b/database/migrations/077_qrcode_unique.sql deleted file mode 100644 index 27d7a991..00000000 --- a/database/migrations/077_qrcode_unique.sql +++ /dev/null @@ -1,34 +0,0 @@ --- ============================================================ --- Migration 077: qrcode_record 在 qr_code 上增加唯一约束 uk_qr_code --- --- 背景: --- 入库审核通过时 QrCodeGenerationHandler 为每个入库明细生成一张物料二维码。 --- 二维码取值由「入库单号 + 行序号 + 批次 + 物料 + 仓库」确定性推导, --- 配合本迁移建立的 qr_code 唯一约束,实现「同一入库明细只保留一张有效码, --- 反审核作废、再审核复活(status=1)」的幂等语义(详见 #5 修复)。 --- --- 注意:早期方案曾考虑在 (batch_no, material_id, warehouse_id) 上加唯一约束, --- 但该约束语义错误——同一批次在仓库内本可拥有多张二维码(整料/小料/余料、或 --- 不同入库单的同批次),且不同入库单的同批次会互相覆盖 ref_id 导致数据错乱, --- 因此改为在 qr_code(二维码天然唯一标识)上加唯一约束。 --- --- 幂等模式:使用 INFORMATION_SCHEMA.STATISTICS 检查索引是否存在再添加; --- 迁移运行器按 `;` 同连接顺序执行,会话变量跨语句保持。 --- ============================================================ - --- 检查唯一索引 uk_qr_code 是否已存在,不存在则添加 -SET @idx = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'qrcode_record' AND INDEX_NAME = 'uk_qr_code'); -SET @sql = IF(@idx = 0, - 'ALTER TABLE qrcode_record ADD UNIQUE INDEX uk_qr_code (qr_code)', - 'SELECT ''uk_qr_code already exists'' AS info'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 验证:确认索引已创建 -SELECT - INDEX_NAME, COLUMN_NAME, NON_UNIQUE, SEQ_IN_INDEX -FROM INFORMATION_SCHEMA.STATISTICS -WHERE TABLE_SCHEMA = DATABASE() - AND TABLE_NAME = 'qrcode_record' - AND INDEX_NAME = 'uk_qr_code' -ORDER BY SEQ_IN_INDEX; diff --git a/database/migrations/078_add_material_code_to_inbound_item.sql b/database/migrations/078_add_material_code_to_inbound_item.sql deleted file mode 100644 index 395918c3..00000000 --- a/database/migrations/078_add_material_code_to_inbound_item.sql +++ /dev/null @@ -1,26 +0,0 @@ --- Migration 078: Add material_code to inv_inbound_item --- 入库明细表补齐物料编码冗余列,用于前端列表展示与新增时持久化用户填写的物料编码 --- 修复:新增入库单填写的物料编码落库即丢失,列表永远为空的字段链路断裂问题 - -SET @col_exists = ( - SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() - AND TABLE_NAME = 'inv_inbound_item' - AND COLUMN_NAME = 'material_code' -); - -SET @sql = IF( - @col_exists = 0, - 'ALTER TABLE inv_inbound_item ADD COLUMN material_code VARCHAR(50) NULL COMMENT ''物料编码(冗余,便于查询)'' AFTER material_id', - 'SELECT 1' -); - -PREPARE stmt FROM @sql; -EXECUTE stmt; -DEALLOCATE PREPARE stmt; - --- 回填历史数据:根据 material_id 关联 inv_material 补齐 material_code -UPDATE inv_inbound_item ii -LEFT JOIN inv_material m ON m.id = ii.material_id AND m.deleted = 0 -SET ii.material_code = m.material_code -WHERE ii.material_code IS NULL AND ii.material_id IS NOT NULL; diff --git a/database/migrations/20260701_add_gl_tables.sql b/database/migrations/20260701_add_gl_tables.sql deleted file mode 100644 index d3524d7f..00000000 --- a/database/migrations/20260701_add_gl_tables.sql +++ /dev/null @@ -1,115 +0,0 @@ --- ======================================================== --- 总账模块表结构补丁(2026-07-01) --- 修复 fin_voucher_line 等总账表缺失导致测试失败 --- 依据:src/lib/general-ledger.ts 中的 SQL 语句反推 --- ======================================================== - --- 会计科目表 -CREATE TABLE IF NOT EXISTS `fin_account` ( - `id` INT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '科目ID', - `account_code` VARCHAR(20) NOT NULL COMMENT '科目编码', - `account_name` VARCHAR(100) NOT NULL COMMENT '科目名称', - `full_name` VARCHAR(200) COMMENT '全称(含父级)', - `parent_id` INT UNSIGNED COMMENT '父级科目ID', - `level` TINYINT DEFAULT 1 COMMENT '层级(1=一级)', - `account_type` TINYINT NOT NULL COMMENT '类型: 1=资产, 2=负债, 3=权益, 4=成本, 5=损益', - `balance_direction` TINYINT NOT NULL COMMENT '余额方向: 1=借, 2=贷', - `is_leaf` TINYINT DEFAULT 1 COMMENT '是否末级: 0=否, 1=是', - `assist_types` VARCHAR(200) COMMENT '辅助核算类型(JSON 数组)', - `status` TINYINT DEFAULT 1 COMMENT '状态: 0=禁用, 1=启用', - `sort_order` INT DEFAULT 0 COMMENT '排序', - PRIMARY KEY (`id`), - UNIQUE KEY `uk_account_code` (`account_code`), - KEY `idx_parent` (`parent_id`), - KEY `idx_type` (`account_type`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='会计科目表'; - --- 会计期间表 -CREATE TABLE IF NOT EXISTS `fin_period` ( - `id` INT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '期间ID', - `period_code` VARCHAR(10) NOT NULL COMMENT '期间编码(如 2026-07)', - `period_name` VARCHAR(20) COMMENT '期间名称(如 2026年7月)', - `start_date` DATE NOT NULL COMMENT '开始日期', - `end_date` DATE NOT NULL COMMENT '结束日期', - `is_closed` TINYINT DEFAULT 0 COMMENT '是否已结账: 0=否, 1=是', - `status` TINYINT DEFAULT 0 COMMENT '状态: 0=开放, 1=结账中, 2=已关闭', - PRIMARY KEY (`id`), - UNIQUE KEY `uk_period_code` (`period_code`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='会计期间表'; - --- 凭证主表 -CREATE TABLE IF NOT EXISTS `fin_voucher` ( - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '凭证ID', - `voucher_no` VARCHAR(50) NOT NULL COMMENT '凭证编号', - `period_code` VARCHAR(10) NOT NULL COMMENT '所属期间', - `voucher_date` DATE NOT NULL COMMENT '凭证日期', - `voucher_type` TINYINT NOT NULL COMMENT '类型: 1=收, 2=付, 3=转, 4=调整', - `source_type` VARCHAR(50) COMMENT '来源类型(sale/purchase/payment/receipt/cost 等)', - `source_id` BIGINT UNSIGNED COMMENT '来源单据ID', - `source_no` VARCHAR(50) COMMENT '来源单据号', - `total_debit` DECIMAL(18,2) DEFAULT 0 COMMENT '借方合计(新版总账)', - `total_credit` DECIMAL(18,2) DEFAULT 0 COMMENT '贷方合计(新版总账)', - `total_amount` DECIMAL(18,2) DEFAULT 0 COMMENT '凭证总额(旧版 FinanceVoucherHandler 兼容)', - `status` TINYINT DEFAULT 0 COMMENT '状态: 0=草稿, 1=已提交, 2=已审核, 3=已记账, 4=已作废', - `summary` TEXT COMMENT '摘要(新版总账)', - `remark` VARCHAR(500) COMMENT '备注(旧版 FinanceVoucherHandler 兼容)', - `attachment_count` INT DEFAULT 0 COMMENT '附件数', - `deleted` TINYINT DEFAULT 0 COMMENT '软删除: 0=正常, 1=已删除', - `created_by` VARCHAR(50) COMMENT '制单人', - `created_at` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间(旧版兼容)', - `audited_by` VARCHAR(50) COMMENT '审核人', - `audited_at` DATETIME COMMENT '审核时间', - `posted_by` VARCHAR(50) COMMENT '记账人', - `posted_at` DATETIME COMMENT '记账时间', - PRIMARY KEY (`id`), - UNIQUE KEY `uk_voucher_no` (`voucher_no`), - KEY `idx_period` (`period_code`), - KEY `idx_source` (`source_type`, `source_id`), - KEY `idx_status` (`status`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='会计凭证主表'; - --- 凭证明细表 -CREATE TABLE IF NOT EXISTS `fin_voucher_line` ( - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '明细ID', - `voucher_id` BIGINT UNSIGNED NOT NULL COMMENT '凭证ID', - `line_no` INT NOT NULL COMMENT '行号', - `account_id` INT UNSIGNED NOT NULL COMMENT '科目ID', - `account_code` VARCHAR(20) COMMENT '科目编码(冗余)', - `account_name` VARCHAR(100) COMMENT '科目名称(冗余)', - `summary` VARCHAR(500) COMMENT '摘要(新版总账)', - `debit_amount` DECIMAL(18,2) DEFAULT 0 COMMENT '借方金额(新版总账)', - `credit_amount` DECIMAL(18,2) DEFAULT 0 COMMENT '贷方金额(新版总账)', - `debit_account` VARCHAR(100) COMMENT '借方科目名称(旧版 FinanceVoucherHandler 兼容)', - `credit_account` VARCHAR(100) COMMENT '贷方科目名称(旧版 FinanceVoucherHandler 兼容)', - `amount` DECIMAL(18,2) DEFAULT 0 COMMENT '金额(旧版 FinanceVoucherHandler 兼容)', - `description` VARCHAR(500) COMMENT '描述(旧版 FinanceVoucherHandler 兼容)', - `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间(旧版兼容)', - `customer_id` BIGINT UNSIGNED COMMENT '客户ID(辅助核算)', - `supplier_id` BIGINT UNSIGNED COMMENT '供应商ID(辅助核算)', - `department_id` INT UNSIGNED COMMENT '部门ID(辅助核算)', - `project_id` INT UNSIGNED COMMENT '项目ID(辅助核算)', - PRIMARY KEY (`id`), - KEY `idx_voucher` (`voucher_id`), - KEY `idx_account` (`account_id`), - CONSTRAINT `fk_voucher_line_voucher` FOREIGN KEY (`voucher_id`) REFERENCES `fin_voucher` (`id`) ON DELETE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='会计凭证明细表'; - --- 科目余额表 -CREATE TABLE IF NOT EXISTS `fin_account_balance` ( - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '余额ID', - `period_code` VARCHAR(10) NOT NULL COMMENT '期间编码', - `account_id` INT UNSIGNED NOT NULL COMMENT '科目ID', - `account_code` VARCHAR(20) COMMENT '科目编码(冗余)', - `begin_debit` DECIMAL(18,2) DEFAULT 0 COMMENT '期初借方', - `begin_credit` DECIMAL(18,2) DEFAULT 0 COMMENT '期初贷方', - `current_debit` DECIMAL(18,2) DEFAULT 0 COMMENT '本期借方发生', - `current_credit` DECIMAL(18,2) DEFAULT 0 COMMENT '本期贷方发生', - `year_debit` DECIMAL(18,2) DEFAULT 0 COMMENT '本年累计借方', - `year_credit` DECIMAL(18,2) DEFAULT 0 COMMENT '本年累计贷方', - `end_debit` DECIMAL(18,2) DEFAULT 0 COMMENT '期末借方', - `end_credit` DECIMAL(18,2) DEFAULT 0 COMMENT '期末贷方', - PRIMARY KEY (`id`), - UNIQUE KEY `uk_period_account` (`period_code`, `account_id`), - KEY `idx_account` (`account_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='科目余额表'; diff --git a/database/migrations/20260701_add_std_master_tables.sql b/database/migrations/20260701_add_std_master_tables.sql deleted file mode 100644 index c8236a43..00000000 --- a/database/migrations/20260701_add_std_master_tables.sql +++ /dev/null @@ -1,128 +0,0 @@ --- ======================================================== --- 标准主档表补丁(2026-07-01) --- 修复 inv_material_std / prd_bom_std / prd_bom_line_std 缺失 --- 以及 inv_inventory / sal_order_detail 列缺失导致端到端测试 beforeAll 失败 --- 依据: --- 1. src/app/api/migrations/readmd-fixes/route.ts 中的 DDL(HTTP 接口已禁用,改走 CLI) --- 2. src/lib/services/sales-order-service.ts 与 data-fix-tool.ts 的实际查询字段 --- 3. src/lib/__tests__/end-to-end-flow.test.ts 的 beforeAll INSERT 字段 --- 注意: --- - CREATE TABLE IF NOT EXISTS 保证建表幂等 --- - ALTER TABLE ADD COLUMN 在 MySQL 8.0 不支持 IF NOT EXISTS, --- 重复执行会因列已存在报错,setup-db.mjs 的 executeStatements 会逐语句容错跳过 --- ======================================================== - --- -------------------------------------------------------- --- 1. 标准物料主档 inv_material_std(三合一) --- 生产代码引用:src/lib/services/data-fix-tool.ts:13,114,136 --- 测试引用:end-to-end-flow.test.ts:28,34 --- 与 route.ts DDL 的差异:补 status 列(route.ts 遗漏,测试与生产查询均需要) --- -------------------------------------------------------- -CREATE TABLE IF NOT EXISTS `inv_material_std` ( - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '物料ID', - `material_code` VARCHAR(50) NOT NULL COMMENT '物料编码', - `material_name` VARCHAR(100) NOT NULL COMMENT '物料名称', - `material_spec` VARCHAR(200) NULL COMMENT '规格型号', - `unit` VARCHAR(20) NOT NULL COMMENT '计量单位', - `material_type` TINYINT NOT NULL DEFAULT 1 COMMENT '1原材料 2半成品 3成品 4辅料 5包材', - `category_id` BIGINT UNSIGNED DEFAULT NULL COMMENT '分类ID', - `is_batch` TINYINT NOT NULL DEFAULT 1 COMMENT '是否批次管理', - `is_expire` TINYINT NOT NULL DEFAULT 0 COMMENT '是否效期管理', - `safe_stock` DECIMAL(18,4) NOT NULL DEFAULT 0 COMMENT '安全库存', - `standard_cost` DECIMAL(18,4) DEFAULT 0 COMMENT '标准成本', - `shelf_life_days` INT DEFAULT NULL COMMENT '保质期天数', - `status` TINYINT NOT NULL DEFAULT 1 COMMENT '0禁用 1启用(route.ts 遗漏,测试与生产查询需要)', - `remark` TEXT DEFAULT NULL COMMENT '备注', - `legacy_source` VARCHAR(30) DEFAULT NULL COMMENT '旧表来源: inv_material/bom_material/mdm_material', - `legacy_id` BIGINT UNSIGNED DEFAULT NULL COMMENT '旧表原始ID', - `create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - `update_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', - `created_by` BIGINT UNSIGNED DEFAULT NULL COMMENT '创建人ID', - `updated_by` BIGINT UNSIGNED DEFAULT NULL COMMENT '更新人ID', - `deleted` TINYINT NOT NULL DEFAULT 0 COMMENT '是否删除', - PRIMARY KEY (`id`), - UNIQUE KEY `uk_material_code` (`material_code`), - KEY `idx_material_type` (`material_type`), - KEY `idx_category_id` (`category_id`), - KEY `idx_status` (`status`), - KEY `idx_legacy` (`legacy_source`, `legacy_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='标准物料主档'; - --- -------------------------------------------------------- --- 2. 标准BOM头 prd_bom_std --- 生产代码引用:src/lib/services/sales-order-service.ts:42 --- 测试引用:end-to-end-flow.test.ts:46 --- -------------------------------------------------------- -CREATE TABLE IF NOT EXISTS `prd_bom_std` ( - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'BOM ID', - `bom_code` VARCHAR(50) NOT NULL COMMENT 'BOM编码', - `product_id` BIGINT UNSIGNED NOT NULL COMMENT '成品ID', - `product_name` VARCHAR(100) DEFAULT NULL COMMENT '成品名称', - `version` VARCHAR(20) NOT NULL DEFAULT 'V1.0' COMMENT '版本', - `effective_date` DATE NOT NULL COMMENT '生效日期', - `obsolete_date` DATE NULL COMMENT '失效日期', - `status` TINYINT NOT NULL DEFAULT 1 COMMENT '0草稿 1生效 2作废', - `remark` TEXT DEFAULT NULL COMMENT '备注', - `legacy_source` VARCHAR(30) DEFAULT NULL COMMENT '旧表来源', - `legacy_id` BIGINT UNSIGNED DEFAULT NULL COMMENT '旧表原始ID', - `create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - `update_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', - `created_by` BIGINT UNSIGNED DEFAULT NULL COMMENT '创建人ID', - `updated_by` BIGINT UNSIGNED DEFAULT NULL COMMENT '更新人ID', - `deleted` TINYINT NOT NULL DEFAULT 0 COMMENT '是否删除', - PRIMARY KEY (`id`), - UNIQUE KEY `uk_bom_code` (`bom_code`), - KEY `idx_product_id` (`product_id`), - KEY `idx_status` (`status`), - KEY `idx_effective_date` (`effective_date`), - KEY `idx_legacy` (`legacy_source`, `legacy_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='标准BOM头'; - --- -------------------------------------------------------- --- 3. 标准BOM行 prd_bom_line_std --- 生产代码引用:src/lib/services/sales-order-service.ts:69 --- 测试引用:end-to-end-flow.test.ts:53 --- -------------------------------------------------------- -CREATE TABLE IF NOT EXISTS `prd_bom_line_std` ( - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'BOM行ID', - `bom_id` BIGINT UNSIGNED NOT NULL COMMENT 'BOM头ID', - `line_no` INT NOT NULL DEFAULT 1 COMMENT '行号', - `material_id` BIGINT UNSIGNED NOT NULL COMMENT '物料ID', - `material_code` VARCHAR(50) DEFAULT NULL COMMENT '物料编码', - `material_name` VARCHAR(100) DEFAULT NULL COMMENT '物料名称', - `consumption_qty` DECIMAL(18,4) NOT NULL COMMENT '单耗', - `waste_rate` DECIMAL(18,4) NOT NULL DEFAULT 0 COMMENT '损耗率%', - `material_type` TINYINT DEFAULT 1 COMMENT '1原材料 2半成品 3辅料 4包材 5其他', - `remark` VARCHAR(200) DEFAULT NULL COMMENT '备注', - `create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - `update_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', - `deleted` TINYINT NOT NULL DEFAULT 0 COMMENT '是否删除', - PRIMARY KEY (`id`), - KEY `idx_bom_id` (`bom_id`), - KEY `idx_material_id` (`material_id`), - CONSTRAINT `fk_bom_line_std_bom` FOREIGN KEY (`bom_id`) REFERENCES `prd_bom_std` (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='标准BOM行'; - --- -------------------------------------------------------- --- 4. inv_inventory 补列 --- 测试引用:end-to-end-flow.test.ts:59 --- 现有列:id, material_id, warehouse_id, location_code, quantity, locked_qty, --- available_qty, batch_no, production_date, expiry_date, update_time --- 缺失:material_code, material_name, unit, deleted --- -------------------------------------------------------- -ALTER TABLE `inv_inventory` ADD COLUMN `material_code` VARCHAR(50) NULL COMMENT '物料编码(冗余,便于查询)' AFTER `material_id`; -ALTER TABLE `inv_inventory` ADD COLUMN `material_name` VARCHAR(100) NULL COMMENT '物料名称(冗余)' AFTER `material_code`; -ALTER TABLE `inv_inventory` ADD COLUMN `unit` VARCHAR(20) NULL COMMENT '计量单位' AFTER `quantity`; -ALTER TABLE `inv_inventory` ADD COLUMN `deleted` TINYINT NOT NULL DEFAULT 0 COMMENT '删除标记' AFTER `update_time`; -ALTER TABLE `inv_inventory` ADD INDEX `idx_material_code` (`material_code`); - --- -------------------------------------------------------- --- 5. sal_order_detail 补列 --- 测试引用:end-to-end-flow.test.ts:72 --- 现有列:id, order_id, material_id, quantity, unit, unit_price, tax_rate, --- amount, tax_amount, total_amount, delivered_qty, delivery_date, remark, create_time --- 缺失:material_name, deleted --- -------------------------------------------------------- -ALTER TABLE `sal_order_detail` ADD COLUMN `material_name` VARCHAR(100) NULL COMMENT '物料名称(冗余)' AFTER `material_id`; -ALTER TABLE `sal_order_detail` ADD COLUMN `deleted` TINYINT NOT NULL DEFAULT 0 COMMENT '删除标记' AFTER `create_time`; -ALTER TABLE `sal_order_detail` ADD INDEX `idx_material_name` (`material_name`); diff --git a/database/migrations/20260812090000_purchase_inbound_linkage.ts b/database/migrations/20260812090000_purchase_inbound_linkage.ts deleted file mode 100644 index 73909164..00000000 --- a/database/migrations/20260812090000_purchase_inbound_linkage.ts +++ /dev/null @@ -1,73 +0,0 @@ -/** - * 采购入库单与采购订单联动改造 - * - * 背景:让采购入库单强制关联采购订单。 - * - inv_inbound_order 增加来源类型(source_type)与来源单据ID(source_order_id), - * 存量 po_id 非空记录回填为 purchase_order 来源; - * - inv_inbound_item 增加采购订单明细关联字段(purchase_order_item_id / purchase_order_line_no); - * - pur_purchase_order 增加已收数量汇总字段(received_quantity),由明细 received_qty 汇总回填; - * - sys_config 写入采购入库超收比例默认配置。 - * - * 幂等:迁移由 scripts/migrate.ts 基于 sys_migration 记录保证只执行一次; - * sys_config 插入使用 ON DUPLICATE KEY UPDATE,可重复执行。 - */ - -import { Connection } from 'mysql2/promise'; - -export async function up(conn: Connection): Promise { - // 1. inv_inbound_order:来源类型 + 来源单据ID + 联合索引 - await conn.query( - "ALTER TABLE `inv_inbound_order` " + - "ADD COLUMN `source_type` VARCHAR(20) NULL COMMENT '来源类型: purchase_order-采购订单', " + - "ADD COLUMN `source_order_id` BIGINT UNSIGNED NULL COMMENT '来源单据ID(如采购订单ID)', " + - "ADD INDEX `idx_source_order` (`source_type`, `source_order_id`)" - ); - - // 2. inv_inbound_item:采购订单明细关联字段 - await conn.query( - "ALTER TABLE `inv_inbound_item` " + - "ADD COLUMN `purchase_order_item_id` BIGINT UNSIGNED NULL COMMENT '采购订单明细ID(pur_purchase_order_line.id)', " + - "ADD COLUMN `purchase_order_line_no` INT UNSIGNED NULL COMMENT '采购订单行号(pur_purchase_order_line.line_no)'" - ); - - // 3. pur_purchase_order:已收数量汇总字段 - await conn.query( - "ALTER TABLE `pur_purchase_order` " + - "ADD COLUMN `received_quantity` DECIMAL(18,4) NOT NULL DEFAULT 0.0000 COMMENT '已收数量汇总(由明细 received_qty 汇总)'" - ); - - // 4. 历史数据回填 - // 4.1 存量 po_id 非空的入库单回填来源信息 - await conn.query( - "UPDATE `inv_inbound_order` SET `source_type` = 'purchase_order', `source_order_id` = `po_id` WHERE `po_id` IS NOT NULL" - ); - // 4.2 采购订单已收数量按明细汇总回填 - await conn.query( - "UPDATE `pur_purchase_order` o SET `received_quantity` = " + - "(SELECT IFNULL(SUM(l.`received_qty`), 0) FROM `pur_purchase_order_line` l WHERE l.`po_id` = o.`id`)" - ); - - // 5. 系统配置默认值:采购入库超收比例(%),范围0~20 - await conn.query( - "INSERT INTO `sys_config` (`config_key`, `config_value`, `description`, `config_name`, `config_type_enum`, `category`, `display_name`, `sort_order`, `status`) " + - "VALUES ('purchase.over_receipt_tolerance', '5', '采购入库超收比例(%),范围0~20', '采购入库超收比例', 'number', '采购管理', '采购入库超收比例', 0, 1) " + - "ON DUPLICATE KEY UPDATE " + - "`config_value` = VALUES(`config_value`), `description` = VALUES(`description`), `status` = 1, `deleted` = 0" - ); -} - -export async function down(conn: Connection): Promise { - // 与 up 相反顺序回滚;sys_config 配置项保守保留,不删除 - // 1. 采购订单已收数量汇总字段 - await conn.query('ALTER TABLE `pur_purchase_order` DROP COLUMN `received_quantity`'); - - // 2. 入库单明细采购订单关联字段 - await conn.query( - 'ALTER TABLE `inv_inbound_item` DROP COLUMN `purchase_order_line_no`, DROP COLUMN `purchase_order_item_id`' - ); - - // 3. 入库单来源字段与联合索引(先删索引再删列) - await conn.query( - 'ALTER TABLE `inv_inbound_order` DROP INDEX `idx_source_order`, DROP COLUMN `source_order_id`, DROP COLUMN `source_type`' - ); -} diff --git a/database/migrations/20260813000001_add_uk_inventory_batch.sql b/database/migrations/20260813000001_add_uk_inventory_batch.sql deleted file mode 100644 index 0e3d9f8c..00000000 --- a/database/migrations/20260813000001_add_uk_inventory_batch.sql +++ /dev/null @@ -1,14 +0,0 @@ --- 任务 001:库存批次表 联合唯一约束 --- 真实表 inv_inventory_batch(Drizzle schema 误注册为复数 inv_inventory_orders,已漂移,勿依赖) --- 软删除列 deleted tinyint NOT NULL DEFAULT 0,纳入复合键实现「未删唯一、已删可重建」 --- 策略:先加复合唯一(失败则保留旧 uk_batch_no,安全);成功后再删旧单列 uk_batch_no - --- 1) 新增复合唯一索引 uk_warehouse_material_batch -SET @idx = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_inventory_batch' AND INDEX_NAME = 'uk_warehouse_material_batch'); -SET @sql = IF(@idx = 0, 'ALTER TABLE inv_inventory_batch ADD UNIQUE INDEX uk_warehouse_material_batch (warehouse_id, material_id, batch_no, deleted)', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 2) 删除旧的单列 uk_batch_no(允许同批次号跨仓库存在) -SET @idx2 = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_inventory_batch' AND INDEX_NAME = 'uk_batch_no'); -SET @sql2 = IF(@idx2 > 0, 'ALTER TABLE inv_inventory_batch DROP INDEX uk_batch_no', 'SELECT 1'); -PREPARE stmt2 FROM @sql2; EXECUTE stmt2; DEALLOCATE PREPARE stmt2; diff --git a/database/migrations/20260813000002_add_uk_inbound_order.sql b/database/migrations/20260813000002_add_uk_inbound_order.sql deleted file mode 100644 index 5052c6f1..00000000 --- a/database/migrations/20260813000002_add_uk_inbound_order.sql +++ /dev/null @@ -1,6 +0,0 @@ --- 任务 004:入库单 单据编号唯一约束(真实列名 order_no,非 bill_no) --- 软删除列 deleted 纳入复合键(未删唯一、已删可重建同单号) - -SET @idx = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_inbound_order' AND INDEX_NAME = 'uk_inbound_order_no'); -SET @sql = IF(@idx = 0, 'ALTER TABLE inv_inbound_order ADD UNIQUE INDEX uk_inbound_order_no (order_no, deleted)', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; diff --git a/database/migrations/20260813000003_add_uk_inbound_item.sql b/database/migrations/20260813000003_add_uk_inbound_item.sql deleted file mode 100644 index 422d65d3..00000000 --- a/database/migrations/20260813000003_add_uk_inbound_item.sql +++ /dev/null @@ -1,7 +0,0 @@ --- 任务 005:入库明细 行唯一约束 --- 真实列:order_id(非 inbound_order_id)+ material_id + batch_no(无 line_no 列,用业务行键替代) --- 软删除列 deleted 纳入复合键 - -SET @idx = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_inbound_item' AND INDEX_NAME = 'uk_inbound_order_line'); -SET @sql = IF(@idx = 0, 'ALTER TABLE inv_inbound_item ADD UNIQUE INDEX uk_inbound_order_line (order_id, material_id, batch_no, deleted)', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; diff --git a/database/migrations/20260813000004_add_uk_outbound_item.sql b/database/migrations/20260813000004_add_uk_outbound_item.sql deleted file mode 100644 index 86ae0a2b..00000000 --- a/database/migrations/20260813000004_add_uk_outbound_item.sql +++ /dev/null @@ -1,7 +0,0 @@ --- 任务 005:出库明细 行唯一约束 --- 真实列:order_id(非 outbound_order_id)+ material_id + batch_no(无 line_no 列,用业务行键替代) --- 软删除列 deleted 纳入复合键 - -SET @idx = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_outbound_item' AND INDEX_NAME = 'uk_outbound_order_line'); -SET @sql = IF(@idx = 0, 'ALTER TABLE inv_outbound_item ADD UNIQUE INDEX uk_outbound_order_line (order_id, material_id, batch_no, deleted)', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; diff --git a/database/migrations/20260813000005_add_uk_exchange_rate.sql b/database/migrations/20260813000005_add_uk_exchange_rate.sql deleted file mode 100644 index 66696257..00000000 --- a/database/migrations/20260813000005_add_uk_exchange_rate.sql +++ /dev/null @@ -1,7 +0,0 @@ --- 任务 008:汇率表 唯一约束 --- 真实列:from_currency, to_currency, rate_date(非 currency_code / effective_date) --- 同一币种对同一生效日仅存在一条汇率记录 - -SET @idx = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'sys_exchange_rate' AND INDEX_NAME = 'uk_exchange_rate_pair_date'); -SET @sql = IF(@idx = 0, 'ALTER TABLE sys_exchange_rate ADD UNIQUE INDEX uk_exchange_rate_pair_date (from_currency, to_currency, rate_date)', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; diff --git a/database/migrations/20260813000006_alter_process_card_factory.sql b/database/migrations/20260813000006_alter_process_card_factory.sql deleted file mode 100644 index bc6585f2..00000000 --- a/database/migrations/20260813000006_alter_process_card_factory.sql +++ /dev/null @@ -1,12 +0,0 @@ --- 任务 007:工艺卡 新增 factory_id + per-factory 复合唯一 --- 真实表 prd_process_card,已有全局唯一 uk_card_no(card_no),但无 factory_id --- 历史行 factory_id 置 NULL(card_no 已全局唯一,复合唯一安全;per-factory 在应用填充 factory_id 后生效) --- 幂等:列/索引已存在则跳过 - -SET @col = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'prd_process_card' AND COLUMN_NAME = 'factory_id'); -SET @sql = IF(@col = 0, 'ALTER TABLE prd_process_card ADD COLUMN factory_id BIGINT UNSIGNED NULL', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @idx = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'prd_process_card' AND INDEX_NAME = 'uk_factory_card_no'); -SET @sql2 = IF(@idx = 0, 'ALTER TABLE prd_process_card ADD UNIQUE INDEX uk_factory_card_no (factory_id, card_no)', 'SELECT 1'); -PREPARE stmt2 FROM @sql2; EXECUTE stmt2; DEALLOCATE PREPARE stmt2; diff --git a/database/migrations/20260813000007_alter_die_template_factory.sql b/database/migrations/20260813000007_alter_die_template_factory.sql deleted file mode 100644 index 378197d9..00000000 --- a/database/migrations/20260813000007_alter_die_template_factory.sql +++ /dev/null @@ -1,12 +0,0 @@ --- 任务 007:刀模 新增 factory_id + per-factory 复合唯一 --- 真实表 prd_die_template,已有全局唯一 uk_template_code(template_code),但无 factory_id / die_no --- 历史行 factory_id 置 NULL(template_code 已全局唯一,复合唯一安全;per-factory 在应用填充 factory_id 后生效) --- 幂等:列/索引已存在则跳过 - -SET @col = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'prd_die_template' AND COLUMN_NAME = 'factory_id'); -SET @sql = IF(@col = 0, 'ALTER TABLE prd_die_template ADD COLUMN factory_id BIGINT UNSIGNED NULL', 'SELECT 1'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @idx = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'prd_die_template' AND INDEX_NAME = 'uk_factory_die_no'); -SET @sql2 = IF(@idx = 0, 'ALTER TABLE prd_die_template ADD UNIQUE INDEX uk_factory_die_no (factory_id, template_code)', 'SELECT 1'); -PREPARE stmt2 FROM @sql2; EXECUTE stmt2; DEALLOCATE PREPARE stmt2; diff --git a/database/migrations/20260822080000_inventory_area_columns.ts b/database/migrations/20260822080000_inventory_area_columns.ts deleted file mode 100644 index 8f206565..00000000 --- a/database/migrations/20260822080000_inventory_area_columns.ts +++ /dev/null @@ -1,43 +0,0 @@ -/** - * 080 长度/尺寸类物料按"面积"计量库存 - * 分切(母卷分条)时按面积守恒,避免卷数虚增。 - * 仅 ADD COLUMN,不改动既有 quantity(卷) 语义。幂等(information_schema 探测)。 - */ -import { Connection } from 'mysql2/promise'; - -const COLS: Array<[string, string, string]> = [ - ['inv_inventory_batch', 'area', "DECIMAL(18,4) DEFAULT NULL COMMENT '面积(宽×长×数量),长度类物料计量用'"], - ['inv_inventory_batch', 'available_area', "DECIMAL(18,4) DEFAULT NULL COMMENT '可用面积'"], - ['inv_inventory', 'area', "DECIMAL(18,4) DEFAULT NULL COMMENT '面积汇总(派生自批次)'"], - ['inv_inventory', 'available_area', "DECIMAL(18,4) DEFAULT NULL COMMENT '可用面积汇总'"], -]; - -export async function up(conn: Connection): Promise { - for (const [table, col, def] of COLS) { - const [rows] = await conn.query( - `SELECT COUNT(*) c FROM information_schema.columns - WHERE table_schema = 'vnerpdacahng' AND table_name = ? AND column_name = ?`, - [table, col] - ); - if ((rows as any[])[0].c === 0) { - await conn.query(`ALTER TABLE \`${table}\` ADD COLUMN \`${col}\` ${def}`); - console.log(`+ added ${table}.${col}`); - } else { - console.log(`= exists ${table}.${col}`); - } - } -} - -export async function down(conn: Connection): Promise { - for (const [table, col] of COLS) { - const [rows] = await conn.query( - `SELECT COUNT(*) c FROM information_schema.columns - WHERE table_schema = 'vnerpdacahng' AND table_name = ? AND column_name = ?`, - [table, col] - ); - if ((rows as any[])[0].c > 0) { - await conn.query(`ALTER TABLE \`${table}\` DROP COLUMN \`${col}\``); - console.log(`- dropped ${table}.${col}`); - } - } -} diff --git a/database/migrations/20260822080001_outbound_return_schema.ts b/database/migrations/20260822080001_outbound_return_schema.ts deleted file mode 100644 index a3a1ab7a..00000000 --- a/database/migrations/20260822080001_outbound_return_schema.ts +++ /dev/null @@ -1,51 +0,0 @@ -/** - * 081 修复 outbound/confirm 与 purchase/return 后端 500 - * 代码/GET 引用了真实表缺失的列,导致整条链路 500。幂等(information_schema 探测)。 - */ -import { Connection } from 'mysql2/promise'; - -const COLS: Array<[string, string, string]> = [ - ['inv_outbound_order', 'auditor_id', 'BIGINT UNSIGNED DEFAULT NULL COMMENT \'审核人ID\''], - ['inv_outbound_order', 'audit_remark', "VARCHAR(512) DEFAULT NULL COMMENT '审核备注'"], - ['inv_outbound_order', 'customer_id', 'BIGINT UNSIGNED DEFAULT NULL COMMENT \'客户ID\''], - ['inv_outbound_order', 'customer_name', "VARCHAR(128) DEFAULT NULL COMMENT '客户名称'"], - ['inv_outbound_order', 'sales_order_no', "VARCHAR(50) DEFAULT NULL COMMENT '销售单号'"], - ['pur_purchase_return', 'currency', "VARCHAR(10) DEFAULT 'CNY' COMMENT '币种'"], - ['pur_purchase_return', 'exchange_rate', "DECIMAL(10,4) DEFAULT 1.0000 COMMENT '汇率'"], - ['pur_purchase_return', 'base_total_amount', "DECIMAL(18,4) DEFAULT 0.0000 COMMENT '本位币金额'"], - ['pur_purchase_return', 'base_currency', "VARCHAR(10) DEFAULT 'CNY' COMMENT '本位币'"], - ['pur_purchase_return_line', 'base_unit_price', "DECIMAL(18,4) DEFAULT 0.0000 COMMENT '本位币单价'"], - ['pur_purchase_return_line', 'base_amount', "DECIMAL(18,4) DEFAULT 0.0000 COMMENT '本位币金额'"], - ['fin_receivable', 'customer_name', "VARCHAR(128) DEFAULT NULL COMMENT '客户名称'"], - ['fin_receivable', 'source_id', 'BIGINT DEFAULT NULL COMMENT \'来源单ID\''], -]; - -export async function up(conn: Connection): Promise { - for (const [table, col, def] of COLS) { - const [rows] = await conn.query( - `SELECT COUNT(*) c FROM information_schema.columns - WHERE table_schema = 'vnerpdacahng' AND table_name = ? AND column_name = ?`, - [table, col] - ); - if ((rows as any[])[0].c === 0) { - await conn.query(`ALTER TABLE \`${table}\` ADD COLUMN \`${col}\` ${def}`); - console.log(`+ added ${table}.${col}`); - } else { - console.log(`= exists ${table}.${col}`); - } - } -} - -export async function down(conn: Connection): Promise { - for (const [table, col] of COLS) { - const [rows] = await conn.query( - `SELECT COUNT(*) c FROM information_schema.columns - WHERE table_schema = 'vnerpdacahng' AND table_name = ? AND column_name = ?`, - [table, col] - ); - if ((rows as any[])[0].c > 0) { - await conn.query(`ALTER TABLE \`${table}\` DROP COLUMN \`${col}\``); - console.log(`- dropped ${table}.${col}`); - } - } -} diff --git a/database/migrations/20260822080002_outbound_item_width.ts b/database/migrations/20260822080002_outbound_item_width.ts deleted file mode 100644 index a2de8b87..00000000 --- a/database/migrations/20260822080002_outbound_item_width.ts +++ /dev/null @@ -1,39 +0,0 @@ -/** - * 082 出库明细支持"需求宽度",用于长度类物料按宽度做 FIFO 横切(宽卷分条)。 - * 不传则后端回退到物料标称宽度。幂等(information_schema 探测)。 - */ -import { Connection } from 'mysql2/promise'; - -const COLS: Array<[string, string, string]> = [ - ['inv_outbound_item', 'width', "DECIMAL(18,4) DEFAULT NULL COMMENT '需求宽度(mm),长度类物料按此宽度做 FIFO 横切'"], -]; - -export async function up(conn: Connection): Promise { - for (const [table, col, def] of COLS) { - const [rows] = await conn.query( - `SELECT COUNT(*) c FROM information_schema.columns - WHERE table_schema = 'vnerpdacahng' AND table_name = ? AND column_name = ?`, - [table, col] - ); - if ((rows as any[])[0].c === 0) { - await conn.query(`ALTER TABLE \`${table}\` ADD COLUMN \`${col}\` ${def}`); - console.log(`+ added ${table}.${col}`); - } else { - console.log(`= exists ${table}.${col}`); - } - } -} - -export async function down(conn: Connection): Promise { - for (const [table, col] of COLS) { - const [rows] = await conn.query( - `SELECT COUNT(*) c FROM information_schema.columns - WHERE table_schema = 'vnerpdacahng' AND table_name = ? AND column_name = ?`, - [table, col] - ); - if ((rows as any[])[0].c > 0) { - await conn.query(`ALTER TABLE \`${table}\` DROP COLUMN \`${col}\``); - console.log(`- dropped ${table}.${col}`); - } - } -} diff --git a/database/migrations/20260822080003_create_outbound_batch_allocation.ts b/database/migrations/20260822080003_create_outbound_batch_allocation.ts deleted file mode 100644 index 39c6423f..00000000 --- a/database/migrations/20260822080003_create_outbound_batch_allocation.ts +++ /dev/null @@ -1,56 +0,0 @@ -/** - * 083 创建出库批次分配明细表 inv_outbound_batch_allocation - * 用于出库撤销(PUT)时按批次精确回滚(尤其宽度横切产生的母批+子批分配)。 - * 代码结构取自 six-critical-fixes 迁移,幂等(tableExists 探测)。 - */ -import { Connection } from 'mysql2/promise'; - -async function tableExists(conn: Connection, name: string): Promise { - const [rows] = await conn.query( - `SELECT COUNT(*) c FROM information_schema.tables WHERE table_schema = 'vnerpdacahng' AND table_name = ?`, - [name] - ); - return (rows as any[])[0].c > 0; -} - -export async function up(conn: Connection): Promise { - if (await tableExists(conn, 'inv_outbound_batch_allocation')) { - console.log('= exists inv_outbound_batch_allocation'); - return; - } - await conn.query(` - CREATE TABLE inv_outbound_batch_allocation ( - id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键ID', - source_type VARCHAR(30) NOT NULL COMMENT '来源类型: outbound_order/material_issue/outsource_issue', - source_id BIGINT UNSIGNED NOT NULL COMMENT '来源单据ID', - source_no VARCHAR(50) NOT NULL COMMENT '来源单据号', - warehouse_id BIGINT UNSIGNED NOT NULL COMMENT '仓库ID', - material_id BIGINT UNSIGNED NOT NULL COMMENT '物料ID', - batch_id BIGINT UNSIGNED NOT NULL COMMENT '批次ID', - batch_no VARCHAR(50) NOT NULL COMMENT '批次号', - allocated_qty DECIMAL(18,4) NOT NULL DEFAULT 0 COMMENT '分配数量', - unit_cost DECIMAL(18,4) DEFAULT 0 COMMENT '单位成本', - total_cost DECIMAL(18,4) DEFAULT 0 COMMENT '总成本', - fifo_mode VARCHAR(20) DEFAULT 'fifo_auto' COMMENT 'FIFO模式: fifo_auto/specified_batch/manual_override', - operator_id BIGINT UNSIGNED DEFAULT NULL COMMENT '操作人ID', - operator_name VARCHAR(50) DEFAULT NULL COMMENT '操作人姓名', - create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - PRIMARY KEY (id), - INDEX idx_source (source_type, source_id), - INDEX idx_source_no (source_no), - INDEX idx_warehouse (warehouse_id), - INDEX idx_material (material_id), - INDEX idx_batch (batch_id), - INDEX idx_fifo_mode (fifo_mode), - INDEX idx_create_time (create_time) - ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='出库批次分配明细表' - `); - console.log('+ created inv_outbound_batch_allocation'); -} - -export async function down(conn: Connection): Promise { - if (await tableExists(conn, 'inv_outbound_batch_allocation')) { - await conn.query(`DROP TABLE inv_outbound_batch_allocation`); - console.log('- dropped inv_outbound_batch_allocation'); - } -} diff --git a/database/migrations/20260822080004_batch_fifo_columns.ts b/database/migrations/20260822080004_batch_fifo_columns.ts deleted file mode 100644 index 7d5d2ac5..00000000 --- a/database/migrations/20260822080004_batch_fifo_columns.ts +++ /dev/null @@ -1,95 +0,0 @@ -/** - * 084 补齐 inv_inventory_batch 的 FIFO 排序/追溯列。 - * - * 背景(真实缺口,非新增功能): - * src/lib/fifo-allocation.ts 的 buildBatchQuery 一直 SELECT split_flag/opened_at/qr_code/location - * 并按 split_flag、opened_at 排序,但真实库 inv_inventory_batch 从未有这 4 列, - * 导致「不指定批次 → 自动 FIFO 分配」的整条路径运行时必然报 - * ER_BAD_FIELD_ERROR (1054) Unknown column 'split_flag'。 - * 线上未暴露是因为出库/领料实际都指定了批次号,走的是 specified_batch 分支。 - * - * 同样引用这些列的模块: - * - src/lib/fifo-allocation.ts (SELECT + ORDER BY) - * - src/lib/fifo-width-slit.ts (宽度横切 FIFO) - * - src/app/api/warehouse/outbound/fifo/route.ts:76-77 (ORDER BY) - * - src/application/services/MaterialLifecycleService.ts (b.opened_at) - * - src/lib/warehouse-core.ts:517 (b.opened_at) - * - * 语义(split_flag 取值见 src/lib/status-labels.ts SPLIT_FLAG_LABEL): - * 0-整料 1-小料 2-余料(余料优先出库,避免尾数积压) - * - * 幂等(information_schema 探测),可反复执行。 - */ -import { Connection } from 'mysql2/promise'; - -const COLS: Array<[string, string, string]> = [ - [ - 'inv_inventory_batch', - 'split_flag', - "TINYINT DEFAULT 0 COMMENT '拆分标记: 0-整料, 1-小料, 2-余料(余料优先出库)'", - ], - [ - 'inv_inventory_batch', - 'opened_at', - "DATETIME DEFAULT NULL COMMENT '开封时间,已开封批次优先出库'", - ], - ['inv_inventory_batch', 'qr_code', "VARCHAR(100) DEFAULT NULL COMMENT '批次二维码'"], - ['inv_inventory_batch', 'location', "VARCHAR(50) DEFAULT NULL COMMENT '库位'"], -]; - -const INDEXES: Array<[string, string, string]> = [ - ['inv_inventory_batch', 'idx_split_flag', 'split_flag'], - ['inv_inventory_batch', 'idx_qr_code', 'qr_code'], -]; - -async function columnExists(conn: Connection, table: string, col: string): Promise { - const [rows] = await conn.query( - `SELECT COUNT(*) c FROM information_schema.columns - WHERE table_schema = 'vnerpdacahng' AND table_name = ? AND column_name = ?`, - [table, col] - ); - return (rows as any[])[0].c > 0; -} - -async function indexExists(conn: Connection, table: string, idx: string): Promise { - const [rows] = await conn.query( - `SELECT COUNT(*) c FROM information_schema.statistics - WHERE table_schema = 'vnerpdacahng' AND table_name = ? AND index_name = ?`, - [table, idx] - ); - return (rows as any[])[0].c > 0; -} - -export async function up(conn: Connection): Promise { - for (const [table, col, def] of COLS) { - if (!(await columnExists(conn, table, col))) { - await conn.query(`ALTER TABLE \`${table}\` ADD COLUMN \`${col}\` ${def}`); - console.log(`+ added ${table}.${col}`); - } else { - console.log(`= exists ${table}.${col}`); - } - } - for (const [table, idx, cols] of INDEXES) { - if (!(await indexExists(conn, table, idx))) { - await conn.query(`ALTER TABLE \`${table}\` ADD INDEX \`${idx}\` (${cols})`); - console.log(`+ added index ${table}.${idx}`); - } else { - console.log(`= exists index ${table}.${idx}`); - } - } -} - -export async function down(conn: Connection): Promise { - for (const [table, idx] of INDEXES) { - if (await indexExists(conn, table, idx)) { - await conn.query(`ALTER TABLE \`${table}\` DROP INDEX \`${idx}\``); - console.log(`- dropped index ${table}.${idx}`); - } - } - for (const [table, col] of COLS) { - if (await columnExists(conn, table, col)) { - await conn.query(`ALTER TABLE \`${table}\` DROP COLUMN \`${col}\``); - console.log(`- dropped ${table}.${col}`); - } - } -} diff --git a/database/migrations/20260827_add_batch_id_to_detail_tables.sql b/database/migrations/20260827_add_batch_id_to_detail_tables.sql deleted file mode 100644 index b609e734..00000000 --- a/database/migrations/20260827_add_batch_id_to_detail_tables.sql +++ /dev/null @@ -1,168 +0,0 @@ --- ========================================== --- 迁移: 20260827_add_batch_id_to_detail_tables --- 用途: 补齐明细表 batch_id / original_inbound_date / location_id 字段, --- 确保库存全链路可追溯(对齐规则 P0) --- 关联代码: src/app/api/warehouse/**, src/app/api/production/material-* --- 执行: mysql -u root -p vnerpdacahng < 20260827_add_batch_id_to_detail_tables.sql --- 注意: 本迁移幂等,可重复执行(使用 IF NOT EXISTS / 检查列存在) --- ========================================== - -DROP PROCEDURE IF EXISTS add_column_if_not_exists; -DELIMITER // -CREATE PROCEDURE add_column_if_not_exists( - IN tbl VARCHAR(64), - IN col VARCHAR(64), - IN col_def TEXT -) -BEGIN - IF NOT EXISTS ( - SELECT 1 FROM information_schema.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = tbl AND COLUMN_NAME = col - ) THEN - SET @ddl = CONCAT('ALTER TABLE `', tbl, '` ADD COLUMN `', col, '` ', col_def); - PREPARE stmt FROM @ddl; - EXECUTE stmt; - DEALLOCATE PREPARE stmt; - END IF; -END// -DELIMITER ; - --- ========================================== --- 1. inv_sales_outbound_item — 🔴 核心缺失 --- 明细需绑定 batch_id 才能精确扣减批次库存 --- ========================================== -CALL add_column_if_not_exists('inv_sales_outbound_item', 'batch_id', - 'BIGINT UNSIGNED NULL COMMENT "库存批次 ID,关联 inv_inventory_batch.id"'); -CALL add_column_if_not_exists('inv_sales_outbound_item', 'location_id', - 'BIGINT UNSIGNED NULL COMMENT "出库库位 ID"'); -CALL add_column_if_not_exists('inv_sales_outbound_item', 'original_inbound_date', - 'DATE NULL COMMENT "原批次入库日期,用于 FIFO 校验"'); -CALL add_column_if_not_exists('inv_sales_outbound_item', 'qr_code', - 'VARCHAR(100) NULL COMMENT "二维码(副本)"'); - --- ========================================== --- 2. inv_outbound_item — 🔴 核心缺失 --- 销售出库/通用出库明细需绑定 batch_id --- ========================================== -CALL add_column_if_not_exists('inv_outbound_item', 'batch_id', - 'BIGINT UNSIGNED NULL COMMENT "库存批次 ID"'); -CALL add_column_if_not_exists('inv_outbound_item', 'location_id', - 'BIGINT UNSIGNED NULL COMMENT "出库库位 ID"'); -CALL add_column_if_not_exists('inv_outbound_item', 'original_inbound_date', - 'DATE NULL COMMENT "原批次入库日期"'); -CALL add_column_if_not_exists('inv_outbound_item', 'qr_code', - 'VARCHAR(100) NULL COMMENT "二维码"'); - --- ========================================== --- 3. inv_inbound_item — 🟡 业务扩展 --- 入库明细记录批次 ID,便于反向追溯 --- ========================================== -CALL add_column_if_not_exists('inv_inbound_item', 'batch_id', - 'BIGINT UNSIGNED NULL COMMENT "入库批次 ID,审核通过时在 InventorySyncHandler 中回填"'); -CALL add_column_if_not_exists('inv_inbound_item', 'original_inbound_date', - 'DATE NULL COMMENT "原始入库日期(= inbound_date)"'); -CALL add_column_if_not_exists('inv_inbound_item', 'location_id', - 'BIGINT UNSIGNED NULL COMMENT "入库库位 ID"'); - --- ========================================== --- 4. prd_material_issue_item — 🔴 生产领料核心 --- 领料明细必须绑定批次 ID,FIFO 扣减后可精确追溯 --- ========================================== -CALL add_column_if_not_exists('prd_material_issue_item', 'batch_id', - 'BIGINT UNSIGNED NULL COMMENT "领料使用的库存批次 ID"'); -CALL add_column_if_not_exists('prd_material_issue_item', 'original_inbound_date', - 'DATE NULL COMMENT "批次原始入库日期"'); - --- ========================================== --- 5. prd_material_return_item — 🔴 生产退料核心 --- 退料明细必须绑定原批次 ID,退库时需恢复正确批次 --- ========================================== -CALL add_column_if_not_exists('prd_material_return_item', 'batch_id', - 'BIGINT UNSIGNED NULL COMMENT "退料的库存批次 ID(与领料原批次一致)"'); -CALL add_column_if_not_exists('prd_material_return_item', 'original_inbound_date', - 'DATE NULL COMMENT "原批次入库日期"'); - --- ========================================== --- 6. inv_transfer_item — 🔴 调拨核心 --- 调拨明细必须绑定批次 ID,出库/入库流水需精确关联 --- ========================================== -CALL add_column_if_not_exists('inv_transfer_item', 'batch_id', - 'BIGINT UNSIGNED NULL COMMENT "库存批次 ID"'); -CALL add_column_if_not_exists('inv_transfer_item', 'original_inbound_date', - 'DATE NULL COMMENT "原批次入库日期"'); -CALL add_column_if_not_exists('inv_transfer_item', 'location_id', - 'BIGINT UNSIGNED NULL COMMENT "库位 ID"'); -CALL add_column_if_not_exists('inv_transfer_item', 'qr_code', - 'VARCHAR(64) NULL COMMENT "二维码"'); - --- ========================================== --- 7. inv_stocktaking_item — 🟡 盘点扩展 --- 盘点明细关联批次 ID,便于差异追溯 --- ========================================== -CALL add_column_if_not_exists('inv_stocktaking_item', 'batch_id', - 'BIGINT UNSIGNED NULL COMMENT "批次 ID"'); -CALL add_column_if_not_exists('inv_stocktaking_item', 'location_id', - 'BIGINT UNSIGNED NULL COMMENT "库位 ID"'); -CALL add_column_if_not_exists('inv_stocktaking_item', 'original_inbound_date', - 'DATE NULL COMMENT "原批次入库日期"'); -CALL add_column_if_not_exists('inv_stocktaking_item', 'qr_code', - 'VARCHAR(100) NULL COMMENT "二维码"'); - --- ========================================== --- 8. inv_stock_adjust_item — 🟡 调整扩展 --- 调整明细必须关联批次 ID,确保流水精确 --- ========================================== -CALL add_column_if_not_exists('inv_stock_adjust_item', 'batch_id', - 'BIGINT UNSIGNED NULL COMMENT "库存批次 ID"'); -CALL add_column_if_not_exists('inv_stock_adjust_item', 'location_id', - 'BIGINT UNSIGNED NULL COMMENT "库位 ID"'); -CALL add_column_if_not_exists('inv_stock_adjust_item', 'original_inbound_date', - 'DATE NULL COMMENT "原批次入库日期"'); -CALL add_column_if_not_exists('inv_stock_adjust_item', 'qr_code', - 'VARCHAR(100) NULL COMMENT "二维码"'); - --- ========================================== --- 9. 追加索引(IF NOT EXISTS) --- ========================================== -SET @ddl = 'ALTER TABLE `inv_sales_outbound_item` ADD INDEX `idx_batch_id` (`batch_id`)'; -PREPARE stmt FROM IFNULL(@ddl, 'SELECT 1'); -SELECT 1 INTO @dummy WHERE NOT EXISTS ( - SELECT 1 FROM information_schema.STATISTICS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_sales_outbound_item' AND INDEX_NAME = 'idx_batch_id' -); -PREPARE stmt FROM IFNULL(@ddl, 'SELECT 1'); EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @ddl = 'ALTER TABLE `prd_material_issue_item` ADD INDEX `idx_batch_id` (`batch_id`)'; -SELECT 1 INTO @dummy WHERE NOT EXISTS ( - SELECT 1 FROM information_schema.STATISTICS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'prd_material_issue_item' AND INDEX_NAME = 'idx_batch_id' -); -PREPARE stmt FROM IFNULL(@ddl, 'SELECT 1'); EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @ddl = 'ALTER TABLE `prd_material_return_item` ADD INDEX `idx_batch_id` (`batch_id`)'; -SELECT 1 INTO @dummy WHERE NOT EXISTS ( - SELECT 1 FROM information_schema.STATISTICS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'prd_material_return_item' AND INDEX_NAME = 'idx_batch_id' -); -PREPARE stmt FROM IFNULL(@ddl, 'SELECT 1'); EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @ddl = 'ALTER TABLE `inv_transfer_item` ADD INDEX `idx_batch_id` (`batch_id`)'; -SELECT 1 INTO @dummy WHERE NOT EXISTS ( - SELECT 1 FROM information_schema.STATISTICS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_transfer_item' AND INDEX_NAME = 'idx_batch_id' -); -PREPARE stmt FROM IFNULL(@ddl, 'SELECT 1'); EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @ddl = 'ALTER TABLE `inv_stocktaking_item` ADD INDEX `idx_batch_id` (`batch_id`)'; -SELECT 1 INTO @dummy WHERE NOT EXISTS ( - SELECT 1 FROM information_schema.STATISTICS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_stocktaking_item' AND INDEX_NAME = 'idx_batch_id' -); -PREPARE stmt FROM IFNULL(@ddl, 'SELECT 1'); EXECUTE stmt; DEALLOCATE PREPARE stmt; - -SET @ddl = 'ALTER TABLE `inv_stock_adjust_item` ADD INDEX `idx_batch_id` (`batch_id`)'; -SELECT 1 INTO @dummy WHERE NOT EXISTS ( - SELECT 1 FROM information_schema.STATISTICS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'inv_stock_adjust_item' AND INDEX_NAME = 'idx_batch_id' -); -PREPARE stmt FROM IFNULL(@ddl, 'SELECT 1'); EXECUTE stmt; DEALLOCATE PREPARE stmt; diff --git a/database/permission_system.sql b/database/permission_system.sql index fff614a5..88c12a00 100644 --- a/database/permission_system.sql +++ b/database/permission_system.sql @@ -60,7 +60,7 @@ INSERT INTO `sys_menu` (`parent_id`, `menu_name`, `menu_code`, `menu_type`, `ico -- 财务管理 (0, '财务管理', 'finance', 1, 'Banknote', '/finance', NULL, 'finance:*', 6, 1), -(18, '应收应付', 'fin_receivable', 2, NULL, '/finance/receivable', '/finance/receivable', 'finance:receivable:*', 1, 1), +(18, '应收应付', 'finance_receivable', 2, NULL, '/finance/receivable', '/finance/receivable', 'finance:receivable:*', 1, 1), (18, '成本核算', 'finance_cost', 2, NULL, '/finance/cost', '/finance/cost', 'finance:cost:*', 2, 1), (18, '财务报表', 'finance_report', 2, NULL, '/finance/report', '/finance/report', 'finance:report:*', 3, 1), @@ -149,7 +149,7 @@ SELECT 2, id FROM sys_menu WHERE status = 1 AND menu_code NOT LIKE 'settings_%'; -- 财务经理权限 INSERT INTO `sys_role_menu` (`role_id`, `menu_id`) -SELECT 3, id FROM sys_menu WHERE menu_code IN ('dashboard', 'finance', 'fin_receivable', 'finance_cost', 'finance_report', 'orders_sales', 'orders_customers'); +SELECT 3, id FROM sys_menu WHERE menu_code IN ('dashboard', 'finance', 'finance_receivable', 'finance_cost', 'finance_report', 'orders_sales', 'orders_customers'); -- 销售经理权限 INSERT INTO `sys_role_menu` (`role_id`, `menu_id`) @@ -169,7 +169,7 @@ SELECT 7, id FROM sys_menu WHERE menu_code IN ('dashboard', 'quality', 'quality_ -- 采购经理权限 INSERT INTO `sys_role_menu` (`role_id`, `menu_id`) -SELECT 8, id FROM sys_menu WHERE menu_code IN ('dashboard', 'purchase', 'purchase_orders', 'purchase_suppliers', 'warehouse', 'warehouse_inventory', 'finance', 'fin_receivable'); +SELECT 8, id FROM sys_menu WHERE menu_code IN ('dashboard', 'purchase', 'purchase_orders', 'purchase_suppliers', 'warehouse', 'warehouse_inventory', 'finance', 'finance_receivable'); -- 人事经理权限 INSERT INTO `sys_role_menu` (`role_id`, `menu_id`) diff --git a/database/reconcile_inventory.sql b/database/reconcile_inventory.sql deleted file mode 100644 index bf878788..00000000 --- a/database/reconcile_inventory.sql +++ /dev/null @@ -1,87 +0,0 @@ --- ============================================================ --- vnerp 库存账实对账脚本(只读,不修改任何数据) --- 适用库:vnerpdacahng --- 说明: --- inv_inventory_batch = 批次明细库存(最细粒度,quantity 为库存量) --- inv_inventory = 物料+仓库 账面汇总库存 --- inv_inventory_transaction = 流水账(当前库实测为 0 行,未启用,故本对账以两张库存表互校) --- inv_inventory_log = 移动日志(仅 STOCKTAKING/INBOUND,无批次号/前后量,无法重建余额) --- 原则:账面汇总 inv_inventory.quantity 必须等于 SUM(inv_inventory_batch.quantity) --- 同一 (material_id, warehouse_id) 分组,软删行 excluded(deleted=0) --- ============================================================ - --- 0) 前置健康检查 -SELECT 'inv_inventory_batch 行数(含软删)' AS chk, COUNT(*) AS cnt FROM inv_inventory_batch; -SELECT 'inv_inventory 有效行(deleted=0)' AS chk, COUNT(*) AS cnt FROM inv_inventory WHERE deleted=0; -SELECT 'inv_inventory_transaction 行数' AS chk, COUNT(*) AS cnt FROM inv_inventory_transaction; -SELECT 'inv_inventory_log 行数' AS chk, COUNT(*) AS cnt FROM inv_inventory_log; - --- 1) 主对账:账面汇总 vs 批次明细合计(含数量与可用量差异) --- 返回所有不一致的行(diff <> 0) -SELECT - i.material_id, - i.warehouse_id, - i.quantity AS inv_qty, - i.available_qty AS inv_avail, - IFNULL(b.qty,0) AS batch_qty, - IFNULL(b.avail,0) AS batch_avail, - ROUND(i.quantity - IFNULL(b.qty,0), 4) AS diff_qty, - ROUND(i.available_qty - IFNULL(b.avail,0), 4) AS diff_avail -FROM inv_inventory i -LEFT JOIN ( - SELECT material_id, warehouse_id, - SUM(quantity) AS qty, - SUM(available_qty) AS avail - FROM inv_inventory_batch - WHERE deleted = 0 - GROUP BY material_id, warehouse_id -) b ON b.material_id = i.material_id AND b.warehouse_id = i.warehouse_id -WHERE i.deleted = 0 - AND ( - ROUND(i.quantity - IFNULL(b.qty,0), 4) <> 0 - OR ROUND(i.available_qty - IFNULL(b.avail,0), 4) <> 0 - ) -ORDER BY ABS(i.quantity - IFNULL(b.qty,0)) DESC; - --- 2) 孤儿批次:有批次库存,但 inv_inventory 没有对应汇总行 -SELECT - b.material_id, - b.warehouse_id, - SUM(b.quantity) AS batch_qty, - SUM(b.available_qty) AS batch_avail -FROM inv_inventory_batch b -LEFT JOIN inv_inventory i - ON i.material_id = b.material_id - AND i.warehouse_id = b.warehouse_id - AND i.deleted = 0 -WHERE b.deleted = 0 - AND i.id IS NULL -GROUP BY b.material_id, b.warehouse_id; - --- 3) 孤儿汇总:有 inv_inventory 汇总行,但一个批次都没有 -SELECT - i.material_id, - i.warehouse_id, - i.quantity AS inv_qty, - i.available_qty AS inv_avail -FROM inv_inventory i -LEFT JOIN inv_inventory_batch b - ON b.material_id = i.material_id - AND b.warehouse_id = i.warehouse_id - AND b.deleted = 0 -WHERE i.deleted = 0 - AND b.id IS NULL; - --- 4) 行内自相矛盾:available_qty > quantity(库存逻辑不可能) -SELECT 'batch' AS tbl, id, material_id, warehouse_id, quantity, available_qty -FROM inv_inventory_batch -WHERE deleted = 0 AND available_qty > quantity -UNION ALL -SELECT 'inv' AS tbl, id, material_id, warehouse_id, quantity, available_qty -FROM inv_inventory -WHERE deleted = 0 AND available_qty > quantity; - --- 5) 差异物料的批次明细(排查用,按需改 material_id/warehouse_id) --- SELECT batch_no, material_id, warehouse_id, quantity, available_qty, inbound_date, status --- FROM inv_inventory_batch --- WHERE deleted = 0 AND material_id = 11 AND warehouse_id = 3; diff --git a/database/repair_inventory_summary.sql b/database/repair_inventory_summary.sql deleted file mode 100644 index a37ab1f0..00000000 --- a/database/repair_inventory_summary.sql +++ /dev/null @@ -1,55 +0,0 @@ --- ============================================================ --- hot-07 库存脏数据修复:以批次明细(inv_inventory_batch)为准,重建汇总表(inv_inventory) --- 适用库:vnerpdacahng --- 原则(已与老板确认):批次明细为权威源;inv_inventory 汇总由批次派生重算 --- 安全性: --- 1) 先备份 inv_inventory -> inv_inventory_bak_YYYYMMDD(已存在则跳过,可回滚) --- 2) 全部 UPDATE/INSERT 均为幂等,可重复执行 --- 3) 执行后用 database/reconcile_inventory.sql 验证 diff 全为 0 --- 回滚:DROP TABLE IF EXISTS inv_inventory; RENAME TABLE inv_inventory_bak_YYYYMMDD TO inv_inventory; --- ============================================================ - --- 0) 备份(仅首次) -CREATE TABLE IF NOT EXISTS inv_inventory_bak_20260816 AS SELECT * FROM inv_inventory; - --- 1) 现有汇总行:按批次合计刷新数量/可用量(同时修复 available>quantity 的行内矛盾) -UPDATE inv_inventory i -JOIN ( - SELECT material_id, warehouse_id, SUM(quantity) q, SUM(available_qty) a - FROM inv_inventory_batch WHERE deleted = 0 - GROUP BY material_id, warehouse_id -) b ON b.material_id = i.material_id AND b.warehouse_id = i.warehouse_id AND i.deleted = 0 -SET i.quantity = b.q, - i.available_qty = b.a, - i.version = COALESCE(i.version, 0) + 1, - i.update_time = NOW(); - --- 2) 孤儿批次(有批次但无汇总行):补建汇总行,主数据从 inv_material / inv_warehouse 取 -INSERT INTO inv_inventory - (material_id, material_code, material_name, warehouse_id, warehouse_name, - quantity, available_qty, batch_no, locked_qty, unit, version, create_time, update_time, deleted) -SELECT - b.material_id, m.material_code, m.material_name, b.warehouse_id, w.warehouse_name, - b.q, b.a, NULL, 0, m.unit, 0, NOW(), NOW(), 0 -FROM ( - SELECT material_id, warehouse_id, SUM(quantity) q, SUM(available_qty) a - FROM inv_inventory_batch WHERE deleted = 0 - GROUP BY material_id, warehouse_id -) b -JOIN inv_material m ON m.id = b.material_id -JOIN inv_warehouse w ON w.id = b.warehouse_id -LEFT JOIN inv_inventory i - ON i.material_id = b.material_id AND i.warehouse_id = b.warehouse_id AND i.deleted = 0 -WHERE i.id IS NULL; - --- 3) 孤儿汇总(有汇总行但一个批次都没有):清零(保留行,便于追溯) -UPDATE inv_inventory i -LEFT JOIN ( - SELECT material_id, warehouse_id FROM inv_inventory_batch WHERE deleted = 0 - GROUP BY material_id, warehouse_id -) b ON b.material_id = i.material_id AND b.warehouse_id = i.warehouse_id -SET i.quantity = 0, - i.available_qty = 0, - i.version = COALESCE(i.version, 0) + 1, - i.update_time = NOW() -WHERE i.deleted = 0 AND b.material_id IS NULL; diff --git a/database/seed_business_data.sql b/database/seed_business_data.sql index dfccb823..ed991c75 100644 --- a/database/seed_business_data.sql +++ b/database/seed_business_data.sql @@ -1399,17 +1399,32 @@ INSERT IGNORE INTO qc_final_inspection (inspection_no, inspection_date, work_ord -- ============================================================ -- 56. 不合格品记录 (10条) -- ============================================================ -INSERT IGNORE INTO qc_unqualified (unqualified_no, source_type, source_no, material_id, material_name, batch_no, quantity, defect_type, defect_desc, handle_type, handle_status, handle_result, handler, handle_date, remark, create_time) VALUES -('UQ-20250126-001', 'final', 'FQC-20250126-001', 25, '电视背板标签', 'B20250126-001', 400, '外观不良', '印刷偏色', 1, 3, NULL, '周杰', '2025-01-27', '电视背板标签偏色返工', '2025-01-27 10:00:00'), -('UQ-20250306-001', 'final', 'FQC-20250306-001', 23, '手机后盖标签', 'B20250306-001', 200, '尺寸偏差', '尺寸超出公差范围', 1, 3, NULL, '周杰', '2025-03-07', '手机后盖标签尺寸偏差返工', '2025-03-07 10:00:00'), -('UQ-20250321-001', 'final', 'FQC-20250321-001', 24, '汽车内饰标签', 'B20250321-001', 150, '表面缺陷', '表面有轻微划痕', 3, 3, NULL, '周杰', '2025-03-22', '汽车内饰标签有条件接收', '2025-03-22 10:00:00'), -('UQ-20250213-001', 'incoming', 'IQC-20250213-001', 3, 'PVC透明膜0.15mm', 'B20250213-001', 5, '色差', '轻微色差', 3, 3, NULL, '周杰', '2025-02-14', 'PVC透明膜轻微色差有条件接收', '2025-02-14 10:00:00'), -('UQ-20250323-001', 'incoming', 'IQC-20250323-001', 3, 'PVC透明膜0.15mm', 'B20250323-001', 20, '厚度偏差', '厚度超差0.165mm', 4, 3, NULL, '周杰', '2025-03-24', 'PVC透明膜厚度超差退货', '2025-03-24 10:00:00'), -('UQ-20250109-001', 'process', 'PQC-20250109-001', 21, '空调面板标签', 'B20250109-001', 200, '印刷不良', '丝印偏位', 1, 3, NULL, '周杰', '2025-01-10', '空调面板标签丝印偏位返工', '2025-01-10 10:00:00'), -('UQ-20250116-001', 'process', 'PQC-20250116-001', 23, '手机后盖标签', 'B20250116-001', 200, '导电不良', '导电线路断路', 1, 3, NULL, '周杰', '2025-01-17', '手机后盖标签导电线路返工', '2025-01-17 10:00:00'), -('UQ-20250216-001', 'process', 'PQC-20250216-001', 29, '电池产品标签', 'B20250216-001', 250, '外观不良', '印刷模糊', 1, 3, NULL, '周杰', '2025-02-17', '电池产品标签印刷模糊返工', '2025-02-17 10:00:00'), -('UQ-20250311-001', 'process', 'PQC-20250311-001', 21, '空调面板标签', 'B20250311-001', 200, '套印不准', '多色套印偏差', 1, 3, NULL, '周杰', '2025-03-12', '空调面板标签套印偏差返工', '2025-03-12 10:00:00'), -('UQ-20250401-001', 'adjust', 'SA-20250401-001', 24, '汽车内饰标签', 'B20250321-001', 2000, '表面划伤', '运输过程损坏', 2, 3, NULL, '周杰', '2025-04-02', '汽车内饰标签运输损坏报废', '2025-04-02 10:00:00'); +INSERT IGNORE INTO qc_unqualified (unqualified_no, inspection_type, source_no, material_id, material_name, batch_no, unqualified_qty, unqualified_type, unqualified_reason, handle_method, handle_result, handler_id, handle_date, status, remark, create_time) VALUES +('UQ-20250126-001', 'final', 'FQC-20250126-001', 25, '电视背板标签', 'B20250126-001', 400, '外观不良', '印刷偏色', 'rework', '返工后合格', 9, '2025-01-27', 'completed', '电视背板标签偏色返工', '2025-01-27 10:00:00'), +('UQ-20250306-001', 'final', 'FQC-20250306-001', 23, '手机后盖标签', 'B20250306-001', 200, '尺寸偏差', '尺寸超出公差范围', 'rework', '返工后合格', 9, '2025-03-07', 'completed', '手机后盖标签尺寸偏差返工', '2025-03-07 10:00:00'), +('UQ-20250321-001', 'final', 'FQC-20250321-001', 24, '汽车内饰标签', 'B20250321-001', 150, '表面缺陷', '表面有轻微划痕', 'conditional', '有条件接收', 9, '2025-03-22', 'completed', '汽车内饰标签有条件接收', '2025-03-22 10:00:00'), +('UQ-20250213-001', 'incoming', 'IQC-20250213-001', 3, 'PVC透明膜0.15mm', 'B20250213-001', 5, '色差', '轻微色差', 'conditional', '有条件接收', 9, '2025-02-14', 'completed', 'PVC透明膜轻微色差有条件接收', '2025-02-14 10:00:00'), +('UQ-20250323-001', 'incoming', 'IQC-20250323-001', 3, 'PVC透明膜0.15mm', 'B20250323-001', 20, '厚度偏差', '厚度超差0.165mm', 'return', '退货处理', 9, '2025-03-24', 'completed', 'PVC透明膜厚度超差退货', '2025-03-24 10:00:00'), +('UQ-20250109-001', 'process', 'PQC-20250109-001', 21, '空调面板标签', 'B20250109-001', 200, '印刷不良', '丝印偏位', 'rework', '返工后合格', 9, '2025-01-10', 'completed', '空调面板标签丝印偏位返工', '2025-01-10 10:00:00'), +('UQ-20250116-001', 'process', 'PQC-20250116-001', 23, '手机后盖标签', 'B20250116-001', 200, '导电不良', '导电线路断路', 'rework', '返工后合格', 9, '2025-01-17', 'completed', '手机后盖标签导电线路返工', '2025-01-17 10:00:00'), +('UQ-20250216-001', 'process', 'PQC-20250216-001', 29, '电池产品标签', 'B20250216-001', 250, '外观不良', '印刷模糊', 'rework', '返工后合格', 9, '2025-02-17', 'completed', '电池产品标签印刷模糊返工', '2025-02-17 10:00:00'), +('UQ-20250311-001', 'process', 'PQC-20250311-001', 21, '空调面板标签', 'B20250311-001', 200, '套印不准', '多色套印偏差', 'rework', '返工后合格', 9, '2025-03-12', 'completed', '空调面板标签套印偏差返工', '2025-03-12 10:00:00'), +('UQ-20250401-001', 'adjust', 'SA-20250401-001', 24, '汽车内饰标签', 'B20250321-001', 2000, '表面划伤', '运输过程损坏', 'scrap', '报废处理', 9, '2025-04-02', 'completed', '汽车内饰标签运输损坏报废', '2025-04-02 10:00:00'); + +-- ============================================================ +-- 57. 不合格品处理 (10条) +-- ============================================================ +INSERT IGNORE INTO qc_unqualified_handle (unqualified_id, handle_method, handle_qty, handle_result, handler_id, handler_name, handle_date, remark, create_time) VALUES +(1, 'rework', 400, '返工后合格', 9, '周杰', '2025-01-27', '电视背板标签偏色返工处理', '2025-01-27 10:00:00'), +(2, 'rework', 200, '返工后合格', 9, '周杰', '2025-03-07', '手机后盖标签尺寸偏差返工处理', '2025-03-07 10:00:00'), +(3, 'conditional', 150, '有条件接收', 9, '周杰', '2025-03-22', '汽车内饰标签有条件接收处理', '2025-03-22 10:00:00'), +(4, 'conditional', 5, '有条件接收', 9, '周杰', '2025-02-14', 'PVC透明膜轻微色差有条件接收', '2025-02-14 10:00:00'), +(5, 'return', 20, '退货处理', 9, '周杰', '2025-03-24', 'PVC透明膜厚度超差退货处理', '2025-03-24 10:00:00'), +(6, 'rework', 200, '返工后合格', 9, '周杰', '2025-01-10', '空调面板标签丝印偏位返工处理', '2025-01-10 10:00:00'), +(7, 'rework', 200, '返工后合格', 9, '周杰', '2025-01-17', '手机后盖标签导电线路返工处理', '2025-01-17 10:00:00'), +(8, 'rework', 250, '返工后合格', 9, '周杰', '2025-02-17', '电池产品标签印刷模糊返工处理', '2025-02-17 10:00:00'), +(9, 'rework', 200, '返工后合格', 9, '周杰', '2025-03-12', '空调面板标签套印偏差返工处理', '2025-03-12 10:00:00'), +(10, 'scrap', 2000, '报废处理', 9, '周杰', '2025-04-02', '汽车内饰标签运输损坏报废处理', '2025-04-02 10:00:00'); -- ############################################################ diff --git a/database/seed_part4_dcprint_qc_eqp.sql b/database/seed_part4_dcprint_qc_eqp.sql index 19668d78..ef466d7a 100644 --- a/database/seed_part4_dcprint_qc_eqp.sql +++ b/database/seed_part4_dcprint_qc_eqp.sql @@ -685,31 +685,31 @@ VALUES -- ----------------------------------------------------------- --- 22. qc_unqualified 不合格处理 (20条) +-- 22. qc_unqualified_handle 不合格处理 (20条) -- ----------------------------------------------------------- -INSERT IGNORE INTO qc_unqualified -(unqualified_no, handle_no, inspection_id, source_type, material_id, material_code, material_name, quantity, defect_type, defect_desc, handle_type, handle_status, responsible_dept, responsible_person, cost_amount, handler, handle_date, remark, create_time, update_time, create_by, deleted) +INSERT IGNORE INTO qc_unqualified_handle +(handle_no, inspection_id, material_id, material_code, material_name, unqualified_qty, handle_type, handle_status, responsible_dept, responsible_person, handle_result, cost_amount, remark, create_time, update_time, create_by, deleted) VALUES -('UQ-20250512-001', 'UH-20250512-001', 1, 'incoming', 8, 'UV-BK', 'UV油墨-黑色', 18.0000, '来料不合格', '退货处理-供应商承担', 2, 1, '采购部', '张伟', 2520.0000, '张伟', '2025-04-04', 'UV油墨-黑色不合格处理完成', '2025-04-04 17:00:00', '2025-04-04 17:00:00', 4, 0), -('UQ-20250512-002', 'UH-20250512-002', 2, 'incoming', 10, 'SOL-BK', '溶剂型油墨-黑色', 12.0000, '来料不合格', '让步接收-降级使用', 1, 1, '品质部', '李娜', 1440.0000, '李娜', '2025-04-05', '溶剂型油墨-黑色不合格处理完成', '2025-04-05 17:00:00', '2025-04-05 17:00:00', 5, 0), -('UQ-20250512-003', 'UH-20250512-003', 3, 'incoming', 8, 'UV-BK', 'UV油墨-黑色', 9.6000, '来料不合格', '返工处理-重新印刷', 3, 1, '生产部', '王强', 1344.0000, '王强', '2025-04-06', 'UV油墨-黑色不合格处理完成', '2025-04-06 17:00:00', '2025-04-06 17:00:00', 6, 0), -('UQ-20250512-004', 'UH-20250512-004', 4, 'incoming', 8, 'UV-BK', 'UV油墨-黑色', 5.4000, '来料不合格', '退货处理-供应商承担', 2, 1, '采购部', '刘洋', 648.0000, '刘洋', '2025-04-07', 'UV油墨-黑色不合格处理完成', '2025-04-07 17:00:00', '2025-04-07 17:00:00', 7, 0), -('UQ-20250512-005', 'UH-20250512-005', 5, 'incoming', 14, 'SCREEN-300', '丝印网版-300目', 2.2500, '来料不合格', '让步接收-降级使用', 1, 1, '品质部', '陈明', 1012.5000, '陈明', '2025-04-08', '丝印网版-300目不合格处理完成', '2025-04-08 17:00:00', '2025-04-08 17:00:00', 8, 0), -('UQ-20250512-006', 'UH-20250512-006', 6, 'incoming', 9, 'UV-WH', 'UV油墨-白色', 15.0000, '来料不合格', '退货处理-供应商承担', 2, 1, '采购部', '赵磊', 2025.0000, '赵磊', '2025-04-09', 'UV油墨-白色不合格处理完成', '2025-04-09 17:00:00', '2025-04-09 17:00:00', 4, 0), -('UQ-20250512-007', 'UH-20250512-007', 7, 'incoming', 16, 'THINNER', '稀释剂-标准型', 45.0000, '来料不合格', '让步接收-降级使用', 1, 1, '品质部', '孙丽', 1350.0000, '孙丽', '2025-04-10', '稀释剂-标准型不合格处理完成', '2025-04-10 17:00:00', '2025-04-10 17:00:00', 5, 0), -('UQ-20250512-008', 'UH-20250512-008', 8, 'incoming', 17, 'UV-VARNISH', 'UV光油-高光', 7.5000, '来料不合格', '退货处理-供应商承担', 2, 1, '采购部', '周杰', 975.0000, '周杰', '2025-04-11', 'UV光油-高光不合格处理完成', '2025-04-11 17:00:00', '2025-04-11 17:00:00', 6, 0), -('UQ-20250512-009', 'UH-20250512-009', 9, 'incoming', 5, 'BOPP-001', 'BOPP透明膜0.08mm', 90.0000, '来料不合格', '让步接收-降级使用', 1, 1, '品质部', '吴芳', 1800.0000, '吴芳', '2025-04-12', 'BOPP透明膜0.08mm不合格处理完成', '2025-04-12 17:00:00', '2025-04-12 17:00:00', 7, 0), -('UQ-20250512-010', 'UH-20250512-010', 10, 'incoming', 20, 'FILM-SHRINK', '热收缩膜', 30.0000, '来料不合格', '返工处理-重新加工', 3, 1, '生产部', '张伟', 690.0000, '张伟', '2025-04-13', '热收缩膜不合格处理完成', '2025-04-13 17:00:00', '2025-04-13 17:00:00', 8, 0), -('UQ-20250512-011', 'UH-20250512-011', 11, 'incoming', 1, 'PET-001', 'PET透明膜0.1mm', 120.0000, '来料不合格', '让步接收-降级使用', 1, 1, '采购部', '李娜', 3000.0000, '李娜', '2025-04-14', 'PET透明膜0.1mm不合格处理完成', '2025-04-14 17:00:00', '2025-04-14 17:00:00', 4, 0), -('UQ-20250512-012', 'UH-20250512-012', 12, 'incoming', 4, 'PVC-002', 'PVC白膜0.2mm', 112.5000, '来料不合格', '退货处理-供应商承担', 2, 1, '品质部', '王强', 2531.2500, '王强', '2025-04-15', 'PVC白膜0.2mm不合格处理完成', '2025-04-15 17:00:00', '2025-04-15 17:00:00', 5, 0), -('UQ-20250512-013', 'UH-20250512-013', 13, 'incoming', 6, 'PE-001', 'PE膜0.05mm', 60.0000, '来料不合格', '让步接收-降级使用', 1, 1, '采购部', '刘洋', 1200.0000, '刘洋', '2025-04-16', 'PE膜0.05mm不合格处理完成', '2025-04-16 17:00:00', '2025-04-16 17:00:00', 6, 0), -('UQ-20250512-014', 'UH-20250512-014', 14, 'incoming', 7, 'PAPER-001', '铜版纸80g', 75.0000, '来料不合格', '退货处理-供应商承担', 2, 1, '品质部', '陈明', 1500.0000, '陈明', '2025-04-17', '铜版纸80g不合格处理完成', '2025-04-17 17:00:00', '2025-04-17 17:00:00', 7, 0), -('UQ-20250512-015', 'UH-20250512-015', 15, 'incoming', 11, 'AG-PASTE', '导电银浆-高导电', 6.0000, '来料不合格', '返工处理-重新加工', 3, 1, '生产部', '赵磊', 4200.0000, '赵磊', '2025-04-18', '导电银浆-高导电不合格处理完成', '2025-04-18 17:00:00', '2025-04-18 17:00:00', 8, 0), -('UQ-20250512-016', 'UH-20250512-016', 16, 'incoming', 12, 'DIE-001', '模切刀模-平板', 1.5000, '来料不合格', '让步接收-降级使用', 1, 1, '品质部', '孙丽', 750.0000, '孙丽', '2025-04-19', '模切刀模-平板不合格处理完成', '2025-04-19 17:00:00', '2025-04-19 17:00:00', 4, 0), -('UQ-20250512-017', 'UH-20250512-017', 17, 'incoming', 15, 'LABEL-ELEC', '电子标签', 4000.0000, '来料不合格', '退货处理-供应商承担', 2, 1, '生产部', '周杰', 6000.0000, '周杰', '2025-04-20', '电子标签不合格处理完成', '2025-04-20 17:00:00', '2025-04-20 17:00:00', 5, 0), -('UQ-20250512-018', 'UH-20250512-018', 18, 'incoming', 13, 'LABEL-AC', '空调面板标签', 4500.0000, '来料不合格', '让步接收-降级使用', 1, 1, '品质部', '吴芳', 5850.0000, '吴芳', '2025-04-21', '空调面板标签不合格处理完成', '2025-04-21 17:00:00', '2025-04-21 17:00:00', 6, 0), -('UQ-20250512-019', 'UH-20250512-019', 19, 'incoming', 18, 'LABEL-WASH', '洗衣机面板标签', 1600.0000, '来料不合格', '返工处理-重新加工', 3, 1, '生产部', '张伟', 1920.0000, '张伟', '2025-04-22', '洗衣机面板标签不合格处理完成', '2025-04-22 17:00:00', '2025-04-22 17:00:00', 7, 0), -('UQ-20250512-020', 'UH-20250512-020', 20, 'incoming', 19, 'LABEL-PHONE', '手机后盖标签', 5000.0000, '来料不合格', '退货处理-供应商承担', 2, 1, '品质部', '李娜', 3250.0000, '李娜', '2025-04-23', '手机后盖标签不合格处理完成', '2025-04-23 17:00:00', '2025-04-23 17:00:00', 8, 0); +('UH-20250512-001', 1, 8, 'UV-BK', 'UV油墨-黑色', 18.0000, 2, 1, '采购部', '张伟', '退货处理-供应商承担', 2520.0000, 'UV油墨-黑色不合格处理完成', '2025-04-04 17:00:00', '2025-04-04 17:00:00', 4, 0), +('UH-20250512-002', 2, 10, 'SOL-BK', '溶剂型油墨-黑色', 12.0000, 1, 1, '品质部', '李娜', '让步接收-降级使用', 1440.0000, '溶剂型油墨-黑色不合格处理完成', '2025-04-05 17:00:00', '2025-04-05 17:00:00', 5, 0), +('UH-20250512-003', 3, 8, 'UV-BK', 'UV油墨-黑色', 9.6000, 3, 1, '生产部', '王强', '返工处理-重新印刷', 1344.0000, 'UV油墨-黑色不合格处理完成', '2025-04-06 17:00:00', '2025-04-06 17:00:00', 6, 0), +('UH-20250512-004', 4, 8, 'UV-BK', 'UV油墨-黑色', 5.4000, 2, 1, '采购部', '刘洋', '退货处理-供应商承担', 648.0000, 'UV油墨-黑色不合格处理完成', '2025-04-07 17:00:00', '2025-04-07 17:00:00', 7, 0), +('UH-20250512-005', 5, 14, 'SCREEN-300', '丝印网版-300目', 2.2500, 1, 1, '品质部', '陈明', '让步接收-降级使用', 1012.5000, '丝印网版-300目不合格处理完成', '2025-04-08 17:00:00', '2025-04-08 17:00:00', 8, 0), +('UH-20250512-006', 6, 9, 'UV-WH', 'UV油墨-白色', 15.0000, 2, 1, '采购部', '赵磊', '退货处理-供应商承担', 2025.0000, 'UV油墨-白色不合格处理完成', '2025-04-09 17:00:00', '2025-04-09 17:00:00', 4, 0), +('UH-20250512-007', 7, 16, 'THINNER', '稀释剂-标准型', 45.0000, 1, 1, '品质部', '孙丽', '让步接收-降级使用', 1350.0000, '稀释剂-标准型不合格处理完成', '2025-04-10 17:00:00', '2025-04-10 17:00:00', 5, 0), +('UH-20250512-008', 8, 17, 'UV-VARNISH', 'UV光油-高光', 7.5000, 2, 1, '采购部', '周杰', '退货处理-供应商承担', 975.0000, 'UV光油-高光不合格处理完成', '2025-04-11 17:00:00', '2025-04-11 17:00:00', 6, 0), +('UH-20250512-009', 9, 5, 'BOPP-001', 'BOPP透明膜0.08mm', 90.0000, 1, 1, '品质部', '吴芳', '让步接收-降级使用', 1800.0000, 'BOPP透明膜0.08mm不合格处理完成', '2025-04-12 17:00:00', '2025-04-12 17:00:00', 7, 0), +('UH-20250512-010', 10, 20, 'FILM-SHRINK', '热收缩膜', 30.0000, 3, 1, '生产部', '张伟', '返工处理-重新加工', 690.0000, '热收缩膜不合格处理完成', '2025-04-13 17:00:00', '2025-04-13 17:00:00', 8, 0), +('UH-20250512-011', 11, 1, 'PET-001', 'PET透明膜0.1mm', 120.0000, 1, 1, '采购部', '李娜', '让步接收-降级使用', 3000.0000, 'PET透明膜0.1mm不合格处理完成', '2025-04-14 17:00:00', '2025-04-14 17:00:00', 4, 0), +('UH-20250512-012', 12, 4, 'PVC-002', 'PVC白膜0.2mm', 112.5000, 2, 1, '品质部', '王强', '退货处理-供应商承担', 2531.2500, 'PVC白膜0.2mm不合格处理完成', '2025-04-15 17:00:00', '2025-04-15 17:00:00', 5, 0), +('UH-20250512-013', 13, 6, 'PE-001', 'PE膜0.05mm', 60.0000, 1, 1, '采购部', '刘洋', '让步接收-降级使用', 1200.0000, 'PE膜0.05mm不合格处理完成', '2025-04-16 17:00:00', '2025-04-16 17:00:00', 6, 0), +('UH-20250512-014', 14, 7, 'PAPER-001', '铜版纸80g', 75.0000, 2, 1, '品质部', '陈明', '退货处理-供应商承担', 1500.0000, '铜版纸80g不合格处理完成', '2025-04-17 17:00:00', '2025-04-17 17:00:00', 7, 0), +('UH-20250512-015', 15, 11, 'AG-PASTE', '导电银浆-高导电', 6.0000, 3, 1, '生产部', '赵磊', '返工处理-重新加工', 4200.0000, '导电银浆-高导电不合格处理完成', '2025-04-18 17:00:00', '2025-04-18 17:00:00', 8, 0), +('UH-20250512-016', 16, 12, 'DIE-001', '模切刀模-平板', 1.5000, 1, 1, '品质部', '孙丽', '让步接收-降级使用', 750.0000, '模切刀模-平板不合格处理完成', '2025-04-19 17:00:00', '2025-04-19 17:00:00', 4, 0), +('UH-20250512-017', 17, 15, 'LABEL-ELEC', '电子标签', 4000.0000, 2, 1, '生产部', '周杰', '退货处理-供应商承担', 6000.0000, '电子标签不合格处理完成', '2025-04-20 17:00:00', '2025-04-20 17:00:00', 5, 0), +('UH-20250512-018', 18, 13, 'LABEL-AC', '空调面板标签', 4500.0000, 1, 1, '品质部', '吴芳', '让步接收-降级使用', 5850.0000, '空调面板标签不合格处理完成', '2025-04-21 17:00:00', '2025-04-21 17:00:00', 6, 0), +('UH-20250512-019', 19, 18, 'LABEL-WASH', '洗衣机面板标签', 1600.0000, 3, 1, '生产部', '张伟', '返工处理-重新加工', 1920.0000, '洗衣机面板标签不合格处理完成', '2025-04-22 17:00:00', '2025-04-22 17:00:00', 7, 0), +('UH-20250512-020', 20, 19, 'LABEL-PHONE', '手机后盖标签', 5000.0000, 2, 1, '品质部', '李娜', '退货处理-供应商承担', 3250.0000, '手机后盖标签不合格处理完成', '2025-04-23 17:00:00', '2025-04-23 17:00:00', 8, 0); -- ----------------------------------------------------------- diff --git a/database/seeds/demo_seed_data.sql b/database/seeds/demo_seed_data.sql new file mode 100644 index 00000000..37f20ed4 --- /dev/null +++ b/database/seeds/demo_seed_data.sql @@ -0,0 +1,18 @@ +-- Demo 用户 +INSERT INTO sys_user (user_id, username, password, real_name, role_id, status) +VALUES + (1001, 'demo_ceo', '$2a$10$xxxxdemoceopwxxxxxxx', 'Demo CEO', 1, 1), + (1002, 'demo_warehouse', '$2a$10$xxxxdemowarepwxxxxxx', '仓库主管', 2, 1); + +-- 部分演示客户 +INSERT INTO crm_customer (customer_id, customer_name, type, contact, phone) +VALUES + (1, '深圳示例客户', '企业', '张三', '13800008888'), + (2, '上海经销商', '经销', '李四', '13900009999'); + +-- 常用物料/仓库/设备等 +INSERT INTO inv_warehouse (warehouse_id, warehouse_name, location) +VALUES (10, '主仓库', '深圳龙岗'), (11, '成品仓', '深圳宝安'); + +INSERT INTO eqp_equipment (equipment_id, equipment_name, type, status) +VALUES (101, '12色印刷机', '印刷', '正常'); \ No newline at end of file diff --git a/database/seeds/seed.js b/database/seeds/seed.js new file mode 100644 index 00000000..60a3f605 --- /dev/null +++ b/database/seeds/seed.js @@ -0,0 +1,17 @@ +// 用 Node.js 填充种子数据 +import mysql from 'mysql2/promise'; +import fs from 'fs'; + +(async () => { + const conn = await mysql.createConnection({ + host: 'localhost', + user: 'root', + password: 'yourpassword', + database: 'vnerpdacahng' + }); + // 读取SQL文件内容 + const sql = fs.readFileSync('./database/seeds/demo_seed_data.sql', 'utf-8'); + await conn.query(sql); + await conn.end(); + console.log('🌱 Demo seed data inserted!'); +})(); \ No newline at end of file diff --git a/database/seeds/vnerp-seed-data.sql b/database/seeds/vnerp-seed-data.sql index bfc03623..7ec314bd 100644 --- a/database/seeds/vnerp-seed-data.sql +++ b/database/seeds/vnerp-seed-data.sql @@ -1,5 +1,5 @@ -- ########################################################### --- 印刷生产经营信息管理系统 Print MIS 丝网印刷ERP 全新业务种子数据 +-- VNERP 丝网印刷ERP 全新业务种子数据 -- 编码规则、小料拆分、FIFO、库位、工单、检验、财务 全闭环 -- 基于全局配置驱动生成,可直接执行 -- ########################################################### @@ -128,21 +128,20 @@ VALUES -- ======================== -- 12. 批次库存(FIFO正确) -- ======================== --- 注意:批次库存统一写入 inv_inventory(wh_inventory 为已废弃幽灵表,权威 schema 无建表定义) -INSERT INTO inv_inventory (id, material_id, warehouse_id, quantity, available_qty, batch_no, create_time) +INSERT INTO wh_inventory (id, qr_code, material_id, warehouse_id, location_id, quantity, batch_no, inbound_time, status, create_time) VALUES -(1, 2, 1, 10, 10, 'RM20260511001', NOW()), -(2, 2, 1, 10, 10, 'RM20260511001', NOW()), -(3, 2, 1, 10, 10, 'RM20260511001', NOW()), -(4, 2, 1, 10, 10, 'RM20260511001', NOW()), -(5, 2, 1, 10, 10, 'RM20260511001', NOW()), -(6, 2, 1, 10, 10, 'RM20260511001', NOW()), -(7, 2, 1, 10, 10, 'RM20260511001', NOW()), -(8, 2, 1, 10, 10, 'RM20260511001', NOW()), -(9, 2, 1, 10, 10, 'RM20260511001', NOW()), -(10, 2, 1, 10, 10, 'RM20260511001', NOW()), -(11, 3, 1, 50, 50, 'RM20260511002', NOW()), -(12, 5, 1, 20, 20, 'RM20260511003', NOW()); +(1, 'VNR202605110000011', 2, 1, 3, 10, 'RM20260511001', '2026-05-11 08:00:00', 1, NOW()), +(2, 'VNR202605110000012', 2, 1, 3, 10, 'RM20260511001', '2026-05-11 08:00:00', 1, NOW()), +(3, 'VNR202605110000013', 2, 1, 3, 10, 'RM20260511001', '2026-05-11 08:00:00', 1, NOW()), +(4, 'VNR202605110000014', 2, 1, 3, 10, 'RM20260511001', '2026-05-11 08:00:00', 1, NOW()), +(5, 'VNR202605110000015', 2, 1, 3, 10, 'RM20260511001', '2026-05-11 08:00:00', 1, NOW()), +(6, 'VNR202605110000016', 2, 1, 3, 10, 'RM20260511001', '2026-05-11 08:30:00', 1, NOW()), +(7, 'VNR202605110000017', 2, 1, 3, 10, 'RM20260511001', '2026-05-11 08:30:00', 1, NOW()), +(8, 'VNR202605110000018', 2, 1, 3, 10, 'RM20260511001', '2026-05-11 08:30:00', 1, NOW()), +(9, 'VNR202605110000019', 2, 1, 3, 10, 'RM20260511001', '2026-05-11 08:30:00', 1, NOW()), +(10, 'VNR202605110000020', 2, 1, 3, 10, 'RM20260511001', '2026-05-11 08:30:00', 1, NOW()), +(11, 'VNR202605110000021', 3, 1, 3, 50, 'RM20260511002', '2026-05-11 08:00:00', 1, NOW()), +(12, 'VNR202605110000031', 5, 1, 3, 20, 'RM20260511003', '2026-05-11 08:00:00', 1, NOW()); -- ======================== -- 13. 领料单(按BOM、按FIFO) @@ -157,9 +156,9 @@ VALUES (3, 1, 5, 10, 10, 'L', 'VNR202605110000031', 'RM20260511003', 3, 4, NOW()); -- 更新库存(领料出库) -UPDATE inv_inventory SET quantity = quantity - 100, available_qty = available_qty - 100 WHERE id IN (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); -UPDATE inv_inventory SET quantity = quantity - 50, available_qty = available_qty - 50 WHERE id = 11; -UPDATE inv_inventory SET quantity = quantity - 10, available_qty = available_qty - 10 WHERE id = 12; +UPDATE wh_inventory SET quantity = quantity - 100 WHERE id IN (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); +UPDATE wh_inventory SET quantity = quantity - 50 WHERE id = 11; +UPDATE wh_inventory SET quantity = quantity - 10 WHERE id = 12; -- ======================== -- 14. 工序报工(不跳工序) @@ -184,9 +183,9 @@ VALUES (1, 'IR202605110001', 'FQC', 1, 1, '2026-05-15 14:00:00', 99, 97, 2, 'qua INSERT INTO fpr_receipt (id, receipt_no, wo_id, warehouse_id, location_id, qty, unit, quality_status, status, receiver_id, receive_time, remark, create_time) VALUES (1, 'FPR202605110001', 1, 1, 5, 97, 'pcs', 'qualified', 4, 1, '2026-05-16 16:00:00', '成品入库', NOW()); --- 插入成品库存(统一写入 inv_inventory) -INSERT INTO inv_inventory (id, material_id, warehouse_id, quantity, available_qty, batch_no, create_time) -VALUES (13, 4, 1, 97, 97, 'FP20260516001', NOW()); +-- 插入成品库存 +INSERT INTO wh_inventory (id, qr_code, material_id, warehouse_id, location_id, quantity, batch_no, inbound_time, status, create_time) +VALUES (13, 'VNR202605160000001', 4, 1, 5, 97, 'FP20260516001', '2026-05-16 16:00:00', 1, NOW()); -- ======================== -- 17. 发货单 @@ -198,7 +197,7 @@ INSERT INTO ship_shipment_item (id, shipment_id, material_id, qty, unit, qr_code VALUES (1, 1, 4, 97, 'pcs', 'VNR202605160000001', 'FP20260516001', 4, NOW()); -- 更新库存(发货出库) -UPDATE inv_inventory SET quantity = 0, available_qty = 0 WHERE id = 13; +UPDATE wh_inventory SET quantity = 0 WHERE id = 13; -- ======================== -- 18. 财务应收 diff --git a/database/standard_card_migration.sql b/database/standard_card_migration.sql index 9264c7e8..841c758b 100644 --- a/database/standard_card_migration.sql +++ b/database/standard_card_migration.sql @@ -1,24 +1,49 @@ --- 标准卡管理模块数据库迁移脚本(DDD 子表部分) +-- 标准卡管理模块数据库迁移脚本 -- 创建日期: 2026-05-11 -- 说明: 丝网印刷ERP标准卡管理核心模块 --- --- ⚠️ 架构统一说明(P1-17 修复 / 双 schema 统一,2026-08-10): --- `prd_standard_card` 的权威 schema 已确定为「print 取向单表」 --- (见 database/update_standard_card.sql 与 database/vnerpdacahng_schema.sql, --- column: card_no / status TINYINT 1-5 / FK crm_customer),由整个前端驱动,是 live 表。 --- 本文件【不再创建】prd_standard_card,避免与 update_standard_card.sql 重复定义同一张表 --- (历史上两者同表名、不同字段,迁移顺序一旦翻转就会建出错误结构)。 --- 本文件仅保留以下 DDD 子表(CREATE TABLE IF NOT EXISTS,幂等、无害): --- prd_color_standard_item / prd_process_standard_item / prd_quality_standard_item / --- prd_standard_card_material / prd_standard_card_ink / prd_standard_card_tooling / --- prd_standard_card_attachment / prd_standard_card_version_log / --- prd_work_order_standard_card / inv_material_standard_card / qc_inspection_standard_card --- 注意:上述子表在当前 print 主链路中未被使用;状态流转端点已改为直接操作 print 主表 --- (见 src/infrastructure/repositories/PrintStandardCardRepository.ts)。 -- ============================================= --- (原 1. 标准卡主表 prd_standard_card 已统一到 update_standard_card.sql,此处不再创建) +-- 1. 标准卡主表 -- ============================================= +CREATE TABLE IF NOT EXISTS `prd_standard_card` ( + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键', + `code` VARCHAR(50) NOT NULL COMMENT '标准卡编号', + `version` VARCHAR(20) NOT NULL DEFAULT '1.0' COMMENT '版本号', + `name` VARCHAR(100) NOT NULL COMMENT '标准卡名称', + `type` VARCHAR(20) NOT NULL COMMENT '类型: color/process/quality/comprehensive', + `material_id` BIGINT UNSIGNED COMMENT '适用产品ID', + `material_name` VARCHAR(100) COMMENT '产品名称', + `customer_id` BIGINT UNSIGNED COMMENT '客户ID', + `customer_name` VARCHAR(100) COMMENT '客户名称', + `spec` VARCHAR(100) COMMENT '产品规格', + `status` VARCHAR(20) NOT NULL DEFAULT 'draft' COMMENT '状态: draft/auditing/approved/confirmed/obsolete', + `effective_date` DATE COMMENT '生效日期', + `expiry_date` DATE COMMENT '失效日期', + `parent_version_id` INT COMMENT '父版本ID', + `is_current` TINYINT DEFAULT 0 COMMENT '是否为当前生效版本', + `is_obsolete` TINYINT DEFAULT 0 COMMENT '是否作废', + `is_locked` TINYINT DEFAULT 0 COMMENT '是否锁定(客户确认后自动锁定)', + `change_description` TEXT COMMENT '版本变更说明', + `obsolete_reason` TEXT COMMENT '作废原因', + `obsolete_by` INT COMMENT '作废人', + `obsolete_at` DATETIME COMMENT '作废时间', + `quality_requirement` TEXT COMMENT '品质要求', + `material_requirement` TEXT COMMENT '物料要求(自动生成)', + `ink_requirement` TEXT COMMENT '油墨要求(自动生成)', + `tooling_requirement` TEXT COMMENT '工装要求(自动生成)', + `process_requirement` TEXT COMMENT '工艺要求(自动生成)', + `create_user` INT COMMENT '创建人', + `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `deleted` TINYINT DEFAULT 0 COMMENT '删除标记', + PRIMARY KEY (`id`), + UNIQUE KEY `uk_code_version` (`code`, `version`), + KEY `idx_material` (`material_id`), + KEY `idx_customer` (`customer_id`), + KEY `idx_status` (`status`), + KEY `idx_is_current` (`is_current`), + KEY `idx_type` (`type`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='标准卡主表'; -- ============================================= -- 2. 颜色标准卡明细表 diff --git a/database/system_config_migration.sql b/database/system_config_migration.sql index ed3df3f3..09de1dbe 100644 --- a/database/system_config_migration.sql +++ b/database/system_config_migration.sql @@ -1,5 +1,5 @@ -- ======================================================== --- 印刷生产经营信息管理系统 Print MIS 系统全局配置表升级脚本 +-- VNERP 系统全局配置表升级脚本 -- 功能:完善系统配置表结构,支持更丰富的配置功能 -- ======================================================== @@ -131,9 +131,9 @@ VALUES -- ======================================================== -- 十、基础系统配置(保留原配置) -- ======================================================== -('系统名称', 'sys.name', '印刷生产经营信息管理系统 Print MIS丝网印刷管理系统', 'string', '系统基础配置', '系统名称', '系统显示名称', 90, 1, 0, 1), +('系统名称', 'sys.name', 'VNERP丝网印刷管理系统', 'string', '系统基础配置', '系统名称', '系统显示名称', 90, 1, 0, 1), ('系统版本', 'sys.version', 'v2.0.0', 'string', '系统基础配置', '系统版本号', '系统版本号', 91, 1, 0, 1), -('版权信息', 'sys.copyright', '© 2024 印刷生产经营信息管理系统 Print MIS. All Rights Reserved.', 'string', '系统基础配置', '版权信息', '版权声明信息', 92, 1, 0, 1), +('版权信息', 'sys.copyright', '© 2024 VNERP. All Rights Reserved.', 'string', '系统基础配置', '版权信息', '版权声明信息', 92, 1, 0, 1), ('默认密码', 'sys.default.password', 'admin123', 'string', '系统基础配置', '用户默认密码', '新用户默认密码', 93, 1, 0, 1) ON DUPLICATE KEY UPDATE `display_name` = VALUES(`display_name`), diff --git a/database/update_standard_card.sql b/database/update_standard_card.sql index 12e56312..cfe35eeb 100644 --- a/database/update_standard_card.sql +++ b/database/update_standard_card.sql @@ -1,21 +1,13 @@ -- ======================================================== --- 标准卡表结构更新(print / input 页面专用 schema) +-- 标准卡表结构更新 -- 根据 print 页面和 input 页面需求重新设计 -- ======================================================== --- --- ⚠️ 安全警告(P1-17 修复): --- 1. 原脚本使用 `DROP TABLE IF EXISTS prd_standard_card` 会清空所有已有数据, --- 已改为 `CREATE TABLE IF NOT EXISTS`,避免误执行导致生产数据丢失。 --- 2. 本文件与 standard_card_migration.sql 都定义了 `prd_standard_card`, --- 但两者字段结构互不兼容(本文件为 print 取向的 78 字段单表设计; --- standard_card_migration.sql 为 code/is_current 的模块化设计,且被 --- MysqlStandardCardRepository 使用)。两张表名冲突属于架构层面的技术债, --- 需由团队决定唯一权威 schema 后再统一代码与 SQL,禁止在本脚本中 DROP。 --- 3. 如需在开发环境重置该表,请手动执行 TRUNCATE,而非 DROP 整表。 --- ======================================================== --- 创建标准卡表(若不存在则创建,绝不 DROP 已有数据) -CREATE TABLE IF NOT EXISTS `prd_standard_card` ( +-- 删除旧表(如果存在) +DROP TABLE IF EXISTS `prd_standard_card`; + +-- 创建新的标准卡表 +CREATE TABLE `prd_standard_card` ( `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '标准卡ID', -- ========== 基础信息 ========== @@ -146,8 +138,8 @@ CREATE TABLE IF NOT EXISTS `prd_standard_card` ( KEY `idx_create_time` (`create_time`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='标准卡表'; --- 插入测试数据(INSERT IGNORE:uk_card_no 唯一约束保证重复执行不会报错/重复) -INSERT IGNORE INTO `prd_standard_card` ( +-- 插入测试数据 +INSERT INTO `prd_standard_card` ( `card_no`, `customer_name`, `customer_code`, `product_name`, `version`, `date`, `document_code`, `finished_size`, `tolerance`, `material_name`, `material_type`, `layout_type`, `spacing`, `spacing_value`, `sheet_width`, `sheet_length`, `core_type`, `paper_direction`, `roll_width`, `paper_edge`, diff --git a/database/vnerpdacahng_schema.sql b/database/vnerpdacahng_schema.sql index 386b2af1..71931358 100644 --- a/database/vnerpdacahng_schema.sql +++ b/database/vnerpdacahng_schema.sql @@ -1,4172 +1,1345 @@ -- ======================================================== --- ERP 系统数据库设计(自动导出) +-- ERP 系统数据库设计 -- 数据库: vnerpdacahng -- 字符集: utf8mb4 -- 排序规则: utf8mb4_0900_ai_ci --- 连接信息: 通过 .env 环境变量管理,禁止在源码中硬编码 --- 导出时间: 2026-07-07T06:12:48.662Z --- 导出方式: SHOW CREATE TABLE(仅 DDL,不含数据) +-- 连接信息: 127.0.0.1, root, Snqig521223 -- ======================================================== +-- 创建数据库 CREATE DATABASE IF NOT EXISTS `vnerpdacahng` DEFAULT CHARACTER SET utf8mb4 DEFAULT COLLATE utf8mb4_0900_ai_ci; USE `vnerpdacahng`; --- 原油墨基础信息表 -CREATE TABLE `base_ink` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT, - `ink_code` varchar(50) NOT NULL COMMENT '油墨编号', - `ink_name` varchar(200) NOT NULL COMMENT '油墨名称', - `color_code` varchar(50) DEFAULT NULL COMMENT '色号', - `color_name` varchar(100) DEFAULT NULL COMMENT '颜色名称', - `ink_type` varchar(20) DEFAULT NULL COMMENT '油墨类型: solvent-溶剂型, uv-UV型, water-水性', - `supplier_id` bigint unsigned DEFAULT NULL COMMENT '供应商ID', - `supplier_name` varchar(200) DEFAULT NULL COMMENT '供应商名称', - `specification` varchar(200) DEFAULT NULL COMMENT '规格', - `unit` varchar(20) DEFAULT NULL COMMENT '单位', - `unit_price` decimal(18,4) DEFAULT NULL COMMENT '单价', - `stock_qty` decimal(18,4) DEFAULT '0.0000' COMMENT '库存数量', - `min_stock` decimal(18,4) DEFAULT '0.0000' COMMENT '最低库存', - `status` tinyint DEFAULT '1' COMMENT '状态: 0-停用, 1-启用', - `remark` text COMMENT '备注', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP, - `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - `create_by` bigint unsigned DEFAULT NULL, - `deleted` tinyint DEFAULT '0', - PRIMARY KEY (`id`), - UNIQUE KEY `uk_ink_code` (`ink_code`), - KEY `idx_ink_type` (`ink_type`), - KEY `idx_supplier` (`supplier_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='原油墨基础信息表'; +-- ======================================================== +-- 1. 系统管理模块 +-- ======================================================== --- 合同评审表 -CREATE TABLE `biz_contract_review` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT, - `review_no` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '评审编号', - `order_id` bigint unsigned DEFAULT NULL COMMENT '订单ID', - `order_no` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '订单编号', - `customer_id` bigint unsigned DEFAULT NULL COMMENT '客户ID', - `customer_name` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '客户名称', - `product_id` bigint unsigned DEFAULT NULL COMMENT '产品ID', - `product_code` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '产品编码', - `product_name` varchar(200) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '产品名称', - `quantity` decimal(12,2) DEFAULT '0.00' COMMENT '数量', - `amount` decimal(12,2) DEFAULT '0.00' COMMENT '金额', - `delivery_date` date DEFAULT NULL COMMENT '交货日期', - `sample_status` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT 'pending' COMMENT '样品状态', - `quality_requirement` text COLLATE utf8mb4_unicode_ci COMMENT '质量要求', - `production_capacity` text COLLATE utf8mb4_unicode_ci COMMENT '产能评估', - `material_availability` text COLLATE utf8mb4_unicode_ci COMMENT '物料可用性', - `engineering_feasibility` text COLLATE utf8mb4_unicode_ci COMMENT '工程可行性', - `biz_opinion` text COLLATE utf8mb4_unicode_ci COMMENT '商务意见', - `eng_opinion` text COLLATE utf8mb4_unicode_ci COMMENT '工程意见', - `quality_opinion` text COLLATE utf8mb4_unicode_ci COMMENT '质量意见', - `prod_opinion` text COLLATE utf8mb4_unicode_ci COMMENT '生产意见', - `purchase_opinion` text COLLATE utf8mb4_unicode_ci COMMENT '采购意见', - `review_date` date DEFAULT NULL COMMENT '评审日期', - `status` tinyint DEFAULT '0' COMMENT '状态', - `remark` text COLLATE utf8mb4_unicode_ci COMMENT '备注', - `deleted` tinyint DEFAULT '0', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP, - `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, +-- 用户表 +CREATE TABLE `sys_user` ( + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '用户ID', + `username` VARCHAR(50) NOT NULL COMMENT '用户名', + `password` VARCHAR(255) NOT NULL COMMENT '密码(加密存储)', + `real_name` VARCHAR(50) COMMENT '真实姓名', + `email` VARCHAR(100) COMMENT '邮箱', + `phone` VARCHAR(20) COMMENT '手机号', + `avatar` VARCHAR(255) COMMENT '头像URL', + `department_id` BIGINT UNSIGNED COMMENT '部门ID', + `position` VARCHAR(50) COMMENT '职位', + `status` TINYINT DEFAULT 1 COMMENT '状态: 0-禁用, 1-启用', + `last_login_time` DATETIME COMMENT '最后登录时间', + `last_login_ip` VARCHAR(50) COMMENT '最后登录IP', + `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `create_by` BIGINT UNSIGNED COMMENT '创建人ID', + `update_by` BIGINT UNSIGNED COMMENT '更新人ID', + `deleted` TINYINT DEFAULT 0 COMMENT '删除标记: 0-未删除, 1-已删除', PRIMARY KEY (`id`), - UNIQUE KEY `uk_review_no` (`review_no`), - KEY `idx_customer` (`customer_id`), + UNIQUE KEY `uk_username` (`username`), + KEY `idx_department` (`department_id`), KEY `idx_status` (`status`) -) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='合同评审表'; - --- BOM头表 -CREATE TABLE `bom_header` ( - `id` int NOT NULL AUTO_INCREMENT, - `bom_no` varchar(50) NOT NULL COMMENT 'BOM编号', - `product_id` int DEFAULT NULL COMMENT '产品ID', - `product_code` varchar(50) DEFAULT NULL COMMENT '产品编码', - `product_name` varchar(200) DEFAULT NULL COMMENT '产品名称', - `product_spec` varchar(200) DEFAULT NULL COMMENT '产品规格', - `version` varchar(20) DEFAULT '1.0' COMMENT '版本号', - `is_default` tinyint DEFAULT '1' COMMENT '是否默认BOM', - `status` int DEFAULT '10' COMMENT '状态: 10-草稿 20-已审核 30-已发布 90-已停用', - `unit` varchar(20) DEFAULT NULL COMMENT '单位', - `base_qty` decimal(12,2) DEFAULT '1.00' COMMENT '基本数量', - `total_material_count` int DEFAULT '0' COMMENT '物料总数', - `total_cost` decimal(12,2) DEFAULT '0.00' COMMENT '总成本', - `remark` varchar(500) DEFAULT NULL COMMENT '备注', - `deleted` tinyint DEFAULT '0' COMMENT '软删除', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP, - `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - PRIMARY KEY (`id`), - UNIQUE KEY `uk_bom_no` (`bom_no`) -) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='BOM头表'; - --- BOM行表 -CREATE TABLE `bom_line` ( - `id` int NOT NULL AUTO_INCREMENT, - `bom_id` int NOT NULL COMMENT 'BOM头ID', - `line_no` int NOT NULL COMMENT '行号', - `material_id` int DEFAULT NULL COMMENT '物料ID', - `material_code` varchar(50) DEFAULT NULL COMMENT '物料编码', - `material_name` varchar(200) DEFAULT NULL COMMENT '物料名称', - `material_spec` varchar(200) DEFAULT NULL COMMENT '物料规格', - `material_unit` varchar(20) DEFAULT NULL COMMENT '物料单位', - `usage_qty` decimal(12,4) NOT NULL DEFAULT '0.0000' COMMENT '用量', - `loss_rate` decimal(5,2) DEFAULT '0.00' COMMENT '损耗率', - `unit_cost` decimal(12,2) DEFAULT '0.00' COMMENT '单价', - `total_cost` decimal(12,2) DEFAULT '0.00' COMMENT '总成本', - `remark` varchar(500) DEFAULT NULL COMMENT '备注', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP, - `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - PRIMARY KEY (`id`), - KEY `idx_bom_id` (`bom_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='BOM行表'; - --- 客户表 -CREATE TABLE `crm_customer` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT, - `customer_code` varchar(50) NOT NULL COMMENT '客户编码', - `customer_name` varchar(100) NOT NULL COMMENT '客户名称', - `short_name` varchar(50) DEFAULT NULL COMMENT '客户简称', - `customer_type` tinyint DEFAULT NULL COMMENT '客户类型: 1-企业, 2-个人', - `industry` varchar(50) DEFAULT NULL COMMENT '所属行业', - `scale` varchar(50) DEFAULT NULL COMMENT '企业规模', - `credit_level` varchar(20) DEFAULT NULL COMMENT '信用等级', - `province` varchar(50) DEFAULT NULL COMMENT '省份', - `city` varchar(50) DEFAULT NULL COMMENT '城市', - `district` varchar(50) DEFAULT NULL COMMENT '区县', - `address` varchar(255) DEFAULT NULL COMMENT '详细地址', - `contact_name` varchar(50) DEFAULT NULL COMMENT '联系人姓名', - `contact_phone` varchar(20) DEFAULT NULL COMMENT '联系人电话', - `contact_email` varchar(100) DEFAULT NULL COMMENT '联系人邮箱', - `fax` varchar(20) DEFAULT NULL COMMENT '传真', - `website` varchar(100) DEFAULT NULL COMMENT '网站', - `business_license` varchar(50) DEFAULT NULL COMMENT '营业执照号', - `tax_number` varchar(50) DEFAULT NULL COMMENT '税号', - `bank_name` varchar(100) DEFAULT NULL COMMENT '开户银行', - `bank_account` varchar(50) DEFAULT NULL COMMENT '银行账号', - `salesman_id` bigint unsigned DEFAULT NULL COMMENT '业务员ID', - `follow_up_status` tinyint DEFAULT '1' COMMENT '跟进状态: 1-潜在客户, 2-意向客户, 3-成交客户, 4-流失客户', - `status` tinyint DEFAULT '1' COMMENT '状态: 0-禁用, 1-启用', - `remark` text COMMENT '备注', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP, - `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - `create_by` bigint unsigned DEFAULT NULL, - `update_by` bigint unsigned DEFAULT NULL, - `deleted` tinyint NOT NULL DEFAULT '0' COMMENT '软删除: 0-正常, 1-已删除', - PRIMARY KEY (`id`), - UNIQUE KEY `uk_customer_code` (`customer_code`), - KEY `idx_customer_code` (`customer_code`), - KEY `idx_customer_status` (`status`,`deleted`), - KEY `fk_crm_customer_salesman` (`salesman_id`), - CONSTRAINT `fk_crm_customer_salesman` FOREIGN KEY (`salesman_id`) REFERENCES `sys_user` (`id`) ON DELETE SET NULL ON UPDATE CASCADE -) ENGINE=InnoDB AUTO_INCREMENT=57 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='客户表'; - --- 客户分析表 -CREATE TABLE `crm_customer_analysis` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT, - `customer_id` bigint unsigned NOT NULL COMMENT '客户ID', - `customer_name` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '客户名称', - `analysis_period` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT 'month' COMMENT '分析周期', - `period_start` date DEFAULT NULL COMMENT '周期开始', - `period_end` date DEFAULT NULL COMMENT '周期结束', - `order_count` int DEFAULT '0' COMMENT '订单数', - `order_amount` decimal(12,2) DEFAULT '0.00' COMMENT '订单金额', - `delivery_count` int DEFAULT '0' COMMENT '交付数', - `return_count` int DEFAULT '0' COMMENT '退货数', - `complaint_count` int DEFAULT '0' COMMENT '投诉数', - `on_time_rate` decimal(5,2) DEFAULT NULL COMMENT '准时率', - `satisfaction_score` decimal(3,1) DEFAULT NULL COMMENT '满意度', - `customer_level` varchar(5) COLLATE utf8mb4_unicode_ci DEFAULT 'C' COMMENT '客户等级', - `growth_rate` decimal(5,2) DEFAULT NULL COMMENT '增长率', - `remark` text COLLATE utf8mb4_unicode_ci COMMENT '备注', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP, - `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - PRIMARY KEY (`id`), - KEY `idx_customer` (`customer_id`), - KEY `idx_period` (`analysis_period`), - KEY `idx_level` (`customer_level`) -) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='客户分析表'; - --- 客户联系人表 -CREATE TABLE `crm_customer_contact` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT, - `customer_id` bigint unsigned NOT NULL COMMENT '客户ID', - `contact_name` varchar(50) NOT NULL COMMENT '联系人姓名', - `position` varchar(50) DEFAULT NULL COMMENT '职位', - `phone` varchar(20) DEFAULT NULL COMMENT '电话', - `email` varchar(100) DEFAULT NULL COMMENT '邮箱', - `is_primary` tinyint DEFAULT '0' COMMENT '是否主联系人: 0-否, 1-是', - `remark` text COMMENT '备注', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP, - `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - `deleted` tinyint NOT NULL DEFAULT '0' COMMENT '软删除: 0-正常, 1-已删除', - `create_by` bigint unsigned DEFAULT NULL COMMENT '创建人ID', - `update_by` bigint unsigned DEFAULT NULL COMMENT '更新人ID', - PRIMARY KEY (`id`), - KEY `idx_customer` (`customer_id`), - CONSTRAINT `fk_customer_contact_customer` FOREIGN KEY (`customer_id`) REFERENCES `crm_customer` (`id`) ON DELETE CASCADE ON UPDATE CASCADE -) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='客户联系人表'; - --- 客户跟进记录表 -CREATE TABLE `crm_customer_follow_up` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '跟进记录ID', - `customer_id` bigint unsigned NOT NULL COMMENT '客户ID', - `follow_up_type` tinyint DEFAULT NULL COMMENT '跟进方式: 1-电话, 2-邮件, 3-拜访, 4-微信, 5-其他', - `follow_up_content` text COMMENT '跟进内容', - `follow_up_time` datetime DEFAULT NULL COMMENT '跟进时间', - `next_follow_up_time` datetime DEFAULT NULL COMMENT '下次跟进时间', - `follow_up_by` bigint unsigned DEFAULT NULL COMMENT '跟进人ID', - `attachment` varchar(255) DEFAULT NULL COMMENT '附件', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - `deleted` tinyint NOT NULL DEFAULT '0' COMMENT '软删除标记', - PRIMARY KEY (`id`), - KEY `idx_customer` (`customer_id`), - KEY `idx_follow_up_time` (`follow_up_time`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='客户跟进记录表'; - --- 客户跟进记录表 -CREATE TABLE `crm_follow_record` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT, - `customer_id` bigint unsigned NOT NULL COMMENT '客户ID', - `customer_name` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '客户名称', - `follow_type` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT 'phone' COMMENT '跟进方式', - `follow_content` text COLLATE utf8mb4_unicode_ci COMMENT '跟进内容', - `contact_name` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '联系人', - `salesman_name` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '业务员', - `next_follow_date` date DEFAULT NULL COMMENT '下次跟进日期', - `opportunity` varchar(200) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '商机', - `status` tinyint DEFAULT '1' COMMENT '状态', - `remark` text COLLATE utf8mb4_unicode_ci COMMENT '备注', - `deleted` tinyint DEFAULT '0', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP, - `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - PRIMARY KEY (`id`), - KEY `idx_customer` (`customer_id`), - KEY `idx_follow_type` (`follow_type`), - KEY `idx_create_time` (`create_time`) -) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='客户跟进记录表'; - --- 领域事件持久化表(Outbox 模式) -CREATE TABLE `domain_event_outbox` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '事件主键ID', - `event_type` varchar(100) NOT NULL COMMENT '事件类型(如 InboundOrderCreated/SalesOrderApproved)', - `aggregate_type` varchar(50) DEFAULT NULL COMMENT '聚合根类型(如 InboundOrder/SalesOrder)', - `aggregate_id` bigint unsigned DEFAULT NULL COMMENT '聚合根ID', - `payload` json NOT NULL COMMENT '事件完整内容(JSON 序列化的 DomainEvent 对象)', - `status` varchar(20) NOT NULL DEFAULT 'pending' COMMENT '状态: pending-待处理, processed-已处理, failed-失败', - `retry_count` int NOT NULL DEFAULT '0' COMMENT '已重试次数(最大3次,超过标记死信)', - `error_message` text COMMENT '最近一次失败的错误信息(截断500字符)', - `next_execute_at` datetime DEFAULT NULL COMMENT '下次执行时间(指数退避: 1s/3s/9s;NULL 表示立即可执行)', - `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '事件创建时间', - `processed_at` datetime DEFAULT NULL COMMENT '处理完成时间(status=processed 时写入)', - `claimed_at` datetime DEFAULT NULL COMMENT '最近一次被 claim 的时间(status=dispatching 时写入)', - `dispatched_at` datetime DEFAULT NULL COMMENT '成功分发到 Stream 的时间(status=processed 前置)', - PRIMARY KEY (`id`), - KEY `idx_status_next_execute` (`status`,`next_execute_at`) COMMENT '指数退避消费索引', - KEY `idx_aggregate` (`aggregate_type`,`aggregate_id`) COMMENT '聚合根溯源索引', - KEY `idx_status_created` (`status`,`create_time`) COMMENT '待处理事件查询索引' -) ENGINE=InnoDB AUTO_INCREMENT=9002104 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='领域事件持久化表(Outbox 模式)'; - --- 设备校准表 -CREATE TABLE `eqp_calibration` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT, - `calibration_no` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '校准单号', - `equipment_id` bigint unsigned DEFAULT NULL COMMENT '设备ID', - `equipment_code` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '设备编码', - `equipment_name` varchar(200) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '设备名称', - `calibration_date` date DEFAULT NULL COMMENT '校准日期', - `next_calibration_date` date DEFAULT NULL COMMENT '下次校准日期', - `calibration_org` varchar(200) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '校准机构', - `calibration_result` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT 'qualified' COMMENT '校准结果', - `certificate_no` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '证书编号', - `calibration_cost` decimal(12,2) DEFAULT '0.00' COMMENT '校准费用', - `remark` text COLLATE utf8mb4_unicode_ci COMMENT '备注', - `deleted` tinyint DEFAULT '0', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP, - `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - PRIMARY KEY (`id`), - UNIQUE KEY `uk_calibration_no` (`calibration_no`) -) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='设备校准表'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='系统用户表'; --- 设备台账表 -CREATE TABLE `eqp_equipment` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT, - `equipment_code` varchar(50) NOT NULL COMMENT '设备编码', - `equipment_name` varchar(100) NOT NULL COMMENT '设备名称', - `equipment_type` tinyint DEFAULT NULL COMMENT '设备类型: 1-印刷机, 2-覆膜机, 3-模切机, 4-全检机, 5-其他', - `brand` varchar(50) DEFAULT NULL COMMENT '品牌', - `model` varchar(50) DEFAULT NULL COMMENT '型号', - `serial_no` varchar(50) DEFAULT NULL COMMENT '序列号', - `workshop_id` bigint unsigned DEFAULT NULL COMMENT '车间ID', - `location` varchar(100) DEFAULT NULL COMMENT '安装位置', - `purchase_date` date DEFAULT NULL COMMENT '购入日期', - `manufacturer` varchar(100) DEFAULT NULL COMMENT '制造商', - `supplier_id` bigint unsigned DEFAULT NULL COMMENT '供应商ID', - `warranty_expire` date DEFAULT NULL COMMENT '质保到期日', - `rated_capacity` decimal(18,4) DEFAULT NULL COMMENT '额定产能', - `current_status` tinyint DEFAULT '1' COMMENT '当前状态: 1-运行, 2-待机, 3-维修, 4-停机', - `oee` decimal(5,2) DEFAULT '0.00' COMMENT 'OEE综合效率(%)', - `availability` decimal(5,2) DEFAULT '0.00' COMMENT '可用率(%)', - `performance` decimal(5,2) DEFAULT '0.00' COMMENT '性能率(%)', - `quality_rate` decimal(5,2) DEFAULT '0.00' COMMENT '质量率(%)', - `total_run_hours` decimal(10,2) DEFAULT '0.00' COMMENT '累计运行时长', - `last_maintenance_date` date DEFAULT NULL COMMENT '上次维护日期', - `next_maintenance_date` date DEFAULT NULL COMMENT '下次维护日期', - `status` tinyint DEFAULT '1' COMMENT '状态: 0-停用, 1-启用', - `remark` text COMMENT '备注', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP, - `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - `create_by` bigint unsigned DEFAULT NULL, - `deleted` tinyint NOT NULL DEFAULT '0' COMMENT '软删除: 0-正常, 1-已删除', +-- 部门表 +CREATE TABLE `sys_department` ( + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '部门ID', + `parent_id` BIGINT UNSIGNED DEFAULT 0 COMMENT '父部门ID, 0为顶级部门', + `dept_name` VARCHAR(100) NOT NULL COMMENT '部门名称', + `dept_code` VARCHAR(50) COMMENT '部门编码', + `sort_order` INT DEFAULT 0 COMMENT '排序序号', + `leader_id` BIGINT UNSIGNED COMMENT '部门负责人ID', + `phone` VARCHAR(20) COMMENT '联系电话', + `email` VARCHAR(100) COMMENT '邮箱', + `status` TINYINT DEFAULT 1 COMMENT '状态: 0-禁用, 1-启用', + `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `deleted` TINYINT DEFAULT 0 COMMENT '删除标记', PRIMARY KEY (`id`), - UNIQUE KEY `uk_equipment_code` (`equipment_code`), - KEY `idx_type` (`equipment_type`), + KEY `idx_parent` (`parent_id`), KEY `idx_status` (`status`) -) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='设备台账表'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='部门表'; --- 设备维护计划表 -CREATE TABLE `eqp_maintenance_plan` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT, - `plan_no` varchar(50) NOT NULL COMMENT '计划编号', - `equipment_id` bigint unsigned NOT NULL COMMENT '设备ID', - `maintenance_type` tinyint DEFAULT NULL COMMENT '维护类型: 1-日常保养, 2-定期保养, 3-大修', - `cycle_type` tinyint DEFAULT NULL COMMENT '周期类型: 1-按天, 2-按周, 3-按月, 4-按运行时长', - `cycle_value` int DEFAULT NULL COMMENT '周期值', - `plan_date` date DEFAULT NULL COMMENT '计划日期', - `responsible_id` bigint unsigned DEFAULT NULL COMMENT '负责人ID', - `content` text COMMENT '维护内容', - `status` tinyint DEFAULT '1' COMMENT '状态: 1-待执行, 2-执行中, 3-已完成, 4-已逾期', - `complete_date` date DEFAULT NULL COMMENT '完成日期', - `remark` text COMMENT '备注', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP, - `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - `deleted` tinyint DEFAULT '0', +-- 角色表 +CREATE TABLE `sys_role` ( + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '角色ID', + `role_name` VARCHAR(50) NOT NULL COMMENT '角色名称', + `role_code` VARCHAR(50) NOT NULL COMMENT '角色编码', + `description` VARCHAR(255) COMMENT '角色描述', + `data_scope` TINYINT DEFAULT 1 COMMENT '数据范围: 1-全部, 2-本部门, 3-本部门及下级, 4-仅本人', + `status` TINYINT DEFAULT 1 COMMENT '状态: 0-禁用, 1-启用', + `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `deleted` TINYINT DEFAULT 0 COMMENT '删除标记', PRIMARY KEY (`id`), - UNIQUE KEY `uk_plan_no` (`plan_no`), - KEY `idx_equipment` (`equipment_id`), + UNIQUE KEY `uk_role_code` (`role_code`), KEY `idx_status` (`status`) -) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='设备维护计划表'; - --- 设备维护记录表 -CREATE TABLE `eqp_maintenance_record` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT, - `record_no` varchar(50) NOT NULL COMMENT '记录编号', - `plan_id` bigint unsigned DEFAULT NULL COMMENT '维护计划ID', - `equipment_id` bigint unsigned NOT NULL COMMENT '设备ID', - `maintenance_type` tinyint DEFAULT NULL COMMENT '维护类型: 1-日常保养, 2-定期保养, 3-大修, 4-故障维修', - `fault_desc` text COMMENT '故障描述', - `maintenance_content` text COMMENT '维护内容', - `start_time` datetime DEFAULT NULL COMMENT '开始时间', - `end_time` datetime DEFAULT NULL COMMENT '结束时间', - `downtime_hours` decimal(10,2) DEFAULT '0.00' COMMENT '停机时长', - `cost` decimal(18,4) DEFAULT '0.0000' COMMENT '维护费用', - `responsible_id` bigint unsigned DEFAULT NULL COMMENT '负责人ID', - `result` tinyint DEFAULT NULL COMMENT '结果: 1-正常, 2-需跟踪', - `remark` text COMMENT '备注', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP, - `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - `deleted` tinyint DEFAULT '0', - PRIMARY KEY (`id`), - UNIQUE KEY `uk_record_no` (`record_no`), - KEY `idx_equipment` (`equipment_id`), - KEY `idx_plan` (`plan_id`) -) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='设备维护记录表'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='角色表'; --- 设备维修表 -CREATE TABLE `eqp_repair` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT, - `repair_no` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '维修单号', - `equipment_id` bigint unsigned DEFAULT NULL COMMENT '设备ID', - `equipment_code` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '设备编码', - `equipment_name` varchar(200) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '设备名称', - `fault_date` date DEFAULT NULL COMMENT '故障日期', - `fault_desc` text COLLATE utf8mb4_unicode_ci COMMENT '故障描述', - `repair_type` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT 'corrective' COMMENT '维修类型', - `repair_person` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '维修人', - `remark` text COLLATE utf8mb4_unicode_ci COMMENT '备注', - `deleted` tinyint DEFAULT '0', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP, - `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - PRIMARY KEY (`id`), - UNIQUE KEY `uk_repair_no` (`repair_no`) -) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='设备维修表'; - --- 设备报废表 -CREATE TABLE `eqp_scrap` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT, - `scrap_no` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '报废单号', - `equipment_id` bigint unsigned DEFAULT NULL COMMENT '设备ID', - `equipment_code` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '设备编码', - `equipment_name` varchar(200) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '设备名称', - `scrap_date` date DEFAULT NULL COMMENT '报废日期', - `scrap_reason` text COLLATE utf8mb4_unicode_ci COMMENT '报废原因', - `original_value` decimal(12,2) DEFAULT '0.00' COMMENT '原值', - `net_value` decimal(12,2) DEFAULT '0.00' COMMENT '净值', - `approval_person` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '审批人', - `remark` text COLLATE utf8mb4_unicode_ci COMMENT '备注', - `deleted` tinyint DEFAULT '0', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP, - `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, +-- 用户角色关联表 +CREATE TABLE `sys_user_role` ( + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'ID', + `user_id` BIGINT UNSIGNED NOT NULL COMMENT '用户ID', + `role_id` BIGINT UNSIGNED NOT NULL COMMENT '角色ID', + `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', PRIMARY KEY (`id`), - UNIQUE KEY `uk_scrap_no` (`scrap_no`) -) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='设备报废表'; + UNIQUE KEY `uk_user_role` (`user_id`, `role_id`), + KEY `idx_role` (`role_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='用户角色关联表'; --- 会计科目表 -CREATE TABLE `fin_account` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '科目ID', - `account_code` varchar(20) NOT NULL COMMENT '科目编码', - `account_name` varchar(100) NOT NULL COMMENT '科目名称', - `full_name` varchar(200) DEFAULT NULL COMMENT '全称(含父级)', - `parent_id` bigint unsigned DEFAULT NULL COMMENT '父级科目ID', - `level` tinyint DEFAULT '1' COMMENT '层级(1=一级)', - `account_type` tinyint NOT NULL COMMENT '类型: 1=资产, 2=负债, 3=权益, 4=成本, 5=损益', - `balance_direction` tinyint NOT NULL COMMENT '余额方向: 1=借, 2=贷', - `is_leaf` tinyint DEFAULT '1' COMMENT '是否末级: 0=否, 1=是', - `assist_types` varchar(200) DEFAULT NULL COMMENT '辅助核算类型(JSON 数组)', - `status` tinyint DEFAULT '1' COMMENT '状态: 0=禁用, 1=启用', - `sort_order` int DEFAULT '0' COMMENT '排序', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', - `create_by` bigint unsigned DEFAULT NULL COMMENT '创建人ID', - `update_by` bigint unsigned DEFAULT NULL COMMENT '更新人ID', - `deleted` tinyint NOT NULL DEFAULT '0' COMMENT '软删除: 0-正常, 1-已删除', +-- 菜单/权限表 +CREATE TABLE `sys_menu` ( + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '菜单ID', + `parent_id` BIGINT UNSIGNED DEFAULT 0 COMMENT '父菜单ID', + `menu_name` VARCHAR(50) NOT NULL COMMENT '菜单名称', + `menu_type` TINYINT NOT NULL COMMENT '菜单类型: 1-目录, 2-菜单, 3-按钮', + `icon` VARCHAR(50) COMMENT '菜单图标', + `path` VARCHAR(200) COMMENT '路由路径', + `component` VARCHAR(255) COMMENT '组件路径', + `permission` VARCHAR(100) COMMENT '权限标识', + `sort_order` INT DEFAULT 0 COMMENT '排序序号', + `status` TINYINT DEFAULT 1 COMMENT '状态: 0-禁用, 1-启用', + `visible` TINYINT DEFAULT 1 COMMENT '是否可见: 0-隐藏, 1-显示', + `keep_alive` TINYINT DEFAULT 0 COMMENT '是否缓存: 0-否, 1-是', + `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', PRIMARY KEY (`id`), - UNIQUE KEY `uk_account_code` (`account_code`), KEY `idx_parent` (`parent_id`), - KEY `idx_type` (`account_type`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='会计科目表'; - --- 科目余额表 -CREATE TABLE `fin_account_balance` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '余额ID', - `period_code` varchar(10) NOT NULL COMMENT '期间编码', - `account_id` bigint unsigned NOT NULL COMMENT '科目ID', - `account_code` varchar(20) DEFAULT NULL COMMENT '科目编码(冗余)', - `begin_debit` decimal(18,4) DEFAULT '0.0000' COMMENT '期初借方', - `begin_credit` decimal(18,4) DEFAULT '0.0000' COMMENT '期初贷方', - `current_debit` decimal(18,4) DEFAULT '0.0000' COMMENT '本期借方发生', - `current_credit` decimal(18,4) DEFAULT '0.0000' COMMENT '本期贷方发生', - `year_debit` decimal(18,4) DEFAULT '0.0000' COMMENT '本年累计借方', - `year_credit` decimal(18,4) DEFAULT '0.0000' COMMENT '本年累计贷方', - `end_debit` decimal(18,4) DEFAULT '0.0000' COMMENT '期末借方', - `end_credit` decimal(18,4) DEFAULT '0.0000' COMMENT '期末贷方', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', - `create_by` bigint unsigned DEFAULT NULL COMMENT '创建人ID', - `update_by` bigint unsigned DEFAULT NULL COMMENT '更新人ID', - `deleted` tinyint NOT NULL DEFAULT '0' COMMENT '软删除: 0-正常, 1-已删除', - PRIMARY KEY (`id`), - UNIQUE KEY `uk_period_account` (`period_code`,`account_id`), - KEY `idx_account` (`account_id`), - CONSTRAINT `fk_fin_account_balance_account` FOREIGN KEY (`account_id`) REFERENCES `fin_account` (`id`) ON DELETE RESTRICT ON UPDATE CASCADE, - CONSTRAINT `fk_fin_account_balance_period` FOREIGN KEY (`period_code`) REFERENCES `fin_period` (`period_code`) ON DELETE RESTRICT ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='科目余额表'; - --- 财务成本记录表 -CREATE TABLE `fin_cost_record` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT, - `cost_no` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '成本编号', - `cost_type` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '成本类型', - `cost_category` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '成本分类', - `cost_date` date DEFAULT NULL COMMENT '成本日期', - `amount` decimal(12,2) DEFAULT '0.00' COMMENT '金额', - `order_no` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '关联订单', - `product_name` varchar(200) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '产品名称', - `department` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '部门', - `description` text COLLATE utf8mb4_unicode_ci COMMENT '描述', - `status` tinyint DEFAULT '0' COMMENT '状态', - `remark` text COLLATE utf8mb4_unicode_ci COMMENT '备注', - `deleted` tinyint DEFAULT '0', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP, - `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - PRIMARY KEY (`id`), - UNIQUE KEY `uk_cost_no` (`cost_no`), - KEY `idx_cost_type` (`cost_type`), - KEY `idx_cost_date` (`cost_date`) -) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='财务成本记录表'; - --- 应付账款表 -CREATE TABLE `fin_payable` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT, - `payable_no` varchar(50) NOT NULL COMMENT '应付单号', - `source_type` tinyint DEFAULT '1' COMMENT '来源类型: 1-采购订单', - `source_no` varchar(50) DEFAULT NULL COMMENT '来源单号', - `supplier_id` bigint unsigned DEFAULT NULL COMMENT '供应商ID', - `amount` decimal(18,4) DEFAULT '0.0000' COMMENT '应付金额', - `paid_amount` decimal(18,4) DEFAULT '0.0000' COMMENT '已付金额', - `balance` decimal(18,4) DEFAULT '0.0000' COMMENT '未付余额', - `due_date` date DEFAULT NULL COMMENT '到期日期', - `status` tinyint DEFAULT '1' COMMENT '状态: 1-未付款, 2-部分付款, 3-已付款', - `remark` text COMMENT '备注', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP, - `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - `deleted` tinyint DEFAULT '0', - `create_by` bigint unsigned DEFAULT NULL COMMENT '创建人ID', - `update_by` bigint unsigned DEFAULT NULL COMMENT '更新人ID', - PRIMARY KEY (`id`), - UNIQUE KEY `uk_payable_no` (`payable_no`), - KEY `idx_supplier` (`supplier_id`), - KEY `idx_source` (`source_no`), - KEY `idx_status` (`status`), - CONSTRAINT `fk_fin_payable_supplier` FOREIGN KEY (`supplier_id`) REFERENCES `pur_supplier` (`id`) ON DELETE RESTRICT ON UPDATE CASCADE -) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='应付账款表'; - --- 付款记录表 -CREATE TABLE `fin_payment_record` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT, - `payment_no` varchar(50) NOT NULL COMMENT '付款单号', - `payable_id` bigint unsigned DEFAULT NULL COMMENT '应付ID', - `supplier_id` bigint unsigned DEFAULT NULL COMMENT '供应商ID', - `amount` decimal(18,4) DEFAULT '0.0000' COMMENT '付款金额', - `payment_method` varchar(20) DEFAULT NULL COMMENT '付款方式', - `payment_date` date DEFAULT NULL COMMENT '付款日期', - `remark` text COMMENT '备注', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP, - `deleted` tinyint DEFAULT '0', - PRIMARY KEY (`id`), - KEY `idx_payable` (`payable_id`), - KEY `idx_supplier` (`supplier_id`) -) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='付款记录表'; - --- 会计期间表 -CREATE TABLE `fin_period` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '期间ID', - `period_code` varchar(10) NOT NULL COMMENT '期间编码(如 2026-07)', - `period_name` varchar(20) DEFAULT NULL COMMENT '期间名称(如 2026年7月)', - `start_date` date NOT NULL COMMENT '开始日期', - `end_date` date NOT NULL COMMENT '结束日期', - `is_closed` tinyint DEFAULT '0' COMMENT '是否已结账: 0=否, 1=是', - `status` tinyint DEFAULT '0' COMMENT '状态: 0=开放, 1=结账中, 2=已关闭', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', - `create_by` bigint unsigned DEFAULT NULL COMMENT '创建人ID', - `update_by` bigint unsigned DEFAULT NULL COMMENT '更新人ID', - `deleted` tinyint NOT NULL DEFAULT '0' COMMENT '软删除: 0-正常, 1-已删除', - PRIMARY KEY (`id`), - UNIQUE KEY `uk_period_code` (`period_code`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='会计期间表'; + KEY `idx_type` (`menu_type`), + KEY `idx_status` (`status`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='菜单权限表'; --- 收款记录表 -CREATE TABLE `fin_receipt_record` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT, - `receipt_no` varchar(50) NOT NULL COMMENT '收款单号', - `receivable_id` bigint unsigned DEFAULT NULL COMMENT '应收ID', - `customer_id` bigint unsigned DEFAULT NULL COMMENT '客户ID', - `amount` decimal(18,4) DEFAULT '0.0000' COMMENT '收款金额', - `payment_method` varchar(20) DEFAULT NULL COMMENT '付款方式', - `receipt_date` date DEFAULT NULL COMMENT '收款日期', - `remark` text COMMENT '备注', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP, - `deleted` tinyint DEFAULT '0', +-- 角色菜单关联表 +CREATE TABLE `sys_role_menu` ( + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'ID', + `role_id` BIGINT UNSIGNED NOT NULL COMMENT '角色ID', + `menu_id` BIGINT UNSIGNED NOT NULL COMMENT '菜单ID', + `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', PRIMARY KEY (`id`), - KEY `idx_receivable` (`receivable_id`), - KEY `idx_customer` (`customer_id`) -) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='收款记录表'; + UNIQUE KEY `uk_role_menu` (`role_id`, `menu_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='角色菜单关联表'; --- 应收账款表 -CREATE TABLE `fin_receivable` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT, - `receivable_no` varchar(50) NOT NULL COMMENT '应收单号', - `source_type` tinyint DEFAULT '1' COMMENT '来源类型: 1-销售订单', - `source_no` varchar(50) DEFAULT NULL COMMENT '来源单号', - `customer_id` bigint unsigned DEFAULT NULL COMMENT '客户ID', - `amount` decimal(18,4) DEFAULT '0.0000' COMMENT '应收金额', - `received_amount` decimal(18,4) DEFAULT '0.0000' COMMENT '已收金额', - `balance` decimal(18,4) DEFAULT '0.0000' COMMENT '未收余额', - `due_date` date DEFAULT NULL COMMENT '到期日期', - `status` tinyint DEFAULT '1' COMMENT '状态: 1-未收款, 2-部分收款, 3-已收款', - `remark` text COMMENT '备注', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP, - `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - `deleted` tinyint DEFAULT '0', - `create_by` bigint unsigned DEFAULT NULL COMMENT '创建人ID', - `update_by` bigint unsigned DEFAULT NULL COMMENT '更新人ID', +-- 操作日志表 +CREATE TABLE `sys_operation_log` ( + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '日志ID', + `user_id` BIGINT UNSIGNED COMMENT '用户ID', + `username` VARCHAR(50) COMMENT '用户名', + `operation` VARCHAR(100) COMMENT '操作描述', + `method` VARCHAR(10) COMMENT '请求方法', + `request_url` VARCHAR(500) COMMENT '请求URL', + `request_params` TEXT COMMENT '请求参数', + `response_data` TEXT COMMENT '响应数据', + `ip` VARCHAR(50) COMMENT 'IP地址', + `user_agent` VARCHAR(500) COMMENT '浏览器UA', + `execute_time` INT COMMENT '执行时长(ms)', + `status` TINYINT COMMENT '操作状态: 0-失败, 1-成功', + `error_msg` TEXT COMMENT '错误信息', + `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', PRIMARY KEY (`id`), - UNIQUE KEY `uk_receivable_no` (`receivable_no`), - KEY `idx_customer` (`customer_id`), - KEY `idx_source` (`source_no`), - KEY `idx_status` (`status`), - CONSTRAINT `fk_fin_receivable_customer` FOREIGN KEY (`customer_id`) REFERENCES `crm_customer` (`id`) ON DELETE RESTRICT ON UPDATE CASCADE -) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='应收账款表'; + KEY `idx_user` (`user_id`), + KEY `idx_create_time` (`create_time`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='操作日志表'; --- 会计凭证主表 -CREATE TABLE `fin_voucher` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '凭证ID', - `voucher_no` varchar(50) NOT NULL COMMENT '凭证编号', - `period_code` varchar(10) NOT NULL COMMENT '所属期间', - `voucher_date` date NOT NULL COMMENT '凭证日期', - `voucher_type` tinyint NOT NULL COMMENT '类型: 1=收, 2=付, 3=转, 4=调整', - `source_type` varchar(50) DEFAULT NULL COMMENT '来源类型(sale/purchase/payment/receipt/cost 等)', - `source_id` bigint unsigned DEFAULT NULL COMMENT '来源单据ID', - `source_no` varchar(50) DEFAULT NULL COMMENT '来源单据号', - `total_debit` decimal(18,4) DEFAULT '0.0000' COMMENT '借方合计', - `total_credit` decimal(18,4) DEFAULT '0.0000' COMMENT '贷方合计', - `total_amount` decimal(18,4) DEFAULT '0.0000' COMMENT '凭证总额(旧版兼容)', - `status` tinyint DEFAULT '0' COMMENT '状态: 0=草稿, 1=已提交, 2=已审核, 3=已记账, 4=已作废', - `summary` text COMMENT '摘要(新版总账)', - `remark` varchar(500) DEFAULT NULL COMMENT '备注(旧版 FinanceVoucherHandler 兼容)', - `attachment_count` int DEFAULT '0' COMMENT '附件数', - `deleted` tinyint NOT NULL DEFAULT '0' COMMENT '软删除: 0-正常, 1-已删除', - `create_by` varchar(50) DEFAULT NULL COMMENT '制单人', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间(旧版兼容)', - `audited_by` varchar(50) DEFAULT NULL COMMENT '审核人', - `audited_at` datetime DEFAULT NULL COMMENT '审核时间', - `posted_by` varchar(50) DEFAULT NULL COMMENT '记账人', - `posted_at` datetime DEFAULT NULL COMMENT '记账时间', - `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', - `update_by` bigint unsigned DEFAULT NULL COMMENT '更新人ID', +-- 登录日志表 +CREATE TABLE `sys_login_log` ( + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '日志ID', + `user_id` BIGINT UNSIGNED COMMENT '用户ID', + `username` VARCHAR(50) COMMENT '用户名', + `login_type` TINYINT COMMENT '登录类型: 1-账号密码, 2-手机验证码', + `ip` VARCHAR(50) COMMENT 'IP地址', + `location` VARCHAR(100) COMMENT '登录地点', + `user_agent` VARCHAR(500) COMMENT '浏览器UA', + `status` TINYINT COMMENT '登录状态: 0-失败, 1-成功', + `error_msg` VARCHAR(255) COMMENT '错误信息', + `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', PRIMARY KEY (`id`), - UNIQUE KEY `uk_voucher_no` (`voucher_no`), - KEY `idx_period` (`period_code`), - KEY `idx_source` (`source_type`,`source_id`), - KEY `idx_status` (`status`), - CONSTRAINT `fk_fin_voucher_period` FOREIGN KEY (`period_code`) REFERENCES `fin_period` (`period_code`) ON DELETE RESTRICT ON UPDATE CASCADE -) ENGINE=InnoDB AUTO_INCREMENT=39 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='会计凭证主表'; + KEY `idx_user` (`user_id`), + KEY `idx_create_time` (`create_time`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='登录日志表'; --- 会计凭证明细表 -CREATE TABLE `fin_voucher_line` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '明细ID', - `voucher_id` bigint unsigned NOT NULL COMMENT '凭证ID', - `line_no` int NOT NULL COMMENT '行号', - `account_id` bigint unsigned NOT NULL COMMENT '科目ID', - `account_code` varchar(20) DEFAULT NULL COMMENT '科目编码(冗余)', - `account_name` varchar(100) DEFAULT NULL COMMENT '科目名称(冗余)', - `summary` varchar(500) DEFAULT NULL COMMENT '摘要(新版总账)', - `debit_amount` decimal(18,4) DEFAULT '0.0000' COMMENT '借方金额', - `credit_amount` decimal(18,4) DEFAULT '0.0000' COMMENT '贷方金额', - `debit_account` varchar(100) DEFAULT NULL COMMENT '借方科目名称(旧版 FinanceVoucherHandler 兼容)', - `credit_account` varchar(100) DEFAULT NULL COMMENT '贷方科目名称(旧版 FinanceVoucherHandler 兼容)', - `amount` decimal(18,4) DEFAULT '0.0000' COMMENT '金额(旧版兼容)', - `description` varchar(500) DEFAULT NULL COMMENT '描述(旧版 FinanceVoucherHandler 兼容)', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间(旧版兼容)', - `customer_id` bigint unsigned DEFAULT NULL COMMENT '客户ID(辅助核算)', - `supplier_id` bigint unsigned DEFAULT NULL COMMENT '供应商ID(辅助核算)', - `department_id` int unsigned DEFAULT NULL COMMENT '部门ID(辅助核算)', - `project_id` int unsigned DEFAULT NULL COMMENT '项目ID(辅助核算)', +-- 数据字典表 +CREATE TABLE `sys_dict_type` ( + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '字典类型ID', + `dict_name` VARCHAR(50) NOT NULL COMMENT '字典名称', + `dict_code` VARCHAR(50) NOT NULL COMMENT '字典编码', + `description` VARCHAR(255) COMMENT '描述', + `status` TINYINT DEFAULT 1 COMMENT '状态: 0-禁用, 1-启用', + `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', PRIMARY KEY (`id`), - KEY `idx_voucher` (`voucher_id`), - KEY `idx_account` (`account_id`), - KEY `idx_customer` (`customer_id`), - KEY `idx_supplier` (`supplier_id`), - KEY `idx_department` (`department_id`), - KEY `idx_project` (`project_id`), - CONSTRAINT `fk_fin_voucher_line_account` FOREIGN KEY (`account_id`) REFERENCES `fin_account` (`id`) ON DELETE RESTRICT ON UPDATE CASCADE, - CONSTRAINT `fk_fin_voucher_line_customer` FOREIGN KEY (`customer_id`) REFERENCES `crm_customer` (`id`) ON DELETE SET NULL ON UPDATE CASCADE, - CONSTRAINT `fk_fin_voucher_line_supplier` FOREIGN KEY (`supplier_id`) REFERENCES `pur_supplier` (`id`) ON DELETE SET NULL ON UPDATE CASCADE, - CONSTRAINT `fk_voucher_line_voucher` FOREIGN KEY (`voucher_id`) REFERENCES `fin_voucher` (`id`) ON DELETE CASCADE -) ENGINE=InnoDB AUTO_INCREMENT=23 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='会计凭证明细表'; + UNIQUE KEY `uk_dict_code` (`dict_code`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='字典类型表'; --- 考勤记录表 -CREATE TABLE `hr_attendance` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT, - `attendance_date` date NOT NULL, - `employee_id` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL, - `employee_id_int` bigint unsigned DEFAULT NULL, - `employee_name` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL, - `department_name` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL, - `check_in_time` varchar(10) COLLATE utf8mb4_unicode_ci DEFAULT NULL, - `check_out_time` varchar(10) COLLATE utf8mb4_unicode_ci DEFAULT NULL, - `status` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'normal', - `working_hours` decimal(5,2) DEFAULT '0.00', - `overtime_hours` decimal(5,2) DEFAULT '0.00', - `remark` text COLLATE utf8mb4_unicode_ci, - `deleted` tinyint DEFAULT '0', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP, - `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, +-- 字典数据表 +CREATE TABLE `sys_dict_data` ( + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '字典数据ID', + `dict_type_id` BIGINT UNSIGNED NOT NULL COMMENT '字典类型ID', + `dict_label` VARCHAR(50) NOT NULL COMMENT '字典标签', + `dict_value` VARCHAR(100) NOT NULL COMMENT '字典键值', + `sort_order` INT DEFAULT 0 COMMENT '排序序号', + `status` TINYINT DEFAULT 1 COMMENT '状态: 0-禁用, 1-启用', + `remark` VARCHAR(255) COMMENT '备注', + `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', PRIMARY KEY (`id`), - KEY `idx_employee` (`employee_id`), - KEY `idx_date` (`attendance_date`), + KEY `idx_dict_type` (`dict_type_id`), KEY `idx_status` (`status`) -) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='考勤记录表'; - --- 培训记录表 -CREATE TABLE `hr_training` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT, - `training_no` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '培训编号', - `training_name` varchar(200) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '培训名称', - `training_type` tinyint DEFAULT NULL COMMENT '培训类型', - `training_date` date DEFAULT NULL COMMENT '培训日期', - `training_hours` decimal(5,1) DEFAULT '0.0' COMMENT '培训学时', - `trainer` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '培训讲师', - `training_content` text COLLATE utf8mb4_unicode_ci COMMENT '培训内容', - `training_place` varchar(200) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '培训地点', - `status` tinyint DEFAULT '0' COMMENT '状态 0-待开始 1-进行中 2-已完成', - `remark` text COLLATE utf8mb4_unicode_ci, - `deleted` tinyint DEFAULT '0', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP, - `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - PRIMARY KEY (`id`), - UNIQUE KEY `uk_training_no` (`training_no`) -) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='培训记录表'; - --- 培训参与人员表 -CREATE TABLE `hr_training_participant` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT, - `training_id` bigint unsigned NOT NULL COMMENT '培训ID', - `employee_id` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '员工ID', - `employee_name` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '员工姓名', - `score` decimal(5,1) DEFAULT NULL COMMENT '成绩', - `is_qualified` tinyint DEFAULT NULL COMMENT '是否合格', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP, - PRIMARY KEY (`id`), - KEY `idx_training_id` (`training_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='培训参与人员表'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='字典数据表'; --- 调墨批次表 -CREATE TABLE `ink_mixed_batch` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT, - `batch_no` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '调墨批次号', - `formula_no` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '配方编号', - `formula_name` varchar(200) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '配方名称', - `total_qty` decimal(12,2) DEFAULT '0.00' COMMENT '总数量', - `unit` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '单位', - `mixed_date` date DEFAULT NULL COMMENT '调墨日期', - `expire_date` date DEFAULT NULL COMMENT '有效期', - `operator_id` bigint unsigned DEFAULT NULL COMMENT '操作人ID', - `operator_name` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '操作人', - `status` tinyint DEFAULT '1' COMMENT '状态', - `remark` text COLLATE utf8mb4_unicode_ci COMMENT '备注', - `deleted` tinyint DEFAULT '0', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP, - `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, +-- 系统配置表 +CREATE TABLE `sys_config` ( + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '配置ID', + `config_name` VARCHAR(50) NOT NULL COMMENT '配置名称', + `config_key` VARCHAR(50) NOT NULL COMMENT '配置键名', + `config_value` VARCHAR(500) NOT NULL COMMENT '配置值', + `config_type` TINYINT DEFAULT 1 COMMENT '配置类型: 1-系统, 2-业务', + `description` VARCHAR(255) COMMENT '描述', + `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', PRIMARY KEY (`id`), - UNIQUE KEY `uk_batch_no` (`batch_no`) -) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='调墨批次表'; + UNIQUE KEY `uk_config_key` (`config_key`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='系统配置表'; --- 调墨明细 -CREATE TABLE `ink_mixed_batch_detail` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT, - `mixed_batch_id` bigint unsigned NOT NULL COMMENT '调墨批次ID', - `source_batch_no` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '来源批次号', - `source_label_no` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '来源标签号', - `material_id` bigint unsigned DEFAULT NULL COMMENT '物料ID', - `material_name` varchar(200) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '物料名称', - `used_qty` decimal(12,2) DEFAULT '0.00' COMMENT '使用数量', - `unit` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '单位', - PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='调墨明细'; +-- ======================================================== +-- 2. 客户管理模块 +-- ======================================================== --- 调色后油墨入库记录表 -CREATE TABLE `ink_mixed_record` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT, - `record_no` varchar(50) NOT NULL COMMENT '记录单号', - `base_ink_id` bigint unsigned NOT NULL COMMENT '原油墨ID', - `base_ink_code` varchar(50) DEFAULT NULL COMMENT '原油墨编号', - `base_ink_name` varchar(200) DEFAULT NULL COMMENT '原油墨名称', - `mix_ratio` varchar(100) DEFAULT NULL COMMENT '调色比例', - `color_name` varchar(100) DEFAULT NULL COMMENT '色彩名称', - `color_code` varchar(50) DEFAULT NULL COMMENT '色彩编码', - `company_id` bigint unsigned DEFAULT NULL COMMENT '使用公司/客户ID', - `company_name` varchar(200) DEFAULT NULL COMMENT '使用公司/客户名称', - `mix_time` datetime NOT NULL COMMENT '调色时间', - `operator_id` bigint unsigned DEFAULT NULL COMMENT '操作员ID', - `operator_name` varchar(50) DEFAULT NULL COMMENT '操作员名称', - `quantity` decimal(18,4) DEFAULT NULL COMMENT '入库数量', - `unit` varchar(20) DEFAULT NULL COMMENT '单位', - `warehouse_id` bigint unsigned DEFAULT NULL COMMENT '仓库ID', - `location_id` bigint unsigned DEFAULT NULL COMMENT '库位ID', - `status` tinyint DEFAULT '1' COMMENT '状态: 1-已入库, 2-已使用, 3-已过期', - `expire_time` datetime DEFAULT NULL COMMENT '过期时间', - `remark` text COMMENT '备注', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP, - `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - `deleted` tinyint DEFAULT '0', +-- 客户表 +CREATE TABLE `crm_customer` ( + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '客户ID', + `customer_code` VARCHAR(50) NOT NULL COMMENT '客户编码', + `customer_name` VARCHAR(100) NOT NULL COMMENT '客户名称', + `short_name` VARCHAR(50) COMMENT '客户简称', + `customer_type` TINYINT COMMENT '客户类型: 1-企业, 2-个人', + `industry` VARCHAR(50) COMMENT '所属行业', + `scale` VARCHAR(50) COMMENT '企业规模', + `credit_level` VARCHAR(20) COMMENT '信用等级', + `province` VARCHAR(50) COMMENT '省份', + `city` VARCHAR(50) COMMENT '城市', + `district` VARCHAR(50) COMMENT '区县', + `address` VARCHAR(255) COMMENT '详细地址', + `contact_name` VARCHAR(50) COMMENT '联系人姓名', + `contact_phone` VARCHAR(20) COMMENT '联系人电话', + `contact_email` VARCHAR(100) COMMENT '联系人邮箱', + `fax` VARCHAR(20) COMMENT '传真', + `website` VARCHAR(100) COMMENT '网站', + `business_license` VARCHAR(50) COMMENT '营业执照号', + `tax_number` VARCHAR(50) COMMENT '税号', + `bank_name` VARCHAR(100) COMMENT '开户银行', + `bank_account` VARCHAR(50) COMMENT '银行账号', + `salesman_id` BIGINT UNSIGNED COMMENT '业务员ID', + `follow_up_status` TINYINT DEFAULT 1 COMMENT '跟进状态: 1-潜在客户, 2-意向客户, 3-成交客户, 4-流失客户', + `status` TINYINT DEFAULT 1 COMMENT '状态: 0-禁用, 1-启用', + `remark` TEXT COMMENT '备注', + `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `create_by` BIGINT UNSIGNED COMMENT '创建人ID', + `update_by` BIGINT UNSIGNED COMMENT '更新人ID', + `deleted` TINYINT DEFAULT 0 COMMENT '删除标记', PRIMARY KEY (`id`), - UNIQUE KEY `uk_record_no` (`record_no`), - KEY `idx_base_ink` (`base_ink_id`), - KEY `idx_company` (`company_id`), + UNIQUE KEY `uk_customer_code` (`customer_code`), + KEY `idx_customer_name` (`customer_name`), + KEY `idx_salesman` (`salesman_id`), KEY `idx_status` (`status`) -) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='调色后油墨入库记录表'; - --- 油墨开罐记录表 -CREATE TABLE `ink_opening_record` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT, - `record_no` varchar(50) NOT NULL COMMENT '记录单号', - `material_id` bigint unsigned NOT NULL COMMENT '物料ID', - `material_code` varchar(50) DEFAULT NULL COMMENT '物料编码', - `material_name` varchar(200) DEFAULT NULL COMMENT '物料名称', - `batch_no` varchar(50) DEFAULT NULL COMMENT '批次号', - `label_id` bigint unsigned DEFAULT NULL COMMENT '标签ID', - `ink_type` varchar(20) DEFAULT NULL COMMENT '油墨类型: solvent-溶剂型, uv-UV型, water-水性', - `open_time` datetime NOT NULL COMMENT '开罐时间', - `expire_hours` int NOT NULL COMMENT '有效时长(小时)', - `expire_time` datetime DEFAULT NULL COMMENT '过期时间', - `remaining_qty` decimal(18,4) DEFAULT NULL COMMENT '剩余数量', - `unit` varchar(20) DEFAULT NULL COMMENT '单位', - `status` tinyint DEFAULT '1' COMMENT '状态: 1-使用中, 2-已过期, 3-已报废', - `operator_id` bigint unsigned DEFAULT NULL COMMENT '操作员ID', - `operator_name` varchar(50) DEFAULT NULL COMMENT '操作员名称', - `remark` text COMMENT '备注', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP, - `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - `deleted` tinyint DEFAULT '0', - PRIMARY KEY (`id`), - UNIQUE KEY `uk_record_no` (`record_no`), - KEY `idx_material` (`material_id`), - KEY `idx_batch` (`batch_no`), - KEY `idx_status` (`status`), - KEY `idx_expire_time` (`expire_time`) -) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='油墨开罐记录表'; - --- 辅料库存表 -CREATE TABLE `inv_auxiliary_inventory` ( - `id` int NOT NULL AUTO_INCREMENT, - `aux_code` varchar(100) DEFAULT NULL, - `aux_name` varchar(255) DEFAULT NULL, - `specification` varchar(255) DEFAULT NULL, - `category` varchar(100) DEFAULT NULL, - `unit` varchar(50) DEFAULT NULL, - `warehouse` varchar(100) DEFAULT NULL, - `location` varchar(100) DEFAULT NULL, - `supplier` varchar(255) DEFAULT NULL, - `opening_balance` decimal(18,4) DEFAULT '0.0000', - `total_in` decimal(18,4) DEFAULT '0.0000', - `total_out` decimal(18,4) DEFAULT '0.0000', - `current_balance` decimal(18,4) DEFAULT '0.0000', - `source_file` varchar(255) DEFAULT NULL, - `source_sheet` varchar(100) DEFAULT NULL, - `import_time` datetime DEFAULT CURRENT_TIMESTAMP, - `remarks` text, - PRIMARY KEY (`id`), - KEY `idx_aux_code` (`aux_code`), - KEY `idx_aux_name` (`aux_name`), - KEY `idx_supplier` (`supplier`) -) ENGINE=InnoDB AUTO_INCREMENT=740 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='辅料库存表'; - --- 分切明细表 -CREATE TABLE `inv_cutting_detail` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT, - `record_id` bigint unsigned NOT NULL COMMENT '分切记录ID', - `new_label_id` bigint unsigned NOT NULL COMMENT '新标签ID', - `new_label_no` varchar(50) NOT NULL COMMENT '新标签编号', - `cut_width` decimal(18,2) DEFAULT NULL COMMENT '分切宽幅', - `sequence` int DEFAULT '0' COMMENT '分切序号', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP, - PRIMARY KEY (`id`), - KEY `idx_record_id` (`record_id`), - KEY `idx_new_label` (`new_label_id`) -) ENGINE=InnoDB AUTO_INCREMENT=32 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='分切明细表'; - --- 分切记录表 -CREATE TABLE `inv_cutting_record` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT, - `record_no` varchar(50) NOT NULL COMMENT '分切单号', - `source_label_id` bigint unsigned NOT NULL COMMENT '源标签ID', - `source_label_no` varchar(50) NOT NULL COMMENT '源标签编号', - `cut_width_str` varchar(200) DEFAULT NULL COMMENT '分切宽幅(如:10+20+30)', - `original_width` decimal(18,2) DEFAULT NULL COMMENT '原宽幅', - `cut_total_width` decimal(18,2) DEFAULT NULL COMMENT '分切总宽幅', - `remain_width` decimal(18,2) DEFAULT NULL COMMENT '剩余宽幅', - `operator_id` bigint unsigned DEFAULT NULL COMMENT '操作员ID', - `operator_name` varchar(50) DEFAULT NULL COMMENT '操作员名称', - `cut_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '分切时间', - `remark` varchar(500) DEFAULT NULL COMMENT '备注', - `status` tinyint DEFAULT '1' COMMENT '状态: 0-作废, 1-正常', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP, - PRIMARY KEY (`id`), - UNIQUE KEY `uk_record_no` (`record_no`), - KEY `idx_source_label` (`source_label_id`), - KEY `idx_cut_time` (`cut_time`) -) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='分切记录表'; - --- FIFO异常覆盖记录表 -CREATE TABLE `inv_fifo_override_log` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '记录ID', - `material_id` bigint unsigned NOT NULL COMMENT '物料ID', - `recommended_batch_no` varchar(50) DEFAULT NULL COMMENT '推荐批次号', - `actual_batch_no` varchar(50) DEFAULT NULL COMMENT '实际出库批次号', - `requisition_id` bigint unsigned DEFAULT NULL COMMENT '领料单ID', - `reason` varchar(255) NOT NULL COMMENT '异常原因', - `operator_id` bigint unsigned DEFAULT NULL COMMENT '操作人ID', - `operator_name` varchar(50) DEFAULT NULL COMMENT '操作人姓名', - `approve_id` bigint unsigned DEFAULT NULL COMMENT '审批人ID', - `approve_name` varchar(50) DEFAULT NULL COMMENT '审批人姓名', - `status` tinyint DEFAULT '0' COMMENT '状态:0=待审批,1=已批准,2=已拒绝', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - `deleted` tinyint NOT NULL DEFAULT '0' COMMENT '软删除标记', - PRIMARY KEY (`id`), - KEY `idx_material` (`material_id`), - KEY `idx_requisition` (`requisition_id`), - KEY `idx_operator` (`operator_id`), - KEY `idx_approve` (`approve_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='FIFO异常覆盖记录表'; - --- 入库订单明细表 -CREATE TABLE `inv_inbound_item` ( - `id` int unsigned NOT NULL AUTO_INCREMENT, - `order_id` int unsigned NOT NULL COMMENT '入库订单ID', - `material_id` bigint unsigned DEFAULT NULL COMMENT '物料ID', - `material_name` varchar(100) NOT NULL COMMENT '物料名称', - `material_spec` varchar(200) DEFAULT NULL COMMENT '物料规格', - `batch_no` varchar(50) DEFAULT NULL COMMENT '批次号', - `quantity` decimal(18,4) DEFAULT NULL COMMENT '数量', - `unit` varchar(20) DEFAULT NULL COMMENT '单位', - `unit_price` decimal(18,4) DEFAULT NULL COMMENT '单价', - `total_price` decimal(18,4) DEFAULT NULL COMMENT '总价', - `warehouse_location` varchar(50) DEFAULT NULL COMMENT '库位', - `produce_date` date DEFAULT NULL COMMENT '生产日期', - `expire_date` date DEFAULT NULL COMMENT '过期日期', - `remark` text COMMENT '备注', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP, - `deleted` tinyint DEFAULT '0' COMMENT '软删除', - PRIMARY KEY (`id`), - KEY `idx_order` (`order_id`), - KEY `idx_material` (`material_id`), - CONSTRAINT `fk_inbound_item_order` FOREIGN KEY (`order_id`) REFERENCES `inv_inbound_order` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, - CONSTRAINT `fk_inv_inbound_item_order` FOREIGN KEY (`order_id`) REFERENCES `inv_inbound_order` (`id`) ON DELETE CASCADE ON UPDATE CASCADE -) ENGINE=InnoDB AUTO_INCREMENT=86 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='入库订单明细表'; - --- 入库物料标签 -CREATE TABLE `inv_inbound_label` ( - `id` bigint NOT NULL AUTO_INCREMENT, - `label_id` varchar(50) NOT NULL COMMENT '标签唯一编号', - `order_id` bigint DEFAULT NULL COMMENT '入库单ID', - `order_no` varchar(50) DEFAULT NULL COMMENT '入库单号', - `item_id` bigint DEFAULT NULL COMMENT '入库明细ID', - `purchase_order_no` varchar(50) DEFAULT NULL COMMENT '采购单号', - `supplier_name` varchar(200) DEFAULT NULL COMMENT '供应商名称', - `inbound_date` date DEFAULT NULL COMMENT '入库日期', - `warehouse_code` varchar(50) DEFAULT NULL COMMENT '仓库编码', - `material_code` varchar(50) NOT NULL COMMENT '物料编码', - `material_name` varchar(200) NOT NULL COMMENT '物料名称', - `specification` varchar(200) DEFAULT NULL COMMENT '规格', - `width` decimal(18,4) DEFAULT '0.0000' COMMENT '幅宽', - `batch_no` varchar(50) DEFAULT NULL COMMENT '批次号', - `qty` decimal(18,4) NOT NULL DEFAULT '0.0000' COMMENT '数量', - `unit` varchar(20) NOT NULL DEFAULT '件' COMMENT '单位', - `is_raw_material` tinyint NOT NULL DEFAULT '0' COMMENT '是否原料: 0=否, 1=是', - `package_qty` int DEFAULT '0' COMMENT '包装数量', - `label_qty` int DEFAULT '1' COMMENT '标签数量', - `label_status` varchar(20) NOT NULL DEFAULT 'generated' COMMENT '标签状态: generated|used|split|void', - `audit_status` tinyint DEFAULT '0' COMMENT '审核状态: 0=未审核, 1=已审核', - `operator_id` bigint DEFAULT NULL COMMENT '操作员ID', - `operator_name` varchar(50) DEFAULT NULL COMMENT '操作员姓名', - `auditor_id` bigint DEFAULT NULL COMMENT '审核员ID', - `auditor_name` varchar(50) DEFAULT NULL COMMENT '审核员姓名', - `audit_time` datetime DEFAULT NULL COMMENT '审核时间', - `color_code` varchar(50) DEFAULT NULL COMMENT '色号', - `mixed_material_remark` varchar(500) DEFAULT NULL COMMENT '混料备注', - `machine_no` varchar(50) DEFAULT NULL COMMENT '机台号', - `remark` varchar(500) DEFAULT NULL COMMENT '备注', - `deleted` tinyint NOT NULL DEFAULT '0' COMMENT '软删除: 0=正常, 1=已删除', - `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, - `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - PRIMARY KEY (`id`), - UNIQUE KEY `uk_label_id` (`label_id`), - KEY `idx_order_no` (`order_no`), - KEY `idx_material_code` (`material_code`), - KEY `idx_batch_no` (`batch_no`), - KEY `idx_label_status` (`label_status`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='入库物料标签'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='客户表'; --- 入库订单主表 -CREATE TABLE `inv_inbound_order` ( - `id` int unsigned NOT NULL AUTO_INCREMENT, - `order_no` varchar(50) NOT NULL COMMENT '入库单号', - `order_type` enum('purchase','return','transfer','other') DEFAULT 'purchase' COMMENT '入库类型', - `warehouse_id` bigint unsigned NOT NULL COMMENT '入库仓库ID', - `warehouse_code` varchar(50) DEFAULT NULL COMMENT '仓库编码', - `warehouse_name` varchar(100) DEFAULT NULL COMMENT '仓库名称', - `supplier_id` int unsigned DEFAULT NULL COMMENT '供应商ID', - `supplier_name` varchar(100) DEFAULT NULL COMMENT '供应商名称', - `operator_id` bigint unsigned DEFAULT NULL COMMENT '操作员ID', - `operator_name` varchar(50) DEFAULT NULL COMMENT '操作员姓名', - `po_id` int unsigned DEFAULT NULL COMMENT '关联采购单ID', - `po_no` varchar(50) DEFAULT NULL COMMENT '采购单号', - `grn_type` enum('po','blind','return') DEFAULT 'po' COMMENT '入库类型', - `total_amount` decimal(18,4) DEFAULT NULL COMMENT '总金额', - `total_quantity` decimal(18,4) DEFAULT '0.0000' COMMENT '总数量', - `status` enum('draft','pending','approved','completed','cancelled') DEFAULT 'draft' COMMENT '状态', - `qc_status` enum('pending','pass','fail','partial') DEFAULT 'pending' COMMENT '质检状态', - `inbound_date` date DEFAULT NULL COMMENT '入库日期', - `remark` text COMMENT '备注', - `create_by` int unsigned DEFAULT NULL, - `create_time` datetime DEFAULT CURRENT_TIMESTAMP, - `update_by` int unsigned DEFAULT NULL, - `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - `deleted` tinyint NOT NULL DEFAULT '0' COMMENT '软删除: 0-正常, 1-已删除', - PRIMARY KEY (`id`), - KEY `idx_order_no` (`order_no`), - KEY `idx_warehouse` (`warehouse_id`), - KEY `idx_status` (`status`), - KEY `idx_po_id` (`po_id`), - CONSTRAINT `fk_inv_inbound_warehouse` FOREIGN KEY (`warehouse_id`) REFERENCES `inv_warehouse` (`id`) ON DELETE RESTRICT ON UPDATE CASCADE -) ENGINE=InnoDB AUTO_INCREMENT=84 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='入库订单主表'; - --- 库存表 -CREATE TABLE `inv_inventory` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT, - `material_id` bigint unsigned NOT NULL COMMENT '物料ID', - `material_code` varchar(50) DEFAULT NULL COMMENT '物料编码(冗余,便于查询)', - `material_name` varchar(100) DEFAULT NULL COMMENT '物料名称', - `warehouse_id` bigint unsigned NOT NULL COMMENT '仓库ID', - `warehouse_name` varchar(100) DEFAULT NULL COMMENT '仓库名称', - `quantity` decimal(18,4) DEFAULT '0.0000' COMMENT '库存数量', - `available_qty` decimal(18,4) DEFAULT '0.0000' COMMENT '可用数量', - `batch_no` varchar(50) DEFAULT NULL COMMENT '批次号', - `locked_qty` decimal(18,4) DEFAULT '0.0000' COMMENT '锁定数量', - `unit` varchar(20) DEFAULT NULL COMMENT '单位', - `unit_cost` decimal(18,4) DEFAULT '0.0000' COMMENT '单位成本', - `total_cost` decimal(18,4) DEFAULT '0.0000' COMMENT '总成本', - `safety_stock` decimal(18,4) DEFAULT '0.0000' COMMENT '安全库存', - `version` int unsigned DEFAULT '1' COMMENT '乐观锁版本号', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP, - `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - `deleted` tinyint NOT NULL DEFAULT '0' COMMENT '软删除: 0-正常, 1-已删除', - `create_by` bigint unsigned DEFAULT NULL COMMENT '创建人ID', - `update_by` bigint unsigned DEFAULT NULL COMMENT '更新人ID', - PRIMARY KEY (`id`), - UNIQUE KEY `uk_material_warehouse` (`material_id`,`warehouse_id`), - KEY `idx_material` (`material_id`), - KEY `idx_warehouse` (`warehouse_id`), - KEY `idx_material_code` (`material_code`), - CONSTRAINT `fk_inv_inventory_material` FOREIGN KEY (`material_id`) REFERENCES `inv_material` (`id`) ON DELETE RESTRICT ON UPDATE CASCADE, - CONSTRAINT `fk_inv_inventory_warehouse` FOREIGN KEY (`warehouse_id`) REFERENCES `inv_warehouse` (`id`) ON DELETE RESTRICT ON UPDATE CASCADE -) ENGINE=InnoDB AUTO_INCREMENT=162 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='库存表'; - --- 库存批次表 -CREATE TABLE `inv_inventory_batch` ( - `id` int unsigned NOT NULL AUTO_INCREMENT, - `batch_no` varchar(50) NOT NULL COMMENT '批次号', - `material_id` bigint unsigned NOT NULL COMMENT '物料ID', - `material_name` varchar(100) NOT NULL COMMENT '物料名称', - `warehouse_id` bigint unsigned NOT NULL COMMENT '仓库ID', - `warehouse_name` varchar(100) DEFAULT NULL COMMENT '仓库名称', - `quantity` decimal(12,3) DEFAULT '0.000' COMMENT '总数量', - `available_qty` decimal(12,3) DEFAULT '0.000' COMMENT '可用数量', - `locked_qty` decimal(12,3) DEFAULT '0.000' COMMENT '锁定数量', - `unit` varchar(20) DEFAULT '件' COMMENT '单位', - `unit_price` decimal(12,2) DEFAULT '0.00' COMMENT '单价', - `produce_date` date DEFAULT NULL COMMENT '生产日期', - `expire_date` date DEFAULT NULL COMMENT '有效期至', - `inbound_date` date DEFAULT NULL COMMENT '入库日期', - `status` tinyint NOT NULL DEFAULT '1' COMMENT '1-normal 可用, 2-frozen 冻结, 3-expired 过期', - `version` int unsigned DEFAULT '1' COMMENT '乐观锁版本号', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP, - `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - `deleted` tinyint NOT NULL DEFAULT '0' COMMENT '软删除: 0-正常, 1-已删除', - `alert_level` varchar(20) DEFAULT 'normal' COMMENT '库存告警级别: normal/warning/critical', - `last_alert_time` timestamp NULL DEFAULT NULL COMMENT '最后告警时间', - `inspection_status` varchar(20) DEFAULT 'pending' COMMENT '检验状态: pending/pass/fail', - `quarantine_status` varchar(20) DEFAULT 'none' COMMENT '隔离状态: none/quarantined/released', - `create_by` bigint unsigned DEFAULT NULL COMMENT '创建人ID', - `update_by` bigint unsigned DEFAULT NULL COMMENT '更新人ID', +-- 客户联系人表 +CREATE TABLE `crm_customer_contact` ( + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '联系人ID', + `customer_id` BIGINT UNSIGNED NOT NULL COMMENT '客户ID', + `contact_name` VARCHAR(50) NOT NULL COMMENT '联系人姓名', + `gender` TINYINT COMMENT '性别: 1-男, 2-女', + `position` VARCHAR(50) COMMENT '职位', + `department` VARCHAR(50) COMMENT '部门', + `phone` VARCHAR(20) COMMENT '手机号', + `telephone` VARCHAR(20) COMMENT '座机', + `email` VARCHAR(100) COMMENT '邮箱', + `wechat` VARCHAR(50) COMMENT '微信', + `qq` VARCHAR(20) COMMENT 'QQ', + `is_primary` TINYINT DEFAULT 0 COMMENT '是否主要联系人: 0-否, 1-是', + `remark` VARCHAR(255) COMMENT '备注', + `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `deleted` TINYINT DEFAULT 0 COMMENT '删除标记', PRIMARY KEY (`id`), - UNIQUE KEY `uk_batch_no` (`batch_no`), - KEY `idx_material` (`material_id`), - KEY `idx_warehouse` (`warehouse_id`), - KEY `idx_status` (`status`), - KEY `idx_alert_level` (`alert_level`), - CONSTRAINT `fk_inv_inventory_batch_material` FOREIGN KEY (`material_id`) REFERENCES `inv_material` (`id`) ON DELETE RESTRICT ON UPDATE CASCADE, - CONSTRAINT `fk_inv_inventory_batch_warehouse` FOREIGN KEY (`warehouse_id`) REFERENCES `inv_warehouse` (`id`) ON DELETE RESTRICT ON UPDATE CASCADE -) ENGINE=InnoDB AUTO_INCREMENT=192 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='库存批次表'; + KEY `idx_customer` (`customer_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='客户联系人表'; --- 库存变动日志表 -CREATE TABLE `inv_inventory_log` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT, - `warehouse_id` int unsigned DEFAULT NULL COMMENT '仓库ID', - `material_id` int unsigned DEFAULT NULL COMMENT '物料ID', - `change_type` varchar(20) DEFAULT NULL COMMENT '变动类型', - `change_qty` decimal(12,3) DEFAULT '0.000' COMMENT '变动数量', - `order_no` varchar(50) DEFAULT NULL COMMENT '关联单号', - `remark` text COMMENT '备注', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP, +-- 客户跟进记录表 +CREATE TABLE `crm_customer_follow_up` ( + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '跟进记录ID', + `customer_id` BIGINT UNSIGNED NOT NULL COMMENT '客户ID', + `follow_up_type` TINYINT COMMENT '跟进方式: 1-电话, 2-邮件, 3-拜访, 4-微信, 5-其他', + `follow_up_content` TEXT COMMENT '跟进内容', + `follow_up_time` DATETIME COMMENT '跟进时间', + `next_follow_up_time` DATETIME COMMENT '下次跟进时间', + `follow_up_by` BIGINT UNSIGNED COMMENT '跟进人ID', + `attachment` VARCHAR(255) COMMENT '附件', + `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', PRIMARY KEY (`id`), - KEY `idx_warehouse` (`warehouse_id`), - KEY `idx_material` (`material_id`), - KEY `idx_create_time` (`create_time`) -) ENGINE=InnoDB AUTO_INCREMENT=80 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='库存变动日志表'; + KEY `idx_customer` (`customer_id`), + KEY `idx_follow_up_time` (`follow_up_time`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='客户跟进记录表'; --- 库存事务表 -CREATE TABLE `inv_inventory_transaction` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT, - `trans_no` varchar(50) NOT NULL COMMENT '事务单号', - `trans_type` enum('in','out','transfer','adjust','return') NOT NULL COMMENT '事务类型', - `source_type` varchar(20) DEFAULT NULL COMMENT '来源类型', - `source_id` bigint unsigned DEFAULT NULL COMMENT '来源单据ID', - `source_line_id` bigint unsigned DEFAULT NULL COMMENT '来源单据行ID', - `material_id` bigint unsigned DEFAULT NULL COMMENT '物料ID', - `material_code` varchar(50) DEFAULT NULL COMMENT '物料编码', - `batch_no` varchar(50) DEFAULT NULL COMMENT '批次号', - `warehouse_id` bigint unsigned DEFAULT NULL COMMENT '仓库ID', - `location_id` bigint unsigned DEFAULT NULL COMMENT '库位ID', - `quantity` decimal(14,3) DEFAULT '0.000' COMMENT '数量', - `unit_cost` decimal(14,4) DEFAULT '0.0000' COMMENT '单位成本', - `total_cost` decimal(14,2) DEFAULT '0.00' COMMENT '总成本', - `unit_price` decimal(14,4) DEFAULT '0.0000' COMMENT '单价', - `total_amount` decimal(14,2) DEFAULT '0.00' COMMENT '总金额', - `reference_no` varchar(100) DEFAULT NULL COMMENT '参考单号', - `remark` text COMMENT '备注', - `create_by` bigint unsigned DEFAULT NULL, - `create_time` datetime DEFAULT CURRENT_TIMESTAMP, - PRIMARY KEY (`id`), - UNIQUE KEY `uk_trans_no` (`trans_no`), - KEY `idx_trans_type` (`trans_type`), - KEY `idx_source` (`source_type`,`source_id`), - KEY `idx_material` (`material_id`), - KEY `idx_batch` (`batch_no`), - KEY `idx_warehouse` (`warehouse_id`), - KEY `idx_create_time` (`create_time`) -) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='库存事务表'; +-- ======================================================== +-- 3. 供应商管理模块 +-- ======================================================== --- 库存事务日志表 -CREATE TABLE `inv_inventory_transaction_log` ( - `id` int NOT NULL AUTO_INCREMENT, - `transaction_type` varchar(50) DEFAULT NULL, - `item_type` varchar(50) DEFAULT NULL, - `item_id` int DEFAULT NULL, - `item_code` varchar(100) DEFAULT NULL, - `item_name` varchar(255) DEFAULT NULL, - `quantity` decimal(18,4) DEFAULT NULL, - `transaction_date` date DEFAULT NULL, - `reference_no` varchar(100) DEFAULT NULL, - `warehouse` varchar(100) DEFAULT NULL, - `location` varchar(100) DEFAULT NULL, - `operator` varchar(100) DEFAULT NULL, - `source_file` varchar(255) DEFAULT NULL, - `create_time` datetime DEFAULT CURRENT_TIMESTAMP, - `remarks` text, - PRIMARY KEY (`id`), - KEY `idx_transaction_type` (`transaction_type`), - KEY `idx_item_type` (`item_type`), - KEY `idx_transaction_date` (`transaction_date`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='库存事务日志表'; +-- 供应商表 +CREATE TABLE `pur_supplier` ( + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '供应商ID', + `supplier_code` VARCHAR(50) NOT NULL COMMENT '供应商编码', + `supplier_name` VARCHAR(100) NOT NULL COMMENT '供应商名称', + `short_name` VARCHAR(50) COMMENT '供应商简称', + `supplier_type` TINYINT COMMENT '供应商类型: 1-原材料, 2-辅料, 3-设备, 4-服务', + `province` VARCHAR(50) COMMENT '省份', + `city` VARCHAR(50) COMMENT '城市', + `address` VARCHAR(255) COMMENT '详细地址', + `contact_name` VARCHAR(50) COMMENT '联系人', + `contact_phone` VARCHAR(20) COMMENT '联系电话', + `contact_email` VARCHAR(100) COMMENT '联系邮箱', + `business_license` VARCHAR(50) COMMENT '营业执照号', + `tax_number` VARCHAR(50) COMMENT '税号', + `bank_name` VARCHAR(100) COMMENT '开户银行', + `bank_account` VARCHAR(50) COMMENT '银行账号', + `credit_level` VARCHAR(20) COMMENT '信用等级', + `cooperation_status` TINYINT DEFAULT 1 COMMENT '合作状态: 1-合作中, 2-暂停合作, 3-终止合作', + `settlement_method` VARCHAR(50) COMMENT '结算方式', + `payment_terms` VARCHAR(100) COMMENT '付款条件', + `status` TINYINT DEFAULT 1 COMMENT '状态: 0-禁用, 1-启用', + `remark` TEXT COMMENT '备注', + `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `create_by` BIGINT UNSIGNED COMMENT '创建人ID', + `update_by` BIGINT UNSIGNED COMMENT '更新人ID', + `deleted` TINYINT DEFAULT 0 COMMENT '删除标记', + PRIMARY KEY (`id`), + UNIQUE KEY `uk_supplier_code` (`supplier_code`), + KEY `idx_supplier_name` (`supplier_name`), + KEY `idx_status` (`status`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='供应商表'; --- 库位表 -CREATE TABLE `inv_location` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT, - `location_code` varchar(50) NOT NULL COMMENT '库位编码', - `location_name` varchar(100) NOT NULL COMMENT '库位名称', - `warehouse_id` bigint unsigned NOT NULL COMMENT '仓库ID', - `zone` varchar(50) DEFAULT NULL COMMENT '区域', - `row_no` varchar(20) DEFAULT NULL COMMENT '排', - `column_no` varchar(20) DEFAULT NULL COMMENT '列', - `layer_no` varchar(20) DEFAULT NULL COMMENT '层', - `location_type` tinyint DEFAULT '1' COMMENT '库位类型: 1-原料, 2-成品, 3-半成品, 4-余料, 5-网版/刀模专用', - `status` tinyint DEFAULT '1' COMMENT '状态: 0-禁用, 1-启用', - `remark` text COMMENT '备注', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP, - `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - `deleted` tinyint DEFAULT '0', - PRIMARY KEY (`id`), - UNIQUE KEY `uk_location_code` (`location_code`), - KEY `idx_warehouse` (`warehouse_id`) -) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='库位表'; +-- 供应商物料关联表 +CREATE TABLE `pur_supplier_material` ( + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'ID', + `supplier_id` BIGINT UNSIGNED NOT NULL COMMENT '供应商ID', + `material_id` BIGINT UNSIGNED NOT NULL COMMENT '物料ID', + `supply_price` DECIMAL(18,4) COMMENT '供应价格', + `min_order_qty` DECIMAL(18,4) COMMENT '最小订购量', + `lead_time` INT COMMENT '交货周期(天)', + `is_default` TINYINT DEFAULT 0 COMMENT '是否默认供应商: 0-否, 1-是', + `status` TINYINT DEFAULT 1 COMMENT '状态', + `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + PRIMARY KEY (`id`), + UNIQUE KEY `uk_supplier_material` (`supplier_id`, `material_id`), + KEY `idx_material` (`material_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='供应商物料关联表'; --- 物料表 -CREATE TABLE `inv_material` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT, - `material_code` varchar(50) NOT NULL COMMENT '物料编码', - `material_name` varchar(100) NOT NULL COMMENT '物料名称', - `specification` varchar(255) DEFAULT NULL COMMENT '规格型号', - `category_id` bigint unsigned DEFAULT NULL COMMENT '分类ID', - `material_type` tinyint DEFAULT NULL COMMENT '物料类型: 1-原材料, 2-半成品, 3-成品, 4-辅料, 5-包材', - `unit` varchar(20) DEFAULT NULL COMMENT '计量单位', - `barcode` varchar(50) DEFAULT NULL COMMENT '条形码', - `brand` varchar(50) DEFAULT NULL COMMENT '品牌', - `safety_stock` decimal(18,4) DEFAULT '0.0000' COMMENT '安全库存', - `max_stock` decimal(18,4) DEFAULT NULL COMMENT '最大库存', - `min_stock` decimal(18,4) DEFAULT NULL COMMENT '最小库存', - `purchase_price` decimal(18,4) DEFAULT NULL COMMENT '采购单价', - `sale_price` decimal(18,4) DEFAULT NULL COMMENT '销售单价', - `cost_price` decimal(18,4) DEFAULT NULL COMMENT '成本单价', - `warehouse_id` bigint unsigned DEFAULT NULL COMMENT '默认仓库ID', - `shelf_life` int DEFAULT NULL COMMENT '保质期(天)', - `warning_days` int DEFAULT NULL COMMENT '预警天数', - `is_batch_managed` tinyint DEFAULT '0' COMMENT '是否批次管理: 0-否, 1-是', - `is_serial_managed` tinyint DEFAULT '0' COMMENT '是否序列号管理: 0-否, 1-是', - `status` tinyint DEFAULT '1' COMMENT '状态: 0-禁用, 1-启用', - `remark` text COMMENT '备注', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP, - `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - `create_by` bigint unsigned DEFAULT NULL, - `update_by` bigint unsigned DEFAULT NULL, - `deleted` tinyint NOT NULL DEFAULT '0' COMMENT '软删除: 0-正常, 1-已删除', - PRIMARY KEY (`id`), - UNIQUE KEY `uk_material_code` (`material_code`), - KEY `idx_material_code` (`material_code`), - KEY `idx_material_type` (`material_type`,`deleted`), - KEY `idx_warehouse` (`warehouse_id`), - KEY `fk_material_category` (`category_id`), - CONSTRAINT `fk_material_category` FOREIGN KEY (`category_id`) REFERENCES `inv_material_category` (`id`) ON DELETE SET NULL ON UPDATE CASCADE -) ENGINE=InnoDB AUTO_INCREMENT=4181 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='物料表'; +-- ======================================================== +-- 4. 物料管理模块 +-- ======================================================== -- 物料分类表 CREATE TABLE `inv_material_category` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '分类ID', - `parent_id` bigint unsigned DEFAULT NULL COMMENT '父分类ID, NULL为顶级分类', - `category_code` varchar(50) NOT NULL COMMENT '分类编码', - `category_name` varchar(100) NOT NULL COMMENT '分类名称', - `sort_order` int DEFAULT '0' COMMENT '排序序号', - `status` tinyint DEFAULT '1' COMMENT '状态: 0-禁用, 1-启用', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', - `deleted` tinyint NOT NULL DEFAULT '0' COMMENT '软删除: 0-正常, 1-已删除', - `create_by` bigint unsigned DEFAULT NULL COMMENT '创建人ID', - `update_by` bigint unsigned DEFAULT NULL COMMENT '更新人ID', + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '分类ID', + `parent_id` BIGINT UNSIGNED DEFAULT 0 COMMENT '父分类ID', + `category_code` VARCHAR(50) NOT NULL COMMENT '分类编码', + `category_name` VARCHAR(100) NOT NULL COMMENT '分类名称', + `sort_order` INT DEFAULT 0 COMMENT '排序序号', + `status` TINYINT DEFAULT 1 COMMENT '状态: 0-禁用, 1-启用', + `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `deleted` TINYINT DEFAULT 0 COMMENT '删除标记', PRIMARY KEY (`id`), UNIQUE KEY `uk_category_code` (`category_code`), - KEY `idx_parent` (`parent_id`), - CONSTRAINT `fk_material_category_parent` FOREIGN KEY (`parent_id`) REFERENCES `inv_material_category` (`id`) ON DELETE RESTRICT ON UPDATE CASCADE -) ENGINE=InnoDB AUTO_INCREMENT=199 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='物料分类表'; - --- 原料库存表 -CREATE TABLE `inv_material_inventory` ( - `id` int NOT NULL AUTO_INCREMENT, - `material_code` varchar(100) DEFAULT NULL, - `material_name` varchar(255) DEFAULT NULL, - `specification` varchar(255) DEFAULT NULL, - `category` varchar(100) DEFAULT NULL, - `material_type` varchar(50) DEFAULT NULL, - `unit` varchar(50) DEFAULT NULL, - `warehouse` varchar(100) DEFAULT NULL, - `location` varchar(100) DEFAULT NULL, - `supplier` varchar(255) DEFAULT NULL, - `opening_balance` decimal(18,4) DEFAULT '0.0000', - `total_in` decimal(18,4) DEFAULT '0.0000', - `total_out` decimal(18,4) DEFAULT '0.0000', - `current_balance` decimal(18,4) DEFAULT '0.0000', - `safety_stock` decimal(18,4) DEFAULT '0.0000', - `source_file` varchar(255) DEFAULT NULL, - `source_sheet` varchar(100) DEFAULT NULL, - `import_time` datetime DEFAULT CURRENT_TIMESTAMP, - `remarks` text, - PRIMARY KEY (`id`), - KEY `idx_material_code` (`material_code`), - KEY `idx_material_name` (`material_name`), - KEY `idx_category` (`category`), - KEY `idx_supplier` (`supplier`) -) ENGINE=InnoDB AUTO_INCREMENT=1848 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='原料库存表'; - --- 物料标签表 -CREATE TABLE `inv_material_label` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT, - `label_no` varchar(50) NOT NULL COMMENT '标签编号', - `qr_code` varchar(255) DEFAULT NULL COMMENT '二维码内容', - `purchase_order_no` varchar(50) DEFAULT NULL COMMENT '采购单号', - `supplier_name` varchar(200) DEFAULT NULL COMMENT '供应商名称', - `receive_date` date DEFAULT NULL COMMENT '进料日期', - `material_code` varchar(50) NOT NULL COMMENT '物料代号', - `material_name` varchar(200) DEFAULT NULL COMMENT '品名', - `specification` varchar(200) DEFAULT NULL COMMENT '进料规格', - `unit` varchar(20) DEFAULT NULL COMMENT '单位', - `batch_no` varchar(50) DEFAULT NULL COMMENT '批号', - `quantity` decimal(18,4) DEFAULT '0.0000' COMMENT '数量', - `package_qty` decimal(18,4) DEFAULT '0.0000' COMMENT '包装量', - `width` decimal(18,2) DEFAULT NULL COMMENT '宽幅', - `length_per_roll` decimal(18,2) DEFAULT NULL COMMENT '每卷米数', - `remark` varchar(500) DEFAULT NULL COMMENT '备注', - `color_code` varchar(50) DEFAULT NULL COMMENT '颜色代号', - `mix_remark` varchar(500) DEFAULT NULL COMMENT '混料备注', - `warehouse_id` bigint unsigned DEFAULT NULL COMMENT '仓库ID', - `location_id` bigint unsigned DEFAULT NULL COMMENT '库位ID', - `is_main_material` tinyint DEFAULT '0' COMMENT '是否母材: 0-否, 1-是', - `is_used` tinyint DEFAULT '0' COMMENT '是否已使用: 0-否, 1-是', - `is_cut` tinyint DEFAULT '0' COMMENT '是否已分切: 0-否, 1-是', - `parent_label_id` bigint unsigned DEFAULT NULL COMMENT '父标签ID(分切来源)', - `label_type` tinyint DEFAULT '1' COMMENT '标签类型: 1-原材料, 2-分切子批, 3-余料', - `remaining_width` decimal(18,2) DEFAULT NULL COMMENT '剩余宽幅(余料)', - `remaining_length` decimal(18,2) DEFAULT NULL COMMENT '剩余长度(余料)', - `status` tinyint DEFAULT '1' COMMENT '状态: 0-禁用, 1-启用, 2-冻结, 3-已过期', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP, - `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - `deleted` tinyint DEFAULT '0', - PRIMARY KEY (`id`), - UNIQUE KEY `uk_label_no` (`label_no`), - KEY `idx_material_code` (`material_code`), - KEY `idx_batch_no` (`batch_no`), - KEY `idx_warehouse` (`warehouse_id`), - KEY `idx_parent_label` (`parent_label_id`), - KEY `idx_label_type` (`label_type`) -) ENGINE=InnoDB AUTO_INCREMENT=46 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='物料标签表'; + KEY `idx_parent` (`parent_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='物料分类表'; --- 标准物料主档 -CREATE TABLE `inv_material_std` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '物料ID', - `material_code` varchar(50) NOT NULL COMMENT '物料编码', - `material_name` varchar(100) NOT NULL COMMENT '物料名称', - `material_spec` varchar(200) DEFAULT NULL COMMENT '规格型号', - `unit` varchar(20) NOT NULL COMMENT '计量单位', - `material_type` tinyint NOT NULL DEFAULT '1' COMMENT '1原材料 2半成品 3成品 4辅料 5包材', - `category_id` bigint unsigned DEFAULT NULL COMMENT '分类ID', - `is_batch` tinyint NOT NULL DEFAULT '1' COMMENT '是否批次管理', - `is_expire` tinyint NOT NULL DEFAULT '0' COMMENT '是否效期管理', - `safe_stock` decimal(18,4) NOT NULL DEFAULT '0.0000' COMMENT '安全库存', - `standard_cost` decimal(18,4) DEFAULT '0.0000' COMMENT '标准成本', - `shelf_life_days` int DEFAULT NULL COMMENT '保质期天数', - `status` tinyint NOT NULL DEFAULT '1' COMMENT '0禁用 1启用(route.ts 遗漏,测试与生产查询需要)', - `remark` text COMMENT '备注', - `legacy_source` varchar(30) DEFAULT NULL COMMENT '旧表来源: inv_material/bom_material/mdm_material', - `legacy_id` bigint unsigned DEFAULT NULL COMMENT '旧表原始ID', - `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', - `create_by` bigint unsigned DEFAULT NULL COMMENT '创建人ID', - `update_by` bigint unsigned DEFAULT NULL COMMENT '更新人ID', - `deleted` tinyint NOT NULL DEFAULT '0' COMMENT '是否删除', +-- 物料表 +CREATE TABLE `inv_material` ( + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '物料ID', + `material_code` VARCHAR(50) NOT NULL COMMENT '物料编码', + `material_name` VARCHAR(100) NOT NULL COMMENT '物料名称', + `specification` VARCHAR(255) COMMENT '规格型号', + `category_id` BIGINT UNSIGNED COMMENT '分类ID', + `material_type` TINYINT COMMENT '物料类型: 1-原材料, 2-半成品, 3-成品, 4-辅料, 5-包材', + `unit` VARCHAR(20) COMMENT '计量单位', + `barcode` VARCHAR(50) COMMENT '条形码', + `brand` VARCHAR(50) COMMENT '品牌', + `safety_stock` DECIMAL(18,4) DEFAULT 0 COMMENT '安全库存', + `max_stock` DECIMAL(18,4) COMMENT '最大库存', + `min_stock` DECIMAL(18,4) COMMENT '最小库存', + `purchase_price` DECIMAL(18,4) COMMENT '采购单价', + `sale_price` DECIMAL(18,4) COMMENT '销售单价', + `cost_price` DECIMAL(18,4) COMMENT '成本单价', + `warehouse_id` BIGINT UNSIGNED COMMENT '默认仓库ID', + `shelf_life` INT COMMENT '保质期(天)', + `warning_days` INT COMMENT '预警天数', + `is_batch_managed` TINYINT DEFAULT 0 COMMENT '是否批次管理: 0-否, 1-是', + `is_serial_managed` TINYINT DEFAULT 0 COMMENT '是否序列号管理: 0-否, 1-是', + `status` TINYINT DEFAULT 1 COMMENT '状态: 0-禁用, 1-启用', + `remark` TEXT COMMENT '备注', + `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `create_by` BIGINT UNSIGNED COMMENT '创建人ID', + `update_by` BIGINT UNSIGNED COMMENT '更新人ID', + `deleted` TINYINT DEFAULT 0 COMMENT '删除标记', PRIMARY KEY (`id`), UNIQUE KEY `uk_material_code` (`material_code`), - KEY `idx_material_type` (`material_type`), - KEY `idx_category_id` (`category_id`), - KEY `idx_status` (`status`), - KEY `idx_legacy` (`legacy_source`,`legacy_id`) -) ENGINE=InnoDB AUTO_INCREMENT=65 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='标准物料主档'; - --- 出库单明细表 -CREATE TABLE `inv_outbound_item` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT, - `order_id` bigint unsigned NOT NULL COMMENT '出库单ID', - `material_id` bigint unsigned NOT NULL COMMENT '物料ID', - `material_name` varchar(100) DEFAULT NULL COMMENT '物料名称', - `material_spec` varchar(255) DEFAULT NULL COMMENT '规格型号', - `quantity` decimal(18,4) NOT NULL COMMENT '出库数量', - `unit` varchar(20) DEFAULT NULL COMMENT '单位', - `unit_price` decimal(18,4) DEFAULT NULL COMMENT '单价', - `amount` decimal(18,4) DEFAULT NULL COMMENT '金额', - `batch_no` varchar(50) DEFAULT NULL COMMENT '批次号', - `remark` varchar(255) DEFAULT NULL COMMENT '备注', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP, - `deleted` tinyint NOT NULL DEFAULT '0' COMMENT '软删除: 0-正常, 1-已删除', - PRIMARY KEY (`id`), - KEY `idx_order` (`order_id`), - KEY `idx_material` (`material_id`), - CONSTRAINT `fk_inv_outbound_item_material` FOREIGN KEY (`material_id`) REFERENCES `inv_material` (`id`) ON DELETE RESTRICT ON UPDATE CASCADE, - CONSTRAINT `fk_inv_outbound_item_order` FOREIGN KEY (`order_id`) REFERENCES `inv_outbound_order` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, - CONSTRAINT `fk_outbound_item_order` FOREIGN KEY (`order_id`) REFERENCES `inv_outbound_order` (`id`) ON DELETE CASCADE ON UPDATE CASCADE -) ENGINE=InnoDB AUTO_INCREMENT=136 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='出库单明细表'; - --- 出库单表 -CREATE TABLE `inv_outbound_order` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT, - `order_no` varchar(50) NOT NULL COMMENT '出库单号', - `order_date` date DEFAULT NULL COMMENT '出库日期', - `outbound_type` varchar(20) DEFAULT 'sale' COMMENT '出库类型: sale/transfer/production/other', - `warehouse_id` bigint unsigned DEFAULT NULL COMMENT '仓库ID', - `warehouse_code` varchar(50) DEFAULT NULL COMMENT '仓库编码', - `warehouse_name` varchar(100) DEFAULT NULL COMMENT '仓库名称', - `total_qty` decimal(18,4) DEFAULT '0.0000' COMMENT '总数量', - `total_amount` decimal(18,4) DEFAULT '0.0000' COMMENT '总金额', - `currency` varchar(10) DEFAULT 'CNY' COMMENT '币种', - `status` varchar(20) DEFAULT 'draft' COMMENT '状态: draft/pending/approved/completed/cancelled', - `remark` text COMMENT '备注', - `operator_name` varchar(50) DEFAULT NULL COMMENT '操作人', - `operator_id` bigint unsigned DEFAULT NULL COMMENT '操作员ID', - `audit_status` varchar(20) DEFAULT 'pending' COMMENT '审核状态', - `auditor_name` varchar(50) DEFAULT NULL COMMENT '审核人', - `audit_time` datetime DEFAULT NULL COMMENT '审核时间', - `create_by` bigint unsigned DEFAULT NULL, - `create_time` datetime DEFAULT CURRENT_TIMESTAMP, - `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - `deleted` tinyint NOT NULL DEFAULT '0' COMMENT '软删除: 0-正常, 1-已删除', - `version` int DEFAULT '0' COMMENT '乐观锁版本号', - PRIMARY KEY (`id`), - UNIQUE KEY `uk_order_no` (`order_no`), - KEY `idx_warehouse` (`warehouse_id`), - KEY `idx_status` (`status`), - KEY `idx_operator` (`operator_id`), - CONSTRAINT `fk_inv_outbound_operator` FOREIGN KEY (`operator_id`) REFERENCES `sys_user` (`id`) ON DELETE SET NULL ON UPDATE CASCADE, - CONSTRAINT `fk_inv_outbound_order_warehouse` FOREIGN KEY (`warehouse_id`) REFERENCES `inv_warehouse` (`id`) ON DELETE RESTRICT ON UPDATE CASCADE, - CONSTRAINT `fk_inv_outbound_warehouse` FOREIGN KEY (`warehouse_id`) REFERENCES `inv_warehouse` (`id`) ON DELETE RESTRICT ON UPDATE CASCADE -) ENGINE=InnoDB AUTO_INCREMENT=201 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='出库单表'; - --- 成品库存表 -CREATE TABLE `inv_product_inventory` ( - `id` int NOT NULL AUTO_INCREMENT, - `product_code` varchar(100) DEFAULT NULL, - `product_name` varchar(255) DEFAULT NULL, - `specification` varchar(255) DEFAULT NULL, - `category` varchar(100) DEFAULT NULL, - `customer` varchar(255) DEFAULT NULL, - `unit` varchar(50) DEFAULT NULL, - `warehouse` varchar(100) DEFAULT NULL, - `location` varchar(100) DEFAULT NULL, - `opening_balance` decimal(18,4) DEFAULT '0.0000', - `total_in` decimal(18,4) DEFAULT '0.0000', - `total_out` decimal(18,4) DEFAULT '0.0000', - `current_balance` decimal(18,4) DEFAULT '0.0000', - `batch_no` varchar(100) DEFAULT NULL, - `supplier` varchar(255) DEFAULT NULL, - `source_file` varchar(255) DEFAULT NULL, - `source_sheet` varchar(100) DEFAULT NULL, - `import_time` datetime DEFAULT CURRENT_TIMESTAMP, - `remarks` text, - PRIMARY KEY (`id`), - KEY `idx_product_code` (`product_code`), - KEY `idx_product_name` (`product_name`), - KEY `idx_category` (`category`), - KEY `idx_warehouse` (`warehouse`) -) ENGINE=InnoDB AUTO_INCREMENT=5604 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='成品库存表'; - --- 生产入库单 -CREATE TABLE `inv_production_inbound` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT, - `inbound_no` varchar(50) NOT NULL COMMENT '入库单号', - `work_order_id` bigint unsigned DEFAULT NULL COMMENT '工单ID', - `work_order_no` varchar(50) DEFAULT NULL COMMENT '工单号', - `warehouse_id` bigint unsigned DEFAULT NULL COMMENT '仓库ID', - `inbound_date` date DEFAULT NULL COMMENT '入库日期', - `operator_name` varchar(50) DEFAULT NULL COMMENT '操作人', - `qc_status` varchar(20) DEFAULT 'pass' COMMENT '质检状态: pass/fail/pending', - `status` tinyint DEFAULT '1' COMMENT '状态: 1-待入库, 2-入库中, 3-已完成', - `remark` varchar(500) DEFAULT NULL COMMENT '备注', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP, - `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - `deleted` tinyint NOT NULL DEFAULT '0' COMMENT '软删除: 0-正常, 1-已删除', - `create_by` bigint unsigned DEFAULT NULL COMMENT '创建人ID', - `update_by` bigint unsigned DEFAULT NULL COMMENT '更新人ID', - PRIMARY KEY (`id`), - UNIQUE KEY `uk_inbound_no` (`inbound_no`), - KEY `idx_work_order_id` (`work_order_id`), - KEY `idx_warehouse_id` (`warehouse_id`), - CONSTRAINT `fk_inv_prod_inbound_warehouse` FOREIGN KEY (`warehouse_id`) REFERENCES `inv_warehouse` (`id`) ON DELETE RESTRICT ON UPDATE CASCADE, - CONSTRAINT `fk_inv_prod_inbound_workorder` FOREIGN KEY (`work_order_id`) REFERENCES `prd_work_order` (`id`) ON DELETE SET NULL ON UPDATE CASCADE -) ENGINE=InnoDB AUTO_INCREMENT=15 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='生产入库单'; - --- 生产入库单明细 -CREATE TABLE `inv_production_inbound_item` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT, - `inbound_id` bigint unsigned NOT NULL COMMENT '入库单ID', - `material_id` bigint unsigned DEFAULT NULL COMMENT '物料ID', - `material_code` varchar(50) DEFAULT NULL COMMENT '物料编码', - `material_name` varchar(100) DEFAULT NULL COMMENT '物料名称', - `quantity` decimal(18,3) NOT NULL COMMENT '数量', - `unit` varchar(20) DEFAULT NULL COMMENT '单位', - `batch_no` varchar(50) DEFAULT NULL COMMENT '批次号', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP, - PRIMARY KEY (`id`), - KEY `idx_inbound_id` (`inbound_id`), - KEY `fk_inv_prod_inbound_item_material` (`material_id`), - CONSTRAINT `fk_inv_prod_inbound_item_inbound` FOREIGN KEY (`inbound_id`) REFERENCES `inv_production_inbound` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, - CONSTRAINT `fk_inv_prod_inbound_item_material` FOREIGN KEY (`material_id`) REFERENCES `inv_material` (`id`) ON DELETE RESTRICT ON UPDATE CASCADE -) ENGINE=InnoDB AUTO_INCREMENT=15 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='生产入库单明细'; - --- 销售出库单 -CREATE TABLE `inv_sales_outbound` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT, - `outbound_no` varchar(50) NOT NULL COMMENT '出库单号', - `order_id` bigint unsigned DEFAULT NULL COMMENT '销售订单ID', - `order_no` varchar(50) DEFAULT NULL COMMENT '销售订单号', - `customer_id` bigint unsigned DEFAULT NULL COMMENT '客户ID', - `customer_name` varchar(100) DEFAULT NULL COMMENT '客户名称', - `warehouse_id` bigint unsigned DEFAULT NULL COMMENT '仓库ID', - `outbound_date` date DEFAULT NULL COMMENT '出库日期', - `delivery_person` varchar(50) DEFAULT NULL COMMENT '发货人', - `status` tinyint DEFAULT '1' COMMENT '状态: 1-待出库, 2-出库中, 3-已完成', - `finance_posted` tinyint DEFAULT '0' COMMENT '财务过账: 0-未过账, 1-已过账', - `remark` varchar(500) DEFAULT NULL COMMENT '备注', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP, - `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - `deleted` tinyint NOT NULL DEFAULT '0' COMMENT '软删除: 0-正常, 1-已删除', - `create_by` bigint unsigned DEFAULT NULL COMMENT '创建人ID', - `update_by` bigint unsigned DEFAULT NULL COMMENT '更新人ID', - PRIMARY KEY (`id`), - UNIQUE KEY `uk_outbound_no` (`outbound_no`), - KEY `idx_order_id` (`order_id`), - KEY `idx_warehouse_id` (`warehouse_id`), - KEY `idx_customer` (`customer_id`), - CONSTRAINT `fk_inv_sales_outbound_customer` FOREIGN KEY (`customer_id`) REFERENCES `crm_customer` (`id`) ON DELETE SET NULL ON UPDATE CASCADE, - CONSTRAINT `fk_inv_sales_outbound_order` FOREIGN KEY (`order_id`) REFERENCES `sal_order` (`id`) ON DELETE SET NULL ON UPDATE CASCADE, - CONSTRAINT `fk_inv_sales_outbound_warehouse` FOREIGN KEY (`warehouse_id`) REFERENCES `inv_warehouse` (`id`) ON DELETE RESTRICT ON UPDATE CASCADE -) ENGINE=InnoDB AUTO_INCREMENT=22 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='销售出库单'; - --- 销售出库单明细 -CREATE TABLE `inv_sales_outbound_item` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT, - `outbound_id` bigint unsigned NOT NULL COMMENT '出库单ID', - `material_id` bigint unsigned DEFAULT NULL COMMENT '物料ID', - `material_code` varchar(50) DEFAULT NULL COMMENT '物料编码', - `material_name` varchar(100) DEFAULT NULL COMMENT '物料名称', - `quantity` decimal(18,3) NOT NULL COMMENT '数量', - `unit` varchar(20) DEFAULT NULL COMMENT '单位', - `batch_no` varchar(50) DEFAULT NULL COMMENT '批次号', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP, - PRIMARY KEY (`id`), - KEY `idx_outbound_id` (`outbound_id`), - KEY `fk_inv_sales_outbound_item_material` (`material_id`), - CONSTRAINT `fk_inv_sales_outbound_item_material` FOREIGN KEY (`material_id`) REFERENCES `inv_material` (`id`) ON DELETE RESTRICT ON UPDATE CASCADE, - CONSTRAINT `fk_inv_sales_outbound_item_outbound` FOREIGN KEY (`outbound_id`) REFERENCES `inv_sales_outbound` (`id`) ON DELETE CASCADE ON UPDATE CASCADE -) ENGINE=InnoDB AUTO_INCREMENT=18 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='销售出库单明细'; - --- 扫码操作日志表 -CREATE TABLE `inv_scan_log` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT, - `scan_type` varchar(50) NOT NULL COMMENT '扫码类型: cutting-分切, process-流程卡, trace-追溯', - `qr_content` varchar(500) DEFAULT NULL COMMENT '二维码内容', - `label_no` varchar(50) DEFAULT NULL COMMENT '标签编号', - `operation` varchar(50) DEFAULT NULL COMMENT '操作类型', - `result` tinyint DEFAULT '1' COMMENT '结果: 0-失败, 1-成功', - `message` varchar(500) DEFAULT NULL COMMENT '结果消息', - `operator_id` bigint unsigned DEFAULT NULL COMMENT '操作员ID', - `operator_name` varchar(50) DEFAULT NULL COMMENT '操作员名称', - `scan_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '扫码时间', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP, - PRIMARY KEY (`id`), - KEY `idx_scan_type` (`scan_type`), - KEY `idx_label_no` (`label_no`), - KEY `idx_scan_time` (`scan_time`) -) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='扫码操作日志表'; - --- 库存调整单主表 -CREATE TABLE `inv_stock_adjust` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '调整单ID', - `adjust_no` varchar(30) NOT NULL COMMENT '调整单号', - `warehouse_id` bigint unsigned NOT NULL COMMENT '仓库ID', - `adjust_date` date NOT NULL COMMENT '调整日期', - `adjust_type` tinyint NOT NULL DEFAULT '1' COMMENT '调整类型: 1-盘盈, 2-盘亏, 3-其他', - `operator_id` bigint unsigned DEFAULT NULL COMMENT '操作人ID', - `operator_name` varchar(50) DEFAULT NULL COMMENT '操作人姓名', - `approver_id` bigint unsigned DEFAULT NULL COMMENT '审批人ID', - `approver_name` varchar(50) DEFAULT NULL COMMENT '审批人姓名', - `approve_time` datetime DEFAULT NULL COMMENT '审批时间', - `status` tinyint DEFAULT '0' COMMENT '0-待审, 1-已审, 2-已驳回', - `total_qty` decimal(18,4) DEFAULT '0.0000' COMMENT '总数量', - `total_amount` decimal(18,4) DEFAULT '0.0000' COMMENT '总金额', - `version` int DEFAULT '0' COMMENT '乐观锁版本号', - `remark` varchar(500) DEFAULT NULL COMMENT '备注', - `deleted` tinyint NOT NULL DEFAULT '0' COMMENT '软删除: 0-正常, 1-已删除', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', - `create_by` bigint unsigned DEFAULT NULL COMMENT '创建人ID', - `update_by` bigint unsigned DEFAULT NULL COMMENT '更新人ID', - PRIMARY KEY (`id`), - UNIQUE KEY `uk_adjust_no` (`adjust_no`), - KEY `idx_status` (`status`), - KEY `idx_warehouse` (`warehouse_id`), - KEY `idx_operator` (`operator_id`), - KEY `idx_approver` (`approver_id`), - CONSTRAINT `fk_inv_adjust_approver` FOREIGN KEY (`approver_id`) REFERENCES `sys_user` (`id`) ON DELETE SET NULL ON UPDATE CASCADE, - CONSTRAINT `fk_inv_adjust_operator` FOREIGN KEY (`operator_id`) REFERENCES `sys_user` (`id`) ON DELETE SET NULL ON UPDATE CASCADE, - CONSTRAINT `fk_inv_adjust_warehouse` FOREIGN KEY (`warehouse_id`) REFERENCES `inv_warehouse` (`id`) ON DELETE RESTRICT ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='库存调整单主表'; - --- 库存调整单明细表 -CREATE TABLE `inv_stock_adjust_item` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '明细ID', - `adjust_id` bigint unsigned NOT NULL COMMENT '调整单ID', - `material_id` bigint unsigned NOT NULL COMMENT '物料ID', - `material_code` varchar(50) DEFAULT NULL COMMENT '物料编码(冗余)', - `material_name` varchar(200) DEFAULT NULL COMMENT '物料名称(冗余)', - `batch_no` varchar(50) DEFAULT NULL COMMENT '批次号', - `before_qty` decimal(18,4) DEFAULT '0.0000' COMMENT '调整前数量', - `adjust_qty` decimal(18,4) NOT NULL COMMENT '调整数量(正/负)', - `after_qty` decimal(18,4) DEFAULT '0.0000' COMMENT '调整后数量', - `unit` varchar(20) DEFAULT NULL COMMENT '单位', - `unit_price` decimal(18,4) DEFAULT '0.0000' COMMENT '单价', - `amount` decimal(18,4) DEFAULT '0.0000' COMMENT '金额', - `reason` varchar(255) DEFAULT NULL COMMENT '调整原因', - `deleted` tinyint NOT NULL DEFAULT '0' COMMENT '软删除: 0-正常, 1-已删除', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - PRIMARY KEY (`id`), - KEY `idx_adjust` (`adjust_id`), - KEY `idx_material` (`material_id`), - CONSTRAINT `fk_inv_adjust_item_adjust` FOREIGN KEY (`adjust_id`) REFERENCES `inv_stock_adjust` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, - CONSTRAINT `fk_inv_adjust_item_material` FOREIGN KEY (`material_id`) REFERENCES `inv_material` (`id`) ON DELETE RESTRICT ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='库存调整单明细表'; - --- 盘点单主表 -CREATE TABLE `inv_stocktaking` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT, - `taking_no` varchar(50) NOT NULL COMMENT '盘点单号', - `taking_type` tinyint DEFAULT '1' COMMENT '盘点类型: 1-全盘, 2-部分盘', - `warehouse_id` bigint unsigned NOT NULL COMMENT '仓库ID', - `status` tinyint DEFAULT '1' COMMENT '状态: 1-待盘点, 2-盘点中, 3-待审批, 4-已审批, 9-已取消', - `taking_date` date DEFAULT NULL COMMENT '盘点日期', - `operator_id` bigint unsigned DEFAULT NULL COMMENT '操作员ID', - `operator_name` varchar(50) DEFAULT NULL COMMENT '操作员姓名', - `remark` varchar(500) DEFAULT NULL COMMENT '备注', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP, - `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - `deleted` tinyint NOT NULL DEFAULT '0' COMMENT '软删除: 0-正常, 1-已删除', - `create_by` bigint unsigned DEFAULT NULL COMMENT '创建人ID', - `update_by` bigint unsigned DEFAULT NULL COMMENT '更新人ID', - PRIMARY KEY (`id`), - UNIQUE KEY `uk_taking_no` (`taking_no`), - KEY `idx_warehouse` (`warehouse_id`), - KEY `idx_status` (`status`), - CONSTRAINT `fk_inv_stocktaking_warehouse` FOREIGN KEY (`warehouse_id`) REFERENCES `inv_warehouse` (`id`) ON DELETE RESTRICT ON UPDATE CASCADE -) ENGINE=InnoDB AUTO_INCREMENT=105 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='盘点单主表'; - --- 盘点单明细表 -CREATE TABLE `inv_stocktaking_item` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT, - `taking_id` bigint unsigned NOT NULL COMMENT '盘点单ID', - `material_id` bigint unsigned NOT NULL COMMENT '物料ID', - `material_code` varchar(50) DEFAULT NULL COMMENT '物料编码', - `material_name` varchar(100) DEFAULT NULL COMMENT '物料名称', - `system_qty` decimal(18,4) DEFAULT '0.0000' COMMENT '系统数量', - `actual_qty` decimal(18,4) DEFAULT '0.0000' COMMENT '实盘数量', - `diff_qty` decimal(18,4) DEFAULT '0.0000' COMMENT '差异数量', - `unit` varchar(20) DEFAULT NULL COMMENT '单位', - `batch_no` varchar(50) DEFAULT NULL COMMENT '批次号', - `remark` varchar(255) DEFAULT NULL COMMENT '备注', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP, - `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - PRIMARY KEY (`id`), - KEY `idx_taking` (`taking_id`), - KEY `idx_material` (`material_id`), - CONSTRAINT `fk_inv_stocktaking_item_material` FOREIGN KEY (`material_id`) REFERENCES `inv_material` (`id`) ON DELETE RESTRICT ON UPDATE CASCADE, - CONSTRAINT `fk_stocktaking_item_taking` FOREIGN KEY (`taking_id`) REFERENCES `inv_stocktaking` (`id`) ON DELETE CASCADE ON UPDATE CASCADE -) ENGINE=InnoDB AUTO_INCREMENT=105 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='盘点单明细表'; - --- 追溯明细表 -CREATE TABLE `inv_trace_detail` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT, - `trace_id` bigint unsigned NOT NULL COMMENT '追溯记录ID', - `label_id` bigint unsigned NOT NULL COMMENT '物料标签ID', - `label_no` varchar(50) NOT NULL COMMENT '物料标签编号', - `material_code` varchar(50) DEFAULT NULL COMMENT '物料代号', - `material_name` varchar(200) DEFAULT NULL COMMENT '物料名称', - `specification` varchar(200) DEFAULT NULL COMMENT '规格', - `batch_no` varchar(50) DEFAULT NULL COMMENT '批号', - `supplier_name` varchar(200) DEFAULT NULL COMMENT '供应商名称', - `receive_date` date DEFAULT NULL COMMENT '进料日期', - `material_type` tinyint DEFAULT '2' COMMENT '物料类型: 1-主材, 2-辅料', - `remark` varchar(500) DEFAULT NULL COMMENT '备注', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP, - PRIMARY KEY (`id`), - KEY `idx_trace_id` (`trace_id`), - KEY `idx_label_id` (`label_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='追溯明细表'; - --- 追溯记录表 -CREATE TABLE `inv_trace_record` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT, - `trace_no` varchar(50) NOT NULL COMMENT '追溯单号', - `card_id` bigint unsigned DEFAULT NULL COMMENT '流程卡ID', - `card_no` varchar(50) DEFAULT NULL COMMENT '流程卡卡号', - `work_order_no` varchar(50) DEFAULT NULL COMMENT '工单号', - `product_code` varchar(50) DEFAULT NULL COMMENT '成品料号', - `main_label_id` bigint unsigned DEFAULT NULL COMMENT '主材标签ID', - `trace_type` tinyint DEFAULT '1' COMMENT '追溯类型: 1-正向追溯, 2-反向追溯', - `operator_id` bigint unsigned DEFAULT NULL COMMENT '操作员ID', - `operator_name` varchar(50) DEFAULT NULL COMMENT '操作员名称', - `trace_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '追溯时间', - `remark` varchar(500) DEFAULT NULL COMMENT '备注', - `deleted` tinyint DEFAULT '0', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP, - PRIMARY KEY (`id`), - UNIQUE KEY `uk_trace_no` (`trace_no`), - KEY `idx_card_id` (`card_id`), - KEY `idx_main_label` (`main_label_id`), - KEY `idx_deleted` (`deleted`) -) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='追溯记录表'; - --- 调拨单明细表 -CREATE TABLE `inv_transfer_item` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '明细ID', - `transfer_id` bigint unsigned NOT NULL COMMENT '调拨单ID', - `material_id` bigint unsigned NOT NULL COMMENT '物料ID', - `material_code` varchar(50) DEFAULT NULL COMMENT '物料编码(冗余)', - `material_name` varchar(200) DEFAULT NULL COMMENT '物料名称(冗余)', - `qr_code` varchar(50) DEFAULT NULL COMMENT '二维码', - `batch_no` varchar(50) DEFAULT NULL COMMENT '批次号', - `quantity` decimal(18,4) NOT NULL COMMENT '申请数量', - `out_quantity` decimal(18,4) DEFAULT '0.0000' COMMENT '实际出库数量', - `in_quantity` decimal(18,4) DEFAULT '0.0000' COMMENT '实际入库数量', - `unit` varchar(20) DEFAULT NULL COMMENT '单位', - `unit_price` decimal(18,4) DEFAULT '0.0000' COMMENT '单价', - `amount` decimal(18,4) DEFAULT '0.0000' COMMENT '金额', - `remark` varchar(255) DEFAULT NULL COMMENT '备注', - `deleted` tinyint NOT NULL DEFAULT '0' COMMENT '软删除: 0-正常, 1-已删除', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - PRIMARY KEY (`id`), - KEY `idx_transfer` (`transfer_id`), - KEY `idx_material` (`material_id`), - KEY `idx_batch` (`batch_no`), - CONSTRAINT `fk_inv_transfer_item_material` FOREIGN KEY (`material_id`) REFERENCES `inv_material` (`id`) ON DELETE RESTRICT ON UPDATE CASCADE, - CONSTRAINT `fk_transfer_item_order` FOREIGN KEY (`transfer_id`) REFERENCES `inv_transfer_order` (`id`) ON DELETE CASCADE ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='调拨单明细表'; - --- 调拨单主表 -CREATE TABLE `inv_transfer_order` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '调拨单ID', - `transfer_no` varchar(30) NOT NULL COMMENT '调拨单号', - `type` tinyint NOT NULL COMMENT '1-库位调拨, 2-仓库调拨', - `from_warehouse_id` bigint unsigned NOT NULL COMMENT '源仓库ID', - `to_warehouse_id` bigint unsigned NOT NULL COMMENT '目标仓库ID', - `from_location` varchar(50) DEFAULT NULL COMMENT '源库位', - `to_location` varchar(50) DEFAULT NULL COMMENT '目标库位', - `status` tinyint NOT NULL DEFAULT '0' COMMENT '0-草稿, 1-待审批, 2-已出库, 3-已入库, 4-已取消', - `applicant_id` bigint unsigned DEFAULT NULL COMMENT '申请人ID', - `applicant_name` varchar(50) DEFAULT NULL COMMENT '申请人姓名', - `approver_id` bigint unsigned DEFAULT NULL COMMENT '审批人ID', - `approver_name` varchar(50) DEFAULT NULL COMMENT '审批人姓名', - `operator_id` bigint unsigned DEFAULT NULL COMMENT '操作人ID', - `operator_name` varchar(50) DEFAULT NULL COMMENT '操作人姓名', - `out_time` datetime DEFAULT NULL COMMENT '出库时间', - `in_time` datetime DEFAULT NULL COMMENT '入库时间', - `total_qty` decimal(18,4) DEFAULT '0.0000' COMMENT '总数量', - `total_amount` decimal(18,4) DEFAULT '0.0000' COMMENT '总金额', - `version` int DEFAULT '0' COMMENT '乐观锁版本号', - `remark` varchar(500) DEFAULT NULL COMMENT '备注', - `deleted` tinyint NOT NULL DEFAULT '0' COMMENT '软删除: 0-正常, 1-已删除', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', - `create_by` bigint unsigned DEFAULT NULL COMMENT '创建人ID', - `update_by` bigint unsigned DEFAULT NULL COMMENT '更新人ID', - PRIMARY KEY (`id`), - UNIQUE KEY `uk_transfer_no` (`transfer_no`), - KEY `idx_status` (`status`), - KEY `idx_from_warehouse` (`from_warehouse_id`), - KEY `idx_to_warehouse` (`to_warehouse_id`), - KEY `idx_operator` (`operator_id`), - KEY `idx_applicant` (`applicant_id`), - KEY `idx_approver` (`approver_id`), - CONSTRAINT `fk_inv_transfer_applicant` FOREIGN KEY (`applicant_id`) REFERENCES `sys_user` (`id`) ON DELETE SET NULL ON UPDATE CASCADE, - CONSTRAINT `fk_inv_transfer_approver` FOREIGN KEY (`approver_id`) REFERENCES `sys_user` (`id`) ON DELETE SET NULL ON UPDATE CASCADE, - CONSTRAINT `fk_inv_transfer_from_wh` FOREIGN KEY (`from_warehouse_id`) REFERENCES `inv_warehouse` (`id`) ON DELETE RESTRICT ON UPDATE CASCADE, - CONSTRAINT `fk_inv_transfer_operator` FOREIGN KEY (`operator_id`) REFERENCES `sys_user` (`id`) ON DELETE SET NULL ON UPDATE CASCADE, - CONSTRAINT `fk_inv_transfer_to_wh` FOREIGN KEY (`to_warehouse_id`) REFERENCES `inv_warehouse` (`id`) ON DELETE RESTRICT ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='调拨单主表'; - --- 单位换算表 -CREATE TABLE `inv_unit_conversion` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '换算ID', - `material_id` bigint unsigned NOT NULL COMMENT '物料ID', - `from_unit` varchar(20) NOT NULL COMMENT '源单位', - `to_unit` varchar(20) NOT NULL COMMENT '目标单位', - `ratio` decimal(18,4) NOT NULL COMMENT '换算比率: from_unit * ratio = to_unit', - `is_default` tinyint DEFAULT '0' COMMENT '是否默认换算: 0-否, 1-是', - `remark` varchar(255) DEFAULT NULL COMMENT '备注', - `deleted` tinyint NOT NULL DEFAULT '0' COMMENT '软删除: 0-正常, 1-已删除', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', - `create_by` bigint unsigned DEFAULT NULL COMMENT '创建人ID', - `update_by` bigint unsigned DEFAULT NULL COMMENT '更新人ID', - PRIMARY KEY (`id`), - UNIQUE KEY `uk_material_units` (`material_id`,`from_unit`,`to_unit`), - KEY `idx_material` (`material_id`), - CONSTRAINT `fk_inv_unit_conv_material` FOREIGN KEY (`material_id`) REFERENCES `inv_material` (`id`) ON DELETE RESTRICT ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='单位换算表'; - --- 仓库表 -CREATE TABLE `inv_warehouse` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT, - `warehouse_code` varchar(50) NOT NULL COMMENT '仓库编码', - `warehouse_name` varchar(100) NOT NULL COMMENT '仓库名称', - `warehouse_type` tinyint DEFAULT NULL COMMENT '仓库类型: 1-原材料仓, 2-半成品仓, 3-成品仓, 4-辅料仓', - `province` varchar(50) DEFAULT NULL COMMENT '省份', - `city` varchar(50) DEFAULT NULL COMMENT '城市', - `address` varchar(255) DEFAULT NULL COMMENT '详细地址', - `manager_id` bigint unsigned DEFAULT NULL COMMENT '仓库负责人ID', - `contact_phone` varchar(20) DEFAULT NULL COMMENT '联系电话', - `status` tinyint DEFAULT '1' COMMENT '状态: 0-禁用, 1-启用', - `remark` varchar(255) DEFAULT NULL COMMENT '备注', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP, - `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - `deleted` tinyint NOT NULL DEFAULT '0' COMMENT '软删除: 0-正常, 1-已删除', - `create_by` bigint unsigned DEFAULT NULL COMMENT '创建人ID', - `update_by` bigint unsigned DEFAULT NULL COMMENT '更新人ID', - PRIMARY KEY (`id`), - UNIQUE KEY `uk_warehouse_code` (`warehouse_code`), - KEY `idx_warehouse_status` (`status`,`deleted`), - KEY `idx_manager` (`manager_id`), - CONSTRAINT `fk_warehouse_manager` FOREIGN KEY (`manager_id`) REFERENCES `sys_user` (`id`) ON DELETE SET NULL ON UPDATE CASCADE -) ENGINE=InnoDB AUTO_INCREMENT=180 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='仓库表'; - --- 仓库操作日志表 -CREATE TABLE `inv_warehouse_log` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '日志ID', - `warehouse_id` bigint unsigned NOT NULL COMMENT '仓库ID', - `operation_type` varchar(30) NOT NULL COMMENT 'create-创建, update-更新, delete-删除, freeze-冻结, unfreeze-解冻', - `operation_content` text COMMENT '操作内容(JSON 或文本)', - `operator_id` bigint unsigned DEFAULT NULL COMMENT '操作人ID', - `operator_name` varchar(50) DEFAULT NULL COMMENT '操作人姓名', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - PRIMARY KEY (`id`), - KEY `idx_warehouse` (`warehouse_id`), - KEY `idx_create_time` (`create_time`), - KEY `idx_operator` (`operator_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='仓库操作日志表'; - --- 小料批次成本表 -CREATE TABLE `material_batch_costs` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '成本ID', - `qr_code` varchar(50) NOT NULL COMMENT '小料二维码编码', - `material_id` bigint unsigned NOT NULL COMMENT '物料ID', - `material_code` varchar(50) DEFAULT NULL COMMENT '物料编码', - `material_name` varchar(100) DEFAULT NULL COMMENT '物料名称', - `batch_no` varchar(50) DEFAULT NULL COMMENT '批次号', - `quantity` decimal(18,4) NOT NULL COMMENT '数量', - `unit_cost` decimal(18,4) NOT NULL COMMENT '单位成本', - `total_cost` decimal(18,4) NOT NULL COMMENT '总成本', - `used_quantity` decimal(18,4) DEFAULT '0.0000' COMMENT '已使用数量', - `remaining_quantity` decimal(18,4) DEFAULT '0.0000' COMMENT '剩余数量', - `split_flag` tinyint DEFAULT '1' COMMENT '拆分标记:0-整料,1-小料,2-余料', - `warehouse_id` bigint unsigned DEFAULT NULL COMMENT '仓库ID', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - `deleted` tinyint NOT NULL DEFAULT '0' COMMENT '软删除标记', - `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', - `create_by` bigint unsigned DEFAULT NULL COMMENT '创建人ID', - `update_by` bigint unsigned DEFAULT NULL COMMENT '更新人ID', - PRIMARY KEY (`id`), - UNIQUE KEY `uk_qr_code` (`qr_code`), - KEY `idx_material` (`material_id`), - KEY `idx_batch` (`batch_no`), - KEY `idx_warehouse` (`warehouse_id`), - CONSTRAINT `fk_mat_batch_cost_material` FOREIGN KEY (`material_id`) REFERENCES `inv_material` (`id`) ON DELETE RESTRICT ON UPDATE CASCADE, - CONSTRAINT `fk_mat_batch_cost_warehouse` FOREIGN KEY (`warehouse_id`) REFERENCES `inv_warehouse` (`id`) ON DELETE SET NULL ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='小料批次成本表'; - --- 领料单明细表 -CREATE TABLE `material_requisition_items` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '明细ID', - `requisition_id` bigint unsigned NOT NULL COMMENT '领料单ID', - `material_id` bigint unsigned NOT NULL COMMENT '物料ID', - `material_code` varchar(50) DEFAULT NULL COMMENT '物料编码', - `material_name` varchar(100) DEFAULT NULL COMMENT '物料名称', - `planned_quantity` decimal(18,4) NOT NULL COMMENT '计划数量(BOM计算)', - `actual_quantity` decimal(18,4) DEFAULT '0.0000' COMMENT '实际领用数量', - `issued_quantity` decimal(18,4) DEFAULT '0.0000' COMMENT '已出库数量', - `unit` varchar(20) DEFAULT NULL COMMENT '单位', - `qr_code` varchar(50) DEFAULT NULL COMMENT '小料二维码', - `batch_no` varchar(50) DEFAULT NULL COMMENT '批次号', - `warehouse_location` varchar(50) DEFAULT NULL COMMENT '库位', - `fifo_recommended` tinyint DEFAULT '0' COMMENT '是否为FIFO推荐批次:0-否,1-是', - `split_flag` tinyint DEFAULT '1' COMMENT '拆分标记:0-整料,1-小料,2-余料', - `unit_cost` decimal(18,4) DEFAULT NULL COMMENT '单位成本', - `total_cost` decimal(18,4) DEFAULT NULL COMMENT '总成本', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - `deleted` tinyint NOT NULL DEFAULT '0' COMMENT '软删除标记', - PRIMARY KEY (`id`), - KEY `idx_requisition` (`requisition_id`), - KEY `idx_material` (`material_id`), - KEY `idx_qr_code` (`qr_code`), - CONSTRAINT `fk_mat_req_item_material` FOREIGN KEY (`material_id`) REFERENCES `inv_material` (`id`) ON DELETE RESTRICT ON UPDATE CASCADE, - CONSTRAINT `fk_req_items_requisition` FOREIGN KEY (`requisition_id`) REFERENCES `material_requisitions` (`id`) ON DELETE CASCADE ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='领料单明细表'; - --- 领料单主表 -CREATE TABLE `material_requisitions` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '领料单ID', - `requisition_no` varchar(50) NOT NULL COMMENT '领料单编号,格式:MR+YYYYMMDD+4位序号', - `work_order_id` bigint unsigned DEFAULT NULL COMMENT '关联工单ID', - `work_order_no` varchar(50) DEFAULT NULL COMMENT '工单编号', - `type` varchar(20) NOT NULL COMMENT '类型:normal-正常领料, over-超领, supplementary-补料', - `status` tinyint DEFAULT '0' COMMENT '状态:0=待审批,1=待出库,2=已出库,3=已取消', - `applicant_id` bigint unsigned DEFAULT NULL COMMENT '申请人ID', - `applicant_name` varchar(50) DEFAULT NULL COMMENT '申请人姓名', - `approver_id` bigint unsigned DEFAULT NULL COMMENT '审批人ID', - `approver_name` varchar(50) DEFAULT NULL COMMENT '审批人姓名', - `approve_time` datetime DEFAULT NULL COMMENT '审批时间', - `total_quantity` decimal(18,4) DEFAULT '0.0000' COMMENT '领料总数量', - `issued_quantity` decimal(18,4) DEFAULT '0.0000' COMMENT '已出库数量', - `warehouse_id` bigint unsigned DEFAULT NULL COMMENT '仓库ID', - `original_requisition_id` bigint unsigned DEFAULT NULL COMMENT '原领料单ID(补料时关联)', - `reason` varchar(255) DEFAULT NULL COMMENT '超领/补料原因', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', - `remark` text COMMENT '备注', - `deleted` tinyint NOT NULL DEFAULT '0' COMMENT '软删除: 0-正常, 1-已删除', - `create_by` bigint unsigned DEFAULT NULL COMMENT '创建人ID', - `update_by` bigint unsigned DEFAULT NULL COMMENT '更新人ID', - PRIMARY KEY (`id`), - UNIQUE KEY `uk_requisition_no` (`requisition_no`), - KEY `idx_work_order` (`work_order_id`), - KEY `idx_type` (`type`), - KEY `idx_status` (`status`), - KEY `idx_applicant` (`applicant_id`), - KEY `idx_warehouse` (`warehouse_id`), - KEY `idx_approver` (`approver_id`), - KEY `idx_original_requisition` (`original_requisition_id`), - CONSTRAINT `fk_mat_req_original` FOREIGN KEY (`original_requisition_id`) REFERENCES `material_requisitions` (`id`) ON DELETE SET NULL ON UPDATE CASCADE, - CONSTRAINT `fk_mat_req_warehouse` FOREIGN KEY (`warehouse_id`) REFERENCES `inv_warehouse` (`id`) ON DELETE SET NULL ON UPDATE CASCADE, - CONSTRAINT `fk_mat_req_work_order` FOREIGN KEY (`work_order_id`) REFERENCES `prd_work_order` (`id`) ON DELETE SET NULL ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='领料单主表'; - --- 退料单明细表 -CREATE TABLE `material_return_items` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '明细ID', - `return_id` bigint unsigned NOT NULL COMMENT '退料单ID', - `material_id` bigint unsigned NOT NULL COMMENT '物料ID', - `material_code` varchar(50) DEFAULT NULL COMMENT '物料编码', - `material_name` varchar(100) DEFAULT NULL COMMENT '物料名称', - `quantity` decimal(18,4) NOT NULL COMMENT '退料数量', - `unit` varchar(20) DEFAULT NULL COMMENT '单位', - `qr_code` varchar(50) DEFAULT NULL COMMENT '小料二维码', - `batch_no` varchar(50) DEFAULT NULL COMMENT '批次号', - `reason` varchar(255) DEFAULT NULL COMMENT '退料原因', - `unit_cost` decimal(18,4) DEFAULT NULL COMMENT '单位成本', - `total_cost` decimal(18,4) DEFAULT NULL COMMENT '总成本', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - `deleted` tinyint NOT NULL DEFAULT '0' COMMENT '软删除标记', - PRIMARY KEY (`id`), - KEY `idx_return` (`return_id`), - KEY `idx_material` (`material_id`), - CONSTRAINT `fk_mat_ret_item_material` FOREIGN KEY (`material_id`) REFERENCES `inv_material` (`id`) ON DELETE RESTRICT ON UPDATE CASCADE, - CONSTRAINT `fk_ret_items_return` FOREIGN KEY (`return_id`) REFERENCES `material_returns` (`id`) ON DELETE CASCADE ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='退料单明细表'; - --- 退料单主表 -CREATE TABLE `material_returns` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '退料单ID', - `return_no` varchar(50) NOT NULL COMMENT '退料单编号,格式:RT+YYYYMMDD+4位序号', - `work_order_id` bigint unsigned DEFAULT NULL COMMENT '关联工单ID', - `requisition_id` bigint unsigned DEFAULT NULL COMMENT '关联领料单ID', - `status` tinyint DEFAULT '0' COMMENT '状态:0=待确认,1=已入库,2=已取消', - `applicant_id` bigint unsigned DEFAULT NULL COMMENT '申请人ID', - `applicant_name` varchar(50) DEFAULT NULL COMMENT '申请人姓名', - `confirm_id` bigint unsigned DEFAULT NULL COMMENT '确认人ID', - `confirm_name` varchar(50) DEFAULT NULL COMMENT '确认人姓名', - `confirm_time` datetime DEFAULT NULL COMMENT '确认时间', - `total_quantity` decimal(18,4) DEFAULT '0.0000' COMMENT '退料总数量', - `warehouse_id` bigint unsigned DEFAULT NULL COMMENT '仓库ID', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', - `remark` text COMMENT '备注', - `deleted` tinyint NOT NULL DEFAULT '0' COMMENT '软删除: 0-正常, 1-已删除', - `create_by` bigint unsigned DEFAULT NULL COMMENT '创建人ID', - `update_by` bigint unsigned DEFAULT NULL COMMENT '更新人ID', - PRIMARY KEY (`id`), - UNIQUE KEY `uk_return_no` (`return_no`), - KEY `idx_work_order` (`work_order_id`), - KEY `idx_requisition` (`requisition_id`), - KEY `idx_status` (`status`), - KEY `idx_warehouse` (`warehouse_id`), - KEY `idx_applicant` (`applicant_id`), - KEY `idx_confirm` (`confirm_id`), - CONSTRAINT `fk_mat_ret_requisition` FOREIGN KEY (`requisition_id`) REFERENCES `material_requisitions` (`id`) ON DELETE SET NULL ON UPDATE CASCADE, - CONSTRAINT `fk_mat_ret_warehouse` FOREIGN KEY (`warehouse_id`) REFERENCES `inv_warehouse` (`id`) ON DELETE SET NULL ON UPDATE CASCADE, - CONSTRAINT `fk_mat_ret_work_order` FOREIGN KEY (`work_order_id`) REFERENCES `prd_work_order` (`id`) ON DELETE SET NULL ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='退料单主表'; - --- 产品主数据表 -CREATE TABLE `mdm_product` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT, - `product_code` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '产品编码', - `product_name` varchar(200) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '产品名称', - `short_name` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '简称', - `specification` varchar(200) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '规格型号', - `unit` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT '件' COMMENT '计量单位', - `category_id` bigint unsigned DEFAULT NULL COMMENT '分类ID', - `category_name` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '分类名称', - `customer_id` bigint unsigned DEFAULT NULL COMMENT '客户ID', - `customer_name` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '客户名称', - `bom_version` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT 'V1.0' COMMENT 'BOM版本', - `description` text COLLATE utf8mb4_unicode_ci COMMENT '描述', - `status` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT 'active' COMMENT '状态', - `cost_price` decimal(12,2) DEFAULT '0.00' COMMENT '成本价', - `sale_price` decimal(12,2) DEFAULT '0.00' COMMENT '销售价', - `min_stock` decimal(12,2) DEFAULT '0.00' COMMENT '最小库存', - `max_stock` decimal(12,2) DEFAULT '0.00' COMMENT '最大库存', - `safety_stock` decimal(12,2) DEFAULT '0.00' COMMENT '安全库存', - `deleted` tinyint DEFAULT '0', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP, - `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - PRIMARY KEY (`id`), - UNIQUE KEY `uk_product_code` (`product_code`), KEY `idx_category` (`category_id`), - KEY `idx_customer` (`customer_id`), - KEY `idx_status` (`status`) -) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='产品主数据表'; - --- 委外发料表 -CREATE TABLE `outsource_issue` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT, - `issue_no` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '发料单号', - `outsource_order_id` bigint unsigned DEFAULT NULL COMMENT '委外订单ID', - `outsource_order_no` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '委外单号', - `warehouse_id` bigint unsigned DEFAULT NULL COMMENT '仓库ID', - `issue_date` date DEFAULT NULL COMMENT '发料日期', - `status` tinyint DEFAULT '1' COMMENT '状态', - `operator_name` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '操作人', - `remark` text COLLATE utf8mb4_unicode_ci COMMENT '备注', - `deleted` tinyint DEFAULT '0', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP, - `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - PRIMARY KEY (`id`), - UNIQUE KEY `uk_issue_no` (`issue_no`) -) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='委外发料表'; - --- 委外发料明细 -CREATE TABLE `outsource_issue_item` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT, - `issue_id` bigint unsigned NOT NULL COMMENT '发料单ID', - `material_id` bigint unsigned DEFAULT NULL COMMENT '物料ID', - `material_code` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '物料编码', - `material_name` varchar(200) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '物料名称', - `quantity` decimal(12,2) DEFAULT '0.00' COMMENT '数量', - `unit` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '单位', - `batch_no` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '批次号', - PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='委外发料明细'; - --- 委外订单表 -CREATE TABLE `outsource_order` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT, - `order_no` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '委外单号', - `work_order_id` bigint unsigned DEFAULT NULL COMMENT '工单ID', - `work_order_no` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '工单编号', - `supplier_id` bigint unsigned DEFAULT NULL COMMENT '供应商ID', - `supplier_name` varchar(200) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '供应商名称', - `product_id` bigint unsigned DEFAULT NULL COMMENT '产品ID', - `product_code` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '产品编码', - `product_name` varchar(200) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '产品名称', - `plan_qty` decimal(12,2) DEFAULT '0.00' COMMENT '计划数量', - `unit` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '单位', - `unit_price` decimal(12,2) DEFAULT '0.00' COMMENT '单价', - `total_amount` decimal(12,2) DEFAULT '0.00' COMMENT '总金额', - `delivery_date` date DEFAULT NULL COMMENT '交货日期', - `outsource_type` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT 'process' COMMENT '委外类型', - `process_name` varchar(200) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '工序名称', - `status` tinyint DEFAULT '0' COMMENT '状态', - `remark` text COLLATE utf8mb4_unicode_ci COMMENT '备注', - `deleted` tinyint DEFAULT '0', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP, - `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - PRIMARY KEY (`id`), - UNIQUE KEY `uk_order_no` (`order_no`) -) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='委外订单表'; - --- 委外收货表 -CREATE TABLE `outsource_receive` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT, - `receive_no` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '收货单号', - `outsource_order_id` bigint unsigned DEFAULT NULL COMMENT '委外订单ID', - `outsource_order_no` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '委外单号', - `warehouse_id` bigint unsigned DEFAULT NULL COMMENT '仓库ID', - `receive_date` date DEFAULT NULL COMMENT '收货日期', - `receive_qty` decimal(12,2) DEFAULT '0.00' COMMENT '收货数量', - `qualified_qty` decimal(12,2) DEFAULT '0.00' COMMENT '合格数量', - `defective_qty` decimal(12,2) DEFAULT '0.00' COMMENT '不良数量', - `qc_status` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT 'pending' COMMENT '质检状态', - `status` tinyint DEFAULT '1' COMMENT '状态', - `operator_name` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '操作人', - `remark` text COLLATE utf8mb4_unicode_ci COMMENT '备注', - `deleted` tinyint DEFAULT '0', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP, - `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - PRIMARY KEY (`id`), - UNIQUE KEY `uk_receive_no` (`receive_no`) -) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='委外收货表'; - --- 委外结算表 -CREATE TABLE `outsource_settlement` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT, - `settlement_no` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '结算单号', - `outsource_order_id` bigint unsigned DEFAULT NULL COMMENT '委外订单ID', - `outsource_order_no` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '委外单号', - `supplier_id` bigint unsigned DEFAULT NULL COMMENT '供应商ID', - `supplier_name` varchar(200) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '供应商名称', - `settlement_date` date DEFAULT NULL COMMENT '结算日期', - `settlement_qty` decimal(12,2) DEFAULT '0.00' COMMENT '结算数量', - `unit_price` decimal(12,2) DEFAULT '0.00' COMMENT '单价', - `settlement_amount` decimal(12,2) DEFAULT '0.00' COMMENT '结算金额', - `deduct_amount` decimal(12,2) DEFAULT '0.00' COMMENT '扣款金额', - `actual_amount` decimal(12,2) DEFAULT '0.00' COMMENT '实付金额', - `payment_status` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT 'unpaid' COMMENT '付款状态', - `status` tinyint DEFAULT '0' COMMENT '状态', - `remark` text COLLATE utf8mb4_unicode_ci COMMENT '备注', - `deleted` tinyint DEFAULT '0', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP, - `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - PRIMARY KEY (`id`), - UNIQUE KEY `uk_settlement_no` (`settlement_no`) -) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='委外结算表'; - --- BOM表 -CREATE TABLE `prd_bom` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT, - `bom_name` varchar(200) NOT NULL COMMENT 'BOM名称', - `product_id` bigint unsigned DEFAULT NULL COMMENT '产品ID', - `version` varchar(20) DEFAULT '1.0' COMMENT '版本号', - `total_cost` decimal(18,4) DEFAULT '0.0000' COMMENT '总成本', - `status` tinyint DEFAULT '1' COMMENT '状态: 0-禁用, 1-启用', - `remark` text COMMENT '备注', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP, - `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - `create_by` bigint unsigned DEFAULT NULL, - `deleted` tinyint DEFAULT '0', - `update_by` bigint unsigned DEFAULT NULL COMMENT '更新人ID', - PRIMARY KEY (`id`), - KEY `idx_product` (`product_id`) -) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='BOM表'; - --- BOM明细表 -CREATE TABLE `prd_bom_detail` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT, - `bom_id` bigint unsigned NOT NULL COMMENT 'BOM ID', - `material_id` bigint unsigned NOT NULL COMMENT '物料ID', - `material_name` varchar(200) DEFAULT NULL COMMENT '物料名称', - `quantity` decimal(18,4) DEFAULT '0.0000' COMMENT '数量', - `unit` varchar(20) DEFAULT NULL COMMENT '单位', - `loss_rate` decimal(18,4) DEFAULT '0.0000' COMMENT '损耗率(%)', - `unit_cost` decimal(18,4) DEFAULT '0.0000' COMMENT '单位成本', - `total_cost` decimal(18,4) DEFAULT '0.0000' COMMENT '总成本', - `item_type` tinyint DEFAULT '1' COMMENT '物料类型: 1-原材料, 2-半成品, 3-辅料', - `remark` text COMMENT '备注', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP, - PRIMARY KEY (`id`), - KEY `idx_bom` (`bom_id`), - KEY `idx_material` (`material_id`), - CONSTRAINT `fk_bom_detail_bom` FOREIGN KEY (`bom_id`) REFERENCES `prd_bom` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, - CONSTRAINT `fk_prd_bom_detail_material` FOREIGN KEY (`material_id`) REFERENCES `inv_material` (`id`) ON DELETE RESTRICT ON UPDATE CASCADE -) ENGINE=InnoDB AUTO_INCREMENT=31 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='BOM明细表'; - --- 标准BOM行 -CREATE TABLE `prd_bom_line_std` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT 'BOM行ID', - `bom_id` bigint unsigned NOT NULL COMMENT 'BOM头ID', - `line_no` int NOT NULL DEFAULT '1' COMMENT '行号', - `material_id` bigint unsigned NOT NULL COMMENT '物料ID', - `material_code` varchar(50) DEFAULT NULL COMMENT '物料编码', - `material_name` varchar(100) DEFAULT NULL COMMENT '物料名称', - `consumption_qty` decimal(18,4) NOT NULL COMMENT '单耗', - `waste_rate` decimal(18,4) NOT NULL DEFAULT '0.0000' COMMENT '损耗率%', - `material_type` tinyint DEFAULT '1' COMMENT '1原材料 2半成品 3辅料 4包材 5其他', - `remark` varchar(200) DEFAULT NULL COMMENT '备注', - `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', - `deleted` tinyint NOT NULL DEFAULT '0' COMMENT '是否删除', - PRIMARY KEY (`id`), - KEY `idx_bom_id` (`bom_id`), - KEY `idx_material_id` (`material_id`), - CONSTRAINT `fk_bom_line_std_bom` FOREIGN KEY (`bom_id`) REFERENCES `prd_bom_std` (`id`) -) ENGINE=InnoDB AUTO_INCREMENT=31 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='标准BOM行'; - --- 标准BOM头 -CREATE TABLE `prd_bom_std` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT 'BOM ID', - `bom_code` varchar(50) NOT NULL COMMENT 'BOM编码', - `product_id` bigint unsigned NOT NULL COMMENT '成品ID', - `product_name` varchar(100) DEFAULT NULL COMMENT '成品名称', - `version` varchar(20) NOT NULL DEFAULT 'V1.0' COMMENT '版本', - `effective_date` date NOT NULL COMMENT '生效日期', - `obsolete_date` date DEFAULT NULL COMMENT '失效日期', - `status` tinyint NOT NULL DEFAULT '1' COMMENT '0草稿 1生效 2作废', - `remark` text COMMENT '备注', - `legacy_source` varchar(30) DEFAULT NULL COMMENT '旧表来源', - `legacy_id` bigint unsigned DEFAULT NULL COMMENT '旧表原始ID', - `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', - `create_by` bigint unsigned DEFAULT NULL COMMENT '创建人ID', - `update_by` bigint unsigned DEFAULT NULL COMMENT '更新人ID', - `deleted` tinyint NOT NULL DEFAULT '0' COMMENT '是否删除', - PRIMARY KEY (`id`), - UNIQUE KEY `uk_bom_code` (`bom_code`), - KEY `idx_product_id` (`product_id`), - KEY `idx_status` (`status`), - KEY `idx_effective_date` (`effective_date`), - KEY `idx_legacy` (`legacy_source`,`legacy_id`) -) ENGINE=InnoDB AUTO_INCREMENT=31 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='标准BOM头'; - --- prd_die -CREATE TABLE `prd_die` ( - `id` int NOT NULL AUTO_INCREMENT, - `die_code` varchar(50) NOT NULL, - `die_name` varchar(100) DEFAULT NULL, - `die_type` varchar(50) DEFAULT NULL, - `size_spec` varchar(100) DEFAULT NULL, - `customer_id` int DEFAULT NULL, - `product_name` varchar(200) DEFAULT NULL, - `max_use_count` int DEFAULT '0', - `used_count` int DEFAULT '0', - `remaining_count` int DEFAULT '0', - `maintenance_days` int DEFAULT '180', - `last_maintenance_date` date DEFAULT NULL, - `next_maintenance_date` date DEFAULT NULL, - `warehouse_id` int DEFAULT NULL, - `location_id` int DEFAULT NULL, - `status` int DEFAULT '1', - `remark` text, - `deleted` tinyint NOT NULL DEFAULT '0', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP, - `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - PRIMARY KEY (`id`), - UNIQUE KEY `uk_die_code` (`die_code`), - KEY `idx_die_code` (`die_code`), - KEY `idx_die_status` (`status`,`deleted`) -) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; - --- 刀模板/网版管理表 -CREATE TABLE `prd_die_template` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT, - `template_code` varchar(50) NOT NULL COMMENT '刀模板编号', - `template_name` varchar(100) NOT NULL COMMENT '刀模板名称', - `asset_type` varchar(20) DEFAULT 'die' COMMENT '资产类型: die-刀模, flexo_plate-柔印版, screen_mesh-丝网版', - `layout_type` varchar(20) DEFAULT 'single_row' COMMENT '排版方式: single_row-单排, double_row-双排, triple_row-三排', - `pieces_per_impression` int DEFAULT '1' COMMENT '每印件数', - `template_type` tinyint DEFAULT NULL COMMENT '类型: 1-刀模, 2-丝网版', - `specification` varchar(255) DEFAULT NULL COMMENT '规格尺寸', - `material` varchar(50) DEFAULT NULL COMMENT '材质', - `max_usage` int DEFAULT NULL COMMENT '最大使用次数', - `current_usage` int DEFAULT '0' COMMENT '当前使用次数', - `remaining_usage` int DEFAULT NULL COMMENT '剩余使用次数', - `warning_usage` int DEFAULT NULL COMMENT '预警使用次数', - `max_impressions` int DEFAULT '0' COMMENT '最大冲压次数', - `cumulative_impressions` int DEFAULT '0' COMMENT '累计冲压次数', - `warning_threshold` int DEFAULT '80' COMMENT '预警阈值(%)', - `maintenance_interval` int DEFAULT '8000' COMMENT '保养间隔(次)', - `maintenance_count` int DEFAULT '0' COMMENT '保养次数', - `last_maintenance_impressions` int DEFAULT '0' COMMENT '上次保养时冲压次数', - `last_maintenance_date` date DEFAULT NULL COMMENT '上次保养日期', - `last_used_date` date DEFAULT NULL COMMENT '最后使用日期', - `status` tinyint DEFAULT '1' COMMENT '状态: 1-在用, 2-待更换, 3-已报废', - `die_status` varchar(30) DEFAULT 'available' COMMENT '刀模状态: available-可用, in_use-使用中, maintenance_needed-需保养, re_rule_needed-需重做, scrap-已报废', - `storage_location` varchar(100) DEFAULT NULL COMMENT '存放位置', - `purchase_date` date DEFAULT NULL COMMENT '购入日期', - `supplier_id` bigint unsigned DEFAULT NULL COMMENT '供应商ID', - `unit_price` decimal(12,2) DEFAULT '0.00' COMMENT '单价', - `qr_code` varchar(100) DEFAULT NULL COMMENT '二维码', - `remark` text COMMENT '备注', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP, - `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - `create_by` bigint unsigned DEFAULT NULL, - `deleted` tinyint DEFAULT '0', - PRIMARY KEY (`id`), - UNIQUE KEY `uk_template_code` (`template_code`), - KEY `idx_type` (`template_type`), + KEY `idx_material_name` (`material_name`), + KEY `idx_type` (`material_type`), KEY `idx_status` (`status`) -) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='刀模板/网版管理表'; - --- prd_ink -CREATE TABLE `prd_ink` ( - `id` int NOT NULL AUTO_INCREMENT, - `ink_code` varchar(50) NOT NULL, - `ink_name` varchar(100) NOT NULL, - `ink_type` int DEFAULT NULL, - `color_name` varchar(50) DEFAULT NULL, - `color_code` varchar(50) DEFAULT NULL, - `brand` varchar(100) DEFAULT NULL, - `supplier_id` int DEFAULT NULL, - `unit` varchar(20) DEFAULT 'kg', - `specification` varchar(200) DEFAULT NULL, - `safety_stock` decimal(10,2) DEFAULT '0.00', - `shelf_life` int DEFAULT NULL, - `stock_qty` decimal(10,2) DEFAULT '0.00', - `status` int DEFAULT '1', - `remark` text, - `deleted` tinyint NOT NULL DEFAULT '0', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP, - `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - PRIMARY KEY (`id`), - UNIQUE KEY `uk_ink_code` (`ink_code`), - KEY `idx_ink_code` (`ink_code`), - KEY `idx_ink_status` (`status`,`deleted`) -) ENGINE=InnoDB AUTO_INCREMENT=21 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; - --- 生产领料单 -CREATE TABLE `prd_material_issue` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT, - `issue_no` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '领料单号', - `work_order_id` bigint unsigned DEFAULT NULL COMMENT '工单ID', - `work_order_no` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '工单编号', - `warehouse_id` bigint unsigned DEFAULT NULL COMMENT '仓库ID', - `issue_date` date DEFAULT NULL COMMENT '领料日期', - `issue_type` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT 'normal' COMMENT '领料类型', - `operator_name` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '操作人', - `operator_id` bigint unsigned DEFAULT NULL COMMENT '操作员ID', - `status` tinyint DEFAULT '1' COMMENT '状态', - `remark` text COLLATE utf8mb4_unicode_ci COMMENT '备注', - `deleted` tinyint DEFAULT '0', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP, - `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - PRIMARY KEY (`id`), - UNIQUE KEY `uk_issue_no` (`issue_no`) -) ENGINE=InnoDB AUTO_INCREMENT=107 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='生产领料单'; - --- 领料明细 -CREATE TABLE `prd_material_issue_item` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT, - `issue_id` bigint unsigned NOT NULL COMMENT '领料单ID', - `material_id` bigint unsigned DEFAULT NULL COMMENT '物料ID', - `material_code` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '物料编码', - `material_name` varchar(200) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '物料名称', - `required_qty` decimal(12,2) DEFAULT '0.00' COMMENT '需求数量', - `issued_qty` decimal(12,2) DEFAULT '0.00' COMMENT '已领数量', - `unit` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '单位', - `batch_no` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '批次号', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - PRIMARY KEY (`id`) -) ENGINE=InnoDB AUTO_INCREMENT=103 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='领料明细'; - --- 生产退料单 -CREATE TABLE `prd_material_return` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT, - `return_no` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '退料单号', - `work_order_id` bigint unsigned DEFAULT NULL COMMENT '工单ID', - `work_order_no` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '工单编号', - `warehouse_id` bigint unsigned DEFAULT NULL COMMENT '仓库ID', - `return_date` date DEFAULT NULL COMMENT '退料日期', - `operator_name` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '操作人', - `status` tinyint DEFAULT '1' COMMENT '状态', - `remark` text COLLATE utf8mb4_unicode_ci COMMENT '备注', - `deleted` tinyint DEFAULT '0', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP, - `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - PRIMARY KEY (`id`), - UNIQUE KEY `uk_return_no` (`return_no`) -) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='生产退料单'; - --- 退料明细 -CREATE TABLE `prd_material_return_item` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT, - `return_id` bigint unsigned NOT NULL COMMENT '退料单ID', - `material_id` bigint unsigned DEFAULT NULL COMMENT '物料ID', - `material_code` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '物料编码', - `material_name` varchar(200) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '物料名称', - `return_qty` decimal(12,2) DEFAULT '0.00' COMMENT '退料数量', - `unit` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '单位', - `batch_no` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '批次号', - PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='退料明细'; - --- 生产流程卡表 -CREATE TABLE `prd_process_card` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT, - `card_no` varchar(50) NOT NULL COMMENT '流程卡卡号', - `qr_code` varchar(255) DEFAULT NULL COMMENT '二维码内容', - `work_order_id` bigint unsigned DEFAULT NULL COMMENT '工单ID', - `work_order_no` varchar(50) DEFAULT NULL COMMENT '工单号', - `product_code` varchar(50) DEFAULT NULL COMMENT '成品料号', - `product_name` varchar(200) DEFAULT NULL COMMENT '成品品名', - `material_spec` varchar(200) DEFAULT NULL COMMENT '材料规格', - `work_order_date` date DEFAULT NULL COMMENT '工单日期', - `plan_qty` decimal(18,4) DEFAULT '0.0000' COMMENT '计划生产数量', - `main_label_id` bigint unsigned DEFAULT NULL COMMENT '主材标签ID', - `main_label_no` varchar(50) DEFAULT NULL COMMENT '主材标签编号', - `burdening_status` tinyint DEFAULT '0' COMMENT '配料状态: 0-未配料, 1-已配料', - `lock_status` tinyint DEFAULT '0' COMMENT '锁住状态: 0-未锁, 1-已锁', - `create_user_id` bigint unsigned DEFAULT NULL COMMENT '创建人ID', - `create_user_name` varchar(50) DEFAULT NULL COMMENT '创建人名称', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP, - `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - `deleted` tinyint DEFAULT '0', - PRIMARY KEY (`id`), - UNIQUE KEY `uk_card_no` (`card_no`), - KEY `idx_work_order` (`work_order_id`), - KEY `idx_main_label` (`main_label_id`) -) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='生产流程卡表'; - --- 流程卡物料关联表 -CREATE TABLE `prd_process_card_material` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT, - `card_id` bigint unsigned NOT NULL COMMENT '流程卡ID', - `card_no` varchar(50) DEFAULT NULL COMMENT '流程卡卡号', - `label_id` bigint unsigned NOT NULL COMMENT '物料标签ID', - `label_no` varchar(50) NOT NULL COMMENT '物料标签编号', - `material_type` tinyint DEFAULT '1' COMMENT '物料类型: 1-主材, 2-辅料', - `material_code` varchar(50) DEFAULT NULL COMMENT '物料代号', - `material_name` varchar(200) DEFAULT NULL COMMENT '物料名称', - `specification` varchar(200) DEFAULT NULL COMMENT '规格', - `batch_no` varchar(50) DEFAULT NULL COMMENT '批号', - `quantity` decimal(18,4) DEFAULT '0.0000' COMMENT '用量', - `unit` varchar(20) DEFAULT NULL COMMENT '单位', - `remark` varchar(500) DEFAULT NULL COMMENT '备注', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP, - PRIMARY KEY (`id`), - KEY `idx_card_id` (`card_id`), - KEY `idx_label_id` (`label_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='流程卡物料关联表'; - --- 工艺路线表 -CREATE TABLE `prd_process_route` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT, - `route_code` varchar(50) NOT NULL COMMENT '工艺路线编码', - `route_name` varchar(100) NOT NULL COMMENT '工艺路线名称', - `product_id` bigint unsigned DEFAULT NULL COMMENT '产品ID', - `version` varchar(10) DEFAULT '1.0' COMMENT '版本号', - `is_default` tinyint DEFAULT '1' COMMENT '是否默认', - `status` tinyint DEFAULT '1' COMMENT '状态: 0-禁用, 1-启用', - `remark` text COMMENT '备注', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP, - `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - `create_by` bigint unsigned DEFAULT NULL, - `deleted` tinyint DEFAULT '0', - PRIMARY KEY (`id`), - UNIQUE KEY `uk_route_code` (`route_code`), - KEY `idx_product` (`product_id`) -) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='工艺路线表'; - --- 工艺路线工序表 -CREATE TABLE `prd_process_route_step` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT, - `route_id` bigint unsigned NOT NULL COMMENT '工艺路线ID', - `step_seq` int NOT NULL COMMENT '工序序号', - `step_name` varchar(50) NOT NULL COMMENT '工序名称', - `step_type` tinyint DEFAULT NULL COMMENT '工序类型: 1-印刷, 2-覆膜, 3-模切, 4-全检, 5-包装, 6-其他', - `equipment_type` tinyint DEFAULT NULL COMMENT '所需设备类型', - `standard_time` decimal(10,2) DEFAULT NULL COMMENT '标准工时(分钟)', - `setup_time` decimal(10,2) DEFAULT NULL COMMENT '准备时间(分钟)', - `is_key_process` tinyint DEFAULT '0' COMMENT '是否关键工序', - `is_first_piece_required` tinyint DEFAULT '0' COMMENT '是否需要首件签样', - `quality_check` tinyint DEFAULT '0' COMMENT '是否质检: 0-否, 1-是', - `remark` varchar(255) DEFAULT NULL COMMENT '备注', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP, - PRIMARY KEY (`id`), - KEY `idx_route` (`route_id`) -) ENGINE=InnoDB AUTO_INCREMENT=33 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='工艺路线工序表'; - --- 产品标签表 -CREATE TABLE `prd_product_label` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT, - `label_no` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '标签编号', - `work_order_id` bigint unsigned DEFAULT NULL COMMENT '工单ID', - `work_order_no` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '工单编号', - `material_id` bigint unsigned DEFAULT NULL COMMENT '物料ID', - `material_code` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '物料编码', - `material_name` varchar(200) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '物料名称', - `quantity` decimal(12,2) DEFAULT '0.00' COMMENT '数量', - `unit` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '单位', - `batch_no` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '批次号', - `qc_result` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT 'pending' COMMENT '质检结果', - `remark` text COLLATE utf8mb4_unicode_ci COMMENT '备注', - `deleted` tinyint DEFAULT '0', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP, - `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - PRIMARY KEY (`id`), - UNIQUE KEY `uk_label_no` (`label_no`) -) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='产品标签表'; - --- 生产排程主表 -CREATE TABLE `prd_schedule` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '排程ID', - `schedule_no` varchar(50) NOT NULL COMMENT '排产单号', - `order_id` bigint unsigned DEFAULT NULL COMMENT '销售订单ID', - `order_no` varchar(50) DEFAULT NULL COMMENT '销售订单号', - `work_order_id` bigint unsigned DEFAULT NULL COMMENT '生产工单ID', - `work_order_no` varchar(50) DEFAULT NULL COMMENT '生产工单号', - `product_id` bigint unsigned DEFAULT NULL COMMENT '产品ID', - `product_code` varchar(50) DEFAULT NULL COMMENT '产品编码', - `product_name` varchar(100) NOT NULL COMMENT '产品名称', - `workshop` varchar(50) NOT NULL COMMENT '车间: die_cut, trademark, printing, packaging', - `planned_qty` decimal(18,4) NOT NULL DEFAULT '0.0000' COMMENT '计划数量', - `completed_qty` decimal(18,4) DEFAULT '0.0000' COMMENT '已完成数量', - `planned_start` datetime DEFAULT NULL COMMENT '计划开始时间', - `planned_end` datetime DEFAULT NULL COMMENT '计划结束时间', - `actual_start` datetime DEFAULT NULL COMMENT '实际开始时间', - `actual_end` datetime DEFAULT NULL COMMENT '实际结束时间', - `priority` tinyint DEFAULT '2' COMMENT '优先级: 1-紧急, 2-正常, 3-低', - `status` tinyint DEFAULT '1' COMMENT '状态: 1-待排产, 2-已排产, 3-生产中, 4-已完成, 5-已取消', - `scheduler` varchar(50) DEFAULT NULL COMMENT '排产人', - `remark` text COMMENT '备注', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', - `deleted` tinyint NOT NULL DEFAULT '0' COMMENT '软删除: 0-正常, 1-已删除', - `create_by` bigint unsigned DEFAULT NULL COMMENT '创建人ID', - `update_by` bigint unsigned DEFAULT NULL COMMENT '更新人ID', - PRIMARY KEY (`id`), - UNIQUE KEY `uk_schedule_no` (`schedule_no`), - KEY `idx_work_order` (`work_order_id`), - KEY `idx_product` (`product_id`), - KEY `idx_workshop` (`workshop`), - KEY `idx_status` (`status`), - KEY `idx_planned_start` (`planned_start`), - KEY `idx_order` (`order_id`), - CONSTRAINT `fk_prd_schedule_product` FOREIGN KEY (`product_id`) REFERENCES `inv_material` (`id`) ON DELETE SET NULL ON UPDATE CASCADE, - CONSTRAINT `fk_prd_schedule_work_order` FOREIGN KEY (`work_order_id`) REFERENCES `prd_work_order` (`id`) ON DELETE SET NULL ON UPDATE CASCADE -) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='生产排程主表'; - --- 排程明细表(色序级) -CREATE TABLE `prd_schedule_detail` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '明细ID', - `schedule_id` bigint unsigned NOT NULL COMMENT '排程ID', - `work_order_id` bigint unsigned NOT NULL COMMENT '工单ID', - `color_seq_no` int NOT NULL COMMENT '色序号', - `color_name` varchar(50) DEFAULT NULL COMMENT '颜色名称', - `equipment_id` bigint unsigned DEFAULT NULL COMMENT '设备ID', - `equipment_name` varchar(100) DEFAULT NULL COMMENT '设备名称', - `planned_start` datetime DEFAULT NULL COMMENT '计划开始时间', - `planned_end` datetime DEFAULT NULL COMMENT '计划结束时间', - `actual_start` datetime DEFAULT NULL COMMENT '实际开始时间', - `actual_end` datetime DEFAULT NULL COMMENT '实际结束时间', - `duration_hours` decimal(18,4) DEFAULT NULL COMMENT '预计耗时(小时)', - `status` tinyint DEFAULT '1' COMMENT '状态: 1-待排, 2-已排, 3-生产中, 4-已完成', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', - `deleted` tinyint NOT NULL DEFAULT '0' COMMENT '软删除标记', - PRIMARY KEY (`id`), - UNIQUE KEY `uk_work_order_seq` (`work_order_id`,`color_seq_no`), - KEY `idx_schedule` (`schedule_id`), - KEY `idx_equipment` (`equipment_id`), - KEY `idx_status` (`status`), - CONSTRAINT `fk_prd_schedule_detail_equipment` FOREIGN KEY (`equipment_id`) REFERENCES `eqp_equipment` (`id`) ON DELETE SET NULL ON UPDATE CASCADE, - CONSTRAINT `fk_prd_schedule_detail_work_order` FOREIGN KEY (`work_order_id`) REFERENCES `prd_work_order` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, - CONSTRAINT `fk_schedule_detail_schedule` FOREIGN KEY (`schedule_id`) REFERENCES `prd_schedule` (`id`) ON DELETE CASCADE ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='排程明细表(色序级)'; - --- prd_screen_plate -CREATE TABLE `prd_screen_plate` ( - `id` int NOT NULL AUTO_INCREMENT, - `plate_code` varchar(50) NOT NULL, - `plate_name` varchar(100) DEFAULT NULL, - `plate_type` varchar(50) DEFAULT NULL, - `mesh_count` varchar(50) DEFAULT NULL, - `size_spec` varchar(100) DEFAULT NULL, - `customer_id` int DEFAULT NULL, - `product_name` varchar(200) DEFAULT NULL, - `max_use_count` int DEFAULT '0', - `used_count` int DEFAULT '0', - `remaining_count` int DEFAULT '0', - `maintenance_days` int DEFAULT '360', - `last_maintenance_date` date DEFAULT NULL, - `next_maintenance_date` date DEFAULT NULL, - `warehouse_id` int DEFAULT NULL, - `location_id` int DEFAULT NULL, - `storage_location` varchar(100) DEFAULT NULL, - `status` int DEFAULT '1', - `remark` text, - `deleted` tinyint NOT NULL DEFAULT '0', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP, - `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - PRIMARY KEY (`id`), - UNIQUE KEY `uk_plate_code` (`plate_code`), - KEY `idx_screen_plate_code` (`plate_code`), - KEY `idx_screen_plate_status` (`status`,`deleted`) -) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; - --- 标准卡表 -CREATE TABLE `prd_standard_card` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '标准卡ID', - `card_no` varchar(50) NOT NULL COMMENT '标准卡编号', - `name` varchar(200) DEFAULT NULL, - `type` varchar(20) DEFAULT 'process', - `customer_id` bigint unsigned DEFAULT NULL COMMENT '客户ID', - `customer_name` varchar(100) DEFAULT NULL COMMENT '客户名称', - `customer_code` varchar(50) DEFAULT NULL COMMENT '客户代码', - `product_name` varchar(100) DEFAULT NULL COMMENT '产品名称', - `version` varchar(10) DEFAULT NULL COMMENT '版本', - `effective_date` date DEFAULT NULL, - `expiry_date` date DEFAULT NULL, - `create_user` int DEFAULT NULL, - `audit_user` int DEFAULT NULL, - `date` date DEFAULT NULL COMMENT '日期', - `document_code` varchar(50) DEFAULT NULL COMMENT '文件编号', - `finished_size` varchar(50) DEFAULT NULL COMMENT '成品尺寸', - `tolerance` varchar(50) DEFAULT NULL COMMENT '公差', - `material_name` varchar(100) DEFAULT NULL COMMENT '材料名称', - `material_type` varchar(20) DEFAULT NULL COMMENT '材料类型', - `mold_type` varchar(100) DEFAULT '' COMMENT '模号/种类', - `layout_type` varchar(50) DEFAULT NULL COMMENT '排版方式', - `spacing` varchar(20) DEFAULT NULL COMMENT '间距', - `spacing_value` varchar(20) DEFAULT NULL COMMENT '间距值', - `sheet_width` varchar(20) DEFAULT NULL COMMENT '片材宽', - `sheet_length` varchar(20) DEFAULT NULL COMMENT '片材长', - `core_type` varchar(50) DEFAULT NULL COMMENT '纸芯类型', - `paper_direction` varchar(20) DEFAULT NULL COMMENT '纸向', - `roll_width` varchar(20) DEFAULT NULL COMMENT '料宽', - `paper_edge` varchar(20) DEFAULT NULL COMMENT '纸边', - `standard_usage` varchar(50) DEFAULT NULL COMMENT '标准用量', - `jump_distance` varchar(20) DEFAULT NULL COMMENT '跳距', - `process_flow1` varchar(100) DEFAULT NULL COMMENT '工艺流程1', - `process_flow2` varchar(100) DEFAULT NULL COMMENT '工艺流程2', - `print_type` varchar(50) DEFAULT NULL COMMENT '表面处理', - `first_jump_distance` varchar(20) DEFAULT NULL COMMENT '第一跳距', - `sequences` json DEFAULT NULL COMMENT '印序数据', - `film_manufacturer` varchar(100) DEFAULT NULL COMMENT '膜厂商', - `film_code` varchar(50) DEFAULT NULL COMMENT '膜编号', - `film_size` varchar(50) DEFAULT NULL COMMENT '膜规格', - `process_method` varchar(50) DEFAULT NULL COMMENT '工艺方式', - `stamping_method` varchar(50) DEFAULT NULL COMMENT '冲压方法', - `mold_code` varchar(50) DEFAULT NULL COMMENT '模具编号', - `back_mold_code` varchar(50) DEFAULT NULL COMMENT '模具编号(选择)', - `layout_method` varchar(50) DEFAULT NULL COMMENT '排版方式', - `layout_way` varchar(50) DEFAULT NULL COMMENT '排版方向', - `jump_distance2` varchar(20) DEFAULT NULL COMMENT '跳距2', - `mylar_material` varchar(100) DEFAULT NULL COMMENT '麦拉材料', - `mylar_specs` varchar(50) DEFAULT NULL COMMENT '麦拉规格', - `mylar_layout` varchar(50) DEFAULT NULL COMMENT '麦拉排版', - `mylar_jump` varchar(20) DEFAULT NULL COMMENT '麦拉跳距', - `adhesive_type` varchar(50) DEFAULT NULL COMMENT '背胶种类', - `adhesive_manufacturer` varchar(100) DEFAULT NULL COMMENT '背胶厂商', - `adhesive_code` varchar(50) DEFAULT NULL COMMENT '背胶编号', - `adhesive_size` varchar(50) DEFAULT NULL COMMENT '背胶尺寸', - `adhesive_specs` varchar(50) DEFAULT NULL COMMENT '背胶规格', - `dashed_knife` tinyint DEFAULT '0' COMMENT '加虚线刀: 0-否, 1-是', - `slice_per_row` varchar(20) DEFAULT NULL COMMENT 'PCS/排', - `slice_per_roll` varchar(20) DEFAULT NULL COMMENT 'PCS/卷', - `slice_per_bundle` varchar(20) DEFAULT NULL COMMENT 'PCS/扎', - `slice_per_bag` varchar(20) DEFAULT NULL COMMENT 'PCS/袋', - `slice_per_box` varchar(20) DEFAULT NULL COMMENT 'PCS/箱', - `packing_qty` varchar(20) DEFAULT NULL COMMENT '包装数量(PCS/袋)', - `back_knife_mold` varchar(50) DEFAULT NULL COMMENT '背胶刀模存放', - `back_mylar_mold` varchar(50) DEFAULT NULL COMMENT '背麦拉刀模存放', - `etch_mold` varchar(100) DEFAULT '' COMMENT '腐蚀刀模', - `storage_location` varchar(100) DEFAULT '' COMMENT '存放位置', - `extra_field` varchar(100) DEFAULT '' COMMENT '额外字段', - `release_paper_code` varchar(50) DEFAULT NULL COMMENT '离型纸编号', - `release_paper_type` varchar(50) DEFAULT NULL COMMENT '离型纸种类', - `release_paper_category` varchar(50) DEFAULT NULL COMMENT '离型纸类别', - `release_paper_specs` varchar(50) DEFAULT NULL COMMENT '离型纸规格', - `padding_material` varchar(100) DEFAULT NULL COMMENT '填充材料', - `packing_material` varchar(100) DEFAULT NULL COMMENT '包装材料', - `special_color` varchar(200) DEFAULT NULL COMMENT '专色配比', - `color_formula` varchar(200) DEFAULT NULL COMMENT '颜色配方', - `file_path` varchar(200) DEFAULT NULL COMMENT '电脑图档存储路径', - `sample_info` varchar(200) DEFAULT NULL COMMENT '样品信息', - `notes` text COMMENT '注意事项', - `remark` text, - `glue_type` varchar(50) DEFAULT NULL COMMENT '滴胶类型', - `packing_type` varchar(50) DEFAULT NULL COMMENT '包装类型', - `creator` varchar(50) DEFAULT NULL COMMENT '制作', - `reviewer` varchar(50) DEFAULT NULL COMMENT '审核', - `factory_manager` varchar(50) DEFAULT NULL COMMENT '厂长', - `quality_manager` varchar(50) DEFAULT NULL COMMENT '品管', - `sales` varchar(50) DEFAULT NULL COMMENT '业务', - `approver` varchar(50) DEFAULT NULL COMMENT '核准', - `status` tinyint DEFAULT '1' COMMENT '状态: 1-草稿, 2-待审核, 3-已启用, 4-已归档', - `create_by` bigint unsigned DEFAULT NULL COMMENT '创建人ID', - `reviewer_id` bigint unsigned DEFAULT NULL COMMENT '审核人ID', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', - `deleted` tinyint NOT NULL DEFAULT '0' COMMENT '软删除: 0-正常, 1-已删除', - `update_by` bigint unsigned DEFAULT NULL COMMENT '更新人ID', - PRIMARY KEY (`id`), - UNIQUE KEY `uk_card_no` (`card_no`), - KEY `idx_customer` (`customer_id`), - KEY `idx_status` (`status`), - KEY `idx_standard_card_customer` (`customer_id`), - KEY `idx_standard_card_status` (`status`), - KEY `idx_creator` (`create_by`), - KEY `idx_reviewer` (`reviewer_id`), - CONSTRAINT `fk_prd_std_card_customer` FOREIGN KEY (`customer_id`) REFERENCES `crm_customer` (`id`) ON DELETE SET NULL ON UPDATE CASCADE -) ENGINE=InnoDB AUTO_INCREMENT=16 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='标准卡表'; - --- 生产工单表 -CREATE TABLE `prd_work_order` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '工单ID', - `work_order_no` varchar(50) NOT NULL COMMENT '工单编号', - `work_order_date` date DEFAULT NULL COMMENT '工单日期', - `sales_order_id` bigint unsigned DEFAULT NULL COMMENT '销售订单ID', - `material_id` bigint unsigned NOT NULL COMMENT '产品ID', - `plan_qty` decimal(18,4) NOT NULL COMMENT '计划数量', - `completed_qty` decimal(18,4) DEFAULT '0.0000' COMMENT '已完成数量', - `unit` varchar(20) DEFAULT NULL COMMENT '单位', - `plan_start_date` date DEFAULT NULL COMMENT '计划开工日期', - `plan_end_date` date DEFAULT NULL COMMENT '计划完工日期', - `actual_start_date` date DEFAULT NULL COMMENT '实际开工日期', - `actual_end_date` date DEFAULT NULL COMMENT '实际完工日期', - `workshop_id` bigint unsigned DEFAULT NULL COMMENT '车间ID', - `workcenter_id` bigint unsigned DEFAULT NULL COMMENT '工作中心ID', - `priority` tinyint DEFAULT '1' COMMENT '优先级: 1-低, 2-中, 3-高, 4-紧急', - `status` tinyint DEFAULT '1' COMMENT '状态: 1-待开工, 2-生产中, 3-已完成, 4-已关闭', - `remark` text COMMENT '备注', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', - `create_by` bigint unsigned DEFAULT NULL COMMENT '创建人ID', - `deleted` tinyint NOT NULL DEFAULT '0' COMMENT '软删除标记', - `update_by` bigint unsigned DEFAULT NULL COMMENT '更新人ID', - PRIMARY KEY (`id`), - UNIQUE KEY `uk_work_order_no` (`work_order_no`), - KEY `idx_material` (`material_id`), - KEY `idx_status` (`status`), - KEY `idx_sales_order` (`sales_order_id`), - KEY `idx_workshop` (`workshop_id`), - KEY `idx_workcenter` (`workcenter_id`), - CONSTRAINT `fk_prd_wo_material` FOREIGN KEY (`material_id`) REFERENCES `inv_material` (`id`) ON DELETE RESTRICT ON UPDATE CASCADE, - CONSTRAINT `fk_prd_wo_sales_order` FOREIGN KEY (`sales_order_id`) REFERENCES `sal_order` (`id`) ON DELETE SET NULL ON UPDATE CASCADE, - CONSTRAINT `fk_prd_work_order_material` FOREIGN KEY (`material_id`) REFERENCES `inv_material` (`id`) ON DELETE RESTRICT ON UPDATE CASCADE, - CONSTRAINT `fk_prd_work_order_sales_order` FOREIGN KEY (`sales_order_id`) REFERENCES `sal_order` (`id`) ON DELETE SET NULL ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='生产工单表'; - --- 工单色序表(多色套印) -CREATE TABLE `prd_work_order_color_seq` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID', - `work_order_id` bigint unsigned NOT NULL COMMENT '工单ID', - `seq_no` int NOT NULL COMMENT '色序号: 1,2,3...', - `color_name` varchar(50) NOT NULL COMMENT '颜色名称: 红,黄,蓝,黑...', - `screen_plate_id` bigint unsigned DEFAULT NULL COMMENT '网版ID', - `ink_formula_id` bigint unsigned DEFAULT NULL COMMENT '油墨配方ID', - `estimated_duration_hours` decimal(18,4) DEFAULT '4.0000' COMMENT '预计耗时(小时)', - `equipment_type_required` varchar(50) NOT NULL COMMENT '所需设备类型: printing, die_cut...', - `depends_on_seq` int DEFAULT NULL COMMENT '依赖前序色序号(前序完成后才能开始)', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - `deleted` tinyint NOT NULL DEFAULT '0' COMMENT '软删除标记', - PRIMARY KEY (`id`), - UNIQUE KEY `uk_work_order_seq` (`work_order_id`,`seq_no`), - KEY `idx_work_order` (`work_order_id`), - KEY `idx_screen_plate` (`screen_plate_id`), - KEY `idx_ink_formula` (`ink_formula_id`), - CONSTRAINT `fk_prd_wo_color_seq_workorder` FOREIGN KEY (`work_order_id`) REFERENCES `prd_work_order` (`id`) ON DELETE CASCADE ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='工单色序表(多色套印)'; - --- 生产报工表 -CREATE TABLE `prd_work_report` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT, - `report_no` varchar(50) NOT NULL COMMENT '报工单号', - `work_order_id` bigint unsigned NOT NULL COMMENT '工单ID', - `work_order_no` varchar(50) DEFAULT NULL COMMENT '工单编号', - `process_name` varchar(50) DEFAULT NULL COMMENT '工序名称', - `process_seq` int DEFAULT NULL COMMENT '工序序号', - `equipment_id` bigint unsigned DEFAULT NULL COMMENT '设备ID', - `operator_id` bigint unsigned DEFAULT NULL COMMENT '操作员ID', - `operator_name` varchar(50) DEFAULT NULL COMMENT '操作员姓名', - `plan_qty` decimal(18,4) DEFAULT NULL COMMENT '计划数量', - `completed_qty` decimal(18,4) DEFAULT '0.0000' COMMENT '完成数量', - `qualified_qty` decimal(18,4) DEFAULT '0.0000' COMMENT '合格数量', - `defective_qty` decimal(18,4) DEFAULT '0.0000' COMMENT '不良数量', - `scrap_qty` decimal(18,4) DEFAULT '0.0000' COMMENT '报废数量', - `start_time` datetime DEFAULT NULL COMMENT '开始时间', - `end_time` datetime DEFAULT NULL COMMENT '结束时间', - `work_hours` decimal(10,2) DEFAULT '0.00' COMMENT '工时', - `is_first_piece` tinyint DEFAULT '0' COMMENT '是否首件: 0-否, 1-是', - `first_piece_status` tinyint DEFAULT NULL COMMENT '首件签样: 1-待签样, 2-已签样, 3-不合格', - `first_piece_inspector` varchar(50) DEFAULT NULL COMMENT '首件签样人', - `remark` text COMMENT '备注', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP, - `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - `deleted` tinyint DEFAULT '0', - PRIMARY KEY (`id`), - UNIQUE KEY `uk_report_no` (`report_no`), - KEY `idx_work_order` (`work_order_id`), - KEY `idx_operator` (`operator_id`), - KEY `idx_create_time` (`create_time`) -) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='生产报工表'; - --- 生产工单主表 -CREATE TABLE `prod_work_order` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT, - `work_order_no` varchar(50) NOT NULL COMMENT '工单号', - `order_id` bigint unsigned DEFAULT NULL COMMENT '关联销售订单ID', - `order_no` varchar(50) DEFAULT NULL COMMENT '关联销售订单号', - `bom_id` bigint unsigned DEFAULT NULL COMMENT '关联BOM ID', - `customer_name` varchar(200) DEFAULT NULL COMMENT '客户名称', - `product_name` varchar(200) DEFAULT NULL COMMENT '产品名称', - `quantity` decimal(15,2) DEFAULT '0.00' COMMENT '生产数量', - `unit` varchar(20) DEFAULT NULL COMMENT '单位', - `status` varchar(20) DEFAULT 'pending' COMMENT '状态: pending/confirmed/producing/completed/cancelled', - `priority` varchar(20) DEFAULT 'normal' COMMENT '优先级: low/normal/high/urgent', - `plan_start_date` date DEFAULT NULL COMMENT '计划开始日期', - `plan_end_date` date DEFAULT NULL COMMENT '计划完成日期', - `actual_start_date` date DEFAULT NULL COMMENT '实际开始日期', - `actual_end_date` date DEFAULT NULL COMMENT '实际完成日期', - `remark` text COMMENT '备注', - `create_by` bigint unsigned DEFAULT NULL COMMENT '创建人ID', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - `update_by` bigint unsigned DEFAULT NULL COMMENT '更新人ID', - `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', - `deleted` tinyint DEFAULT '0' COMMENT '删除标记', - PRIMARY KEY (`id`), - UNIQUE KEY `uk_work_order_no` (`work_order_no`), - KEY `idx_order_no` (`order_no`), - KEY `idx_status` (`status`), - KEY `idx_create_time` (`create_time`) -) ENGINE=InnoDB AUTO_INCREMENT=125 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='生产工单主表'; - --- 生产工单明细表 -CREATE TABLE `prod_work_order_item` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT, - `work_order_id` bigint unsigned NOT NULL COMMENT '工单ID', - `line_no` int DEFAULT '0' COMMENT '行号', - `material_id` bigint unsigned DEFAULT NULL COMMENT '物料ID', - `material_name` varchar(200) DEFAULT NULL COMMENT '物料名称', - `quantity` decimal(15,2) DEFAULT '0.00' COMMENT '数量', - `unit` varchar(20) DEFAULT NULL COMMENT '单位', - `unit_price` decimal(15,2) DEFAULT '0.00' COMMENT '单价', - `total_price` decimal(15,2) DEFAULT '0.00' COMMENT '总价', - `remark` text COMMENT '备注', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - PRIMARY KEY (`id`), - KEY `idx_work_order_id` (`work_order_id`) -) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='生产工单明细表'; - --- 工单物料需求表 -CREATE TABLE `prod_work_order_material_req` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT, - `work_order_id` bigint unsigned NOT NULL COMMENT '工单ID', - `bom_line_id` bigint unsigned DEFAULT NULL COMMENT 'BOM行ID', - `material_id` bigint unsigned DEFAULT NULL COMMENT '物料ID', - `material_name` varchar(200) DEFAULT NULL COMMENT '物料名称', - `required_qty` decimal(14,3) DEFAULT '0.000' COMMENT '需求数量', - `unit` varchar(20) DEFAULT NULL COMMENT '单位', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP, - PRIMARY KEY (`id`), - KEY `idx_work_order` (`work_order_id`), - KEY `idx_material` (`material_id`) -) ENGINE=InnoDB AUTO_INCREMENT=19 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='工单物料需求表'; - --- 采购订单表 -CREATE TABLE `pur_order_deprecated` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT, - `order_no` varchar(50) NOT NULL COMMENT '订单编号', - `order_date` date DEFAULT NULL COMMENT '订单日期', - `supplier_id` bigint unsigned NOT NULL COMMENT '供应商ID', - `contact_name` varchar(50) DEFAULT NULL COMMENT '联系人', - `contact_phone` varchar(20) DEFAULT NULL COMMENT '联系电话', - `delivery_address` varchar(255) DEFAULT NULL COMMENT '送货地址', - `total_amount` decimal(18,4) DEFAULT '0.0000' COMMENT '总金额', - `tax_amount` decimal(18,4) DEFAULT '0.0000' COMMENT '税额', - `total_with_tax` decimal(18,4) DEFAULT '0.0000' COMMENT '含税总额', - `currency` varchar(10) DEFAULT 'CNY' COMMENT '币种', - `exchange_rate` decimal(10,4) DEFAULT '1.0000' COMMENT '汇率', - `payment_terms` varchar(100) DEFAULT NULL COMMENT '付款条件', - `delivery_date` date DEFAULT NULL COMMENT '交货日期', - `settlement_method` varchar(50) DEFAULT NULL COMMENT '结算方式', - `status` tinyint DEFAULT '1' COMMENT '状态: 1-待确认, 2-已确认, 3-部分到货, 4-已完成, 5-已取消', - `remark` text COMMENT '备注', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP, - `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - `create_by` bigint unsigned DEFAULT NULL, - `update_by` bigint unsigned DEFAULT NULL, - `deleted` tinyint DEFAULT '0', - PRIMARY KEY (`id`), - UNIQUE KEY `uk_order_no` (`order_no`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='采购订单表'; - --- 采购订单明细表 -CREATE TABLE `pur_order_detail_deprecated` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT, - `order_id` bigint unsigned NOT NULL COMMENT '订单ID', - `material_id` bigint unsigned NOT NULL COMMENT '物料ID', - `quantity` decimal(18,4) NOT NULL COMMENT '采购数量', - `unit` varchar(20) DEFAULT NULL COMMENT '单位', - `unit_price` decimal(18,4) DEFAULT NULL COMMENT '单价', - `tax_rate` decimal(5,2) DEFAULT '0.00' COMMENT '税率(%)', - `amount` decimal(18,4) DEFAULT NULL COMMENT '金额', - `tax_amount` decimal(18,4) DEFAULT NULL COMMENT '税额', - `total_amount` decimal(18,4) DEFAULT NULL COMMENT '含税金额', - `received_qty` decimal(18,4) DEFAULT '0.0000' COMMENT '已到货数量', - `delivery_date` date DEFAULT NULL COMMENT '交货日期', - `remark` varchar(255) DEFAULT NULL COMMENT '备注', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP, - PRIMARY KEY (`id`), - KEY `fk_pur_order_detail_order` (`order_id`), - CONSTRAINT `fk_pur_order_detail_order` FOREIGN KEY (`order_id`) REFERENCES `pur_order_deprecated` (`id`) ON DELETE CASCADE ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='采购订单明细表'; - --- 采购单主表 -CREATE TABLE `pur_purchase_order` ( - `id` int unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID', - `po_no` varchar(50) NOT NULL COMMENT '采购单号', - `supplier_id` bigint unsigned DEFAULT NULL COMMENT '供应商ID', - `supplier_name` varchar(100) NOT NULL COMMENT '供应商名称', - `supplier_code` varchar(50) DEFAULT NULL COMMENT '供应商编码', - `order_date` date NOT NULL COMMENT '订单日期', - `delivery_date` date DEFAULT NULL COMMENT '预计交货日期', - `currency` varchar(10) DEFAULT 'CNY' COMMENT '币种', - `exchange_rate` decimal(18,4) DEFAULT '1.0000' COMMENT '汇率', - `total_amount` decimal(18,4) DEFAULT '0.0000' COMMENT '订单总金额', - `total_quantity` decimal(18,4) DEFAULT '0.0000' COMMENT '订单总数量', - `tax_rate` decimal(18,4) DEFAULT '13.0000' COMMENT '税率%', - `tax_amount` decimal(18,4) DEFAULT '0.0000' COMMENT '税额', - `grand_total` decimal(18,4) DEFAULT '0.0000' COMMENT '含税总金额', - `status` tinyint unsigned DEFAULT '10' COMMENT '状态: 10-草稿,20-待审批,30-已审批,40-部分收货,50-已完成,90-已关闭', - `over_receipt_tolerance` decimal(18,4) DEFAULT '5.0000' COMMENT '超收容差率%', - `payment_terms` varchar(100) DEFAULT NULL COMMENT '付款条款', - `delivery_address` text COMMENT '送货地址', - `contact_person` varchar(50) DEFAULT NULL COMMENT '联系人', - `contact_phone` varchar(50) DEFAULT NULL COMMENT '联系电话', - `remark` text COMMENT '备注', - `create_by` int unsigned DEFAULT NULL COMMENT '创建人ID', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - `update_by` int unsigned DEFAULT NULL COMMENT '更新人ID', - `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', - `audit_by` int unsigned DEFAULT NULL COMMENT '审批人ID', - `audit_time` datetime DEFAULT NULL COMMENT '审批时间', - `close_by` int unsigned DEFAULT NULL COMMENT '关闭人ID', - `close_time` datetime DEFAULT NULL COMMENT '关闭时间', - `close_reason` varchar(200) DEFAULT NULL COMMENT '关闭原因', - `deleted` tinyint NOT NULL DEFAULT '0' COMMENT '软删除: 0-正常, 1-已删除', - PRIMARY KEY (`id`), - UNIQUE KEY `uk_po_no` (`po_no`), - KEY `idx_supplier` (`supplier_id`), - KEY `idx_status` (`status`), - KEY `idx_order_date` (`order_date`), - CONSTRAINT `fk_pur_po_supplier` FOREIGN KEY (`supplier_id`) REFERENCES `pur_supplier` (`id`) ON DELETE SET NULL ON UPDATE CASCADE -) ENGINE=InnoDB AUTO_INCREMENT=84 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='采购单主表'; - --- 采购单行表 -CREATE TABLE `pur_purchase_order_line` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID', - `po_id` int unsigned NOT NULL COMMENT '采购单ID', - `line_no` int unsigned NOT NULL COMMENT '行号', - `material_id` bigint unsigned DEFAULT NULL COMMENT '物料ID', - `material_code` varchar(50) NOT NULL COMMENT '物料编码', - `material_name` varchar(200) NOT NULL COMMENT '物料名称', - `material_spec` varchar(500) DEFAULT NULL COMMENT '物料规格', - `unit` varchar(20) DEFAULT '件' COMMENT '单位', - `order_qty` decimal(18,4) NOT NULL DEFAULT '0.0000' COMMENT '订购数量', - `received_qty` decimal(18,4) DEFAULT '0.0000' COMMENT '累计入库数量', - `returned_qty` decimal(18,4) DEFAULT '0.0000' COMMENT '累计退货数量', - `unit_price` decimal(18,4) NOT NULL DEFAULT '0.0000' COMMENT '单价', - `amount` decimal(18,4) DEFAULT '0.0000' COMMENT '金额', - `tax_rate` decimal(18,4) DEFAULT '13.0000' COMMENT '税率%', - `tax_amount` decimal(18,4) DEFAULT '0.0000' COMMENT '税额', - `line_total` decimal(18,4) DEFAULT '0.0000' COMMENT '行合计', - `require_date` date DEFAULT NULL COMMENT '需求日期', - `closed_flag` tinyint(1) DEFAULT '0' COMMENT '行关闭标志', - `closed_reason` varchar(200) DEFAULT NULL COMMENT '关闭原因', - `remark` text COMMENT '备注', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', - PRIMARY KEY (`id`), - UNIQUE KEY `uk_po_line` (`po_id`,`line_no`), - KEY `idx_material` (`material_id`), - CONSTRAINT `fk_pur_line_material` FOREIGN KEY (`material_id`) REFERENCES `inv_material` (`id`) ON DELETE SET NULL ON UPDATE CASCADE, - CONSTRAINT `fk_pur_line_po` FOREIGN KEY (`po_id`) REFERENCES `pur_purchase_order` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, - CONSTRAINT `pur_purchase_order_line_ibfk_1` FOREIGN KEY (`po_id`) REFERENCES `pur_purchase_order` (`id`) ON DELETE CASCADE -) ENGINE=InnoDB AUTO_INCREMENT=89 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='采购单行表'; - --- 采购对账主表 -CREATE TABLE `pur_purchase_reconciliation` ( - `id` bigint NOT NULL AUTO_INCREMENT, - `reconciliation_no` varchar(32) NOT NULL COMMENT '对账单号', - `status` tinyint NOT NULL DEFAULT '1' COMMENT '状态: 1=草稿, 2=已确认, 3=部分核销, 4=已核销完成, 9=已关闭', - `supplier_id` bigint NOT NULL COMMENT '供应商ID', - `supplier_name` varchar(128) NOT NULL DEFAULT '' COMMENT '供应商名称', - `period_start` date NOT NULL COMMENT '对账开始日期', - `period_end` date NOT NULL COMMENT '对账结束日期', - `receipt_amount` decimal(14,2) NOT NULL DEFAULT '0.00' COMMENT '收货金额', - `return_amount` decimal(14,2) NOT NULL DEFAULT '0.00' COMMENT '退货金额', - `net_amount` decimal(14,2) NOT NULL DEFAULT '0.00' COMMENT '净额', - `discount_amount` decimal(14,2) NOT NULL DEFAULT '0.00' COMMENT '折扣金额', - `paid_amount` decimal(14,2) NOT NULL DEFAULT '0.00' COMMENT '已核销金额', - `balance_amount` decimal(14,2) NOT NULL DEFAULT '0.00' COMMENT '余额', - `remark` varchar(512) NOT NULL DEFAULT '' COMMENT '备注', - `create_by` bigint DEFAULT NULL COMMENT '创建人', - `confirm_by` bigint DEFAULT NULL COMMENT '确认人', - `confirm_time` datetime DEFAULT NULL COMMENT '确认时间', - `close_by` bigint DEFAULT NULL COMMENT '关闭人', - `close_time` datetime DEFAULT NULL COMMENT '关闭时间', - `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', - `deleted` tinyint NOT NULL DEFAULT '0' COMMENT '软删除: 0=未删除, 1=已删除', - PRIMARY KEY (`id`), - UNIQUE KEY `uk_reconciliation_no` (`reconciliation_no`), - KEY `idx_supplier_id` (`supplier_id`), - KEY `idx_status` (`status`), - KEY `idx_period` (`period_start`,`period_end`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='采购对账主表'; - --- 采购对账核销记录表 -CREATE TABLE `pur_purchase_reconciliation_writeoff` ( - `id` bigint NOT NULL AUTO_INCREMENT, - `reconciliation_id` bigint NOT NULL COMMENT '对账单ID', - `payable_id` bigint NOT NULL COMMENT '应付单ID', - `amount` decimal(14,2) NOT NULL COMMENT '核销金额', - `write_off_date` date NOT NULL COMMENT '核销日期', - `remark` varchar(512) NOT NULL DEFAULT '' COMMENT '备注', - `create_by` bigint DEFAULT NULL COMMENT '创建人', - `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - PRIMARY KEY (`id`), - KEY `idx_reconciliation_id` (`reconciliation_id`), - KEY `idx_payable_id` (`payable_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='采购对账核销记录表'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='物料表'; --- 采购退货主表 -CREATE TABLE `pur_purchase_return` ( - `id` bigint NOT NULL AUTO_INCREMENT, - `return_no` varchar(32) NOT NULL COMMENT '退货单号', - `status` tinyint NOT NULL DEFAULT '1' COMMENT '状态: 1=待审核, 2=已审核, 3=已完成, 9=已取消', - `order_id` bigint NOT NULL COMMENT '采购订单ID', - `order_no` varchar(32) NOT NULL DEFAULT '' COMMENT '采购订单号', - `supplier_id` bigint NOT NULL COMMENT '供应商ID', - `supplier_name` varchar(128) NOT NULL DEFAULT '' COMMENT '供应商名称', - `warehouse_id` bigint NOT NULL COMMENT '仓库ID', - `receipt_id` bigint DEFAULT NULL COMMENT '收货单ID', - `receipt_no` varchar(32) NOT NULL DEFAULT '' COMMENT '收货单号', - `reason` varchar(512) NOT NULL COMMENT '退货原因', - `return_date` date NOT NULL COMMENT '退货日期', - `total_amount` decimal(14,2) NOT NULL DEFAULT '0.00' COMMENT '退货总金额', - `approve_by` bigint DEFAULT NULL COMMENT '审核人', - `approve_time` datetime DEFAULT NULL COMMENT '审核时间', - `complete_by` bigint DEFAULT NULL COMMENT '完成人', - `complete_time` datetime DEFAULT NULL COMMENT '完成时间', - `outbound_order_id` bigint DEFAULT NULL COMMENT '出库单ID', - `outbound_order_no` varchar(32) DEFAULT NULL COMMENT '出库单号', - `payable_id` bigint DEFAULT NULL COMMENT '红字应付单ID', - `payable_no` varchar(32) DEFAULT NULL COMMENT '红字应付单号', - `remark` varchar(512) NOT NULL DEFAULT '' COMMENT '备注', - `create_by` bigint DEFAULT NULL COMMENT '创建人', - `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', - `deleted` tinyint NOT NULL DEFAULT '0' COMMENT '软删除: 0=未删除, 1=已删除', - PRIMARY KEY (`id`), - UNIQUE KEY `uk_return_no` (`return_no`), - KEY `idx_order_id` (`order_id`), - KEY `idx_supplier_id` (`supplier_id`), - KEY `idx_status` (`status`), - KEY `idx_return_date` (`return_date`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='采购退货主表'; - --- 采购退货明细表 -CREATE TABLE `pur_purchase_return_line` ( - `id` bigint NOT NULL AUTO_INCREMENT, - `return_id` bigint NOT NULL COMMENT '退货单ID', - `line_no` int NOT NULL COMMENT '行号', - `order_line_id` bigint DEFAULT NULL COMMENT '采购订单行ID', - `material_id` bigint NOT NULL COMMENT '物料ID', - `material_code` varchar(64) NOT NULL DEFAULT '' COMMENT '物料编码', - `material_name` varchar(256) NOT NULL DEFAULT '' COMMENT '物料名称', - `material_spec` varchar(256) NOT NULL DEFAULT '' COMMENT '物料规格', - `unit` varchar(32) NOT NULL DEFAULT '件' COMMENT '单位', - `quantity` decimal(14,4) NOT NULL COMMENT '退货数量', - `unit_price` decimal(14,2) NOT NULL COMMENT '单价', - `amount` decimal(14,2) NOT NULL COMMENT '金额', - `batch_no` varchar(64) NOT NULL DEFAULT '' COMMENT '批次号', - `reason` varchar(512) NOT NULL DEFAULT '' COMMENT '退货原因', - `remark` varchar(512) NOT NULL DEFAULT '' COMMENT '备注', - PRIMARY KEY (`id`), - KEY `idx_return_id` (`return_id`), - KEY `idx_material_id` (`material_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='采购退货明细表'; +-- 仓库表 +CREATE TABLE `inv_warehouse` ( + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '仓库ID', + `warehouse_code` VARCHAR(50) NOT NULL COMMENT '仓库编码', + `warehouse_name` VARCHAR(100) NOT NULL COMMENT '仓库名称', + `warehouse_type` TINYINT COMMENT '仓库类型: 1-原材料仓, 2-半成品仓, 3-成品仓, 4-辅料仓', + `province` VARCHAR(50) COMMENT '省份', + `city` VARCHAR(50) COMMENT '城市', + `address` VARCHAR(255) COMMENT '详细地址', + `manager_id` BIGINT UNSIGNED COMMENT '仓库负责人ID', + `contact_phone` VARCHAR(20) COMMENT '联系电话', + `status` TINYINT DEFAULT 1 COMMENT '状态: 0-禁用, 1-启用', + `remark` VARCHAR(255) COMMENT '备注', + `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `deleted` TINYINT DEFAULT 0 COMMENT '删除标记', + PRIMARY KEY (`id`), + UNIQUE KEY `uk_warehouse_code` (`warehouse_code`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='仓库表'; --- 采购入库单表 -CREATE TABLE `pur_receipt_deprecated` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '入库单ID', - `receipt_no` varchar(50) NOT NULL COMMENT '入库单号', - `receipt_date` date DEFAULT NULL COMMENT '入库日期', - `order_id` bigint unsigned DEFAULT NULL COMMENT '采购订单ID', - `supplier_id` bigint unsigned NOT NULL COMMENT '供应商ID', - `warehouse_id` bigint unsigned NOT NULL COMMENT '仓库ID', - `total_amount` decimal(18,4) DEFAULT '0.0000' COMMENT '总金额', - `inspector_id` bigint unsigned DEFAULT NULL COMMENT '检验员ID', - `inspection_result` tinyint DEFAULT NULL COMMENT '检验结果: 1-合格, 2-不合格', - `status` tinyint DEFAULT '1' COMMENT '状态: 1-待入库, 2-已入库', - `remark` text COMMENT '备注', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', - `create_by` bigint unsigned DEFAULT NULL COMMENT '创建人ID', - PRIMARY KEY (`id`), - UNIQUE KEY `uk_receipt_no` (`receipt_no`), - KEY `idx_order` (`order_id`), - KEY `idx_supplier` (`supplier_id`), +-- 库存表 +CREATE TABLE `inv_inventory` ( + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '库存ID', + `material_id` BIGINT UNSIGNED NOT NULL COMMENT '物料ID', + `warehouse_id` BIGINT UNSIGNED NOT NULL COMMENT '仓库ID', + `location_code` VARCHAR(50) COMMENT '库位编码', + `quantity` DECIMAL(18,4) DEFAULT 0 COMMENT '库存数量', + `locked_qty` DECIMAL(18,4) DEFAULT 0 COMMENT '锁定数量', + `available_qty` DECIMAL(18,4) DEFAULT 0 COMMENT '可用数量', + `batch_no` VARCHAR(50) COMMENT '批次号', + `production_date` DATE COMMENT '生产日期', + `expiry_date` DATE COMMENT '过期日期', + `update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + PRIMARY KEY (`id`), + UNIQUE KEY `uk_material_warehouse_batch` (`material_id`, `warehouse_id`, `batch_no`), KEY `idx_warehouse` (`warehouse_id`), - KEY `idx_inspector` (`inspector_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='采购入库单表'; + KEY `idx_batch` (`batch_no`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='库存表'; --- 采购入库明细表 -CREATE TABLE `pur_receipt_detail_deprecated` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '明细ID', - `receipt_id` bigint unsigned NOT NULL COMMENT '入库单ID', - `material_id` bigint unsigned NOT NULL COMMENT '物料ID', - `order_detail_id` bigint unsigned DEFAULT NULL COMMENT '订单明细ID', - `quantity` decimal(18,4) NOT NULL COMMENT '入库数量', - `unit` varchar(20) DEFAULT NULL COMMENT '单位', - `unit_price` decimal(18,4) DEFAULT NULL COMMENT '单价', - `amount` decimal(18,4) DEFAULT NULL COMMENT '金额', - `batch_no` varchar(50) DEFAULT NULL COMMENT '批次号', - `production_date` date DEFAULT NULL COMMENT '生产日期', - `expiry_date` date DEFAULT NULL COMMENT '过期日期', - `location_code` varchar(50) DEFAULT NULL COMMENT '库位编码', - `remark` varchar(255) DEFAULT NULL COMMENT '备注', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', +-- 库存流水表(不可修改、不可删除 - 审计要求) +CREATE TABLE `inv_inventory_log` ( + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '流水ID', + `material_id` BIGINT UNSIGNED NOT NULL COMMENT '物料ID', + `warehouse_id` BIGINT UNSIGNED NOT NULL COMMENT '仓库ID', + `batch_no` VARCHAR(50) COMMENT '批次号', + `operation_type` TINYINT NOT NULL COMMENT '操作类型: 1-入库, 2-出库, 3-盘点, 4-调拨, 5-报废, 6-锁定, 7-解锁', + `operation_qty` DECIMAL(18,4) NOT NULL COMMENT '操作数量', + `before_qty` DECIMAL(18,4) COMMENT '操作前数量', + `after_qty` DECIMAL(18,4) COMMENT '操作后数量', + `business_type` VARCHAR(50) COMMENT '业务类型', + `business_no` VARCHAR(50) COMMENT '业务单号', + `remark` VARCHAR(255) COMMENT '备注', + `operator_id` BIGINT UNSIGNED COMMENT '操作人ID', + `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', PRIMARY KEY (`id`), - KEY `idx_receipt` (`receipt_id`), KEY `idx_material` (`material_id`), - KEY `idx_order_detail` (`order_detail_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='采购入库明细表'; + KEY `idx_warehouse` (`warehouse_id`), + KEY `idx_operation_type` (`operation_type`), + KEY `idx_business_no` (`business_no`), + KEY `idx_create_time` (`create_time`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_ai_ci COMMENT='库存流水表(不可修改、不可删除)'; --- 采购申请主表 +-- ======================================================== +-- 5. 采购管理模块 +-- ======================================================== + +-- 采购申请单表 CREATE TABLE `pur_request` ( - `id` int unsigned NOT NULL AUTO_INCREMENT, - `request_no` varchar(50) NOT NULL COMMENT '申请单号', - `request_date` date DEFAULT NULL COMMENT '申请日期', - `request_type` varchar(20) DEFAULT 'material' COMMENT '申请类型', - `request_dept_id` int unsigned DEFAULT NULL COMMENT '申请部门ID', - `request_dept` varchar(100) DEFAULT NULL COMMENT '申请部门', - `requester_id` int unsigned DEFAULT NULL COMMENT '申请人ID', - `requester_name` varchar(50) DEFAULT NULL COMMENT '申请人', - `reviewer_id` int unsigned DEFAULT NULL COMMENT '审校人ID', - `reviewer_name` varchar(50) DEFAULT NULL COMMENT '审校人姓名', - `approver_id` int unsigned DEFAULT NULL COMMENT '批准人ID', - `approver_name` varchar(50) DEFAULT NULL COMMENT '批准人姓名', - `total_amount` decimal(14,2) DEFAULT '0.00' COMMENT '总金额', - `currency` varchar(10) DEFAULT 'CNY' COMMENT '币种', - `status` tinyint DEFAULT '0' COMMENT '状态: 0-草稿, 1-待审批, 2-已审批, 3-已转采购, 9-已关闭', - `priority` tinyint DEFAULT '1' COMMENT '优先级', - `expected_date` date DEFAULT NULL COMMENT '期望交期', - `supplier_name` varchar(100) DEFAULT NULL COMMENT '供应商名称', - `remark` text COMMENT '备注', - `create_by` int unsigned DEFAULT NULL, - `create_time` datetime DEFAULT CURRENT_TIMESTAMP, - `update_by` int unsigned DEFAULT NULL, - `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - `deleted` tinyint NOT NULL DEFAULT '0' COMMENT '软删除: 0-正常, 1-已删除', + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '申请单ID', + `request_no` VARCHAR(50) NOT NULL COMMENT '申请单号', + `request_date` DATE COMMENT '申请日期', + `request_dept_id` BIGINT UNSIGNED COMMENT '申请部门ID', + `requester_id` BIGINT UNSIGNED COMMENT '申请人ID', + `total_amount` DECIMAL(18,4) DEFAULT 0 COMMENT '总金额', + `currency` VARCHAR(10) DEFAULT 'CNY' COMMENT '币种', + `urgency_level` TINYINT DEFAULT 1 COMMENT '紧急程度: 1-一般, 2-紧急, 3-特急', + `status` TINYINT DEFAULT 1 COMMENT '状态: 1-待审批, 2-审批中, 3-已批准, 4-已拒绝, 5-已关闭', + `remark` TEXT COMMENT '备注', + `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `create_by` BIGINT UNSIGNED COMMENT '创建人ID', + `update_by` BIGINT UNSIGNED COMMENT '更新人ID', + `deleted` TINYINT DEFAULT 0 COMMENT '删除标记', PRIMARY KEY (`id`), UNIQUE KEY `uk_request_no` (`request_no`), KEY `idx_status` (`status`), - KEY `idx_request_date` (`request_date`), - KEY `idx_request_dept` (`request_dept_id`), - KEY `idx_requester` (`requester_id`) -) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='采购申请主表'; + KEY `idx_request_date` (`request_date`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='采购申请单表'; -- 采购申请明细表 CREATE TABLE `pur_request_detail` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '明细ID', - `request_id` bigint unsigned NOT NULL COMMENT '申请单ID', - `material_id` bigint unsigned NOT NULL COMMENT '物料ID', - `quantity` decimal(18,4) NOT NULL COMMENT '申请数量', - `unit` varchar(20) DEFAULT NULL COMMENT '单位', - `required_date` date DEFAULT NULL COMMENT '需求日期', - `purpose` varchar(255) DEFAULT NULL COMMENT '用途说明', - `remark` varchar(255) DEFAULT NULL COMMENT '备注', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - `deleted` tinyint NOT NULL DEFAULT '0' COMMENT '软删除标记', + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '明细ID', + `request_id` BIGINT UNSIGNED NOT NULL COMMENT '申请单ID', + `material_id` BIGINT UNSIGNED NOT NULL COMMENT '物料ID', + `quantity` DECIMAL(18,4) NOT NULL COMMENT '申请数量', + `unit` VARCHAR(20) COMMENT '单位', + `required_date` DATE COMMENT '需求日期', + `purpose` VARCHAR(255) COMMENT '用途说明', + `remark` VARCHAR(255) COMMENT '备注', + `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', PRIMARY KEY (`id`), KEY `idx_request` (`request_id`), KEY `idx_material` (`material_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='采购申请明细表'; --- 采购申请明细表 -CREATE TABLE `pur_request_item` ( - `id` int unsigned NOT NULL AUTO_INCREMENT, - `request_id` int unsigned NOT NULL COMMENT '申请ID', - `line_no` int unsigned NOT NULL COMMENT '行号', - `material_id` int unsigned DEFAULT NULL COMMENT '物料ID', - `material_code` varchar(50) DEFAULT NULL COMMENT '物料编码', - `material_name` varchar(200) DEFAULT NULL COMMENT '物料名称', - `material_spec` varchar(500) DEFAULT NULL COMMENT '物料规格', - `material_unit` varchar(20) DEFAULT NULL COMMENT '单位', - `quantity` decimal(14,3) DEFAULT '0.000' COMMENT '数量', - `price` decimal(14,4) DEFAULT '0.0000' COMMENT '单价', - `amount` decimal(14,2) DEFAULT '0.00' COMMENT '金额', - `supplier_name` varchar(100) DEFAULT NULL COMMENT '供应商名称', - `expected_date` date DEFAULT NULL COMMENT '期望交期', - `remark` text COMMENT '备注', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP, - `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - `deleted` tinyint(1) DEFAULT '0', +-- 采购订单表 +CREATE TABLE `pur_order` ( + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '订单ID', + `order_no` VARCHAR(50) NOT NULL COMMENT '订单编号', + `order_date` DATE COMMENT '订单日期', + `supplier_id` BIGINT UNSIGNED NOT NULL COMMENT '供应商ID', + `contact_name` VARCHAR(50) COMMENT '联系人', + `contact_phone` VARCHAR(20) COMMENT '联系电话', + `delivery_address` VARCHAR(255) COMMENT '送货地址', + `total_amount` DECIMAL(18,4) DEFAULT 0 COMMENT '总金额', + `tax_amount` DECIMAL(18,4) DEFAULT 0 COMMENT '税额', + `total_with_tax` DECIMAL(18,4) DEFAULT 0 COMMENT '含税总额', + `currency` VARCHAR(10) DEFAULT 'CNY' COMMENT '币种', + `exchange_rate` DECIMAL(10,4) DEFAULT 1 COMMENT '汇率', + `payment_terms` VARCHAR(100) COMMENT '付款条件', + `delivery_date` DATE COMMENT '交货日期', + `settlement_method` VARCHAR(50) COMMENT '结算方式', + `status` TINYINT DEFAULT 1 COMMENT '状态: 1-待确认, 2-已确认, 3-部分到货, 4-已完成, 5-已取消', + `remark` TEXT COMMENT '备注', + `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `create_by` BIGINT UNSIGNED COMMENT '创建人ID', + `update_by` BIGINT UNSIGNED COMMENT '更新人ID', + `deleted` TINYINT DEFAULT 0 COMMENT '删除标记', PRIMARY KEY (`id`), - KEY `idx_request` (`request_id`), - KEY `idx_material_id` (`material_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='采购申请明细表'; + UNIQUE KEY `uk_order_no` (`order_no`), + KEY `idx_supplier` (`supplier_id`), + KEY `idx_status` (`status`), + KEY `idx_order_date` (`order_date`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='采购订单表'; --- 供应商表 -CREATE TABLE `pur_supplier` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT, - `supplier_code` varchar(50) NOT NULL COMMENT '供应商编码', - `supplier_name` varchar(100) NOT NULL COMMENT '供应商名称', - `short_name` varchar(50) DEFAULT NULL COMMENT '供应商简称', - `supplier_type` tinyint DEFAULT NULL COMMENT '供应商类型: 1-原材料, 2-辅料, 3-设备, 4-服务', - `province` varchar(50) DEFAULT NULL COMMENT '省份', - `city` varchar(50) DEFAULT NULL COMMENT '城市', - `address` varchar(255) DEFAULT NULL COMMENT '详细地址', - `contact_name` varchar(50) DEFAULT NULL COMMENT '联系人', - `contact_phone` varchar(20) DEFAULT NULL COMMENT '联系电话', - `contact_email` varchar(100) DEFAULT NULL COMMENT '联系邮箱', - `business_license` varchar(50) DEFAULT NULL COMMENT '营业执照号', - `tax_number` varchar(50) DEFAULT NULL COMMENT '税号', - `bank_name` varchar(100) DEFAULT NULL COMMENT '开户银行', - `bank_account` varchar(50) DEFAULT NULL COMMENT '银行账号', - `credit_level` varchar(20) DEFAULT NULL COMMENT '信用等级', - `cooperation_status` tinyint DEFAULT '1' COMMENT '合作状态: 1-合作中, 2-暂停合作, 3-终止合作', - `settlement_method` varchar(50) DEFAULT NULL COMMENT '结算方式', - `payment_terms` varchar(100) DEFAULT NULL COMMENT '付款条件', - `status` tinyint DEFAULT '1' COMMENT '状态: 0-禁用, 1-启用', - `remark` text COMMENT '备注', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP, - `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - `create_by` bigint unsigned DEFAULT NULL, - `update_by` bigint unsigned DEFAULT NULL, - `deleted` tinyint NOT NULL DEFAULT '0' COMMENT '软删除: 0-正常, 1-已删除', +-- 采购订单明细表 +CREATE TABLE `pur_order_detail` ( + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '明细ID', + `order_id` BIGINT UNSIGNED NOT NULL COMMENT '订单ID', + `material_id` BIGINT UNSIGNED NOT NULL COMMENT '物料ID', + `quantity` DECIMAL(18,4) NOT NULL COMMENT '采购数量', + `unit` VARCHAR(20) COMMENT '单位', + `unit_price` DECIMAL(18,4) COMMENT '单价', + `tax_rate` DECIMAL(5,2) DEFAULT 0 COMMENT '税率(%)', + `amount` DECIMAL(18,4) COMMENT '金额', + `tax_amount` DECIMAL(18,4) COMMENT '税额', + `total_amount` DECIMAL(18,4) COMMENT '含税金额', + `received_qty` DECIMAL(18,4) DEFAULT 0 COMMENT '已到货数量', + `delivery_date` DATE COMMENT '交货日期', + `remark` VARCHAR(255) COMMENT '备注', + `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', PRIMARY KEY (`id`), - UNIQUE KEY `uk_supplier_code` (`supplier_code`) -) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='供应商表'; + KEY `idx_order` (`order_id`), + KEY `idx_material` (`material_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='采购订单明细表'; --- 供应商物料关联表 -CREATE TABLE `pur_supplier_material` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID', - `supplier_id` bigint unsigned NOT NULL COMMENT '供应商ID', - `material_id` bigint unsigned NOT NULL COMMENT '物料ID', - `supply_price` decimal(18,4) DEFAULT NULL COMMENT '供应价格', - `min_order_qty` decimal(18,4) DEFAULT NULL COMMENT '最小订购量', - `lead_time` int DEFAULT NULL COMMENT '交货周期(天)', - `is_default` tinyint DEFAULT '0' COMMENT '是否默认供应商: 0-否, 1-是', - `status` tinyint DEFAULT '1' COMMENT '状态', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', - `deleted` tinyint NOT NULL DEFAULT '0' COMMENT '软删除标记', +-- 采购入库单表 +CREATE TABLE `pur_receipt` ( + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '入库单ID', + `receipt_no` VARCHAR(50) NOT NULL COMMENT '入库单号', + `receipt_date` DATE COMMENT '入库日期', + `order_id` BIGINT UNSIGNED COMMENT '采购订单ID', + `supplier_id` BIGINT UNSIGNED NOT NULL COMMENT '供应商ID', + `warehouse_id` BIGINT UNSIGNED NOT NULL COMMENT '仓库ID', + `total_amount` DECIMAL(18,4) DEFAULT 0 COMMENT '总金额', + `inspector_id` BIGINT UNSIGNED COMMENT '检验员ID', + `inspection_result` TINYINT COMMENT '检验结果: 1-合格, 2-不合格', + `status` TINYINT DEFAULT 1 COMMENT '状态: 1-待入库, 2-已入库', + `remark` TEXT COMMENT '备注', + `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `create_by` BIGINT UNSIGNED COMMENT '创建人ID', + PRIMARY KEY (`id`), + UNIQUE KEY `uk_receipt_no` (`receipt_no`), + KEY `idx_order` (`order_id`), + KEY `idx_supplier` (`supplier_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='采购入库单表'; + +-- 采购入库明细表 +CREATE TABLE `pur_receipt_detail` ( + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '明细ID', + `receipt_id` BIGINT UNSIGNED NOT NULL COMMENT '入库单ID', + `material_id` BIGINT UNSIGNED NOT NULL COMMENT '物料ID', + `order_detail_id` BIGINT UNSIGNED COMMENT '订单明细ID', + `quantity` DECIMAL(18,4) NOT NULL COMMENT '入库数量', + `unit` VARCHAR(20) COMMENT '单位', + `unit_price` DECIMAL(18,4) COMMENT '单价', + `amount` DECIMAL(18,4) COMMENT '金额', + `batch_no` VARCHAR(50) COMMENT '批次号', + `production_date` DATE COMMENT '生产日期', + `expiry_date` DATE COMMENT '过期日期', + `location_code` VARCHAR(50) COMMENT '库位编码', + `remark` VARCHAR(255) COMMENT '备注', + `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', PRIMARY KEY (`id`), - UNIQUE KEY `uk_supplier_material` (`supplier_id`,`material_id`), + KEY `idx_receipt` (`receipt_id`), KEY `idx_material` (`material_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='供应商物料关联表'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='采购入库明细表'; --- 质检记录表 -CREATE TABLE `qc_inspection` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT, - `inspection_no` varchar(50) NOT NULL COMMENT '质检单号', - `inspection_type` tinyint DEFAULT NULL COMMENT '质检类型: 1-来料检验, 2-过程检验, 3-成品检验, 4-出货检验', - `source_type` varchar(50) DEFAULT NULL COMMENT '来源类型', - `source_no` varchar(50) DEFAULT NULL COMMENT '来源单号', - `material_id` bigint unsigned DEFAULT NULL COMMENT '物料ID', - `batch_no` varchar(50) DEFAULT NULL COMMENT '批次号', - `inspection_qty` decimal(18,4) DEFAULT '0.0000' COMMENT '检验数量', - `qualified_qty` decimal(18,4) DEFAULT '0.0000' COMMENT '合格数量', - `unqualified_qty` decimal(18,4) DEFAULT '0.0000' COMMENT '不合格数量', - `inspection_result` tinyint DEFAULT NULL COMMENT '检验结果: 1-合格, 2-不合格, 3-让步接收', - `inspector` varchar(50) DEFAULT NULL COMMENT '检验员', - `inspection_date` date DEFAULT NULL COMMENT '检验日期', - `remark` text COMMENT '备注', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP, - `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - `deleted` tinyint NOT NULL DEFAULT '0' COMMENT '软删除: 0-正常, 1-已删除', - `create_by` bigint unsigned DEFAULT NULL COMMENT '创建人ID', - `update_by` bigint unsigned DEFAULT NULL COMMENT '更新人ID', +-- ======================================================== +-- 6. 销售管理模块 +-- ======================================================== + +-- 销售订单表 +CREATE TABLE `sal_order` ( + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '订单ID', + `order_no` VARCHAR(50) NOT NULL COMMENT '订单编号', + `order_date` DATE COMMENT '订单日期', + `customer_id` BIGINT UNSIGNED NOT NULL COMMENT '客户ID', + `contact_name` VARCHAR(50) COMMENT '联系人', + `contact_phone` VARCHAR(20) COMMENT '联系电话', + `delivery_address` VARCHAR(255) COMMENT '送货地址', + `salesman_id` BIGINT UNSIGNED COMMENT '业务员ID', + `total_amount` DECIMAL(18,4) DEFAULT 0 COMMENT '总金额', + `tax_amount` DECIMAL(18,4) DEFAULT 0 COMMENT '税额', + `total_with_tax` DECIMAL(18,4) DEFAULT 0 COMMENT '含税总额', + `discount_amount` DECIMAL(18,4) DEFAULT 0 COMMENT '折扣金额', + `currency` VARCHAR(10) DEFAULT 'CNY' COMMENT '币种', + `exchange_rate` DECIMAL(10,4) DEFAULT 1 COMMENT '汇率', + `payment_terms` VARCHAR(100) COMMENT '付款条件', + `delivery_date` DATE COMMENT '交货日期', + `contract_no` VARCHAR(50) COMMENT '合同编号', + `status` TINYINT DEFAULT 1 COMMENT '状态: 1-待确认, 2-已确认, 3-部分发货, 4-已完成, 5-已取消', + `remark` TEXT COMMENT '备注', + `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `create_by` BIGINT UNSIGNED COMMENT '创建人ID', + `update_by` BIGINT UNSIGNED COMMENT '更新人ID', + `deleted` TINYINT DEFAULT 0 COMMENT '删除标记', PRIMARY KEY (`id`), - UNIQUE KEY `uk_inspection_no` (`inspection_no`), - KEY `idx_source` (`source_no`), - KEY `idx_material` (`material_id`), - KEY `idx_type` (`inspection_type`), - CONSTRAINT `fk_qc_inspection_material` FOREIGN KEY (`material_id`) REFERENCES `inv_material` (`id`) ON DELETE SET NULL ON UPDATE CASCADE -) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='质检记录表'; + UNIQUE KEY `uk_order_no` (`order_no`), + KEY `idx_customer` (`customer_id`), + KEY `idx_salesman` (`salesman_id`), + KEY `idx_status` (`status`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='销售订单表'; --- 不合格品处理表 -CREATE TABLE `qc_unqualified` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT, - `unqualified_no` varchar(50) NOT NULL COMMENT '不合格品单号', - `handle_no` varchar(50) DEFAULT NULL COMMENT '处理单号', - `inspection_id` bigint unsigned DEFAULT NULL COMMENT '关联质检ID', - `source_type` varchar(50) DEFAULT NULL COMMENT '来源类型', - `source_no` varchar(50) DEFAULT NULL COMMENT '来源单号', - `material_id` bigint unsigned DEFAULT NULL COMMENT '物料ID', - `material_code` varchar(50) DEFAULT NULL COMMENT '物料编码', - `material_name` varchar(100) DEFAULT NULL COMMENT '物料名称', - `quantity` decimal(18,4) DEFAULT '0.0000' COMMENT '不合格数量', - `defect_type` varchar(50) DEFAULT NULL COMMENT '缺陷类型', - `defect_desc` text COMMENT '缺陷描述', - `handle_type` tinyint DEFAULT NULL COMMENT '处置方式: 1-返工, 2-报废, 3-让步接收, 4-退货', - `handle_status` tinyint NOT NULL DEFAULT '1' COMMENT '处理状态: 1-待处理, 2-处理中, 3-已完成', - `responsible_dept` varchar(100) DEFAULT NULL COMMENT '责任部门', - `responsible_person` varchar(50) DEFAULT NULL COMMENT '责任人', - `handle_result` tinyint DEFAULT NULL COMMENT '处理结果: 1-已处理, 2-处理中', - `cost_amount` decimal(18,4) DEFAULT '0.0000' COMMENT '损失金额', - `handler` varchar(50) DEFAULT NULL COMMENT '处理人', - `handle_date` date DEFAULT NULL COMMENT '处理日期', - `remark` text COMMENT '备注', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP, - `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - `create_by` bigint unsigned DEFAULT NULL COMMENT '创建人ID', - `update_by` bigint unsigned DEFAULT NULL COMMENT '更新人ID', - `deleted` tinyint NOT NULL DEFAULT '0' COMMENT '软删除: 0-正常, 1-已删除', +-- 销售订单明细表 +CREATE TABLE `sal_order_detail` ( + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '明细ID', + `order_id` BIGINT UNSIGNED NOT NULL COMMENT '订单ID', + `material_id` BIGINT UNSIGNED NOT NULL COMMENT '物料ID', + `quantity` DECIMAL(18,4) NOT NULL COMMENT '销售数量', + `unit` VARCHAR(20) COMMENT '单位', + `unit_price` DECIMAL(18,4) COMMENT '单价', + `tax_rate` DECIMAL(5,2) DEFAULT 0 COMMENT '税率(%)', + `amount` DECIMAL(18,4) COMMENT '金额', + `tax_amount` DECIMAL(18,4) COMMENT '税额', + `total_amount` DECIMAL(18,4) COMMENT '含税金额', + `delivered_qty` DECIMAL(18,4) DEFAULT 0 COMMENT '已发货数量', + `delivery_date` DATE COMMENT '交货日期', + `remark` VARCHAR(255) COMMENT '备注', + `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', PRIMARY KEY (`id`), - UNIQUE KEY `uk_unqualified_no` (`unqualified_no`), - UNIQUE KEY `uk_handle_no` (`handle_no`), - KEY `idx_inspection` (`inspection_id`), - KEY `idx_material` (`material_id`), - KEY `idx_handle_status` (`handle_status`), - KEY `idx_material_id` (`material_id`), - KEY `idx_source` (`source_type`,`source_no`), - KEY `fk_qc_unqualified_create_by` (`create_by`), - KEY `fk_qc_unqualified_update_by` (`update_by`), - CONSTRAINT `fk_qc_unqualified_create_by` FOREIGN KEY (`create_by`) REFERENCES `sys_user` (`id`) ON DELETE SET NULL ON UPDATE CASCADE, - CONSTRAINT `fk_qc_unqualified_material` FOREIGN KEY (`material_id`) REFERENCES `inv_material` (`id`) ON DELETE SET NULL ON UPDATE CASCADE, - CONSTRAINT `fk_qc_unqualified_update_by` FOREIGN KEY (`update_by`) REFERENCES `sys_user` (`id`) ON DELETE SET NULL ON UPDATE CASCADE -) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='不合格品处理表'; + KEY `idx_order` (`order_id`), + KEY `idx_material` (`material_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='销售订单明细表'; -- 销售出库单表 CREATE TABLE `sal_delivery` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '出库单ID', - `delivery_no` varchar(50) NOT NULL COMMENT '出库单号', - `delivery_date` date DEFAULT NULL COMMENT '出库日期', - `order_id` bigint unsigned DEFAULT NULL COMMENT '销售订单ID', - `order_no` varchar(50) DEFAULT NULL COMMENT '销售订单号(冗余)', - `customer_id` bigint unsigned NOT NULL COMMENT '客户ID', - `customer_name` varchar(100) DEFAULT NULL COMMENT '客户名称(冗余)', - `warehouse_id` bigint unsigned NOT NULL COMMENT '仓库ID', - `total_amount` decimal(18,4) DEFAULT '0.0000' COMMENT '总金额', - `total_qty` decimal(18,4) DEFAULT '0.0000' COMMENT '总数量(明细数量合计)', - `logistics_company` varchar(100) DEFAULT NULL COMMENT '物流公司', - `tracking_no` varchar(50) DEFAULT NULL COMMENT '物流单号', - `contact_name` varchar(50) DEFAULT NULL COMMENT '收货人姓名', - `contact_phone` varchar(30) DEFAULT NULL COMMENT '收货人电话', - `delivery_address` varchar(500) DEFAULT NULL COMMENT '配送地址', - `status` tinyint DEFAULT '1' COMMENT '状态: 1-待发货, 2-已发货, 3-已签收', - `remark` text COMMENT '备注', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', - `create_by` bigint unsigned DEFAULT NULL COMMENT '创建人ID', - `ship_by` bigint unsigned DEFAULT NULL COMMENT '发货人ID', - `ship_time` datetime DEFAULT NULL COMMENT '发货时间', - `sign_by` bigint unsigned DEFAULT NULL COMMENT '签收人ID', - `sign_time` datetime DEFAULT NULL COMMENT '签收时间', - `sign_status` tinyint DEFAULT '0' COMMENT '签收状态: 0-未签收, 1-已签收', - `deleted` tinyint NOT NULL DEFAULT '0' COMMENT '软删除标记', - `version` int DEFAULT '0' COMMENT '乐观锁版本号', - `update_by` bigint unsigned DEFAULT NULL COMMENT '更新人ID', + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '出库单ID', + `delivery_no` VARCHAR(50) NOT NULL COMMENT '出库单号', + `delivery_date` DATE COMMENT '出库日期', + `order_id` BIGINT UNSIGNED COMMENT '销售订单ID', + `customer_id` BIGINT UNSIGNED NOT NULL COMMENT '客户ID', + `warehouse_id` BIGINT UNSIGNED NOT NULL COMMENT '仓库ID', + `total_amount` DECIMAL(18,4) DEFAULT 0 COMMENT '总金额', + `logistics_company` VARCHAR(100) COMMENT '物流公司', + `tracking_no` VARCHAR(50) COMMENT '物流单号', + `status` TINYINT DEFAULT 1 COMMENT '状态: 1-待发货, 2-已发货, 3-已签收', + `remark` TEXT COMMENT '备注', + `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `create_by` BIGINT UNSIGNED COMMENT '创建人ID', PRIMARY KEY (`id`), UNIQUE KEY `uk_delivery_no` (`delivery_no`), KEY `idx_order` (`order_id`), - KEY `idx_customer` (`customer_id`), - KEY `idx_warehouse` (`warehouse_id`), - CONSTRAINT `fk_sal_delivery_customer` FOREIGN KEY (`customer_id`) REFERENCES `crm_customer` (`id`) ON DELETE RESTRICT ON UPDATE CASCADE, - CONSTRAINT `fk_sal_delivery_order` FOREIGN KEY (`order_id`) REFERENCES `sal_order` (`id`) ON DELETE SET NULL ON UPDATE CASCADE, - CONSTRAINT `fk_sal_delivery_warehouse` FOREIGN KEY (`warehouse_id`) REFERENCES `inv_warehouse` (`id`) ON DELETE RESTRICT ON UPDATE CASCADE + KEY `idx_customer` (`customer_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='销售出库单表'; -- 销售出库明细表 CREATE TABLE `sal_delivery_detail` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '明细ID', - `delivery_id` bigint unsigned NOT NULL COMMENT '出库单ID', - `line_no` int DEFAULT NULL COMMENT '行号', - `material_id` bigint unsigned NOT NULL COMMENT '物料ID', - `material_code` varchar(50) DEFAULT NULL COMMENT '物料编码(冗余)', - `material_name` varchar(200) DEFAULT NULL COMMENT '物料名称(冗余)', - `material_spec` varchar(100) DEFAULT NULL COMMENT '物料规格(冗余)', - `order_detail_id` bigint unsigned DEFAULT NULL COMMENT '订单明细ID', - `quantity` decimal(18,4) NOT NULL COMMENT '出库数量', - `unit` varchar(20) DEFAULT NULL COMMENT '单位', - `unit_price` decimal(18,4) DEFAULT NULL COMMENT '单价', - `amount` decimal(18,4) DEFAULT NULL COMMENT '金额', - `batch_no` varchar(50) DEFAULT NULL COMMENT '批次号', - `remark` varchar(255) DEFAULT NULL COMMENT '备注', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - `deleted` tinyint NOT NULL DEFAULT '0' COMMENT '软删除标记', + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '明细ID', + `delivery_id` BIGINT UNSIGNED NOT NULL COMMENT '出库单ID', + `material_id` BIGINT UNSIGNED NOT NULL COMMENT '物料ID', + `order_detail_id` BIGINT UNSIGNED COMMENT '订单明细ID', + `quantity` DECIMAL(18,4) NOT NULL COMMENT '出库数量', + `unit` VARCHAR(20) COMMENT '单位', + `unit_price` DECIMAL(18,4) COMMENT '单价', + `amount` DECIMAL(18,4) COMMENT '金额', + `batch_no` VARCHAR(50) COMMENT '批次号', + `remark` VARCHAR(255) COMMENT '备注', + `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', PRIMARY KEY (`id`), KEY `idx_delivery` (`delivery_id`), - KEY `idx_material` (`material_id`), - KEY `idx_order_detail` (`order_detail_id`), - CONSTRAINT `fk_delivery_detail_delivery` FOREIGN KEY (`delivery_id`) REFERENCES `sal_delivery` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, - CONSTRAINT `fk_sal_delivery_detail_delivery` FOREIGN KEY (`delivery_id`) REFERENCES `sal_delivery` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, - CONSTRAINT `fk_sal_delivery_detail_material` FOREIGN KEY (`material_id`) REFERENCES `inv_material` (`id`) ON DELETE RESTRICT ON UPDATE CASCADE, - CONSTRAINT `fk_sal_delivery_detail_order_detail` FOREIGN KEY (`order_detail_id`) REFERENCES `sal_order_detail` (`id`) ON DELETE SET NULL ON UPDATE CASCADE + KEY `idx_material` (`material_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='销售出库明细表'; --- 送货单表 -CREATE TABLE `sal_delivery_order` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT, - `delivery_no` varchar(50) NOT NULL COMMENT '送货单号', - `order_id` bigint unsigned DEFAULT NULL COMMENT '销售订单ID', - `order_no` varchar(50) DEFAULT NULL COMMENT '销售订单编号', - `customer_id` bigint unsigned NOT NULL COMMENT '客户ID', - `customer_name` varchar(100) DEFAULT NULL COMMENT '客户名称', - `delivery_date` date DEFAULT NULL COMMENT '送货日期', - `contact_name` varchar(50) DEFAULT NULL COMMENT '收货联系人', - `contact_phone` varchar(20) DEFAULT NULL COMMENT '联系电话', - `delivery_address` varchar(255) DEFAULT NULL COMMENT '送货地址', - `warehouse_id` bigint unsigned DEFAULT NULL COMMENT '发货仓库ID', - `logistics_company` varchar(100) DEFAULT NULL COMMENT '物流公司', - `tracking_no` varchar(50) DEFAULT NULL COMMENT '物流单号', - `driver_name` varchar(50) DEFAULT NULL COMMENT '司机姓名', - `vehicle_no` varchar(20) DEFAULT NULL COMMENT '车牌号', - `total_qty` decimal(18,4) DEFAULT '0.0000' COMMENT '总数量', - `total_amount` decimal(18,4) DEFAULT '0.0000' COMMENT '总金额', - `sign_status` tinyint DEFAULT '0' COMMENT '签收状态: 0-未签收, 1-已签收, 2-部分签收, 3-拒收', - `sign_person` varchar(50) DEFAULT NULL COMMENT '签收人', - `sign_time` datetime DEFAULT NULL COMMENT '签收时间', - `sign_remark` varchar(255) DEFAULT NULL COMMENT '签收备注', - `status` tinyint DEFAULT '1' COMMENT '状态: 1-待发货, 2-已发货, 3-已签收, 4-已取消', - `remark` text COMMENT '备注', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP, - `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - `create_by` bigint unsigned DEFAULT NULL, - `deleted` tinyint DEFAULT '0', +-- ======================================================== +-- 7. 生产管理模块 +-- ======================================================== + +-- 标准卡表(样品管理) +CREATE TABLE `prd_standard_card` ( + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '标准卡ID', + `card_no` VARCHAR(50) NOT NULL COMMENT '标准卡编号', + `customer_id` BIGINT UNSIGNED COMMENT '客户ID', + `customer_name` VARCHAR(100) COMMENT '客户名称', + `customer_code` VARCHAR(50) COMMENT '客户代码', + `product_name` VARCHAR(100) COMMENT '产品名称', + `version` VARCHAR(10) COMMENT '版本', + `date` DATE COMMENT '日期', + `document_code` VARCHAR(50) COMMENT '文件编号', + `finished_size` VARCHAR(50) COMMENT '成品尺寸', + `tolerance` VARCHAR(50) COMMENT '公差', + `material_name` VARCHAR(100) COMMENT '材料名称', + `material_type` VARCHAR(20) COMMENT '材料类型', + `layout_type` VARCHAR(50) COMMENT '排版方式', + `spacing` VARCHAR(20) COMMENT '间距', + `spacing_value` VARCHAR(20) COMMENT '间距值', + `sheet_width` VARCHAR(20) COMMENT '片材宽', + `sheet_length` VARCHAR(20) COMMENT '片材长', + `core_type` VARCHAR(50) COMMENT '纸芯类型', + `paper_direction` VARCHAR(20) COMMENT '纸向', + `roll_width` VARCHAR(20) COMMENT '料宽', + `paper_edge` VARCHAR(20) COMMENT '纸边', + `standard_usage` VARCHAR(50) COMMENT '标准用量', + `jump_distance` VARCHAR(20) COMMENT '跳距', + `process_flow1` VARCHAR(100) COMMENT '工艺流程1', + `process_flow2` VARCHAR(100) COMMENT '工艺流程2', + `print_type` VARCHAR(50) COMMENT '表面处理', + `first_jump_distance` VARCHAR(20) COMMENT '第一跳距', + -- 印序数据(7行JSON存储) + `sequences` JSON COMMENT '印序数据', + `film_manufacturer` VARCHAR(100) COMMENT '膜厂商', + `film_code` VARCHAR(50) COMMENT '膜编号', + `film_size` VARCHAR(50) COMMENT '膜规格', + `process_method` VARCHAR(50) COMMENT '工艺方式', + `stamping_method` VARCHAR(50) COMMENT '冲压方法', + `mold_code` VARCHAR(50) COMMENT '模具编号', + `layout_method` VARCHAR(50) COMMENT '排版方式', + `layout_way` VARCHAR(50) COMMENT '排版方向', + `jump_distance2` VARCHAR(20) COMMENT '跳距2', + `mylar_material` VARCHAR(100) COMMENT '麦拉材料', + `mylar_specs` VARCHAR(50) COMMENT '麦拉规格', + `mylar_layout` VARCHAR(50) COMMENT '麦拉排版', + `mylar_jump` VARCHAR(20) COMMENT '麦拉跳距', + `adhesive_type` VARCHAR(50) COMMENT '背胶种类', + `adhesive_manufacturer` VARCHAR(100) COMMENT '背胶厂商', + `adhesive_code` VARCHAR(50) COMMENT '背胶编号', + `adhesive_size` VARCHAR(50) COMMENT '背胶尺寸', + `dashed_knife` TINYINT DEFAULT 0 COMMENT '加虚线刀: 0-否, 1-是', + `slice_per_row` VARCHAR(20) COMMENT 'PCS/排', + `slice_per_roll` VARCHAR(20) COMMENT 'PCS/卷', + `slice_per_bundle` VARCHAR(20) COMMENT 'PCS/扎', + `slice_per_bag` VARCHAR(20) COMMENT 'PCS/袋', + `slice_per_box` VARCHAR(20) COMMENT 'PCS/箱', + `back_knife_mold` VARCHAR(50) COMMENT '背胶刀模存放', + `back_mylar_mold` VARCHAR(50) COMMENT '背麦拉刀模存放', + `release_paper_code` VARCHAR(50) COMMENT '离型纸编号', + `release_paper_type` VARCHAR(50) COMMENT '离型纸种类', + `release_paper_specs` VARCHAR(50) COMMENT '离型纸规格', + `padding_material` VARCHAR(100) COMMENT '填充材料', + `packing_material` VARCHAR(100) COMMENT '包装材料', + `special_color` VARCHAR(200) COMMENT '专色配比', + `color_formula` VARCHAR(200) COMMENT '颜色配方', + `file_path` VARCHAR(200) COMMENT '电脑图档存储路径', + `sample_info` VARCHAR(200) COMMENT '样品信息', + `notes` TEXT COMMENT '注意事项', + `glue_type` VARCHAR(50) COMMENT '滴胶类型', + `packing_type` VARCHAR(50) COMMENT '包装类型', + `creator` VARCHAR(50) COMMENT '制作', + `reviewer` VARCHAR(50) COMMENT '审核', + `factory_manager` VARCHAR(50) COMMENT '厂长', + `quality_manager` VARCHAR(50) COMMENT '品管', + `sales` VARCHAR(50) COMMENT '业务', + `approver` VARCHAR(50) COMMENT '核准', + `status` TINYINT DEFAULT 1 COMMENT '状态: 1-草稿, 2-待审核, 3-已启用, 4-已归档', + `creator_id` BIGINT UNSIGNED COMMENT '创建人ID', + `reviewer_id` BIGINT UNSIGNED COMMENT '审核人ID', + `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `deleted` TINYINT DEFAULT 0 COMMENT '删除标记', PRIMARY KEY (`id`), - UNIQUE KEY `uk_delivery_no` (`delivery_no`), - KEY `idx_order` (`order_id`), + UNIQUE KEY `uk_card_no` (`card_no`), KEY `idx_customer` (`customer_id`), KEY `idx_status` (`status`) -) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='送货单表'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='标准卡表'; --- 送货单明细表 -CREATE TABLE `sal_delivery_order_item` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT, - `delivery_id` bigint unsigned NOT NULL COMMENT '送货单ID', - `order_detail_id` bigint unsigned DEFAULT NULL COMMENT '订单明细ID', - `material_id` bigint unsigned NOT NULL COMMENT '物料ID', - `material_name` varchar(100) DEFAULT NULL COMMENT '物料名称', - `material_spec` varchar(255) DEFAULT NULL COMMENT '规格型号', - `quantity` decimal(18,4) NOT NULL COMMENT '送货数量', - `unit` varchar(20) DEFAULT NULL COMMENT '单位', - `unit_price` decimal(18,4) DEFAULT NULL COMMENT '单价', - `amount` decimal(18,4) DEFAULT NULL COMMENT '金额', - `batch_no` varchar(50) DEFAULT NULL COMMENT '批次号', - `sign_qty` decimal(18,4) DEFAULT '0.0000' COMMENT '签收数量', - `remark` varchar(255) DEFAULT NULL COMMENT '备注', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP, +-- 生产工单表 +CREATE TABLE `prd_work_order` ( + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '工单ID', + `work_order_no` VARCHAR(50) NOT NULL COMMENT '工单编号', + `work_order_date` DATE COMMENT '工单日期', + `sales_order_id` BIGINT UNSIGNED COMMENT '销售订单ID', + `material_id` BIGINT UNSIGNED NOT NULL COMMENT '产品ID', + `plan_qty` DECIMAL(18,4) NOT NULL COMMENT '计划数量', + `completed_qty` DECIMAL(18,4) DEFAULT 0 COMMENT '已完成数量', + `unit` VARCHAR(20) COMMENT '单位', + `plan_start_date` DATE COMMENT '计划开工日期', + `plan_end_date` DATE COMMENT '计划完工日期', + `actual_start_date` DATE COMMENT '实际开工日期', + `actual_end_date` DATE COMMENT '实际完工日期', + `workshop_id` BIGINT UNSIGNED COMMENT '车间ID', + `workcenter_id` BIGINT UNSIGNED COMMENT '工作中心ID', + `priority` TINYINT DEFAULT 1 COMMENT '优先级: 1-低, 2-中, 3-高, 4-紧急', + `status` TINYINT DEFAULT 1 COMMENT '状态: 1-待开工, 2-生产中, 3-已完成, 4-已关闭', + `remark` TEXT COMMENT '备注', + `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `create_by` BIGINT UNSIGNED COMMENT '创建人ID', PRIMARY KEY (`id`), - KEY `idx_delivery` (`delivery_id`), - KEY `idx_material` (`material_id`) -) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='送货单明细表'; + UNIQUE KEY `uk_work_order_no` (`work_order_no`), + KEY `idx_material` (`material_id`), + KEY `idx_status` (`status`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='生产工单表'; --- 销售订单表 -CREATE TABLE `sal_order` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT, - `order_no` varchar(50) NOT NULL COMMENT '订单编号', - `order_date` date DEFAULT NULL COMMENT '订单日期', - `customer_id` bigint unsigned NOT NULL COMMENT '客户ID', - `contact_name` varchar(50) DEFAULT NULL COMMENT '联系人', - `contact_phone` varchar(20) DEFAULT NULL COMMENT '联系电话', - `delivery_address` varchar(255) DEFAULT NULL COMMENT '送货地址', - `salesman_id` bigint unsigned DEFAULT NULL COMMENT '业务员ID', - `total_amount` decimal(18,4) DEFAULT '0.0000' COMMENT '总金额', - `tax_amount` decimal(18,4) DEFAULT '0.0000' COMMENT '税额', - `total_with_tax` decimal(18,4) DEFAULT '0.0000' COMMENT '含税总额', - `discount_amount` decimal(18,4) DEFAULT '0.0000' COMMENT '折扣金额', - `currency` varchar(10) DEFAULT 'CNY' COMMENT '币种', - `exchange_rate` decimal(18,4) DEFAULT '1.0000' COMMENT '汇率', - `payment_terms` varchar(100) DEFAULT NULL COMMENT '付款条件', - `delivery_date` date DEFAULT NULL COMMENT '交货日期', - `contract_no` varchar(50) DEFAULT NULL COMMENT '合同编号', - `status` tinyint DEFAULT '1' COMMENT '状态: 1-待确认, 2-已确认, 3-部分发货, 4-已完成, 5-已取消', - `remark` text COMMENT '备注', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP, - `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - `create_by` bigint unsigned DEFAULT NULL, - `update_by` bigint unsigned DEFAULT NULL, - `deleted` tinyint NOT NULL DEFAULT '0' COMMENT '软删除: 0-正常, 1-已删除', - PRIMARY KEY (`id`), - UNIQUE KEY `uk_order_no` (`order_no`), - KEY `fk_sal_order_customer` (`customer_id`), - KEY `fk_sal_order_salesman` (`salesman_id`), - CONSTRAINT `fk_sal_order_customer` FOREIGN KEY (`customer_id`) REFERENCES `crm_customer` (`id`) ON DELETE RESTRICT ON UPDATE CASCADE, - CONSTRAINT `fk_sal_order_salesman` FOREIGN KEY (`salesman_id`) REFERENCES `sys_user` (`id`) ON DELETE SET NULL ON UPDATE CASCADE -) ENGINE=InnoDB AUTO_INCREMENT=42 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='销售订单表'; +-- BOM表(物料清单) +CREATE TABLE `prd_bom` ( + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'BOM ID', + `bom_no` VARCHAR(50) NOT NULL COMMENT 'BOM编号', + `material_id` BIGINT UNSIGNED NOT NULL COMMENT '成品物料ID', + `version` VARCHAR(10) DEFAULT '1.0' COMMENT '版本号', + `is_default` TINYINT DEFAULT 1 COMMENT '是否默认: 0-否, 1-是', + `status` TINYINT DEFAULT 1 COMMENT '状态: 0-禁用, 1-启用', + `remark` TEXT COMMENT '备注', + `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `create_by` BIGINT UNSIGNED COMMENT '创建人ID', + PRIMARY KEY (`id`), + UNIQUE KEY `uk_bom_version` (`material_id`, `version`), + KEY `idx_material` (`material_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='BOM表'; --- 销售订单明细表 -CREATE TABLE `sal_order_detail` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT, - `order_id` bigint unsigned NOT NULL COMMENT '订单ID', - `material_id` bigint unsigned NOT NULL COMMENT '物料ID', - `material_name` varchar(100) DEFAULT NULL COMMENT '物料名称(冗余)', - `quantity` decimal(18,4) NOT NULL COMMENT '销售数量', - `unit` varchar(20) DEFAULT NULL COMMENT '单位', - `unit_price` decimal(18,4) DEFAULT NULL COMMENT '单价', - `tax_rate` decimal(18,4) DEFAULT '0.0000' COMMENT '税率(%)', - `amount` decimal(18,4) DEFAULT NULL COMMENT '金额', - `tax_amount` decimal(18,4) DEFAULT NULL COMMENT '税额', - `total_amount` decimal(18,4) DEFAULT NULL COMMENT '含税金额', - `delivered_qty` decimal(18,4) DEFAULT '0.0000' COMMENT '已发货数量', - `delivery_date` date DEFAULT NULL COMMENT '交货日期', - `remark` varchar(255) DEFAULT NULL COMMENT '备注', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP, - `deleted` tinyint NOT NULL DEFAULT '0' COMMENT '删除标记', +-- BOM明细表 +CREATE TABLE `prd_bom_detail` ( + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '明细ID', + `bom_id` BIGINT UNSIGNED NOT NULL COMMENT 'BOM ID', + `material_id` BIGINT UNSIGNED NOT NULL COMMENT '物料ID', + `quantity` DECIMAL(18,4) NOT NULL COMMENT '用量', + `unit` VARCHAR(20) COMMENT '单位', + `loss_rate` DECIMAL(5,2) DEFAULT 0 COMMENT '损耗率(%)', + `process_sequence` INT COMMENT '工序序号', + `remark` VARCHAR(255) COMMENT '备注', + `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', PRIMARY KEY (`id`), - KEY `idx_material_name` (`material_name`), - KEY `fk_sal_order_detail_order` (`order_id`), - KEY `fk_sal_order_detail_material` (`material_id`), - CONSTRAINT `fk_sal_order_detail_material` FOREIGN KEY (`material_id`) REFERENCES `inv_material` (`id`) ON DELETE RESTRICT ON UPDATE CASCADE, - CONSTRAINT `fk_sal_order_detail_order` FOREIGN KEY (`order_id`) REFERENCES `sal_order` (`id`) ON DELETE CASCADE ON UPDATE CASCADE -) ENGINE=InnoDB AUTO_INCREMENT=39 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='销售订单明细表'; + KEY `idx_bom` (`bom_id`), + KEY `idx_material` (`material_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='BOM明细表'; --- 销售订单明细表(API) -CREATE TABLE `sal_order_item` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT, - `order_id` bigint unsigned NOT NULL COMMENT '订单ID', - `material_name` varchar(200) DEFAULT NULL COMMENT '物料名称', - `quantity` decimal(14,3) DEFAULT '0.000' COMMENT '数量', - `unit` varchar(20) DEFAULT NULL COMMENT '单位', - `unit_price` decimal(14,4) DEFAULT '0.0000' COMMENT '单价', - `total_price` decimal(14,2) DEFAULT '0.00' COMMENT '总价', - `remark` text COMMENT '备注', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP, - PRIMARY KEY (`id`), - KEY `idx_order` (`order_id`) -) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='销售订单明细表(API)'; +-- ======================================================== +-- 8. 财务管理模块 +-- ======================================================== --- 销售对账表 -CREATE TABLE `sal_reconciliation` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT, - `reconciliation_no` varchar(50) NOT NULL COMMENT '对账单号', - `customer_id` bigint unsigned NOT NULL COMMENT '客户ID', - `customer_name` varchar(100) DEFAULT NULL COMMENT '客户名称', - `period_start` date NOT NULL COMMENT '对账期间开始', - `period_end` date NOT NULL COMMENT '对账期间结束', - `delivery_amount` decimal(18,4) DEFAULT '0.0000' COMMENT '送货金额', - `return_amount` decimal(18,4) DEFAULT '0.0000' COMMENT '退货金额', - `discount_amount` decimal(18,4) DEFAULT '0.0000' COMMENT '折扣金额', - `net_amount` decimal(18,4) DEFAULT '0.0000' COMMENT '对账净额', - `received_amount` decimal(18,4) DEFAULT '0.0000' COMMENT '已收金额', - `balance_amount` decimal(18,4) DEFAULT '0.0000' COMMENT '未收余额', - `confirm_status` tinyint DEFAULT '0' COMMENT '客户确认: 0-未确认, 1-已确认, 2-有异议', - `confirm_person` varchar(50) DEFAULT NULL COMMENT '确认人', - `confirm_time` datetime DEFAULT NULL COMMENT '确认时间', - `confirm_remark` varchar(255) DEFAULT NULL COMMENT '确认备注', - `status` tinyint DEFAULT '1' COMMENT '状态: 1-草稿, 2-已发送, 3-已确认, 4-已关闭', - `remark` text COMMENT '备注', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP, - `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - `create_by` bigint unsigned DEFAULT NULL, - `deleted` tinyint NOT NULL DEFAULT '0' COMMENT '软删除: 0-正常, 1-已删除', +-- 应收款表 +CREATE TABLE `fin_receivable` ( + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '应收款ID', + `receivable_no` VARCHAR(50) NOT NULL COMMENT '应收款编号', + `source_type` TINYINT COMMENT '来源类型: 1-销售订单, 2-其他', + `source_id` BIGINT UNSIGNED COMMENT '来源单据ID', + `source_no` VARCHAR(50) COMMENT '来源单据号', + `customer_id` BIGINT UNSIGNED NOT NULL COMMENT '客户ID', + `amount` DECIMAL(18,4) NOT NULL COMMENT '应收金额', + `received_amount` DECIMAL(18,4) DEFAULT 0 COMMENT '已收金额', + `balance` DECIMAL(18,4) COMMENT '余额', + `due_date` DATE COMMENT '到期日期', + `status` TINYINT DEFAULT 1 COMMENT '状态: 1-未收款, 2-部分收款, 3-已结清, 4-已坏账', + `remark` VARCHAR(255) COMMENT '备注', + `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', PRIMARY KEY (`id`), - UNIQUE KEY `uk_reconciliation_no` (`reconciliation_no`), + UNIQUE KEY `uk_receivable_no` (`receivable_no`), KEY `idx_customer` (`customer_id`), - KEY `idx_period` (`period_start`,`period_end`), - KEY `idx_status` (`status`), - CONSTRAINT `fk_sal_recon_customer` FOREIGN KEY (`customer_id`) REFERENCES `crm_customer` (`id`) ON DELETE RESTRICT ON UPDATE CASCADE -) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='销售对账表'; - --- 销售对账明细表 -CREATE TABLE `sal_reconciliation_detail` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT, - `reconciliation_id` bigint unsigned NOT NULL COMMENT '对账单ID', - `source_type` tinyint NOT NULL COMMENT '来源类型: 1-送货单, 2-退货单', - `source_id` bigint unsigned DEFAULT NULL COMMENT '来源单据ID', - `source_no` varchar(50) DEFAULT NULL COMMENT '来源单号', - `source_date` date DEFAULT NULL COMMENT '单据日期', - `amount` decimal(18,4) NOT NULL COMMENT '金额', - `remark` varchar(255) DEFAULT NULL COMMENT '备注', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP, - PRIMARY KEY (`id`), - KEY `idx_reconciliation` (`reconciliation_id`), - KEY `idx_source` (`source_type`,`source_id`) -) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='销售对账明细表'; - --- 对账单明细表 -CREATE TABLE `sal_reconciliation_line` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '明细ID', - `reconciliation_id` bigint unsigned NOT NULL COMMENT '对账单ID', - `source_type` tinyint NOT NULL COMMENT '来源类型: 1-发货单, 2-退货单', - `source_id` bigint unsigned NOT NULL COMMENT '来源单据ID', - `source_no` varchar(50) NOT NULL COMMENT '来源单据号', - `source_date` date NOT NULL COMMENT '来源单据日期', - `amount` decimal(18,4) NOT NULL COMMENT '单据金额', - `remark` varchar(255) DEFAULT NULL COMMENT '备注', - `deleted` tinyint NOT NULL DEFAULT '0' COMMENT '软删除: 0-正常, 1-已删除', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - PRIMARY KEY (`id`), - KEY `idx_reconciliation` (`reconciliation_id`), - KEY `idx_source` (`source_type`,`source_id`), - KEY `idx_source_no` (`source_no`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='对账单明细表'; + KEY `idx_status` (`status`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='应收款表'; --- 对账核销记录表 -CREATE TABLE `sal_reconciliation_writeoff` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '核销记录ID', - `reconciliation_id` bigint unsigned NOT NULL COMMENT '对账单ID', - `receivable_id` bigint unsigned NOT NULL COMMENT '应收单ID', - `amount` decimal(18,4) NOT NULL COMMENT '核销金额', - `write_off_date` date NOT NULL COMMENT '核销日期', - `remark` varchar(255) DEFAULT NULL COMMENT '备注', - `deleted` tinyint NOT NULL DEFAULT '0' COMMENT '软删除: 0-正常, 1-已删除', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - `create_by` bigint unsigned DEFAULT NULL COMMENT '创建人ID', +-- 应付款表 +CREATE TABLE `fin_payable` ( + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '应付款ID', + `payable_no` VARCHAR(50) NOT NULL COMMENT '应付款编号', + `source_type` TINYINT COMMENT '来源类型: 1-采购订单, 2-其他', + `source_id` BIGINT UNSIGNED COMMENT '来源单据ID', + `source_no` VARCHAR(50) COMMENT '来源单据号', + `supplier_id` BIGINT UNSIGNED NOT NULL COMMENT '供应商ID', + `amount` DECIMAL(18,4) NOT NULL COMMENT '应付金额', + `paid_amount` DECIMAL(18,4) DEFAULT 0 COMMENT '已付金额', + `balance` DECIMAL(18,4) COMMENT '余额', + `due_date` DATE COMMENT '到期日期', + `status` TINYINT DEFAULT 1 COMMENT '状态: 1-未付款, 2-部分付款, 3-已结清', + `remark` VARCHAR(255) COMMENT '备注', + `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', PRIMARY KEY (`id`), - KEY `idx_reconciliation` (`reconciliation_id`), - KEY `idx_receivable` (`receivable_id`), - KEY `idx_write_off_date` (`write_off_date`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='对账核销记录表'; + UNIQUE KEY `uk_payable_no` (`payable_no`), + KEY `idx_supplier` (`supplier_id`), + KEY `idx_status` (`status`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='应付款表'; --- 退货单主表 -CREATE TABLE `sal_return` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '退货单ID', - `return_no` varchar(50) NOT NULL COMMENT '退货单号', - `status` tinyint NOT NULL DEFAULT '1' COMMENT '状态: 1-待审核, 2-已审核, 3-已完成, 9-已取消', - `order_id` bigint unsigned NOT NULL COMMENT '销售订单ID', - `order_no` varchar(50) DEFAULT NULL COMMENT '销售订单号(冗余)', - `customer_id` bigint unsigned NOT NULL COMMENT '客户ID', - `customer_name` varchar(100) DEFAULT NULL COMMENT '客户名称(冗余)', - `warehouse_id` bigint unsigned NOT NULL COMMENT '入库仓库ID', - `delivery_id` bigint unsigned DEFAULT NULL COMMENT '关联发货单ID', - `delivery_no` varchar(50) DEFAULT NULL COMMENT '关联发货单号(冗余)', - `reason` varchar(500) NOT NULL COMMENT '退货原因', - `return_date` date NOT NULL COMMENT '退货日期', - `total_amount` decimal(18,4) DEFAULT '0.0000' COMMENT '退货总金额', - `approve_by` bigint unsigned DEFAULT NULL COMMENT '审核人ID', - `approve_time` datetime DEFAULT NULL COMMENT '审核时间', - `complete_by` bigint unsigned DEFAULT NULL COMMENT '完成人ID', - `complete_time` datetime DEFAULT NULL COMMENT '完成时间', - `inbound_order_id` bigint unsigned DEFAULT NULL COMMENT '关联入库单ID(退货入库)', - `inbound_order_no` varchar(50) DEFAULT NULL COMMENT '关联入库单号(冗余)', - `receivable_id` bigint unsigned DEFAULT NULL COMMENT '关联红字应收单ID', - `receivable_no` varchar(50) DEFAULT NULL COMMENT '关联红字应收单号(冗余)', - `remark` varchar(500) DEFAULT NULL COMMENT '备注', - `version` int DEFAULT '0' COMMENT '乐观锁版本号', - `deleted` tinyint NOT NULL DEFAULT '0' COMMENT '软删除: 0-正常, 1-已删除', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', - `create_by` bigint unsigned DEFAULT NULL COMMENT '创建人ID', - `update_by` bigint unsigned DEFAULT NULL COMMENT '更新人ID', +-- 收款记录表 +CREATE TABLE `fin_receipt_record` ( + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '记录ID', + `receipt_no` VARCHAR(50) NOT NULL COMMENT '收款单号', + `receivable_id` BIGINT UNSIGNED COMMENT '应收款ID', + `customer_id` BIGINT UNSIGNED NOT NULL COMMENT '客户ID', + `amount` DECIMAL(18,4) NOT NULL COMMENT '收款金额', + `receipt_date` DATE COMMENT '收款日期', + `receipt_method` VARCHAR(50) COMMENT '收款方式: 银行转账, 现金, 支票等', + `bank_account` VARCHAR(50) COMMENT '收款账户', + `reference_no` VARCHAR(50) COMMENT '参考号/流水号', + `handler_id` BIGINT UNSIGNED COMMENT '经办人ID', + `remark` VARCHAR(255) COMMENT '备注', + `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', PRIMARY KEY (`id`), - UNIQUE KEY `uk_return_no` (`return_no`), - KEY `idx_status` (`status`), - KEY `idx_order` (`order_id`), - KEY `idx_customer` (`customer_id`), - KEY `idx_warehouse` (`warehouse_id`), - KEY `idx_delivery` (`delivery_id`), - KEY `idx_return_date` (`return_date`), - KEY `idx_inbound_order` (`inbound_order_id`), + UNIQUE KEY `uk_receipt_no` (`receipt_no`), KEY `idx_receivable` (`receivable_id`), - CONSTRAINT `fk_sal_return_customer` FOREIGN KEY (`customer_id`) REFERENCES `crm_customer` (`id`) ON DELETE RESTRICT ON UPDATE CASCADE, - CONSTRAINT `fk_sal_return_delivery` FOREIGN KEY (`delivery_id`) REFERENCES `sal_delivery` (`id`) ON DELETE SET NULL ON UPDATE CASCADE, - CONSTRAINT `fk_sal_return_order` FOREIGN KEY (`order_id`) REFERENCES `sal_order` (`id`) ON DELETE RESTRICT ON UPDATE CASCADE, - CONSTRAINT `fk_sal_return_receivable` FOREIGN KEY (`receivable_id`) REFERENCES `fin_receivable` (`id`) ON DELETE SET NULL ON UPDATE CASCADE, - CONSTRAINT `fk_sal_return_warehouse` FOREIGN KEY (`warehouse_id`) REFERENCES `inv_warehouse` (`id`) ON DELETE RESTRICT ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='退货单主表'; - --- 退货单明细表 -CREATE TABLE `sal_return_detail` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '明细ID', - `return_id` bigint unsigned NOT NULL COMMENT '退货单ID', - `line_no` int NOT NULL COMMENT '行号', - `delivery_detail_id` bigint unsigned DEFAULT NULL COMMENT '关联发货明细ID', - `order_detail_id` bigint unsigned DEFAULT NULL COMMENT '关联订单明细ID', - `material_id` bigint unsigned NOT NULL COMMENT '物料ID', - `material_code` varchar(50) DEFAULT NULL COMMENT '物料编码(冗余)', - `material_name` varchar(200) DEFAULT NULL COMMENT '物料名称(冗余)', - `material_spec` varchar(100) DEFAULT NULL COMMENT '物料规格(冗余)', - `unit` varchar(20) DEFAULT NULL COMMENT '单位', - `quantity` decimal(18,4) NOT NULL COMMENT '退货数量', - `unit_price` decimal(18,4) DEFAULT '0.0000' COMMENT '单价', - `amount` decimal(18,4) DEFAULT '0.0000' COMMENT '金额', - `batch_no` varchar(50) DEFAULT NULL COMMENT '批次号', - `remark` varchar(255) DEFAULT NULL COMMENT '备注', - `deleted` tinyint NOT NULL DEFAULT '0' COMMENT '软删除: 0-正常, 1-已删除', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - PRIMARY KEY (`id`), - KEY `idx_return` (`return_id`), - KEY `idx_material` (`material_id`), - KEY `idx_order_detail` (`order_detail_id`), - KEY `idx_batch` (`batch_no`), - KEY `idx_delivery_detail` (`delivery_detail_id`), - CONSTRAINT `fk_return_detail_return` FOREIGN KEY (`return_id`) REFERENCES `sal_return` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, - CONSTRAINT `fk_sal_return_detail_delivery_detail` FOREIGN KEY (`delivery_detail_id`) REFERENCES `sal_delivery_detail` (`id`) ON DELETE SET NULL ON UPDATE CASCADE, - CONSTRAINT `fk_sal_return_detail_material` FOREIGN KEY (`material_id`) REFERENCES `inv_material` (`id`) ON DELETE RESTRICT ON UPDATE CASCADE, - CONSTRAINT `fk_sal_return_detail_order_detail` FOREIGN KEY (`order_detail_id`) REFERENCES `sal_order_detail` (`id`) ON DELETE SET NULL ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='退货单明细表'; + KEY `idx_customer` (`customer_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='收款记录表'; --- 退货单表 -CREATE TABLE `sal_return_order` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT, - `return_no` varchar(50) NOT NULL COMMENT '退货单号', - `order_id` bigint unsigned DEFAULT NULL COMMENT '原销售订单ID', - `order_no` varchar(50) DEFAULT NULL COMMENT '原销售订单编号', - `delivery_id` bigint unsigned DEFAULT NULL COMMENT '原送货单ID', - `delivery_no` varchar(50) DEFAULT NULL COMMENT '原送货单号', - `customer_id` bigint unsigned NOT NULL COMMENT '客户ID', - `customer_name` varchar(100) DEFAULT NULL COMMENT '客户名称', - `return_date` date DEFAULT NULL COMMENT '退货日期', - `return_type` tinyint DEFAULT '1' COMMENT '退货类型: 1-质量退货, 2-数量差异, 3-规格不符, 4-其他', - `return_reason` text COMMENT '退货原因', - `total_qty` decimal(18,4) DEFAULT '0.0000' COMMENT '退货总数量', - `total_amount` decimal(18,4) DEFAULT '0.0000' COMMENT '退货总金额', - `inspection_status` tinyint DEFAULT '0' COMMENT '质检状态: 0-未质检, 1-质检中, 2-已质检', - `inspection_result` tinyint DEFAULT NULL COMMENT '质检结果: 1-合格, 2-不合格, 3-部分合格', - `warehouse_id` bigint unsigned DEFAULT NULL COMMENT '退货入库仓库ID', - `inbound_status` tinyint DEFAULT '0' COMMENT '入库状态: 0-未入库, 1-已入库', - `status` tinyint DEFAULT '1' COMMENT '状态: 1-待审核, 2-已审核, 3-已退货, 4-已拒绝', - `remark` text COMMENT '备注', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP, - `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - `create_by` bigint unsigned DEFAULT NULL, - `deleted` tinyint DEFAULT '0', - PRIMARY KEY (`id`), - UNIQUE KEY `uk_return_no` (`return_no`), - KEY `idx_order` (`order_id`), - KEY `idx_customer` (`customer_id`), - KEY `idx_status` (`status`), - CONSTRAINT `fk_sal_return_order_order` FOREIGN KEY (`order_id`) REFERENCES `sal_order` (`id`) ON DELETE RESTRICT ON UPDATE CASCADE -) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='退货单表'; +-- 付款记录表 +CREATE TABLE `fin_payment_record` ( + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '记录ID', + `payment_no` VARCHAR(50) NOT NULL COMMENT '付款单号', + `payable_id` BIGINT UNSIGNED COMMENT '应付款ID', + `supplier_id` BIGINT UNSIGNED NOT NULL COMMENT '供应商ID', + `amount` DECIMAL(18,4) NOT NULL COMMENT '付款金额', + `payment_date` DATE COMMENT '付款日期', + `payment_method` VARCHAR(50) COMMENT '付款方式: 银行转账, 现金, 支票等', + `bank_account` VARCHAR(50) COMMENT '付款账户', + `reference_no` VARCHAR(50) COMMENT '参考号/流水号', + `handler_id` BIGINT UNSIGNED COMMENT '经办人ID', + `remark` VARCHAR(255) COMMENT '备注', + `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + PRIMARY KEY (`id`), + UNIQUE KEY `uk_payment_no` (`payment_no`), + KEY `idx_payable` (`payable_id`), + KEY `idx_supplier` (`supplier_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='付款记录表'; --- 退货单明细表 -CREATE TABLE `sal_return_order_item` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT, - `return_id` bigint unsigned NOT NULL COMMENT '退货单ID', - `delivery_item_id` bigint unsigned DEFAULT NULL COMMENT '送货单明细ID', - `material_id` bigint unsigned NOT NULL COMMENT '物料ID', - `material_name` varchar(100) DEFAULT NULL COMMENT '物料名称', - `material_spec` varchar(255) DEFAULT NULL COMMENT '规格型号', - `quantity` decimal(18,4) NOT NULL COMMENT '退货数量', - `unit` varchar(20) DEFAULT NULL COMMENT '单位', - `unit_price` decimal(18,4) DEFAULT NULL COMMENT '单价', - `amount` decimal(18,4) DEFAULT NULL COMMENT '金额', - `batch_no` varchar(50) DEFAULT NULL COMMENT '批次号', - `inspection_qty` decimal(18,4) DEFAULT '0.0000' COMMENT '质检数量', - `qualified_qty` decimal(18,4) DEFAULT '0.0000' COMMENT '合格数量', - `remark` varchar(255) DEFAULT NULL COMMENT '备注', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP, - PRIMARY KEY (`id`), - KEY `idx_return` (`return_id`), - KEY `idx_material` (`material_id`), - CONSTRAINT `fk_sal_return_order_item_return` FOREIGN KEY (`return_id`) REFERENCES `sal_return_order` (`id`) ON DELETE CASCADE ON UPDATE CASCADE -) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='退货单明细表'; +-- ======================================================== +-- 9. 质量管理模块 +-- ======================================================== --- 打样订单表 -CREATE TABLE `sal_sample_order` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT, - `order_no` varchar(50) NOT NULL COMMENT '打样订单号', - `notify_date` date DEFAULT NULL COMMENT '通知日期', - `customer_id` bigint unsigned DEFAULT NULL COMMENT '客户ID', - `customer_name` varchar(100) DEFAULT NULL COMMENT '客户名称', - `product_name` varchar(200) DEFAULT NULL COMMENT '产品名称', - `material_no` varchar(50) DEFAULT NULL COMMENT '物料编号', - `version` varchar(20) DEFAULT 'A' COMMENT '版本', - `size_spec` varchar(200) DEFAULT NULL COMMENT '尺寸规格', - `material_spec` varchar(200) DEFAULT NULL COMMENT '材料规格', - `specification` varchar(200) DEFAULT NULL COMMENT '规格型号', - `quantity` int DEFAULT '0' COMMENT '数量', - `order_date` date DEFAULT NULL COMMENT '订单日期', - `customer_require_date` date DEFAULT NULL COMMENT '客户需求日期', - `delivery_date` date DEFAULT NULL COMMENT '交付日期', - `actual_delivery_date` date DEFAULT NULL COMMENT '实际交付日期', - `delivery_status` varchar(20) DEFAULT 'pending' COMMENT '交付状态', - `status` varchar(20) DEFAULT 'pending' COMMENT '状态: pending/producing/completed/cancelled', - `remark` text COMMENT '备注', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP, - `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - `create_by` bigint unsigned DEFAULT NULL, - `deleted` tinyint DEFAULT '0', +-- 检验单表 +CREATE TABLE `qc_inspection` ( + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '检验单ID', + `inspection_no` VARCHAR(50) NOT NULL COMMENT '检验单号', + `inspection_type` TINYINT COMMENT '检验类型: 1-来料检验, 2-过程检验, 3-成品检验', + `source_type` VARCHAR(50) COMMENT '来源类型', + `source_id` BIGINT UNSIGNED COMMENT '来源单据ID', + `source_no` VARCHAR(50) COMMENT '来源单据号', + `material_id` BIGINT UNSIGNED COMMENT '物料ID', + `batch_no` VARCHAR(50) COMMENT '批次号', + `inspection_qty` DECIMAL(18,4) COMMENT '检验数量', + `qualified_qty` DECIMAL(18,4) DEFAULT 0 COMMENT '合格数量', + `unqualified_qty` DECIMAL(18,4) DEFAULT 0 COMMENT '不合格数量', + `inspection_result` TINYINT COMMENT '检验结果: 1-合格, 2-不合格, 3-特采', + `inspector_id` BIGINT UNSIGNED COMMENT '检验员ID', + `inspection_date` DATE COMMENT '检验日期', + `remark` TEXT COMMENT '备注', + `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', PRIMARY KEY (`id`), - UNIQUE KEY `uk_order_no` (`order_no`), - KEY `idx_customer` (`customer_id`), - KEY `idx_status` (`status`), - KEY `idx_notify_date` (`notify_date`) -) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='打样订单表'; + UNIQUE KEY `uk_inspection_no` (`inspection_no`), + KEY `idx_type` (`inspection_type`), + KEY `idx_result` (`inspection_result`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='检验单表'; --- 供应商评估表 -CREATE TABLE `srm_supplier_eval` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT, - `eval_no` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '评估编号', - `supplier_id` bigint unsigned DEFAULT NULL COMMENT '供应商ID', - `supplier_name` varchar(200) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '供应商名称', - `eval_period` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT 'month' COMMENT '评估周期', - `period_start` date DEFAULT NULL COMMENT '周期开始', - `period_end` date DEFAULT NULL COMMENT '周期结束', - `quality_score` decimal(5,2) DEFAULT '0.00' COMMENT '质量分', - `delivery_score` decimal(5,2) DEFAULT '0.00' COMMENT '交付分', - `price_score` decimal(5,2) DEFAULT '0.00' COMMENT '价格分', - `service_score` decimal(5,2) DEFAULT '0.00' COMMENT '服务分', - `total_score` decimal(5,2) DEFAULT '0.00' COMMENT '总分', - `quality_rate` decimal(5,2) DEFAULT '0.00' COMMENT '合格率', - `on_time_rate` decimal(5,2) DEFAULT '0.00' COMMENT '准时率', - `order_count` int DEFAULT '0' COMMENT '订单数', - `defect_count` int DEFAULT '0' COMMENT '缺陷数', - `supplier_level` varchar(5) COLLATE utf8mb4_unicode_ci DEFAULT 'C' COMMENT '供应商等级', - `status` tinyint DEFAULT '0' COMMENT '状态', - `evaluator` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '评估人', - `eval_time` datetime DEFAULT NULL COMMENT '评估时间', - `remark` text COLLATE utf8mb4_unicode_ci COMMENT '备注', - `deleted` tinyint DEFAULT '0', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP, - `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - PRIMARY KEY (`id`), - UNIQUE KEY `uk_eval_no` (`eval_no`) -) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='供应商评估表'; +-- 不合格品处理表 +CREATE TABLE `qc_unqualified` ( + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'ID', + `unqualified_no` VARCHAR(50) NOT NULL COMMENT '不合格品编号', + `inspection_id` BIGINT UNSIGNED COMMENT '检验单ID', + `material_id` BIGINT UNSIGNED COMMENT '物料ID', + `batch_no` VARCHAR(50) COMMENT '批次号', + `unqualified_qty` DECIMAL(18,4) COMMENT '不合格数量', + `unqualified_type` VARCHAR(50) COMMENT '不合格类型', + `unqualified_reason` TEXT COMMENT '不合格原因', + `handle_method` TINYINT COMMENT '处理方式: 1-返工, 2-报废, 3-特采', + `handle_result` TEXT COMMENT '处理结果', + `handler_id` BIGINT UNSIGNED COMMENT '处理人ID', + `handle_date` DATE COMMENT '处理日期', + `status` TINYINT DEFAULT 1 COMMENT '状态: 1-待处理, 2-处理中, 3-已处理', + `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + PRIMARY KEY (`id`), + UNIQUE KEY `uk_unqualified_no` (`unqualified_no`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='不合格品处理表'; --- 供应商评估明细 -CREATE TABLE `srm_supplier_eval_item` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT, - `eval_id` bigint unsigned NOT NULL COMMENT '评估ID', - `category` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '分类', - `item_name` varchar(200) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '项目名称', - `weight` decimal(5,2) DEFAULT '0.00' COMMENT '权重', - `score` decimal(5,2) DEFAULT '0.00' COMMENT '得分', - `actual_value` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '实际值', - `target_value` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '目标值', - `remark` text COLLATE utf8mb4_unicode_ci COMMENT '备注', - `sort_order` int DEFAULT '0' COMMENT '排序', - PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='供应商评估明细'; +-- ======================================================== +-- 初始化数据 +-- ======================================================== --- sys_company -CREATE TABLE `sys_company` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT, - `full_name` varchar(200) COLLATE utf8mb4_unicode_ci DEFAULT NULL, - `company_name` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL, - `company_code` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL, - `address` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL, - `phone` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL, - `email` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL, - `logo` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL, - `status` tinyint DEFAULT '1', - `deleted` tinyint DEFAULT '0', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP, - `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - `short_name` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL, - `code` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL, - `legal_person` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL, - `reg_address` varchar(500) COLLATE utf8mb4_unicode_ci DEFAULT NULL, - `contact_phone` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL, - `tax_no` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL, - `bank_name` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL, - `bank_account` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL, - `website` varchar(200) COLLATE utf8mb4_unicode_ci DEFAULT NULL, - `fax` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL, - `postcode` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT NULL, - `description` text COLLATE utf8mb4_unicode_ci, - PRIMARY KEY (`id`) -) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +-- 初始化管理员用户(密码需要加密后存储) +INSERT INTO `sys_user` (`username`, `password`, `real_name`, `status`, `create_time`) VALUES +('admin', '$2b$10$exPRft/Zkzh5o1QmCaKy/uVilWGDosF8un/6yA6jEIneEZNJ319F2', '系统管理员', 1, NOW()); + +-- 初始化部门 +INSERT INTO `sys_department` (`dept_name`, `dept_code`, `sort_order`, `status`, `create_time`) VALUES +('总经办', 'DEPT001', 1, 1, NOW()), +('销售部', 'DEPT002', 2, 1, NOW()), +('采购部', 'DEPT003', 3, 1, NOW()), +('生产部', 'DEPT004', 4, 1, NOW()), +('仓库部', 'DEPT005', 5, 1, NOW()), +('财务部', 'DEPT006', 6, 1, NOW()), +('品质部', 'DEPT007', 7, 1, NOW()); + +-- 初始化角色 +INSERT INTO `sys_role` (`role_name`, `role_code`, `description`, `status`, `create_time`) VALUES +('系统管理员', 'ROLE_ADMIN', '拥有所有权限', 1, NOW()), +('销售人员', 'ROLE_SALES', '销售相关权限', 1, NOW()), +('采购人员', 'ROLE_PURCHASE', '采购相关权限', 1, NOW()), +('仓库人员', 'ROLE_WAREHOUSE', '仓库相关权限', 1, NOW()), +('财务人员', 'ROLE_FINANCE', '财务相关权限', 1, NOW()); + +-- 初始化字典类型 +INSERT INTO `sys_dict_type` (`dict_name`, `dict_code`, `status`, `create_time`) VALUES +('用户状态', 'user_status', 1, NOW()), +('订单状态', 'order_status', 1, NOW()), +('付款方式', 'payment_method', 1, NOW()), +('物料类型', 'material_type', 1, NOW()); + +-- 初始化字典数据 +INSERT INTO `sys_dict_data` (`dict_type_id`, `dict_label`, `dict_value`, `sort_order`, `status`) VALUES +(1, '启用', '1', 1, 1), +(1, '禁用', '0', 2, 1), +(2, '待确认', '1', 1, 1), +(2, '已确认', '2', 2, 1), +(2, '已完成', '3', 3, 1), +(3, '银行转账', 'bank_transfer', 1, 1), +(3, '现金', 'cash', 2, 1), +(3, '支票', 'check', 3, 1), +(4, '原材料', '1', 1, 1), +(4, '半成品', '2', 2, 1), +(4, '成品', '3', 3, 1); + +-- 初始化系统配置 +INSERT INTO `sys_config` (`config_name`, `config_key`, `config_value`, `config_type`, `description`) VALUES +('系统名称', 'sys.name', 'ERP管理系统', 1, '系统显示名称'), +('系统版本', 'sys.version', 'v1.0.0', 1, '系统版本号'), +('版权信息', 'sys.copyright', '© 2024 All Rights Reserved', 1, '版权信息'), +('默认密码', 'sys.default.password', '123456', 1, '用户默认密码'); --- 系统配置表 -CREATE TABLE `sys_config` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '配置ID', - `config_name` varchar(50) NOT NULL COMMENT '配置名称', - `config_key` varchar(50) NOT NULL COMMENT '配置键名', - `config_value` varchar(500) NOT NULL COMMENT '配置值', - `config_type` tinyint DEFAULT '1' COMMENT '配置类型: 1-系统, 2-业务', - `description` varchar(255) DEFAULT NULL COMMENT '描述', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', - `config_type_enum` varchar(20) DEFAULT 'string' COMMENT '值类型: string/number/boolean/json', - `category` varchar(50) DEFAULT '系统基础配置' COMMENT '配置分类', - `display_name` varchar(100) DEFAULT NULL COMMENT '显示名称', - `sort_order` int DEFAULT '0' COMMENT '排序号', - `is_required` tinyint DEFAULT '0' COMMENT '是否必填: 1-是, 0-否', - `approval_required` tinyint DEFAULT '0' COMMENT '是否需要审批: 1-是, 0-否', - `status` tinyint DEFAULT '1' COMMENT '状态: 1-启用, 0-禁用', - `create_by` bigint unsigned DEFAULT NULL COMMENT '创建人ID', - `update_by` bigint unsigned DEFAULT NULL COMMENT '更新人ID', - `deleted` tinyint NOT NULL DEFAULT '0' COMMENT '软删除: 0-正常, 1-已删除', - PRIMARY KEY (`id`), - UNIQUE KEY `uk_config_key` (`config_key`) -) ENGINE=InnoDB AUTO_INCREMENT=71 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='系统配置表'; +-- ======================================================== +-- 10. 生产排程核心表(P0-2 甘特图 + 有限产能) +-- ======================================================== --- 部门表 -CREATE TABLE `sys_department` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '部门ID', - `parent_id` bigint unsigned DEFAULT NULL COMMENT '父部门ID, NULL为顶级部门', - `dept_name` varchar(100) NOT NULL COMMENT '部门名称', - `dept_code` varchar(50) DEFAULT NULL COMMENT '部门编码', - `sort_order` int DEFAULT '0' COMMENT '排序序号', - `leader_id` bigint unsigned DEFAULT NULL COMMENT '部门负责人ID', - `phone` varchar(20) DEFAULT NULL COMMENT '联系电话', - `email` varchar(100) DEFAULT NULL COMMENT '邮箱', - `status` tinyint DEFAULT '1' COMMENT '状态: 0-禁用, 1-启用', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', - `deleted` tinyint NOT NULL DEFAULT '0' COMMENT '软删除: 0-正常, 1-已删除', - `create_by` bigint unsigned DEFAULT NULL COMMENT '创建人ID', - `update_by` bigint unsigned DEFAULT NULL COMMENT '更新人ID', +-- 设备表(产线/机台) +CREATE TABLE `eqp_equipment` ( + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '设备ID', + `equipment_code` VARCHAR(50) NOT NULL COMMENT '设备编码', + `equipment_name` VARCHAR(100) NOT NULL COMMENT '设备名称', + `equipment_type` VARCHAR(50) NOT NULL COMMENT '设备类型: printing-印刷机, die_cut-模切机, trademark-商标机, packaging-包装机', + `workshop` VARCHAR(50) NOT NULL COMMENT '所属车间: die_cut, trademark, printing, packaging', + `capacity_per_hour` DECIMAL(10,2) DEFAULT 100 COMMENT '每小时产能(件/小时)', + `max_colors` INT DEFAULT 1 COMMENT '最大支持色数', + `setup_time_minutes` INT DEFAULT 30 COMMENT '换型/准备时间(分钟)', + `status` VARCHAR(20) DEFAULT 'available' COMMENT '状态: available-可用, maintenance-维护, offline-离线', + `remark` VARCHAR(255) COMMENT '备注', + `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `deleted` TINYINT DEFAULT 0 COMMENT '删除标记: 0-未删除, 1-已删除', PRIMARY KEY (`id`), - KEY `idx_parent` (`parent_id`), - KEY `idx_status` (`status`), - KEY `idx_leader` (`leader_id`), - CONSTRAINT `fk_sys_department_leader` FOREIGN KEY (`leader_id`) REFERENCES `sys_user` (`id`) ON DELETE SET NULL ON UPDATE CASCADE, - CONSTRAINT `fk_sys_department_parent` FOREIGN KEY (`parent_id`) REFERENCES `sys_department` (`id`) ON DELETE RESTRICT ON UPDATE CASCADE -) ENGINE=InnoDB AUTO_INCREMENT=53 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='部门表'; + UNIQUE KEY `uk_equipment_code` (`equipment_code`), + KEY `idx_type` (`equipment_type`), + KEY `idx_workshop` (`workshop`), + KEY `idx_status` (`status`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='设备表'; --- 字典数据表 -CREATE TABLE `sys_dict_data` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '字典数据ID', - `dict_type_id` bigint unsigned NOT NULL COMMENT '字典类型ID', - `dict_label` varchar(50) NOT NULL COMMENT '字典标签', - `dict_value` varchar(100) NOT NULL COMMENT '字典键值', - `sort_order` int DEFAULT '0' COMMENT '排序序号', - `status` tinyint DEFAULT '1' COMMENT '状态: 0-禁用, 1-启用', - `remark` varchar(255) DEFAULT NULL COMMENT '备注', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', - `create_by` bigint unsigned DEFAULT NULL COMMENT '创建人ID', - `update_by` bigint unsigned DEFAULT NULL COMMENT '更新人ID', - `deleted` tinyint NOT NULL DEFAULT '0' COMMENT '软删除: 0-正常, 1-已删除', +-- 生产排程主表 +CREATE TABLE `prd_schedule` ( + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '排程ID', + `schedule_no` VARCHAR(50) NOT NULL COMMENT '排产单号', + `order_id` BIGINT UNSIGNED COMMENT '销售订单ID', + `order_no` VARCHAR(50) COMMENT '销售订单号', + `work_order_id` BIGINT UNSIGNED COMMENT '生产工单ID', + `work_order_no` VARCHAR(50) COMMENT '生产工单号', + `product_id` BIGINT UNSIGNED COMMENT '产品ID', + `product_code` VARCHAR(50) COMMENT '产品编码', + `product_name` VARCHAR(100) NOT NULL COMMENT '产品名称', + `workshop` VARCHAR(50) NOT NULL COMMENT '车间: die_cut, trademark, printing, packaging', + `planned_qty` DECIMAL(18,4) NOT NULL DEFAULT 0 COMMENT '计划数量', + `completed_qty` DECIMAL(18,4) DEFAULT 0 COMMENT '已完成数量', + `planned_start` DATETIME COMMENT '计划开始时间', + `planned_end` DATETIME COMMENT '计划结束时间', + `actual_start` DATETIME COMMENT '实际开始时间', + `actual_end` DATETIME COMMENT '实际结束时间', + `priority` TINYINT DEFAULT 2 COMMENT '优先级: 1-紧急, 2-正常, 3-低', + `status` TINYINT DEFAULT 1 COMMENT '状态: 1-待排产, 2-已排产, 3-生产中, 4-已完成, 5-已取消', + `scheduler` VARCHAR(50) COMMENT '排产人', + `remark` TEXT COMMENT '备注', + `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `deleted` TINYINT DEFAULT 0 COMMENT '删除标记: 0-未删除, 1-已删除', PRIMARY KEY (`id`), - KEY `idx_dict_type` (`dict_type_id`), + UNIQUE KEY `uk_schedule_no` (`schedule_no`), + KEY `idx_work_order` (`work_order_id`), + KEY `idx_product` (`product_id`), + KEY `idx_workshop` (`workshop`), KEY `idx_status` (`status`), - CONSTRAINT `fk_dict_data_type` FOREIGN KEY (`dict_type_id`) REFERENCES `sys_dict_type` (`id`) ON DELETE RESTRICT ON UPDATE CASCADE -) ENGINE=InnoDB AUTO_INCREMENT=67 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='字典数据表'; - --- 字典类型表 -CREATE TABLE `sys_dict_type` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '字典类型ID', - `dict_name` varchar(50) NOT NULL COMMENT '字典名称', - `dict_code` varchar(50) NOT NULL COMMENT '字典编码', - `description` varchar(255) DEFAULT NULL COMMENT '描述', - `status` tinyint DEFAULT '1' COMMENT '状态: 0-禁用, 1-启用', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', - `create_by` bigint unsigned DEFAULT NULL COMMENT '创建人ID', - `update_by` bigint unsigned DEFAULT NULL COMMENT '更新人ID', - `deleted` tinyint NOT NULL DEFAULT '0' COMMENT '软删除: 0-正常, 1-已删除', - PRIMARY KEY (`id`), - UNIQUE KEY `uk_dict_code` (`dict_code`) -) ENGINE=InnoDB AUTO_INCREMENT=26 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='字典类型表'; - --- sys_employee -CREATE TABLE `sys_employee` ( - `id` int NOT NULL AUTO_INCREMENT, - `employee_no` varchar(50) NOT NULL, - `name` varchar(100) NOT NULL, - `gender` int DEFAULT '1', - `age` int DEFAULT NULL, - `id_card` varchar(20) DEFAULT NULL, - `phone` varchar(20) DEFAULT NULL, - `email` varchar(100) DEFAULT NULL, - `dept_id` int DEFAULT NULL, - `dept_name` varchar(100) DEFAULT NULL, - `section` varchar(100) DEFAULT NULL, - `role_id` int DEFAULT NULL, - `role_name` varchar(100) DEFAULT NULL, - `position` varchar(100) DEFAULT NULL, - `entry_date` date DEFAULT NULL, - `birth_date` date DEFAULT NULL, - `native_place` varchar(100) DEFAULT NULL, - `home_address` varchar(255) DEFAULT NULL, - `current_address` varchar(255) DEFAULT NULL, - `birth_month` varchar(10) DEFAULT NULL, - `id_card_expiry` date DEFAULT NULL, - `education` varchar(50) DEFAULT NULL, - `remark` text, - `status` int DEFAULT '1', - `photo` varchar(500) DEFAULT NULL, - `create_time` datetime DEFAULT CURRENT_TIMESTAMP, - `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - PRIMARY KEY (`id`), - UNIQUE KEY `uk_employee_no` (`employee_no`) -) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + KEY `idx_planned_start` (`planned_start`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='生产排程主表'; --- 事件幂等表 -CREATE TABLE `sys_event_processed` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT, - `event_id` bigint NOT NULL COMMENT 'domain_event_outbox.id', - `handler_name` varchar(255) NOT NULL COMMENT '处理器类名', - `status` varchar(20) NOT NULL DEFAULT 'processed' COMMENT 'processing-处理中, processed-已处理', - `processed_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, - PRIMARY KEY (`id`), - UNIQUE KEY `uk_event_handler` (`event_id`,`handler_name`), - KEY `idx_processed_at` (`processed_at`), - KEY `idx_status_processed_at` (`status`,`processed_at`) -) ENGINE=InnoDB AUTO_INCREMENT=729 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='事件幂等表'; - --- 登录日志表 -CREATE TABLE `sys_login_log` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '日志ID', - `user_id` bigint unsigned DEFAULT NULL COMMENT '用户ID', - `username` varchar(50) DEFAULT NULL COMMENT '用户名', - `login_type` tinyint DEFAULT NULL COMMENT '登录类型: 1-账号密码, 2-手机验证码', - `ip` varchar(50) DEFAULT NULL COMMENT 'IP地址', - `location` varchar(100) DEFAULT NULL COMMENT '登录地点', - `user_agent` varchar(500) DEFAULT NULL COMMENT '浏览器UA', - `status` tinyint DEFAULT NULL COMMENT '登录状态: 0-失败, 1-成功', - `error_msg` varchar(255) DEFAULT NULL COMMENT '错误信息', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - PRIMARY KEY (`id`), - KEY `idx_user` (`user_id`), - KEY `idx_create_time` (`create_time`) -) ENGINE=InnoDB AUTO_INCREMENT=32 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='登录日志表'; - --- 菜单权限表 -CREATE TABLE `sys_menu` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '菜单ID', - `parent_id` bigint unsigned DEFAULT NULL COMMENT '父菜单ID, NULL为顶级菜单', - `menu_name` varchar(50) NOT NULL COMMENT '菜单名称', - `menu_code` varchar(50) DEFAULT NULL, - `menu_type` tinyint NOT NULL COMMENT '菜单类型: 1-目录, 2-菜单, 3-按钮', - `icon` varchar(50) DEFAULT NULL COMMENT '菜单图标', - `path` varchar(200) DEFAULT NULL COMMENT '路由路径', - `component` varchar(255) DEFAULT NULL COMMENT '组件路径', - `permission` varchar(100) DEFAULT NULL COMMENT '权限标识', - `sort_order` int DEFAULT '0' COMMENT '排序序号', - `status` tinyint DEFAULT '1' COMMENT '状态: 0-禁用, 1-启用', - `visible` tinyint DEFAULT '1' COMMENT '是否可见: 0-隐藏, 1-显示', - `is_external` tinyint DEFAULT '0', - `is_cache` tinyint DEFAULT '1', - `is_visible` tinyint DEFAULT '1', - `keep_alive` tinyint DEFAULT '0' COMMENT '是否缓存: 0-否, 1-是', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', - `create_by` bigint unsigned DEFAULT NULL COMMENT '创建人ID', - `update_by` bigint unsigned DEFAULT NULL COMMENT '更新人ID', - `deleted` tinyint NOT NULL DEFAULT '0' COMMENT '软删除: 0-正常, 1-已删除', - PRIMARY KEY (`id`), - UNIQUE KEY `uk_menu_code` (`menu_code`), - KEY `idx_parent` (`parent_id`), - KEY `idx_type` (`menu_type`), - KEY `idx_status` (`status`), - CONSTRAINT `fk_sys_menu_parent` FOREIGN KEY (`parent_id`) REFERENCES `sys_menu` (`id`) ON DELETE RESTRICT ON UPDATE CASCADE -) ENGINE=InnoDB AUTO_INCREMENT=101 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='菜单权限表'; +-- 排程明细表(色序工序级排程) +CREATE TABLE `prd_schedule_detail` ( + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '明细ID', + `schedule_id` BIGINT UNSIGNED NOT NULL COMMENT '排程ID', + `work_order_id` BIGINT UNSIGNED NOT NULL COMMENT '工单ID', + `color_seq_no` INT NOT NULL COMMENT '色序号', + `color_name` VARCHAR(50) COMMENT '颜色名称', + `equipment_id` BIGINT UNSIGNED COMMENT '设备ID', + `equipment_name` VARCHAR(100) COMMENT '设备名称', + `planned_start` DATETIME COMMENT '计划开始时间', + `planned_end` DATETIME COMMENT '计划结束时间', + `actual_start` DATETIME COMMENT '实际开始时间', + `actual_end` DATETIME COMMENT '实际结束时间', + `duration_hours` DECIMAL(8,2) COMMENT '预计耗时(小时)', + `status` TINYINT DEFAULT 1 COMMENT '状态: 1-待排, 2-已排, 3-生产中, 4-已完成', + `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + PRIMARY KEY (`id`), + UNIQUE KEY `uk_work_order_seq` (`work_order_id`, `color_seq_no`), + KEY `idx_schedule` (`schedule_id`), + KEY `idx_equipment` (`equipment_id`), + KEY `idx_status` (`status`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='排程明细表(色序级)'; --- sys_migration -CREATE TABLE `sys_migration` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT, - `migration_name` varchar(255) NOT NULL, - `batch` int NOT NULL DEFAULT '1', - `applied_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP, - `execution_time` int DEFAULT '0', - PRIMARY KEY (`id`), - UNIQUE KEY `migration_name` (`migration_name`), - KEY `idx_migration_name` (`migration_name`), - KEY `idx_applied_at` (`applied_at`) -) ENGINE=InnoDB AUTO_INCREMENT=40 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; +-- 工单色序表(多色套印工艺) +CREATE TABLE `prd_work_order_color_seq` ( + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'ID', + `work_order_id` BIGINT UNSIGNED NOT NULL COMMENT '工单ID', + `seq_no` INT NOT NULL COMMENT '色序号: 1,2,3...', + `color_name` VARCHAR(50) NOT NULL COMMENT '颜色名称: 红,黄,蓝,黑...', + `screen_plate_id` BIGINT UNSIGNED COMMENT '网版ID', + `ink_formula_id` BIGINT UNSIGNED COMMENT '油墨配方ID', + `estimated_duration_hours` DECIMAL(8,2) DEFAULT 4 COMMENT '预计耗时(小时)', + `equipment_type_required` VARCHAR(50) NOT NULL COMMENT '所需设备类型: printing, die_cut...', + `depends_on_seq` INT COMMENT '依赖前序色序号(前序完成后才能开始)', + `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + PRIMARY KEY (`id`), + UNIQUE KEY `uk_work_order_seq` (`work_order_id`, `seq_no`), + KEY `idx_work_order` (`work_order_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='工单色序表(多色套印)'; + +-- 初始化设备数据 +INSERT INTO `eqp_equipment` (`equipment_code`, `equipment_name`, `equipment_type`, `workshop`, `capacity_per_hour`, `max_colors`, `setup_time_minutes`, `status`) VALUES +('PT001', '全自动丝印机A', 'printing', 'printing', 500, 6, 45, 'available'), +('PT002', '全自动丝印机B', 'printing', 'printing', 450, 4, 45, 'available'), +('PT003', '半自动丝印机C', 'printing', 'printing', 300, 2, 30, 'available'), +('PT004', '半自动丝印机D', 'printing', 'printing', 300, 2, 30, 'available'), +('DC001', '数控模切机A', 'die_cut', 'die_cut', 800, 1, 20, 'available'), +('DC002', '数控模切机B', 'die_cut', 'die_cut', 800, 1, 20, 'available'), +('DC003', '平压模切机C', 'die_cut', 'die_cut', 600, 1, 15, 'available'), +('TM001', '商标印刷机A', 'trademark', 'trademark', 400, 4, 30, 'available'), +('TM002', '商标印刷机B', 'trademark', 'trademark', 400, 4, 30, 'available'), +('TM003', '商标模切机C', 'trademark', 'trademark', 600, 1, 20, 'available'), +('PK001', '自动包装线A', 'packaging', 'packaging', 1000, 1, 10, 'available'), +('PK002', '自动包装线B', 'packaging', 'packaging', 1000, 1, 10, 'available'); --- 通知公告表 -CREATE TABLE `sys_notice` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT, - `notice_title` varchar(200) NOT NULL COMMENT '公告标题', - `notice_type` tinyint NOT NULL COMMENT '公告类型: 1-通知, 2-公告', - `notice_content` text COMMENT '公告内容', - `status` tinyint DEFAULT '1' COMMENT '状态: 0-关闭, 1-正常', - `create_by` bigint unsigned DEFAULT NULL COMMENT '创建者', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP, - `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - `remark` varchar(500) DEFAULT NULL COMMENT '备注', - `deleted` tinyint DEFAULT '0', - PRIMARY KEY (`id`) -) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='通知公告表'; +-- ======================================================== +-- 10. 物料领用管理模块 +-- ======================================================== --- 系统通知 -CREATE TABLE `sys_notification` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '通知ID', - `type` varchar(30) NOT NULL COMMENT '通知类型:inventory_alert/system/task/security等', - `title` varchar(200) NOT NULL COMMENT '通知标题', - `content` text COMMENT '通知内容', - `user_id` bigint unsigned DEFAULT NULL COMMENT '接收用户ID(空表示广播)', - `is_read` tinyint DEFAULT '0' COMMENT '是否已读:0未读 1已读', - `read_time` datetime DEFAULT NULL COMMENT '阅读时间', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', +-- 领料单主表 +CREATE TABLE `material_requisitions` ( + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '领料单ID', + `requisition_no` VARCHAR(50) NOT NULL COMMENT '领料单编号,格式:MR+YYYYMMDD+4位序号', + `work_order_id` BIGINT UNSIGNED COMMENT '关联工单ID', + `work_order_no` VARCHAR(50) COMMENT '工单编号', + `type` VARCHAR(20) NOT NULL COMMENT '类型:normal-正常领料, over-超领, supplementary-补料', + `status` TINYINT DEFAULT 0 COMMENT '状态:0=待审批,1=待出库,2=已出库,3=已取消', + `applicant_id` BIGINT UNSIGNED COMMENT '申请人ID', + `applicant_name` VARCHAR(50) COMMENT '申请人姓名', + `approver_id` BIGINT UNSIGNED COMMENT '审批人ID', + `approver_name` VARCHAR(50) COMMENT '审批人姓名', + `approve_time` DATETIME COMMENT '审批时间', + `total_quantity` DECIMAL(18,4) DEFAULT 0 COMMENT '领料总数量', + `issued_quantity` DECIMAL(18,4) DEFAULT 0 COMMENT '已出库数量', + `warehouse_id` BIGINT UNSIGNED COMMENT '仓库ID', + `original_requisition_id` BIGINT UNSIGNED COMMENT '原领料单ID(补料时关联)', + `reason` VARCHAR(255) COMMENT '超领/补料原因', + `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `remark` TEXT COMMENT '备注', + `deleted` TINYINT DEFAULT 0 COMMENT '删除标记', PRIMARY KEY (`id`), - KEY `idx_user` (`user_id`), + UNIQUE KEY `uk_requisition_no` (`requisition_no`), + KEY `idx_work_order` (`work_order_id`), KEY `idx_type` (`type`), - KEY `idx_read` (`is_read`), - KEY `idx_create_time` (`create_time`) -) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='系统通知'; - --- 操作日志记录表 -CREATE TABLE `sys_oper_log` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT, - `title` varchar(50) DEFAULT NULL COMMENT '操作模块', - `business_type` tinyint DEFAULT '0' COMMENT '业务类型: 0-其它, 1-新增, 2-修改, 3-删除', - `method` varchar(200) DEFAULT NULL COMMENT '方法名称', - `request_method` varchar(10) DEFAULT NULL COMMENT '请求方式', - `operator_type` tinyint DEFAULT '0' COMMENT '操作类别: 0-其它, 1-后台用户', - `oper_name` varchar(50) DEFAULT NULL COMMENT '操作人员', - `oper_url` varchar(500) DEFAULT NULL COMMENT '请求URL', - `oper_ip` varchar(128) DEFAULT NULL COMMENT '主机地址', - `oper_param` text COMMENT '请求参数', - `json_result` text COMMENT '返回参数', - `status` tinyint DEFAULT '1' COMMENT '操作状态: 1-正常, 0-异常', - `error_msg` text COMMENT '错误消息', - `oper_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '操作时间', - `cost_time` bigint DEFAULT '0' COMMENT '消耗时间(毫秒)', - PRIMARY KEY (`id`), - KEY `idx_business_type` (`business_type`), - KEY `idx_oper_time` (`oper_time`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='操作日志记录表'; - --- 操作日志表 -CREATE TABLE `sys_operation_log` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '日志ID', - `user_id` bigint unsigned DEFAULT NULL COMMENT '用户ID', - `username` varchar(50) DEFAULT NULL COMMENT '用户名', - `operation` varchar(100) DEFAULT NULL COMMENT '操作描述', - `method` varchar(10) DEFAULT NULL COMMENT '请求方法', - `request_url` varchar(500) DEFAULT NULL COMMENT '请求URL', - `request_params` text COMMENT '请求参数', - `response_data` text COMMENT '响应数据', - `ip` varchar(50) DEFAULT NULL COMMENT 'IP地址', - `user_agent` varchar(500) DEFAULT NULL COMMENT '浏览器UA', - `execute_time` int DEFAULT NULL COMMENT '执行时长(ms)', - `status` tinyint DEFAULT NULL COMMENT '操作状态: 0-失败, 1-成功', - `error_msg` text COMMENT '错误信息', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - PRIMARY KEY (`id`), - KEY `idx_user` (`user_id`), - KEY `idx_create_time` (`create_time`), - KEY `idx_oper_log_time` (`create_time`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='操作日志表'; - --- 角色表 -CREATE TABLE `sys_role` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '角色ID', - `role_name` varchar(50) NOT NULL COMMENT '角色名称', - `role_code` varchar(50) NOT NULL COMMENT '角色编码', - `description` varchar(255) DEFAULT NULL COMMENT '角色描述', - `data_scope` tinyint DEFAULT '1' COMMENT '数据范围: 1-全部, 2-本部门, 3-本部门及下级, 4-仅本人', - `permissions` json DEFAULT NULL, - `status` tinyint DEFAULT '1' COMMENT '状态: 0-禁用, 1-启用', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', - `deleted` tinyint NOT NULL DEFAULT '0' COMMENT '软删除: 0-正常, 1-已删除', - `parent_id` int DEFAULT NULL COMMENT '父角色ID(角色继承)', - `inherit_mode` varchar(20) NOT NULL DEFAULT 'merge' COMMENT '权限继承模式: merge(合并) | override(覆盖)', - `create_by` bigint unsigned DEFAULT NULL COMMENT '创建人ID', - `update_by` bigint unsigned DEFAULT NULL COMMENT '更新人ID', - PRIMARY KEY (`id`), - UNIQUE KEY `uk_role_code` (`role_code`), KEY `idx_status` (`status`), - KEY `idx_sys_role_parent_id` (`parent_id`) -) ENGINE=InnoDB AUTO_INCREMENT=33 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='角色表'; + KEY `idx_applicant` (`applicant_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='领料单主表'; --- 角色菜单关联表 -CREATE TABLE `sys_role_menu` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID', - `role_id` bigint unsigned NOT NULL COMMENT '角色ID', - `menu_id` bigint unsigned NOT NULL COMMENT '菜单ID', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', +-- 领料单明细表 +CREATE TABLE `material_requisition_items` ( + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '明细ID', + `requisition_id` BIGINT UNSIGNED NOT NULL COMMENT '领料单ID', + `material_id` BIGINT UNSIGNED NOT NULL COMMENT '物料ID', + `material_code` VARCHAR(50) COMMENT '物料编码', + `material_name` VARCHAR(100) COMMENT '物料名称', + `planned_quantity` DECIMAL(18,4) NOT NULL COMMENT '计划数量(BOM计算)', + `actual_quantity` DECIMAL(18,4) DEFAULT 0 COMMENT '实际领用数量', + `issued_quantity` DECIMAL(18,4) DEFAULT 0 COMMENT '已出库数量', + `unit` VARCHAR(20) COMMENT '单位', + `qr_code` VARCHAR(50) COMMENT '小料二维码', + `batch_no` VARCHAR(50) COMMENT '批次号', + `warehouse_location` VARCHAR(50) COMMENT '库位', + `fifo_recommended` TINYINT DEFAULT 0 COMMENT '是否为FIFO推荐批次:0-否,1-是', + `split_flag` TINYINT DEFAULT 1 COMMENT '拆分标记:0-整料,1-小料,2-余料', + `unit_cost` DECIMAL(18,4) COMMENT '单位成本', + `total_cost` DECIMAL(18,4) COMMENT '总成本', + `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', PRIMARY KEY (`id`), - UNIQUE KEY `uk_role_menu` (`role_id`,`menu_id`), - KEY `idx_menu` (`menu_id`), - CONSTRAINT `fk_role_menu_menu` FOREIGN KEY (`menu_id`) REFERENCES `sys_menu` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, - CONSTRAINT `fk_role_menu_role` FOREIGN KEY (`role_id`) REFERENCES `sys_role` (`id`) ON DELETE CASCADE ON UPDATE CASCADE -) ENGINE=InnoDB AUTO_INCREMENT=101 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='角色菜单关联表'; + KEY `idx_requisition` (`requisition_id`), + KEY `idx_material` (`material_id`), + KEY `idx_qr_code` (`qr_code`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='领料单明细表'; --- 薪资表 -CREATE TABLE `sys_salary` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT, - `employee_id` bigint unsigned NOT NULL, - `month` varchar(7) COLLATE utf8mb4_unicode_ci NOT NULL, - `basic_salary` decimal(12,2) DEFAULT '0.00' COMMENT '基本工资', - `position_allowance` decimal(12,2) DEFAULT '0.00' COMMENT '岗位津贴', - `performance_bonus` decimal(12,2) DEFAULT '0.00' COMMENT '绩效奖金', - `overtime_pay` decimal(12,2) DEFAULT '0.00' COMMENT '加班费', - `other_bonus` decimal(12,2) DEFAULT '0.00' COMMENT '其他奖金', - `social_security` decimal(12,2) DEFAULT '0.00' COMMENT '社保', - `housing_fund` decimal(12,2) DEFAULT '0.00' COMMENT '公积金', - `personal_tax` decimal(12,2) DEFAULT '0.00' COMMENT '个人所得税', - `other_deduction` decimal(12,2) DEFAULT '0.00' COMMENT '其他扣款', - `actual_salary` decimal(12,2) DEFAULT '0.00' COMMENT '实发工资', - `remark` text COLLATE utf8mb4_unicode_ci, - `create_time` datetime DEFAULT CURRENT_TIMESTAMP, - `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, +-- 退料单主表 +CREATE TABLE `material_returns` ( + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '退料单ID', + `return_no` VARCHAR(50) NOT NULL COMMENT '退料单编号,格式:RT+YYYYMMDD+4位序号', + `work_order_id` BIGINT UNSIGNED COMMENT '关联工单ID', + `requisition_id` BIGINT UNSIGNED COMMENT '关联领料单ID', + `status` TINYINT DEFAULT 0 COMMENT '状态:0=待确认,1=已入库,2=已取消', + `applicant_id` BIGINT UNSIGNED COMMENT '申请人ID', + `applicant_name` VARCHAR(50) COMMENT '申请人姓名', + `confirm_id` BIGINT UNSIGNED COMMENT '确认人ID', + `confirm_name` VARCHAR(50) COMMENT '确认人姓名', + `confirm_time` DATETIME COMMENT '确认时间', + `total_quantity` DECIMAL(18,4) DEFAULT 0 COMMENT '退料总数量', + `warehouse_id` BIGINT UNSIGNED COMMENT '仓库ID', + `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `remark` TEXT COMMENT '备注', + `deleted` TINYINT DEFAULT 0 COMMENT '删除标记', PRIMARY KEY (`id`), - UNIQUE KEY `uk_employee_month` (`employee_id`,`month`), - KEY `idx_month` (`month`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='薪资表'; + UNIQUE KEY `uk_return_no` (`return_no`), + KEY `idx_work_order` (`work_order_id`), + KEY `idx_requisition` (`requisition_id`), + KEY `idx_status` (`status`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='退料单主表'; --- 系统用户表 -CREATE TABLE `sys_user` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '用户ID', - `username` varchar(50) NOT NULL COMMENT '用户名', - `password` varchar(255) NOT NULL COMMENT '密码(加密存储)', - `real_name` varchar(50) DEFAULT NULL COMMENT '真实姓名', - `email` varchar(100) DEFAULT NULL COMMENT '邮箱', - `phone` varchar(20) DEFAULT NULL COMMENT '手机号', - `avatar` varchar(255) DEFAULT NULL COMMENT '头像URL', - `department_id` bigint unsigned DEFAULT NULL COMMENT '部门ID', - `position` varchar(50) DEFAULT NULL COMMENT '职位', - `status` tinyint DEFAULT '1' COMMENT '状态: 0-禁用, 1-启用', - `first_login` tinyint DEFAULT '1', - `pwd_update_time` datetime DEFAULT NULL COMMENT '密码更新时间', - `login_fail_count` int DEFAULT '0', - `lock_time` datetime DEFAULT NULL, - `last_login_time` datetime DEFAULT NULL COMMENT '最后登录时间', - `last_login_ip` varchar(50) DEFAULT NULL COMMENT '最后登录IP', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', - `create_by` bigint unsigned DEFAULT NULL COMMENT '创建人ID', - `update_by` bigint unsigned DEFAULT NULL COMMENT '更新人ID', - `deleted` tinyint NOT NULL DEFAULT '0' COMMENT '软删除: 0-正常, 1-已删除', +-- 退料单明细表 +CREATE TABLE `material_return_items` ( + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '明细ID', + `return_id` BIGINT UNSIGNED NOT NULL COMMENT '退料单ID', + `material_id` BIGINT UNSIGNED NOT NULL COMMENT '物料ID', + `material_code` VARCHAR(50) COMMENT '物料编码', + `material_name` VARCHAR(100) COMMENT '物料名称', + `quantity` DECIMAL(18,4) NOT NULL COMMENT '退料数量', + `unit` VARCHAR(20) COMMENT '单位', + `qr_code` VARCHAR(50) COMMENT '小料二维码', + `batch_no` VARCHAR(50) COMMENT '批次号', + `reason` VARCHAR(255) COMMENT '退料原因', + `unit_cost` DECIMAL(18,4) COMMENT '单位成本', + `total_cost` DECIMAL(18,4) COMMENT '总成本', + `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', PRIMARY KEY (`id`), - UNIQUE KEY `uk_username` (`username`), - KEY `idx_department` (`department_id`), - KEY `idx_status` (`status`), - CONSTRAINT `fk_sys_user_department` FOREIGN KEY (`department_id`) REFERENCES `sys_department` (`id`) ON DELETE SET NULL ON UPDATE CASCADE -) ENGINE=InnoDB AUTO_INCREMENT=19 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='系统用户表'; + KEY `idx_return` (`return_id`), + KEY `idx_material` (`material_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='退料单明细表'; --- 用户角色关联表 -CREATE TABLE `sys_user_role` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID', - `user_id` bigint unsigned NOT NULL COMMENT '用户ID', - `role_id` bigint unsigned NOT NULL COMMENT '角色ID', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', +-- FIFO异常覆盖记录表 +CREATE TABLE `inv_fifo_override_log` ( + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '记录ID', + `material_id` BIGINT UNSIGNED NOT NULL COMMENT '物料ID', + `recommended_batch_no` VARCHAR(50) COMMENT '推荐批次号', + `actual_batch_no` VARCHAR(50) COMMENT '实际出库批次号', + `requisition_id` BIGINT UNSIGNED COMMENT '领料单ID', + `reason` VARCHAR(255) NOT NULL COMMENT '异常原因', + `operator_id` BIGINT UNSIGNED COMMENT '操作人ID', + `operator_name` VARCHAR(50) COMMENT '操作人姓名', + `approve_id` BIGINT UNSIGNED COMMENT '审批人ID', + `approve_name` VARCHAR(50) COMMENT '审批人姓名', + `status` TINYINT DEFAULT 0 COMMENT '状态:0=待审批,1=已批准,2=已拒绝', + `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', PRIMARY KEY (`id`), - UNIQUE KEY `uk_user_role` (`user_id`,`role_id`), - KEY `idx_role` (`role_id`), - CONSTRAINT `fk_user_role_role` FOREIGN KEY (`role_id`) REFERENCES `sys_role` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, - CONSTRAINT `fk_user_role_user` FOREIGN KEY (`user_id`) REFERENCES `sys_user` (`id`) ON DELETE CASCADE ON UPDATE CASCADE -) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='用户角色关联表'; + KEY `idx_material` (`material_id`), + KEY `idx_requisition` (`requisition_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='FIFO异常覆盖记录表'; --- sys_warehouse_category -CREATE TABLE `sys_warehouse_category` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT, - `code` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL, - `name` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL, - `description` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL, - `sort_order` int DEFAULT '0', - `status` tinyint DEFAULT '1', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP, - `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - PRIMARY KEY (`id`) -) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +-- ======================================================== +-- 11. 成本核算模块 +-- ======================================================== -- 工单成本表 CREATE TABLE `work_order_costs` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '成本ID', - `work_order_id` bigint unsigned NOT NULL COMMENT '工单ID', - `work_order_no` varchar(50) DEFAULT NULL COMMENT '工单编号', - `material_cost` decimal(18,4) DEFAULT '0.0000' COMMENT '原材料成本', - `labor_cost` decimal(18,4) DEFAULT '0.0000' COMMENT '人工成本', - `manufacturing_cost` decimal(18,4) DEFAULT '0.0000' COMMENT '制造费用', - `total_cost` decimal(18,4) DEFAULT '0.0000' COMMENT '总成本', - `unit_cost` decimal(18,4) DEFAULT '0.0000' COMMENT '单位成本', - `quantity` decimal(18,4) DEFAULT '0.0000' COMMENT '完工数量', - `calculate_time` datetime DEFAULT NULL COMMENT '成本计算时间', - `status` tinyint DEFAULT '0' COMMENT '状态:0=未计算,1=已计算,2=已结转', - `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', - `deleted` tinyint NOT NULL DEFAULT '0' COMMENT '软删除标记', - `create_by` bigint unsigned DEFAULT NULL COMMENT '创建人ID', - `update_by` bigint unsigned DEFAULT NULL COMMENT '更新人ID', + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '成本ID', + `work_order_id` BIGINT UNSIGNED NOT NULL COMMENT '工单ID', + `work_order_no` VARCHAR(50) COMMENT '工单编号', + `material_cost` DECIMAL(18,4) DEFAULT 0 COMMENT '原材料成本', + `labor_cost` DECIMAL(18,4) DEFAULT 0 COMMENT '人工成本', + `manufacturing_cost` DECIMAL(18,4) DEFAULT 0 COMMENT '制造费用', + `total_cost` DECIMAL(18,4) DEFAULT 0 COMMENT '总成本', + `unit_cost` DECIMAL(18,4) DEFAULT 0 COMMENT '单位成本', + `quantity` DECIMAL(18,4) DEFAULT 0 COMMENT '完工数量', + `calculate_time` DATETIME COMMENT '成本计算时间', + `status` TINYINT DEFAULT 0 COMMENT '状态:0=未计算,1=已计算,2=已结转', + `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', PRIMARY KEY (`id`), UNIQUE KEY `uk_work_order` (`work_order_id`), - KEY `idx_status` (`status`), - CONSTRAINT `fk_wo_cost_work_order` FOREIGN KEY (`work_order_id`) REFERENCES `prd_work_order` (`id`) ON DELETE CASCADE ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='工单成本表'; + KEY `idx_status` (`status`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='工单成本表'; + +-- 小料批次成本表 +CREATE TABLE `material_batch_costs` ( + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '成本ID', + `qr_code` VARCHAR(50) NOT NULL COMMENT '小料二维码编码', + `material_id` BIGINT UNSIGNED NOT NULL COMMENT '物料ID', + `material_code` VARCHAR(50) COMMENT '物料编码', + `material_name` VARCHAR(100) COMMENT '物料名称', + `batch_no` VARCHAR(50) COMMENT '批次号', + `quantity` DECIMAL(18,4) NOT NULL COMMENT '数量', + `unit_cost` DECIMAL(18,4) NOT NULL COMMENT '单位成本', + `total_cost` DECIMAL(18,4) NOT NULL COMMENT '总成本', + `used_quantity` DECIMAL(18,4) DEFAULT 0 COMMENT '已使用数量', + `remaining_quantity` DECIMAL(18,4) DEFAULT 0 COMMENT '剩余数量', + `split_flag` TINYINT DEFAULT 1 COMMENT '拆分标记:0-整料,1-小料,2-余料', + `warehouse_id` BIGINT UNSIGNED COMMENT '仓库ID', + `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + PRIMARY KEY (`id`), + UNIQUE KEY `uk_qr_code` (`qr_code`), + KEY `idx_material` (`material_id`), + KEY `idx_batch` (`batch_no`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='小料批次成本表'; + +-- 库存批次表(支持FIFO和小料管理) +CREATE TABLE `inv_inventory_batch` ( + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '批次ID', + `material_id` BIGINT UNSIGNED NOT NULL COMMENT '物料ID', + `batch_no` VARCHAR(50) NOT NULL COMMENT '批次号', + `qr_code` VARCHAR(50) COMMENT '二维码', + `quantity` DECIMAL(18,4) NOT NULL COMMENT '数量', + `remaining_quantity` DECIMAL(18,4) DEFAULT 0 COMMENT '剩余数量', + `unit_cost` DECIMAL(18,4) COMMENT '单位成本', + `warehouse_id` BIGINT UNSIGNED COMMENT '仓库ID', + `location` VARCHAR(50) COMMENT '库位', + `split_flag` TINYINT DEFAULT 0 COMMENT '拆分标记:0-整料,1-小料,2-余料', + `parent_qr_code` VARCHAR(50) COMMENT '父级二维码(小料关联整料)', + `inbound_date` DATE COMMENT '入库日期', + `expire_date` DATE COMMENT '过期日期', + `status` TINYINT DEFAULT 1 COMMENT '状态:1-可用,2-冻结,3-过期', + `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + PRIMARY KEY (`id`), + KEY `idx_material` (`material_id`), + KEY `idx_batch` (`batch_no`), + KEY `idx_qr` (`qr_code`), + KEY `idx_inbound_date` (`inbound_date`), + KEY `idx_expire_date` (`expire_date`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='库存批次表(FIFO+小料管理)'; diff --git a/dcprint.bat b/dcprint.bat deleted file mode 100644 index b48c8914..00000000 --- a/dcprint.bat +++ /dev/null @@ -1,28 +0,0 @@ -@echo off -chcp 65001 >nul -title Start Dev Server - pnpm dev - -cd /d "D:\dcprint\erp-project" - -echo Checking pnpm installation... -where pnpm >nul 2>&1 -if errorlevel 1 ( - echo Error: pnpm not found. Please install with "npm install -g pnpm" - pause - exit /b -) - -if not exist "node_modules\" ( - echo node_modules not found, installing dependencies... - call pnpm install - if errorlevel 1 ( - echo Dependency installation failed. - pause - exit /b - ) -) - -echo Starting dev server... -call pnpm dev - -pause \ No newline at end of file diff --git a/do_split.js b/do_split.js deleted file mode 100644 index e66ac648..00000000 --- a/do_split.js +++ /dev/null @@ -1,196 +0,0 @@ -const fs = require('fs'); -const path = require('path'); - -const content = fs.readFileSync('src/lib/db/schema.ts', 'utf8'); -const allLines = content.split('\n'); - -// ── Table extraction ───────────────────────────────────────────────────────── -function findTableBlocks(lines) { - const tables = []; - let i = 0; - while (i < lines.length) { - const trimmed = lines[i].trim(); - if (trimmed.startsWith('export const ') && trimmed.includes('mysqlTable')) { - const nameMatch = trimmed.match(/export const (\w+)/); - const name = nameMatch[1]; - let start = i; - let parenDepth = 0, braceDepth = 0, bracketDepth = 0; - let inString = false, stringChar = '', foundOpen = false, end = start; - for (let j = start; j < lines.length; j++) { - const line = lines[j]; - let k = 0; - while (k < line.length) { - const ch = line[k]; - if (inString) { - if (ch === '\\') { k += 2; continue; } - if (ch === stringChar) inString = false; - k++; continue; - } - if (ch === "'" || ch === '"' || ch === '`') { inString = true; stringChar = ch; k++; continue; } - if (ch === '(') { parenDepth++; foundOpen = true; } - else if (ch === ')') parenDepth--; - else if (ch === '{') braceDepth++; - else if (ch === '}') braceDepth--; - else if (ch === '[') bracketDepth++; - else if (ch === ']') bracketDepth--; - k++; - } - const lt = line.trim(); - if (foundOpen && parenDepth === 0 && braceDepth === 0 && bracketDepth === 0 && lt.endsWith(');')) { - end = j; i = j + 1; break; - } - } - tables.push({ name, start, end, text: lines.slice(start, end + 1).join('\n') }); - } else { i++; } - } - return tables; -} - -function findTypeAliases(lines) { - const aliases = []; - for (let i = 0; i < lines.length; i++) { - const m = lines[i].match(/^export type (\w+) = typeof (\w+)\.\$inferSelect;/); - if (m) aliases.push({ typeName: m[1], tableName: m[2], line: i }); - } - return aliases; -} - -// ── Categorization ─────────────────────────────────────────────────────────── -function categorize(tables) { - const domains = {}; - ['warehouse','sales','procurement','finance','production','workorder','quote','process','sample','prepress','tooling','hr','crm','common'].forEach(d => domains[d] = []); - - for (const t of tables) { - const n = t.name; - if (/^inv/.test(n)) domains.warehouse.push(t); - else if (/^salQuote/.test(n)) domains.quote.push(t); - else if (/^sal/.test(n) && !/^salSample/.test(n) && n !== 'sampleOrder') domains.sales.push(t); - else if (/^pur/.test(n)) domains.procurement.push(t); - else if (/^fin/.test(n)) domains.finance.push(t); - else if (/^prd/.test(n) && !/^prdDie/.test(n) && !/^prdScreen/.test(n) && !/^prdInk/.test(n)) domains.production.push(t); - else if (/^prod/.test(n)) domains.workorder.push(t); - else if (/^sampleProcessTemplate/.test(n)) domains.process.push(t); - else if (/^dcprintSampleProcess/.test(n) || n === 'sampleOrder' || /^salSample/.test(n)) domains.sample.push(t); - else if (/^dcprintInk/.test(n)) domains.prepress.push(t); - else if (/^dcprintTool/.test(n)) domains.tooling.push(t); - else if (/^prdDie/.test(n) || /^prdScreen/.test(n) || /^prdInk/.test(n)) domains.tooling.push(t); - else if (/^hr/.test(n) || /^org/.test(n)) domains.hr.push(t); - else domains.common.push(t); - } - return domains; -} - -// ── Imports needed per domain ──────────────────────────────────────────────── -const sharedImports = `import { mysqlTable, varchar, datetime, date, timestamp, decimal, int, bigint, tinyint, text, boolean, serial, index, uniqueIndex } from 'drizzle-orm/mysql-core'; -import { sql } from 'drizzle-orm';`; - -function getImports(tables) { - // Determine which drizzle-orm types are used - const textSet = new Set(); - tables.forEach(t => { - const re = /(?:mysqlTable|varchar|datetime|date|timestamp|decimal|int|bigint|tinyint|text|boolean|serial|index|uniqueIndex|sql)\b/g; - let m; - while ((m = re.exec(t.text)) !== null) textSet.add(m[0]); - }); - const needsSql = textSet.has('sql'); - const base = "import { " + [...textSet].filter(x => x !== 'sql').sort().join(', ') + " } from 'drizzle-orm/mysql-core';"; - const sqlImport = needsSql ? "import { sql } from 'drizzle-orm';" : ''; - return [base, sqlImport].filter(Boolean).join('\n'); -} - -// ── Build domain files ─────────────────────────────────────────────────────── -const tables = findTableBlocks(allLines); -const aliases = findTypeAliases(allLines); -const domains = categorize(tables); - -const schemasDir = 'src/lib/db/schemas'; -if (!fs.existsSync(schemasDir)) fs.mkdirSync(schemasDir, { recursive: true }); - -const domainOrder = ['warehouse','sales','procurement','finance','production','workorder','quote','process','sample','prepress','tooling','hr','crm','common']; -const allExportNames = []; // for the re-export file - -for (const domain of domainOrder) { - const tbls = domains[domain]; - if (tbls.length === 0 && domain !== 'crm' && domain !== 'common') continue; - - const filePath = path.join(schemasDir, `${domain}.ts`); - - // Gather imports - const importSet = new Set(); - let needsSql = false; - tbls.forEach(t => { - const re = /(?:mysqlTable|varchar|datetime|date|timestamp|decimal|int|bigint|tinyint|text|boolean|serial|index|uniqueIndex)\b/g; - let m; - while ((m = re.exec(t.text)) !== null) importSet.add(m[0]); - if (t.text.includes('sql`')) needsSql = true; - }); - const imports = [...importSet].sort().join(', '); - const sqlLine = needsSql ? "import { sql } from 'drizzle-orm';\n" : ''; - - // Table definitions - const tableDefs = tbls.map(t => t.text).join('\n\n'); - - // Type aliases for this domain - const tableNames = new Set(tbls.map(t => t.name)); - const typeAliases = aliases - .filter(a => tableNames.has(a.tableName)) - .map(a => `export type ${a.typeName} = typeof ${a.tableName}.$inferSelect;`) - .join('\n'); - - let fileContent = ''; - if (tbls.length > 0) { - fileContent = `import { ${imports} } from 'drizzle-orm/mysql-core';\n${sqlLine}${tableDefs}`; - if (typeAliases) fileContent += `\n\n${typeAliases}`; - } else { - // Empty domain (crm) - fileContent = `// No tables in this domain yet\n`; - } - - fs.writeFileSync(filePath, fileContent); - tbls.forEach(t => allExportNames.push({ domain, name: t.name })); - console.log(`Created ${filePath} (${tbls.length} tables)`); -} - -// ── Build new schema.ts ─────────────────────────────────────────────────────── -const schemaPath = 'src/lib/db/schema.ts'; -let reexports = domainOrder - .filter(d => domains[d].length > 0) - .map(d => `export { ${domains[d].map(t => t.name).join(', ')} } from './schemas/${d}';`) - .join('\n'); - -// Also re-export types -const typeReexports = []; -for (const domain of domainOrder) { - if (domains[domain].length === 0) continue; - const tableNames = new Set(domains[domain].map(t => t.name)); - const domainTypes = aliases.filter(a => tableNames.has(a.tableName)); - if (domainTypes.length > 0) { - typeReexports.push(`export type { ${domainTypes.map(a => a.typeName).join(', ')} } from './schemas/${domain}';`); - } -} - -const newSchemaContent = `/** - * Drizzle ORM Schema 映射 - * - * 权威 schema 来源:database/vnerpdacahng_schema.sql(从目标数据库 SHOW CREATE TABLE 导出) - * 本文件包含被 Drizzle ORM 构建器实际消费的表定义。 - * 新增 ORM 消费表时,从 SQL DDL 对应翻译并在对应 domain 文件中追加。 - * - * 覆盖范围:95 张核心业务表 - * drizzle-kit 迁移路径已废弃(drizzle/ 目录已清理),ORM 查询构建器活跃使用中。 - * - * 表定义按 domain 拆分在 src/lib/db/schemas/ 目录下,本文件负责统一 re-export。 - */ - -${reexports} - -${typeReexports.join('\n')} -`; - -fs.writeFileSync(schemaPath, newSchemaContent); -console.log(`\nCreated new ${schemaPath}`); -console.log('All files generated successfully!'); - -// Verify -const finalCount = Object.values(domains).reduce((s, a) => s + a.length, 0); -console.log(`Total tables: ${finalCount}`); diff --git a/docker-compose.monitoring.yml b/docker-compose.monitoring.yml deleted file mode 100644 index f4a35664..00000000 --- a/docker-compose.monitoring.yml +++ /dev/null @@ -1,182 +0,0 @@ -# ============================================================ -# 监控栈编排 — Prometheus + Grafana + Exporters -# 依赖:docker-compose.prod.yml 中的 vnerp-network 网络 -# 启动:docker compose -f docker-compose.monitoring.yml --env-file .env.production up -d -# ============================================================ - -version: '3.8' - -services: - # ============================================================ - # Prometheus — 指标采集 + 存储 + 告警评估 - # ============================================================ - prometheus: - image: prom/prometheus:latest - container_name: vnerp-prometheus - restart: always - volumes: - - ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml:ro - - ./prometheus/rules:/etc/prometheus/rules:ro - - prometheus_data:/prometheus - command: - - '--config.file=/etc/prometheus/prometheus.yml' - - '--storage.tsdb.path=/prometheus' - - '--storage.tsdb.retention.time=30d' - - '--web.console.libraries=/etc/prometheus/console_libraries' - - '--web.console.templates=/etc/prometheus/consoles' - - '--web.enable-lifecycle' - expose: - - "9090" - deploy: - resources: - limits: - cpus: '1.0' - memory: 1G - reservations: - cpus: '0.25' - memory: 256M - security_opt: - - no-new-privileges:true - networks: - - vnerp-network - - # ============================================================ - # Alertmanager — 告警路由与通知 - # ============================================================ - alertmanager: - image: prom/alertmanager:latest - container_name: vnerp-alertmanager - restart: always - volumes: - - ./alertmanager/alertmanager.yml:/etc/alertmanager/alertmanager.yml:ro - - alertmanager_data:/alertmanager - command: - - '--config.file=/etc/alertmanager/alertmanager.yml' - - '--storage.path=/alertmanager' - expose: - - "9093" - deploy: - resources: - limits: - cpus: '0.5' - memory: 256M - security_opt: - - no-new-privileges:true - networks: - - vnerp-network - - # ============================================================ - # Grafana — 可视化仪表盘 - # ============================================================ - grafana: - image: grafana/grafana:latest - container_name: vnerp-grafana - restart: always - environment: - GF_SECURITY_ADMIN_USER: ${GRAFANA_ADMIN_USER:-vnerp_admin} - GF_SECURITY_ADMIN_PASSWORD: ${GRAFANA_ADMIN_PASSWORD:-change_me_in_production} - GF_USERS_ALLOW_SIGN_UP: "false" - GF_SERVER_ROOT_URL: "https://erp.your-domain.com/grafana" - GF_SERVER_SERVE_FROM_SUB_PATH: "true" - volumes: - - grafana_data:/var/lib/grafana - - ./grafana/provisioning:/etc/grafana/provisioning:ro - expose: - - "3000" - ports: - - "${GRAFANA_PORT:-3001}:3000" - depends_on: - - prometheus - deploy: - resources: - limits: - cpus: '0.5' - memory: 512M - security_opt: - - no-new-privileges:true - networks: - - vnerp-network - - # ============================================================ - # Exporters — 指标采集器 - # ============================================================ - - # 主机指标(CPU/内存/磁盘/网络) - node-exporter: - image: prom/node-exporter:latest - container_name: vnerp-node-exporter - restart: always - pid: host - volumes: - - /proc:/host/proc:ro - - /sys:/host/sys:ro - - /:/rootfs:ro - command: - - '--path.procfs=/host/proc' - - '--path.sysfs=/host/sys' - - '--path.rootfs=/rootfs' - - '--collector.filesystem.mount-points-exclude=^/(sys|proc|dev|host|etc)($$|/)' - expose: - - "9100" - security_opt: - - no-new-privileges:true - networks: - - vnerp-network - - # MySQL 指标 - mysqld-exporter: - image: prom/mysqld-exporter:latest - container_name: vnerp-mysqld-exporter - restart: always - environment: - DATA_SOURCE_NAME: "exporter:${MYSQL_EXPORTER_PASSWORD:-exporterpw}@tcp(mysql:3306)/" - expose: - - "9104" - depends_on: - - prometheus - security_opt: - - no-new-privileges:true - networks: - - vnerp-network - - # Redis 指标 - redis-exporter: - image: oliver006/redis_exporter:latest - container_name: vnerp-redis-exporter - restart: always - environment: - REDIS_ADDR: "redis://redis:6379" - REDIS_PASSWORD: "${REDIS_PASSWORD:-}" - expose: - - "9121" - depends_on: - - prometheus - security_opt: - - no-new-privileges:true - networks: - - vnerp-network - - # HTTP 黑盒探针(检测 App 健康检查) - blackbox-exporter: - image: prom/blackbox-exporter:latest - container_name: vnerp-blackbox-exporter - restart: always - expose: - - "9115" - security_opt: - - no-new-privileges:true - networks: - - vnerp-network - -volumes: - prometheus_data: - driver: local - alertmanager_data: - driver: local - grafana_data: - driver: local - -networks: - vnerp-network: - external: true - name: vnerp-network diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml index c01a7cf0..019ac3eb 100644 --- a/docker-compose.prod.yml +++ b/docker-compose.prod.yml @@ -1,7 +1,6 @@ # 生产环境 Docker Compose 配置 # 使用: docker compose -f docker-compose.prod.yml --env-file .env.production up -d # 需先构建镜像: docker compose -f docker-compose.prod.yml build -# 滚动更新: docker compose -f docker-compose.prod.yml up -d --no-deps app version: '3.8' @@ -11,49 +10,29 @@ services: container_name: vnerp-mysql-prod restart: always environment: - MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD:?MYSQL_ROOT_PASSWORD is required} - MYSQL_DATABASE: ${DB_NAME:?DB_NAME is required} - MYSQL_USER: ${DB_USER:?DB_USER is required} - MYSQL_PASSWORD: ${DB_PASSWORD:?DB_PASSWORD is required} + MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD} + MYSQL_DATABASE: ${DB_NAME} + MYSQL_USER: ${DB_USER} + MYSQL_PASSWORD: ${DB_PASSWORD} MYSQL_CHARSET: utf8mb4 MYSQL_COLLATION: utf8mb4_unicode_ci - # 生产环境不对外暴露 MySQL 端口,仅容器内网可达 - # 如需远程维护,通过 SSH 隧道或临时取消注释 ports - expose: - - "3306" + ports: + - "${DB_PORT:-3306}:3306" volumes: - # bind mount:数据落在项目 ./data/mysql 目录,可见、不会被 docker volume prune 清除 - - ./data/mysql:/var/lib/mysql + - mysql_data:/var/lib/mysql - ./database/vnerpdacahng_schema.sql:/docker-entrypoint-initdb.d/01-schema.sql:ro - ./database/seeds/vnerp-seed-data.sql:/docker-entrypoint-initdb.d/02-seed.sql:ro - - ./backups/mysql:/var/backups command: > --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci - --max-connections=500 - --innodb-buffer-pool-size=1G - --innodb-log-file-size=256M - --innodb-flush-log-at-trx-commit=1 - --slow-query-log=1 - --slow-query-log-file=/var/lib/mysql/slow.log - --long-query-time=2 - --log-error=/var/lib/mysql/error.log + --default-authentication-plugin=mysql_native_password + --max-connections=200 + --innodb-buffer-pool-size=512M healthcheck: - test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u${DB_USER}", "-p${DB_PASSWORD}"] + test: ["CMD", "mysqladmin", "ping", "-h", "localhost"] interval: 10s timeout: 5s retries: 5 - start_period: 30s - deploy: - resources: - limits: - cpus: '2.0' - memory: 2G - reservations: - cpus: '0.5' - memory: 512M - security_opt: - - no-new-privileges:true networks: - vnerp-network @@ -61,38 +40,16 @@ services: image: redis:7-alpine container_name: vnerp-redis-prod restart: always - # 生产环境不对外暴露 Redis 端口,仅容器内网可达 - expose: - - "6379" + ports: + - "6379:6379" volumes: - redis_data:/data - - ./backups/redis:/var/backups - command: > - redis-server - --appendonly yes - --appendfsync everysec - --maxmemory 512mb - --maxmemory-policy allkeys-lru - --save 900 1 - --save 300 10 - --save 60 10000 - --loglevel notice + command: redis-server --appendonly yes --maxmemory 256mb --maxmemory-policy allkeys-lru healthcheck: test: ["CMD", "redis-cli", "ping"] interval: 10s timeout: 5s retries: 5 - start_period: 10s - deploy: - resources: - limits: - cpus: '0.5' - memory: 512M - reservations: - cpus: '0.1' - memory: 64M - security_opt: - - no-new-privileges:true networks: - vnerp-network @@ -100,8 +57,6 @@ services: build: context: . dockerfile: Dockerfile - # CI/CD 推送到 GHCR;本地构建时 image 标签被 build 覆盖 - image: ghcr.io/snqig/vnerp:latest container_name: vnerp-app-prod restart: always depends_on: @@ -113,122 +68,26 @@ services: NODE_ENV: production DB_HOST: mysql DB_PORT: 3306 - DB_USER: ${DB_USER:?DB_USER is required} - DB_PASSWORD: ${DB_PASSWORD:?DB_PASSWORD is required} - DB_NAME: ${DB_NAME:?DB_NAME is required} - JWT_SECRET: ${JWT_SECRET:?JWT_SECRET is required} - JWT_EXPIRES_IN: ${JWT_EXPIRES_IN:-7d} - DEBUG_DB: "false" - CORS_ALLOW_ORIGIN: ${CORS_ALLOW_ORIGIN:?CORS_ALLOW_ORIGIN is required} + DB_USER: ${DB_USER} + DB_PASSWORD: ${DB_PASSWORD} + DB_NAME: ${DB_NAME} + JWT_SECRET: ${JWT_SECRET} + DEBUG_DB: false + CORS_ALLOW_ORIGIN: ${CORS_ALLOW_ORIGIN} REDIS_URL: redis://redis:6379 PORT: 5000 HOSTNAME: 0.0.0.0 - APP_VERSION: ${APP_VERSION:-1.0.0} - LOG_LEVEL: ${LOG_LEVEL:-info} - # 生产环境不直接暴露 app 端口,由 nginx 反代 - # 如需调试可取消注释:ports: ["${APP_PORT:-5000}:5000"] - expose: - - "5000" - volumes: - - app_logs:/app/logs - - app_uploads:/app/uploads - - app_cache:/app/.next/cache - healthcheck: - test: ["CMD", "node", "-e", "require('http').get('http://127.0.0.1:5000/api/health', res => { process.exit(res.statusCode === 200 ? 0 : 1) }).on('error', () => process.exit(1))"] - interval: 30s - timeout: 5s - retries: 3 - start_period: 60s - deploy: - resources: - limits: - cpus: '2.0' - memory: 2G - reservations: - cpus: '0.25' - memory: 256M - security_opt: - - no-new-privileges:true - read_only: true - tmpfs: - - /tmp:noexec,nosuid,size=64m - cap_drop: - - ALL - cap_add: - - NET_BIND_SERVICE - logging: - driver: "json-file" - options: - max-size: "10m" - max-file: "5" - networks: - - vnerp-network - - # ============================================================================ - # Nginx 反向代理 — SSL 终结 + 静态缓存 + 限流 + 安全头 - # 项目约束:生产部署必须在 Node.js 前部署反向代理 (Nginx) - # ============================================================================ - nginx: - image: nginx:1.27-alpine - container_name: vnerp-nginx-prod - restart: always - depends_on: - app: - condition: service_healthy ports: - - "${HTTP_PORT:-80}:80" - - "${HTTPS_PORT:-443}:443" - volumes: - - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro - - ./nginx/ssl:/etc/nginx/ssl:ro - - nginx_cache:/var/cache/nginx - - nginx_logs:/var/log/nginx - healthcheck: - test: ["CMD", "wget", "--spider", "-q", "http://127.0.0.1/"] - interval: 30s - timeout: 5s - retries: 3 - start_period: 10s - deploy: - resources: - limits: - cpus: '1.0' - memory: 256M - reservations: - cpus: '0.25' - memory: 64M - security_opt: - - no-new-privileges:true - cap_drop: - - ALL - cap_add: - - NET_BIND_SERVICE - logging: - driver: "json-file" - options: - max-size: "10m" - max-file: "5" + - "${APP_PORT:-5000}:5000" networks: - vnerp-network volumes: - # mysql_data 已改为 bind mount(./data/mysql),无需 named volume 声明 - redis_data: + mysql_data: driver: local - app_logs: - driver: local - app_uploads: - driver: local - app_cache: - driver: local - nginx_cache: - driver: local - nginx_logs: + redis_data: driver: local networks: vnerp-network: driver: bridge - ipam: - config: - - subnet: 172.28.0.0/16 diff --git a/docker-compose.yml b/docker-compose.yml index f51b57df..c3c3e930 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -6,13 +6,10 @@ services: container_name: vnerp-mysql restart: unless-stopped environment: - MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD:-vnerp-root-2026} + MYSQL_ROOT_PASSWORD: ${DB_PASSWORD:-vnerp2026} MYSQL_DATABASE: ${DB_NAME:-vnerp} - MYSQL_USER: ${DB_USER:-vnerp_app} - MYSQL_PASSWORD: ${DB_PASSWORD:-vnerp2026} MYSQL_CHARSET: utf8mb4 MYSQL_COLLATION: utf8mb4_unicode_ci - # dev only: expose MySQL port for debugging tools (remove in production) ports: - "${DB_PORT:-3306}:3306" volumes: @@ -25,24 +22,21 @@ services: --default-authentication-plugin=mysql_native_password --max-connections=200 --innodb-buffer-pool-size=256M - --slow-query-log=1 - --slow-query-log-file=/var/log/mysql/slow.log - --long-query-time=2 healthcheck: - test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u${DB_USER:-vnerp_app}", "-p${DB_PASSWORD:-vnerp2026}"] + test: ["CMD", "mysqladmin", "ping", "-h", "localhost"] interval: 10s timeout: 5s retries: 5 - start_period: 30s networks: - vnerp-network + # Redis (optional, for caching and token blacklist) redis: image: redis:7-alpine container_name: vnerp-redis restart: unless-stopped - expose: - - "6379" + ports: + - "6379:6379" volumes: - redis_data:/data command: redis-server --appendonly yes --maxmemory 128mb --maxmemory-policy allkeys-lru @@ -51,46 +45,6 @@ services: interval: 10s timeout: 5s retries: 5 - start_period: 10s - networks: - - vnerp-network - - app: - build: - context: . - dockerfile: Dockerfile - target: runner - container_name: vnerp-app - restart: unless-stopped - depends_on: - mysql: - condition: service_healthy - redis: - condition: service_healthy - environment: - NODE_ENV: development - DB_HOST: mysql - DB_PORT: 3306 - DB_USER: ${DB_USER:-vnerp_app} - DB_PASSWORD: ${DB_PASSWORD:-vnerp2026} - DB_NAME: ${DB_NAME:-vnerp} - JWT_SECRET: ${JWT_SECRET:-vnerp-dev-secret-key-change-in-production} - DEBUG_DB: "true" - CORS_ALLOW_ORIGIN: "*" - REDIS_URL: redis://redis:6379 - PORT: 5000 - HOSTNAME: 0.0.0.0 - ports: - - "5000:5000" - volumes: - - app_logs:/app/logs - - app_uploads:/app/uploads - healthcheck: - test: ["CMD", "node", "-e", "require('http').get('http://127.0.0.1:5000/api/health', res => { process.exit(res.statusCode === 200 ? 0 : 1) }).on('error', () => process.exit(1))"] - interval: 30s - timeout: 5s - retries: 3 - start_period: 60s networks: - vnerp-network @@ -99,10 +53,6 @@ volumes: driver: local redis_data: driver: local - app_logs: - driver: local - app_uploads: - driver: local networks: vnerp-network: diff --git "a/docs/01-\351\241\271\347\233\256\346\246\202\350\277\260/\346\212\200\346\234\257\346\240\210.md" "b/docs/01-\351\241\271\347\233\256\346\246\202\350\277\260/\346\212\200\346\234\257\346\240\210.md" index 83c89f11..4d7434e2 100644 --- "a/docs/01-\351\241\271\347\233\256\346\246\202\350\277\260/\346\212\200\346\234\257\346\240\210.md" +++ "b/docs/01-\351\241\271\347\233\256\346\246\202\350\277\260/\346\212\200\346\234\257\346\240\210.md" @@ -1,148 +1,119 @@ # 技术栈 -> 基于 `package.json`(vnerp@0.1.0)实际依赖,所有版本均经核实。`dependencies` 为运行时依赖,`devDependencies` 为开发期依赖。 +> 基于 `package.json`(vnerp@0.1.0)实际依赖。 ## 框架与运行时 -| 依赖 | 版本 | 类型 | 用途 | -|------|------|------|------| -| next | 16.1.1 | dependencies | 全栈框架(App Router),入口中间件为 `src/proxy.ts` | -| react | 19.2.3 | dependencies | UI 视图层 | -| react-dom | 19.2.3 | dependencies | React DOM 渲染 | -| typescript | ^5 | devDependencies | 类型系统 | -| @types/node | ^20 | devDependencies | Node 类型 | -| @types/react | ^19 | devDependencies | React 类型 | -| @types/react-dom | ^19 | devDependencies | React DOM 类型 | -| tsx | ^4.23.0 | devDependencies | 执行 TypeScript 脚本(`scripts/migrate.ts` 等) | +| 依赖 | 版本 | 用途 | +|------|------|------| +| next | 16.1.1 | 全栈框架(App Router) | +| react | 19.2.3 | UI 视图层 | +| react-dom | 19.2.3 | React DOM 渲染 | +| typescript | ^5 | 类型系统(devDep) | +| @types/node | ^20 | Node 类型(devDep) | +| @types/react | ^19 | React 类型(devDep) | +| @types/react-dom | ^19 | React DOM 类型(devDep) | ## 数据库与 ORM -| 依赖 | 版本 | 类型 | 用途 | -|------|------|------|------| -| drizzle-orm | ^0.45.1 | dependencies | TypeScript ORM,查询构建器(`drizzleDb` 实例) | -| drizzle-kit | ^0.31.10 | devDependencies | Drizzle Studio(迁移路径已废弃,仅保留 Studio) | -| drizzle-zod | ^0.8.3 | dependencies | Schema → zod 校验 | -| mysql2 | ^3.20.0 | dependencies | MySQL 驱动与连接池(`src/lib/db/index.ts` 单例池) | - -> 数据库为 MySQL 8.0+,权威 schema 来源 `database/vnerpdacahng_schema.sql`。Drizzle 消费 42 张表(`src/lib/db/schema.ts`),其余遗留表由原始 SQL 管理。迁移脚本为 `scripts/migrate.ts`(编号 001–060)与全量初始化 `scripts/setup-db.mjs`。 - -## 缓存与会话 - -| 依赖 | 版本 | 类型 | 用途 | -|------|------|------|------| -| ioredis | ^5.11.1 | dependencies | Redis 客户端,用于事件总线 Streams、缓存、鉴权缓存 | +| 依赖 | 版本 | 用途 | +|------|------|------| +| drizzle-orm | ^0.45.1 | TypeScript ORM | +| drizzle-kit | ^0.31.8 | 迁移与 Studio(devDep) | +| drizzle-zod | ^0.8.3 | Schema → zod 校验 | +| mysql2 | ^3.20.0 | MySQL 驱动与连接池 | +| ioredis | ^5.11.1 | Redis 客户端(缓存/会话) | ## 国际化与表单 -| 依赖 | 版本 | 类型 | 用途 | -|------|------|------|------| -| next-intl | ^4.13.0 | dependencies | i18n(zh-CN 默认 / zh-TW / en / vi,`localePrefix: 'as-needed'`) | -| opencc-js | 1.0.5 | devDependencies | 繁简转换(zh-CN ↔ zh-TW) | -| zod | ^4.3.5 | dependencies | Schema 校验 | -| react-hook-form | ^7.70.0 | dependencies | 表单管理 | -| @hookform/resolvers | ^5.2.2 | dependencies | zod resolver 桥接 | +| 依赖 | 版本 | 用途 | +|------|------|------| +| next-intl | ^4.13.0 | i18n(zh-CN/zh-TW/en/vi) | +| zod | ^4.3.5 | Schema 校验 | +| react-hook-form | ^7.70.0 | 表单管理 | +| @hookform/resolvers | ^5.2.2 | zod resolver | ## UI 与样式 -| 依赖 | 版本 | 类型 | 用途 | -|------|------|------|------| -| tailwindcss | ^4 | devDependencies | 原子化 CSS | -| @tailwindcss/postcss | ^4 | devDependencies | PostCSS 插件 | -| @tailwindcss/typography | ^0.5.19 | dependencies | 排版插件(prose) | -| tailwind-merge | ^2.6.0 | dependencies | 类合并 | -| tailwindcss-animate | ^1.0.7 | dependencies | 动画工具 | -| tw-animate-css | ^1.4.0 | dependencies | 动画工具 | -| class-variance-authority | ^0.7.1 | dependencies | 组件变体 | -| clsx | ^2.1.1 | dependencies | 类名拼接 | -| lucide-react | ^0.468.0 | dependencies | 图标库 | -| @heroicons/react | ^2.2.0 | dependencies | 图标库 | -| cmdk | ^1.1.1 | dependencies | 命令面板 | -| next-themes | ^0.4.6 | dependencies | 主题切换 | -| shadcn | 4.7.0 | devDependencies | shadcn/ui CLI(组件基座) | -| postcss | ^8.5.8 | devDependencies | CSS 处理 | +| 依赖 | 版本 | 用途 | +|------|------|------| +| tailwindcss | ^4 | 原子化 CSS(devDep) | +| @tailwindcss/postcss | ^4 | PostCSS 插件(devDep) | +| @tailwindcss/typography | ^0.5.19 | 排版插件 | +| tailwind-merge | ^2.6.0 | 类合并 | +| tailwindcss-animate | ^1.0.7 | 动画工具 | +| tw-animate-css | ^1.4.0 | 动画工具 | +| class-variance-authority | ^0.7.1 | 组件变体 | +| clsx | ^2.1.1 | 类名拼接 | +| lucide-react | ^0.468.0 | 图标库 | +| @heroicons/react | ^2.2.0 | 图标库 | +| cmdk | ^1.1.1 | 命令面板 | +| next-themes | ^0.4.6 | 主题切换 | +| shadcn | latest | shadcn/ui CLI(devDep) | ### Radix UI 组件(shadcn/ui 基座) -`@radix-ui/react-*`(版本均 ^1.x):accordion、alert-dialog、aspect-ratio、avatar、checkbox、collapsible、context-menu、dialog、dropdown-menu、hover-card、label、menubar、navigation-menu、popover、progress、radio-group、scroll-area、select、separator、slider、slot、switch、tabs、toggle、toggle-group、tooltip。 +`@radix-ui/react-*`:accordion、alert-dialog、aspect-ratio、avatar、checkbox、collapsible、context-menu、dialog、dropdown-menu、hover-card、label、menubar、navigation-menu、popover、progress、radio-group、scroll-area、select、separator、slider、slot、switch、tabs、toggle、toggle-group、tooltip(版本均 ^1.x)。 ## 图表与可视化 -| 依赖 | 版本 | 类型 | 用途 | -|------|------|------|------| -| recharts | 2.15.4 | dependencies | 图表库(看板/报表) | -| @xyflow/react | ^12.11.0 | dependencies | 流程图(Dashboard Flow) | -| three | ^0.183.2 | dependencies | 3D 库 | -| @react-three/fiber | ^9.6.0 | dependencies | React Three.js 渲染器 | -| @react-three/drei | ^10.7.7 | dependencies | Three.js 辅助库 | -| framer-motion | ^12.34.4 | dependencies | 动画 | -| lottie-react | ^2.4.1 | dependencies | Lottie 动画 | -| embla-carousel-react | ^8.6.0 | dependencies | 轮播 | - -## 导出与文档 - -| 依赖 | 版本 | 类型 | 用途 | -|------|------|------|------| -| @e965/xlsx | ^0.20.3 | dependencies | Excel 导入导出(`src/lib/excel-service.ts`) | -| jspdf | ^4.2.1 | dependencies | PDF 生成 | -| jspdf-autotable | ^5.0.8 | dependencies | PDF 表格 | -| docx | ^9.7.1 | dependencies | Word 文档生成 | -| file-saver | ^2.0.5 | dependencies | 文件下载 | -| react-to-print | ^3.3.0 | dependencies | 打印 | -| qrcode | ^1.5.4 | dependencies | 二维码生成(Node 端) | -| qrcode.react | ^4.2.0 | dependencies | 二维码生成(React 组件) | -| swagger-ui-react | ^5.32.8 | dependencies | OpenAPI 文档渲染(`/api-docs`) | - -## 认证与安全 - -| 依赖 | 版本 | 类型 | 用途 | -|------|------|------|------| -| jose | ^6.2.2 | dependencies | JWT 签发/校验(`src/lib/auth.ts`) | -| bcryptjs | ^3.0.3 | dependencies | 密码哈希 | -| he | ^1.2.0 | dependencies | HTML 实体编码/解码(XSS 防护辅助) | +| 依赖 | 版本 | 用途 | +|------|------|------| +| recharts | 2.15.4 | 图表库 | +| @xyflow/react | ^12.11.0 | 流程图(Dashboard Flow) | +| @react-three/fiber | ^9.6.0 | React Three.js 渲染器 | +| @react-three/drei | ^10.7.7 | Three.js 辅助库 | +| three | ^0.183.2 | 3D 库 | +| framer-motion | ^12.34.4 | 动画 | +| lottie-react | ^2.4.1 | Lottie 动画 | +| embla-carousel-react | ^8.6.0 | 轮播 | ## 工具与业务库 -| 依赖 | 版本 | 类型 | 用途 | -|------|------|------|------| -| date-fns | ^4.1.0 | dependencies | 日期处理 | -| decimal.js | ^10.6.0 | dependencies | 精度计算(金额,`src/lib/decimal-utils.ts`) | -| @dnd-kit/core | ^6.3.1 | dependencies | 拖拽核心 | -| @dnd-kit/sortable | ^10.0.0 | dependencies | 排序拖拽 | -| @dnd-kit/utilities | ^3.2.2 | dependencies | 拖拽工具 | -| react-resizable-panels | ^4.2.0 | dependencies | 可缩放面板 | -| input-otp | ^1.4.2 | dependencies | OTP 输入 | -| react-day-picker | ^9.13.0 | dependencies | 日期选择 | -| sonner | ^2.0.7 | dependencies | Toast 通知 | -| vaul | ^1.1.2 | dependencies | Drawer | - -## 测试与质量 - -| 依赖 | 版本 | 类型 | 用途 | -|------|------|------|------| -| vitest | ^4.1.5 | devDependencies | 单元测试 | -| @vitest/coverage-v8 | ^4.1.5 | devDependencies | 覆盖率 | -| @vitejs/plugin-react | ^6.0.1 | devDependencies | Vitest React 插件 | -| jsdom | ^29.1.0 | devDependencies | DOM 环境 | -| @testing-library/react | ^16.3.2 | devDependencies | React 测试工具 | -| @testing-library/jest-dom | ^6.9.1 | devDependencies | jest-dom 断言 | -| @playwright/test | ^1.58.2 | devDependencies | E2E 测试 | -| @types/three | ^0.183.1 | devDependencies | Three.js 类型 | -| @types/qrcode | ^1.5.6 | devDependencies | qrcode 类型 | -| @types/file-saver | ^2.0.7 | devDependencies | file-saver 类型 | -| @types/he | ^1.2.3 | devDependencies | he 类型 | - -## 工程化 - -| 依赖 | 版本 | 类型 | 用途 | -|------|------|------|------| -| pnpm(packageManager) | 9.0.0 | - | 包管理器(`preinstall` 强制) | -| only-allow | ^1.2.2 | devDependencies | 强制 pnpm | -| eslint | ^9 | devDependencies | Lint | -| eslint-config-next | 16.1.1 | devDependencies | Next ESLint 配置 | -| prettier | ^3.5.3 | devDependencies | 格式化 | -| husky | ^9.1.7 | devDependencies | Git 钩子(`prepare` 脚本) | -| lint-staged | ^15.5.2 | devDependencies | 暂存区 lint(`src/**/*.{ts,tsx,js,jsx}` → eslint --fix + prettier --write) | -| @commitlint/cli | ^21.1.0 | devDependencies | commit message 校验 | -| @commitlint/config-conventional | ^21.1.0 | devDependencies | conventional 规范 | - -> 最后更新:2026-07-10 +| 依赖 | 版本 | 用途 | +|------|------|------| +| date-fns | ^4.1.0 | 日期处理 | +| decimal.js | ^10.6.0 | 精度计算(金额) | +| xlsx | ^0.18.5 | Excel 导入导出 | +| qrcode | ^1.5.4 | 二维码生成(Node) | +| qrcode.react | ^4.2.0 | 二维码生成(React) | +| react-to-print | ^3.3.0 | 打印 | +| bcryptjs | ^3.0.3 | 密码哈希 | +| jose | ^6.2.2 | JWT 签发/校验 | +| @dnd-kit/core | ^6.3.1 | 拖拽核心 | +| @dnd-kit/sortable | ^10.0.0 | 排序拖拽 | +| @dnd-kit/utilities | ^3.2.2 | 拖拽工具 | +| react-resizable-panels | ^4.2.0 | 可缩放面板 | +| input-otp | ^1.4.2 | OTP 输入 | +| react-day-picker | ^9.13.0 | 日期选择 | +| sonner | ^2.0.7 | Toast 通知 | +| vaul | ^1.1.2 | Drawer | + +## 测试与质量(devDependencies) + +| 依赖 | 版本 | 用途 | +|------|------|------| +| vitest | ^4.1.5 | 单元测试 | +| @vitest/coverage-v8 | ^4.1.5 | 覆盖率 | +| @vitejs/plugin-react | ^6.0.1 | Vitest React 插件 | +| jsdom | ^29.1.0 | DOM 环境 | +| @testing-library/react | ^16.3.2 | React 测试工具 | +| @testing-library/jest-dom | ^6.9.1 | jest-dom 断言 | +| @playwright/test | ^1.58.2 | E2E 测试 | + +## 工程化(devDependencies) + +| 依赖 | 版本 | 用途 | +|------|------|------| +| pnpm(packageManager) | 9.0.0 | 包管理器(强制) | +| only-allow | ^1.2.2 | 强制 pnpm | +| eslint | ^9 | Lint | +| eslint-config-next | 16.1.1 | Next ESLint 配置 | +| prettier | ^3.5.3 | 格式化 | +| husky | ^9.1.7 | Git 钩子 | +| lint-staged | ^15.5.2 | 暂存区 lint | +| @commitlint/cli | ^21.1.0 | commit message 校验 | +| @commitlint/config-conventional | ^21.1.0 | conventional 规范 | +| postcss | ^8.5.8 | CSS 处理 | + +> 最后更新:2026-06-30 diff --git "a/docs/01-\351\241\271\347\233\256\346\246\202\350\277\260/\346\225\264\344\275\223\346\236\266\346\236\204.md" "b/docs/01-\351\241\271\347\233\256\346\246\202\350\277\260/\346\225\264\344\275\223\346\236\266\346\236\204.md" index c976767c..5fbed307 100644 --- "a/docs/01-\351\241\271\347\233\256\346\246\202\350\277\260/\346\225\264\344\275\223\346\236\266\346\236\204.md" +++ "b/docs/01-\351\241\271\347\233\256\346\246\202\350\277\260/\346\225\264\344\275\223\346\236\266\346\236\204.md" @@ -1,267 +1,104 @@ # 整体架构 -> 基于 `src/` 实际目录结构与代码分层。所有路径均经核实。 +> 基于 `src/` 实际目录结构与代码分层。 ## DDD 分层架构 -印刷生产经营信息管理系统 Print MIS 采用 DDD(领域驱动设计)分层架构,将领域模型、应用编排、基础设施与用户界面解耦。依赖方向严格自上而下,领域层不依赖任何框架。 +VNERP 采用 DDD(领域驱动设计)分层架构,将业务逻辑、应用编排、基础设施与用户界面解耦。 ``` -┌─────────────────────────────────────────────────────────────────┐ -│ 表现层 (Presentation) │ -│ src/app/[locale]/* 140+ 业务页面(多语言路由) │ -│ src/app/api/* 356 个方法导出(REST Route Handlers) │ -│ src/components/* UI 与布局组件(ui/、layout/、qr-code/ 等) │ -│ src/hooks/* 自定义 Hooks(useAuth、usePermission 等) │ -│ src/proxy.ts 入口中间件(i18n + 鉴权 + 调试路由封堵) │ -└──────────────────────────────┬──────────────────────────────────┘ - │ 调用应用服务 / 渲染领域数据 -┌──────────────────────────────▼──────────────────────────────────┐ -│ 应用层 (Application) │ -│ src/application/services/ 23 个应用服务(编排领域对象) │ -│ src/application/handlers/ 36 个领域事件处理器 │ -│ src/application/workflow/ WorkflowEngine.ts(工作流引擎) │ -│ src/application/EventRegistry.ts 事件订阅注册 │ -│ src/application/AppInitializer.ts 应用初始化 │ -└──────────────────────────────┬──────────────────────────────────┘ - │ 调用聚合根 / 仓储接口 -┌──────────────────────────────▼──────────────────────────────────┐ -│ 领域层 (Domain) │ -│ src/domain/* 9 个业务模块 + shared/shipment/sample-card 辅助 │ -│ aggregates/ 聚合根(25 个,如 WorkOrder、SalesOrder) │ -│ entities/ 实体 │ -│ value-objects/ 值对象(WorkOrderStatus、Money 等) │ -│ events/ 领域事件 │ -│ repositories/ 仓储接口(IWorkOrderRepository 等) │ -│ 不依赖任何框架,纯 TypeScript │ -└──────────────────────────────┬──────────────────────────────────┘ - │ 实现仓储接口 -┌──────────────────────────────▼──────────────────────────────────┐ -│ 基础设施层 (Infrastructure) │ -│ src/infrastructure/repositories/ 31 个 Mysql*/Drizzle* 仓储实现│ -│ src/infrastructure/event-bus/ 事件总线(Streams + Outbox) │ -│ src/infrastructure/schedulers/ BatchExpiryScheduler │ -│ src/infrastructure/RepositoryRegistry.ts 仓储注册表 │ -│ src/lib/db/* mysql2 连接池 + Drizzle ORM │ -│ src/lib/* 工具库(FIFO、成本、状态机、鉴权等) │ -│ src/contexts/AuthContext.tsx 客户端认证上下文 │ -│ src/i18n/* next-intl 配置 │ -└─────────────────────────────────────────────────────────────────┘ +┌─────────────────────────────────────────────────────┐ +│ 表现层 (Presentation) │ +│ src/app/[locale]/* 页面 + src/app/api/* 路由 │ +│ src/components/* 共享组件 │ +└──────────────────────┬──────────────────────────────┘ + │ 调用 +┌──────────────────────▼──────────────────────────────┐ +│ 应用层 (Application) │ +│ src/application/{handlers,services,workflow} │ +│ 编排领域对象、订阅领域事件、事务边界 │ +└──────────────────────┬──────────────────────────────┘ + │ 调用 +┌──────────────────────▼──────────────────────────────┐ +│ 领域层 (Domain) │ +│ src/domain/* 聚合根 / 实体 / 值对象 / 领域事件 / 仓储接口│ +│ 不依赖任何框架,纯 TypeScript │ +└──────────────────────┬──────────────────────────────┘ + │ 实现接口 +┌──────────────────────▼──────────────────────────────┐ +│ 基础设施层 (Infrastructure) │ +│ src/lib/db/* Drizzle + mysql2 | src/lib/* 工具库 │ +│ src/contexts/AuthContext | src/i18n/* | src/services/*│ +└─────────────────────────────────────────────────────┘ ``` ## 目录组织 | 目录 | 层 | 职责 | |------|----|------| -| `src/domain/` | 领域层 | 9 个业务模块的聚合根、实体、值对象、领域事件、仓储接口;`shared/` 放 `Money`、`DomainTypes` 等共享值对象 | -| `src/application/` | 应用层 | 应用服务、36 个领域事件处理器、工作流引擎、事件注册 | -| `src/infrastructure/` | 基础设施层 | 仓储实现、事件总线、定时调度器、仓储注册表 | -| `src/lib/` | 基础设施层 | DB 连接池、FIFO、成本引擎、状态机、鉴权、审计、限流、缓存等工具 | +| `src/domain/` | 领域层 | 聚合根、实体、值对象、领域事件、仓储接口 | +| `src/application/` | 应用层 | 应用服务、领域事件处理器、工作流引擎 | +| `src/lib/` | 基础设施层 | DB 连接、工具、状态机、FIFO、缓存、鉴权 | +| `src/services/` | 基础设施层 | 横切服务(如 `CostAmortizationService`) | | `src/contexts/` | 基础设施层 | React Context(`AuthContext`) | -| `src/i18n/` | 基础设施层 | next-intl 配置(`locales.ts` / `request.ts` / `navigation.ts`) | -| `src/app/api/` | 表现层 | Route Handlers(REST API,356 方法导出) | -| `src/app/[locale]/` | 表现层 | 业务页面(140+ page.tsx,多语言路由) | +| `src/i18n/` | 基础设施层 | next-intl 配置(locales / request / navigation) | +| `src/app/api/` | 表现层 | Route Handlers(REST API) | +| `src/app/[locale]/` | 表现层 | 业务页面(多语言路由) | | `src/components/` | 表现层 | UI 与布局组件(`ui/`、`layout/`、`qr-code/` 等) | | `src/hooks/` | 表现层 | 自定义 Hooks(`useAuth`、`usePermission` 等) | -| `src/proxy.ts` | 表现层 | i18n 中间件 + API/页面鉴权 + 调试路由封堵(替代已删的 `src/middleware.ts`) | +| `src/middleware.ts` | 表现层 | i18n 中间件 + 调试路由屏蔽 | ## 领域层结构 -`src/domain/` 下按业务模块划分,9 个核心业务模块如下: +`src/domain/` 下按业务模块划分,每个模块统一包含: -| 模块 | 聚合根 | -|------|--------| -| `dcprint` | Tool、SampleProcessCard、SampleProcessTemplate、InkFormulaVersion | -| `production` | WorkOrder、FinishOrder、PickOrder、WorkReport | -| `warehouse` | InboundOrder、OutboundOrder、TransferOrder、StocktakingOrder | -| `sales` | SalesOrder、ReturnOrder、Reconciliation、Delivery | -| `purchase` | PurchaseOrder、PurchaseReturn、PurchaseReconciliation | -| `finance` | Receivable、Payable、Voucher | -| `sample` | SampleOrder | -| `quality` | UnqualifiedProduct | -| `standard-card` | StandardCard | - -辅助目录:`shared/`(`Money.ts`、`DomainTypes.ts` 含 `DomainError`/`DomainEvent`)、`shipment/`(`ShipmentStateMachine.ts`)、`sample-card/`(`SampleCardEvents.ts`)。 +| 子目录 | 内容 | +|--------|------| +| `aggregates/` | 聚合根(如 `WorkOrder`、`SalesOrder`、`PurchaseOrder`、`InboundOrder`、`StandardCard`) | +| `entities/` | 实体(如 `MaterialRequirement`、`SalesOrderLine`) | +| `value-objects/` | 值对象(如 `WorkOrderStatus`、`Money`) | +| `events/` | 领域事件(如 `WorkOrderCreatedEvent`) | +| `repositories/` | 仓储接口(如 `IWorkOrderRepository`) | -每个业务模块统一包含 `aggregates/`(聚合根)、`entities/`(实体)、`value-objects/`(值对象,如 `WorkOrderStatus`、`Money`)、`events/`(领域事件)、`repositories/`(仓储接口,如 `IWorkOrderRepository`)子目录。 +已存在的领域模块:`production`、`sales`、`purchase`、`warehouse`、`standard-card`、`shared`(`Money`、`DomainTypes` 含 `DomainError`/`DomainEvent`)。 ## 应用层结构 -| 子目录 / 文件 | 内容 | +| 子目录 | 内容 | |--------|------| -| `services/` | 23 个应用服务(`SalesApplicationService`、`ProductionApplicationService`、`PurchaseApplicationService`、`FinanceApplicationService`、`StandardCardApplicationService`、`ToolManagementService`、`DashboardDataService`、`MaterialLifecycleService` 等) | -| `handlers/` | 36 个领域事件处理器(见 [事件总线架构](#事件总线架构)) | -| `workflow/WorkflowEngine.ts` | 工作流引擎 | -| `EventRegistry.ts` | 事件订阅注册 | +| `services/` | 应用服务(`SalesApplicationService`、`ProductionApplicationService` 等) | +| `handlers/` | 领域事件处理器(`SalesShippedHandler`、`PurchasePayableHandler`、`InventorySyncHandler` 等) | +| `workflow/` | 工作流引擎 `WorkflowEngine.ts` | | `AppInitializer.ts` | 应用初始化 | +| `EventRegistry.ts` | 事件订阅注册 | -## 请求流转 - -一次典型的 API 请求从入口中间件到数据库的完整链路: - -``` -客户端请求 - │ - ▼ -src/proxy.ts(Edge runtime) - ├─ 生产环境封堵调试路由(/qrcode、/api/init、/api/debug、/api/diagnose → 404) - ├─ /api/* 路由:公开 API 放行;其余校验 access_token cookie(无→401) - │ + requiresCsrfValidation 校验 CSRF token(失败→403) - ├─ 页面路由:26 个受保护前缀无 access_token → 重定向 /login?next=原路径 - └─ i18n:createIntlMiddleware(localePrefix: 'as-needed') - │ - ▼ -src/app/api//route.ts(Route Handler) - ├─ withPermission(async (req, userInfo) => {...}) 装饰器(src/lib/api-auth.ts) - │ ├─ verifyToken(JWT) 解析 access_token、校验有效性(src/lib/auth.ts) - │ └─ 权限校验(src/lib/api-permissions.ts) - └─ 统一响应 src/lib/api-response.ts - │ - ▼ -src/application/services/*ApplicationService.ts(应用服务) - ├─ 编排领域对象、控制事务边界(transactionWithRetry 乐观锁重试) - ├─ 调用聚合根业务方法(状态机流转等) - └─ 发布领域事件 → 事件总线 - │ - ▼ -src/domain//aggregates/*(聚合根) - └─ 封装业务规则、状态流转(如 WorkOrderStatusVO: release→start→complete→close) - │ - ▼ -src/domain//repositories/I*Repository.ts(仓储接口) - │ - ▼ -src/infrastructure/repositories/Mysql*Repository.ts(仓储实现) - ├─ 基于 src/lib/db 的 mysql2 连接池(参数化查询防注入) - └─ 或 Drizzle 查询构建器(drizzleDb) - │ - ▼ -MySQL 8.0+(vnerpdacahng 库,42 张 Drizzle 表 + 遗留表) -``` - -要点: - -- **proxy.ts 只校验 cookie 存在性**(Edge runtime 不解析 JWT),JWT 实际有效性由 API 路由的 `verifyToken` 与 SSR `layout.tsx` 校验。 -- 应用服务通过 `transactionWithRetry`(`src/lib/db/index.ts`)实现乐观锁冲突重试(指数退避,最多 3 次)。 -- 仓储实现分两类:`Mysql*Repository`(基于参数化 SQL)与 `Drizzle*Repository`(基于 Drizzle 查询构建器,如 `DrizzleSalesOrderRepository`、`DrizzlePurchaseOrderRepository`、`DrizzleInboundOrderRepository`)。 - -## 事件总线架构 - -系统采用 **Redis Streams + Outbox 两阶段投递** 的事件总线,保证领域事件的可靠投递与幂等消费。 - -``` -应用服务发布领域事件 - │ - ▼ -┌───────────────────────────────────────────────────────┐ -│ 第一阶段:Outbox 持久化(sys_event_processed 表) │ -│ DomainEventOutbox.checkAndMark() → status='processing'│ -│ DomainEventOutbox.markAsProcessed() → status='processed'│ -│ 失败 deleteMark() │ -└───────────────────────┬───────────────────────────────┘ - │ - ▼ -┌───────────────────────────────────────────────────────┐ -│ 第二阶段:Redis Streams 投递 │ -│ StreamPublisher → XADD erp:domain-events │ -│ STREAM_MAX_LENGTH=10000(流长度上限) │ -│ STREAM_RECLAIM_IDLE_MS=60s(死消费者回收阈值) │ -└───────────────────────┬───────────────────────────────┘ - │ - ▼ -┌───────────────────────────────────────────────────────┐ -│ 消费端 │ -│ StreamConsumer → XAUTOCLAIM 回收死消费者 │ -│ → IdempotencyGuard / IdempotentHandler 幂等保护 │ -│ → 36 个事件处理器(handlers/*) │ -└───────────────────────────────────────────────────────┘ -``` - -关键组件(`src/infrastructure/event-bus/`): - -| 文件 | 职责 | -|------|------| -| `EventBus.ts` | 事件总线统一入口 | -| `StreamPublisher.ts` | 发布事件到 Redis Streams | -| `StreamConsumer.ts` | 消费 Streams,XAUTOCLAIM 回收死消费者 | -| `DomainEventOutbox.ts` | Outbox 两阶段持久化(`sys_event_processed` 表) | -| `MemoryDomainEventOutbox.ts` | 内存模式 Outbox(无 Redis 降级用) | -| `OutboxPoller.ts` | 定时回收过期 `processing`(`IDEMPOTENCY_STALE_THRESHOLD_MINUTES=5min`)+ 清理 30 天前已处理记录 | -| `IdempotencyGuard.ts` / `IdempotentHandler.ts` | 幂等保护 | -| `DomainEventOutboxFactory.ts` | 根据 `EVENT_BUS_TYPE` 创建 Outbox 实例 | - -降级策略: - -- `EVENT_BUS_TYPE=db`(默认):启用 Redis Streams + Outbox 两阶段,`OutboxPoller` 定时回收。 -- `EVENT_BUS_TYPE=memory`:无 Redis 时降级为内存直发(direct publish),**不启动 `OutboxPoller`**。 - -36 个事件处理器(`src/application/handlers/`)覆盖销售、采购、仓储、生产、财务、印前、标准卡、二维码、打样、审计、缓存失效等联动场景,如 `SalesShippedHandler`(销售发货→库存/应收联动)、`PurchasePayableHandler`(采购入库→应付)、`InventorySyncHandler`(库存同步)等。 - -## i18n 架构 - -| 组件 | 位置 | 职责 | -|------|------|------| -| locale 定义 | `src/i18n/locales.ts` | `locales=['zh-CN','zh-TW','en','vi']`,`defaultLocale='zh-CN'`,`localeNames` 映射显示名 | -| 中间件 | `src/proxy.ts` | `createIntlMiddleware`,`localePrefix: 'as-needed'`(默认 `zh-CN` 不带前缀,其余带 `/zh-TW` 等前缀) | -| 服务端消息 | `src/i18n/request.ts` | `getRequestConfig` 加载对应语言消息包 | -| 导航 | `src/i18n/navigation.ts` | `Link`、`useRouter`、`usePathname`、`redirect` 等国际化导航 API | -| Provider | `src/components/IntlProvider.tsx` | 在 `layout.tsx` 中包裹,注入 `locale` 与 `messages` | -| 繁简转换 | `opencc-js`(1.0.5) | zh-CN ↔ zh-TW 转换辅助 | - -页面统一挂载于 `[locale]` 下,`generateStaticParams` 预生成四种语言的静态参数,非法 locale 回退 `zh-CN` 并 `notFound()`。 - -## 认证架构 - -认证采用 **access_token cookie + JWT + CSRF** 三层防护: +## 依赖流向 ``` -登录成功 - ├─ 签发 JWT(jose,src/lib/auth.ts)→ 写入 access_token cookie - ├─ 密码哈希(bcryptjs) - └─ 种入 CSRF cookie(src/lib/csrf.ts) - │ - ▼ -后续请求 - ├─ src/proxy.ts(Edge) - │ ├─ 页面:校验 access_token cookie 存在性(不解析 JWT) - │ │ 受保护前缀无 cookie → 重定向 /login?next=原路径 - │ └─ API:校验 access_token cookie 存在性(无→401) - │ + requiresCsrfValidation → validateCsrfToken(失败→403) - │ - ├─ SSR(src/app/[locale]/layout.tsx) - │ └─ prefetchMenus() → getMenusByToken(token) 轻量校验 JWT + 查菜单 - │ 失败静默降级到客户端 fetch - │ - └─ API Route(src/lib/api-auth.ts withPermission 装饰器) - ├─ verifyToken(JWT) 完整校验签名/过期 - ├─ token-blacklist.ts 黑名单校验(登出/改密后失效) - ├─ auth-cache.ts 鉴权结果缓存 - └─ api-permissions.ts 权限点校验 +表现层 ──调用──▶ 应用层 ──调用──▶ 领域层 + ▲ + │ 实现接口 + 基础设施层 ``` -关键文件: - -| 文件 | 作用 | -|------|------| -| `src/lib/auth.ts` | JWT 签发/校验(jose)、密码哈希(bcryptjs) | -| `src/lib/api-auth.ts` | `withPermission` 装饰器,JWT 校验 + 权限校验 | -| `src/lib/api-permissions.ts` | 权限点定义与校验 | -| `src/lib/csrf.ts` | CSRF cookie 种入、`requiresCsrfValidation`、`validateCsrfToken` | -| `src/lib/token-blacklist.ts` | token 黑名单(登出/改密失效) | -| `src/lib/auth-cache.ts` | 鉴权结果缓存 | -| `src/contexts/AuthContext.tsx` | 客户端认证上下文,承载 menus/permissions | -| `src/lib/audit-logger.ts` + `audit-middleware.ts` | 审计日志 | -| `src/lib/rate-limit.ts` | 限流 | +- **领域层**不依赖任何框架,是纯 TypeScript 代码,封装业务规则、状态机(如 `WorkOrderStatusVO` 限定 `release/start/complete/close` 等流转) +- **应用层**编排领域对象,发布/订阅领域事件(如销售发货 → 触发 `SalesShippedHandler` → 库存/财务联动) +- **基础设施层**实现领域层定义的仓储接口(基于 `src/lib/db` 的 mysql2 连接池 + Drizzle ORM) +- **表现层**通过 Route Handler 暴露 REST API,并通过 `src/components` 提供多语言 UI ## 跨层基础设施 | 文件 | 作用 | |------|------| -| `src/proxy.ts` | i18n 路由前缀(`as-needed`)、API/页面鉴权、生产环境屏蔽调试路由(替代 `src/middleware.ts`) | -| `src/lib/db/index.ts` | MySQL 连接池(mysql2 + Drizzle),全局单例,参数化查询防注入,乐观锁重试 | -| `src/lib/db/schema.ts` | Drizzle Schema 定义(42 张消费表) | +| `src/middleware.ts` | i18n 路由前缀(`as-needed`)、生产环境屏蔽 `/debug` 等调试路由 | +| `src/lib/db/index.ts` | MySQL 连接池(mysql2 + Drizzle),全局单例 | +| `src/lib/db/schema.ts` | Drizzle Schema 定义 | | `src/lib/api-response.ts` | 统一 API 响应封装 | -| `src/infrastructure/RepositoryRegistry.ts` | 仓储注册表,统一获取仓储实例 | -| `src/application/EventRegistry.ts` | 事件订阅注册,绑定事件与处理器 | +| `src/lib/auth.ts` + `auth-cache.ts` + `token-blacklist.ts` | JWT 鉴权与缓存 | +| `src/lib/api-permissions.ts` + `api-auth.ts` | 权限校验 | +| `src/lib/audit-logger.ts` + `audit-middleware.ts` | 审计日志 | +| `src/lib/rate-limit.ts` | 限流 | +| `src/contexts/AuthContext.tsx` | 客户端认证上下文 | +| `src/components/IntlProvider.tsx` | next-intl Provider | -> 最后更新:2026-07-10 +> 最后更新:2026-06-30 diff --git "a/docs/01-\351\241\271\347\233\256\346\246\202\350\277\260/\346\250\241\345\235\227\346\236\266\346\236\204.md" "b/docs/01-\351\241\271\347\233\256\346\246\202\350\277\260/\346\250\241\345\235\227\346\236\266\346\236\204.md" index 51c6527a..0c300028 100644 --- "a/docs/01-\351\241\271\347\233\256\346\246\202\350\277\260/\346\250\241\345\235\227\346\236\266\346\236\204.md" +++ "b/docs/01-\351\241\271\347\233\256\346\246\202\350\277\260/\346\250\241\345\235\227\346\236\266\346\236\204.md" @@ -1,109 +1,55 @@ # 模块架构 -> 基于 `src/domain/`、`src/application/`、`src/infrastructure/repositories/`、`src/app/api/`、`src/app/[locale]/` 实际目录。所有聚合根、应用服务、仓储、路由均经代码核实。 - -## 九大领域模块 - -系统在领域层沉淀了 9 个核心业务模块(共 25 个聚合根),每个模块在应用层有对应应用服务与事件处理器,在基础设施层有仓储实现,在表现层有 API 路由与页面。 - -### 1. dcprint(印前 / 印刷车间) - -| 维度 | 内容 | -|------|------| -| 聚合根 | `Tool`(刀模/网版)、`SampleProcessCard`(打样工艺卡)、`SampleProcessTemplate`(工艺卡模板)、`InkFormulaVersion`(油墨配方版本) | -| 应用服务 | `ToolManagementService`、`SampleProcessCardService`、`SampleProcessTemplateService` | -| 仓储 | `MysqlToolRepository`、`MysqlSampleProcessCardRepository`、`MysqlSampleProcessTemplateRepository`、`MysqlFormulaVersionRepository` | -| 事件处理器 | `ToolUsageSyncHandler`、`ToolCostHandler`、`ScreenPlateCostHandler`、`FormulaVersionEventHandler`、`InkCostHandler` | -| 主要 API 路由 | `/api/dcprint/tool/*`、`/api/dcprint/sample-card/*`、`/api/dcprint/formula/*`、`/api/dcprint/ink-*`、`/api/dcprint/labels`、`/api/dcprint/trace`、`/api/dcprint/scan`、`/api/dcprint/process-cards` | -| 页面 | `/dcprint`(主)、`/dcprint/process-card`、`/dcprint/process-cards`、`/dcprint/tool`、`/dcprint/tool-manage`、`/dcprint/ink-formula`、`/dcprint/ink`、`/dcprint/ink-usage`、`/dcprint/ink-mixed`、`/dcprint/ink-opening`、`/dcprint/die`、`/dcprint/screen-plate`、`/dcprint/labels`、`/dcprint/trace` | - -### 2. production(生产) - -| 维度 | 内容 | -|------|------| -| 聚合根 | `WorkOrder`(工单)、`FinishOrder`(完工单)、`PickOrder`(领料单)、`WorkReport`(报工单) | -| 应用服务 | `ProductionApplicationService` | -| 仓储 | `MysqlWorkOrderRepository`、`MysqlFinishOrderRepository`、`MysqlPickOrderRepository`、`MysqlWorkReportRepository`、`MysqlScheduleRepository` | -| 事件处理器 | `SalesToWorkOrderHandler`、`WorkOrderMaterialIssuedHandler`、`WorkOrderCompletedHandler`、`PickOrderInventoryHandler`、`FinishOrderInventoryHandler`、`ProductionFinanceHandler`、`MaterialReturnInventoryHandler` | -| 主要 API 路由 | `/api/production/work-orders`、`/api/production/work-order/*`、`/api/production/work-report`、`/api/production/material-issue`、`/api/production/schedule/*`、`/api/production/trace`、`/api/production/process-route`、`/api/production/product-label`、`/api/production/process` | -| 页面 | `/production/schedule`、`/production/report`、`/production/material-issue`、`/production/material-return`、`/production/orders`、`/production/workorder`、`/production/process`、`/production/mrp`、`/production/product-label` | - -### 3. warehouse(仓储) - -| 维度 | 内容 | -|------|------| -| 聚合根 | `InboundOrder`(入库单)、`OutboundOrder`(出库单)、`TransferOrder`(调拨单)、`StocktakingOrder`(盘点单) | -| 应用服务 | `InboundApplicationService`、`OutboundApplicationService`、`TransferApplicationService`、`StocktakingApplicationService`、`InventoryValidationService`、`InventoryCostService` | -| 仓储 | `MysqlInboundOrderRepository`(+ `DrizzleInboundOrderRepository`)、`MysqlOutboundOrderRepository`、`MysqlTransferOrderRepository`、`MysqlStocktakingOrderRepository` | -| 事件处理器 | `OutboundInventoryHandler`、`OutboundReceivableHandler`、`InventorySyncHandler`、`InventoryRollbackHandler` | -| 主要 API 路由 | `/api/warehouse/inbound/*`、`/api/warehouse/outbound/*`、`/api/warehouse/transfer/*`、`/api/warehouse/stocktaking/*`、`/api/warehouse/inventory/*`、`/api/warehouse/batch*`、`/api/warehouse/cost`、`/api/warehouse/fifo-recommend`、`/api/warehouse/stock-adjust`、`/api/warehouse/freeze` | -| 页面 | `/warehouse`(主)、`/warehouse/inbound`、`/warehouse/inbound-simple`、`/warehouse/cutting`、`/warehouse/outbound`、`/warehouse/inventory`、`/warehouse/transfer`、`/warehouse/stocktaking`、`/warehouse/stock-adjust`、`/warehouse/sales-outbound`、`/warehouse/production-inbound`、`/warehouse/batch`、`/warehouse/cost`、`/warehouse/setup`、`/warehouse/categories` | - -### 4. sales(销售) - -| 维度 | 内容 | -|------|------| -| 聚合根 | `SalesOrder`(销售订单)、`ReturnOrder`(退货单)、`Reconciliation`(对账单)、`Delivery`(发货单) | -| 应用服务 | `SalesApplicationService`、`ReturnOrderApplicationService`、`ReconciliationApplicationService`、`DeliveryApplicationService` | -| 仓储 | `MysqlSalesOrderRepository`(+ `DrizzleSalesOrderRepository`)、`MysqlReturnOrderRepository`、`MysqlReconciliationRepository`、`MysqlDeliveryRepository` | -| 事件处理器 | `SalesReceivableHandler`、`SalesShippedHandler`、`SalesToWorkOrderHandler`、`DeliveryShippedHandler`、`DeliveryCancelledHandler`、`DeliveryReceivableHandler`、`ReconciliationWriteOffHandler`、`ReturnOrderInventoryHandler` | -| 主要 API 路由 | `/api/sales/orders/*`、`/api/sales/return/*`、`/api/sales/reconciliation/*`、`/api/sales/delivery/*`、`/api/sales/convert-wo` | -| 页面 | `/sales/delivery`、`/sales/reconciliation`、`/sales/return`、`/orders/sales` | - -### 5. purchase(采购) - -| 维度 | 内容 | -|------|------| -| 聚合根 | `PurchaseOrder`(采购订单)、`PurchaseReturn`(采购退货)、`PurchaseReconciliation`(采购对账) | -| 应用服务 | `PurchaseApplicationService`、`PurchaseReturnApplicationService`、`PurchaseReconciliationApplicationService` | -| 仓储 | `MysqlPurchaseOrderRepository`(+ `DrizzlePurchaseOrderRepository`)、`MysqlPurchaseReturnRepository`、`MysqlPurchaseReconciliationRepository` | -| 事件处理器 | `PurchaseApprovedHandler`、`PurchaseReceivedHandler`、`PurchasePayableHandler`、`PurchaseInboundSyncHandler`、`PurchaseReturnCompletedHandler`、`PurchaseReconciliationWrittenOffHandler` | -| 主要 API 路由 | `/api/purchase/orders/*`、`/api/purchase/return/*`、`/api/purchase/request/*`、`/api/purchase/reconciliation/*`、`/api/purchase/suppliers`、`/api/purchase/convert-po` | -| 页面 | `/purchase/orders`、`/purchase/return`、`/purchase/request/*`、`/purchase/suppliers` | - -### 6. finance(财务) - -| 维度 | 内容 | -|------|------| -| 聚合根 | `Receivable`(应收)、`Payable`(应付)、`Voucher`(凭证) | -| 应用服务 | `FinanceApplicationService` | -| 仓储 | `MysqlReceivableRepository`、`MysqlPayableRepository`、`MysqlVoucherRepository` | -| 事件处理器 | `FinanceVoucherHandler`(`ProductionFinanceHandler` 跨生产/财务联动) | -| 主要 API 路由 | `/api/finance/receivable/*`、`/api/finance/payable/*`、`/api/finance/receipt`、`/api/finance/payment`、`/api/finance/cost*`、`/api/finance/invoice`、`/api/finance/expense`、`/api/finance/aging`、`/api/finance/stats`、`/api/finance/report`、`/api/finance/summary` | -| 页面 | `/finance`(主)、`/finance/receivables`、`/finance/receivable`、`/finance/payables`、`/finance/costs`、`/finance/cost`、`/finance/report` | - -### 7. sample(打样) - -| 维度 | 内容 | -|------|------| -| 聚合根 | `SampleOrder`(打样订单) | -| 应用服务 | `SampleOrderApplicationService` | -| 仓储 | `MysqlSampleOrderRepository`、`MysqlSampleFeedbackRepository` | -| 事件处理器 | `SampleOrderConversionHandler`、`SampleOrderInventoryHandler` | -| 主要 API 路由 | `/api/sample/orders/*`、`/api/sample/feedback`、`/api/sample/inventory` | -| 页面 | `/sample/orders/*`(含 new、详情、edit)、`/sample/management` | - -### 8. quality(质量) - -| 维度 | 内容 | -|------|------| -| 聚合根 | `UnqualifiedProduct`(不合格品) | -| 应用服务 | `UnqualifiedApplicationService` | -| 仓储 | `MysqlUnqualifiedRepository` | -| 事件处理器 | 质量联动多由库存/生产处理器承担(如 `InventoryRollbackHandler`),无独立 quality 事件处理器 | -| 主要 API 路由 | `/api/quality/incoming`、`/api/quality/process/*`、`/api/quality/final/*`、`/api/quality/lab-test`、`/api/quality/sgs/*`、`/api/quality/complaint`、`/api/quality/supplier-audit`、`/api/quality/unqualified`、`/api/quality/spc` | -| 页面 | `/quality/trace`、`/quality/process`、`/quality/incoming`、`/quality/final`、`/quality/spc`、`/quality/sgs`、`/quality/unqualified`、`/quality/complaint`、`/quality/lab-test`、`/quality/supplier-audit` | - -### 9. standard-card(标准卡) - -| 维度 | 内容 | -|------|------| -| 聚合根 | `StandardCard`(标准卡) | -| 应用服务 | `StandardCardApplicationService` | -| 仓储 | `MysqlStandardCardRepository` | -| 事件处理器 | `StandardCardHandlers` | -| 主要 API 路由 | `/api/standard-card/*`(`scan`、`check-deviation`、`by-material`、`by-work-order`、`approve`、`action`) | -| 页面 | `/sample/standard-card/input`、`/sample/standard-card/input-v2`、`/sample/standard-card/template`、`/sample/standard-card/print` | +> 基于 `src/app/[locale]/` 页面路由与 `src/app/api/` API 路由实际目录。 + +## 业务模块总览 + +| 模块 | 页面路径 | API 路径 | 主要职责 | +|------|---------|---------|---------| +| 看板(Dashboard) | `/dashboard` 及子页 | `/api/dashboard/*` | 首页看板、CEO/销售/生产/质量/仓储/财务分项看板、流程图 | +| 仓储管理 | `/warehouse` | `/api/warehouse/*` + `/api/inventory/*` | 入库(含切割拆分)、出库、库存、盘点、调拨、批次、成本、出入库记账 | +| 销售管理 | `/sales` + `/orders/sales` | `/api/sales/*` + `/api/orders/*` | 销售订单、发货、退货、对账、转工单 | +| 采购管理 | `/purchase` | `/api/purchase/*` | 采购申请、采购订单、供应商、退货、转 PO | +| 生产管理 | `/production` | `/api/production/*` | 工单、工序、排产、报工、领料/退料、MRP、追溯 | +| 印前管理 | `/dcprint` + `/prepress` | `/api/dcprint/*` + `/api/prepress/*` | 油墨配方/开盖/调色/使用、刀模/网版寿命、标签、追溯 | +| 质量管理 | `/quality` | `/api/quality/*` | 来料/过程/成品检验、SGS、SPC、不合格、客诉、追溯 | +| 设备管理 | `/equipment` | `/api/equipment/*` | 设备档案、校准、维修、报废 | +| 财务管理 | `/finance` | `/api/finance/*` | 应收/应付、发票、收款/付款、费用、成本、报表、账龄 | +| 人力资源 | `/hr` | `/api/hr/*` | 员工、考勤、薪资、培训、部门 | +| 打样管理 | `/sample` | `/api/sample/*` | 打样订单、标准卡(录入/打印)、管理 | +| 系统设置 | `/settings` | `/api/system/*` + `/api/organization/*` | 用户、角色、菜单、字典、组织、权限、调度、公告、登录/操作日志 | +| 客户关系 | `/crm` | `/api/crm/*` | 客户分析、跟进 | +| 供应商关系 | `/srm` | `/api/srm/*` | 供应商评估 | +| 二维码追溯 | `/qrcode` | `/api/qrcode/*` | 二维码生成、打印、追溯、扫码 | +| 报表中心 | `/reports` | `/api/reports/*` | 综合报表 | +| 印刷车间 | `/dcprint` | `/api/dcprint/*` | 油墨、刀模、标签、追溯、工艺卡 | +| 生产排程 | `/production/schedule` | `/api/production/schedule` | 排产 | +| MRP | `/production/mrp` | `/api/production/mrp` | 物料需求计划 | +| 工程管理 | `/engineering` | `/api/engineering/sop` | SOP、打样转量产 | +| 外协管理 | `/outsource` | `/api/outsource/*` | 外协发料、外协订单、外协收货、结算 | +| PLM | `/plm` | `/api/plm/*` | 工程变更(ECO)、产品生命周期 | +| 物料领用 | `/material-requisitions` | `/api/material-lifecycle/*` | 物料领用单 | +| 发货车辆 | `/delivery/vehicles` | `/api/delivery/vehicles` | 车辆档案 | +| BOM | `/orders/bom` | `/api/orders/bom` | 物料清单与展开 | +| 工作台 | `/modules` | - | 模块导航 | +| 调试/诊断 | `/debug`、`/diagnostic`、`/test-api`、`/test` | `/api/debug/*`、`/api/diagnose/*` | 仅开发环境,生产屏蔽 | + +## 模块与领域层映射 + +应用层 / 领域层已沉淀的模块: + +| 业务模块 | 领域层 (`src/domain/`) | 应用服务 (`src/application/services/`) | 事件处理器 (`src/application/handlers/`) | +|---------|------------------------|--------------------------------------|----------------------------------------| +| 生产 | `production` | `ProductionApplicationService` | `SalesToWorkOrderHandler`、`ScreenPlateCostHandler` | +| 销售 | `sales` | `SalesApplicationService` | `SalesShippedHandler`、`SalesReceivableHandler` | +| 采购 | `purchase` | `PurchaseApplicationService` | `PurchaseApprovedHandler`、`PurchaseReceivedHandler`、`PurchasePayableHandler` | +| 仓储 | `warehouse` | `InboundApplicationService`、`InventoryValidationService` | `InventorySyncHandler`、`InventoryRollbackHandler` | +| 标准卡 | `standard-card` | `StandardCardApplicationService` | `StandardCardHandlers`、`InkCostHandler` | +| 财务 | - | `FinanceApplicationService` | `FinanceVoucherHandler` | +| 物料生命周期 | - | `MaterialLifecycleService` | - | +| 看板 | - | `DashboardDataService` | - | +| 审计 | - | - | `AuditLogHandler`、`CacheInvalidationHandler` | +| 二维码 | - | - | `QrCodeGenerationHandler` | ## 模块路由约定 @@ -115,50 +61,17 @@ API : src/app/api//route.ts # 集合(GET/POST) src/app/api//[id]/route.ts # 单资源(GET/PUT/DELETE) ``` -- 页面统一挂载于 `[locale]` 下,自动适配四种语言(zh-CN / zh-TW / en / vi)。 -- API 不带 locale 前缀,由 `src/proxy.ts` 的 `config.matcher` 排除 `/api` 走 i18n,但保留 `/api/:path*` 走鉴权与 CSRF 校验。 -- API 路由风格:多数用 `export const GET/POST/PUT/DELETE = withPermission(async (req, userInfo) => {...})`(`src/lib/api-auth.ts` 装饰器,含 JWT + 权限校验);`dashboard` / `auth` / `health` / `init` 等公开或特殊路由用 `export async function GET/POST(...)`。统一响应封装于 `src/lib/api-response.ts`。 -- 生产环境封堵调试路由:`/qrcode`、`/api/init`、`/api/debug`、`/api/diagnose` 返回 404(见 `src/proxy.ts` 的 `BLOCKED_ROUTES`)。 -- 仓储模块额外含 `error.tsx` 与 `loading.tsx`,作为路由级错误/加载边界示例。 +- 页面统一挂载于 `[locale]` 下,自动适配四种语言 +- API 不带 locale 前缀,由 `src/middleware.ts` matcher 排除 `/api` +- 调试路由(`/debug`、`/test-api`、`/diagnostic`、`/test`、`/qrcode`)在生产环境返回 404 +- 仓储模块额外含 `error.tsx` 与 `loading.tsx`,作为路由级错误/加载边界示例 ## 核心业务闭环 -销售到生产到财务的核心闭环,串联多个领域模块与事件处理器: - ``` -销售订单 - → 销售转工单(SalesToWorkOrderHandler) - → 工单下达(production) - → MRP / 领料(PickOrderInventoryHandler) - → 生产执行 → 报工(WorkReport) - → 完工入库(FinishOrderInventoryHandler) - → 成品检验(quality) - → 发货(SalesShippedHandler / DeliveryShippedHandler) - → 应收(SalesReceivableHandler / DeliveryReceivableHandler) - → 财务核算(FinanceVoucherHandler) +销售订单 → 销售转工单(SalesToWorkOrderHandler) → 工单下达 → MRP/领料 + → 生产执行 → 完工入库 → 成品检验 → 发货(SalesShippedHandler) + → 应收(SalesReceivableHandler) → 财务核算(FinanceVoucherHandler) ``` -横向联动还包括: - -- 采购入库 → 应付(`PurchasePayableHandler`)→ 财务凭证 -- 出库 → 库存扣减(`OutboundInventoryHandler`)→ 成本归集(`cost-engine` + `InkCostHandler` / `ToolCostHandler` / `ScreenPlateCostHandler`) -- 刀模/网版使用次数同步(`ToolUsageSyncHandler`)→ 寿命预警 -- 打样订单转量产(`SampleOrderConversionHandler`) - -## 跨模块支撑服务 - -除上述 9 个领域模块外,应用层与基础设施层还提供以下跨模块支撑: - -| 支撑能力 | 实现 | 说明 | -|---------|------|------| -| 看板数据 | `DashboardDataService` | 聚合各域 KPI(CEO/销售/生产/质量/仓储/财务) | -| 物料生命周期 | `MaterialLifecycleService` | 物料领用与全生命周期跟踪 | -| FIFO 分配 | `src/lib/fifo-allocation.ts` | 先进先出分配(已修复 SQL 注入) | -| 成本核算 | `src/lib/cost-engine.ts` | 工单级成本归集(油墨/刀模/网版分摊) | -| MRP | `src/lib/mrp-engine.ts` / `mrp-engine-v2.ts` | 物料需求计划 | -| 二维码追溯 | `src/lib/qrcode-service.ts` + `QrCodeGenerationHandler` | 全流程扫码追溯 | -| 工作流 | `src/application/workflow/WorkflowEngine.ts` | 业务流程编排 | -| 审计日志 | `AuditLogHandler` + `src/lib/audit-logger.ts` | 操作审计 | -| 缓存失效 | `CacheInvalidationHandler` | 事件驱动缓存失效 | - -> 最后更新:2026-07-10 +> 最后更新:2026-06-30 diff --git "a/docs/01-\351\241\271\347\233\256\346\246\202\350\277\260/\351\241\271\347\233\256\346\246\202\350\277\260.md" "b/docs/01-\351\241\271\347\233\256\346\246\202\350\277\260/\351\241\271\347\233\256\346\246\202\350\277\260.md" index c7f3b59b..87d83029 100644 --- "a/docs/01-\351\241\271\347\233\256\346\246\202\350\277\260/\351\241\271\347\233\256\346\246\202\350\277\260.md" +++ "b/docs/01-\351\241\271\347\233\256\346\246\202\350\277\260/\351\241\271\347\233\256\346\246\202\350\277\260.md" @@ -2,126 +2,82 @@ ## 项目定位 -印刷生产经营信息管理系统 Print MIS(包名 `vnerp`)是面向**丝网印刷行业**的企业资源规划系统,覆盖销售、生产、采购、仓储、品质、财务、人力资源等核心业务流程。系统针对印刷行业沉淀了二维码全流程追溯、物料 FIFO 分配、标准卡工艺数字化、刀模/网版寿命管理、油墨配方版本、工单级成本核算等专项能力,并采用 DDD(领域驱动设计)分层架构保证业务逻辑的可演进性。 +VNERP(越南达昌科技 ERP)是面向**丝网印刷行业**的企业资源规划系统,覆盖销售、生产、采购、仓储、品质、财务、HR 等核心业务流程,主打印刷行业的二维码全流程追溯、刀模/网版寿命管理、油墨配方与标准卡等专项能力。 -- **项目名称**:vnerp(`package.json` 的 `name` 字段) -- **产品名称**:印刷生产经营信息管理系统 Print MIS +- **项目名称**:vnerp(`package.json` name 字段) - **当前版本**:0.1.0 - **私有项目**:`"private": true` -- **包管理器**:pnpm 9.0.0(`preinstall` 通过 `only-allow pnpm` 强制,`engines.pnpm >= 9.0.0`) +- **包管理器**:pnpm 9(`preinstall` 通过 `only-allow pnpm` 强制) - **开发端口**:5000(`next dev -p 5000`) -- **默认公司**:公司名称(`src/app/[locale]/layout.tsx`,可由 `sys_config` 表 `company_name` / `company_short_name` 配置覆盖,5 分钟缓存) +- **默认公司**:越南达昌科技有限公司(`src/app/[locale]/layout.tsx`,可由 `sys_config` 表 `company_name` 配置覆盖) ## 核心能力 -| 能力 | 说明 | 关键实现 | -|------|------|---------| -| 二维码全流程追溯 | 原材料入库 → 生产 → 成品出库扫码追溯 | `src/lib/qrcode-service.ts`、`/api/qrcode/*`、`QrCodeGenerationHandler` | -| 物料 FIFO 分配 | 强制先进先出,已修复 SQL 注入 | `src/lib/fifo-allocation.ts`、`/api/warehouse/fifo-recommend` | -| 标准卡管理 | 丝网印刷工艺参数(油墨、网版、刀具等)数字化 | `src/domain/standard-card`、`StandardCardApplicationService` | -| 刀模/网版寿命 | 使用次数与寿命预警、超期锁定 | `src/domain/dcprint`(`Tool` 聚合根)、`ToolManagementService` | -| 油墨配方版本 | 配方版本激活、成本预览、版本对比 | `MysqlFormulaVersionRepository`、`FormulaVersionEventHandler` | -| 工单级成本核算 | 批次/工单实际成本归集(油墨、刀模、网版分摊) | `src/lib/cost-engine.ts`、`InkCostHandler`、`ToolCostHandler` | -| 多语言 | zh-CN(默认)/ zh-TW / en / vi 四语言切换 | `next-intl`,`localePrefix: 'as-needed'` | -| DDD 分层 | 领域层 + 应用层 + 基础设施层 + 表现层 | `src/domain` → `src/application` → `src/infrastructure` → `src/app` | -| 事件驱动 | 领域事件经 Redis Streams + Outbox 两阶段投递 | `src/infrastructure/event-bus/*`、36 个事件处理器 | +| 能力 | 说明 | +|------|------| +| 二维码全流程追溯 | 原材料入库 → 生产 → 成品出库扫码追溯 | +| 物料 FIFO 分配 | `src/lib/fifo-allocation.ts` 强制先进先出 | +| 标准卡管理 | 丝网印刷工艺参数(油墨、网版、刀具等)数字化 | +| 刀模/网版寿命 | 自动预警、超期锁定 | +| 工单级成本核算 | 批次/工单实际成本归集 | +| 多语言 | zh-CN / zh-TW / en / vi 四语言切换 | +| DDD 分层 | 领域层 + 应用层 + 基础设施层 + 表现层 | ## 技术栈速览 -> 精确版本来自 `package.json`,详见 [技术栈.md](./技术栈.md)。 - | 层级 | 技术 | 版本 | |------|------|------| | 前端框架 | Next.js(App Router) | 16.1.1 | -| UI 库 | React / react-dom | 19.2.3 | +| UI 库 | React | 19.2.3 | | 语言 | TypeScript | ^5 | | 样式 | Tailwind CSS | ^4 | -| 组件库 | shadcn/ui(基于 @radix-ui/* 全家桶) | 4.7.0 | -| ORM | drizzle-orm + mysql2 | 0.45.1 / 3.20.0 | +| 组件库 | shadcn/ui(基于 Radix UI) | latest | +| ORM | Drizzle ORM + mysql2 | 0.45.1 / 3.20.0 | | 数据库 | MySQL | 8.0+ | -| 缓存 | ioredis | ^5.11.1 | | 国际化 | next-intl | ^4.13.0 | -| 表单校验 | react-hook-form + @hookform/resolvers + zod | ^7.70.0 / ^5.2.2 / ^4.3.5 | -| 认证 | jose(JWT)+ bcryptjs | ^6.2.2 / ^3.0.3 | -| 图表 | recharts | 2.15.4 | -| 3D / 流程图 | three + @react-three/fiber + @xyflow/react | ^0.183.2 / ^9.6.0 / ^12.11.0 | -| 导出 | @e965/xlsx + jspdf + docx + file-saver | ^0.20.3 / ^4.2.1 / ^9.7.1 / ^2.0.5 | +| 表单校验 | zod + react-hook-form + @hookform/resolvers | ^4.3.5 / ^7.70.0 / ^5.2.2 | | 单元测试 | Vitest | ^4.1.5 | | E2E 测试 | Playwright | ^1.58.2 | -| Git 钩子 | husky + lint-staged + commitlint | ^9.1.7 / ^15.5.2 / ^21.1.0 | | 包管理 | pnpm | 9.0.0 | +| Git 钩子 | husky + lint-staged + commitlint | ^9.1.7 / ^15.5.2 / ^21.1.0 | +| 缓存 | ioredis | ^5.11.1 | +| 认证 | jose(JWT)+ bcryptjs | ^6.2.2 / ^3.0.3 | ## 入口与布局 -### 根布局 - -入口布局位于 `src/app/[locale]/layout.tsx`,是所有多语言页面的根布局。其核心结构如下: +入口布局位于 `src/app/[locale]/layout.tsx`,关键结构: ```tsx -<> - - - - - - {children} - - - - + + + + + + + {children} + + + + + ``` -关键行为: - -- 通过 `locales`(`src/i18n/locales.ts`)校验 locale,非法则回退 `zh-CN` 并 `notFound()`;`generateStaticParams` 预生成四种语言的静态参数。 -- 支持的 locale:`zh-CN`(默认)、`zh-TW`、`en`、`vi`,定义于 `src/i18n/locales.ts`。 -- 服务端预取:`getCompanyName()` 从 `sys_config` 读取公司名(5 分钟缓存,失败回退默认值);`prefetchMenus()` 读取 `access_token` cookie 并调用 `getMenusByToken` 预取菜单与权限,失败静默降级到客户端 fetch。 -- `AuthProvider` 接收 `initialAuth`( menus + permissions),未登录或 token 过期时降级为客户端鉴权。 - -### 中间件(proxy.ts,替代 middleware.ts) - -> 注意:Next.js 16 下入口中间件为 `src/proxy.ts`(自动探测),原 `src/middleware.ts` 已删除。本项目中 `proxy.ts` 同时承担 i18n 路由、API 鉴权与页面鉴权三重职责。 - -`src/proxy.ts` 的核心职责: - -1. **i18n 路由**:`createIntlMiddleware`,`locales=[zh-CN, zh-TW, en, vi]`,`defaultLocale=zh-CN`,`localePrefix: 'as-needed'`(默认 `zh-CN` 不带前缀,其余语言带 `/zh-TW` 等前缀)。 -2. **生产环境封堵调试路由**:`BLOCKED_ROUTES = ['/qrcode', '/api/init', '/api/debug', '/api/diagnose']`,生产环境访问返回 404。 -3. **API 路由鉴权**:`/api/` 下公开 API(`/api/auth/login`、`/api/auth/register`、`/api/health`、`/api/migrations`)放行;其余须携带 `access_token` cookie(无则 401);`requiresCsrfValidation` 的请求校验 CSRF token(失败 403)。 -4. **页面鉴权**:26 个受保护路由前缀(`/dashboard`、`/warehouse`、`/sales`、`/purchase`、`/production`、`/quality`、`/hr`、`/finance`、`/equipment`、`/engineering`、`/outsource`、`/orders`、`/reports`、`/settings`、`/dcprint`、`/sample`、`/analysis`、`/base-data`、`/business`、`/srm`、`/prepress`、`/plm`、`/delivery`、`/crm`、`/tools`、`/material-requisitions`、`/modules`、`/qrcode`)无 `access_token` 时重定向 `/login?next=原路径`;已登录访问 `/login` 等公共页重定向 `/dashboard`。 -5. **CSRF cookie**:对页面请求首次访问时种入 CSRF cookie。 - -> 重要:proxy 运行于 Edge runtime,**只校验 cookie 存在性,不解析 JWT**;JWT 实际有效性由 SSR `layout.tsx` 与 API 路由的 `verifyToken` 校验。 - -`config.matcher`:页面路径(排除 `api/_next/_vercel/静态`)+ `/api/:path*` + `/qrcode/:path*`。 - -### 数据库连接 - -数据库连接池见 `src/lib/db/index.ts`,基于 mysql2 连接池 + Drizzle ORM,使用全局单例(`globalThis.pool`)防止热重载重复创建。连接池配置:最大 20 连接、最多 10 空闲、空闲超时 30s、队列限制 100、连接超时 8s。所有查询使用参数化语句防止 SQL 注入。Drizzle 实例 `drizzleDb` 基于该连接池创建并加载完整 schema。 +- 通过 `locales`(`src/i18n/locales.ts`)校验 locale,非法则 `notFound()` +- 支持的 locale:`zh-CN`、`zh-TW`、`en`、`vi`,默认 `zh-CN` +- 中间件 `src/middleware.ts` 处理 i18n 前缀(`localePrefix: 'as-needed'`),并在生产环境屏蔽 `/debug`、`/test-api`、`/diagnostic`、`/test`、`/qrcode` 调试路由 +- 数据库连接池见 `src/lib/db/index.ts`,使用 mysql2 连接池 + Drizzle ORM,全局单例防止热重载重复创建 ## 脚本命令 -> 迁移路径已切换:全量初始化用 `pnpm setup:db`,增量迁移用 `pnpm migrate`;`db:generate` / `db:push` / `db:migrate` 已废弃(仅打印警告)。 - -| 命令 | 用途 | 说明 | -|------|------|------| -| `pnpm dev` | 开发服务器 | 端口 5000(Turbopack) | -| `pnpm dev:webpack` | 开发服务器(webpack 模式) | 规避 Turbopack 在 Windows 下 globals.css `nul` 崩溃 | -| `pnpm build` / `pnpm start` | 生产构建与启动 | `start` 同样监听 5000 | -| `pnpm lint` / `pnpm lint:fix` | ESLint 检查与修复 | 扫描 `src/` | -| `pnpm format` / `pnpm format:check` | Prettier 格式化 | | -| `pnpm ts-check` | TypeScript 类型检查 | `tsc --noEmit` | -| `pnpm test` | Playwright E2E | 另有 `test:ui` / `test:headed` / `test:debug` / `test:report` / `test:install` | -| `pnpm test:unit` / `test:unit:run` / `test:coverage` | Vitest 单测 | | -| `pnpm setup:db` | **全量初始化数据库** | `node scripts/setup-db.mjs`(建库 + 导入 schema + 可选 seed) | -| `pnpm migrate` | **增量迁移(up)** | `npx tsx scripts/migrate.ts up` | -| `pnpm migrate:down` / `migrate:status` / `migrate:create` | 迁移回滚 / 状态 / 新建 | `scripts/migrate.ts` | -| `pnpm db:studio` | Drizzle Studio | | -| `pnpm backup` / `backup:list` / `backup:restore` | 数据库备份 / 列表 / 恢复 | `scripts/backup-database.ts` | -| `pnpm docs:api` | 生成 API 文档 | `scripts/generate-api-docs.js` | -| `pnpm cleanup:events` | 清理事件已处理记录 | `scripts/cleanup-event-processed.mjs` | -| `pnpm init` / `init:full` | 项目初始化 | `scripts/init-project.js` | -| ~~`pnpm db:generate`~~ | **已废弃** | 仅打印警告,改用 `pnpm setup:db` / `pnpm migrate` | -| ~~`pnpm db:push`~~ | **已废弃** | 仅打印警告 | -| ~~`pnpm db:migrate`~~ | **已废弃** | 仅打印警告,改用 `pnpm migrate` | - -> 最后更新:2026-07-10 +| 命令 | 用途 | +|------|------| +| `pnpm dev` | 开发服务器(端口 5000) | +| `pnpm build` / `pnpm start` | 构建与启动生产服务 | +| `pnpm lint` / `pnpm lint:fix` | ESLint 检查与自动修复 | +| `pnpm format` / `pnpm format:check` | Prettier 格式化 | +| `pnpm ts-check` | TypeScript 类型检查 | +| `pnpm test` | Playwright E2E | +| `pnpm test:unit` / `pnpm test:coverage` | Vitest 单测与覆盖率 | +| `pnpm db:studio` / `db:generate` / `db:migrate` | Drizzle 数据库工具 | + +> 最后更新:2026-06-30 diff --git "a/docs/02-\346\250\241\345\235\227\350\257\246\347\273\206\350\256\276\350\256\241/00-\346\250\241\345\235\227\350\257\246\347\273\206\350\256\276\350\256\241\346\200\273\350\247\210.md" "b/docs/02-\346\250\241\345\235\227\350\257\246\347\273\206\350\256\276\350\256\241/00-\346\250\241\345\235\227\350\257\246\347\273\206\350\256\276\350\256\241\346\200\273\350\247\210.md" index 0a8a8bb3..e346e97a 100644 --- "a/docs/02-\346\250\241\345\235\227\350\257\246\347\273\206\350\256\276\350\256\241/00-\346\250\241\345\235\227\350\257\246\347\273\206\350\256\276\350\256\241\346\200\273\350\247\210.md" +++ "b/docs/02-\346\250\241\345\235\227\350\257\246\347\273\206\350\256\276\350\256\241/00-\346\250\241\345\235\227\350\257\246\347\273\206\350\256\276\350\256\241\346\200\273\350\247\210.md" @@ -2,137 +2,69 @@ ## 1. 项目背景 -印刷生产经营信息管理系统 Print MIS(包名 `vnerp`,版本 0.1.0)是面向印刷行业的 ERP 系统,采用 DDD 分层架构(domain / application / infrastructure)+ Next.js 16 + Drizzle ORM + MySQL 技术栈。本文档作为模块详细设计的统一索引,所有设计均基于当前代码现状(截至 2026-07-10),不包含未实现规划。 +VNERP 是面向印刷行业的 ERP 系统,采用 DDD 分层架构(domain/application/infrastructure)+ Next.js 16 + Drizzle ORM + MySQL 技术栈。本文档作为模块详细设计的统一索引,所有设计均基于当前代码现状,不包含未实现规划。 -权威事实底座参见 `.trae/documents/doc-fact-base.md`,权威 SQL 参见 `database/vnerpdacahng_schema.sql`。 - -## 2. 技术栈要点 - -| 层 | 技术 | 版本 | -|---|---|---| -| 框架 | next | 16.1.1(开发端口 5000,`pnpm dev` / `pnpm dev:webpack`) | -| UI | react / react-dom | 19.2.3 | -| 语言 | typescript | ^5 | -| ORM | drizzle-orm 0.45.1 + mysql2 3.20.0(drizzle-kit 迁移路径已废弃) | -| DB | MySQL 8.0+ | -| i18n | next-intl ^4.13.0(zh-CN 默认 / zh-TW / en / vi,`localePrefix: as-needed`) | -| 表单 | react-hook-form ^7 + zod ^4 | -| 缓存/事件总线 | ioredis ^5.11.1(Redis Streams + Outbox 两阶段) | -| 认证 | jose ^6(JWT)+ bcryptjs ^3 | -| 单测 | vitest ^4 + @vitest/coverage-v8 | -| E2E | @playwright/test ^1.58 | - -> 中间件:`src/proxy.ts`(Next.js 16 自动探测,**取代已删除的 `src/middleware.ts`**)。 - -## 3. DDD 分层架构 +## 2. 架构分层 | 层 | 路径 | 职责 | |---|---|---| -| 表现层 | `src/app/[locale]/`(140+ 页面)、`src/app/api/`(356 方法导出) | 页面渲染、REST API 路由(多数经 `withPermission` 装饰器,含 JWT + 权限校验) | -| 应用层 | `src/application/services/`、`src/application/handlers/`、`src/application/EventRegistry.ts`、`src/application/workflow/WorkflowEngine.ts` | 用例编排、事务边界、聚合根调用、36 个事件处理器注册 | -| 领域层 | `src/domain//`(9 模块、25 聚合根) | 聚合根、领域实体、值对象、领域事件、仓储接口 | -| 基础设施层 | `src/lib/`、`src/lib/db/`、`src/infrastructure/` | 仓储实现(`Mysql*Repository.ts`)、外部服务、`schema.ts`、事件总线(`StreamPublisher` / `StreamConsumer` / `OutboxPoller`)、`RepositoryRegistry.ts` | - -## 4. 领域模块清单(9 模块 / 25 聚合根) - -| 模块 | 路径 | 聚合根 | 关键表 | -|---|---|---|---| -| dcprint(印前) | `src/domain/dcprint/` | `Tool`、`SampleProcessCard`、`SampleProcessTemplate`、`InkFormulaVersion` | `dcprint_tool`、`dcprint_tool_usage`、`dcprint_tool_maintenance`、`dcprint_ink_color`、`dcprint_ink_formula_version`、`dcprint_ink_formula_item`、`dcprint_sample_process_template` | -| production(生产) | `src/domain/production/` | `WorkOrder`、`FinishOrder`、`PickOrder`、`WorkReport` | `prd_work_order`、`prod_work_order`、`prod_work_order_item`、`prod_work_order_material_req`、`prd_pick_order`、`prd_pick_order_item`、`prd_return_order`、`prd_work_report`、`prd_finish_order`、`prd_schedule`、`prd_schedule_detail` | -| warehouse(仓库) | `src/domain/warehouse/` | `InboundOrder`、`OutboundOrder`、`TransferOrder`、`StocktakingOrder` | `inv_inbound_order`、`inv_inbound_item`、`inv_outbound_order`、`inv_outbound_item`、`inv_transfer_order`、`inv_transfer_item`、`inv_stocktaking`、`inv_stocktaking_item`、`inv_inventory`、`inv_inventory_batch`、`inv_inventory_transaction`、`inv_warehouse` | -| sales(销售) | `src/domain/sales/` | `SalesOrder`、`ReturnOrder`、`Reconciliation`、`Delivery` | `sal_order`、`sal_order_detail`、`sal_delivery`、`sal_delivery_detail`、`sal_return_order`、`sal_reconciliation`、`sal_quote`、`sal_quote_item` | -| purchase(采购) | `src/domain/purchase/` | `PurchaseOrder`、`PurchaseReturn`、`PurchaseReconciliation` | `pur_purchase_order`、`pur_purchase_order_line`、`pur_purchase_return`、`pur_purchase_return_line`、`pur_purchase_reconciliation`、`pur_request` | -| finance(财务) | `src/domain/finance/` | `Receivable`、`Payable`、`Voucher` | `fin_receivable`、`fin_payable`、`fin_receipt_record`、`fin_payment_record`、`fin_voucher`、`fin_voucher_line`、`fin_account`、`fin_account_balance`、`fin_period`、`fin_cost_record` | -| sample(打样) | `src/domain/sample/` | `SampleOrder` | `sal_sample_order` | -| quality(质量) | `src/domain/quality/` | `UnqualifiedProduct` | `qc_unqualified`、`qc_inspection` | -| standard-card(标准卡) | `src/domain/standard-card/` | `StandardCard` | `prd_standard_card`、`prd_process_card`、`prd_process_card_audit` | +| 表现层 | `src/app/[locale]/`、`src/app/api/` | 页面渲染、REST API 路由 | +| 应用层 | `src/application/services/` | 用例编排、事务边界、聚合根调用 | +| 领域层 | `src/domain//aggregates/` | 聚合根、领域实体、值对象 | +| 基础设施层 | `src/lib/`、`src/lib/db/` | 仓储实现、外部服务、Schema 定义 | -> 注:`src/domain/sample-card/`(仅含 `SampleCardEvents.ts`)为旧目录,事件已并入 `dcprint` 模块。 +注:项目未使用 `src/modules/` 目录,业务能力以 `src/lib/*-service.ts`、`src/lib/*-core.ts` 形式组织,部分模块(如标准卡)已落地完整 DDD 结构。 -## 5. 模块设计文档索引 +## 3. 模块设计文档索引 | 编号 | 文档 | 核心代码依据 | 关键表 | |---|---|---|---| -| 01 | [二维码全流程追溯系统设计](./01-二维码全流程追溯系统设计.md) | `src/lib/qrcode-service.ts`、`src/app/api/qrcode/route.ts` | `qrcode_record`、`qrcode_scan_log`、`inv_qr_code`、`inv_inventory_batch`、`inv_inventory_transaction` | -| 06 | [标准卡管理设计](./06-标准卡管理设计.md) | `src/domain/standard-card/`、`src/lib/standard-card-service.ts`、`src/application/services/StandardCardApplicationService.ts` | `prd_standard_card`、`prd_process_card`、`prd_process_card_audit` | -| 07 | [发货管理设计](./07-发货管理设计.md) | `src/domain/shipment/value-objects/ShipmentStateMachine.ts`、`src/domain/sales/aggregates/Delivery.ts`、`src/app/api/sales/delivery/[id]/ship/route.ts` | `sal_delivery`、`sal_delivery_detail`、`sal_order`、`sal_order_detail` | -| 15 | [盘点与调拨设计](./15-盘点与调拨设计.md) | `src/domain/warehouse/aggregates/StocktakingOrder.ts`、`src/domain/warehouse/aggregates/TransferOrder.ts`、`src/app/api/warehouse/stocktaking/route.ts`、`src/app/api/warehouse/transfer/route.ts` | `inv_stocktaking`、`inv_stocktaking_item`、`inv_transfer_order`、`inv_transfer_item`、`inv_inventory` | -| 21 | [物料领用设计](./21-物料领用设计.md) | `src/lib/material-requisition.ts`、`src/domain/production/aggregates/PickOrder.ts`、`src/app/api/production/material-issue/route.ts` | `material_requisitions`、`material_requisition_items`、`prd_pick_order`、`prd_pick_order_item`、`prd_return_order` | -| 24 | [财务管理设计](./24-财务管理设计.md) | `src/lib/finance-core.ts`、`src/lib/general-ledger.ts`、`src/lib/cost-engine.ts`、`src/domain/finance/` | `fin_receivable`、`fin_payable`、`fin_receipt_record`、`fin_payment_record`、`fin_voucher`、`fin_account`、`fin_cost_record`、`work_order_costs`、`material_batch_costs` | -| 26 | [仓库管理核心原则专项说明](./26-仓库管理核心原则专项说明.md) | `src/lib/warehouse-core.ts`、`src/lib/fifo-allocation.ts`、`src/domain/warehouse/value-objects/WarehouseStateMachine.ts` | `inv_inventory`、`inv_inventory_batch`、`inv_inventory_transaction`、`inv_inventory_log`、`inv_fifo_override_log` | -| — | [ERP 核心模块深化技术规格](./ERP核心模块深化技术规格.md) | `src/lib/mrp-engine.ts`、`src/lib/mrp-engine-v2.ts`、`src/lib/bom-expansion.ts`、`src/lib/cost-engine.ts`、`src/lib/general-ledger.ts` | `prd_bom`、`prd_bom_detail`、`prd_schedule`、`fin_voucher`、`fin_account`、`fin_cost_record`、`work_order_costs` | -| — | [仓库管理模块分析](./仓库管理模块分析.md) | 跨仓库模块综合分析 | `inv_inventory`、`inv_warehouse`、`inv_inventory_batch` | - -## 6. 模块间依赖关系 +| 01 | [二维码全流程追溯系统设计](./01-二维码全流程追溯系统设计.md) | `src/lib/qrcode-service.ts`、`src/app/api/qrcode/route.ts` | `qrcode_record`、`qrcode_scan_log` | +| 06 | [标准卡管理设计](./06-标准卡管理设计.md) | `src/lib/standard-card-service.ts`、`src/domain/standard-card/` | `prd_process_card`、`prd_process_card_audit` | +| 07 | [发货管理设计](./07-发货管理设计.md) | `src/lib/shipment-state-machine.ts`、`src/app/api/sales/delivery/[id]/ship/route.ts` | `shipments`、`shipment_items`、`sal_order` | +| 15 | [盘点与调拨设计](./15-盘点与调拨设计.md) | `src/app/api/warehouse/stocktaking/route.ts` | `inv_stocktaking`、`inv_stocktaking_item` | +| 21 | [物料领用设计](./21-物料领用设计.md) | `src/lib/material-requisition.ts` | `material_requisition`、`material_requisition_item` | +| 24 | [财务管理设计](./24-财务管理设计.md) | `src/lib/finance-core.ts`、`src/app/api/finance/cost/route.ts` | `fin_receivable`、`fin_payable`、`fin_cost_record` | +| 26 | [仓库管理核心原则专项说明](./26-仓库管理核心原则专项说明.md) | `src/lib/warehouse-core.ts`、`src/lib/fifo-allocation.ts` | `inv_inventory_batch`、`inv_inventory_transaction` | +| — | [仓库管理模块分析](./仓库管理模块分析.md) | 跨仓库模块综合分析 | `inv_inventory`、`inv_warehouse` | + +## 4. 模块间依赖关系 ``` 二维码追溯 ─┐ - ├──> 仓库管理(FIFO/批次) ──> 财务管理(成本归集/凭证) + ├──> 仓库管理(FIFO/批次) ──> 财务管理(成本归集) 标准卡 ──┤ ↑ ├──> 物料领用 ──────────────────────────┤ 发货管理 ──┘ │ 盘点调拨 ──────────────────────────────────────────┘ -打样订单 ──> 工艺卡(SampleProcessCard) ──> 工单 ──> 完工入库 ──> 财务凭证 -印前工装(Tool/InkFormulaVersion) ──> 工单成本归集 ``` -- 发货扫码触发库存扣减并自动生成应收单(`fin_receivable`),由 `SalesReceivableHandler` / `DeliveryReceivableHandler` 监听 `sales.shipped` / `delivery.shipped` 事件 -- 物料领用按 FIFO 推荐批次,领用后归集到工单成本(`work_order_costs`),由 `WorkOrderMaterialIssuedHandler` 监听 `workorder.material_issued` 事件 -- 盘点锁定库存(`inv_inventory.stocktaking_flag = 1`),审批通过后回写库存并记录 `inv_inventory_transaction` 流水 -- 完工入库由 `FinishOrderInventoryHandler` 监听 `finish_order.approved` 事件触发库存增加 + `ProductionFinanceHandler` 归集生产成本 - -## 7. 事件驱动架构 - -事件总线:Redis Streams(流名 `erp:domain-events`,`STREAM_MAX_LENGTH` 默认 10000)+ Outbox 两阶段(`sys_event_processed`:`checkAndMark` → `status='processing'`,`markAsProcessed` → `status='processed'`)+ `OutboxPoller` 定时回收过期 processing。无 Redis 时降级 direct publish。 +- 发货扫码触发库存扣减并自动生成应收单(`fin_receivable`) +- 物料领用按 FIFO 推荐批次,领用后归集到工单成本(`work_order_costs`) +- 盘点锁定库存(`stocktaking_flag`),审批通过后回写库存并记录流水 -36 个事件处理器位于 `src/application/handlers/`,由 `src/application/EventRegistry.ts` 统一注册。关键事件 → 处理器映射: +## 5. 关键工程约定 -| 领域事件 | 处理器 | 动作 | -|---|---|---| -| `inbound.approved` | `InventorySyncHandler` + `FinanceVoucherHandler` + `PurchaseInboundSyncHandler` + `QrCodeGenerationHandler` | 入库同步库存、生成应付凭证、关联采购单、生成二维码 | -| `sales.approved` | `SalesToWorkOrderHandler` | 销售审批后自动生成工单 | -| `sales.shipped` | `SalesReceivableHandler` | 生成应收单 | -| `delivery.shipped` | `DeliveryShippedHandler` + `DeliveryReceivableHandler` | 发货状态流转、生成应收 | -| `workorder.material_issued` | `WorkOrderMaterialIssuedHandler` + `InventorySyncHandler` | 领料扣减库存 | -| `workorder.completed` | `WorkOrderCompletedHandler` + `InkCostHandler` + `ScreenPlateCostHandler` | 完工归集油墨/网版成本 | -| `pick_order.approved` | `PickOrderInventoryHandler` | 领料单扣减批次库存 | -| `finish_order.approved` | `FinishOrderInventoryHandler` | 完工入库 | -| `formula_version.activated` | `FormulaVersionActivatedHandler` | 配方生效 | -| `tool.usage_synced` | `ToolUsageSyncHandler` + `ToolCostHandler` | 工装使用同步、成本摊销 | - -## 8. 关键工程约定 - -1. **精度**:库存与金额计算统一使用 `decimal.js`(`src/lib/decimal-utils.ts`、`src/lib/enhanced-money.ts`),禁止浮点运算 -2. **并发**:批次库存使用 `version` 乐观锁(`inv_inventory_batch` / `inv_inventory` 表均含 `version` 字段),冲突重试上限 3 次 -3. **状态机**:入库/出库/发货/工单均使用显式状态机校验流转合法性(位于 `src/domain//value-objects/`) +1. **精度**:库存与金额计算统一使用 `Decimal.js`,禁止浮点运算 +2. **并发**:库存批次使用 `version` 乐观锁,盘点/调拨使用数据库事务 +3. **状态机**:入库/出库/发货均使用显式状态机校验流转合法性 4. **追溯**:二维码通过 `parent_qr_code` 建立父子关联,`split_flag` 0/1/2 表示整料/小料/余料 -5. **业务驱动财务**:所有财务单据由业务事件自动生成,应收/应付禁止手工录入,凭证由 `FinanceVoucherHandler` 自动生成 +5. **业务驱动财务**:所有财务单据由业务事件自动生成,不支持手工录入应收/应付 6. **响应校验**:API 返回数据必须 `Array.isArray()` 校验后再调用 `.map()/.filter()` -7. **软删除**:所有业务表统一 `deleted` 字段(boolean 或 tinyint),查询时统一带 `deleted = 0` 过滤 -8. **审计字段**:`create_by` / `update_by` / `create_time` / `update_time` / `version` 为公共字段约定 - -## 9. 数据库 Schema 说明 -- **Drizzle ORM 消费表**(42 张,`src/lib/db/schema.ts`):仓库 8 + 销售 7 + 采购 4 + 财务 2 + 生产 12 + 打样模板 3 + 印前油墨/工装 6 -- **遗留表**(经 `database/vnerpdacahng_schema.sql` 管理,共 164 张):`sys_*`(用户/角色/权限/菜单/字典/日志等)、`hr_*`、`qc_*`、`crm_*`、`pur_supplier`、`bas_material`、`material_batch_costs`、`material_requisitions`、`material_requisition_items`、`inv_inventory_batch`、`inv_inventory_transaction`、`inv_inventory_log`、`inv_fifo_override_log`、`qrcode_record`、`qrcode_scan_log`、`prd_standard_card`、`prd_process_card`、`prd_process_card_audit`、`fin_account`、`fin_voucher`、`fin_voucher_line`、`work_order_costs` 等 +## 6. 数据库 Schema 说明 -> 文件头注释「35 张」已过时,实际 42 张 Drizzle 消费表。 +项目存在两套并行的表结构定义: -## 10. 迁移与脚本 +- **Drizzle ORM 定义**(`src/lib/db/schema.ts`):`inventory_batches`、`inventory_transactions`、`sales_orders`、`work_orders` 等 +- **业务表**(API 路由直接 SQL):`inv_inventory`、`inv_inventory_batch`、`inv_stocktaking`、`fin_*`、`qrcode_record` 等 -- **迁移编号**:`database/migrations/` 当前为 `001`–`060`(含两个日期命名:`20260701_add_std_master_tables.sql`、`20260701_add_gl_tables.sql`)。新增 053–060:工具列/打样增强、工单累计字段、生产表增强、排程索引、刀模字段扩展、PK 统一 bigint、collation 统一、补缺失索引、状态码统一 -- **执行**: - - 全量初始化:`pnpm setup:db`(`node scripts/setup-db.mjs`,建库 + 导入 schema + 可选 seed) - - 增量迁移:`pnpm migrate` / `migrate:down` / `migrate:status` / `migrate:create`(`npx tsx scripts/migrate.ts`,按 `;` 拆分,须幂等) - - **已废弃**:`pnpm db:generate` / `db:push` / `db:migrate` 仅打印警告 - - 备份:`pnpm backup` / `backup:list` / `backup:restore` +模块设计文档以业务表(API 实际操作的表)为准。 -## 11. 相关文档 +## 7. 相关文档 -- [业务知识](../06-业务知识/) -- [技术规范](../03-技术规范/) -- [数据库设计](../09-数据库/) -- [接口文档](../10-接口文档/) -- [开发指南](../11-开发指南/) +- [业务知识](../06-业务知识/术语表.md) +- [业务流程](../06-业务知识/业务流程.md) +- [丝网印刷基础](../06-业务知识/丝网印刷基础.md) -> 最后更新:2026-07-10 +> 最后更新:2026-06-30 diff --git "a/docs/02-\346\250\241\345\235\227\350\257\246\347\273\206\350\256\276\350\256\241/01-\344\272\214\347\273\264\347\240\201\345\205\250\346\265\201\347\250\213\350\277\275\346\272\257\347\263\273\347\273\237\350\256\276\350\256\241.md" "b/docs/02-\346\250\241\345\235\227\350\257\246\347\273\206\350\256\276\350\256\241/01-\344\272\214\347\273\264\347\240\201\345\205\250\346\265\201\347\250\213\350\277\275\346\272\257\347\263\273\347\273\237\350\256\276\350\256\241.md" index cead08c3..44ecfbee 100644 --- "a/docs/02-\346\250\241\345\235\227\350\257\246\347\273\206\350\256\276\350\256\241/01-\344\272\214\347\273\264\347\240\201\345\205\250\346\265\201\347\250\213\350\277\275\346\272\257\347\263\273\347\273\237\350\256\276\350\256\241.md" +++ "b/docs/02-\346\250\241\345\235\227\350\257\246\347\273\206\350\256\276\350\256\241/01-\344\272\214\347\273\264\347\240\201\345\205\250\346\265\201\347\250\213\350\277\275\346\272\257\347\263\273\347\273\237\350\256\276\350\256\241.md" @@ -2,100 +2,66 @@ ## 1. 概述 -二维码追溯系统是印刷 ERP 全链路数据打通的核心基础设施。从原材料入库、生产领料、工序扫码、到成品发货,所有节点通过二维码关联,形成完整的物料谱系。本文档基于 `src/lib/qrcode-service.ts` 与 `src/app/api/qrcode/` 路由现状编写,所有内容均经代码核实。 - -> 关键修正:项目内同时存在两套二维码表——`inv_qr_code`(旧表,被 `qrcode-service.ts` 使用)与 `qrcode_record` + `qrcode_scan_log`(新表,被 `app/api/qrcode/route.ts` 使用)。新表为权威,旧表逐步迁移中。 +二维码追溯系统是印刷 ERP 全链路数据打通的核心基础设施。从原材料入库、生产领料、工序扫码、到成品发货,所有节点通过二维码关联,形成完整的物料谱系。本文档基于 `src/lib/qrcode-service.ts` 与 `src/app/api/qrcode/` 路由现状编写。 ## 2. 二维码类型 -依据 `src/app/api/qrcode/route.ts` 与 `src/lib/qrcode-service.ts` 实际定义,二维码 `qr_type` 取值: - -| 类型代码 | 业务含义 | 生成入口 | 关联实体 | +| 类型代码 | 业务含义 | 前缀 | 关联实体 | |---|---|---|---| -| `material` | 原材料/物料 | 入库审批通过(`inbound.approved` → `QrCodeGenerationHandler`) | `bas_material` / `inv_material` | -| `product` | 成品 | 完工入库 | `mdm_product` | -| `batch` | 批次 | 入库生成批次(`qrcode-service.ts`) | `inv_inventory_batch` | -| `workorder` | 工单 | 工单创建 | `prd_work_order` / `prod_work_order` | -| `ink` | 油墨 | 油墨主数据 | `prd_ink` | -| `screen_plate` | 网版 | 网版主数据 | `prd_screen_plate` | -| `die` | 刀模 | 刀模主数据 | `prd_die` / `dcprint_tool`(toolType=1) | -| `shipment` | 发货单 | 发货出库 | `sal_delivery` | -| `ink_open` | 已开油墨 | 油墨开罐 | `ink_opening_record` | -| `ink_mixed` | 已调配油墨 | 油墨调配 | `ink_mixed_batch` | +| `material` | 原材料 | `MA-` | `bas_material` | +| `product` | 成品 | `PR-` | `bas_product` | +| `workorder` | 工单 | `WO-` | `prd_work_order` | +| `ink` | 油墨 | `IN-` | 油墨主数据 | +| `screen_plate` | 网版 | `SP-` | 网版主数据 | +| `die` | 刀模 | `DI-` | 刀模主数据 | +| `shipment` | 发货单 | `SH-` | `shipments` | +| `ink_open` | 已开油墨 | `IK-` | 油墨开罐记录 | +| `ink_mixed` | 已调配油墨 | `IM-` | 油墨调配记录 | ## 3. 数据存储 -### 3.1 二维码主表 `qrcode_record`(权威) - -被 `src/app/api/qrcode/route.ts` 直接 CRUD。字段(按 INSERT/SELECT 语句推断): +### 3.1 二维码主表 `qrcode_record` | 字段 | 类型 | 说明 | |---|---|---| -| `id` | bigint unsigned PK | 主键 | -| `qr_code` | varchar | 二维码字符串(唯一,格式 `前缀-UUID前16位`) | -| `qr_type` | varchar | 类型代码(见 §2) | +| `id` | bigint | 主键 | +| `qr_code` | varchar | 二维码字符串(唯一) | +| `qr_type` | varchar | 类型代码 | | `ref_id` | bigint | 关联业务主键 | | `ref_no` | varchar | 关联业务编号 | -| `batch_no` | varchar | 批次号(关联 `inv_inventory_batch.batch_no`) | +| `batch_no` | varchar | 批次号 | | `material_id` / `material_code` / `material_name` | - | 物料冗余字段 | -| `specification` | varchar | 规格 | | `quantity` / `unit` | - | 数量与单位 | -| `warehouse_id` / `warehouse_name` / `location` | - | 仓库冗余 | -| `supplier_id` / `supplier_name` | - | 供应商冗余 | -| `customer_id` / `customer_name` | - | 客户冗余 | -| `work_order_id` / `work_order_no` | - | 工单冗余 | +| `warehouse_name` / `supplier_name` / `customer_name` | - | 冗余信息 | +| `work_order_no` | varchar | 关联工单 | | `production_date` / `expiry_date` | datetime | 生产/过期时间 | -| `extra_data` | json | 扩展数据 | -| `print_count` / `last_print_time` | int / datetime | 打印次数/最后打印时间 | -| `scan_count` / `last_scan_time` | int / datetime | 扫描次数/最后扫描时间 | -| `status` | tinyint | 1=有效 / 2=已使用 / 3=已失效 / 4=批次已消耗 / 9=已作废 | -| `remark` | varchar | 备注 | -| `deleted` | boolean | 软删除 | +| `print_count` / `scan_count` | int | 打印次数/扫描次数 | +| `status` | tinyint | 1=有效 / 2=已使用 / 3=已过期 / 9=已作废 | | `create_time` | datetime | 创建时间 | ### 3.2 扫码日志表 `qrcode_scan_log` -被 `src/app/api/qrcode/route.ts` 的 `PUT ?action=scan` 写入。字段: - -| 字段 | 说明 | -|---|---| -| `qr_code` / `qr_type` | 二维码与类型 | -| `scan_type` | 扫描场景(默认 `trace`) | -| `ref_id` / `ref_no` | 关联业务主键/编号 | -| `operator_id` / `operator_name` | 操作员 | -| `scan_result` | 扫描结果(`success` / `failed`) | -| `scan_message` / `scan_data` | 扫描消息/数据 JSON | -| `device_info` | 设备信息 | - -### 3.3 旧表 `inv_qr_code`(迁移中) - -被 `src/lib/qrcode-service.ts` 使用,字段与 `qrcode_record` 类似但更精简:`qr_code`、`qr_type`、`source_type`、`source_id`、`source_no`、`material_id`、`material_code`、`material_name`、`batch_no`、`warehouse_id`、`production_date`、`expire_date`、`qr_image_url`、`status`、`remark`、`deleted`、`create_time`。 +记录每次扫码行为,字段含 `qr_code`、`scan_type`、`operator_id`、`scan_time`、`location`、`device_info`。 ## 4. 二维码内容格式 -二维码内容采用紧凑 JSON 编码(`QRCodeService.buildQRContent`,`src/lib/qrcode-service.ts`): +二维码内容采用紧凑 JSON 编码(`QRCodeService.generateContent`): ```json { "v": "1", "t": "material", "id": 1024, - "c": "MA-a1b2c3d4e5f6a7b8", + "c": "MA-20260630-0001", "mc": "M001", "b": "B20260630-001", - "w": "WO20260630001", + "w": 5, "pd": "2026-06-30", "ed": "2027-06-30" } ``` -字段含义:`v` 版本、`t` 类型、`id` 业务主键、`c` 二维码字符串、`mc` 物料编码、`b` 批次号、`w` 工单号、`pd` 生产日期、`ed` 过期日期。解析使用 `QRCodeService.parseQRContent`。 - -二维码字符串生成规则(`src/app/api/qrcode/route.ts`): -``` -qrCode = qr_type.toUpperCase().substring(0,2) + '-' + randomUUID().replace(/-/g,'').substring(0,16) -``` -例如 `MA-a1b2c3d4e5f6a7b8`、`WO-...`、`SH-...`。 +字段含义:`v` 版本、`t` 类型、`id` 业务主键、`c` 二维码字符串、`mc` 物料编码、`b` 批次号、`w` 仓库 ID、`pd` 生产日期、`ed` 过期日期。 ## 5. 核心服务 `QRCodeService` @@ -105,19 +71,13 @@ qrCode = qr_type.toUpperCase().substring(0,2) + '-' + randomUUID().replace(/-/g, | 方法 | 职责 | |---|---| -| `buildQRContent(data)` | 构造紧凑 JSON 内容字符串 | -| `parseQRContent(content)` | 反向解析二维码内容 | -| `generateQRCodeImage(content)` | 调用 `qrcode` 库生成 Base64 PNG | -| `generateTraceUrl(type, id, code)` | 生成追溯 URL:`${BASE_URL}/api/qrcode/trace?code=...` | -| `generateMaterialQR(...)` | 物料二维码,写入 `inv_qr_code`(`qr_type='material'`) | -| `generateBatchQR(...)` | 批次二维码,写入 `inv_qr_code`(`qr_type='batch'`) | -| `generateWorkOrderQR(...)` | 工单二维码,写入 `inv_qr_code`(`qr_type='workorder'`) | -| `queryByQrCode(qrCode)` | 查询二维码基础信息(关联 `inv_warehouse`) | -| `queryBatchByMaterial(materialId)` | 按物料查询所有批次二维码 | -| `invalidate(qrCode, reason)` | 作废二维码(`status='invalidated'`) | -| `getTraceHistory(materialId)` | 查询物料追溯历史(关联 `inv_inventory_transaction`) | - -### 5.2 父子关联(小料拆分) +| `generate(params)` | 生成二维码并写入 `qrcode_record`,自动拼接前缀 | +| `invalidate(id)` | 作废二维码(status→9),不可逆 | +| `recordPrint(id)` | 累加 `print_count`,写入 `label_print_records` | +| `recordScan(qr_code, ctx)` | 累加 `scan_count`,写入 `qrcode_scan_log` | +| `query({ keyword, qr_type, page })` | 分页查询,支持模糊搜索与类型过滤 | + +### 5.2 父子关联 小料拆分场景下,子二维码通过 `parent_qr_code` 字段关联父二维码,`split_flag` 标记物料形态: @@ -125,110 +85,81 @@ qrCode = qr_type.toUpperCase().substring(0,2) + '-' + randomUUID().replace(/-/g, - `1` 小料:从整料拆分得到 - `2` 余料:生产剩余回库 -拆分逻辑由 `src/lib/warehouse-core.ts` 的 `splitMaterial` 方法实现,写入 `inv_inventory_batch` 与 `material_splits` 表(见 [26-仓库管理核心原则专项说明](./26-仓库管理核心原则专项说明.md))。 - ## 6. API 接口 ### 6.1 二维码管理 `/api/qrcode` -文件:`src/app/api/qrcode/route.ts`,全部经 `withPermission` 装饰器。 - | 方法 | 路径 | 功能 | |---|---|---| -| GET | `/api/qrcode?page=&pageSize=&keyword=&qr_type=&status=` | 分页查询,LEFT JOIN `inv_inventory_batch` 取批次可用量 | -| POST | `/api/qrcode` | 生成二维码;若 `qr_type` 为 `material`/`product` 且传入 `batch_no`,事务内校验批次存在 | -| PUT | `/api/qrcode` | 多种 action:`print`(累加 `print_count`)/`scan`(累加 `scan_count` + 写 `qrcode_scan_log`)/`invalidate`(status→3)/`void`(status→9)/`batch-consumed`(批次消耗,关联二维码 status→4)/`status` & `remark` 更新 | +| GET | `/api/qrcode?page=&pageSize=&keyword=&qr_type=` | 分页查询 | +| POST | `/api/qrcode` | 生成二维码 | +| PUT | `/api/qrcode` | 更新(`action=print` 累加打印次数,`action=invalidate` 作废) | ### 6.2 追溯查询 `/api/qrcode/trace` -文件:`src/app/api/qrcode/trace/route.ts` - -支持查询入参: -- `code`:二维码字符串 -- `materialId`:物料 ID +支持两种查询入参: +- `qr_code`:直接二维码字符串(如 `MA-20260630-0001`) +- `ref_no`:业务单号(如 `SO20260630001`、`WO20260630001`) -返回 `TraceResult` 结构(见 `src/lib/qrcode-service.ts`): -- `qrCode` / `materialCode` / `materialName` / `batchNo` -- `productionDate` / `expireDate` -- `currentWarehouse` / `currentQuantity` -- `traceHistory: TraceRecord[]`:来自 `inv_inventory_transaction` 流水 -- `supplierInfo` / `customerInfo`:冗余信息 +返回字段: +- 基础信息:二维码、类型、物料、数量、状态 +- 入库信息:`inv_inbound_item`(入库单号、入库时间、供应商) +- 工单信息:`prd_work_order`(工单号、产品、客户、工序) +- 检验信息:`qc_incoming_inspection`(检验结果、检验员) +- 发货信息:`sal_delivery`(发货单号、发货时间、客户) +- 采购信息:`pur_order`(采购单号、采购时间) ### 6.3 打印 `/api/qrcode/print` -文件:`src/app/api/qrcode/print/route.ts`,POST 请求生成打印任务。 - -### 6.4 二维码载荷 `/api/qrcode/payload` - -文件:`src/app/api/qrcode/payload/route.ts`,返回二维码原始 JSON 载荷。 - -### 6.5 二维码记录列表 `/api/qrcode/records` - -文件:`src/app/api/qrcode/records/route.ts`,记录列表查询。 +POST 请求生成打印任务,写入 `label_print_records` 表,记录打印机、打印模板、份数、操作员。 ## 7. 追溯链路 ``` -采购入库 (inv_inbound_order 审批通过) - ↓ QrCodeGenerationHandler 监听 inbound.approved - ↓ 生成物料/批次二维码 qrcode_record -生产领料 (material_requisitions / prd_pick_order) - ↓ 扫码领用,记录 qrcode_scan_log (scan_type='pick') -工序扫码 (prd_work_report) - ↓ 工序流转,关联 prd_work_order / prod_work_order -小料拆分 (material_splits + inv_inventory_batch) +采购入库 (inv_inbound_item) + ↓ 生成二维码 qrcode_record +生产领料 (material_requisition) + ↓ 扫码领用,记录 qrcode_scan_log +工序扫码 (prd_production_report) + ↓ 工序流转,关联 prd_work_order +小料拆分 (material_splits) ↓ parent_qr_code 关联,split_flag=1 -完工入库 (prd_finish_order 审批通过) +成品入库 (inv_inbound_item) ↓ 生成成品二维码 发货扫码 (sal_delivery ship) - ↓ 扫码出库,扣减 inv_inventory_batch - ↓ qrcode_record.status = 2 (已使用) + ↓ 扫码出库,扣减库存 ``` -## 8. 事件驱动生成 - -二维码主要由事件总线自动生成,处理器 `QrCodeGenerationHandler`(`src/application/handlers/QrCodeGenerationHandler.ts`)由 `EventRegistry.initialize()` 注册到 `inbound.approved` 事件(见 `src/application/EventRegistry.ts`)。 - -`inbound.approved` 事件的处理器链: -1. `InventorySyncHandler`:入库同步库存 -2. `FinanceVoucherHandler`:生成应付凭证 -3. `PurchaseInboundSyncHandler`:关联采购单 -4. `QrCodeGenerationHandler`:生成物料/批次二维码 -5. `AuditLogHandler` + `CacheInvalidationHandler`:审计日志与缓存失效 - -## 9. 前端页面 +## 8. 前端页面 页面路径:`src/app/[locale]/qrcode/page.tsx` 功能模块: - **记录管理**:列表展示、按类型过滤、关键字搜索、生成、打印、作废 - **追溯查询**:输入二维码或业务单号,展示全链路追溯信息 -- **状态映射**:`statusMap` 渲染 Badge 颜色(有效/已使用/已失效/已作废) +- **状态映射**:`statusMap` 渲染 Badge 颜色(有效/已使用/已过期/已作废) - **类型映射**:`typeMap` 渲染类型中文名 -> 注:`/qrcode` 路径在生产环境被 `src/proxy.ts` 封堵为 404(仅开发/测试可访问页面,API 仍可用)。 +类型前缀识别(用于追溯查询自动判断入参): +`MA-` `PR-` `WO-` `IN-` `SP-` `DI-` `SH-` `IK-` → 按 `qr_code` 查询 +`SO` `PO` `WO` 开头 → 按 `ref_no` 查询 -## 10. 工程约定 +## 9. 工程约定 -1. 二维码生成后不可修改内容,只能作废(`status=9`)或失效(`status=3`)后重新生成 -2. 作废操作需写明 `reason`,写入 `remark` 字段 -3. 扫码日志每次扫码追加一条 `qrcode_scan_log` 记录,同时累加 `qrcode_record.scan_count` -4. 追溯查询关联多张业务表,使用 LEFT JOIN 避免数据缺失 -5. 二维码字符串遵循 `前缀-UUID前16位` 格式,保证全局唯一 -6. 批次消耗时联动更新所有关联物料/成品二维码状态为 4(`batch-consumed` action) -7. 软删除:所有查询带 `deleted = 0` 过滤 +1. 二维码生成后不可修改内容,只能作废(`status=9`)后重新生成 +2. 作废操作需要二次确认(前端 `confirm` 提示) +3. 扫码日志每次扫码追加一条记录,`scan_count` 累加 +4. 追溯查询需关联多张业务表,使用 LEFT JOIN 避免数据缺失 +5. 二维码字符串遵循 `前缀-日期-序号` 格式,保证全局唯一 -## 11. 关键文件清单 +## 10. 关键文件清单 | 文件 | 说明 | |---|---| -| `src/lib/qrcode-service.ts` | 二维码服务核心(生成/解析/查询/作废/追溯) | -| `src/app/api/qrcode/route.ts` | 二维码 CRUD API(`qrcode_record` / `qrcode_scan_log`) | +| `src/lib/qrcode-service.ts` | 二维码服务核心 | +| `src/app/api/qrcode/route.ts` | 二维码 CRUD API | | `src/app/api/qrcode/trace/route.ts` | 追溯查询 API | | `src/app/api/qrcode/print/route.ts` | 打印 API | -| `src/app/api/qrcode/payload/route.ts` | 载荷查询 API | -| `src/app/api/qrcode/records/route.ts` | 记录列表 API | -| `src/application/handlers/QrCodeGenerationHandler.ts` | 入库事件 → 二维码生成处理器 | | `src/app/[locale]/qrcode/page.tsx` | 前端页面 | -> 最后更新:2026-07-10 +> 最后更新:2026-06-30 diff --git "a/docs/02-\346\250\241\345\235\227\350\257\246\347\273\206\350\256\276\350\256\241/06-\346\240\207\345\207\206\345\215\241\347\256\241\347\220\206\350\256\276\350\256\241.md" "b/docs/02-\346\250\241\345\235\227\350\257\246\347\273\206\350\256\276\350\256\241/06-\346\240\207\345\207\206\345\215\241\347\256\241\347\220\206\350\256\276\350\256\241.md" index cd73b096..9c3ceb70 100644 --- "a/docs/02-\346\250\241\345\235\227\350\257\246\347\273\206\350\256\276\350\256\241/06-\346\240\207\345\207\206\345\215\241\347\256\241\347\220\206\350\256\276\350\256\241.md" +++ "b/docs/02-\346\250\241\345\235\227\350\257\246\347\273\206\350\256\276\350\256\241/06-\346\240\207\345\207\206\345\215\241\347\256\241\347\220\206\350\256\276\350\256\241.md" @@ -2,295 +2,188 @@ ## 1. 概述 -标准卡(Standard Card)是印刷产品的工艺标准定义文件,承载产品从原材料、油墨、工序、质量检验到工装的完整工艺规范。本模块是项目中 DDD 实践最完整的模块之一,已落地完整分层(聚合根 + 实体 + 值对象 + 领域事件 + 仓储接口 + 仓储实现 + 应用服务 + API 路由)。 - -> **概念区分**:项目中存在两套并行的"工艺标准"系统: -> - **标准卡(本模块,DDD)**:表 `prd_standard_card` + `prd_*_standard_item`,由 `src/domain/standard-card/` + `src/application/services/StandardCardApplicationService.ts` + `src/infrastructure/repositories/MysqlStandardCardRepository.ts` 实现 -> - **工艺卡(旧版,lib service)**:表 `prd_process_card` + `prd_process_card_audit`,由 `src/lib/standard-card-service.ts` 实现,支持版本对比、模板复制等,与新标准卡模块并存 - -本文档以 DDD 标准卡为主,旧版工艺卡在 §10 补充说明。 +标准卡(Process Card)是印刷产品的工艺标准定义文件,承载产品从原材料、油墨、工序、质量检验到工装的完整工艺规范。本模块已落地 DDD 完整分层(聚合根 + 应用服务 + 基础设施),是项目中 DDD 实践最完整的模块。本文档基于 `src/lib/standard-card-service.ts`、`src/domain/standard-card/`、`src/application/services/StandardCardApplicationService.ts` 现状编写。 ## 2. 架构分层 ``` -src/app/api/standard-card/route.ts 表现层(withPermission) -src/app/api/standard-card/by-material/route.ts -src/app/api/standard-card/action/route.ts +src/app/api/standard-card/route.ts 表现层 ↓ -src/application/services/StandardCardApplicationService 应用层(用例编排 + 事务 + Outbox) +src/application/services/ + StandardCardApplicationService.ts 应用层(用例编排) ↓ -src/domain/standard-card/aggregates/StandardCard.ts 领域层(聚合根) -src/domain/standard-card/entities/*.ts 领域实体(明细项) -src/domain/standard-card/value-objects/*.ts 值对象(状态/类型) -src/domain/standard-card/events/StandardCardEvents.ts 领域事件 -src/domain/standard-card/repositories/IStandardCardRepository.ts 仓储接口 +src/domain/standard-card/aggregates/ + StandardCard.ts 领域层(聚合根) ↓ -src/infrastructure/repositories/MysqlStandardCardRepository.ts 基础设施层(仓储实现) +src/lib/standard-card-service.ts 基础设施层(仓储实现) ↓ -prd_standard_card / prd_color_standard_item / prd_process_standard_item / -prd_quality_standard_item / prd_standard_card_material / prd_standard_card_ink / -prd_standard_card_tooling / prd_standard_card_attachment / -prd_standard_card_version_log 数据库 +prd_process_card / prd_process_card_audit 数据库 ``` ## 3. 数据存储 -### 3.1 标准卡主表 `prd_standard_card` - -字段(依据 `MysqlStandardCardRepository.ts` SELECT/UPDATE 与 `StandardCardProps`): - -| 字段 | 类型 | 说明 | -|---|---|---| -| `id` | bigint unsigned PK | 主键 | -| `code` | varchar | 标准卡编号(唯一,前缀按类型:`SCC`/`SCP`/`SCQ`/`SCZ`) | -| `version` | varchar | 版本号(如 `V1.0`) | -| `name` | varchar | 标准卡名称 | -| `type` | varchar | 类型:`color` / `process` / `quality` / `comprehensive` | -| `material_id` / `material_name` | bigint / varchar | 关联物料 | -| `customer_id` / `customer_name` | bigint / varchar | 关联客户 | -| `spec` | varchar | 规格 | -| `status` | varchar | 状态:`draft` / `auditing` / `approved` / `confirmed` / `obsolete` | -| `effective_date` / `expiry_date` | date | 生效/失效日期 | -| `parent_version_id` | bigint | 父版本 ID(版本树) | -| `is_current` | boolean | 是否当前版本 | -| `is_obsolete` | boolean | 是否已作废 | -| `is_locked` | boolean | 是否锁定 | -| `change_description` | varchar | 变更说明 | -| `obsolete_reason` / `obsolete_by` / `obsolete_at` | - | 作废信息 | -| `quality_requirement` / `material_requirement` / `ink_requirement` / `tooling_requirement` / `process_requirement` | text | 各维度要求 | -| `create_user` / `audit_user` / `approve_user` / `confirm_user` | bigint | 各阶段操作人 | -| `create_time` / `update_time` | datetime | 时间戳 | -| `deleted` | boolean | 软删除 | - -### 3.2 明细表 - -| 表 | 用途 | 关联实体 | -|---|---|---| -| `prd_color_standard_item` | 色相项目 | `ColorStandardItem` | -| `prd_process_standard_item` | 工序项目(按 `process_order` 排序) | `ProcessStandardItem` | -| `prd_quality_standard_item` | 质量检验项目 | `QualityStandardItem` | -| `prd_standard_card_material` | 物料清单(BOM),含 `unit_consumption`、`loss_rate` | `StandardCardMaterial` | -| `prd_standard_card_ink` | 油墨配方 | `StandardCardInk` | -| `prd_standard_card_tooling` | 工装(网版、刀模等) | `StandardCardTooling` | -| `prd_standard_card_attachment` | 附件 | `StandardCardAttachment` | - -### 3.3 版本变更日志 `prd_standard_card_version_log` - -记录每次版本变更,关联实体 `VersionChangeLog`: +### 3.1 工艺卡主表 `prd_process_card` | 字段 | 说明 | |---|---| -| `standard_card_id` | 关联标准卡 | -| `version` | 版本号 | -| `change_type` | `create` / `update` / `obsolete` / `restore` | -| `change_content` | 变更内容描述 | -| `changed_by` / `changed_by_name` | 操作人 | -| `changed_at` | 操作时间 | +| `id` | 主键 | +| `card_no` | 标准卡编号 | +| `card_type` | 类型:`sample`(样品)/ `mass`(量产) | +| `product_id` / `product_code` / `product_name` | 产品信息 | +| `customer_id` / `customer_name` | 客户信息 | +| `version` | 版本号(整数递增) | +| `status` | 状态:`draft`/`approved`/`obsolete` | +| `specifications` | JSON:工艺规格详情 | +| `color_items` | JSON:色相项目 | +| `process_items` | JSON:工序项目 | +| `quality_items` | JSON:质量检验项目 | +| `materials` | JSON:物料清单(BOM) | +| `inks` | JSON:油墨配方 | +| `toolings` | JSON:工装(网版、刀模等) | +| `created_by` / `approved_by` | 创建人/审批人 | +| `create_time` / `update_time` | 时间戳 | + +### 3.2 审计表 `prd_process_card_audit` + +记录每次版本变更,用于版本对比与回溯: +- `card_id`、`version`、`action`(create/update/approve/convert) +- `change_snapshot`(变更前快照 JSON) +- `operator_id`、`operate_time`、`remark` ## 4. 聚合根 `StandardCard` 文件:`src/domain/standard-card/aggregates/StandardCard.ts` -聚合根封装标准卡的业务规则,持有以下子实体集合(私有 `_` 前缀字段): -- `_colorItems: ColorStandardItem[]` -- `_processItems: ProcessStandardItem[]` -- `_qualityItems: QualityStandardItem[]` -- `_materials: StandardCardMaterial[]` -- `_inks: StandardCardInk[]` -- `_toolings: StandardCardTooling[]` -- `_attachments: StandardCardAttachment[]` -- `_versionLogs: VersionChangeLog[]` - -核心业务规则(构造时 `validateCreation`): -- `code` 必须非空 -- `version` 必须非空 -- `name` 必须非空 -- `type` 必须为合法枚举 -- 状态流转必须合法(调用 `canTransition`) - -## 5. 值对象 - -### 5.1 `StandardCardStatus` - -文件:`src/domain/standard-card/value-objects/StandardCardStatus.ts` - -状态枚举:`DRAFT`(草稿)→ `AUDITING`(待审核)→ `APPROVED`(已批准)→ `CONFIRMED`(已确认)→ `OBSOLETE`(已作废) - -状态机配置: - -| 当前状态 | 允许流转到 | 标签 | 颜色 | -|---|---|---|---| -| `draft` | `auditing` | 草稿 | gray | -| `auditing` | `approved`, `draft` | 待审核 | orange | -| `approved` | `confirmed`, `obsolete` | 已批准 | blue | -| `confirmed` | `obsolete` | 已确认 | green | -| `obsolete` | (终态) | 已作废 | red | - -### 5.2 `StandardCardType` - -文件:`src/domain/standard-card/value-objects/StandardCardType.ts` - -类型枚举与编号前缀: - -| 类型 | 中文 | 前缀 | -|---|---|---| -| `color` | 颜色标准卡 | `SCC` | -| `process` | 工艺标准卡 | `SCP` | -| `quality` | 质量标准卡 | `SCQ` | -| `comprehensive` | 综合标准卡 | `SCZ` | - -### 5.3 状态流转动作 - -`statusTransitionActions` 定义合法流转所需动作与角色: - -| from | to | action | requiredRole | -|---|---|---|---| -| `draft` | `auditing` | `submit` | `process_engineer`, `admin` | -| `auditing` | `approved` | `approve` | `process_manager`, `admin` | -| `auditing` | `draft` | `reject` | `process_manager`, `admin` | -| `approved` | `confirmed` | `confirm` | `general_manager`, `admin` | -| `approved` | `obsolete` | `obsolete` | `process_manager`, `admin` | -| `confirmed` | `obsolete` | `obsolete` | `process_manager`, `admin` | - -## 6. 领域事件 +聚合根封装标准卡的业务规则: +- 状态校验:仅 `draft` 状态可编辑,`approved` 不可修改 +- 版本递增:每次保存生成新版本号 +- 样品转量产:`card_type` 从 `sample` 转为 `mass` 时校验完整性 -文件:`src/domain/standard-card/events/StandardCardEvents.ts` - -| 事件类 | 触发时机 | -|---|---| -| `StandardCardCreatedEvent` | 创建标准卡 | -| `StandardCardSubmittedEvent` | 提交审核(draft → auditing) | -| `StandardCardApprovedEvent` | 审批通过(auditing → approved) | -| `StandardCardConfirmedEvent` | 确认(approved → confirmed) | -| `StandardCardObsoletedEvent` | 作废 | - -事件由 `StandardCardApplicationService` 通过 `getDomainEventOutbox()` 写入 Outbox(`domain_event_outbox` 表),再由事件总线分发。处理器 `StandardCardHandlers`(`src/application/handlers/StandardCardHandlers.ts`)由 `EventRegistry` 注册。 - -## 7. 应用服务 +## 5. 应用服务 文件:`src/application/services/StandardCardApplicationService.ts` | 方法 | 职责 | |---|---| -| `create(dto)` | 创建标准卡(状态 `draft`,版本 `V1.0`,写主表 + 明细 + 版本日志 + 发布 `StandardCardCreatedEvent`) | -| `update(dto)` | 更新标准卡(仅 `draft` 状态可改,写版本日志 + 发布事件) | -| `submit(id, userId)` | 提交审核(draft → auditing) | -| `approve(id, userId)` | 审批通过(auditing → approved) | -| `reject(id, userId)` | 审批驳回(auditing → draft) | -| `confirm(id, userId)` | 确认(approved → confirmed) | -| `obsolete(id, reason, userId)` | 作废(→ obsolete) | -| `getById(id)` / `getByCode(code)` | 查询主表 | -| `getDetailItems(id)` | 查询所有明细项(色相/工序/质量/物料/油墨/工装/附件) | -| `getCurrentByMaterialId(materialId)` | 按物料查询当前版本(`is_current = 1`) | -| `query(filter)` | 分页查询,支持 `code` / `name` / `type` / `status` / `materialId` / `customerId` / `isCurrent` / `isObsolete` / 时间范围 | - -## 8. 仓储实现 +| `create(input)` | 创建标准卡,初始化版本为 1 | +| `update(id, input)` | 更新草稿状态的标准卡 | +| `approve(id, approverId)` | 审批通过,状态变更为 `approved` | +| `obsolete(id)` | 作废标准卡 | +| `getById(id)` | 查询标准卡详情 | +| `list(filter)` | 分页查询 | +| `compareVersions(id, v1, v2)` | 版本对比,返回差异字段 | +| `convertSampleToMass(id)` | 样品卡转量产卡,复制并生成新记录 | + +## 6. 仓储服务 `StandardCardService` + +文件:`src/lib/standard-card-service.ts` + +提供数据持久化能力,主要方法: +- `save(card)`:写入 `prd_process_card`,同时写入 `prd_process_card_audit` +- `findById(id)`:查询主表并解析 JSON 字段 +- `findByProduct(productId)`:按产品查询有效标准卡 +- `findVersions(cardId)`:查询版本历史 + +## 7. 工艺规格详情 + +### 7.1 色相项目 `color_items` + +```json +[ + { + "color_name": "Pantone 185C", + "color_code": "185C", + "ink_type": "solvent", + "density": 1.2, + "网点": 75 + } +] +``` -文件:`src/infrastructure/repositories/MysqlStandardCardRepository.ts`,实现 `IStandardCardRepository` 接口。 +### 7.2 工序项目 `process_items` + +```json +[ + { + "process_code": "P001", + "process_name": "调墨", + "sequence": 1, + "standard_time": 30, + "equipment": "调墨机", + "parameters": { "temperature": 25 } + } +] +``` -主要方法: -- `findById(id)` / `findByCode(code)` / `findByMaterialId(materialId, includeObsolete)` / `findCurrentByMaterialId(materialId)` -- `save(card)` / `update(card)` / `delete(id)` / `obsolete(id, reason, userId)` -- `query(filter)`:分页查询,按 `is_current DESC, version DESC, create_time DESC` 排序 -- `findColorItems(standardCardId)` / `findProcessItems` / `findQualityItems` / `findMaterials` / `findInks` / `findToolings` / `findAttachments` / `findVersionLogs` -- `saveColorItems` / `saveProcessItems` / ... / `saveVersionLog`:明细保存(先 DELETE 再批量 INSERT) +### 7.3 质量检验项目 `quality_items` + +```json +[ + { + "inspection_item": "套印精度", + "standard_value": "±0.1mm", + "inspection_method": "显微镜", + "sampling_rate": 5 + } +] +``` -## 9. API 接口 +### 7.4 物料清单 `materials` + +```json +[ + { + "material_id": 101, + "material_code": "M001", + "material_name": "PET薄膜", + "quantity": 100, + "unit": "kg", + "loss_rate": 0.03 + } +] +``` -### 9.1 `/api/standard-card` +## 8. API 接口 文件:`src/app/api/standard-card/route.ts` | 方法 | 路径 | 功能 | |---|---|---| -| GET | `/api/standard-card?id=` | 按 ID 查询详情(含明细) | -| GET | `/api/standard-card?code=` | 按编号查询 | -| GET | `/api/standard-card?materialId=¤t=true` | 按物料查询当前版本 | -| GET | `/api/standard-card?code=&name=&type=&status=&materialId=&customerId=&isCurrent=&isObsolete=&page=&pageSize=` | 分页查询 | +| GET | `/api/standard-card?page=&keyword=&status=&card_type=` | 分页查询 | +| GET | `/api/standard-card?id=` | 查询详情 | | POST | `/api/standard-card` | 创建标准卡 | -| PUT | `/api/standard-card` | 更新或状态流转(`action=submit/approve/reject/confirm/obsolete`) | - -### 9.2 `/api/standard-card/by-material` - -按物料查询标准卡(含历史版本)。 - -### 9.3 `/api/standard-card/action` +| PUT | `/api/standard-card` | 更新或审批(`action=update/approve/obsolete/convert`) | +| GET | `/api/standard-card/compare?id=&v1=&v2=` | 版本对比 | -状态流转专用端点(`action=submit/approve/reject/confirm/obsolete`)。 - -### 9.4 `/api/standard-card/scan`、`/api/standard-card/check-deviation`、`/api/standard-card/by-work-order/[work_order_id]`、`/api/standard-card/approve` - -由 fact-base §5 列出的辅助端点:扫码核验、偏差检查、按工单查询、审批。 - -## 10. 业务流程 +## 9. 业务流程 ``` 新产品打样 - ↓ create 创建标准卡 (status=draft, version=V1.0, is_current=1) -工艺工程师编辑(多次 update,每次写 version_log) - ↓ submit (draft → auditing) -工艺经理审批 - ↓ approve (auditing → approved) 或 reject (auditing → draft) -总经理确认 - ↓ confirm (approved → confirmed) -关联生产工单(prd_work_order / prod_work_order) -工艺迭代时 obsolete 旧版本,新建新版本(version=V2.0,is_current=1,旧版本 is_current=0) + ↓ 创建样品卡 (card_type=sample, version=1) +工艺验证(多次 update,每次生成审计记录) + ↓ approve 审批通过 +样品生产验证 + ↓ convertSampleToMass 转量产卡 +量产卡 (card_type=mass) + ↓ 关联生产工单 prd_work_order +工艺迭代时 obsolete 旧版本,新建新版本 ``` -## 11. 旧版工艺卡系统(`src/lib/standard-card-service.ts`) - -文件:`src/lib/standard-card-service.ts`,操作 `prd_process_card` 与 `prd_process_card_audit` 表,与新标准卡模块并存。 - -### 11.1 工艺卡主表 `prd_process_card` - -字段:`id`、`card_no`、`customer_id`/`customer_name`/`customer_code`、`product_name`、`version`、`tech_params`(JSON,含 `print_params`/`screen_params`/`ink_params`/`cutting_params`/`quality_standards` 五维度)、`status`(`draft`/`pending_approval`/`approved`/`obsolete`)、`creator_id`、`create_time`/`update_time`、`deleted`。 +## 10. 工程约定 -### 11.2 工艺卡审计表 `prd_process_card_audit` +1. **JSON 字段**:所有工艺项目以 JSON 存储,应用层负责解析与校验 +2. **版本不可变**:已审批版本不可修改,迭代需新建版本 +3. **审计完整**:所有变更(创建/更新/审批/转换)写入 `prd_process_card_audit` +4. **样品与量产分离**:`card_type` 区分,转换时复制生成新记录 +5. **聚合根一致性**:业务规则集中在 `StandardCard` 聚合根,应用服务仅编排 -字段:`card_id`、`version`、`action`(`create`/`update`/`approve`/`convert`)、`operator`、`change_description`、`tech_params`(变更前快照 JSON)、`create_time`。 - -### 11.3 工艺卡模板表 `prd_process_card_templates` - -字段:`id`、`name`、`category`、`tech_params`(JSON)、`description`、`deleted`。 - -### 11.4 主要方法 - -| 方法 | 职责 | -|---|---| -| `createCard(input)` | 创建工艺卡,初始化版本 `V1.0`,写 `prd_process_card` + `prd_process_card_audit` | -| `updateCard(id, input)` | 更新草稿状态工艺卡,写审计 | -| `approveCard(id, approver)` | 审批通过 | -| `obsoleteCard(id)` | 作废 | -| `getCard(id)` | 查询详情,解析 `tech_params` JSON | -| `getCardVersions(cardId)` | 查询版本历史 | -| `compareVersions(cardId, v1, v2)` | 版本对比,返回 `VersionDiff[]` | -| `copyCardFrom(sourceCardId, newCardNo)` | 从已有卡片复制创建 | -| `createCardFromTemplate(templateId, input)` | 从模板创建 | - -## 12. 工程约定 - -1. **JSON 字段**:工艺卡的 `tech_params` 以 JSON 存储,应用层负责解析与校验 -2. **版本不可变**:标准卡已确认版本不可修改,迭代需新建版本(`version` 递增,旧版本 `is_current=0`) -3. **审计完整**:所有变更(创建/更新/审批/作废)写入 `prd_standard_card_version_log` 或 `prd_process_card_audit` -4. **聚合根一致性**:业务规则集中在 `StandardCard` 聚合根,应用服务仅编排 -5. **事件 Outbox**:状态变更通过 `domain_event_outbox` 表持久化,再由事件总线分发,保证最终一致性 -6. **角色校验**:状态流转动作含 `requiredRole`,由 API 层权限装饰器校验 -7. **当前版本唯一**:同一物料同一时间仅一个 `is_current=1` 的标准卡 - -## 13. 关键文件清单 +## 11. 关键文件清单 | 文件 | 说明 | |---|---| | `src/domain/standard-card/aggregates/StandardCard.ts` | 聚合根 | -| `src/domain/standard-card/entities/*.ts` | 7 个明细实体(Color/Process/Quality/Material/Ink/Tooling/Attachment/VersionChangeLog) | -| `src/domain/standard-card/value-objects/StandardCardStatus.ts` | 状态机 | -| `src/domain/standard-card/value-objects/StandardCardType.ts` | 类型枚举与前缀 | -| `src/domain/standard-card/events/StandardCardEvents.ts` | 5 个领域事件 | -| `src/domain/standard-card/repositories/IStandardCardRepository.ts` | 仓储接口 | | `src/application/services/StandardCardApplicationService.ts` | 应用服务 | -| `src/application/handlers/StandardCardHandlers.ts` | 事件处理器 | -| `src/infrastructure/repositories/MysqlStandardCardRepository.ts` | 仓储实现 | +| `src/lib/standard-card-service.ts` | 仓储实现 | | `src/app/api/standard-card/route.ts` | API 路由 | -| `src/lib/standard-card-service.ts` | 旧版工艺卡服务(`prd_process_card`) | -> 最后更新:2026-07-10 +> 最后更新:2026-06-30 diff --git "a/docs/02-\346\250\241\345\235\227\350\257\246\347\273\206\350\256\276\350\256\241/07-\345\217\221\350\264\247\347\256\241\347\220\206\350\256\276\350\256\241.md" "b/docs/02-\346\250\241\345\235\227\350\257\246\347\273\206\350\256\276\350\256\241/07-\345\217\221\350\264\247\347\256\241\347\220\206\350\256\276\350\256\241.md" index 66b07e91..a2bcb36f 100644 --- "a/docs/02-\346\250\241\345\235\227\350\257\246\347\273\206\350\256\276\350\256\241/07-\345\217\221\350\264\247\347\256\241\347\220\206\350\256\276\350\256\241.md" +++ "b/docs/02-\346\250\241\345\235\227\350\257\246\347\273\206\350\256\276\350\256\241/07-\345\217\221\350\264\247\347\256\241\347\220\206\350\256\276\350\256\241.md" @@ -2,244 +2,161 @@ ## 1. 概述 -发货管理是销售流程的最后一环,承接销售订单(`sal_order`)与成品库存,通过扫码出库实现精确到批次的发货追溯。发货完成后自动生成应收单(`fin_receivable`),驱动财务核算。本文档基于 `src/domain/shipment/value-objects/ShipmentStateMachine.ts`、`src/domain/sales/aggregates/Delivery.ts`、`src/app/api/sales/delivery/` 路由现状编写。 - -> **重要说明**:项目内发货模块存在两套并行的实现路径: -> 1. **DDD 路径**(主路由 `/api/sales/delivery`):表 `sal_delivery` + `sal_delivery_detail`,聚合根 `Delivery`,状态字段为 `tinyint`(0=待发货、1=已发货等) -> 2. **扫码发货路径**(子路由 `/api/sales/delivery/[id]/ship`):表 `shipments` + `shipment_items` + `wh_inventory`,状态字段为数字(1=草稿、2=待审批、3=待发货、4=部分发货、5=已发货、6=已取消),由 `ShipmentStateMachine` 管理更细粒度状态 -> -> 两套并存源于历史迁移,下文分别说明。 +发货管理是销售流程的最后一环,承接销售订单(`sal_order`)与成品库存,通过扫码出库实现精确到批次的发货追溯。发货完成后自动生成应收单,驱动财务核算。本文档基于 `src/lib/shipment-state-machine.ts`、`src/app/api/sales/delivery/[id]/ship/route.ts` 现状编写。 ## 2. 数据存储 -### 2.1 发货主表 `sal_delivery`(DDD 路径) - -由 `src/lib/db/schema.ts` 定义为 Drizzle 消费表,字段: - -| 字段 | 类型 | 说明 | -|---|---|---| -| `id` | serial PK | 主键 | -| `delivery_no` | varchar(50) UNIQUE | 发货单号 | -| `delivery_date` | date | 发货日期 | -| `order_id` / `order_no` | bigint / varchar | 关联销售订单 | -| `customer_id` / `customer_name` | bigint / varchar | 客户 | -| `warehouse_id` | bigint | 发货仓库 | -| `total_amount` / `total_qty` | decimal(18,4) | 总金额/总数量 | -| `logistics_company` / `tracking_no` | varchar | 物流信息 | -| `contact_name` / `contact_phone` / `delivery_address` | varchar | 收货信息 | -| `status` | tinyint | 状态(1=待发货,2=已发货,3=已签收,4=已取消) | -| `ship_by` / `ship_time` | bigint / datetime | 发货人/时间 | -| `sign_by` / `sign_time` / `sign_status` | bigint / datetime / tinyint | 签收信息 | -| `create_by` / `update_by` | bigint | 操作人 | -| `version` | int | 乐观锁 | -| `deleted` | boolean | 软删除 | -| `create_time` / `update_time` | datetime | 时间戳 | - -### 2.2 发货明细 `sal_delivery_detail` - -| 字段 | 说明 | -|---|---| -| `delivery_id` | 关联发货单 | -| `line_no` | 行号 | -| `material_id` / `material_name` | 产品信息 | -| `quantity` / `unit` | 应发数量/单位 | -| `unit_price` / `amount` | 单价/金额 | -| `qr_code` | 扫码关联的二维码 | -| `batch_no` | 批次号 | - -### 2.3 扫码发货主表 `shipments`(扫码路径) - -由 `src/app/api/sales/delivery/[id]/ship/route.ts` 使用,字段(按 SQL 推断): +### 2.1 发货单 `shipments` | 字段 | 说明 | |---|---| | `id` | 主键 | | `shipment_no` | 发货单号 | -| `sales_order_id` | 关联销售订单 ID | -| `customer_id` | 客户 | -| `status` | 数字状态:1=草稿 / 2=待审批 / 3=待发货 / 4=部分发货 / 5=已发货 / 6=已取消 | +| `order_id` | 关联销售订单 ID | +| `order_no` | 关联销售订单号 | +| `customer_id` / `customer_name` | 客户信息 | +| `warehouse_id` / `warehouse_name` | 发货仓库 | +| `status` | 发货状态(见状态机) | | `total_quantity` / `shipped_quantity` | 应发/已发数量 | -| `logistics_company` / `tracking_no` | 物流信息 | -| `ship_time` | 发货时间 | -| `deleted` / `create_time` | 软删除/创建时间 | +| `carrier` / `tracking_no` / `shipping_cost` | 物流信息 | +| `shipping_date` / `delivery_date` | 发货/送达日期 | +| `created_by` / `approved_by` | 创建人/审批人 | +| `create_time` / `update_time` | 时间戳 | -### 2.4 扫码发货明细 `shipment_items` +### 2.2 发货明细 `shipment_items` | 字段 | 说明 | |---|---| +| `id` | 主键 | | `shipment_id` | 关联发货单 | -| `material_id` | 产品 | -| `quantity` | 应发数量 | -| `shipped_quantity` | 已发数量 | +| `order_item_id` | 关联订单明细 | +| `product_id` / `product_code` / `product_name` | 产品信息 | | `qr_code` | 扫码关联的二维码 | +| `batch_no` | 批次号 | +| `quantity` | 发货数量 | +| `unit` | 单位 | +| `scanned` | 是否已扫码 | ## 3. 发货状态机 -文件:`src/domain/shipment/value-objects/ShipmentStateMachine.ts` +文件:`src/lib/shipment-state-machine.ts` 发货单共 11 个状态,流转规则严格校验: | 状态 | 中文 | 允许流转到 | 允许操作 | |---|---|---|---| -| `draft` | 草稿 | `pending_review`, `cancelled` | `edit`, `submit_for_approval`, `delete` | -| `pending_review` | 待审批 | `approved`, `draft`, `cancelled` | `approve`, `reject`, `view` | -| `approved` | 待发货 | `picking`, `cancelled` | `start_picking`, `cancel`, `view` | -| `picking` | 拣货中 | `picked`, `partial_ship`, `approved` | `complete_pick`, `partial_pick`, `cancel`, `view` | -| `picked` | 已拣货 | `shipped`, `picking`, `cancelled` | `confirm_ship`, `cancel`, `print_label`, `view` | -| `partial_ship` | 部分发货 | `shipped`, `picking`, `picked`, `cancelled` | `continue_ship`, `cancel`, `view` | -| `shipped` | 已发货 | `in_transit`, `delivered`, `returned` | `update_tracking`, `confirm_delivery`, `process_return`, `view` | -| `in_transit` | 运输中 | `delivered`, `shipped`, `returned` | `update_location`, `confirm_delivery`, `report_issue`, `view` | -| `delivered` | 已签收 | (终态) | `archive`, `generate_receipt`, `view` | -| `returned` | 已退货 | `draft`, `cancelled` | `create_return_order`, `dispose`, `view` | -| `cancelled` | 已取消 | `draft` | `reactivate`, `view` | - -状态机校验通过 `ShipmentStateMachine.canTransition(from, to)` 与 `validateTransition(currentStatus, targetStatus, operation)` 方法,非法流转返回错误对象。 - -> 注:扫码发货路径(`shipments` 表)使用简化的 6 状态数字编码(1-6),与上述 11 状态机不直接对应。 - -## 4. 聚合根 `Delivery` +| `draft` | 草稿 | pending_review, cancelled | edit, delete, submit | +| `pending_review` | 待审核 | approved, cancelled | audit, cancel | +| `approved` | 已审核 | picking, cancelled | start_picking, cancel | +| `picking` | 拣货中 | picked, partial_ship | scan, complete_picking | +| `picked` | 拣货完成 | partial_ship, shipped | ship, partial_ship | +| `partial_ship` | 部分发货 | shipped, picked | ship, back_to_picked | +| `shipped` | 已发货 | in_transit | start_transit | +| `in_transit` | 运输中 | delivered, returned | deliver, return | +| `delivered` | 已送达 | (终态) | view | +| `returned` | 已退货 | (终态) | view | +| `cancelled` | 已取消 | (终态) | view | -文件:`src/domain/sales/aggregates/Delivery.ts` +状态机校验通过 `ShipmentStateMachine.canTransition(from, to)` 方法,非法流转抛出错误。 -聚合根持有 `_lines: DeliveryLine[]`(发货明细),构造时校验: -- `orderId` 必须大于 0 -- `customerId` 必须大于 0 -- `warehouseId` 必须大于 0 -- `lines` 不能为空 +## 4. 扫码发货流程 -`Delivery.create` 会计算 `totalAmount = Σ(line.amount)`,并发布 `DeliveryCreatedEvent`。 +文件:`src/app/api/sales/delivery/[id]/ship/route.ts` -### 4.1 领域事件 +### 4.1 拣货扫码 -文件:`src/domain/sales/events/DeliveryEvents.ts` - -| 事件类 | 触发时机 | -|---|---| -| `DeliveryCreatedEvent` | 创建发货单 | -| `DeliveryShippedEvent` | 发货出库(`delivery.shipped`) | -| `DeliverySignedEvent` | 签收 | -| `DeliveryCancelledEvent` | 取消 | - -事件处理器(`src/application/handlers/`,由 `EventRegistry` 注册到 `delivery.shipped`): -- `DeliveryShippedHandler`:发货状态流转、库存扣减 -- `DeliveryReceivableHandler`:生成应收单 -- `DeliveryCancelledHandler`:取消时回退库存与二维码状态 - -## 5. 扫码发货流程 - -文件:`src/app/api/sales/delivery/[id]/ship/route.ts`,POST 请求,`withPermission` 装饰。 +``` +1. 发货单进入 picking 状态 +2. 仓管员扫描成品二维码 (qr_code) +3. 系统校验: + - qr_code 在 qrcode_record 中存在且状态为有效 + - 关联 product_id 与发货明细匹配 + - 关联 batch_no 在 wh_inventory 中库存充足 +4. 写入 shipment_items 记录(scanned=1) +5. 累加 shipped_quantity +6. 当 shipped_quantity >= total_quantity 时,自动流转到 picked +``` -### 5.1 流程 +### 4.2 发货出库 ``` -1. 接收 items[{material_id, qr_code, quantity}]、logistics_company、tracking_no -2. 查询 shipments 表确认发货单存在且 status=3(待发货) -3. 校验每个 qr_code: - - 在 qrcode_record 中存在 - - status 不为 'shipped' -4. 逐条执行: - - UPDATE shipment_items SET shipped_quantity += ?, qr_code = ? - - UPDATE wh_inventory SET quantity -= ?(扣减库存) - - UPDATE qrcode_record SET status='shipped', shipped_at=NOW(), shipment_id=? -5. 累加 totalShippedQty -6. 计算 newStatus = newShippedQty >= total_quantity ? 5 : 4 -7. UPDATE shipments SET shipped_quantity=?, status=?, ship_time=NOW(), logistics_company=..., tracking_no=... -8. UPDATE sal_order SET shipped_qty += ?, status = (全部发完 ? 4 : status) -9. 若 newStatus=5(已发货),INSERT INTO fin_receivable 生成应收单 +1. 发货单从 picked 流转到 shipped +2. 扣减 wh_inventory 库存(按扫码批次) +3. 更新 qrcode_record.status = 2(已使用) +4. 写入 inv_inventory_log 出库流水 +5. 更新 sal_order 发货状态(部分发货/全部发货) +6. 自动生成 fin_receivable 应收单 ``` -### 5.2 关键 SQL 示意 +### 4.3 关键 SQL 示意 ```sql --- 扣减库存(wh_inventory 而非 inv_inventory_batch) +-- 扣减库存 UPDATE wh_inventory -SET quantity = quantity - ?, updated_at = NOW() -WHERE qr_code = ?; - --- 更新二维码状态 -UPDATE qrcode_record -SET status = 'shipped', shipped_at = NOW(), shipment_id = ? -WHERE qr_code = ?; +SET quantity = quantity - ?, update_time = NOW() +WHERE qr_code = ? AND warehouse_id = ?; -- 生成应收单 -INSERT INTO fin_receivable ( - receivable_no, order_id, order_type, customer_id, - amount, status, create_time -) VALUES (?, ?, 'sales', ?, 0, 1, NOW()); +INSERT INTO fin_receivable (receivable_no, source_type, source_no, customer_id, amount, status, create_time) +VALUES (?, 'shipment', ?, ?, ?, 'pending', NOW()); ``` -> 注:扫码路径使用 `wh_inventory` 表(旧表名,与 `inv_inventory` 并存),应收单 `amount` 初始为 0,由后续对账或 `DeliveryReceivableHandler` 补金额。 +## 5. API 接口 -## 6. API 接口 - -| 方法 | 路径 | 功能 | 表 | -|---|---|---|---| -| GET | `/api/sales/delivery` | 发货单列表 | `sal_delivery` LEFT JOIN `crm_customer` | -| GET | `/api/sales/delivery?id=` | 发货单详情(含 `sal_delivery_detail`) | `sal_delivery` + `sal_delivery_detail` | -| POST | `/api/sales/delivery` | 创建发货单(从销售订单) | `sal_delivery` | -| PUT | `/api/sales/delivery` | 状态流转 | `sal_delivery` | -| POST | `/api/sales/delivery/[id]/ship` | 扫码发货(`shipments` 路径) | `shipments` + `shipment_items` + `wh_inventory` + `qrcode_record` + `sal_order` + `fin_receivable` | -| POST | `/api/sales/delivery/re-ship` | 重新发货 | - | -| POST | `/api/sales/delivery/partial` | 部分发货 | - | +| 方法 | 路径 | 功能 | +|---|---|---| +| GET | `/api/sales/delivery` | 发货单列表 | +| GET | `/api/sales/delivery/[id]` | 发货单详情 | +| POST | `/api/sales/delivery` | 创建发货单(从销售订单) | +| PUT | `/api/sales/delivery/[id]` | 状态流转(`action=submit/audit/cancel/start_picking/...`) | +| POST | `/api/sales/delivery/[id]/ship` | 扫码发货 | +| POST | `/api/sales/delivery/[id]/deliver` | 确认送达 | +| POST | `/api/sales/delivery/[id]/return` | 退货处理 | -## 7. 部分发货机制 +## 6. 部分发货机制 当订单数量大于当前可发库存时,支持部分发货: 1. `picked` → `partial_ship`:本次发货部分明细 -2. 剩余明细保持原状态,可继续后续发货 +2. 剩余明细保持 `picked` 状态,可继续后续发货 3. `partial_ship` → `shipped`:所有明细发完,整体流转 4. 部分发货不修改订单状态为"全部发货",仅更新已发数量 -扫码路径中,`newStatus = newShippedQty >= total_quantity ? 5 : 4` 自动判定部分发货(4)或全部发货(5)。 - -## 8. 退货流程 +## 7. 退货流程 ``` -shipped/in_transit → returned +in_transit → returned ↓ 仓管员验收退货 - ↓ wh_inventory.quantity 回退(增加) - ↓ qrcode_record.status 恢复为有效 + ↓ 库存回退(wh_inventory.quantity 增加) + ↓ qrcode_record.status 恢复为 1(有效) ↓ 关联应收单状态变更为 'disputed' 或冲销 ``` -退货单 DDD 实现:`src/domain/sales/aggregates/ReturnOrder.ts`,表 `sal_return_order`,事件 `ReturnOrderEvents`(处理器 `ReturnOrderInventoryHandler` 监听退货事件回退库存)。 +## 8. 与其他模块联动 -## 9. 与其他模块联动 - -| 联动模块 | 联动点 | 实现方式 | -|---|---|---| -| 销售订单 | `sal_order.shipped_qty` + `status` 更新 | 直接 SQL UPDATE | -| 仓库管理 | `wh_inventory` / `inv_inventory_batch` 库存扣减 | 直接 SQL UPDATE(扫码路径)或 `DeliveryShippedHandler` | -| 二维码追溯 | `qrcode_record.status='shipped'` + `shipped_at` + `shipment_id` | 直接 SQL UPDATE | -| 财务管理 | `fin_receivable` 应收单生成 | 直接 SQL INSERT 或 `DeliveryReceivableHandler` 监听事件 | -| 销售对账 | `sal_reconciliation` 关联发货金额 | `ReconciliationWriteOffHandler` 处理核销 | +| 联动模块 | 联动点 | +|---|---| +| 销售订单 | `sal_order` 发货状态更新 | +| 仓库管理 | `wh_inventory` 库存扣减 | +| 二维码追溯 | `qrcode_record` 状态变更 | +| 财务管理 | `fin_receivable` 应收单生成 | +| 库存流水 | `inv_inventory_log` 出库记录 | -## 10. 工程约定 +## 9. 工程约定 -1. **状态机强约束**:所有状态流转必须经过 `ShipmentStateMachine.canTransition` 校验,非法流转返回错误 -2. **扫码原子性**:扫码出库的多次 UPDATE 应在事务内(当前实现未显式 `transaction()`,存在一致性风险,已在改进计划中) -3. **应收自动生成**:发货完成(status=5)自动生成应收单,禁止手工录入 -4. **批次精确**:扫码绑定具体批次(`qr_code`),不允许多批次合并扣减 -5. **负库存防护**:扣减前应校验 `wh_inventory.quantity >= 发货数量`(当前实现未显式校验,依赖业务前置检查) -6. **双表路径并存**:`sal_delivery` 与 `shipments` 表并存,新功能建议走 DDD 路径(`sal_delivery` + `Delivery` 聚合根) +1. **状态机强约束**:所有状态流转必须经过 `ShipmentStateMachine.canTransition` 校验 +2. **扫码原子性**:扫码出库使用数据库事务,避免库存扣减与二维码状态不一致 +3. **应收自动生成**:发货完成自动生成应收单,禁止手工录入 +4. **批次精确**:扫码绑定具体批次(`qr_code` + `batch_no`),不允许多批次合并扣减 +5. **负库存防护**:扣减前校验 `wh_inventory.quantity >= 发货数量`,否则拒绝 -## 11. 关键文件清单 +## 10. 关键文件清单 | 文件 | 说明 | |---|---| -| `src/domain/shipment/value-objects/ShipmentStateMachine.ts` | 11 状态机(细粒度) | -| `src/domain/sales/aggregates/Delivery.ts` | 发货聚合根 | -| `src/domain/sales/entities/DeliveryLine.ts` | 发货明细实体 | -| `src/domain/sales/value-objects/DeliveryStatus.ts` | 发货状态值对象 | -| `src/domain/sales/events/DeliveryEvents.ts` | 4 个领域事件 | -| `src/application/services/DeliveryApplicationService.ts` | 发货应用服务 | -| `src/application/handlers/DeliveryShippedHandler.ts` | 发货事件处理器 | -| `src/application/handlers/DeliveryReceivableHandler.ts` | 应收生成处理器 | -| `src/application/handlers/DeliveryCancelledHandler.ts` | 取消回退处理器 | -| `src/app/api/sales/delivery/route.ts` | 发货 CRUD(`sal_delivery`) | -| `src/app/api/sales/delivery/[id]/ship/route.ts` | 扫码发货(`shipments`) | -| `src/app/api/sales/delivery/re-ship/route.ts` | 重新发货 | -| `src/app/api/sales/delivery/partial/route.ts` | 部分发货 | - -> 最后更新:2026-07-10 +| `src/lib/shipment-state-machine.ts` | 发货状态机 | +| `src/app/api/sales/delivery/route.ts` | 发货单 CRUD | +| `src/app/api/sales/delivery/[id]/route.ts` | 发货单详情 | +| `src/app/api/sales/delivery/[id]/ship/route.ts` | 扫码发货 | + +> 最后更新:2026-06-30 diff --git "a/docs/02-\346\250\241\345\235\227\350\257\246\347\273\206\350\256\276\350\256\241/16-\345\215\260\345\211\215\346\250\241\345\235\227\350\256\276\350\256\241.md" "b/docs/02-\346\250\241\345\235\227\350\257\246\347\273\206\350\256\276\350\256\241/16-\345\215\260\345\211\215\346\250\241\345\235\227\350\256\276\350\256\241.md" deleted file mode 100644 index 1487e193..00000000 --- "a/docs/02-\346\250\241\345\235\227\350\257\246\347\273\206\350\256\276\350\256\241/16-\345\215\260\345\211\215\346\250\241\345\235\227\350\256\276\350\256\241.md" +++ /dev/null @@ -1,391 +0,0 @@ -# 印前模块文档 - -## 1. 概述 - -印前模块(dcprint 域)负责印刷生产前的工艺准备工作,包含三大核心聚合根: - -- **油墨配方版本(InkFormulaVersion)**:管理色号对应的油墨配方版本,支持版本复用、成本快照、生效/作废流转 -- **工装(Tool)**:管理刀模与网版的全生命周期,含寿命追踪、成本摊销、维修与报废 -- **打样工艺卡(SampleProcessCard)**:定义打样产品的物料明细、工序明细与成本估算,确认后可联动生成打样工单 - -本模块是 DDD 落地较完整的模块之一,包含聚合根、值对象、领域事件、仓储接口与应用服务完整分层。 - -> **依据**:`docs/油墨配方版本管理完整落地方案.md`、`docs/刀模 _ 网版工装全生命周期管理 完整落地方案.md`、`docs/打样工艺卡录入页统一完善方案.md` - -## 2. 架构分层 - -``` -src/app/api/dcprint/formula/version/route.ts 表现层(withPermission) -src/app/api/dcprint/tool/route.ts -src/app/api/dcprint/sample-card/route.ts - ↓ -src/application/services/InkFormulaVersionService.ts 应用层(用例编排 + 事务 + Outbox) -src/application/handlers/FormulaVersionEventHandler.ts 事件处理器 - ↓ -src/domain/dcprint/aggregates/InkFormulaVersion.ts 领域层(聚合根) -src/domain/dcprint/aggregates/Tool.ts -src/domain/dcprint/aggregates/SampleProcessCard.ts -src/domain/dcprint/value-objects/FormulaStatus.ts 值对象(状态机) -src/domain/dcprint/value-objects/ToolStatus.ts -src/domain/dcprint/value-objects/FormulaItemVO.ts -src/domain/dcprint/events/FormulaVersionEvents.ts 领域事件 -src/domain/dcprint/events/ToolEvents.ts -src/domain/dcprint/events/ProcessCardEvents.ts -src/domain/dcprint/repositories/IFormulaVersionRepository.ts 仓储接口 - ↓ -src/infrastructure/repositories/Mysql*Repository.ts 基础设施层(仓储实现) -``` - -## 3. 领域模型 - -### 3.1 聚合根:InkFormulaVersion(油墨配方版本) - -文件:`src/domain/dcprint/aggregates/InkFormulaVersion.ts` - -封装核心业务规则:状态流转、版本号生成、一键复用、成本快照、明细校验。 - -| 属性 | 类型 | 说明 | -|---|---|---| -| `id` | number | 主键 | -| `colorId` | number | 关联色号 ID(必填) | -| `versionNo` | string | 版本号,格式 `V主.次`(如 `V1.0`、`V2.3`,必填) | -| `versionName` | string \| null | 版本名称 | -| `status` | FormulaStatus | 状态:草稿/已生效/已作废 | -| `changeReason` | string \| null | 变更原因 | -| `sourceVersionId` | number \| null | 一键复用时的源版本 ID | -| `processNote` | string \| null | 工艺说明 | -| `totalWeight` | number \| null | 总重量 | -| `unit` | string | 单位(默认 `kg`) | -| `shelfLifeHours` | number | 保质期小时(默认 168) | -| `theoreticalCost` | number \| null | 理论成本(生效时快照) | -| `costSnapshotTime` | Date \| null | 成本快照时间 | -| `costCalcStatus` | number | 成本计算状态:0 未计算 / 1 完成 / 2 部分缺失 | -| `costWarning` | string \| null | 成本警告信息 | -| `activateBy` / `activateTime` | - | 生效操作人与时间 | -| `cancelBy` / `cancelReason` / `cancelTime` | - | 作废信息 | -| `items` | FormulaItemVO[] | 配方明细列表 | - -**核心方法**: - -| 方法 | 职责 | -|---|---| -| `activate(operatorId)` | 版本生效(草稿 → 已生效),触发 `FormulaVersionActivatedEvent` | -| `cancel(operatorId, reason)` | 版本作废(已生效 → 已作废),作废原因必填,触发 `FormulaVersionCancelledEvent` | -| `updateItems(newItems)` | 更新明细(仅草稿可操作),重置成本状态 | -| `updateBaseInfo(data)` | 更新基础信息(仅草稿可操作) | -| `snapshotCost(costResult)` | 固化成本快照(生效时调用) | -| `createDraft(...)` | 工厂方法:创建草稿版本 | -| `duplicateFrom(source, options, operatorId)` | 工厂方法:一键复用,复制源版本全部明细与工艺参数 | -| `generateNextVersionNo(current, majorBump)` | 静态方法:生成下一版本号(主版本+1 或次版本+1) | - -**版本号规则**: -- 格式:`V主.次`,正则 `^V\d+\.\d+$` -- 次版本升级:`V1.2` → `V1.3` -- 主版本升级:`V1.2` → `V2.0` -- 一键复用时默认次版本+1,`majorVersion=true` 时主版本+1 - -### 3.2 聚合根:Tool(工装 - 刀模/网版) - -文件:`src/domain/dcprint/aggregates/Tool.ts` - -工装支持两套体系字段:体系B(刀模)与体系C(网版),统一管理寿命、成本与状态。 - -| 属性分组 | 字段 | 说明 | -|---|---|---| -| **基础** | `toolType` / `toolCode` / `toolName` / `spec` | 类型、编码、名称、规格 | -| **寿命** | `totalLife` / `usedCount` / `remainLife` / `warningThreshold` | 总寿命、已用、剩余、预警阈值 | -| **成本** | `originalCost` / `accumulatedCost` / `netValue` / `unitCost` | 原值、累计摊销、净值、单位成本 | -| **状态** | `status` | ToolStatus(5 态) | -| **体系B(刀模)** | `assetType` / `layoutType` / `piecesPerImpression` / `material` / `qrCode` / `supplierId` / `maintenanceInterval` / `maintenanceCount` / `lastMaintenanceDate` / `lastMaintenanceImpressions` / `lastUsedDate` | 刀模专属字段 | -| **体系C(网版)** | `meshCount` / `meshMaterial` / `size` / `tensionValue` / `frameType` / `customerId` / `reclaimCount` / `exposureDate` / `lastCleanDate` / `lastReclaimDate` / `tensionDate` | 网版专属字段 | -| **报废** | `scrapReason` / `scrapTime` / `scrapBy` | 报废信息 | - -**核心方法**: - -| 方法 | 职责 | -|---|---| -| `recordUsage(useCount, context)` | 记录使用:累计已用、扣减剩余、摊销成本、自动状态流转、触发 `ToolUsedEvent` 与 `ToolWarningTriggeredEvent` | -| `activate()` | 激活(待用 → 在用) | -| `startMaintenance()` | 开始维修(在用/预警 → 维修中) | -| `completeMaintenance(maintenanceCost, lifeAfter)` | 完成维修:重算净值/原值/单位成本,恢复在用或预警状态 | -| `scrap(reason, operatorId)` | 报废:触发 `ToolScrappedEvent` | -| `create(input)` | 工厂方法:创建工装(初始状态为待用,单位成本=原值/总寿命) | - -**寿命与成本规则**: -- `unitCost = originalCost / totalLife` -- 每次使用:`amortizedCost = unitCost × useCount`,`netValue = originalCost - accumulatedCost` -- 剩余寿命 ≤ 0 时自动转为已报废 -- 已用次数 ≥ 预警阈值时转为预警状态 -- 仅在用/预警状态可记录使用 - -### 3.3 聚合根:SampleProcessCard(打样工艺卡) - -文件:`src/domain/dcprint/aggregates/SampleProcessCard.ts` - -打样工艺卡承载物料明细与工序明细,确认后可联动生成打样工单。 - -| 属性 | 说明 | -|---|---| -| `sampleNo` / `sampleName` | 打样编号、名称(必填) | -| `customerId` / `customerName` | 客户 | -| `productId` / `productName` | 产品 | -| `versionNo` | 版本号(默认 `V1.0`) | -| `status` | CardStatus:1 草稿 / 2 打样中 / 3 已确认 / 4 已作废 | -| `substrateMaterialId` / `substrateMaterialName` | 承印物 | -| `spec` / `printColor` | 规格、印刷颜色 | -| `inkColorId` / `screenPlateId` / `dieToolId` | 关联油墨色号、网版、刀模 | -| `materialLossRate` | 物料损耗率(默认 5) | -| `estimatedHour` | 预估工时 | -| `totalMaterialCost` / `totalLaborCost` / `totalToolCost` / `totalCost` | 成本汇总 | -| `diagramUrl` | 图纸 URL | -| `sampleWorkOrderId` / `sampleWorkOrderNo` | 关联打样工单 | -| `quoteId` | 关联报价单 | -| `formalWorkOrderId` | 关联正式工单 | -| `sourceVersionId` | 源版本 ID | -| `items` | 物料明细(SampleProcessItemProps[]) | -| `steps` | 工序明细(SampleProcessStepProps[]) | - -**核心方法**: - -| 方法 | 职责 | -|---|---| -| `submit()` | 提交(草稿 → 打样中) | -| `confirm(confirmBy)` | 确认(打样中 → 已确认),触发 `ProcessCardConfirmedEvent` | -| `cancel()` | 作废(草稿/打样中 → 已作废) | -| `updateCosts(material, labor, tool)` | 更新成本汇总(4 位小数精度) | -| `create(props)` | 工厂方法:创建工艺卡(需至少 1 条物料明细 + 1 条工序明细) | - -## 4. 值对象与状态机 - -### 4.1 FormulaStatus(配方状态 - 3 态) - -文件:`src/domain/dcprint/value-objects/FormulaStatus.ts` - -| 枚举值 | 数值 | 标签 | 颜色 | -|---|---|---|---| -| `DRAFT` | 1 | 草稿 | gray | -| `ACTIVE` | 2 | 已生效 | green | -| `CANCELLED` | 3 | 已作废 | red | - -**状态流转**: - -```mermaid -stateDiagram-v2 - [*] --> DRAFT: createDraft - DRAFT --> ACTIVE: activate - ACTIVE --> CANCELLED: cancel(reason) - CANCELLED --> [*] -``` - -| 当前状态 | 允许流转到 | -|---|---| -| `DRAFT` | `ACTIVE` | -| `ACTIVE` | `CANCELLED` | -| `CANCELLED` | (终态) | - -**辅助函数**:`canTransition(from, to)`、`getStatusLabel(status)`、`getStatusColor(status)`、`isEditable(status)`(仅草稿可编辑)、`isUsableForProduction(status)`(仅已生效可用于生产)。 - -### 4.2 ToolStatus(工装状态 - 5 态) - -文件:`src/domain/dcprint/value-objects/ToolStatus.ts` - -| 枚举值 | 数值 | 标签 | 颜色 | -|---|---|---|---| -| `STANDBY` | 1 | 待用 | default | -| `ACTIVE` | 2 | 在用 | success | -| `MAINTENANCE` | 3 | 维修中 | warning | -| `WARNING` | 4 | 预警 | error | -| `SCRAPPED` | 5 | 已报废 | default | - -**状态流转**: - -```mermaid -stateDiagram-v2 - [*] --> STANDBY: create - STANDBY --> ACTIVE: activate - STANDBY --> SCRAPPED: scrap - ACTIVE --> MAINTENANCE: startMaintenance - ACTIVE --> WARNING: 达到预警阈值 - ACTIVE --> SCRAPPED: scrap - MAINTENANCE --> ACTIVE: completeMaintenance - MAINTENANCE --> WARNING: completeMaintenance - WARNING --> ACTIVE: 重新启用 - WARNING --> MAINTENANCE: startMaintenance - WARNING --> SCRAPPED: scrap / 寿命耗尽 - SCRAPPED --> [*] -``` - -| 当前状态 | 允许流转到 | -|---|---| -| `STANDBY` | `ACTIVE`, `SCRAPPED` | -| `ACTIVE` | `MAINTENANCE`, `WARNING`, `SCRAPPED` | -| `MAINTENANCE` | `ACTIVE`, `WARNING` | -| `WARNING` | `ACTIVE`, `MAINTENANCE`, `SCRAPPED` | -| `SCRAPPED` | (终态) | - -**辅助函数**:`canToolTransition(from, to)`、`getToolStatusLabel(status)`、`getToolStatusColor(status)`、`isToolUsable(status)`(仅在用/预警可用)。 - -### 4.3 CardStatus(工艺卡状态 - 4 态) - -定义于 `SampleProcessCard` 聚合根内,类型 `1 | 2 | 3 | 4`。 - -| 数值 | 标签 | -|---|---| -| 1 | 草稿 | -| 2 | 打样中 | -| 3 | 已确认 | -| 4 | 已作废 | - -```mermaid -stateDiagram-v2 - [*] --> 草稿: create - 草稿 --> 打样中: submit - 打样中 --> 已确认: confirm - 草稿 --> 已作废: cancel - 打样中 --> 已作废: cancel - 已确认 --> [*] - 已作废 --> [*] -``` - -**操作权限**: -- `canEdit` / `canDelete` / `canSubmit`:仅草稿(1) -- `canConfirm`:仅打样中(2) -- `canCancel`:草稿(1)或打样中(2) - -## 5. 领域事件 - -### 5.1 配方版本事件 - -文件:`src/domain/dcprint/events/FormulaVersionEvents.ts` - -| 事件类 | eventType | 触发时机 | 载荷 | -|---|---|---|---| -| `FormulaVersionActivatedEvent` | `inkFormulaVersion.activated` | 草稿版本被激活为已生效 | versionId, colorId, versionNo, activatedBy, theoreticalCost | -| `FormulaVersionCancelledEvent` | `inkFormulaVersion.cancelled` | 已生效版本被作废 | versionId, colorId, versionNo, cancelledBy, reason | - -### 5.2 工装事件 - -文件:`src/domain/dcprint/events/ToolEvents.ts` - -| 事件类 | eventType | 触发时机 | -|---|---|---| -| `ToolCreatedEvent` | `tool.created` | 新工装录入完成 | -| `ToolActivatedEvent` | `tool.activated` | 工装从待用变为在用 | -| `ToolMaintenanceStartedEvent` | `tool.maintenance_started` | 工装进入维修状态 | -| `ToolMaintenanceCompletedEvent` | `tool.maintenance_completed` | 工装维修完成 | -| `ToolWarningTriggeredEvent` | `tool.warning_triggered` | 使用累计达到预警阈值 | -| `ToolScrappedEvent` | `tool.scrapped` | 工装被报废(手动或寿命耗尽) | -| `ToolUsedEvent` | `tool.used` | 报工审核时联动累计刀模/网版使用次数 | - -### 5.3 工艺卡事件 - -文件:`src/domain/dcprint/events/ProcessCardEvents.ts` - -| 事件类 | eventType | 触发时机 | -|---|---|---| -| `ProcessCardConfirmedEvent` | `process_card.confirmed` | 工艺卡确认(可联动生成打样工单) | - -### 5.4 事件处理器注册 - -文件:`src/application/EventRegistry.ts` - -| eventType | 处理器 | -|---|---| -| `inkFormulaVersion.activated` | `FormulaVersionActivatedHandler`(幂等)、`AuditLogHandler`、`CacheInvalidationHandler` | -| `inkFormulaVersion.cancelled` | `FormulaVersionCancelledHandler`(幂等)、`AuditLogHandler`、`CacheInvalidationHandler` | -| `tool.created` / `tool.activated` / `tool.maintenance_*` / `tool.warning_triggered` / `tool.scrapped` | `AuditLogHandler`、`CacheInvalidationHandler` | -| `tool.used` | 由 `WorkReportApprovedEvent` 链路触发,联动 `ToolUsageSyncHandler` | - -## 6. API 接口 - -### 6.1 油墨配方版本 API - -| 方法 | 路径 | 功能 | -|---|---|---| -| GET | `/api/dcprint/formula/version?colorId=` | 按色号查询版本列表 | -| GET | `/api/dcprint/formula/version?id=` | 获取版本详情 | -| GET | `/api/dcprint/formula/version?compare=1&leftId=&rightId=` | 版本对比 | -| POST | `/api/dcprint/formula/version` | 新建草稿版本 | -| POST | `/api/dcprint/formula/version?previewCost=1` | 成本预览 | -| PUT | `/api/dcprint/formula/version/[id]` | 更新版本基础信息 | -| PUT | `/api/dcprint/formula/version/[id]/items` | 更新配方明细 | -| PUT | `/api/dcprint/formula/version/[id]/activate` | 版本生效 | -| PUT | `/api/dcprint/formula/version/[id]/cancel` | 版本作废 | -| POST | `/api/dcprint/formula/version/[id]/duplicate` | 一键复用 | -| GET | `/api/dcprint/formula/version/[id]/preview-cost` | 单版本成本预览 | -| PUT | `/api/dcprint/formula/version/[id]/recalculate-cost` | 重算成本 | -| GET | `/api/dcprint/formula/version/compare` | 版本对比(独立端点) | -| GET | `/api/dcprint/formula/color` | 色号管理 | - -### 6.2 工装 API - -| 方法 | 路径 | 功能 | -|---|---|---| -| GET | `/api/dcprint/tool` | 工装列表查询(支持类型/状态/关键字筛选) | -| POST | `/api/dcprint/tool` | 创建工装 | -| GET | `/api/dcprint/tool/[id]` | 工装详情 | -| PUT | `/api/dcprint/tool/[id]` | 更新工装 | -| PUT | `/api/dcprint/tool/[id]/activate` | 激活工装 | -| PUT | `/api/dcprint/tool/[id]/scrap` | 报废工装 | -| PUT | `/api/dcprint/tool/[id]/maintenance` | 维修工装(开始/完成) | -| POST | `/api/dcprint/tool/[id]/usage` | 记录使用 | -| GET | `/api/dcprint/tool/dashboard` | 工装看板(寿命预警、状态统计) | - -### 6.3 工艺卡 API - -| 方法 | 路径 | 功能 | -|---|---|---| -| GET | `/api/dcprint/sample-card` | 工艺卡列表 | -| POST | `/api/dcprint/sample-card` | 创建工艺卡 | -| GET | `/api/dcprint/sample-card/[id]` | 工艺卡详情 | -| PUT | `/api/dcprint/sample-card/[id]` | 更新工艺卡(仅草稿) | -| PUT | `/api/dcprint/sample-card/[id]/submit` | 提交工艺卡 | -| PUT | `/api/dcprint/sample-card/[id]/confirm` | 确认工艺卡 | -| PUT | `/api/dcprint/sample-card/[id]/cancel` | 作废工艺卡 | -| POST | `/api/dcprint/sample-card/[id]/duplicate` | 复制工艺卡 | -| POST | `/api/dcprint/sample-card/[id]/save-as-template` | 另存为模板 | -| POST | `/api/dcprint/sample-card/[id]/convert-work-order` | 转打样工单 | -| GET | `/api/dcprint/sample-card/[id]/cost-variance` | 成本差异分析 | -| GET | `/api/dcprint/sample-card/[id]/generate-quote` | 生成报价单 | -| POST | `/api/dcprint/sample-card/cost-preview` | 成本预览 | -| GET/POST | `/api/dcprint/sample-card/template` | 模板管理 | -| GET/PUT/DELETE | `/api/dcprint/sample-card/template/[id]` | 模板详情 | - -### 6.4 其他印前 API - -| 路径前缀 | 功能 | -|---|---| -| `/api/dcprint/ink-formula` | 油墨配方(旧版) | -| `/api/dcprint/ink-*` | 油墨相关(消耗、用量、余量、查询、期初、混合、派工、初始化) | -| `/api/dcprint/die` / `/api/dcprint/die-template` / `/api/dcprint/die-usage` / `/api/dcprint/die-maintenance` | 刀模管理 | -| `/api/dcprint/screen-plate` | 网版管理 | -| `/api/dcprint/process-cards` | 旧版工艺卡 | -| `/api/dcprint/labels` | 标签 | -| `/api/dcprint/scan` | 扫码 | -| `/api/dcprint/trace` | 追溯 | -| `/api/dcprint/ink-consumption` | 油墨消耗 | - -## 7. 关键文件清单 - -| 文件 | 说明 | -|---|---| -| `src/domain/dcprint/aggregates/InkFormulaVersion.ts` | 油墨配方版本聚合根 | -| `src/domain/dcprint/aggregates/Tool.ts` | 工装聚合根(刀模/网版) | -| `src/domain/dcprint/aggregates/SampleProcessCard.ts` | 打样工艺卡聚合根 | -| `src/domain/dcprint/aggregates/SampleProcessTemplate.ts` | 工艺卡模板 | -| `src/domain/dcprint/value-objects/FormulaStatus.ts` | 配方状态机(3 态) | -| `src/domain/dcprint/value-objects/ToolStatus.ts` | 工装状态机(5 态) | -| `src/domain/dcprint/value-objects/FormulaItemVO.ts` | 配方明细值对象 | -| `src/domain/dcprint/events/FormulaVersionEvents.ts` | 配方版本事件(2 个) | -| `src/domain/dcprint/events/ToolEvents.ts` | 工装事件(7 个) | -| `src/domain/dcprint/events/ProcessCardEvents.ts` | 工艺卡事件(1 个) | -| `src/domain/dcprint/repositories/IFormulaVersionRepository.ts` | 配方仓储接口 | -| `src/domain/dcprint/repositories/IToolRepository.ts` | 工装仓储接口 | -| `src/domain/dcprint/repositories/ISampleProcessCardRepository.ts` | 工艺卡仓储接口 | -| `src/domain/dcprint/repositories/ISampleProcessTemplateRepository.ts` | 模板仓储接口 | -| `src/domain/dcprint/services/FormulaCompareService.ts` | 配方对比服务 | -| `src/domain/dcprint/services/FormulaCostService.ts` | 配方成本服务 | -| `src/application/services/InkFormulaVersionService.ts` | 配方版本应用服务 | -| `src/application/handlers/FormulaVersionEventHandler.ts` | 配方事件处理器 | - -> 最后更新:2026-07-15 diff --git "a/docs/02-\346\250\241\345\235\227\350\257\246\347\273\206\350\256\276\350\256\241/17-\346\211\223\346\240\267\346\250\241\345\235\227\350\256\276\350\256\241.md" "b/docs/02-\346\250\241\345\235\227\350\257\246\347\273\206\350\256\276\350\256\241/17-\346\211\223\346\240\267\346\250\241\345\235\227\350\256\276\350\256\241.md" deleted file mode 100644 index a2fb2a48..00000000 --- "a/docs/02-\346\250\241\345\235\227\350\257\246\347\273\206\350\256\276\350\256\241/17-\346\211\223\346\240\267\346\250\241\345\235\227\350\256\276\350\256\241.md" +++ /dev/null @@ -1,421 +0,0 @@ -# 打样模块文档 - -## 1. 概述 - -打样模块(sample 域)负责打样订单的全生命周期管理,是连接客户需求与正式大货生产的关键桥梁。核心聚合根为 **SampleOrder(打样单)**,承载 7 态状态机,支持从草稿创建、提交打样、生产、完成、客户确认到一键转大货订单的完整流程。 - -打样模块的核心价值: -- **打样费管理**:支持打样费收取、可抵扣标记,转大货时自动抵扣 -- **关联管理**:可关联工艺卡(processCardId)、打样工单(workOrderId)、销售订单(salesOrderId) -- **版本管理**:支持 sampleVersion 与 parentVersionId,承载打样迭代 -- **T305 一键转大货**:已确认状态可自动生成销售订单并完成转化 - -> **依据**:`docs/印前打样生产模块完善总结报告-2026-07-14.md`、`docs/05-测试文档/打样管理模块单元测试报告.md`、`docs/prototypes/sample-order-flow-prototype.html`、`database/migrations/053_enhance_sample_order.sql` - -## 2. 架构分层 - -``` -src/app/api/sample/orders/route.ts 表现层(withPermission) -src/app/api/sample/orders/status/route.ts -src/app/api/sample/orders/linkage/route.ts -src/app/api/sample/feedback/route.ts -src/app/api/sample/inventory/route.ts - ↓ -src/application/services/SampleOrderApplicationService.ts 应用层(用例编排 + 事务 + Outbox) -src/application/handlers/SampleOrderInventoryHandler.ts 事件处理器 -src/application/handlers/SampleOrderConversionHandler.ts - ↓ -src/domain/sample/aggregates/SampleOrder.ts 领域层(聚合根) -src/domain/sample/entities/SampleFeedback.ts 领域实体 -src/domain/sample/entities/SampleInventory.ts -src/domain/sample/value-objects/SampleOrderStatus.ts 值对象(7 态状态机) -src/domain/sample/events/SampleOrderEvents.ts 领域事件(7 个) -src/domain/sample/repositories/ISampleOrderRepository.ts 仓储接口 -src/domain/sample/repositories/ISampleFeedbackRepository.ts - ↓ -src/infrastructure/repositories/MysqlSampleOrderRepository.ts 基础设施层(仓储实现) -``` - -## 3. 领域模型 - -### 3.1 聚合根:SampleOrder(打样单) - -文件:`src/domain/sample/aggregates/SampleOrder.ts` - -打样单聚合根封装状态流转、关联管理与费用处理规则,与 `sal_sample_order` 表一一对齐。 - -| 属性分组 | 字段 | 说明 | -|---|---|---| -| **基础** | `id` / `orderNo` | 主键、打样单号(格式 `SP + YYYYMMDD + 4位序号`) | -| **客户/产品** | `customerId` / `customerName` / `productName` / `materialNo` | 客户与产品信息 | -| **规格** | `version` / `sizeSpec` / `materialSpec` / `specification` / `quantity` | 版本(默认 `A`)、规格、数量 | -| **日期** | `notifyDate` / `orderDate` / `customerRequireDate` / `deliveryDate` / `actualDeliveryDate` | 通知、下单、要求交期、计划/实际交货日期 | -| **状态** | `status` / `deliveryStatus` | 打样状态(7 态)、交货状态(默认 `pending`) | -| **关联** | `processCardId` / `workOrderId` / `salesOrderId` | 工艺卡、工单、销售订单关联 | -| **费用** | `sampleFee` / `feeCharged` / `feeDeductible` / `feeDeducted` | 打样费、是否已收取、是否可抵扣、是否已抵扣 | -| **版本** | `sampleVersion` / `parentVersionId` | 打样版本号、父版本 ID | -| **转化** | `convertedAt` / `convertedBy` | 转大货时间与操作人 | -| **审计** | `createBy` / `createTime` / `updateTime` / `remark` | 创建人、时间戳、备注 | - -**核心方法**: - -| 方法 | 职责 | 状态流转 | 触发事件 | -|---|---|---|---| -| `submit(userId)` | 提交打样 | 草稿 → 待打样 | `SampleOrderSubmittedEvent` | -| `startProduction(userId)` | 开始打样生产 | 待打样 → 打样中 | `SampleOrderStartedEvent` | -| `complete(userId)` | 完成打样 | 打样中 → 已完成 | `SampleOrderCompletedEvent` | -| `confirm(userId)` | 客户确认 | 已完成 → 已确认 | `SampleOrderConfirmedEvent` | -| `convertToSalesOrder(salesOrderId, userId)` | 转大货 | 已确认 → 已转大货 | `SampleOrderConvertedEvent` | -| `cancel(reason, userId)` | 作废 | 多状态 → 已作废 | `SampleOrderCancelledEvent` | -| `linkProcessCard(processCardId)` | 关联工艺卡 | - | - | -| `linkWorkOrder(workOrderId)` | 关联工单 | - | - | -| `updateSampleFee(fee, charged, deductible)` | 更新打样费 | - | - | - -**工厂方法**: -- `create(props)`:创建打样单(触发 `SampleOrderCreatedEvent`) -- `reconstitute(props)`:从数据库重建 -- `generateCode(sequence)`:静态方法,生成单号 `SP + YYYYMMDD + 4位序号` - -**转大货费用规则**:转大货时若 `feeCharged` 且 `feeDeductible` 为真,则自动标记 `feeDeducted = 1`(已抵扣)。 - -### 3.2 实体:SampleFeedback(打样反馈) - -文件:`src/domain/sample/entities/SampleFeedback.ts` - -记录客户对打样的反馈意见,支持 `approve()` / `reject()` 流转。 - -### 3.3 实体:SampleInventory(打样库存) - -文件:`src/domain/sample/entities/SampleInventory.ts` - -管理打样成品库存,打样完成事件触发库存增加。 - -## 4. 值对象与状态机 - -### 4.1 SampleOrderStatus(打样单状态 - 7 态) - -文件:`src/domain/sample/value-objects/SampleOrderStatus.ts` - -| 枚举值 | 状态值 | 标签 | 颜色 | -|---|---|---|---| -| `DRAFT` | `draft` | 草稿 | `#909399`(灰色) | -| `PENDING` | `pending` | 待打样 | `#E6A23C`(橙色) | -| `IN_PROGRESS` | `in_progress` | 打样中 | `#409EFF`(蓝色) | -| `COMPLETED` | `completed` | 已完成 | `#67C23A`(绿色) | -| `CONFIRMED` | `confirmed` | 已确认 | `#00C853`(亮绿色) | -| `CONVERTED` | `converted` | 已转大货 | `#000000`(黑色) | -| `CANCELLED` | `cancelled` | 已作废 | `#F56C6C`(红色) | - -**状态流转**: - -```mermaid -stateDiagram-v2 - [*] --> 草稿: create - 草稿 --> 待打样: submit - 草稿 --> 已作废: cancel - 待打样 --> 打样中: startProduction - 待打样 --> 已作废: cancel - 打样中 --> 已完成: complete - 打样中 --> 已作废: cancel - 已完成 --> 已确认: confirm - 已完成 --> 已作废: cancel - 已确认 --> 已转大货: convertToSalesOrder - 已转大货 --> [*] - 已作废 --> [*] -``` - -| 当前状态 | 允许流转到 | -|---|---| -| `DRAFT`(草稿) | `PENDING`、`CANCELLED` | -| `PENDING`(待打样) | `IN_PROGRESS`、`CANCELLED` | -| `IN_PROGRESS`(打样中) | `COMPLETED`、`CANCELLED` | -| `COMPLETED`(已完成) | `CONFIRMED`、`CANCELLED` | -| `CONFIRMED`(已确认) | `CONVERTED` | -| `CONVERTED`(已转大货) | (终态) | -| `CANCELLED`(已作废) | (终态) | - -### 4.2 状态转换动作定义 - -`statusTransitionActions` 定义所有合法流转动作: - -| action | 标签 | from | to | -|---|---|---|---| -| `submit` | 提交 | DRAFT | PENDING | -| `startProduction` | 开始生产 | PENDING | IN_PROGRESS | -| `complete` | 完成生产 | IN_PROGRESS | COMPLETED | -| `confirm` | 确认合格 | COMPLETED | CONFIRMED | -| `convert` | 转为大货订单 | CONFIRMED | CONVERTED | -| `cancel` | 作废 | DRAFT | CANCELLED | -| `cancel` | 作废 | PENDING | CANCELLED | -| `cancel` | 作废 | IN_PROGRESS | CANCELLED | -| `cancel` | 作废 | COMPLETED | CANCELLED | - -**辅助函数**:`canTransition(current, target)`、`getStatusLabel(status)`、`getStatusColor(status)`。 - -## 5. 领域事件 - -### 5.1 打样单事件 - -文件:`src/domain/sample/events/SampleOrderEvents.ts` - -| 事件类 | eventType | 触发时机 | 载荷 | -|---|---|---|---| -| `SampleOrderCreatedEvent` | `SampleOrderCreated` | 创建打样单 | sampleOrderId, orderNo, customerId, userId | -| `SampleOrderSubmittedEvent` | `SampleOrderSubmitted` | 草稿 → 待打样 | sampleOrderId, orderNo, userId | -| `SampleOrderStartedEvent` | `SampleOrderStarted` | 待打样 → 打样中 | sampleOrderId, orderNo, userId | -| `SampleOrderCompletedEvent` | `SampleOrderCompleted` | 打样中 → 已完成 | sampleOrderId, orderNo, userId | -| `SampleOrderConfirmedEvent` | `SampleOrderConfirmed` | 已完成 → 已确认 | sampleOrderId, orderNo, userId | -| `SampleOrderConvertedEvent` | `SampleOrderConverted` | 已确认 → 已转大货 | sampleOrderId, orderNo, salesOrderId, userId | -| `SampleOrderCancelledEvent` | `SampleOrderCancelled` | 作废 | sampleOrderId, orderNo, reason, userId | - -### 5.2 事件处理器注册 - -文件:`src/application/EventRegistry.ts` - -| eventType | 处理器 | -|---|---| -| `SampleOrderCompleted` | `SampleOrderInventoryHandler`(打样成品入库)、`AuditLogHandler` | -| `SampleOrderConverted` | `SampleOrderConversionHandler`(幂等,回写 sales_order_id)、`AuditLogHandler` | -| `SampleOrderSubmitted` | `AuditLogHandler` | -| `SampleOrderStarted` | `AuditLogHandler` | -| `SampleOrderConfirmed` | `AuditLogHandler` | -| `SampleOrderCancelled` | `AuditLogHandler` | - -**SampleOrderConversionHandler**(文件:`src/application/handlers/SampleOrderConversionHandler.ts`): -- 监听 `SampleOrderConverted` 事件 -- 执行 `UPDATE sal_sample_order SET sales_order_id, converted_at = NOW(), converted_by = ? WHERE id = ? AND deleted = 0` -- 作为转大货流程的幂等兜底,保证 `sal_sample_order` 表的关联字段最终一致 - -## 6. 转大货流程:T305 一键转大货 - -T305 是打样模块的核心业务流程,实现从已确认打样单到销售大货订单的一键转化。 - -### 6.1 流程入口 - -``` -PUT /api/sample/orders/status -Body: { id, action: 'convert', salesOrderId?: number } -``` - -文件:`src/app/api/sample/orders/status/route.ts` - -### 6.2 完整链路 - -``` -┌─────────────────────────────────────────────────────────────────────┐ -│ 1. Route 层 │ -│ PUT /api/sample/orders/status body: { id, action: 'convert' } │ -│ 文件:src/app/api/sample/orders/status/route.ts │ -└──────────────────────────┬──────────────────────────────────────────┘ - │ - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 2. 判断是否已有 salesOrderId │ -│ - 已提供 → 直接进入步骤 4 │ -│ - 未提供 → 调用 T305 自动生成 │ -└──────────────────────────┬──────────────────────────────────────────┘ - │ 未提供 salesOrderId - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 3. SampleOrderApplicationService.createSalesOrderFromSample(id, userId)│ -│ 文件:src/application/services/SampleOrderApplicationService.ts │ -│ │ -│ a. 读取打样单(getOrderById) │ -│ b. 生成销售单号:SO + YYYYMMDD + 4位序号 │ -│ (SELECT COUNT(*) FROM sal_order WHERE order_no LIKE 'SO...%') │ -│ c. 通过 material_no 查询 inv_material.id(materialId) │ -│ d. 计算金额:unitPrice = sampleFee, totalAmount = sampleFee │ -│ e. 事务内原子写入: │ -│ - INSERT sal_order(status=1 草稿, currency='CNY') │ -│ - INSERT sal_order_detail(material_id, quantity, unit_price) │ -│ f. 返回 newSalesOrderId │ -└──────────────────────────┬──────────────────────────────────────────┘ - │ - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 4. SampleOrderApplicationService.convertOrder(id, salesOrderId, userId)│ -│ a. 读取打样单(getOrderById) │ -│ b. 调用聚合根 order.convertToSalesOrder(salesOrderId, userId) │ -│ - 状态流转:CONFIRMED → CONVERTED │ -│ - 设置 salesOrderId, convertedAt, convertedBy │ -│ - 费用抵扣:若 feeCharged && feeDeductible → feeDeducted = 1 │ -│ - 触发 SampleOrderConvertedEvent │ -│ c. 事务内: │ -│ - orderRepo.update(order, conn) 更新打样单 │ -│ - persistEvents(id, order, conn) 写入 Outbox │ -│ d. 事务提交后 order.clearDomainEvents() │ -└──────────────────────────┬──────────────────────────────────────────┘ - │ Outbox Poller 投递 - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 5. Event Bus 分发(EventRegistry 注册) │ -│ eventType: 'SampleOrderConverted' │ -│ 订阅者: │ -│ - IdempotentHandler(SampleOrderConversionHandler) │ -│ - AuditLogHandler │ -└──────────────────────────┬──────────────────────────────────────────┘ - │ - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 6. SampleOrderConversionHandler.handle(event) │ -│ 文件:src/application/handlers/SampleOrderConversionHandler.ts │ -│ 功能:回写 sal_sample_order 表的关联字段(幂等兜底) │ -│ │ -│ UPDATE sal_sample_order SET │ -│ sales_order_id = ?, │ -│ converted_at = NOW(), │ -│ converted_by = ? │ -│ WHERE id = ? AND deleted = 0 │ -└─────────────────────────────────────────────────────────────────────┘ -``` - -### 6.3 关键设计要点 - -1. **单号生成规则**: - - 打样单号:`SP + YYYYMMDD + 4位序号`(如 `SP202607150001`) - - 销售单号:`SO + YYYYMMDD + 4位序号`(如 `SO202607150001`) - -2. **Transactional Outbox**:`convertOrder` 在事务内同时更新打样单状态与写入 Outbox,保证状态变更与事件发布原子性 - -3. **幂等兜底**:`SampleOrderConversionHandler` 作为事件驱动补充,确保 `sal_sample_order` 表的 `sales_order_id`、`converted_at`、`converted_by` 字段最终一致 - -4. **费用抵扣自动化**:转大货时若打样费已收取(`feeCharged`)且标记为可抵扣(`feeDeductible`),自动设置 `feeDeducted = 1` - -5. **物料映射**:通过打样单的 `material_no` 查询 `inv_material.material_code` 获取 `materialId`,写入销售单明细 - -## 7. API 接口 - -### 7.1 打样单 API - -文件:`src/app/api/sample/orders/route.ts` - -| 方法 | 路径 | 功能 | -|---|---|---| -| GET | `/api/sample/orders` | 打样单列表查询(支持 keyword/customerName/status/deliveryStatus/startDate/endDate/page/pageSize) | -| POST | `/api/sample/orders` | 创建打样单(必填:notify_date, customer_name, product_name, material_no) | -| PUT | `/api/sample/orders` | 更新打样单 | -| DELETE | `/api/sample/orders?id=` | 删除打样单 | - -**字段命名约定**:API 层使用 snake_case(如 `order_no`、`customer_id`),应用层与领域层使用 camelCase(如 `orderNo`、`customerId`),由 Route 层负责双向转换。 - -### 7.2 状态流转 API - -文件:`src/app/api/sample/orders/status/route.ts` - -| 方法 | 路径 | 功能 | -|---|---|---| -| PUT | `/api/sample/orders/status` | 状态流转(统一入口) | - -**请求体**:`{ id, action, reason?, salesOrderId? }` - -| action | 说明 | 附加参数 | -|---|---|---| -| `submit` | 提交打样(草稿 → 待打样) | - | -| `startProduction` | 开始生产(待打样 → 打样中) | - | -| `complete` | 完成生产(打样中 → 已完成) | - | -| `confirm` | 确认合格(已完成 → 已确认) | - | -| `convert` | 转大货(已确认 → 已转大货) | `salesOrderId?`(未提供则 T305 自动生成) | -| `cancel` | 作废 | `reason?`(默认"手动作废") | - -### 7.3 关联管理 API - -文件:`src/app/api/sample/orders/linkage/route.ts` - -| 方法 | 路径 | 功能 | -|---|---|---| -| PUT | `/api/sample/orders/linkage` | 关联工艺卡 / 工单 | - -### 7.4 打样反馈 API - -文件:`src/app/api/sample/feedback/route.ts` - -| 方法 | 路径 | 功能 | -|---|---|---| -| GET | `/api/sample/feedback` | 查询反馈列表 | -| POST | `/api/sample/feedback` | 新增反馈 | -| PUT | `/api/sample/feedback` | 审核反馈(approve/reject) | - -### 7.5 打样库存 API - -文件:`src/app/api/sample/inventory/route.ts` - -| 方法 | 路径 | 功能 | -|---|---|---| -| GET | `/api/sample/inventory` | 打样成品库存查询 | -| POST | `/api/sample/inventory` | 库存调整 | - -## 8. 业务流程 - -### 8.1 打样全生命周期 - -``` -客户提出打样需求 - ↓ create(创建打样单,status=DRAFT) -草稿 - ↓ submit(提交打样) -待打样 ──── 关联工艺卡(linkProcessCard) - ↓ startProduction(开始生产) -打样中 ──── 关联打样工单(linkWorkOrder) - ↓ complete(完成打样)→ 触发 SampleOrderCompletedEvent → 打样成品入库 -已完成 - ↓ confirm(客户确认合格) -已确认 - ↓ convert(转大货)→ T305 自动生成销售订单 + 触发 SampleOrderConvertedEvent -已转大货(终态)→ 进入销售/生产大货流程 -``` - -### 8.2 作废流程 - -草稿 / 待打样 / 打样中 / 已完成 均可作废(cancel),作废后为终态,不可恢复。 - -### 8.3 打样费用流程 - -``` -创建打样单(sampleFee=0, feeCharged=0, feeDeductible=0) - ↓ updateSampleFee(fee, charged=1, deductible=1) -收取打样费(feeCharged=1, feeDeductible=1) - ↓ convert(转大货) -自动抵扣(feeDeducted=1)→ 打样费可抵扣大货订单金额 -``` - -## 9. 关键文件清单 - -| 文件 | 说明 | -|---|---| -| `src/domain/sample/aggregates/SampleOrder.ts` | 打样单聚合根(7 态状态机) | -| `src/domain/sample/entities/SampleFeedback.ts` | 打样反馈实体 | -| `src/domain/sample/entities/SampleInventory.ts` | 打样库存实体 | -| `src/domain/sample/value-objects/SampleOrderStatus.ts` | 打样单状态机(7 态) | -| `src/domain/sample/events/SampleOrderEvents.ts` | 打样单事件(7 个) | -| `src/domain/sample/repositories/ISampleOrderRepository.ts` | 打样单仓储接口 | -| `src/domain/sample/repositories/ISampleFeedbackRepository.ts` | 反馈仓储接口 | -| `src/domain/sample/standard-card/schema.ts` | 标准卡 schema | -| `src/domain/sample/standard-card/types.ts` | 标准卡类型 | -| `src/domain/sample/standard-card/utils.ts` | 标准卡工具 | -| `src/domain/sample/index.ts` | 模块导出 | -| `src/application/services/SampleOrderApplicationService.ts` | 打样单应用服务(含 T305) | -| `src/application/handlers/SampleOrderInventoryHandler.ts` | 打样成品入库处理器 | -| `src/application/handlers/SampleOrderConversionHandler.ts` | 转大货回写处理器(幂等) | -| `src/infrastructure/repositories/MysqlSampleOrderRepository.ts` | 打样单仓储实现 | -| `src/app/api/sample/orders/route.ts` | 打样单 API 路由 | -| `src/app/api/sample/orders/status/route.ts` | 状态流转 API 路由 | -| `src/app/api/sample/orders/linkage/route.ts` | 关联管理 API 路由 | -| `src/app/api/sample/feedback/route.ts` | 反馈 API 路由 | -| `src/app/api/sample/inventory/route.ts` | 打样库存 API 路由 | -| `database/migrations/053_enhance_sample_order.sql` | 打样单表增强迁移(关联字段) | -| `database/migrations/055_create_work_order_bom_and_sample_quotation.sql` | 工单 BOM 与打样报价 | - -## 10. 测试参考 - -| 文件 | 说明 | -|---|---| -| `tests/unit/domain/sample/aggregates/sample-order.test.ts` | 打样单聚合根单元测试 | -| `tests/unit/domain/sample/value-objects/sample-order-status.test.ts` | 状态机单元测试 | -| `tests/unit/app/api/sample/orders/status.test.ts` | 状态流转 API 测试 | -| `tests/unit/application/services/SampleOrderApplicationService.test.ts` | 应用服务测试 | -| `tests/e2e/sample-to-order.spec.ts` | 打样转大货 E2E 测试 | -| `docs/05-测试文档/打样管理模块单元测试报告.md` | 测试报告 | -| `docs/prototypes/sample-order-flow-prototype.html` | 流程原型 | -| `scripts/test-sample-order-flow.mjs` | 流程测试脚本 | - -> 最后更新:2026-07-15 diff --git "a/docs/02-\346\250\241\345\235\227\350\257\246\347\273\206\350\256\276\350\256\241/18-\347\224\237\344\272\247\346\250\241\345\235\227\350\256\276\350\256\241.md" "b/docs/02-\346\250\241\345\235\227\350\257\246\347\273\206\350\256\276\350\256\241/18-\347\224\237\344\272\247\346\250\241\345\235\227\350\256\276\350\256\241.md" deleted file mode 100644 index 3e4702c9..00000000 --- "a/docs/02-\346\250\241\345\235\227\350\257\246\347\273\206\350\256\276\350\256\241/18-\347\224\237\344\272\247\346\250\241\345\235\227\350\256\276\350\256\241.md" +++ /dev/null @@ -1,495 +0,0 @@ -# 生产模块文档 - -## 1. 概述 - -生产模块(production 域)负责生产执行的全流程管理,包含四个核心聚合根: - -- **生产工单(WorkOrder)**:生产任务的主体,7 态状态机贯穿草稿到结案全生命周期 -- **领料单(PickOrder)**:从仓库领取生产所需物料,审核后扣减库存 -- **报工单(WorkReport)**:记录工序产出(合格/不良),审核后联动工装使用次数 -- **完工入库单(FinishOrder)**:成品入库,审核后增加库存、回写工单完工数量 - -四个聚合根以 WorkOrder 为中心协同:PickOrder/WorkReport/FinishOrder 均关联 workOrderId,通过领域事件驱动库存与成本联动。 - -> **依据**:`docs/生产工单 - 领料 - 库存 - 完工入库 全链路完善方案.md`、`docs/03-技术规范/业务规则-生产.md`、`docs/plans/2026-07-10-production-module-upgrade.md` - -## 2. 架构分层 - -``` -src/app/api/workorders/route.ts 表现层(withPermission) -src/app/api/warehouse/production-inbound/route.ts -src/app/api/material-returns/route.ts - ↓ -src/application/handlers/WorkOrderMaterialIssuedHandler.ts 应用层(事件处理器) -src/application/handlers/WorkOrderCompletedHandler.ts -src/application/handlers/PickOrderInventoryHandler.ts -src/application/handlers/FinishOrderInventoryHandler.ts -src/application/handlers/MaterialReturnInventoryHandler.ts - ↓ -src/domain/production/aggregates/WorkOrder.ts 领域层(聚合根) -src/domain/production/aggregates/PickOrder.ts -src/domain/production/aggregates/WorkReport.ts -src/domain/production/aggregates/FinishOrder.ts -src/domain/production/entities/MaterialRequirement.ts 领域实体 -src/domain/production/entities/ProductionSchedule.ts -src/domain/production/value-objects/WorkOrderStatus.ts 值对象(状态机) -src/domain/production/value-objects/WorkOrderStateMachine.ts -src/domain/production/events/WorkOrderEvents.ts 领域事件 -src/domain/production/events/PickOrderEvents.ts -src/domain/production/events/WorkReportEvents.ts -src/domain/production/events/FinishOrderEvents.ts -src/domain/production/events/ReturnOrderEvents.ts -src/domain/production/repositories/IWorkOrderRepository.ts 仓储接口 - ↓ -src/infrastructure/event-bus/EventBus.ts 基础设施层(事件总线 + Outbox) -src/infrastructure/event-bus/DomainEventOutboxFactory.ts -``` - -## 3. 领域模型 - -### 3.1 聚合根:WorkOrder(生产工单) - -文件:`src/domain/production/aggregates/WorkOrder.ts` - -生产工单是生产模块的核心聚合根,承载 7 态状态机与物料需求(BOM)。 - -| 属性 | 类型 | 说明 | -|---|---|---| -| `id` | number | 主键 | -| `workOrderNo` | string | 工单编号 | -| `status` | WorkOrderStatusVO | 状态值对象(7 态) | -| `productId` / `productName` / `productCode` | number / string | 产品信息 | -| `plannedQty` | number | 计划数量 | -| `completedQty` | number | 已完工数量 | -| `orderType` | number | 订单类型(0 普通工单 / 1 打样工单) | -| `processId` / `processName` | number / string | 工序 | -| `warehouseId` | number | 入库仓库(默认 1) | -| `plannedStartDate` / `plannedEndDate` | string | 计划起止日期 | -| `actualStartDate` / `actualEndDate` | string | 实际起止日期 | -| `materialRequirements` | MaterialRequirement[] | 物料需求列表 | -| `createBy` | number | 创建人 | -| `remark` | string | 备注 | - -**核心方法**: - -| 方法 | 职责 | 触发事件 | -|---|---|---| -| `approve(userId)` | 审核(草稿 → 已审核) | `WorkOrderApprovedEvent` | -| `start()` | 开工(已审核/领料中 → 生产中),记录实际开始时间 | `WorkOrderStartedEvent` | -| `markPicking()` | 标记领料中(已审核 → 领料中) | `WorkOrderPickingEvent` | -| `issueMaterials(issues)` | 领料(已审核/领料中/生产中可操作),扣减 BOM 已发数量 | `WorkOrderMaterialIssuedEvent` | -| `complete(completedQty, warehouseId)` | 完工(生产中 → 已完工),累计完工数量并校验不超计划 | `WorkOrderCompletedEvent` | -| `close()` | 结案(已完工 → 已结案) | `WorkOrderClosedEvent` | -| `cancel(reason, userId)` | 作废(非结案/作废/完工状态可作废) | `WorkOrderCancelledEvent` | - -**工厂方法**:`create(props)`(新建,触发 `WorkOrderCreatedEvent`)、`reconstitute(props)`(从数据库重建)。 - -**物料需求(MaterialRequirement)**:实体,承载 `materialId`、`materialCode`、`materialName`、`requiredQty`、`issuedQty`,支持 `issue(quantity)` 累计发料。 - -### 3.2 聚合根:PickOrder(领料单) - -文件:`src/domain/production/aggregates/PickOrder.ts` - -领料单关联工单,3 态状态机,审核后扣减库存。 - -| 属性 | 说明 | -|---|---| -| `id` / `pickNo` | 主键、领料单号 | -| `workOrderId` | 关联工单 ID(必填) | -| `warehouseId` | 出库仓库(默认 1) | -| `pickerName` | 领料人 | -| `totalQty` | 总数量(明细汇总) | -| `status` | PickOrderStatus:draft / approved / cancelled | -| `items` | 领料明细(PickOrderItem[]) | - -**PickOrderItem**:`materialId`、`materialName`、`materialSpec`、`requiredQty`、`actualQty`、`batchNo`、`unitCost`、`lineAmount`、`unit`。 - -**核心方法**: - -| 方法 | 职责 | 触发事件 | -|---|---|---| -| `approve(userId)` | 审核(草稿 → 已审核),携带明细触发库存扣减 | `PickOrderApprovedEvent` | -| `cancel(reason, userId)` | 作废(草稿/已审核 → 已作废) | `PickOrderCancelledEvent` | - -### 3.3 聚合根:WorkReport(报工单) - -文件:`src/domain/production/aggregates/WorkReport.ts` - -报工单记录工序产出,3 态状态机,审核后联动工装使用次数与成本归集。 - -| 属性 | 说明 | -|---|---| -| `id` / `reportNo` | 主键、报工单号 | -| `workOrderId` | 关联工单 ID(必填) | -| `processName` | 工序名称(必填) | -| `equipmentId` / `equipmentName` | 设备 | -| `shift` | 班次 | -| `operatorName` | 操作员 | -| `qualifiedQty` | 合格数量 | -| `defectiveQty` | 不良数量 | -| `defectReason` | 不良原因 | -| `workHours` | 工时 | -| `reportDate` | 报工日期 | -| `status` | WorkReportStatus:draft / approved / cancelled | - -**核心方法**: - -| 方法 | 职责 | 触发事件 | -|---|---|---| -| `approve(userId)` | 审核(草稿 → 已审核),携带 toolIds 与工序信息 | `WorkReportApprovedEvent` | -| `cancel(reason, userId)` | 作废(草稿/已审核 → 已作废) | `WorkReportCancelledEvent` | - -**校验规则**:合格数或不良数至少有一个大于 0。 - -### 3.4 聚合根:FinishOrder(完工入库单) - -文件:`src/domain/production/aggregates/FinishOrder.ts` - -完工入库单承载成品入库信息,3 态状态机,审核后增加成品库存。 - -| 属性 | 说明 | -|---|---| -| `id` / `finishNo` | 主键、完工单号 | -| `workOrderId` | 关联工单 ID(必填) | -| `warehouseId` | 入库仓库(必填) | -| `qualifiedQty` | 合格数量(必填,>0) | -| `defectiveQty` | 不良数量 | -| `status` | FinishOrderStatus:draft / approved / cancelled | - -**核心方法**: - -| 方法 | 职责 | 触发事件 | -|---|---|---| -| `approve(userId, workOrderNo, productName)` | 审核(草稿 → 已审核),携带完整入库信息 | `FinishOrderApprovedEvent` | -| `cancel(reason, userId)` | 作废(草稿/已审核 → 已作废) | `FinishOrderCancelledEvent` | - -## 4. 值对象与状态机 - -### 4.1 WorkOrderStatus(工单状态 - 7 态) - -文件:`src/domain/production/value-objects/WorkOrderStatus.ts` - -| 状态值 | DB Code | 标签 | -|---|---|---| -| `draft` | 1 | 草稿 | -| `approved` | 2 | 已审核 | -| `picking` | 3 | 领料中 | -| `in_progress` | 4 | 生产中 | -| `completed` | 5 | 已完工 | -| `closed` | 6 | 已结案 | -| `cancelled` | 7 | 已作废 | - -**状态流转**: - -```mermaid -stateDiagram-v2 - [*] --> draft: create - draft --> approved: approve - draft --> cancelled: cancel - approved --> picking: markPicking - approved --> cancelled: cancel - approved --> in_progress: start - picking --> in_progress: start - picking --> cancelled: cancel - in_progress --> completed: complete - in_progress --> cancelled: cancel - completed --> closed: close - closed --> [*] - cancelled --> [*] -``` - -| 当前状态 | 允许流转到 | -|---|---| -| `draft` | `approved`, `cancelled` | -| `approved` | `picking`, `cancelled` | -| `picking` | `in_progress`, `cancelled` | -| `in_progress` | `completed`, `cancelled` | -| `completed` | `closed` | -| `closed` | (终态) | -| `cancelled` | (终态) | - -**操作权限**: -- `canEdit` / `canDelete` / `canApprove`:仅草稿 -- `canPick`:仅已审核 -- `canStart`:已审核或领料中 -- `canComplete`:仅生产中 -- `canClose`:仅已完工 -- `canCancel`:非结案/作废/完工状态 - -**WorkOrderStatusVO 方法**:`canTransitionTo(target)`、`transitionTo(target)`(非法流转抛 `DomainError`)、`fromDbCode(code)`、`toDbCode()`、`label()`、`equals(other)`。 - -### 4.2 其他聚合根状态机 - -| 聚合根 | 状态 | 流转 | -|---|---|---| -| PickOrder | draft → approved / cancelled | 仅 draft 可 approve;draft/approved 可 cancel | -| WorkReport | draft → approved / cancelled | 仅 draft 可 approve;draft/approved 可 cancel | -| FinishOrder | draft → approved / cancelled | 仅 draft 可 approve;draft/approved 可 cancel | - -## 5. 领域事件 - -### 5.1 工单事件 - -文件:`src/domain/production/events/WorkOrderEvents.ts` - -| 事件类 | eventType | 触发时机 | -|---|---|---| -| `WorkOrderCreatedEvent` | `workorder.created` | 创建工单 | -| `WorkOrderApprovedEvent` | `workorder.approved` | 审核通过 | -| `WorkOrderStartedEvent` | `workorder.started` | 开工 | -| `WorkOrderPickingEvent` | `workorder.picking` | 标记领料中 | -| `WorkOrderMaterialIssuedEvent` | `workorder.material_issued` | 领料(携带 issuedItems 明细) | -| `WorkOrderCompletedEvent` | `workorder.completed` | 完工(携带 completedQty、warehouseId) | -| `WorkOrderClosedEvent` | `workorder.closed` | 结案 | -| `WorkOrderCancelledEvent` | `workorder.cancelled` | 作废 | -| `WorkReportedEvent` | `workorder.reported` | 报工(携带 toolIds 用于工装使用联动) | - -### 5.2 领料单事件 - -文件:`src/domain/production/events/PickOrderEvents.ts` - -| 事件类 | eventType | 触发时机 | -|---|---|---| -| `PickOrderCreatedEvent` | `prod.pick.created` | 创建领料单 | -| `PickOrderApprovedEvent` | `prod.pick.approved` | 领料审核(携带 items 触发库存扣减) | -| `PickOrderCancelledEvent` | `prod.pick.cancelled` | 作废 | -| `MaterialReturnApprovedEvent` | `prod.return.approved` | 退料审核(携带 items 触发库存增加) | - -### 5.3 报工单事件 - -文件:`src/domain/production/events/WorkReportEvents.ts` - -| 事件类 | eventType | 触发时机 | -|---|---|---| -| `WorkReportCreatedEvent` | `prod.report.created` | 创建报工单 | -| `WorkReportApprovedEvent` | `prod.report.approved` | 报工审核(携带 toolIds 联动工装使用) | -| `WorkReportCancelledEvent` | `prod.report.cancelled` | 作废 | - -### 5.4 完工入库单事件 - -文件:`src/domain/production/events/FinishOrderEvents.ts` - -| 事件类 | eventType | 触发时机 | -|---|---|---| -| `FinishOrderCreatedEvent` | `prod.finish.created` | 创建完工单 | -| `FinishOrderApprovedEvent` | `prod.finish.approved` | 完工审核(触发库存增加) | -| `FinishOrderCancelledEvent` | `prod.finish.cancelled` | 作废 | - -### 5.5 退料事件 - -文件:`src/domain/production/events/ReturnOrderEvents.ts` - -包含生产退料相关事件,与 `MaterialReturnApprovedEvent` 协同处理退料入库。 - -### 5.6 事件处理器注册 - -文件:`src/application/EventRegistry.ts` - -| eventType | 处理器 | -|---|---| -| `workorder.created` | `AuditLogHandler`、`CacheInvalidationHandler` | -| `workorder.reported` | `ToolUsageSyncHandler`(幂等) | -| `workorder.material_issued` | `WorkOrderMaterialIssuedHandler`(幂等)、`AuditLogHandler` | -| `workorder.completed` | `WorkOrderCompletedHandler`、`FinanceVoucherHandler`、`QrCodeGenerationHandler`、`InkCostHandler`、`ScreenPlateCostHandler`、`ToolCostHandler`、`AuditLogHandler`、`CacheInvalidationHandler` | -| `workorder.closed` | `ProductionFinanceHandler`(幂等)、`AuditLogHandler` | -| `prod.pick.approved` | `PickOrderInventoryHandler`(幂等)、`AuditLogHandler`、`CacheInvalidationHandler` | -| `prod.return.approved` | `MaterialReturnInventoryHandler`(幂等)、`AuditLogHandler`、`CacheInvalidationHandler` | -| `prod.report.approved` | `WorkOrderMaterialIssuedHandler`(幂等)、`AuditLogHandler` | -| `prod.finish.approved` | `FinishOrderInventoryHandler`(幂等)、`AuditLogHandler`、`CacheInvalidationHandler` | - -## 6. 事件链路:FinishOrderApprovedEvent 完整链路 - -完工入库审核后的事件链路是生产模块最关键的库存联动场景,完整路径如下: - -``` -┌─────────────────────────────────────────────────────────────────────┐ -│ 1. Route 层(HTTP 入口) │ -│ src/app/api/warehouse/production-inbound/route.ts │ -│ PUT /api/warehouse/production-inbound body: { id, action: 'post' }│ -└──────────────────────────┬──────────────────────────────────────────┘ - │ - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 2. Route 事务内操作(transaction) │ -│ a. SELECT 入库单 FOR UPDATE(校验 status < 3、qc_status ≠ fail) │ -│ b. 遍历明细:UPDATE/INSERT inv_inventory(增加成品库存) │ -│ c. INSERT inv_inventory_transaction(库存流水,source_type= │ -│ 'production_inbound') │ -│ d. UPDATE inv_production_inbound SET status = 3 │ -│ e. UPDATE prod_work_order SET completed_qty += totalQty │ -│ f. 工单状态联动:completed_qty >= plan_qty → status=50 │ -│ completed_qty > 0 → status=40 │ -│ g. ★ 写入 Outbox:getDomainEventOutbox().saveEvents(conn, │ -│ 'ProductionInbound', id, [new FinishOrderApprovedEvent(...)]) │ -└──────────────────────────┬──────────────────────────────────────────┘ - │ 事务提交 - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 3. Route 事务后操作 │ -│ a. 生成成品二维码 qrcode_record(每个明细一条,qr_type='product') │ -│ b. logOperation 记录操作日志 │ -└──────────────────────────┬──────────────────────────────────────────┘ - │ Outbox Poller 投递 - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 4. Event Bus 分发(EventRegistry 注册) │ -│ eventType: 'prod.finish.approved' │ -│ 订阅者: │ -│ - IdempotentHandler(FinishOrderInventoryHandler) │ -│ - AuditLogHandler │ -│ - CacheInvalidationHandler │ -└──────────────────────────┬──────────────────────────────────────────┘ - │ - ▼ -┌─────────────────────────────────────────────────────────────────────┐ -│ 5. FinishOrderInventoryHandler.handle(event) │ -│ 文件:src/application/handlers/FinishOrderInventoryHandler.ts │ -│ 功能:增加成品库存 + 回写工单完工数量(幂等) │ -│ │ -│ 幂等机制:INSERT IGNORE INTO inv_inventory_transaction, │ -│ 唯一索引 uk_inv_txn_source (source_type, source_id) 防重复 │ -│ - source_type = 'prod_finish' │ -│ - source_id = finishOrderId │ -│ - affectedRows === 0 → 已处理,跳过 │ -│ - affectedRows === 1 → 首次处理,继续 │ -│ │ -│ 事务内操作: │ -│ a. SELECT prod_work_order 获取产品信息 │ -│ b. INSERT IGNORE inv_inventory_transaction(幂等屏障) │ -│ c. SELECT inv_inventory 判断库存是否存在 │ -│ d. 存在:UPDATE inv_inventory SET quantity += qualifiedQty │ -│ e. 不存在:INSERT inv_inventory │ -└─────────────────────────────────────────────────────────────────────┘ -``` - -**关键设计要点**: - -1. **Transactional Outbox**:Route 层在事务内写入 `domain_event_outbox` 表,保证聚合状态变更与事件发布原子性 -2. **幂等处理**:`FinishOrderInventoryHandler` 通过 `INSERT IGNORE` + 唯一索引 `uk_inv_txn_source` 防止 XAUTOCLAIM 重投递导致的重复库存增加(TOCTOU 竞态) -3. **事务边界**:Handler 内部使用独立事务包裹 INSERT IGNORE + 库存 UPDATE,保证三步原子性 -4. **双写说明**:当前 Route 层在事务内已直接更新 `inv_inventory`,Handler 作为事件驱动补充(二维码生成、审计等),两者通过幂等机制保证最终一致 - -> **相关文档**:`docs/07-决策记录/005-完工入库幂等策略.md` 详细记录幂等设计决策。 - -## 7. API 接口 - -### 7.1 工单 API - -| 方法 | 路径 | 功能 | -|---|---|---| -| GET | `/api/workorders` | 工单列表查询(支持状态/产品/日期筛选) | -| POST | `/api/workorders` | 创建工单 | -| PUT | `/api/workorders` | 更新工单 / 状态流转 | -| DELETE | `/api/workorders` | 删除工单(仅草稿) | - -### 7.2 生产入库 API - -文件:`src/app/api/warehouse/production-inbound/route.ts` - -| 方法 | 路径 | 功能 | -|---|---|---| -| GET | `/api/warehouse/production-inbound` | 入库单列表(支持 inboundNo/status/workOrderNo 筛选) | -| POST | `/api/warehouse/production-inbound` | 创建入库单(校验工单状态 ≥ 20 且 < 90) | -| PUT | `/api/warehouse/production-inbound` | `action=post` 过账入库 / `action=qc` 质检 / 更新状态 | -| DELETE | `/api/warehouse/production-inbound` | 删除入库单(仅 status < 3) | - -**过账(action=post)流程**: -1. 校验入库单状态与质检结果 -2. 遍历明细:增加 `inv_inventory` 库存 + 写 `inv_inventory_transaction` 流水 -3. 更新入库单 `status = 3` -4. 回写工单 `completed_qty` 并联动工单状态(40 生产中 / 50 已完工) -5. 写入 `FinishOrderApprovedEvent` 到 Outbox -6. 事务后生成成品二维码 `qrcode_record` - -### 7.3 退料 API - -| 方法 | 路径 | 功能 | -|---|---|---| -| GET | `/api/material-returns` | 退料单列表 | -| POST | `/api/material-returns` | 创建退料单 | -| PUT | `/api/material-returns` | 审核退料单(触发 `MaterialReturnApprovedEvent`) | - -### 7.4 领料/报工 API - -领料单与报工单的 API 通过工单聚合根的 `issueMaterials`、报工审核等用例间接暴露,主要经由 `/api/workorders` 与生产管理页面调用。 - -### 7.5 关联 API - -| 路径 | 功能 | -|---|---| -| `/api/dashboard/production` | 生产看板 | -| `/api/advanced/scheduling/optimize` | 排产优化 | -| `/api/material-requisitions` | 物料领用 | -| `/api/material-requisitions/[id]/issue` | 物料领用发料 | - -## 8. 业务流程 - -### 8.1 工单全生命周期 - -``` -创建工单(draft) - ↓ approve -已审核(approved) - ↓ markPicking / issueMaterials -领料中(picking)—— 领料审核触发 PickOrderApprovedEvent → 库存扣减 - ↓ start -生产中(in_progress)—— 报工审核触发 WorkReportApprovedEvent → 工装使用累计 - ↓ complete(completedQty, warehouseId) -已完工(completed)—— 触发 WorkOrderCompletedEvent → 成本归集/二维码生成 - ↓ close -已结案(closed)—— 触发 WorkOrderClosedEvent → 财务凭证 -``` - -### 8.2 完工入库流程 - -``` -创建入库单(status=1) - ↓ 质检(action=qc,qc_status=pass/fail) - ↓ 过账(action=post) -入库完成(status=3) - ├─→ 增加成品库存 inv_inventory - ├─→ 写库存流水 inv_inventory_transaction - ├─→ 回写工单 completed_qty + 状态联动 - ├─→ 写 Outbox: FinishOrderApprovedEvent - └─→ 生成成品二维码 qrcode_record - ↓ Outbox Poller 投递 -FinishOrderInventoryHandler(幂等兜底) -``` - -## 9. 关键文件清单 - -| 文件 | 说明 | -|---|---| -| `src/domain/production/aggregates/WorkOrder.ts` | 生产工单聚合根(7 态状态机) | -| `src/domain/production/aggregates/PickOrder.ts` | 领料单聚合根(3 态) | -| `src/domain/production/aggregates/WorkReport.ts` | 报工单聚合根(3 态) | -| `src/domain/production/aggregates/FinishOrder.ts` | 完工入库单聚合根(3 态) | -| `src/domain/production/entities/MaterialRequirement.ts` | 物料需求实体 | -| `src/domain/production/entities/ProductionSchedule.ts` | 排产计划实体 | -| `src/domain/production/value-objects/WorkOrderStatus.ts` | 工单状态机值对象 | -| `src/domain/production/value-objects/WorkOrderStateMachine.ts` | 工单状态机辅助 | -| `src/domain/production/events/WorkOrderEvents.ts` | 工单事件(9 个) | -| `src/domain/production/events/PickOrderEvents.ts` | 领料单事件(4 个) | -| `src/domain/production/events/WorkReportEvents.ts` | 报工单事件(3 个) | -| `src/domain/production/events/FinishOrderEvents.ts` | 完工单事件(3 个) | -| `src/domain/production/events/ReturnOrderEvents.ts` | 退料事件 | -| `src/domain/production/repositories/IWorkOrderRepository.ts` | 工单仓储接口 | -| `src/domain/production/repositories/IPickOrderRepository.ts` | 领料单仓储接口 | -| `src/domain/production/repositories/IWorkReportRepository.ts` | 报工单仓储接口 | -| `src/domain/production/repositories/IFinishOrderRepository.ts` | 完工单仓储接口 | -| `src/domain/production/repositories/IScheduleRepository.ts` | 排产仓储接口 | -| `src/application/handlers/FinishOrderInventoryHandler.ts` | 完工入库库存联动处理器 | -| `src/application/handlers/PickOrderInventoryHandler.ts` | 领料库存扣减处理器 | -| `src/application/handlers/WorkOrderCompletedHandler.ts` | 工单完工处理器 | -| `src/application/handlers/WorkOrderMaterialIssuedHandler.ts` | 领料发料处理器 | -| `src/application/handlers/MaterialReturnInventoryHandler.ts` | 退料入库处理器 | -| `src/application/handlers/ToolUsageSyncHandler.ts` | 工装使用同步处理器 | -| `src/application/handlers/ToolCostHandler.ts` | 工装成本归集处理器 | -| `src/application/handlers/InkCostHandler.ts` | 油墨成本归集处理器 | -| `src/application/handlers/ScreenPlateCostHandler.ts` | 网版成本归集处理器 | -| `src/application/handlers/ProductionFinanceHandler.ts` | 生产财务凭证处理器 | -| `src/app/api/warehouse/production-inbound/route.ts` | 生产入库 API 路由 | -| `src/app/api/workorders/route.ts` | 工单 API 路由 | -| `src/app/api/material-returns/route.ts` | 退料 API 路由 | - -> 最后更新:2026-07-15 diff --git "a/docs/02-\346\250\241\345\235\227\350\257\246\347\273\206\350\256\276\350\256\241/DRIZZLE-SCHEMA-\350\241\245\345\205\250\350\256\241\345\210\222-WAREHOUSE.md" "b/docs/02-\346\250\241\345\235\227\350\257\246\347\273\206\350\256\276\350\256\241/DRIZZLE-SCHEMA-\350\241\245\345\205\250\350\256\241\345\210\222-WAREHOUSE.md" deleted file mode 100644 index 47d97ef9..00000000 --- "a/docs/02-\346\250\241\345\235\227\350\257\246\347\273\206\350\256\276\350\256\241/DRIZZLE-SCHEMA-\350\241\245\345\205\250\350\256\241\345\210\222-WAREHOUSE.md" +++ /dev/null @@ -1,126 +0,0 @@ -# Drizzle Schema 补全计划(Warehouse 域 / DATA-001 实质处理) - -> 目标:补全仓库域 Drizzle schema,使与 live `information_schema` 在「表 / 列 / 类型 / 索引 / FK」上一致。 -> **实测修正(2026-08-25)**:live 共有 **82 张 `inv_*` 表**(其中约 44 张是 `_bak*` 备份表,系 08-16/17 链修复遗留,已排除不建模);**核心 38 张已建模**(10 张手写于 `warehouse.ts` + 28 张 Phase 0 生成于 `_gen_warehouse_missing.ts`)。剩余缺口为约 129 个外键中未被建模的部分(详见 §0)。 -> 原则:**只读生成、零破坏、可回滚、不依赖 `db:push` 建表**(表已存在,补全仅为类型安全 + studio 准确 + 未来 Drizzle 查询)。 -> 关联文档:`docs/02-模块详细设计/WAREHOUSE-模块详解-校验版.md`(§2、§14、风险注已据本计划修正)。 - ---- - -## 0. 现状复核(2026-08-25 实测,纠正此前误判) - -| 维度 | 真实状态 | -|---|---| -| 真实库 `inv_*` 表 | **82 张**(live `information_schema`);其中约 **44 张为 `_bak*` 备份表**(08-16/17 链修复遗留),已排除不建模 | -| 核心 `inv_*` 表(非备份) | **38 张**均已建模 | -| ├ 手写于 `warehouse.ts` | **10 张**:`inv_material`、`inv_inventory_batch`、`inv_inbound_order`(导出名`invInboundOrders`)、`inv_inbound_item`(`invInboundItems`)、`inv_warehouse`、`inv_inventory`、`inv_outbound_order`(`invOutboundOrders`)、`inv_outbound_item`(`invOutboundItems`)、`inv_transfer_order`(`invTransferOrders`)、`inv_stocktaking` | -| └ Phase 0 生成于 `_gen_warehouse_missing.ts` | **28 张**(见 §4 列表),已与 live 逐列对齐 `missing=0/extra=0`、`tsc` 0 错误 | -| Drizzle FK 覆盖 | 28 张生成表内已声明 **14 个内部 FK**;**6 个被跳过**(1 自引用 + 5 跨域,见下) | -| 全库外键 | 共 **129 个**;其中 **48 个 `inv_*` 内部**、**81 个跨域**(指向 `sys_user`/`pur_purchase_order`/`sal_order` 等非仓库表,Drizzle 未建模这些表,暂以注释保留,依赖 safe-push 护栏) | -| `db:push`/`generate`/`migrate` | ⚠️ **`db:push` 已改造为安全护栏**(见 §3):`package.json` 的 `db:push` 现指向 `scripts/safe-db-push.cjs`,命中任何 DROP 风险即硬拦截;专家绕行用 `db:push-raw`。`db:migrate`(drizzle-kit)指向空 `./drizzle`,无操作;增量迁移请用 `pnpm migrate` | - -> ⚠️ 此前文档称「37 张 `inv_*` 全未建模」「待补 27 张」均基于**陈旧的 `vnerpdacahng_schema.sql`**(该 SQL 漏了迁移补的列,且表清单不全)。**本文及生成器一律以 live `information_schema` 为权威源**。现 38 张核心表已建模,DATA-001 的「表缺失」缺口已实质性闭合;剩余为**跨域外键 + 备份表**两类,非仓库表建模问题。 - ---- - -## 1. 目标与原则 - -- **目标**:先补齐 27 张仓库表 + 全量 ~129 外键(含指向 `inv_*` 的跨域 FK 与 `inv_*` 内部 FK)。 -- **原则** - 1. **只读生成,零破坏**:生成器只 `SELECT information_schema`,永不写库。 - 2. **不靠 `db:push` 建表**:表已存在;补全仅服务于类型安全 / `db:studio` / 未来查询。 - 3. **任何写库(含 `db:push`)必须经 diff 守护确认无 `DROP` 才可执行**。 - 4. **可回滚**:纯文件改动,走 git。 - ---- - -## 2. 策略:自动化 introspection 生成器(禁止手写 27 表) - -手写 27 表 × ~15 列 + 129 FK 极易出错且难校验。改为「读 live `information_schema` → 生成 Drizzle 定义」: - -1. `information_schema.tables` → 取 27 张缺失 `inv_*` 表。 -2. `information_schema.columns` → 按类型映射表生成列定义。 -3. `information_schema.key_column_usage` + `referential_constraints` → 生成 `foreignKey()`。 -4. `information_schema.statistics` → 生成索引。 -5. 输出到新文件 `src/lib/db/schemas/_gen_warehouse_missing.ts`(不污染手写 `warehouse.ts`)。 - ---- - -## 3. 安全护栏(最高优先级)✅ 已落地(2026-08-26) - -> ⚠️ **根本性结论(实测后重构)**:本仓库 Drizzle **仅建模 140 张表**,而 live 实际 **327 张**(含大量 `_ghost_backup_*` / `_bak_*` 修复残留表)。**`drizzle-kit push` 永远不安全**——它会 DROP 掉约 187 张「Drizzle 不认识」的 live 表及其全部数据。因此 Drizzle schema 的用途**仅限于 ORM 查询构建**,**任何结构变更都必须走增量迁移**(`pnpm migrate` → `scripts/migrate.ts` + `database/migrations/`),而非 push。 - -- **`db:push` 已改为安全护栏**:`package.json` 的 `db:push` 现指向 `node scripts/safe-db-push.cjs`。该脚本在真正 push 前做只读 introspection,检测三类 DROP 风险(弃表 / 弃列 / 弃键),**命中任意一项即 `exit 1` 硬拦截,绝不执行 push**。实测一次即捕获 **64 类 DROP 风险(194 弃表 + 多表弃列 + 多个指向 `crm_customer`/`eqp_equipment` 的弃键)**。 -- **专家绕行通道**:原 `db:push-raw`(裸 `drizzle-kit push`)已于 2026-08-27 Phase 0 护栏中移除;任何 schema 变更一律走 `pnpm migrate`(`scripts/migrate.ts`)。`db:generate`/`db:migrate` 现由 `scripts/guard-drizzle-kit.cjs` 拦截。 -- **`db:studio` 安全**:仅可视化查看,只读,无 DROP 风险,保持可用。 -- **生成器只读**:仅 `SELECT`,不 `INSERT/UPDATE/ALTER`。 -- **`verify-schema-sync.mjs`(索引/FK 级全量 diff)**:✅ 已由 `scripts/safe-db-push.cjs` 内置实现(表/列/键三级 diff),并额外提供 `scripts/_audit/verify_gen_aligned.cjs`(列级对齐)。独立 `verify-schema-sync.mjs` 非必需,护栏已覆盖其核心能力。 -- **现有 10 张表**:列类型以手写版为准(保留注释),**仅由生成器补 FK 片段**,不覆盖手写列定义。 - ---- - -## 4. 分阶段执行 - -| 阶段 | 动作 | 产出 | 状态 | -|---|---|---|---| -| **Phase 0** | 写 `scripts/_audit/gen_warehouse_missing.cjs`(只读 introspection → 生成 `_gen_warehouse_missing.ts` + FK 片段) | 生成器脚本 | ✅ 已完成 | -| **Phase 1** | 运行生成器(live 只读连接,凭证取自 `.env`) | **28 张核心表定义**(排除 `_bak*`)+ 14 内部 FK 片段;列对齐 `missing=0/extra=0` | ✅ 已完成 | -| **Phase 2** | 合并:新增 `_gen_warehouse_missing.ts` 并 re-export 到 `src/lib/db/schema.ts`(`warehouse.ts` 10 张保持手写不动) | 更新后的 schema 文件;`tsc --noEmit` 0 错误 | ✅ 已完成 | -| **Phase 3** | 写并运行 `scripts/_audit/verify_gen_aligned.cjs`(列级对齐校验);`safe-db-push.cjs` 内置表/列/键三级 diff(即 `verify-schema-sync` 能力) | 列级校验通过;全量 diff 由护栏覆盖 | ✅ 已完成 | -| **Phase 4** | `db:push` 改造为安全护栏(`scripts/safe-db-push.cjs`,含 DROP 硬拦截);`package.json` `db:push` 已指向护栏,`db:push-raw` 保留专家绕行 | 受控的 schema 工具链 | ✅ 已完成(2026-08-26) | - -> 28 张 Phase 0 生成表:`inv_auxiliary_inventory, inv_cutting_detail, inv_cutting_record, inv_fifo_override_log, inv_inbound_label, inv_inventory_log, inv_inventory_transaction, inv_inventory_transaction_log, inv_location, inv_material_category, inv_material_inventory, inv_material_label, inv_material_std, inv_outbound_batch_allocation, inv_product_inventory, inv_production_inbound, inv_production_inbound_item, inv_sales_outbound, inv_sales_outbound_item, inv_scan_log, inv_stock_adjust, inv_stock_adjust_item, inv_stocktaking_item, inv_trace_detail, inv_trace_record, inv_transfer_item, inv_unit_conversion, inv_warehouse_log`。 - ---- - -## 5. 验证(当前进度) - -- ✅ `npx tsc --noEmit`:**0 错误**(含新增 28 表 + re-export)。 -- ✅ `scripts/_audit/verify_gen_aligned.cjs`:**28 张表全部 `[OK]`,`missing=0/extra=0`**(列级对齐)。 -- ✅ `scripts/safe-db-push.cjs`(即 `verify-schema-sync` 能力):表/列/键三级 diff,命中 DROP 即硬拦截;实测捕获 64 类 DROP 风险并 abort(证明护栏有效)。 -- ✅ 现有仓库 API(原生 SQL)回归:**未改运行时查询路径**,仅补 Drizzle 类型定义,无影响。 -- 🚫 **`db:push` 已永久禁用为安全护栏**;结构变更一律走 `pnpm migrate`(增量迁移)。`db:studio` 仍可只读查看。 - ---- - -## 6. 风险与回滚 - -- 生成器只读 → **零风险**。 -- 合并阶段为纯文件写入 → `git revert` / 分支回滚即可。 -- `db:push` 的 DROP-FK 风险 → 由 §3 护栏彻底消除(默认禁止 + diff 守护)。 -- 既有 10 张表手写列可能与 live 存在小漂移 → Phase 3 校验会暴露,按需单独对齐(不强迫覆盖手写注释)。 - ---- - -## 7. 类型映射表(MySQL → Drizzle `mysql-core`) - -| MySQL 类型 | Drizzle | -|---|---| -| `int` / `integer` | `int()` | -| `bigint` | `bigint()` | -| `tinyint(1)` | `boolean()` | -| `tinyint` | `tinyint()` | -| `smallint` / `mediumint` | `smallint()` / `mediumint()` | -| `varchar(n)` / `char(n)` | `varchar(n)` / `char(n)` | -| `text` / `longtext` | `text()` / `longtext()` | -| `decimal(p,s)` | `decimal(p, s)` | -| `float` / `double` | `float()` / `double()` | -| `datetime` / `timestamp` | `datetime()` / `timestamp()` | -| `date` | `date()` | -| `time` | `time()` | -| `json` | `json()` | -| `blob` / `mediumblob` | `blob()` / `mediumblob()` | -| `enum(...)` | `mysqlEnum('x', [...])` | - -> 注意:`NOT NULL`、默认值、`unsigned`、`autoIncrement`、`onUpdate` 均需从 `columns` / `extra` 字段还原;`charset`/`collate` 一般可省(继承库默认)。 - ---- - -## 8. 待确认 / 下一步 - -1. **Phase 0–4 已全部完成**(28 张核心表建模 + 列对齐 + 14 内部 FK + re-export + tsc 0 错误 + 安全护栏 `safe-db-push.cjs` 落地并实测有效)。这部分改动已**独立 commit**(`4f275ea0` 仓库表补全 + 本次护栏),尚未 push。 -2. **可选:补 6 处跳过的 FK**(1 自引用 `inv_material_category.parent_id` + 5 跨域)——目前由护栏兜底,非紧急。 -3. **`drizzle.config.ts` 过时「已废弃」注释**:可顺手修正(注释称 db:push 是警告提示,实际已是安全护栏),避免误导后续接手者。 -4. **跨域 81 外键 / 44 张 `_bak*` 备份表 / 全库仅 140/327 表建模**:属更深层的「全库 Drizzle 覆盖率」问题,超出本仓库域范围。建议另立专项:建模 `sys_*`/`pur_*`/`sal_*` 等域,或先清理 `_ghost_backup_*`/`_bak_*` 修复残留表,使 Drizzle 覆盖度提升;在此之前 `db:push` 永远不安全。 - -> 结论:DATA-001 的「仓库表缺失」缺口已**实质闭合**(38 张核心 `inv_*` 全部建模、列对齐、tsc 通过),且 **DROP 高危已通过 `safe-db-push.cjs` 护栏彻底消除**——`db:push` 默认硬拦截,结构变更一律走增量迁移 `pnpm migrate`。Drizzle 在本仓库的定位是「ORM 查询构建 + 类型安全」,而非「schema 同步源」。 diff --git "a/docs/02-\346\250\241\345\235\227\350\257\246\347\273\206\350\256\276\350\256\241/WAREHOUSE-\346\250\241\345\235\227\350\257\246\350\247\243-\346\240\241\351\252\214\347\211\210.md" "b/docs/02-\346\250\241\345\235\227\350\257\246\347\273\206\350\256\276\350\256\241/WAREHOUSE-\346\250\241\345\235\227\350\257\246\350\247\243-\346\240\241\351\252\214\347\211\210.md" deleted file mode 100644 index e5c1e79a..00000000 --- "a/docs/02-\346\250\241\345\235\227\350\257\246\347\273\206\350\256\276\350\256\241/WAREHOUSE-\346\250\241\345\235\227\350\257\246\350\247\243-\346\240\241\351\252\214\347\211\210.md" +++ /dev/null @@ -1,250 +0,0 @@ -# 仓库管理(Warehouse)模块详解(代码校验版) - -> **校验说明**:本文基于 `D:\dcprint\erp-project` 实际代码(2026-08-25 核查)逐条修订。 -> 原稿中已核对正确的部分予以保留;**错误/过时部分用 `⚠️ 修正` 标注并给出真实情况**。 -> 末附《校验结论清单》逐条列出每项声明的真伪。 - ---- - -## 1. 模块定位 -仓储模块是典型的业务中枢,处理从采购入库、生产领料、销售出库到库存调拨的完整物流链路。采用 DDD 分层架构,分离了领域逻辑(`src/domain/warehouse/aggregates` + `entities`)、应用服务(`src/application/services`)、基础设施(`src/infrastructure`,含事件总线与 mysql2 访问层)与表现层(`src/app/[locale]/warehouse` + `src/app/api/warehouse`)。 - -``` -销售/采购单 → 入库 → 库存 → 出库 → 财务凭证 - ↑ ↓ ↓ ↓ ↓ - 生产工单 ← 领料 ← 盘点 ← 调拨 ← 质检结果 -``` - ---- - -## 2. 核心数据模型(⚠️ 修正:原稿"8 张 inv_*(Drizzle 管理)+ 5 张遗留表(原生 SQL)"有误) - -### ⚠️ 关键修正(2026-08-25 三次实测校正,以 live `information_schema` 为准) -- 仓库域 **运行时仍主要通过原生 SQL(`mysql2`)访问**,但 Drizzle **已实质覆盖核心表**: - - **10 张手写于 `warehouse.ts`**:`inv_material`、`inv_inventory_batch`、`inv_inbound_order`(导出名`invInboundOrders`)、`inv_inbound_item`(`invInboundItems`)、`inv_warehouse`、`inv_inventory`、`inv_outbound_order`(`invOutboundOrders`)、`inv_outbound_item`(`invOutboundItems`)、`inv_transfer_order`(`invTransferOrders`)、`inv_stocktaking`(外加 2 张 `split_order`/`split_order_detail`)。 - - **28 张由 Phase 0 生成于 `_gen_warehouse_missing.ts`**(列已与 live 逐列对齐 `missing=0/extra=0`,含 14 个内部 FK)。 - - **合计 38 张核心 `inv_*` 表已全部建模**(详见 `DRIZZLE-SCHEMA-补全计划-WAREHOUSE.md` §0/§4)。 -- 数量澄清:live 共有 **82 张 `inv_*` 表**,但其中约 **44 张是 `_bak*` 备份表**(08-16/17 链修复遗留),**已排除不建模**;核心仅 38 张。此前"37 张全部未建模 / 待补 27 张"均基于**陈旧的 `vnerpdacahng_schema.sql`**,**不实**。 -- **FK 现状**:全库 live 共 **129 个外键**,其中 **48 个 `inv_*` 内部 / 81 个跨域**(指向 `sys_user`、`pur_purchase_order`、`sal_order` 等非仓库表)。Drizzle 已声明 14 个内部 FK;跨域 FK 因目标表未建模而暂以注释保留,**依赖 `safe-db-push` 护栏防止误 DROP**。 -- 原稿"8 张 Drizzle + 5 张遗留"二分法不成立。正确表述:**核心 38 张 `inv_*` 表已全部建模;剩余为跨域外键与备份表,非仓库表缺失问题**。补全方案见 `DRIZZLE-SCHEMA-补全计划-WAREHOUSE.md`。 - -### 实际 `inv_*` 表清单(live 共 82 张,节选核心表;约 44 张为 `_bak*` 备份表已排除) -| 表名 | 功能 | 关键字段 | 访问方式 | -|---|---|---|---| -| inv_warehouse | 仓库主数据 | warehouse_code、warehouse_name、warehouse_type(raw/finished/semi/scrap)、status、manager_id | 原生 SQL | -| inv_inbound_order | 入库单 | order_no、warehouse_id、supplier_id、po_id、status、qc_status、currency、exchange_rate | 原生 SQL | -| inv_inbound_item | 入库明细 | material_id、batch_no、quantity、unit_price | 原生 SQL | -| inv_inventory | 库存台账 | material_id、warehouse_id、quantity、locked_qty、version | 原生 SQL | -| inv_outbound_order | 出库单 | order_no、warehouse_id、status | 原生 SQL | -| inv_transfer_order | 调拨单 | transfer_no、from_warehouse_id、to_warehouse_id、status | 原生 SQL | -| inv_stocktaking | 盘点单 | stocktaking_no、warehouse_id、status | 原生 SQL | -| inv_inventory_batch | 批次表 | batch_no、available_qty、locked_qty、inbound_date、expire_date | 原生 SQL | -| inv_inventory_transaction / inv_inventory_log | 库存流水/日志 | trans_type、quantity、before_qty、after_qty | 原生 SQL | -| inv_cutting_record / inv_cutting_detail | 切料记录 | — | 原生 SQL | -| inv_material / inv_material_category / inv_material_inventory | 物料主数据/库存 | — | 原生 SQL | -| inv_production_inbound(+_item) / inv_sales_outbound(+_item) | 生产入库/销售出库 | — | 原生 SQL | -| inv_stock_adjust(+_item) | 库存调整 | — | 原生 SQL | -| inv_unit_conversion / inv_location / inv_fifo_override_log / inv_scan_log / inv_trace_record(+_detail) / inv_warehouse_log … | 单位换算/库位/FIFO 覆盖/扫码/追溯/仓库日志 | — | 原生 SQL | - -> 完整 37 张:`inv_auxiliary_inventory, inv_cutting_detail, inv_cutting_record, inv_fifo_override_log, inv_inbound_item, inv_inbound_label, inv_inbound_order, inv_inventory, inv_inventory_batch, inv_inventory_log, inv_inventory_transaction, inv_inventory_transaction_log, inv_location, inv_material, inv_material_category, inv_material_inventory, inv_material_label, inv_material_std, inv_outbound_item, inv_outbound_order, inv_product_inventory, inv_production_inbound, inv_production_inbound_item, inv_sales_outbound, inv_sales_outbound_item, inv_scan_log, inv_stock_adjust, inv_stock_adjust_item, inv_stocktaking, inv_stocktaking_item, inv_trace_detail, inv_trace_record, inv_transfer_item, inv_transfer_order, inv_unit_conversion, inv_warehouse, inv_warehouse_log`。 - ---- - -## 3. 核心聚合根与应用服务(✅ 原稿准确) -### 4 大聚合根(`src/domain/warehouse/aggregates/`) -- `InboundOrder`(InboundOrder.ts) -- `OutboundOrder`(OutboundOrder.ts) -- `TransferOrder`(TransferOrder.ts) -- `StocktakingOrder`(StocktakingOrder.ts) - -> 另有 `src/domain/warehouse/entities/` 下的行项实体:`InboundItem / OutboundItem / StocktakingItem / TransferItem`。 - -### 6 个应用服务(`src/application/services/`,全部存在 ✅) -- `InboundApplicationService` — 入库审批、创建、查询 -- `OutboundApplicationService` — 出库、FIFO 分配 -- `TransferApplicationService` — 调拨管理 -- `StocktakingApplicationService` — 盘点、差异处理 -- `InventoryValidationService` — 库存验证 -- `InventoryCostService` — 成本计算 - ---- - -## 4. 关键业务流程 - -### 4.1 入库规则 📥(✅ 原稿基本准确) -入口:`POST /api/warehouse/inbound` - -支持 action 子路由(均真实存在 ✅):`inbound/cutting`、`inbound/scan`、`inbound/audit`、`inbound/from-po`、`inbound/with-po`、`inbound/labels`。 -核心流程与原稿一致:校验物料/仓库 → 事务内写 `inv_inventory_batch`(status='normal') + `inv_inventory_transaction`(trans_type='in') + `inv_inventory_log` → `generateDocumentNo('inbound')` 生成单号 → `logOperation` → 发布领域事件。 -切料优化见 §8(触发 `action=cutting`,算法在 `src/lib/cutting-optimizer.ts` ✅)。 - -### 4.2 出库规则 📤(✅ 原稿准确) -入口:`POST /api/warehouse/outbound` -- FIFO 分配实现位于 **`src/lib/fifo-allocation.ts`**(含 `allocateFIFO` 与 `executeFIFODeductionWithRetry`,最多 3 次指数退避重试,乐观锁 `WHERE id=? AND available_qty>=? AND version=?`)✅。 -- 金额计算使用 **`decimal.js`**(`import Decimal from 'decimal.js'`,避免浮点误差)✅。 -- 两种模式(指定批次 / FIFO 自动)与结果结构(batchDetails / totalCost / warning)与原稿一致 ✅。 - -### 4.3 调拨规则 🔄(⚠️ 修正:非单步 UPDATE warehouse_id) -入口:`POST /api/warehouse/transfer`,并配套两个子步骤路由: -- `POST /api/warehouse/transfer/[id]/outbound`(先源仓出库) -- `POST /api/warehouse/transfer/[id]/inbound`(后目标仓入库,校验"需先完成出库") - -⚠️ 实际并非原稿所述"仅 UPDATE warehouse_id、warehouse_name 一步改归属"。它是**先出库、后入库的两阶段流程**(`[id]/outbound` 扣减源仓、`[id]/inbound` 入目标仓)。 -- ⚠️ **INV-002(无"在途"中间态)仍为已知简化限制** ✅ 原稿判定正确:当前模型没有显式 `in_transit` 状态,跨仓移动通过 out→in 两步隐含表达。复杂多仓场景可增强。 - -### 4.4 盘点规则 📊(✅ 流程存在,freeze 字段未逐项核实) -入口:`POST /api/warehouse/stocktaking`,配套 `stocktaking/[id]/scan`、`stocktaking/[id]/split-summary`、`stocktaking/diff-process`。 -> 注:原稿所述 `freeze=1` 仓库冻结机制在本次核查中未在 stocktaking 路由/服务内直接命中 `freeze=1` 字面量,可能实现于仓库表字段或 inventory 冻结接口(`/api/warehouse/freeze` 真实存在)。逻辑(创建→扫码→差异→调整→完成解冻)与路由结构吻合,视为基本准确,freeze 落库位置待进一步确认。 - ---- - -## 5. 库存预警与安全机制 ⚠️(接口路径需修正) -预警分级逻辑与原稿一致,但入口路径修正: -- 库存列表/预警:`GET /api/warehouse/inventory`(✅ 存在) -- 预警专用:`GET /api/warehouse/inventory/warning`(✅ 存在,原稿未列出) -- 补货建议:`checkShortageAndWarn` 相关逻辑存在于库存服务/预警路由中。 - -并发控制(乐观锁 version + 重试、事件同步 `InventorySyncHandler`、回滚 `InventoryRollbackHandler`)✅ 与原稿一致。 - ---- - -## 6. 单据作废机制 ❌(⚠️ 修正:INV-001 已修复) -实现:`src/lib/soft-delete.ts`(✅ 存在)。 -- 经查 `soft-delete.ts` 第 84–85 行:`inbound_order: { tableName: 'inv_inbound_order' }` —— **表名映射已正确,无 `warehouse_inbound` 陈旧映射**。 -- 原稿 INV-001「soft-delete.ts 表名映射过时(warehouse_inbound vs inv_inbound_order)🔴 待修复」**不成立,该问题已修复**。 -- 作废流程(查询→状态/业务约束检查→`status='cancelled'` 软删 + `document_cancel_log` 快照→发布 `DocumentCancelledEvent`)、可作废单据清单,与原稿一致 ✅。 - ---- - -## 7. 二维码追溯 🔗(✅ 原稿准确) -- `src/lib/qrcode-service.ts`(✅ 存在) -- `QrCodeGenerationHandler`(`src/application/handlers/QrCodeGenerationHandler.ts` ✅ 存在,另有 `InboundQrInvalidationHandler`) -- 扫码接口:`/api/dcprint/scan`(✅ 存在,`src/app/api/dcprint/scan/route.ts`) - ---- - -## 8. 切料优化 ✂️(✅ 原稿准确) -- 算法:`src/lib/cutting-optimizer.ts`(✅ 存在) -- 落库表:`inv_cutting_record` + `inv_cutting_detail`(✅ 真实存在) -- 触发:`POST /api/warehouse/inbound?action=cutting`(`/api/warehouse/inbound/cutting/route.ts` ✅ 存在) - ---- - -## 9. 多币种支持 💱(✅ 原稿准确) -- `inv_inbound_order` 确含 `currency`(varchar(10) DEFAULT 'CNY') 与 `exchange_rate`(decimal) 列(schema SQL 第 3385–3386 行附近)✅。 -- 出库单 `inv_outbound_order` 同样含 `currency`/`exchange_rate`(第 2789–2790 行)✅。 -- 汇率来源:系统本位币配置 + `currencyService.getLatestRate()`(实现细节以代码为准)。 - ---- - -## 10. 页面与 API 路由 🗂️(⚠️ 修正:原稿页面/路由列表与实际不符) - -### 前端页面(`src/app/[locale]/warehouse/`,实际存在 ✅) -``` -warehouse/ - ├── page.tsx # 批次库存查询(综合面板) - ├── inbound/page.tsx # 入库单管理 - ├── inbound-simple/page.tsx # 简化入库(快速录入) - ├── outbound/page.tsx # 出库单管理 - ├── inventory/page.tsx # 库存详情与批量操作 - ├── transfer/page.tsx # 调拨管理 - ├── stocktaking/page.tsx # 盘点管理 - ├── setup/page.tsx # 仓库主数据配置 - ├── split-order/page.tsx # ⚠️ 分切单管理(原稿误写为 cutting/page.tsx) - ├── stock-adjust/page.tsx # 库存调整(原稿缺失) - ├── cost/page.tsx # 库存成本(原稿缺失) - ├── production-inbound/page.tsx # 生产入库(原稿缺失) - ├── sales-outbound/page.tsx # 销售出库(原稿缺失) - ├── trace/page.tsx # ⚠️ 批次追溯(原稿误写为 batch-trace/page.tsx) - ├── batch/page.tsx # 批次管理(原稿缺失) - ├── error.tsx / loading.tsx # 错误/加载边界(原稿缺失) -``` - -### 后端 API(`src/app/api/warehouse/`,实际存在 ✅,原稿严重不全) -``` -/api/warehouse/ - ├── route.ts # 仓库列表 / 创建 - ├── inbound/* # CRUD、audit、cutting、from-po、scan、with-po、labels - ├── outbound/* # route、confirm、fifo - ├── inventory/* # route、adjust、export、logs、warning - ├── transfer/* # route、[id]/outbound、[id]/items、[id]/inbound - ├── stocktaking/* # route、[id]/items、[id]/scan、[id]/split-summary、diff-process - ├── batch/* # route、trace - ├── freeze # 库存冻结/解冻 - ├── alert-push # 预警推送(原稿缺失) - ├── batch-inventory # 批次库存(原稿缺失) - ├── categories # 仓库分类(原稿缺失) - ├── cost # 库存成本(原稿缺失) - ├── fifo-config / fifo-recommend # FIFO 配置/推荐(原稿缺失) - ├── ink-mixing / ink-opening # 油墨相关(原稿缺失) - ├── monthly-report # 月度报表(原稿缺失) - ├── production-inbound / sales-outbound # 生产入库/销售出库(原稿缺失) - ├── split-order # 分切单(原稿缺失,对应 split-order/page.tsx) - ├── stock-adjust # 库存调整(原稿缺失) - └── unit-conversion # 单位换算(原稿缺失) -``` -> 原稿仅列出约 9 个路由,实际有 40+ 个 `route.ts`。上述为核查到的真实集合(节选)。 - ---- - -## 11. 仓储管理最佳实践(✅ 与原稿一致) -A. 并发安全:乐观锁(version+重试)、行级锁(FOR UPDATE)、事件驱动最终一致 ✅ -B. 数据完整性:软删除、取消快照(snapshot_data)、原子事务 ✅ -C. 业务约束:FIFO 推荐+过期预警、可选允许过期、缺货/低库存预警 ✅ -D. 成本管理:单位成本追踪、加权平均/FIFO 行成本、decimal.js 精度 ✅ - ---- - -## 12. 常见业务场景(✅ 与原稿一致) -场景 1 采购收货 / 场景 2 销售发货 / 场景 3 跨仓调拨(两阶段 out→in)/ 场景 4 月末盘点 —— 流程与原稿一致(调拨细节按 §4.3 修正理解)。 - ---- - -## 13. 技术栈与依赖(⚠️ 修正 Drizzle 覆盖范围) -| 层级 | 技术 | 说明 | -|---|---|---| -| ORM | Drizzle ORM `^0.45.1` | 管理**非仓库域**约 112 张表(系统/财务/采购/销售/生产等);**仓库 `inv_*` 表不在 Drizzle 内** | -| 并发 | mysql2 `^3.20` + 乐观锁 | 连接池 + `version` 字段;仓库表走 `@/lib/db` 的 `query`/`execute` 原生 SQL | -| 事件 | Redis Streams + Outbox | `AppInitializer` 中:Redis 可用则 `StreamConsumer`(XREADGROUP) 消费;否则 `OutboxPoller` 降级为直接 publish ✅ | -| 金额 | decimal.js `^10.6` | FIFO/成本精确算术 ✅ | -| 业务 | `src/lib/fifo-allocation.ts` | FIFO 分配 & 重试(`allocateFIFO`/`executeFIFODeductionWithRetry`)✅ | -| 审计 | `AuditLogHandler` / `soft-delete.ts` | 操作日志 / 作废快照 ✅ | -| 二维码 | `qrcode-service.ts` | 生成与追溯 ✅ | - ---- - -## 14. 已知问题与优化空间(⚠️ 修正状态) -| 问题代码 | 原稿说明 | 校验状态 | -|---|---|---| -| INV-001 | soft-delete.ts 表名映射过时(warehouse_inbound vs inv_inbound_order) | ❌ **已修复,无需处理**:`soft-delete.ts` 第 84–85 行已是 `inbound_order → 'inv_inbound_order'`,无陈旧映射。原稿"🔴 待修复"不成立 | -| INV-002 | 调拨没有"在途"中间态 | ✅ **仍准确**:两阶段 out→in,无显式 in_transit 状态,可增强 | -| DATA-001 | vnerpdacahng_schema.sql 与 schema.ts 表名不一致 | ⚠️ **已实质处理(2026-08-25 第三轮实测)**:真实缺口是「`inv_*` 核心 38 张表中,10 张手写 + 28 张 Phase 0 生成于 `_gen_warehouse_missing.ts`,均已建模并与 live 列对齐、tsc 0 错误」。**跨域 81 个 FK + 44 张 `_bak*` 备份表**仍超出本域范围(见计划文档 §0/§8)。🚨 **高危仍未消除**:`package.json` 中 `db:push`/`db:generate`/`db:migrate` 仍是真实命令(drizzle.config.ts 注释"已废弃"已过时),若误跑 `drizzle-kit push` 仍可能按残缺 schema DROP 真实跨域外键——**在 `safe-db-push` 护栏(计划 §3/§4)落地前严禁 `db:push`**。详细方案见 `DRIZZLE-SCHEMA-补全计划-WAREHOUSE.md` | - ---- - -## 📊 核心指标与监控(✅ 与原稿一致) -关键 KPI:库存周转率、缺货率、盘点差异率、批次过期率。监控 SQL 示例(低库存、临期批次、缓慢移动物料)与原稿一致,可直接使用。 - ---- - -# 校验结论清单(逐条) -| # | 原稿声明 | 核查结果 | -|---|---|---| -| 1 | 8 inv_* 表由 Drizzle 管理 | ❌ 错:live 共 82 张 `inv_*`(44 张 `_bak*` 备份表已排除),核心 38 张已全部建模(10 手写 + 28 生成),非"全原生 SQL" | -| 2 | +5 张遗留表(原生 SQL) | ⚠️ 二分法不成立,已重述 | -| 3 | 4 大聚合根 InboundOrder/OutboundOrder/TransferOrder/StocktakingOrder | ✅ 准确(`src/domain/warehouse/aggregates/`) | -| 4 | 6 个应用服务 | ✅ 准确(全部存在) | -| 5 | 入库 action:cutting/scan/audit/from-po/with-po | ✅ 准确(对应子路由存在) | -| 6 | 出库 FIFO:`allocateFIFO`/`executeFIFODeductionWithRetry`、decimal.js | ✅ 准确(`fifo-allocation.ts` + `decimal.js`) | -| 7 | 调拨:仅 UPDATE warehouse_id | ❌ 错:两阶段 out→in(`[id]/outbound`+`[id]/inbound`) | -| 8 | 盘点 freeze=1 | ⚠️ 流程存在,freeze 落库位置未逐项确认 | -| 9 | 作废 soft-delete.ts 表名陈旧(INV-001) | ❌ 已修复,原稿判定过时 | -| 10 | 二维码:qrcode-service / QrCodeGenerationHandler / /api/dcprint/scan | ✅ 准确 | -| 11 | 切料:cutting-optimizer.ts + inv_cutting_record/detail | ✅ 准确 | -| 12 | 多币种:inv_inbound_order.currency/exchange_rate | ✅ 准确(列真实存在) | -| 13 | 页面列表(含 cutting、batch-trace) | ❌ 错:实际为 split-order、trace、batch 等;原稿列名/遗漏多项 | -| 14 | API 路由列表 | ❌ 不全:实际 40+ 路由,原稿仅约 9 个 | -| 15 | 技术栈:Drizzle/decimal.js/Redis Streams+Outbox/qrcode | ✅ 准确(仅 Drizzle 覆盖范围需修正) | -| 16 | DATA-001 表名不一致 | ⚠️ 重新定性为"Drizzle 覆盖面缺口" | - -**最需要关注的风险(DATA-001)**:仓库 38 张核心 `inv_*` 表现已全部建模(10 手写 + 28 生成),但 **Drizzle 仅建模全库 140/327 张表**,`db:push` 永远会 DROP 其余约 187 张 live 表。🚨 **`db:push` 已改造为安全护栏 `scripts/safe-db-push.cjs`**(命中任何 DROP 风险即硬拦截,实测捕获 64 类风险并 abort),专家绕行用 `db:push-raw`;**结构变更一律走增量迁移 `pnpm migrate`**(不再直接 push)。详见 `DRIZZLE-SCHEMA-补全计划-WAREHOUSE.md` §3。 diff --git "a/docs/02-\346\250\241\345\235\227\350\257\246\347\273\206\350\256\276\350\256\241/ERP\346\240\270\345\277\203\346\250\241\345\235\227\346\267\261\345\214\226\346\212\200\346\234\257\350\247\204\346\240\274.md" "b/docs/02-\350\256\276\350\256\241\346\226\207\346\241\243/ERP\346\240\270\345\277\203\346\250\241\345\235\227\346\267\261\345\214\226\346\212\200\346\234\257\350\247\204\346\240\274.md" similarity index 99% rename from "docs/02-\346\250\241\345\235\227\350\257\246\347\273\206\350\256\276\350\256\241/ERP\346\240\270\345\277\203\346\250\241\345\235\227\346\267\261\345\214\226\346\212\200\346\234\257\350\247\204\346\240\274.md" rename to "docs/02-\350\256\276\350\256\241\346\226\207\346\241\243/ERP\346\240\270\345\277\203\346\250\241\345\235\227\346\267\261\345\214\226\346\212\200\346\234\257\350\247\204\346\240\274.md" index 24a0d87e..4ea445cc 100644 --- "a/docs/02-\346\250\241\345\235\227\350\257\246\347\273\206\350\256\276\350\256\241/ERP\346\240\270\345\277\203\346\250\241\345\235\227\346\267\261\345\214\226\346\212\200\346\234\257\350\247\204\346\240\274.md" +++ "b/docs/02-\350\256\276\350\256\241\346\226\207\346\241\243/ERP\346\240\270\345\277\203\346\250\241\345\235\227\346\267\261\345\214\226\346\212\200\346\234\257\350\247\204\346\240\274.md" @@ -10,7 +10,7 @@ ### 1.1 背景 -当前 印刷生产经营信息管理系统 Print MIS 系统已具备生产管理、销售管理、采购管理、财务管理四大模块的页面与 API 骨架,但核心业务能力深度不足,特别是: +当前 VNERP 系统已具备生产管理、销售管理、采购管理、财务管理四大模块的页面与 API 骨架,但核心业务能力深度不足,特别是: - **MRP 运算**:仅有基础框架,缺乏能力需求计划、有限能力排程、替代料处理 - **成本核算**:仅实现移动加权平均法,缺乏完整的产品成本卷积、成本差异分析 diff --git "a/docs/03-\346\212\200\346\234\257\350\247\204\350\214\203/00-\346\212\200\346\234\257\350\247\204\350\214\203\346\200\273\350\247\210.md" "b/docs/03-\346\212\200\346\234\257\350\247\204\350\214\203/00-\346\212\200\346\234\257\350\247\204\350\214\203\346\200\273\350\247\210.md" index edd6d9f4..6f2b1a75 100644 --- "a/docs/03-\346\212\200\346\234\257\350\247\204\350\214\203/00-\346\212\200\346\234\257\350\247\204\350\214\203\346\200\273\350\247\210.md" +++ "b/docs/03-\346\212\200\346\234\257\350\247\204\350\214\203/00-\346\212\200\346\234\257\350\247\204\350\214\203\346\200\273\350\247\210.md" @@ -1,52 +1,39 @@ # 03-技术规范 总览 -> 本目录汇总 印刷生产经营信息管理系统 Print MIS(vnerp)的所有技术规范文档,作为开发、评审、运维的统一依据。 -> 所有文档均基于当前代码现状(Next.js 16.1.1 + React 19.2.3 + TypeScript 5 + Drizzle ORM 0.45.1 + mysql2 + next-intl 4),并经 `.trae/documents/doc-fact-base.md` 核实。 +> 本目录汇总 VNERP 丝网印刷 ERP 的所有技术规范文档,作为开发、评审、运维的统一依据。 +> 所有文档均基于当前代码现状(Next.js 16 + TypeScript + Drizzle ORM + next-intl)。 ## 文档索引 | 序号 | 文档 | 主要内容 | |------|------|----------| -| 01 | [编码规范](./编码规范.md) | TypeScript 严格模式、ESLint 扁平配置(`i18n/no-chinese-hardcode`、`ddd/layer-dependencies`)、Prettier | -| 02 | [开发规范](./开发规范.md) | Conventional Commits、husky hooks、分支策略、代码评审、CI | -| 03 | [API设计规范](./API设计规范.md) | RESTful 约定、统一响应、`withAuth`/`withAuthAndErrorHandler`、`authFetch` | -| 04 | [数据库规范](./数据库规范.md) | 42 张 Drizzle 消费表 + 遗留 SQL 表、命名约定、迁移流程(`pnpm setup:db` / `pnpm migrate`,001-060) | -| 05 | [安全规范](./安全规范.md) | JWT 认证、401 无感刷新、Rate Limiting、CSRF、`src/proxy.ts`、环境变量 | +| 01 | [编码规范](./编码规范.md) | ESLint(含 `i18n/no-chinese-hardcode`)、Prettier、TypeScript 严格模式 | +| 02 | [开发规范](./开发规范.md) | Conventional Commits、husky hooks、分支策略、代码评审 | +| 03 | [API设计规范](./API设计规范.md) | RESTful 约定、统一响应、错误处理、`authFetch` / `withAuth` | +| 04 | [数据库规范](./数据库规范.md) | Drizzle ORM 用法、命名约定、迁移流程(`db:generate` / `db:push`) | +| 05 | [安全规范](./安全规范.md) | JWT 认证、401 无感刷新、Rate Limiting、CORS、环境变量 | | 06 | [系统可审计性设计方案](./系统可审计性设计方案.md) | 操作日志、库存流水、单据作废、权限变更审计 | | 07 | [颜色主题修复方案](./颜色主题修复方案.md) | CSS 变量映射、硬编码颜色排查与替换 | -| 08 | [性能调优](./性能调优.md) | Next.js 16 配置、连接池、监控指标 | +| 08 | [性能调优](./性能调优.md) | Next.js 性能配置、连接池、监控指标 | | 09 | [已知问题](./已知问题.md) | Schema 与代码不一致、测试失败等当前缺陷 | -| 10 | [代码审查规范](./代码审查规范.md) | SQL 参数化强制规则、占位符计数、`execute()`/`query()` 使用规范(2026-07-10 SQL 注入修复实战) | -| 11 | [业务规则-仓储](./业务规则-仓储.md) | 入出库、FIFO、盘点、调拨、二维码追溯 | -| 12 | [业务规则-生产](./业务规则-生产.md) | 工单、报工、标准卡、成本归集、印前工装/工艺卡 | -| 13 | [业务规则-采购](./业务规则-采购.md) | 采购申请、采购订单、入库验收、应付 | -| 14 | [业务规则-质量](./业务规则-质量.md) | IQC/IPQC/FQC、SPC、不合格品处理 | -| 15 | [业务规则-财务](./业务规则-财务.md) | 应收应付、账龄、成本核算、收付款、凭证 | -| 16 | [模块联动重构方案](./模块联动重构方案.md) | 销售订单→MRP→工单→领料→入库→出库→凭证 主链路 | -| 17 | [DDD分层迁移方案-equipment-hr](./DDD分层迁移方案-equipment-hr.md) | equipment 与 hr 模块从胖路由迁移至 DDD 分层(标杆 warehouse) | -| 18 | [系统可审计性设计方案](./系统可审计性设计方案.md) | 八大手段与代码实现要点 | -| 19 | [颜色主题修复方案](./颜色主题修复方案.md) | Tailwind v4 + shadcn/ui + next-themes | +| 10 | [业务规则-仓储](./业务规则-仓储.md) | 入出库、FIFO、盘点、调拨、二维码追溯 | +| 11 | [业务规则-生产](./业务规则-生产.md) | 工单、报工、标准卡、成本归集 | +| 12 | [业务规则-采购](./业务规则-采购.md) | 采购申请、采购订单、入库验收、应付 | +| 13 | [业务规则-质量](./业务规则-质量.md) | IQC/IPQC/FQC/OQC、SPC、不合格品处理 | +| 14 | [业务规则-财务](./业务规则-财务.md) | 应收应付、账龄、成本核算、收付款 | ## 阅读约定 - 所有规范文档对应代码以 `src/` 目录为准;冲突时以代码现状为准。 -- 涉及命令的,统一使用 `pnpm - - diff --git a/docs/superpowers/plans/2026-07-16-multi-currency-phase1.md b/docs/superpowers/plans/2026-07-16-multi-currency-phase1.md deleted file mode 100644 index ceff23a4..00000000 --- a/docs/superpowers/plans/2026-07-16-multi-currency-phase1.md +++ /dev/null @@ -1,1871 +0,0 @@ -# 多币种支持 Phase 1:基础设施 实现计划 - -> **面向 AI 代理的工作者:** 必需子技能:使用 superpowers:subagent-driven-development(推荐)或 superpowers:executing-plans 逐任务实现此计划。步骤使用复选框(`- [ ]`)语法来跟踪进度。 - -**目标:** 建立多币种基础设施(币种主数据、汇率表、Money 扩展、Currency 服务、API、前端组件),为后续 Phase 2-4 的采购/销售/财务模块改造提供基础。 - -**架构:** 扩展现有 `Money` 值对象添加 `convertTo`/`format` 方法;新建 `sys_currency` 和 `sys_exchange_rate` 表;领域层定义 `ICurrencyService` 接口,基础设施层用 MySQL 实现,应用层 `CurrencyApplicationService` 封装缓存与换算逻辑;前端新建 ``/`` 组件和币种/汇率管理页面。 - -**技术栈:** Next.js 16 App Router / mysql2 / Drizzle ORM / Vitest / next-intl / shadcn/ui - -**规格文档:** `docs/superpowers/specs/2026-07-16-multi-currency-design.md` - ---- - -## 文件结构 - -### 新建文件 -| 文件 | 职责 | -|------|------| -| `database/migrations/063_create_currency_tables.sql` | 新建 sys_currency + sys_exchange_rate + sys_company.base_currency | -| `src/domain/shared/CurrencyService.ts` | ICurrencyService 接口 + CurrencyInfo/ExchangeRate 类型 | -| `src/infrastructure/repositories/MysqlCurrencyRepository.ts` | ICurrencyService 的 MySQL 实现 | -| `src/application/services/CurrencyApplicationService.ts` | 汇率缓存 + 本位币换算应用服务 | -| `src/app/api/system/currency/route.ts` | 币种 CRUD API | -| `src/app/api/system/exchange-rate/route.ts` | 汇率 CRUD + 查询 API | -| `src/lib/money-format.ts` | formatMoney 前端格式化工具 | -| `src/components/ui/money-display.tsx` | 原币+本位币双行展示组件 | -| `src/components/ui/currency-select.tsx` | 币种下拉选择器 | -| `src/app/[locale]/settings/currency/page.tsx` | 币种管理页面 | -| `src/app/[locale]/settings/exchange-rate/page.tsx` | 汇率管理页面 | -| `tests/unit/application/services/CurrencyApplicationService.test.ts` | 应用服务测试 | -| `tests/unit/infrastructure/repositories/MysqlCurrencyRepository.test.ts` | Repository 测试 | - -### 修改文件 -| 文件 | 变更 | -|------|------| -| `src/domain/shared/value-objects/Money.ts` | 添加 convertTo/format 方法 | -| `tests/unit/domain/shared/value-objects/money.test.ts` | 添加 convertTo/format 测试 | -| `src/lib/db/schema.ts` | 追加 sysCurrency/sysExchangeRate 表定义 | -| `messages/zh-CN.json` / `zh-TW.json` / `en.json` / `vi.json` | 添加多币种 i18n key | - ---- - -## 任务 1:数据库迁移 - -**文件:** -- 创建:`database/migrations/063_create_currency_tables.sql` - -- [ ] **步骤 1:编写迁移 SQL** - -```sql --- T063: 多币种基础设施 — 币种主数据 + 汇率表 + 公司本位币字段 - --- 1. 币种主数据表 -CREATE TABLE IF NOT EXISTS `sys_currency` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT, - `code` varchar(10) NOT NULL COMMENT '币种代码 ISO 4217 (CNY/USD/VND)', - `name` varchar(50) NOT NULL COMMENT '币种名称', - `symbol` varchar(10) DEFAULT NULL COMMENT '符号 (¥/$/₫)', - `decimal_places` tinyint DEFAULT 2 COMMENT '小数位 (CNY=2, USD=2, VND=0)', - `status` tinyint DEFAULT 1 COMMENT '1启用 0停用', - `sort` int DEFAULT 0, - `create_time` datetime DEFAULT CURRENT_TIMESTAMP, - `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - `create_by` bigint unsigned DEFAULT NULL, - `update_by` bigint unsigned DEFAULT NULL, - `deleted` tinyint DEFAULT 0, - PRIMARY KEY (`id`), - UNIQUE KEY `uk_code` (`code`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='币种主数据'; - --- 预置数据 -INSERT INTO `sys_currency` (`code`, `name`, `symbol`, `decimal_places`, `sort`) VALUES - ('CNY', '人民币', '¥', 2, 1), - ('USD', '美元', '$', 2, 2), - ('VND', '越南盾', '₫', 0, 3) -ON DUPLICATE KEY UPDATE `name` = VALUES(`name`); - --- 2. 汇率表 -CREATE TABLE IF NOT EXISTS `sys_exchange_rate` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT, - `from_currency` varchar(10) NOT NULL COMMENT '源币种', - `to_currency` varchar(10) NOT NULL COMMENT '目标币种', - `rate` decimal(18,6) NOT NULL COMMENT '汇率', - `rate_date` date NOT NULL COMMENT '汇率日期', - `source` varchar(50) DEFAULT 'manual' COMMENT '汇率来源 manual/api', - `remark` varchar(200) DEFAULT NULL, - `create_time` datetime DEFAULT CURRENT_TIMESTAMP, - `create_by` bigint unsigned DEFAULT NULL, - PRIMARY KEY (`id`), - KEY `idx_from_to_date` (`from_currency`,`to_currency`,`rate_date`), - KEY `idx_date` (`rate_date`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='汇率表'; - --- 3. 公司表加本位币字段(幂等) -SET @col_exists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'sys_company' AND COLUMN_NAME = 'base_currency'); -SET @sql = IF(@col_exists = 0, - 'ALTER TABLE `sys_company` ADD COLUMN `base_currency` varchar(10) DEFAULT ''CNY'' COMMENT ''本位币'' AFTER `tax_no`', - 'SELECT ''base_currency already exists'''); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; -``` - -- [ ] **步骤 2:执行迁移** - -运行:`node scripts/run-pending-migrations.mjs` -预期:输出 `Migration 063 applied successfully` - -- [ ] **步骤 3:验证表已创建** - -运行: -```bash -node --input-type=module -e "import mysql from 'mysql2/promise'; const c=await mysql.createConnection({host:'127.0.0.1',user:'root',password:'Snqig521223',database:'vnerpdacahng'}); const [r1]=await c.execute('SELECT * FROM sys_currency'); const [r2]=await c.execute('SHOW COLUMNS FROM sys_company LIKE \"base_currency\"'); console.log('currencies:', r1.length); console.log('base_currency col:', r2.length); await c.end();" -``` -预期:`currencies: 3` / `base_currency col: 1` - -- [ ] **步骤 4:Commit** - -```bash -git add database/migrations/063_create_currency_tables.sql -git commit -m "feat(currency): add migration 063 — sys_currency, sys_exchange_rate, sys_company.base_currency" -``` - ---- - -## 任务 2:Drizzle Schema 同步 - -**文件:** -- 修改:`src/lib/db/schema.ts`(在文件末尾追加) - -- [ ] **步骤 1:在 schema.ts 末尾追加两张表定义** - -在 `src/lib/db/schema.ts` 文件末尾追加: - -```typescript -// ==================== 币种与汇率(多币种基础设施) ==================== - -export const sysCurrency = mysqlTable( - 'sys_currency', - { - id: bigint('id', { mode: 'number', unsigned: true }).autoincrement().primaryKey(), - code: varchar('code', { length: 10 }).notNull(), - name: varchar('name', { length: 50 }).notNull(), - symbol: varchar('symbol', { length: 10 }), - decimalPlaces: int('decimal_places').default(2), - status: tinyint('status').default(1), - sort: int('sort').default(0), - createTime: datetime('create_time').default(sql`CURRENT_TIMESTAMP`), - updateTime: datetime('update_time').default(sql`CURRENT_TIMESTAMP`).onUpdateNow(), - createBy: bigint('create_by', { mode: 'number', unsigned: true }), - updateBy: bigint('update_by', { mode: 'number', unsigned: true }), - deleted: tinyint('deleted').default(0), - }, - (table) => ({ - codeIdx: uniqueIndex('uk_code').on(table.code), - }) -); - -export const sysExchangeRate = mysqlTable( - 'sys_exchange_rate', - { - id: bigint('id', { mode: 'number', unsigned: true }).autoincrement().primaryKey(), - fromCurrency: varchar('from_currency', { length: 10 }).notNull(), - toCurrency: varchar('to_currency', { length: 10 }).notNull(), - rate: decimal('rate', { precision: 18, scale: 6 }).notNull(), - rateDate: date('rate_date').notNull(), - source: varchar('source', { length: 50 }).default('manual'), - remark: varchar('remark', { length: 200 }), - createTime: datetime('create_time').default(sql`CURRENT_TIMESTAMP`), - createBy: bigint('create_by', { mode: 'number', unsigned: true }), - }, - (table) => ({ - fromToDateIdx: index('idx_from_to_date').on(table.fromCurrency, table.toCurrency, table.rateDate), - dateIdx: index('idx_date').on(table.rateDate), - }) -); -``` - -- [ ] **步骤 2:验证 TypeScript 编译** - -运行:`npx tsc --noEmit --pretty 2>&1 | head -20` -预期:无新增错误 - -- [ ] **步骤 3:Commit** - -```bash -git add src/lib/db/schema.ts -git commit -m "feat(currency): sync Drizzle schema with sys_currency and sys_exchange_rate" -``` - ---- - -## 任务 3:Money 值对象扩展(TDD) - -**文件:** -- 修改:`tests/unit/domain/shared/value-objects/money.test.ts` -- 修改:`src/domain/shared/value-objects/Money.ts` - -- [ ] **步骤 1:编写失败的测试** - -在 `tests/unit/domain/shared/value-objects/money.test.ts` 的 `describe('8.3 Money 值对象')` 块内末尾(`});` 之前)追加: - -```typescript - describe('convertTo() 汇率转换', () => { - it('同币种转换返回自身', () => { - const m = Money.create(100, 'CNY'); - const result = m.convertTo(7.25, 'CNY', 2); - expect(result.amount).toBe(100); - expect(result.currency).toBe('CNY'); - }); - - it('USD 转 CNY 正确换算', () => { - const usd = Money.create(1000, 'USD'); - const cny = usd.convertTo(7.25, 'CNY', 2); - expect(cny.amount).toBe(7250); - expect(cny.currency).toBe('CNY'); - }); - - it('VND 转 CNY 零小数位', () => { - const vnd = Money.create(250000, 'VND'); - const cny = vnd.convertTo(0.0003, 'CNY', 2); - expect(cny.amount).toBe(75); - expect(cny.currency).toBe('CNY'); - }); - - it('转换结果四舍五入到指定小数位', () => { - const usd = Money.create(100, 'USD'); - const cny = usd.convertTo(7.253, 'CNY', 2); - // 100 * 7.253 = 725.3 → 725.30 - expect(cny.amount).toBe(725.3); - }); - - it('VND 0 位小数转换四舍五入到整数', () => { - const cny = Money.create(99.99, 'CNY'); - const vnd = cny.convertTo(3400, 'VND', 0); - // 99.99 * 3400 = 339966 - expect(vnd.amount).toBe(339966); - expect(vnd.currency).toBe('VND'); - }); - }); - - describe('format() 格式化', () => { - it('默认 2 位小数', () => { - expect(Money.create(1234.5).format()).toBe('1234.50'); - }); - - it('VND 0 位小数', () => { - const vnd = Money.create(250000, 'VND'); - expect(vnd.format(0)).toBe('250000'); - }); - - it('负数格式化(红字)', () => { - const red = Money.redLetter(100, 'CNY'); - expect(red.format(2)).toBe('-100.00'); - }); - }); -``` - -- [ ] **步骤 2:运行测试验证失败** - -运行:`npx vitest run tests/unit/domain/shared/value-objects/money.test.ts` -预期:FAIL — `convertTo is not a function` / `format is not a function` - -- [ ] **步骤 3:在 Money.ts 中实现 convertTo 和 format** - -在 `src/domain/shared/value-objects/Money.ts` 的 `equals` 方法之后、类结束 `}` 之前追加: - -```typescript - /** - * 按汇率转换为目标币种(返回新的 Money 对象) - * @param rate 汇率(from→to) - * @param targetCurrency 目标币种代码 - * @param decimalPlaces 目标币种小数位(VND=0, CNY/USD=2) - */ - convertTo(rate: number, targetCurrency: string, decimalPlaces: number = 2): Money { - if (this.currency === targetCurrency) { - return new Money(this.amount, this.currency); - } - const converted = this.amount * rate; - const factor = Math.pow(10, decimalPlaces); - const rounded = Math.round(converted * factor) / factor; - return new Money(rounded, targetCurrency); - } - - /** - * 按指定小数位格式化金额为字符串 - */ - format(decimalPlaces: number = 2): string { - return this.amount.toFixed(decimalPlaces); - } -``` - -- [ ] **步骤 4:运行测试验证通过** - -运行:`npx vitest run tests/unit/domain/shared/value-objects/money.test.ts` -预期:PASS — 全部用例通过 - -- [ ] **步骤 5:Commit** - -```bash -git add src/domain/shared/value-objects/Money.ts tests/unit/domain/shared/value-objects/money.test.ts -git commit -m "feat(currency): extend Money value object with convertTo and format methods" -``` - ---- - -## 任务 4:ICurrencyService 接口定义 - -**文件:** -- 创建:`src/domain/shared/CurrencyService.ts` - -- [ ] **步骤 1:创建接口文件** - -```typescript -// src/domain/shared/CurrencyService.ts - -/** - * 币种信息 - */ -export interface CurrencyInfo { - code: string; - name: string; - symbol: string; - decimalPlaces: number; -} - -/** - * 汇率记录 - */ -export interface ExchangeRate { - fromCurrency: string; - toCurrency: string; - rate: number; - rateDate: Date; -} - -/** - * 币种与汇率领域服务接口 - * 实现由基础设施层提供(MysqlCurrencyRepository) - */ -export interface ICurrencyService { - /** 获取币种信息(含小数位) */ - getCurrency(code: string): Promise; - - /** 获取最新汇率(from→to),无则返回 null */ - getLatestRate(fromCurrency: string, toCurrency: string): Promise; - - /** 获取指定日期汇率 */ - getRateOnDate( - fromCurrency: string, - toCurrency: string, - date: Date - ): Promise; - - /** 获取所有启用的币种 */ - listActiveCurrencies(): Promise; -} -``` - -- [ ] **步骤 2:验证编译** - -运行:`npx tsc --noEmit --pretty 2>&1 | head -10` -预期:无错误 - -- [ ] **步骤 3:Commit** - -```bash -git add src/domain/shared/CurrencyService.ts -git commit -m "feat(currency): define ICurrencyService domain interface" -``` - ---- - -## 任务 5:MysqlCurrencyRepository 实现(TDD) - -**文件:** -- 创建:`tests/unit/infrastructure/repositories/MysqlCurrencyRepository.test.ts` -- 创建:`src/infrastructure/repositories/MysqlCurrencyRepository.ts` - -- [ ] **步骤 1:编写失败的测试** - -```typescript -// tests/unit/infrastructure/repositories/MysqlCurrencyRepository.test.ts -import { describe, it, expect, vi, beforeEach } from 'vitest'; -import { MysqlCurrencyRepository } from '@/infrastructure/repositories/MysqlCurrencyRepository'; - -// Mock db module -vi.mock('@/lib/db', () => ({ - queryOne: vi.fn(), - query: vi.fn(), -})); - -import { queryOne, query } from '@/lib/db'; - -describe('MysqlCurrencyRepository', () => { - let repo: MysqlCurrencyRepository; - - beforeEach(() => { - vi.clearAllMocks(); - repo = new MysqlCurrencyRepository(); - }); - - describe('getCurrency()', () => { - it('返回存在的币种信息', async () => { - vi.mocked(queryOne).mockResolvedValue({ - code: 'USD', - name: '美元', - symbol: '$', - decimal_places: 2, - }); - const result = await repo.getCurrency('USD'); - expect(result).toEqual({ - code: 'USD', - name: '美元', - symbol: '$', - decimalPlaces: 2, - }); - }); - - it('币种不存在返回 null', async () => { - vi.mocked(queryOne).mockResolvedValue(null); - const result = await repo.getCurrency('EUR'); - expect(result).toBeNull(); - }); - }); - - describe('getLatestRate()', () => { - it('返回最新汇率记录', async () => { - vi.mocked(queryOne).mockResolvedValue({ - from_currency: 'USD', - to_currency: 'CNY', - rate: '7.250000', - rate_date: new Date('2026-07-16'), - }); - const result = await repo.getLatestRate('USD', 'CNY'); - expect(result).toEqual({ - fromCurrency: 'USD', - toCurrency: 'CNY', - rate: 7.25, - rateDate: new Date('2026-07-16'), - }); - }); - - it('汇率未配置返回 null', async () => { - vi.mocked(queryOne).mockResolvedValue(null); - const result = await repo.getLatestRate('EUR', 'VND'); - expect(result).toBeNull(); - }); - }); - - describe('listActiveCurrencies()', () => { - it('返回启用币种列表', async () => { - vi.mocked(query).mockResolvedValue([ - { code: 'CNY', name: '人民币', symbol: '¥', decimal_places: 2 }, - { code: 'USD', name: '美元', symbol: '$', decimal_places: 2 }, - ]); - const result = await repo.listActiveCurrencies(); - expect(result).toHaveLength(2); - expect(result[0].code).toBe('CNY'); - }); - }); -}); -``` - -- [ ] **步骤 2:运行测试验证失败** - -运行:`npx vitest run tests/unit/infrastructure/repositories/MysqlCurrencyRepository.test.ts` -预期:FAIL — 模块不存在 - -- [ ] **步骤 3:实现 MysqlCurrencyRepository** - -```typescript -// src/infrastructure/repositories/MysqlCurrencyRepository.ts -import { queryOne, query } from '@/lib/db'; -import type { ICurrencyService, CurrencyInfo, ExchangeRate } from '@/domain/shared/CurrencyService'; - -interface CurrencyRow { - code: string; - name: string; - symbol: string; - decimal_places: number; -} - -interface ExchangeRateRow { - from_currency: string; - to_currency: string; - rate: string; - rate_date: Date; -} - -export class MysqlCurrencyRepository implements ICurrencyService { - async getCurrency(code: string): Promise { - const row = await queryOne( - 'SELECT code, name, symbol, decimal_places FROM sys_currency WHERE code = ? AND status = 1 AND deleted = 0', - [code] - ); - if (!row) return null; - return { - code: row.code, - name: row.name, - symbol: row.symbol, - decimalPlaces: row.decimal_places, - }; - } - - async getLatestRate(fromCurrency: string, toCurrency: string): Promise { - const row = await queryOne( - 'SELECT from_currency, to_currency, rate, rate_date FROM sys_exchange_rate WHERE from_currency = ? AND to_currency = ? ORDER BY rate_date DESC LIMIT 1', - [fromCurrency, toCurrency] - ); - if (!row) return null; - return { - fromCurrency: row.from_currency, - toCurrency: row.to_currency, - rate: parseFloat(row.rate), - rateDate: new Date(row.rate_date), - }; - } - - async getRateOnDate( - fromCurrency: string, - toCurrency: string, - date: Date - ): Promise { - const row = await queryOne( - 'SELECT from_currency, to_currency, rate, rate_date FROM sys_exchange_rate WHERE from_currency = ? AND to_currency = ? AND rate_date = ? ORDER BY id DESC LIMIT 1', - [fromCurrency, toCurrency, date.toISOString().split('T')[0]] - ); - if (!row) return null; - return { - fromCurrency: row.from_currency, - toCurrency: row.to_currency, - rate: parseFloat(row.rate), - rateDate: new Date(row.rate_date), - }; - } - - async listActiveCurrencies(): Promise { - const rows = await query( - 'SELECT code, name, symbol, decimal_places FROM sys_currency WHERE status = 1 AND deleted = 0 ORDER BY sort ASC' - ); - return rows.map((row) => ({ - code: row.code, - name: row.name, - symbol: row.symbol, - decimalPlaces: row.decimal_places, - })); - } -} -``` - -- [ ] **步骤 4:运行测试验证通过** - -运行:`npx vitest run tests/unit/infrastructure/repositories/MysqlCurrencyRepository.test.ts` -预期:PASS - -- [ ] **步骤 5:Commit** - -```bash -git add src/infrastructure/repositories/MysqlCurrencyRepository.ts tests/unit/infrastructure/repositories/MysqlCurrencyRepository.test.ts -git commit -m "feat(currency): implement MysqlCurrencyRepository with CRUD for currencies and rates" -``` - ---- - -## 任务 6:CurrencyApplicationService 实现(TDD) - -**文件:** -- 创建:`tests/unit/application/services/CurrencyApplicationService.test.ts` -- 创建:`src/application/services/CurrencyApplicationService.ts` - -- [ ] **步骤 1:编写失败的测试** - -```typescript -// tests/unit/application/services/CurrencyApplicationService.test.ts -import { describe, it, expect, vi, beforeEach } from 'vitest'; -import { CurrencyApplicationService } from '@/application/services/CurrencyApplicationService'; -import type { ICurrencyService } from '@/domain/shared/CurrencyService'; -import { Money } from '@/domain/shared/value-objects/Money'; - -describe('CurrencyApplicationService', () => { - let mockCurrencyService: ICurrencyService; - let service: CurrencyApplicationService; - - beforeEach(() => { - vi.clearAllMocks(); - mockCurrencyService = { - getCurrency: vi.fn(), - getLatestRate: vi.fn(), - getRateOnDate: vi.fn(), - listActiveCurrencies: vi.fn(), - }; - service = new CurrencyApplicationService(mockCurrencyService); - }); - - describe('getLatestRate()', () => { - it('同币种返回 1', async () => { - const rate = await service.getLatestRate('CNY', 'CNY'); - expect(rate).toBe(1); - expect(mockCurrencyService.getLatestRate).not.toHaveBeenCalled(); - }); - - it('有缓存时返回缓存值', async () => { - vi.mocked(mockCurrencyService.getLatestRate).mockResolvedValue({ - fromCurrency: 'USD', - toCurrency: 'CNY', - rate: 7.25, - rateDate: new Date('2026-07-16'), - }); - const rate1 = await service.getLatestRate('USD', 'CNY'); - const rate2 = await service.getLatestRate('USD', 'CNY'); - expect(rate1).toBe(7.25); - expect(rate2).toBe(7.25); - // 第二次应命中缓存,不再次查询 DB - expect(mockCurrencyService.getLatestRate).toHaveBeenCalledTimes(1); - }); - - it('汇率未配置抛错', async () => { - vi.mocked(mockCurrencyService.getLatestRate).mockResolvedValue(null); - await expect(service.getLatestRate('EUR', 'VND')).rejects.toThrow(/汇率未配置/); - }); - }); - - describe('convertToBaseCurrency()', () => { - it('同币种直接返回', async () => { - const money = Money.create(100, 'CNY'); - const result = await service.convertToBaseCurrency(money, 'CNY'); - expect(result.amount).toBe(100); - expect(result.currency).toBe('CNY'); - }); - - it('USD 转 CNY 正确换算', async () => { - vi.mocked(mockCurrencyService.getLatestRate).mockResolvedValue({ - fromCurrency: 'USD', - toCurrency: 'CNY', - rate: 7.25, - rateDate: new Date(), - }); - vi.mocked(mockCurrencyService.getCurrency).mockResolvedValue({ - code: 'CNY', - name: '人民币', - symbol: '¥', - decimalPlaces: 2, - }); - const usd = Money.create(1000, 'USD'); - const cny = await service.convertToBaseCurrency(usd, 'CNY'); - expect(cny.amount).toBe(7250); - expect(cny.currency).toBe('CNY'); - }); - - it('VND 转 CNY 零小数位', async () => { - vi.mocked(mockCurrencyService.getLatestRate).mockResolvedValue({ - fromCurrency: 'VND', - toCurrency: 'CNY', - rate: 0.0003, - rateDate: new Date(), - }); - vi.mocked(mockCurrencyService.getCurrency).mockResolvedValue({ - code: 'CNY', - name: '人民币', - symbol: '¥', - decimalPlaces: 2, - }); - const vnd = Money.create(250000, 'VND'); - const cny = await service.convertToBaseCurrency(vnd, 'CNY'); - expect(cny.amount).toBe(75); - }); - }); -}); -``` - -- [ ] **步骤 2:运行测试验证失败** - -运行:`npx vitest run tests/unit/application/services/CurrencyApplicationService.test.ts` -预期:FAIL — 模块不存在 - -- [ ] **步骤 3:实现 CurrencyApplicationService** - -```typescript -// src/application/services/CurrencyApplicationService.ts -import type { ICurrencyService } from '@/domain/shared/CurrencyService'; -import { Money } from '@/domain/shared/value-objects/Money'; -import { logger } from '@/lib/logger'; - -// 内存缓存(无 Redis 时降级使用) -const memoryCache = new Map(); -const CACHE_TTL_MS = 5 * 60 * 1000; // 5 分钟 - -export class CurrencyApplicationService { - constructor(private currencyService: ICurrencyService) {} - - /** - * 获取最新汇率,带缓存(TTL 5分钟) - * 无 Redis 时降级为内存缓存 - */ - async getLatestRate(from: string, to: string): Promise { - if (from === to) return 1; - - const cacheKey = `rate:${from}:${to}`; - const cached = memoryCache.get(cacheKey); - if (cached && cached.expiresAt > Date.now()) { - return cached.value; - } - - const rate = await this.currencyService.getLatestRate(from, to); - if (!rate) { - throw new Error(`汇率未配置: ${from}→${to},请先在汇率管理中录入`); - } - - memoryCache.set(cacheKey, { value: rate.rate, expiresAt: Date.now() + CACHE_TTL_MS }); - return rate.rate; - } - - /** - * 换算金额为本位币 - */ - async convertToBaseCurrency(money: Money, baseCurrency: string): Promise { - if (money.currency === baseCurrency) return money; - - const rate = await this.getLatestRate(money.currency, baseCurrency); - const target = await this.currencyService.getCurrency(baseCurrency); - const decimalPlaces = target?.decimalPlaces ?? 2; - - logger.info( - { module: 'Currency', action: 'convertToBaseCurrency' }, - '汇率换算', - { from: money.currency, to: baseCurrency, rate, amount: money.amount } - ); - - return money.convertTo(rate, baseCurrency, decimalPlaces); - } - - /** - * 清除汇率缓存(录入新汇率后调用) - */ - clearCache(): void { - memoryCache.clear(); - } -} -``` - -- [ ] **步骤 4:运行测试验证通过** - -运行:`npx vitest run tests/unit/application/services/CurrencyApplicationService.test.ts` -预期:PASS - -- [ ] **步骤 5:Commit** - -```bash -git add src/application/services/CurrencyApplicationService.ts tests/unit/application/services/CurrencyApplicationService.test.ts -git commit -m "feat(currency): implement CurrencyApplicationService with cache and conversion" -``` - ---- - -## 任务 7:币种 CRUD API - -**文件:** -- 创建:`src/app/api/system/currency/route.ts` - -- [ ] **步骤 1:实现币种 CRUD API** - -```typescript -// src/app/api/system/currency/route.ts -import { NextRequest } from 'next/server'; -import { execute, queryOne, query } from '@/lib/db'; -import { successResponse, errorResponse, commonErrors, validateRequestBody } from '@/lib/api-response'; -import { withPermission } from '@/lib/api-permissions'; - -// GET - 币种列表(含筛选) -export const GET = withPermission(async (request: NextRequest) => { - const { searchParams } = new URL(request.url); - const status = searchParams.get('status'); - const onlyActive = searchParams.get('active') === 'true'; - - let sql = 'SELECT * FROM sys_currency WHERE deleted = 0'; - const values: unknown[] = []; - - if (onlyActive) { - sql += ' AND status = 1'; - } else if (status !== undefined && status !== null && status !== '') { - sql += ' AND status = ?'; - values.push(parseInt(status)); - } - - sql += ' ORDER BY sort ASC, id ASC'; - - const rows = await query(sql, values); - return successResponse(rows); -}); - -// POST - 新建币种 -export const POST = withPermission( - async (request: NextRequest) => { - const body = await request.json(); - const validation = validateRequestBody(body, ['code', 'name']); - if (!validation.valid) { - return errorResponse(`缺少必填字段: ${validation.missing.join(', ')}`, 400, 400); - } - - // 检查 code 是否已存在 - const existing = await queryOne('SELECT id FROM sys_currency WHERE code = ?', [body.code]); - if (existing) { - return errorResponse('币种代码已存在', 409, 409); - } - - const result = await execute( - `INSERT INTO sys_currency (code, name, symbol, decimal_places, status, sort) - VALUES (?, ?, ?, ?, ?, ?)`, - [ - body.code, - body.name, - body.symbol ?? null, - body.decimal_places ?? 2, - body.status ?? 1, - body.sort ?? 0, - ] - ); - - return successResponse({ id: result.insertId }, '币种创建成功'); - }, - { logTitle: '创建币种' } -); - -// PUT - 更新币种 -export const PUT = withPermission( - async (request: NextRequest) => { - const body = await request.json(); - const { id } = body; - if (!id) { - return commonErrors.badRequest('币种ID不能为空'); - } - - const existing = await queryOne('SELECT id FROM sys_currency WHERE id = ?', [id]); - if (!existing) { - return commonErrors.notFound('币种不存在'); - } - - await execute( - `UPDATE sys_currency SET name = ?, symbol = ?, decimal_places = ?, status = ?, sort = ? WHERE id = ?`, - [ - body.name, - body.symbol ?? null, - body.decimal_places ?? 2, - body.status ?? 1, - body.sort ?? 0, - id, - ] - ); - - return successResponse(null, '币种更新成功'); - }, - { logTitle: '更新币种' } -); - -// DELETE - 删除币种(软删除) -export const DELETE = withPermission( - async (request: NextRequest) => { - const { searchParams } = new URL(request.url); - const id = searchParams.get('id'); - if (!id) { - return commonErrors.badRequest('币种ID不能为空'); - } - - await execute('UPDATE sys_currency SET deleted = 1 WHERE id = ?', [parseInt(id)]); - return successResponse(null, '币种删除成功'); - }, - { logTitle: '删除币种' } -); -``` - -- [ ] **步骤 2:验证编译** - -运行:`npx tsc --noEmit --pretty 2>&1 | head -10` -预期:无错误 - -- [ ] **步骤 3:Commit** - -```bash -git add src/app/api/system/currency/route.ts -git commit -m "feat(currency): add currency CRUD API at /api/system/currency" -``` - ---- - -## 任务 8:汇率 CRUD + 查询 API - -**文件:** -- 创建:`src/app/api/system/exchange-rate/route.ts` - -- [ ] **步骤 1:实现汇率 API** - -```typescript -// src/app/api/system/exchange-rate/route.ts -import { NextRequest } from 'next/server'; -import { execute, queryOne, query, queryPaginated } from '@/lib/db'; -import { successResponse, errorResponse, commonErrors, validateRequestBody } from '@/lib/api-response'; -import { withPermission } from '@/lib/api-permissions'; - -// GET - 汇率列表或最新汇率查询 -export const GET = withPermission(async (request: NextRequest) => { - const { searchParams } = new URL(request.url); - const from = searchParams.get('from'); - const to = searchParams.get('to'); - const date = searchParams.get('date'); - const latest = searchParams.get('latest') === 'true'; - const page = parseInt(searchParams.get('page') || '1'); - const pageSize = parseInt(searchParams.get('pageSize') || '20'); - - // 查询最新汇率: /api/system/exchange-rate?from=USD&to=CNY&latest=true - if (latest && from && to) { - const rate = await queryOne( - 'SELECT * FROM sys_exchange_rate WHERE from_currency = ? AND to_currency = ? ORDER BY rate_date DESC LIMIT 1', - [from, to] - ); - if (!rate) { - return successResponse(null, '未找到汇率记录'); - } - return successResponse(rate); - } - - // 查询指定日期汇率 - if (date && from && to) { - const rate = await queryOne( - 'SELECT * FROM sys_exchange_rate WHERE from_currency = ? AND to_currency = ? AND rate_date = ? ORDER BY id DESC LIMIT 1', - [from, to, date] - ); - return successResponse(rate); - } - - // 分页列表 - let sql = 'SELECT * FROM sys_exchange_rate WHERE 1=1'; - let countSql = 'SELECT COUNT(*) as total FROM sys_exchange_rate WHERE 1=1'; - const values: unknown[] = []; - - if (from) { - sql += ' AND from_currency = ?'; - countSql += ' AND from_currency = ?'; - values.push(from); - } - if (to) { - sql += ' AND to_currency = ?'; - countSql += ' AND to_currency = ?'; - values.push(to); - } - - sql += ' ORDER BY rate_date DESC, id DESC'; - - const result = await queryPaginated(sql, countSql, values, { page, pageSize }); - return successResponse(result.data, undefined, result.pagination); -}); - -// POST - 录入汇率 -export const POST = withPermission( - async (request: NextRequest) => { - const body = await request.json(); - const validation = validateRequestBody(body, ['from_currency', 'to_currency', 'rate', 'rate_date']); - if (!validation.valid) { - return errorResponse(`缺少必填字段: ${validation.missing.join(', ')}`, 400, 400); - } - - if (body.from_currency === body.to_currency) { - return errorResponse('源币种和目标币种不能相同', 400, 400); - } - - if (parseFloat(body.rate) <= 0) { - return errorResponse('汇率必须大于 0', 400, 400); - } - - const result = await execute( - `INSERT INTO sys_exchange_rate (from_currency, to_currency, rate, rate_date, source, remark) - VALUES (?, ?, ?, ?, ?, ?)`, - [ - body.from_currency, - body.to_currency, - body.rate, - body.rate_date, - body.source ?? 'manual', - body.remark ?? null, - ] - ); - - return successResponse({ id: result.insertId }, '汇率录入成功'); - }, - { logTitle: '录入汇率' } -); - -// DELETE - 删除汇率记录 -export const DELETE = withPermission( - async (request: NextRequest) => { - const { searchParams } = new URL(request.url); - const id = searchParams.get('id'); - if (!id) { - return commonErrors.badRequest('汇率记录ID不能为空'); - } - - await execute('DELETE FROM sys_exchange_rate WHERE id = ?', [parseInt(id)]); - return successResponse(null, '汇率记录删除成功'); - }, - { logTitle: '删除汇率' } -); -``` - -- [ ] **步骤 2:验证编译** - -运行:`npx tsc --noEmit --pretty 2>&1 | head -10` -预期:无错误 - -- [ ] **步骤 3:Commit** - -```bash -git add src/app/api/system/exchange-rate/route.ts -git commit -m "feat(currency): add exchange rate CRUD and query API at /api/system/exchange-rate" -``` - ---- - -## 任务 9:前端 formatMoney 工具 - -**文件:** -- 创建:`src/lib/money-format.ts` - -- [ ] **步骤 1:实现 formatMoney 工具** - -```typescript -// src/lib/money-format.ts - -/** - * 根据币种获取 locale - */ -function getLocaleByCurrency(currency: string): string { - const map: Record = { - CNY: 'zh-CN', - USD: 'en-US', - VND: 'vi-VN', - }; - return map[currency] || 'en-US'; -} - -/** - * 币种小数位映射 - */ -const DECIMAL_PLACES: Record = { - CNY: 2, - USD: 2, - VND: 0, -}; - -/** - * 格式化金额为带币种符号的字符串 - * @param amount 金额 - * @param currency 币种代码(CNY/USD/VND) - * @param decimalPlaces 小数位(不传则按币种默认) - */ -export function formatMoney( - amount: number, - currency: string = 'CNY', - decimalPlaces?: number -): string { - const digits = decimalPlaces ?? DECIMAL_PLACES[currency] ?? 2; - try { - return new Intl.NumberFormat(getLocaleByCurrency(currency), { - style: 'currency', - currency, - minimumFractionDigits: digits, - maximumFractionDigits: digits, - }).format(amount); - } catch { - // 币种代码无效时降级为纯数字 - return `${amount.toFixed(digits)}`; - } -} - -/** - * 格式化金额(不带符号,仅千分位 + 小数位) - */ -export function formatAmount( - amount: number, - currency: string = 'CNY', - decimalPlaces?: number -): string { - const digits = decimalPlaces ?? DECIMAL_PLACES[currency] ?? 2; - return new Intl.NumberFormat(getLocaleByCurrency(currency), { - minimumFractionDigits: digits, - maximumFractionDigits: digits, - }).format(amount); -} - -/** - * 获取币种小数位 - */ -export function getDecimalPlaces(currency: string): number { - return DECIMAL_PLACES[currency] ?? 2; -} -``` - -- [ ] **步骤 2:Commit** - -```bash -git add src/lib/money-format.ts -git commit -m "feat(currency): add formatMoney frontend utility" -``` - ---- - -## 任务 10:CurrencySelect 组件 - -**文件:** -- 创建:`src/components/ui/currency-select.tsx` - -- [ ] **步骤 1:实现 CurrencySelect 组件** - -```tsx -// src/components/ui/currency-select.tsx -'use client'; - -import { useEffect, useState } from 'react'; -import { - Select, - SelectContent, - SelectItem, - SelectTrigger, - SelectValue, -} from '@/components/ui/select'; -import { authFetch } from '@/lib/auth-fetch'; - -interface Currency { - id: number; - code: string; - name: string; - symbol: string; - decimal_places: number; -} - -interface CurrencySelectProps { - value: string; - onChange: (value: string, currency?: Currency) => void; - placeholder?: string; - disabled?: boolean; -} - -export function CurrencySelect({ - value, - onChange, - placeholder = '选择币种', - disabled, -}: CurrencySelectProps) { - const [currencies, setCurrencies] = useState([]); - - useEffect(() => { - const loadCurrencies = async () => { - try { - const response = await authFetch('/api/system/currency?active=true'); - const result = await response.json(); - if (result.success && Array.isArray(result.data)) { - setCurrencies(result.data); - } - } catch { - // 降级:使用默认币种列表 - setCurrencies([ - { id: 1, code: 'CNY', name: '人民币', symbol: '¥', decimal_places: 2 }, - { id: 2, code: 'USD', name: '美元', symbol: '$', decimal_places: 2 }, - { id: 3, code: 'VND', name: '越南盾', symbol: '₫', decimal_places: 0 }, - ]); - } - }; - loadCurrencies(); - }, []); - - return ( - - ); -} -``` - -- [ ] **步骤 2:Commit** - -```bash -git add src/components/ui/currency-select.tsx -git commit -m "feat(currency): add CurrencySelect component" -``` - ---- - -## 任务 11:MoneyDisplay 组件 - -**文件:** -- 创建:`src/components/ui/money-display.tsx` - -- [ ] **步骤 1:实现 MoneyDisplay 组件** - -```tsx -// src/components/ui/money-display.tsx -'use client'; - -import { formatMoney, formatAmount } from '@/lib/money-format'; - -interface MoneyDisplayProps { - amount: number; - currency?: string; - baseAmount?: number; - baseCurrency?: string; - showSymbol?: boolean; - className?: string; -} - -export function MoneyDisplay({ - amount, - currency = 'CNY', - baseAmount, - baseCurrency, - showSymbol = true, - className, -}: MoneyDisplayProps) { - const original = showSymbol - ? formatMoney(amount, currency) - : formatAmount(amount, currency); - - // 同币种或无本位币金额时,单行显示 - if (!baseAmount || currency === baseCurrency) { - return {original}; - } - - // 双行显示:原币 + 本位币 - const base = showSymbol - ? formatMoney(baseAmount, baseCurrency!) - : formatAmount(baseAmount, baseCurrency!); - - return ( - - {original} - (≈{base}) - - ); -} -``` - -- [ ] **步骤 2:Commit** - -```bash -git add src/components/ui/money-display.tsx -git commit -m "feat(currency): add MoneyDisplay component for dual-currency display" -``` - ---- - -## 任务 12:i18n key 添加 - -**文件:** -- 修改:`messages/zh-CN.json`、`messages/zh-TW.json`、`messages/en.json`、`messages/vi.json` - -- [ ] **步骤 1:在 4 个语言文件的 Common 块末尾添加 key** - -在 `messages/zh-CN.json` 的 Common 块(`"clearSelection": "清空选择"` 之后)追加: - -```json - "currency": "币种", - "exchangeRate": "汇率", - "baseCurrency": "本位币", - "originalCurrency": "原币种", - "convertedAmount": "换算金额", - "currencyManagement": "币种管理", - "exchangeRateManagement": "汇率管理", -``` - -zh-TW 对应翻译: -```json - "currency": "幣種", - "exchangeRate": "匯率", - "baseCurrency": "本位幣", - "originalCurrency": "原幣種", - "convertedAmount": "換算金額", - "currencyManagement": "幣種管理", - "exchangeRateManagement": "匯率管理", -``` - -en 对应翻译: -```json - "currency": "Currency", - "exchangeRate": "Exchange Rate", - "baseCurrency": "Base Currency", - "originalCurrency": "Original Currency", - "convertedAmount": "Converted Amount", - "currencyManagement": "Currency Management", - "exchangeRateManagement": "Exchange Rate Management", -``` - -vi 对应翻译: -```json - "currency": "Tiền tệ", - "exchangeRate": "Tỷ giá", - "baseCurrency": "Tiền tệ cơ sở", - "originalCurrency": "Tiền tệ gốc", - "convertedAmount": "Số tiền quy đổi", - "currencyManagement": "Quản lý tiền tệ", - "exchangeRateManagement": "Quản lý tỷ giá", -``` - -- [ ] **步骤 2:验证 JSON 格式** - -运行: -```bash -node -e "['zh-CN','zh-TW','en','vi'].forEach(l => { try { JSON.parse(require('fs').readFileSync('messages/'+l+'.json','utf8')); console.log(l+': OK'); } catch(e) { console.log(l+': INVALID - '+e.message); } })" -``` -预期:4 个文件均 OK - -- [ ] **步骤 3:Commit** - -```bash -git add messages/zh-CN.json messages/zh-TW.json messages/en.json messages/vi.json -git commit -m "feat(currency): add multi-currency i18n keys to all 4 language files" -``` - ---- - -## 任务 13:币种管理页面 - -**文件:** -- 创建:`src/app/[locale]/settings/currency/page.tsx` - -- [ ] **步骤 1:实现币种管理页面** - -```tsx -// src/app/[locale]/settings/currency/page.tsx -'use client'; - -import { useState, useEffect, useCallback } from 'react'; -import { MainLayout } from '@/components/layout'; -import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; -import { Button } from '@/components/ui/button'; -import { Input } from '@/components/ui/input'; -import { Label } from '@/components/ui/label'; -import { Badge } from '@/components/ui/badge'; -import { - Table, TableBody, TableCell, TableHead, TableHeader, TableRow, -} from '@/components/ui/table'; -import { - Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter, -} from '@/components/ui/dialog'; -import { Plus, Edit, Trash2, RefreshCw } from 'lucide-react'; -import { toast } from 'sonner'; -import { useTranslations } from 'next-intl'; -import { authFetch } from '@/lib/auth-fetch'; -import { logger } from '@/lib/logger'; - -interface Currency { - id: number; - code: string; - name: string; - symbol: string; - decimal_places: number; - status: number; - sort: number; -} - -export default function CurrencyPage() { - const tc = useTranslations('Common'); - const [currencies, setCurrencies] = useState([]); - const [loading, setLoading] = useState(false); - const [dialogOpen, setDialogOpen] = useState(false); - const [editing, setEditing] = useState(false); - const [form, setForm] = useState>({}); - - const fetchCurrencies = useCallback(async () => { - setLoading(true); - try { - const response = await authFetch('/api/system/currency'); - const result = await response.json(); - if (result.success) { - setCurrencies(Array.isArray(result.data) ? result.data : []); - } - } catch (error) { - logger.error({ module: 'Currency', action: 'fetchCurrencies' }, '获取币种列表失败', { error: (error as Error).message }); - toast.error(tc('fetchFailed')); - } finally { - setLoading(false); - } - }, [tc]); - - useEffect(() => { - fetchCurrencies(); - }, [fetchCurrencies]); - - const handleSave = async () => { - if (!form.code || !form.name) { - toast.error('请填写币种代码和名称'); - return; - } - try { - const method = editing ? 'PUT' : 'POST'; - const response = await authFetch('/api/system/currency', { - method, - body: JSON.stringify(form), - }); - const result = await response.json(); - if (result.success) { - toast.success(editing ? '更新成功' : '创建成功'); - setDialogOpen(false); - fetchCurrencies(); - } else { - toast.error(result.message || '操作失败'); - } - } catch (error) { - toast.error('操作失败'); - } - }; - - const handleDelete = async (id: number) => { - if (!confirm('确认删除此币种?')) return; - try { - const response = await authFetch(`/api/system/currency?id=${id}`, { method: 'DELETE' }); - const result = await response.json(); - if (result.success) { - toast.success('删除成功'); - fetchCurrencies(); - } - } catch (error) { - toast.error('删除失败'); - } - }; - - const handleEdit = (currency: Currency) => { - setForm(currency); - setEditing(true); - setDialogOpen(true); - }; - - const handleAdd = () => { - setForm({ status: 1, decimal_places: 2, sort: 0 }); - setEditing(false); - setDialogOpen(true); - }; - - return ( - -
- - -
- {tc('currencyManagement')} -
- - -
-
-
- - - - - {tc('sort') || '排序'} - CODE - {tc('currency')} - {tc('symbol') || '符号'} - {tc('decimalPlaces') || '小数位'} - {tc('status')} - {tc('operation')} - - - - {currencies.map((currency) => ( - - {currency.sort} - {currency.code} - {currency.name} - {currency.symbol} - {currency.decimal_places} - - - {currency.status === 1 ? tc('enabled') : tc('disabled')} - - - -
- - -
-
-
- ))} -
-
-
-
- - - - - {editing ? tc('edit') : tc('add')}{tc('currency')} - -
-
- - setForm({ ...form, code: e.target.value.toUpperCase() })} - disabled={editing} - placeholder="CNY / USD / VND" - /> -
-
- - setForm({ ...form, name: e.target.value })} - placeholder="人民币 / 美元 / 越南盾" - /> -
-
- - setForm({ ...form, symbol: e.target.value })} - placeholder="¥ / $ / ₫" - /> -
-
- - setForm({ ...form, decimal_places: parseInt(e.target.value) })} - min={0} - max={4} - /> -
-
- - setForm({ ...form, sort: parseInt(e.target.value) })} - /> -
-
- - -
-
- - - - -
-
-
-
- ); -} -``` - -- [ ] **步骤 2:验证编译** - -运行:`npx tsc --noEmit --pretty 2>&1 | head -10` -预期:无错误 - -- [ ] **步骤 3:Commit** - -```bash -git add src/app/[locale]/settings/currency/page.tsx -git commit -m "feat(currency): add currency management page at settings/currency" -``` - ---- - -## 任务 14:汇率管理页面 - -**文件:** -- 创建:`src/app/[locale]/settings/exchange-rate/page.tsx` - -- [ ] **步骤 1:实现汇率管理页面** - -```tsx -// src/app/[locale]/settings/exchange-rate/page.tsx -'use client'; - -import { useState, useEffect, useCallback } from 'react'; -import { MainLayout } from '@/components/layout'; -import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; -import { Button } from '@/components/ui/button'; -import { Input } from '@/components/ui/input'; -import { Label } from '@/components/ui/label'; -import { - Table, TableBody, TableCell, TableHead, TableHeader, TableRow, -} from '@/components/ui/table'; -import { - Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter, -} from '@/components/ui/dialog'; -import { Plus, Trash2, RefreshCw } from 'lucide-react'; -import { toast } from 'sonner'; -import { useTranslations } from 'next-intl'; -import { authFetch } from '@/lib/auth-fetch'; -import { CurrencySelect } from '@/components/ui/currency-select'; -import { logger } from '@/lib/logger'; - -interface ExchangeRate { - id: number; - from_currency: string; - to_currency: string; - rate: string; - rate_date: string; - source: string; - remark: string | null; -} - -export default function ExchangeRatePage() { - const tc = useTranslations('Common'); - const [rates, setRates] = useState([]); - const [loading, setLoading] = useState(false); - const [dialogOpen, setDialogOpen] = useState(false); - const [form, setForm] = useState({ - from_currency: 'USD', - to_currency: 'CNY', - rate: '', - rate_date: new Date().toISOString().split('T')[0], - remark: '', - }); - - const fetchRates = useCallback(async () => { - setLoading(true); - try { - const response = await authFetch('/api/system/exchange-rate?pageSize=100'); - const result = await response.json(); - if (result.success) { - const data = result.data; - setRates(Array.isArray(data) ? data : data?.list || []); - } - } catch (error) { - logger.error({ module: 'Currency', action: 'fetchRates' }, '获取汇率列表失败', { error: (error as Error).message }); - toast.error(tc('fetchFailed')); - } finally { - setLoading(false); - } - }, [tc]); - - useEffect(() => { - fetchRates(); - }, [fetchRates]); - - const handleSave = async () => { - if (!form.rate || parseFloat(form.rate) <= 0) { - toast.error('请输入有效汇率'); - return; - } - if (form.from_currency === form.to_currency) { - toast.error('源币种和目标币种不能相同'); - return; - } - try { - const response = await authFetch('/api/system/exchange-rate', { - method: 'POST', - body: JSON.stringify(form), - }); - const result = await response.json(); - if (result.success) { - toast.success('汇率录入成功'); - setDialogOpen(false); - fetchRates(); - } else { - toast.error(result.message || '录入失败'); - } - } catch (error) { - toast.error('录入失败'); - } - }; - - const handleDelete = async (id: number) => { - if (!confirm('确认删除此汇率记录?')) return; - try { - const response = await authFetch(`/api/system/exchange-rate?id=${id}`, { method: 'DELETE' }); - const result = await response.json(); - if (result.success) { - toast.success('删除成功'); - fetchRates(); - } - } catch (error) { - toast.error('删除失败'); - } - }; - - return ( - -
- - -
- {tc('exchangeRateManagement')} -
- - -
-
-
- - - - - {tc('originalCurrency')} - {tc('baseCurrency')} - {tc('exchangeRate')} - {tc('date') || '日期'} - {tc('source') || '来源'} - {tc('remark') || '备注'} - {tc('operation')} - - - - {rates.map((rate) => ( - - {rate.from_currency} - {rate.to_currency} - {rate.rate} - {rate.rate_date} - {rate.source} - {rate.remark || '-'} - - - - - ))} - -
-
-
- - - - - {tc('add')}{tc('exchangeRate')} - -
-
- - setForm({ ...form, from_currency: v })} - /> -
-
- - setForm({ ...form, to_currency: v })} - /> -
-
- - setForm({ ...form, rate: e.target.value })} - placeholder="7.250000" - /> -
-
- - setForm({ ...form, rate_date: e.target.value })} - /> -
-
- - setForm({ ...form, remark: e.target.value })} - placeholder="备注信息" - /> -
-
- - - - -
-
-
-
- ); -} -``` - -- [ ] **步骤 2:验证编译** - -运行:`npx tsc --noEmit --pretty 2>&1 | head -10` -预期:无错误 - -- [ ] **步骤 3:运行全部测试** - -运行:`npx vitest run tests/unit/domain/shared/value-objects/money.test.ts tests/unit/infrastructure/repositories/MysqlCurrencyRepository.test.ts tests/unit/application/services/CurrencyApplicationService.test.ts` -预期:全部 PASS - -- [ ] **步骤 4:Commit** - -```bash -git add src/app/[locale]/settings/exchange-rate/page.tsx -git commit -m "feat(currency): add exchange rate management page at settings/exchange-rate" -``` - ---- - -## 自检结果 - -### 1. 规格覆盖度 -对照规格 Phase 1 交付物: -- ✅ 数据库迁移:sys_currency + sys_exchange_rate + sys_company.base_currency → 任务 1 -- ✅ Money 值对象扩展 convertTo/format → 任务 3 -- ✅ ICurrencyService 接口 → 任务 4 -- ✅ MysqlCurrencyRepository 实现 → 任务 5 -- ✅ CurrencyApplicationService(含缓存)→ 任务 6 -- ✅ 预置数据 CNY/USD/VND → 任务 1 SQL 中 -- ✅ API /api/system/currency → 任务 7 -- ✅ API /api/system/exchange-rate → 任务 8 -- ✅ 前端 formatMoney 工具 → 任务 9 -- ✅ 组件 → 任务 10 -- ✅ 组件 → 任务 11 -- ✅ 设置页面:币种管理 → 任务 13 -- ✅ 设置页面:汇率管理 → 任务 14 -- ✅ i18n key → 任务 12 -- ✅ Drizzle schema 同步 → 任务 2 - -### 2. 占位符扫描 -- 无 TODO/待定 -- 所有代码步骤都包含完整实现 - -### 3. 类型一致性 -- `CurrencyInfo` 接口在任务 4 定义,任务 5/6 使用 — 一致 -- `ExchangeRate` 接口在任务 4 定义,任务 5/6 使用 — 一致 -- `convertTo(rate, targetCurrency, decimalPlaces)` 在任务 3 定义,任务 6 调用 — 一致 -- `getLatestRate` / `convertToBaseCurrency` 在任务 6 定义,规格中引用 — 一致 diff --git a/docs/superpowers/plans/2026-07-16-purchase-multi-currency-phase2a.md b/docs/superpowers/plans/2026-07-16-purchase-multi-currency-phase2a.md deleted file mode 100644 index 00083dc1..00000000 --- a/docs/superpowers/plans/2026-07-16-purchase-multi-currency-phase2a.md +++ /dev/null @@ -1,1602 +0,0 @@ -# 采购模块多币种改造(Phase 2a)实现计划 - -> **面向 AI 代理的工作者:** 必需子技能:使用 superpowers:subagent-driven-development(推荐)或 superpowers:executing-plans 逐任务实现此计划。步骤使用复选框(`- [ ]`)语法来跟踪进度。 - -**目标:** 为采购模块(订单/退货/对账/供应商)接入多币种支持,实现原币金额 + 本位币金额双轨管理,创建订单时固化汇率,明细行冗余本位币金额字段。 - -**架构:** 在 Phase 1 多币种基础设施(`CurrencyApplicationService` + `Money.convertTo` + `sys_currency`/`sys_exchange_rate` 表)之上,扩展采购领域层 6 张表 + 3 聚合根 + 3 实体,引入 `CurrencySnapshot` 值对象封装固化汇率。应用服务层在 createOrder 时查询当日汇率并固化到聚合根,事件 payload 携带 currency 字段供下游消费。 - -**技术栈:** Next.js 16.1.1 (App Router) + Drizzle ORM 0.45.2 + mysql2/promise + DDD 分层 + Vitest + next-intl + shadcn/ui - ---- - -## 文件结构 - -### 数据库层 -- `database/migrations/064_purchase_multi_currency.sql`(创建)— 6 张表 ALTER 增加 base_* 字段 -- `database/migrations/065_backfill_purchase_currency.sql`(创建)— 历史数据按历史日期查汇率回填 -- `src/lib/db/schema.ts`(修改)— 同步 Drizzle 表定义 - -### 领域层 -- `src/domain/shared/value-objects/CurrencySnapshot.ts`(创建)— 不可变货币快照值对象 -- `tests/unit/domain/shared/value-objects/currency-snapshot.test.ts`(创建)— 单元测试 -- `src/domain/purchase/aggregates/PurchaseOrder.ts`(修改)— 增加 base_* 字段 + currencySnapshot -- `src/domain/purchase/entities/PurchaseOrderLine.ts`(修改)— 增加 base_* 字段 -- `src/domain/purchase/aggregates/PurchaseReturn.ts`(修改)— 增加 currency + exchangeRate + baseTotalAmount -- `src/domain/purchase/entities/PurchaseReturnLine.ts`(修改)— 增加 base_unit_price + base_amount -- `src/domain/purchase/aggregates/PurchaseReconciliation.ts`(修改)— 增加 currency + exchangeRate + 7 个 base_* 字段 -- `tests/unit/domain/purchase/aggregates/purchase-order.test.ts`(扩展)— 增加本位币测试 -- `tests/unit/domain/purchase/aggregates/purchase-return.test.ts`(扩展) -- `tests/unit/domain/purchase/aggregates/purchase-reconciliation.test.ts`(扩展) - -### 基础设施层 -- `src/infrastructure/repositories/MysqlPurchaseOrderRepository.ts`(修改)— INSERT/SELECT/UPDATE 包含 base_* 字段 -- `src/infrastructure/repositories/MysqlPurchaseReturnRepository.ts`(修改) -- `src/infrastructure/repositories/MysqlPurchaseReconciliationRepository.ts`(修改) - -### 应用服务层 -- `src/application/services/PurchaseApplicationService.ts`(修改)— createOrder 查询汇率并固化 -- `src/application/services/PurchaseReturnApplicationService.ts`(修改)— createReturn 从原订单继承 -- `src/application/services/PurchaseReconciliationApplicationService.ts`(修改)— 同币种校验 -- `tests/unit/application/services/purchase-application-service.test.ts`(扩展) - -### API 层 -- `src/app/api/purchase/orders/route.ts`(修改)— 响应包含 base_* 字段 -- `src/app/api/purchase/return/route.ts`(修改) -- `src/app/api/purchase/reconciliation/route.ts`(修改) -- `src/app/api/purchase/suppliers/route.ts`(修改)— 增加 default_currency - -### 前端 -- `messages/zh-CN.json` / `zh-TW.json` / `en.json` / `vi.json`(修改)— 8 个新 i18n key -- `src/app/[locale]/purchase/orders/page.tsx`(修改)— 双币种展示 + CurrencySelect -- `src/app/[locale]/purchase/orders/[id]/page.tsx`(修改)— 详情页双币种 -- `src/app/[locale]/purchase/return/page.tsx`(修改) -- `src/app/[locale]/purchase/reconciliation/page.tsx`(修改) -- `src/app/[locale]/purchase/suppliers/page.tsx`(修改)— default_currency 字段 - ---- - -## 任务 1:Migration 064 — 表结构变更 - -**文件:** -- 创建:`database/migrations/064_purchase_multi_currency.sql` - -- [ ] **步骤 1:编写 migration SQL(幂等版)** - -创建 `database/migrations/064_purchase_multi_currency.sql`: - -```sql --- 064_purchase_multi_currency.sql --- Phase 2a: 采购模块多币种改造 — 表结构变更 --- 幂等性:通过 INFORMATION_SCHEMA 检查列是否存在 - --- 1. pur_supplier 增加 default_currency -SET @col_exists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'pur_supplier' AND COLUMN_NAME = 'default_currency'); -SET @sql = IF(@col_exists = 0, - 'ALTER TABLE pur_supplier ADD COLUMN default_currency VARCHAR(10) DEFAULT ''CNY'' NULL COMMENT ''供应商默认币种代码'' AFTER supplier_code', - 'SELECT ''pur_supplier.default_currency already exists'' AS msg'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 2. pur_purchase_order 增加 base_total_amount, base_tax_amount, base_grand_total -SET @col_exists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'pur_purchase_order' AND COLUMN_NAME = 'base_total_amount'); -SET @sql = IF(@col_exists = 0, - 'ALTER TABLE pur_purchase_order ADD COLUMN base_total_amount DECIMAL(18,4) DEFAULT 0.0000 NOT NULL COMMENT ''本位币含税前金额'' AFTER grand_total, ADD COLUMN base_tax_amount DECIMAL(18,4) DEFAULT 0.0000 NOT NULL COMMENT ''本位币税额'' AFTER base_total_amount, ADD COLUMN base_grand_total DECIMAL(18,4) DEFAULT 0.0000 NOT NULL COMMENT ''本位币价税合计'' AFTER base_tax_amount', - 'SELECT ''pur_purchase_order base_* already exist'' AS msg'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 3. pur_purchase_order_line 增加 base_unit_price, base_amount, base_tax_amount, base_line_total -SET @col_exists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'pur_purchase_order_line' AND COLUMN_NAME = 'base_unit_price'); -SET @sql = IF(@col_exists = 0, - 'ALTER TABLE pur_purchase_order_line ADD COLUMN base_unit_price DECIMAL(18,4) DEFAULT 0.0000 NOT NULL COMMENT ''本位币单价'' AFTER line_total, ADD COLUMN base_amount DECIMAL(18,4) DEFAULT 0.0000 NOT NULL COMMENT ''本位币金额'' AFTER base_unit_price, ADD COLUMN base_tax_amount DECIMAL(18,4) DEFAULT 0.0000 NOT NULL COMMENT ''本位币税额'' AFTER base_amount, ADD COLUMN base_line_total DECIMAL(18,4) DEFAULT 0.0000 NOT NULL COMMENT ''本位币价税合计'' AFTER base_tax_amount', - 'SELECT ''pur_purchase_order_line base_* already exist'' AS msg'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 4. pur_purchase_return 增加 currency, exchange_rate, base_total_amount -SET @col_exists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'pur_purchase_return' AND COLUMN_NAME = 'currency'); -SET @sql = IF(@col_exists = 0, - 'ALTER TABLE pur_purchase_return ADD COLUMN currency VARCHAR(10) DEFAULT ''CNY'' NOT NULL COMMENT ''币种代码(跟随原订单)'' AFTER total_amount, ADD COLUMN exchange_rate DECIMAL(18,4) DEFAULT 1.0000 NOT NULL COMMENT ''汇率(跟随原订单)'' AFTER currency, ADD COLUMN base_total_amount DECIMAL(18,4) DEFAULT 0.0000 NOT NULL COMMENT ''本位币退货总额'' AFTER exchange_rate', - 'SELECT ''pur_purchase_return currency/exchange_rate/base_total_amount already exist'' AS msg'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 5. pur_purchase_return_line 增加 base_unit_price, base_amount -SET @col_exists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'pur_purchase_return_line' AND COLUMN_NAME = 'base_unit_price'); -SET @sql = IF(@col_exists = 0, - 'ALTER TABLE pur_purchase_return_line ADD COLUMN base_unit_price DECIMAL(18,4) DEFAULT 0.0000 NOT NULL COMMENT ''本位币单价'' AFTER amount, ADD COLUMN base_amount DECIMAL(18,4) DEFAULT 0.0000 NOT NULL COMMENT ''本位币金额'' AFTER base_unit_price', - 'SELECT ''pur_purchase_return_line base_* already exist'' AS msg'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; - --- 6. pur_purchase_reconciliation 增加 currency, exchange_rate + 7 个 base_* 字段 -SET @col_exists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'pur_purchase_reconciliation' AND COLUMN_NAME = 'currency'); -SET @sql = IF(@col_exists = 0, - 'ALTER TABLE pur_purchase_reconciliation ADD COLUMN currency VARCHAR(10) DEFAULT ''CNY'' NOT NULL COMMENT ''币种代码(强制与入库单一致)'' AFTER balance_amount, ADD COLUMN exchange_rate DECIMAL(18,4) DEFAULT 1.0000 NOT NULL COMMENT ''汇率(从首笔入库单继承)'' AFTER currency, ADD COLUMN base_receipt_amount DECIMAL(18,4) DEFAULT 0.0000 NOT NULL COMMENT ''本位币入库金额'' AFTER exchange_rate, ADD COLUMN base_return_amount DECIMAL(18,4) DEFAULT 0.0000 NOT NULL COMMENT ''本位币退货金额'' AFTER base_receipt_amount, ADD COLUMN base_net_amount DECIMAL(18,4) DEFAULT 0.0000 NOT NULL COMMENT ''本位币净额'' AFTER base_return_amount, ADD COLUMN base_discount_amount DECIMAL(18,4) DEFAULT 0.0000 NOT NULL COMMENT ''本位币折扣'' AFTER base_net_amount, ADD COLUMN base_paid_amount DECIMAL(18,4) DEFAULT 0.0000 NOT NULL COMMENT ''本位币已付'' AFTER base_discount_amount, ADD COLUMN base_balance_amount DECIMAL(18,4) DEFAULT 0.0000 NOT NULL COMMENT ''本位币余额'' AFTER base_paid_amount', - 'SELECT ''pur_purchase_reconciliation currency/base_* already exist'' AS msg'); -PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; -``` - -- [ ] **步骤 2:执行 migration 验证** - -运行:`node scripts/run-migration.mjs 064` -预期:6 张表 ALTER 成功,重复执行输出 "already exists" - -- [ ] **步骤 3:Commit** - -```bash -git add database/migrations/064_purchase_multi_currency.sql -git commit -m "feat(currency): add migration 064 for purchase multi-currency columns" -``` - ---- - -## 任务 2:Migration 065 — 历史数据回填 - -**文件:** -- 创建:`database/migrations/065_backfill_purchase_currency.sql` - -- [ ] **步骤 1:编写回填 SQL** - -创建 `database/migrations/065_backfill_purchase_currency.sql`: - -```sql --- 065_backfill_purchase_currency.sql --- Phase 2a: 历史数据回填 — 按订单日期查汇率,查不到 fallback 1.0 - --- 1. 回填供应商默认币种 -UPDATE pur_supplier SET default_currency = 'CNY' WHERE default_currency IS NULL; - --- 2. 回填采购订单 currency + exchange_rate(若为空) -UPDATE pur_purchase_order SET currency = 'CNY' WHERE currency IS NULL OR currency = ''; -UPDATE pur_purchase_order SET exchange_rate = 1.0000 WHERE exchange_rate IS NULL OR exchange_rate = 0; - --- 3. 回填采购订单 base_* 字段(仅处理 base_grand_total = 0 且 grand_total > 0 的记录) --- 使用子查询按 order_date 查历史汇率,查不到则用 1.0 -UPDATE pur_purchase_order po -LEFT JOIN ( - SELECT po2.id, COALESCE( - (SELECT rate FROM sys_exchange_rate er - WHERE er.from_currency = po2.currency AND er.to_currency = 'CNY' - AND er.rate_date <= po2.order_date - ORDER BY er.rate_date DESC LIMIT 1), - 1.0000 - ) AS historical_rate - FROM pur_purchase_order po2 - WHERE po2.base_grand_total = 0 AND po2.grand_total > 0 -) rates ON po.id = rates.id -SET po.exchange_rate = rates.historical_rate, - po.base_total_amount = ROUND(po.total_amount * rates.historical_rate, 4), - po.base_tax_amount = ROUND(po.tax_amount * rates.historical_rate, 4), - po.base_grand_total = ROUND(po.grand_total * rates.historical_rate, 4) -WHERE rates.id IS NOT NULL; - --- 4. 回填采购订单明细 base_* 字段 -UPDATE pur_purchase_order_line pol -INNER JOIN pur_purchase_order po ON pol.po_id = po.id -SET pol.base_unit_price = ROUND(pol.unit_price * po.exchange_rate, 4), - pol.base_amount = ROUND(pol.amount * po.exchange_rate, 4), - pol.base_tax_amount = ROUND(pol.tax_amount * po.exchange_rate, 4), - pol.base_line_total = ROUND(pol.line_total * po.exchange_rate, 4) -WHERE pol.base_line_total = 0 AND pol.line_total > 0; - --- 5. 回填退货单 currency + exchange_rate + base_total_amount -UPDATE pur_purchase_return pr -INNER JOIN pur_purchase_order po ON pr.order_id = po.id -SET pr.currency = po.currency, - pr.exchange_rate = po.exchange_rate, - pr.base_total_amount = ROUND(pr.total_amount * po.exchange_rate, 4) -WHERE pr.base_total_amount = 0 AND pr.total_amount > 0; - --- 6. 回填对账单(旧记录按 1.0 回填) -UPDATE pur_purchase_reconciliation SET currency = 'CNY' WHERE currency IS NULL OR currency = ''; -UPDATE pur_purchase_reconciliation SET exchange_rate = 1.0000 WHERE exchange_rate IS NULL OR exchange_rate = 0; -UPDATE pur_purchase_reconciliation -SET base_receipt_amount = ROUND(receipt_amount * exchange_rate, 4), - base_return_amount = ROUND(return_amount * exchange_rate, 4), - base_net_amount = ROUND(net_amount * exchange_rate, 4), - base_discount_amount = ROUND(discount_amount * exchange_rate, 4), - base_paid_amount = ROUND(paid_amount * exchange_rate, 4), - base_balance_amount = ROUND(balance_amount * exchange_rate, 4) -WHERE base_balance_amount = 0 AND balance_amount > 0; -``` - -- [ ] **步骤 2:执行 migration 验证** - -运行:`node scripts/run-migration.mjs 065` -预期:历史记录的 base_* 字段被回填 - -- [ ] **步骤 3:Commit** - -```bash -git add database/migrations/065_backfill_purchase_currency.sql -git commit -m "feat(currency): add migration 065 to backfill historical purchase base amounts" -``` - ---- - -## 任务 3:Drizzle schema.ts 同步 - -**文件:** -- 修改:`src/lib/db/schema.ts`(在现有 `purSupplier`、`purPurchaseOrder` 等表定义中追加字段) - -- [ ] **步骤 1:在 `purSupplier` 表定义中追加 `defaultCurrency`** - -找到 `purSupplier` 表定义(约 L1100-1130 附近),在 `supplierCode` 字段之后追加: - -```typescript -defaultCurrency: varchar('default_currency', { length: 10, default: 'CNY' }), -``` - -- [ ] **步骤 2:在 `purPurchaseOrder` 表定义中追加 base_* 字段** - -找到 `purPurchaseOrder` 表定义,在 `grandTotal` 字段之后追加: - -```typescript -baseTotalAmount: decimal('base_total_amount', { precision: 18, scale: 4 }).default('0.0000').notNull(), -baseTaxAmount: decimal('base_tax_amount', { precision: 18, scale: 4 }).default('0.0000').notNull(), -baseGrandTotal: decimal('base_grand_total', { precision: 18, scale: 4 }).default('0.0000').notNull(), -``` - -- [ ] **步骤 3:在 `purPurchaseOrderLine` 表定义中追加 base_* 字段** - -在 `lineTotal` 字段之后追加: - -```typescript -baseUnitPrice: decimal('base_unit_price', { precision: 18, scale: 4 }).default('0.0000').notNull(), -baseAmount: decimal('base_amount', { precision: 18, scale: 4 }).default('0.0000').notNull(), -baseTaxAmount: decimal('base_tax_amount', { precision: 18, scale: 4 }).default('0.0000').notNull(), -baseLineTotal: decimal('base_line_total', { precision: 18, scale: 4 }).default('0.0000').notNull(), -``` - -- [ ] **步骤 4:在 `purPurchaseReturn` 表定义中追加 currency + exchangeRate + baseTotalAmount** - -在 `totalAmount` 字段之后追加: - -```typescript -currency: varchar('currency', { length: 10 }).default('CNY').notNull(), -exchangeRate: decimal('exchange_rate', { precision: 18, scale: 4 }).default('1.0000').notNull(), -baseTotalAmount: decimal('base_total_amount', { precision: 18, scale: 4 }).default('0.0000').notNull(), -``` - -- [ ] **步骤 5:在 `purPurchaseReturnLine` 表定义中追加 base_* 字段** - -在 `amount` 字段之后追加: - -```typescript -baseUnitPrice: decimal('base_unit_price', { precision: 18, scale: 4 }).default('0.0000').notNull(), -baseAmount: decimal('base_amount', { precision: 18, scale: 4 }).default('0.0000').notNull(), -``` - -- [ ] **步骤 6:在 `purPurchaseReconciliation` 表定义中追加 currency + exchangeRate + 7 个 base_* 字段** - -在 `balanceAmount` 字段之后追加: - -```typescript -currency: varchar('currency', { length: 10 }).default('CNY').notNull(), -exchangeRate: decimal('exchange_rate', { precision: 18, scale: 4 }).default('1.0000').notNull(), -baseReceiptAmount: decimal('base_receipt_amount', { precision: 18, scale: 4 }).default('0.0000').notNull(), -baseReturnAmount: decimal('base_return_amount', { precision: 18, scale: 4 }).default('0.0000').notNull(), -baseNetAmount: decimal('base_net_amount', { precision: 18, scale: 4 }).default('0.0000').notNull(), -baseDiscountAmount: decimal('base_discount_amount', { precision: 18, scale: 4 }).default('0.0000').notNull(), -basePaidAmount: decimal('base_paid_amount', { precision: 18, scale: 4 }).default('0.0000').notNull(), -baseBalanceAmount: decimal('base_balance_amount', { precision: 18, scale: 4 }).default('0.0000').notNull(), -``` - -- [ ] **步骤 7:运行 TypeScript 编译验证** - -运行:`npx tsc --noEmit` -预期:无 schema.ts 相关错误 - -- [ ] **步骤 8:Commit** - -```bash -git add src/lib/db/schema.ts -git commit -m "feat(currency): sync Drizzle schema with purchase multi-currency columns" -``` - ---- - -## 任务 4:CurrencySnapshot 值对象 + 测试 - -**文件:** -- 创建:`src/domain/shared/value-objects/CurrencySnapshot.ts` -- 创建:`tests/unit/domain/shared/value-objects/currency-snapshot.test.ts` - -- [ ] **步骤 1:编写失败的测试** - -创建 `tests/unit/domain/shared/value-objects/currency-snapshot.test.ts`: - -```typescript -import { describe, it, expect } from 'vitest'; -import { CurrencySnapshot } from '@/domain/shared/value-objects/CurrencySnapshot'; -import { Money } from '@/domain/shared/value-objects/Money'; - -describe('CurrencySnapshot', () => { - describe('create', () => { - it('应创建有效的 CurrencySnapshot', () => { - const snapshot = CurrencySnapshot.create('USD', 7.25, 'CNY'); - expect(snapshot.currency).toBe('USD'); - expect(snapshot.exchangeRate).toBe(7.25); - expect(snapshot.baseCurrency).toBe('CNY'); - }); - - it('应拒绝无效的 currency 代码(长度非 3)', () => { - expect(() => CurrencySnapshot.create('US', 7.25, 'CNY')).toThrow(); - expect(() => CurrencySnapshot.create('USDD', 7.25, 'CNY')).toThrow(); - expect(() => CurrencySnapshot.create('', 7.25, 'CNY')).toThrow(); - }); - - it('应拒绝无效的 exchangeRate(<=0 或 NaN)', () => { - expect(() => CurrencySnapshot.create('USD', 0, 'CNY')).toThrow(); - expect(() => CurrencySnapshot.create('USD', -1, 'CNY')).toThrow(); - expect(() => CurrencySnapshot.create('USD', NaN, 'CNY')).toThrow(); - }); - - it('应拒绝无效的 baseCurrency', () => { - expect(() => CurrencySnapshot.create('USD', 7.25, 'CN')).toThrow(); - expect(() => CurrencySnapshot.create('USD', 7.25, '')).toThrow(); - }); - }); - - describe('isSameCurrency', () => { - it('同币种应返回 true', () => { - const snapshot = CurrencySnapshot.create('CNY', 1, 'CNY'); - expect(snapshot.isSameCurrency).toBe(true); - }); - - it('不同币种应返回 false', () => { - const snapshot = CurrencySnapshot.create('USD', 7.25, 'CNY'); - expect(snapshot.isSameCurrency).toBe(false); - }); - }); - - describe('convert', () => { - it('同币种应短路返回原金额', () => { - const snapshot = CurrencySnapshot.create('CNY', 1, 'CNY'); - const money = Money.create(100, 'CNY'); - const result = snapshot.convert(money); - expect(result.amount).toBe(100); - expect(result.currency).toBe('CNY'); - }); - - it('跨币种应按固化汇率换算', () => { - const snapshot = CurrencySnapshot.create('USD', 7.25, 'CNY'); - const money = Money.create(100, 'USD'); - const result = snapshot.convert(money); - expect(result.amount).toBe(725); - expect(result.currency).toBe('CNY'); - }); - - it('VND 应按 0 位小数换算', () => { - const snapshot = CurrencySnapshot.create('VND', 0.0003, 'CNY'); - const money = Money.create(1000000, 'VND'); - const result = snapshot.convert(money, 0); - expect(result.amount).toBe(300); - expect(result.currency).toBe('CNY'); - }); - - it('负数金额应正确换算(使用 redLetter 创建)', () => { - const snapshot = CurrencySnapshot.create('USD', 7.25, 'CNY'); - const money = Money.redLetter(-100, 'USD'); - // convert 内部调用 money.convertTo,convertTo 不支持负数(见 Money.ts JSDoc) - // 这里验证 convertTo 在负数下会抛错 - expect(() => snapshot.convert(money)).toThrow(); - }); - }); -}); -``` - -- [ ] **步骤 2:运行测试验证失败** - -运行:`npx vitest run tests/unit/domain/shared/value-objects/currency-snapshot.test.ts` -预期:FAIL,报错 "Cannot find module '@/domain/shared/value-objects/CurrencySnapshot'" - -- [ ] **步骤 3:编写实现** - -创建 `src/domain/shared/value-objects/CurrencySnapshot.ts`: - -```typescript -import { Money } from './Money'; - -/** - * 货币快照值对象 - * 封装创建订单时的 currency + exchange_rate + 对应的本位币 - * 一旦创建不可变(Immutable),保证历史汇率固化 - */ -export class CurrencySnapshot { - private constructor( - public readonly currency: string, - public readonly exchangeRate: number, - public readonly baseCurrency: string, - ) {} - - static create(currency: string, exchangeRate: number, baseCurrency: string): CurrencySnapshot { - if (!currency || currency.length !== 3) { - throw new Error('Invalid currency code: must be 3 characters'); - } - if (!Number.isFinite(exchangeRate) || exchangeRate <= 0) { - throw new Error('Exchange rate must be a positive finite number'); - } - if (!baseCurrency || baseCurrency.length !== 3) { - throw new Error('Invalid base currency code: must be 3 characters'); - } - return new CurrencySnapshot(currency, exchangeRate, baseCurrency); - } - - /** 是否同币种(汇率应为 1) */ - get isSameCurrency(): boolean { - return this.currency === this.baseCurrency; - } - - /** 将原币金额换算为本位币金额(使用固化的汇率) */ - convert(money: Money, decimalPlaces: number = 2): Money { - return money.convertTo(this.exchangeRate, this.baseCurrency, decimalPlaces); - } -} -``` - -- [ ] **步骤 4:运行测试验证通过** - -运行:`npx vitest run tests/unit/domain/shared/value-objects/currency-snapshot.test.ts` -预期:PASS,全部测试通过 - -- [ ] **步骤 5:Commit** - -```bash -git add src/domain/shared/value-objects/CurrencySnapshot.ts tests/unit/domain/shared/value-objects/currency-snapshot.test.ts -git commit -m "feat(currency): add CurrencySnapshot immutable value object" -``` - ---- - -## 任务 5:PurchaseOrderLine 实体改造 + 测试 - -**文件:** -- 修改:`src/domain/purchase/entities/PurchaseOrderLine.ts` -- 修改:`tests/unit/domain/purchase/entities/purchase-order-line.test.ts`(若存在,否则创建) - -- [ ] **步骤 1:在 PurchaseOrderLineProps 接口追加 base_* 字段** - -修改 `src/domain/purchase/entities/PurchaseOrderLine.ts` 的 `PurchaseOrderLineProps` 接口: - -```typescript -export interface PurchaseOrderLineProps { - id?: number; - orderId?: number; - lineNo: number; - materialId: number; - materialCode: string; - materialName: string; - materialSpec?: string; - unit: string; - orderQty: number; - receivedQty: number; - returnedQty: number; - unitPrice: number; - amount: number; - taxRate: number; - taxAmount: number; - lineTotal: number; - // 新增:本位币金额字段 - baseUnitPrice?: number; - baseAmount?: number; - baseTaxAmount?: number; - baseLineTotal?: number; - requireDate?: string; - remark?: string; -} -``` - -- [ ] **步骤 2:在 PurchaseOrderLine 类中追加 base_* 私有字段和 getter** - -在构造函数中追加: - -```typescript -private _baseUnitPrice: number, -private _baseAmount: number, -private _baseTaxAmount: number, -private _baseLineTotal: number, -``` - -在 getter 区域追加: - -```typescript -get baseUnitPrice(): number { - return this._baseUnitPrice; -} -get baseAmount(): number { - return this._baseAmount; -} -get baseTaxAmount(): number { - return this._baseTaxAmount; -} -get baseLineTotal(): number { - return this._baseLineTotal; -} -``` - -- [ ] **步骤 3:修改 create 和 reconstitute 静态方法** - -`create` 方法计算 base_* 字段(若未提供则默认为 0): - -```typescript -static create(props: PurchaseOrderLineProps): PurchaseOrderLine { - // ...原有校验... - const amount = (props.orderQty || 0) * (props.unitPrice || 0); - const taxRate = props.taxRate || 13; - const taxAmount = (amount * taxRate) / 100; - const lineTotal = amount + taxAmount; - - // base_* 字段:若未传入则默认为 0(由应用服务层在创建时填充固化值) - const baseUnitPrice = props.baseUnitPrice ?? 0; - const baseAmount = props.baseAmount ?? 0; - const baseTaxAmount = props.baseTaxAmount ?? 0; - const baseLineTotal = props.baseLineTotal ?? 0; - - return new PurchaseOrderLine( - // ...原有参数... - baseUnitPrice, baseAmount, baseTaxAmount, baseLineTotal, - props.requireDate, props.remark - ); -} -``` - -`reconstitute` 方法直接读取: - -```typescript -static reconstitute(props: PurchaseOrderLineProps): PurchaseOrderLine { - return new PurchaseOrderLine( - // ...原有参数... - props.baseUnitPrice ?? 0, - props.baseAmount ?? 0, - props.baseTaxAmount ?? 0, - props.baseLineTotal ?? 0, - props.requireDate, props.remark - ); -} -``` - -- [ ] **步骤 4:运行现有测试验证无回归** - -运行:`npx vitest run tests/unit/domain/purchase/entities/` -预期:现有测试全部通过(新增字段为可选,向后兼容) - -- [ ] **步骤 5:Commit** - -```bash -git add src/domain/purchase/entities/PurchaseOrderLine.ts -git commit -m "feat(currency): extend PurchaseOrderLine with base_* fields" -``` - ---- - -## 任务 6:PurchaseOrder 聚合根改造 + 测试 - -**文件:** -- 修改:`src/domain/purchase/aggregates/PurchaseOrder.ts` -- 修改:`tests/unit/domain/purchase/aggregates/purchase-order.test.ts`(若存在) - -- [ ] **步骤 1:在 PurchaseOrderProps 接口追加 base_* 字段** - -```typescript -export interface PurchaseOrderProps { - // ...原有字段... - // 新增:本位币字段 - baseCurrency?: string; - baseTotalAmount?: number; - baseTaxAmount?: number; - baseGrandTotal?: number; -} -``` - -- [ ] **步骤 2:在 PurchaseOrder 类中追加 base_* 私有字段和 getter** - -构造函数追加: - -```typescript -public readonly baseCurrency: string, -private _baseTotalAmount: number, -private _baseTaxAmount: number, -private _baseGrandTotal: number, -``` - -getter 区域追加: - -```typescript -get baseTotalAmount(): number { - return this._baseTotalAmount; -} -get baseTaxAmount(): number { - return this._baseTaxAmount; -} -get baseGrandTotal(): number { - return this._baseGrandTotal; -} -``` - -- [ ] **步骤 3:修改 create 和 reconstitute 静态方法** - -`create` 方法:base_* 字段若未传入则默认 0(由应用服务层在调用前计算并通过 props 传入)。 - -```typescript -static create(props: PurchaseOrderProps): PurchaseOrder { - // ...原有逻辑... - const baseCurrency = props.baseCurrency || 'CNY'; - const baseTotalAmount = props.baseTotalAmount ?? 0; - const baseTaxAmount = props.baseTaxAmount ?? 0; - const baseGrandTotal = props.baseGrandTotal ?? 0; - - return new PurchaseOrder( - // ...原有参数... - baseCurrency, baseTotalAmount, baseTaxAmount, baseGrandTotal, - ); -} -``` - -`reconstitute` 方法直接读取。 - -- [ ] **步骤 4:在 PurchaseOrderCreatedEvent payload 中追加 currency 字段** - -修改 `create` 方法中事件推送部分: - -```typescript -if (order.id) { - order._domainEvents.push( - new PurchaseOrderCreatedEvent({ - orderId: order.id, - orderNo: order.orderNo, - supplierId: order.supplierId, - supplierName: order.supplierName, - totalAmount: order.totalAmount, - totalQuantity: order.totalQuantity, - // 新增字段 - currency: order.currency, - exchangeRate: order.exchangeRate, - baseCurrency: order.baseCurrency, - baseTotalAmount: order.baseTotalAmount, - baseGrandTotal: order.baseGrandTotal, - }) - ); -} -``` - -**注意:** 需同步修改 `src/domain/purchase/events/PurchaseOrderEvents.ts` 的 `PurchaseOrderCreatedEvent` payload 接口,追加可选字段 `currency?: string`、`exchangeRate?: number`、`baseCurrency?: string`、`baseTotalAmount?: number`、`baseGrandTotal?: number`(可选字段,保证向后兼容)。 - -- [ ] **步骤 5:运行现有测试验证无回归** - -运行:`npx vitest run tests/unit/domain/purchase/aggregates/` -预期:现有测试全部通过 - -- [ ] **步骤 6:Commit** - -```bash -git add src/domain/purchase/aggregates/PurchaseOrder.ts src/domain/purchase/events/PurchaseOrderEvents.ts -git commit -m "feat(currency): extend PurchaseOrder aggregate with base_* fields and event payload" -``` - ---- - -## 任务 7:PurchaseReturn 聚合根改造 - -**文件:** -- 修改:`src/domain/purchase/aggregates/PurchaseReturn.ts` -- 修改:`src/domain/purchase/entities/PurchaseReturnLine.ts` -- 修改:`src/domain/purchase/events/PurchaseReturnEvents.ts` - -- [ ] **步骤 1:在 PurchaseReturnLineProps 接口追加 base_* 字段** - -修改 `src/domain/purchase/entities/PurchaseReturnLine.ts`: - -```typescript -export interface PurchaseReturnLineProps { - // ...原有字段... - baseUnitPrice?: number; - baseAmount?: number; -} -``` - -在类中追加 `baseUnitPrice` 和 `baseAmount` 字段、getter,并在 `create` / `reconstitute` 中处理。 - -- [ ] **步骤 2:在 PurchaseReturnProps 接口追加 currency + exchangeRate + baseTotalAmount** - -修改 `src/domain/purchase/aggregates/PurchaseReturn.ts`: - -```typescript -export interface PurchaseReturnProps { - // ...原有字段... - currency?: string; - exchangeRate?: number; - baseCurrency?: string; - baseTotalAmount?: number; -} -``` - -- [ ] **步骤 3:在 PurchaseReturn 类中追加字段和 getter** - -构造函数追加 `currency`、`exchangeRate`、`baseCurrency`、`baseTotalAmount` 字段(public readonly),并在 `create` / `reconstitute` 中读取。 - -- [ ] **步骤 4:在 PurchaseReturnCreatedEvent payload 中追加 currency 字段** - -修改 `src/domain/purchase/events/PurchaseReturnEvents.ts` 的 `PurchaseReturnCreatedEvent` payload 接口,追加可选字段 `currency?: string`、`exchangeRate?: number`、`baseCurrency?: string`、`baseTotalAmount?: number`。 - -- [ ] **步骤 5:运行测试验证无回归** - -运行:`npx vitest run tests/unit/domain/purchase/` -预期:全部通过 - -- [ ] **步骤 6:Commit** - -```bash -git add src/domain/purchase/aggregates/PurchaseReturn.ts src/domain/purchase/entities/PurchaseReturnLine.ts src/domain/purchase/events/PurchaseReturnEvents.ts -git commit -m "feat(currency): extend PurchaseReturn aggregate with currency and base_* fields" -``` - ---- - -## 任务 8:PurchaseReconciliation 聚合根改造 - -**文件:** -- 修改:`src/domain/purchase/aggregates/PurchaseReconciliation.ts` -- 修改:`src/domain/purchase/events/PurchaseReconciliationEvents.ts` - -- [ ] **步骤 1:在 PurchaseReconciliationProps 接口追加 currency + exchangeRate + base_* 字段** - -```typescript -export interface PurchaseReconciliationProps { - // ...原有字段... - currency?: string; - exchangeRate?: number; - baseCurrency?: string; - baseReceiptAmount?: number; - baseReturnAmount?: number; - baseNetAmount?: number; - baseDiscountAmount?: number; - basePaidAmount?: number; - baseBalanceAmount?: number; -} -``` - -- [ ] **步骤 2:在 PurchaseReconciliation 类中追加字段和 getter** - -构造函数追加上述字段(public readonly 或 private),并在 `create` / `reconstitute` 中读取。 - -**关键约束**:在 `create` 方法中,若 `currency` 未传入则默认为 'CNY';`base_*` 字段若未传入则按 `exchangeRate` 换算(或在应用服务层填充)。 - -- [ ] **步骤 3:在 PurchaseReconciliationCreatedEvent payload 中追加 currency 字段** - -修改 `src/domain/purchase/events/PurchaseReconciliationEvents.ts` 的 `PurchaseReconciliationCreatedEvent` payload 接口,追加可选字段 `currency?: string`、`exchangeRate?: number`、`baseCurrency?: string`、`baseReceiptAmount?: number`、`baseNetAmount?: number`、`baseBalanceAmount?: number`。 - -- [ ] **步骤 4:运行测试验证无回归** - -运行:`npx vitest run tests/unit/domain/purchase/aggregates/` -预期:全部通过 - -- [ ] **步骤 5:Commit** - -```bash -git add src/domain/purchase/aggregates/PurchaseReconciliation.ts src/domain/purchase/events/PurchaseReconciliationEvents.ts -git commit -m "feat(currency): extend PurchaseReconciliation aggregate with currency and base_* fields" -``` - ---- - -## 任务 9:MysqlPurchaseOrderRepository 改造 - -**文件:** -- 修改:`src/infrastructure/repositories/MysqlPurchaseOrderRepository.ts` - -- [ ] **步骤 1:扩展 PurPurchaseOrderRow 和 PurPurchaseOrderLineRow 接口** - -在 `PurPurchaseOrderRow` 接口追加: - -```typescript -base_total_amount: number | string; -base_tax_amount: number | string; -base_tax_amount: number | string; -base_grand_total: number | string; -``` - -在 `PurPurchaseOrderLineRow` 接口追加: - -```typescript -base_unit_price: number | string; -base_amount: number | string; -base_tax_amount: number | string; -base_line_total: number | string; -``` - -- [ ] **步骤 2:扩展 LINE_COLUMNS 常量** - -```typescript -const LINE_COLUMNS = `id, po_id, line_no, material_id, material_code, material_name, material_spec, - unit, order_qty, received_qty, returned_qty, unit_price, amount, - tax_rate, tax_amount, line_total, - base_unit_price, base_amount, base_tax_amount, base_line_total, - require_date, remark`; -``` - -- [ ] **步骤 3:修改 save 方法的 INSERT 语句** - -```typescript -const [orderResult] = await conn.execute( - `INSERT INTO pur_purchase_order - (po_no, supplier_id, supplier_name, supplier_code, order_date, delivery_date, - currency, exchange_rate, total_amount, total_quantity, tax_rate, tax_amount, grand_total, - base_total_amount, base_tax_amount, base_grand_total, - status, over_receipt_tolerance, payment_terms, delivery_address, remark, create_by, create_time) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NOW())`, - [ - orderNo, - order.supplierId, - order.supplierName, - order.supplierCode, - order.orderDate, - order.deliveryDate || null, - order.currency, - order.exchangeRate, - order.totalAmount, - order.totalQuantity, - order.taxRate, - order.taxAmount, - order.grandTotal, - order.baseTotalAmount, - order.baseTaxAmount, - order.baseGrandTotal, - order.status.toDbCode(), - order.overReceiptTolerance, - order.paymentTerms, - order.deliveryAddress, - order.remark, - order.createBy || null, - ] -); -``` - -明细行 INSERT 同步追加 base_* 字段。 - -- [ ] **步骤 4:修改 mapToProps 方法** - -```typescript -private mapToProps( - order: PurPurchaseOrderRow, - lines: PurPurchaseOrderLineRow[] -): PurchaseOrderProps { - return { - // ...原有字段... - baseCurrency: 'CNY', // 暂从系统配置读取或固定 CNY,后续在应用服务层注入 - baseTotalAmount: Number(order.base_total_amount) || 0, - baseTaxAmount: Number(order.base_tax_amount) || 0, - baseGrandTotal: Number(order.base_grand_total) || 0, - lines: (lines || []).map((line) => ({ - // ...原有字段... - baseUnitPrice: Number(line.base_unit_price) || 0, - baseAmount: Number(line.base_amount) || 0, - baseTaxAmount: Number(line.base_tax_amount) || 0, - baseLineTotal: Number(line.base_line_total) || 0, - // ... - })), - }; -} -``` - -- [ ] **步骤 5:运行现有测试验证无回归** - -运行:`npx vitest run tests/unit/infrastructure/repositories/` -预期:全部通过(或因新字段默认 0,无破坏性变更) - -- [ ] **步骤 6:Commit** - -```bash -git add src/infrastructure/repositories/MysqlPurchaseOrderRepository.ts -git commit -m "feat(currency): extend MysqlPurchaseOrderRepository with base_* field persistence" -``` - ---- - -## 任务 10:MysqlPurchaseReturnRepository + MysqlPurchaseReconciliationRepository 改造 - -**文件:** -- 修改:`src/infrastructure/repositories/MysqlPurchaseReturnRepository.ts` -- 修改:`src/infrastructure/repositories/MysqlPurchaseReconciliationRepository.ts` - -- [ ] **步骤 1:修改 MysqlPurchaseReturnRepository** - -在 INSERT/SELECT 语句中追加 `currency`、`exchange_rate`、`base_total_amount` 字段;明细行 INSERT/SELECT 追加 `base_unit_price`、`base_amount` 字段。在 `mapToProps` 中读取这些字段。 - -- [ ] **步骤 2:修改 MysqlPurchaseReconciliationRepository** - -在 INSERT/SELECT 语句中追加 `currency`、`exchange_rate`、7 个 `base_*` 字段。在 `mapToProps` 中读取。 - -**对账单 currency 一致性校验**:在 `save` 方法中(或由应用服务层负责),查询关联入库单的 currency(使用 `COALESCE(i.currency, 'CNY')` 兼容 `inv_inbound` 暂无 currency 字段),若不一致抛出 `DomainError`。 - -- [ ] **步骤 3:运行测试验证** - -运行:`npx vitest run tests/unit/infrastructure/repositories/` -预期:全部通过 - -- [ ] **步骤 4:Commit** - -```bash -git add src/infrastructure/repositories/MysqlPurchaseReturnRepository.ts src/infrastructure/repositories/MysqlPurchaseReconciliationRepository.ts -git commit -m "feat(currency): extend return and reconciliation repositories with currency fields" -``` - ---- - -## 任务 11:PurchaseApplicationService.createOrder 改造 + 测试 - -**文件:** -- 修改:`src/application/services/PurchaseApplicationService.ts` -- 修改:`tests/unit/application/services/purchase-application-service.test.ts`(若存在) - -- [ ] **步骤 1:构造函数注入 CurrencyApplicationService** - -```typescript -import { CurrencyApplicationService } from './CurrencyApplicationService'; -import { CurrencySnapshot } from '@/domain/shared/value-objects/CurrencySnapshot'; -import { Money } from '@/domain/shared/value-objects/Money'; -import { getSystemConfig } from '@/lib/system-config'; - -export class PurchaseApplicationService { - constructor( - private readonly orderRepo: IPurchaseOrderRepository, - private readonly currencyService: CurrencyApplicationService, - ) {} - // ... -} -``` - -- [ ] **步骤 2:在 createOrder 方法中查询汇率并固化** - -```typescript -async createOrder(props: PurchaseOrderProps): Promise<{ id: number; orderNo: string }> { - // ...原有 tolerance/taxRate 逻辑... - - // 1. 确定 currency:优先级 = 输入 > 系统默认(finance.default_currency) - if (!effectiveProps.currency) { - const currency = await getSystemConfig('finance.default_currency', 'CNY'); - if (currency) effectiveProps = { ...effectiveProps, currency }; - } - - // 2. 查询本位币 - const baseCurrency = await getSystemConfig('finance.base_currency', 'CNY'); - - // 3. 查询汇率并固化 - let exchangeRate = effectiveProps.exchangeRate || 1.0; - if (effectiveProps.currency && effectiveProps.currency !== baseCurrency) { - // 查询当日汇率(CurrencyApplicationService.getLatestRate 已带缓存) - exchangeRate = await this.currencyService.getLatestRate(effectiveProps.currency, baseCurrency); - } else if (effectiveProps.currency === baseCurrency) { - exchangeRate = 1.0; - } - - // 4. 构建 CurrencySnapshot - const snapshot = CurrencySnapshot.create( - effectiveProps.currency || 'CNY', - exchangeRate, - baseCurrency - ); - - // 5. 为每个明细行计算 base_* 字段 - const baseCurrencyDecimalPlaces = await this.getDecimalPlaces(baseCurrency); - effectiveProps = { - ...effectiveProps, - lines: effectiveProps.lines.map((line) => { - const originalUnitPrice = line.unitPrice || 0; - const originalAmount = (line.orderQty || 0) * originalUnitPrice; - const originalTaxAmount = (originalAmount * (line.taxRate || effectiveProps.taxRate || 13)) / 100; - const originalLineTotal = originalAmount + originalTaxAmount; - - const baseUnitPrice = snapshot.convert( - Money.create(originalUnitPrice, snapshot.currency), - baseCurrencyDecimalPlaces - ).amount; - const baseAmount = snapshot.convert( - Money.create(originalAmount, snapshot.currency), - baseCurrencyDecimalPlaces - ).amount; - const baseTaxAmount = snapshot.convert( - Money.create(originalTaxAmount, snapshot.currency), - baseCurrencyDecimalPlaces - ).amount; - const baseLineTotal = snapshot.convert( - Money.create(originalLineTotal, snapshot.currency), - baseCurrencyDecimalPlaces - ).amount; - - return { - ...line, - baseUnitPrice, baseAmount, baseTaxAmount, baseLineTotal, - }; - }), - }; - - // 6. 计算主表 base_* 字段 - const baseTotalAmount = effectiveProps.lines.reduce((sum, l) => sum + (l.baseAmount || 0), 0); - const baseTaxAmount = effectiveProps.lines.reduce((sum, l) => sum + (l.baseTaxAmount || 0), 0); - const baseGrandTotal = baseTotalAmount + baseTaxAmount; - - effectiveProps = { - ...effectiveProps, - baseCurrency, - exchangeRate, - baseTotalAmount: Math.round(baseTotalAmount * 100) / 100, - baseTaxAmount: Math.round(baseTaxAmount * 100) / 100, - baseGrandTotal: Math.round(baseGrandTotal * 100) / 100, - }; - - // ...原有价格上限控制、create、save、publish 逻辑... -} - -private async getDecimalPlaces(currency: string): Promise { - // 从系统配置或硬编码 VND=0, CNY/USD=2 - const map: Record = { CNY: 2, USD: 2, VND: 0 }; - return map[currency] ?? 2; -} -``` - -- [ ] **步骤 3:修改 updateOrder 方法,拒绝修改 currency** - -```typescript -async updateOrder(id: number, props: PurchaseOrderProps): Promise { - const order = await this.getOrderById(id); - if (props.currency && props.currency !== order.currency) { - throw new DomainError('币种创建后不可修改'); - } - if (props.exchangeRate && props.exchangeRate !== order.exchangeRate) { - throw new DomainError('汇率创建后不可修改'); - } - // ...原有更新逻辑... -} -``` - -- [ ] **步骤 4:修改 getPurchaseService 工厂函数** - -修改 `src/app/api/purchase/orders/route.ts` 的 `getPurchaseService`: - -```typescript -import { CurrencyApplicationService } from '@/application/services/CurrencyApplicationService'; -import { MysqlCurrencyRepository } from '@/infrastructure/repositories/MysqlCurrencyRepository'; - -function getPurchaseService(): PurchaseApplicationService { - registerEventHandlers(); - const orderRepo = RepositoryRegistry.getPurchaseOrderRepository(); - const currencyRepo = new MysqlCurrencyRepository(); - const currencyService = new CurrencyApplicationService(currencyRepo); - return new PurchaseApplicationService(orderRepo, currencyService); -} -``` - -- [ ] **步骤 5:运行测试验证** - -运行:`npx vitest run tests/unit/application/services/` -预期:现有测试可能需要更新构造函数签名(注入 Mock currencyService) - -- [ ] **步骤 6:Commit** - -```bash -git add src/application/services/PurchaseApplicationService.ts src/app/api/purchase/orders/route.ts -git commit -m "feat(currency): inject CurrencyApplicationService and compute base_* on order creation" -``` - ---- - -## 任务 12:PurchaseReturnApplicationService + PurchaseReconciliationApplicationService 改造 - -**文件:** -- 修改:`src/application/services/PurchaseReturnApplicationService.ts` -- 修改:`src/application/services/PurchaseReconciliationApplicationService.ts` - -- [ ] **步骤 1:修改 PurchaseReturnApplicationService.createReturn** - -在创建退货前查询原订单,从原订单继承 `currency` + `exchangeRate` + `baseCurrency`,并为退货明细计算 `base_*` 字段。 - -```typescript -async createReturn(props: ReturnCreateInput): Promise<{ id: number }> { - const originalOrder = await this.orderRepo.findById(props.orderId); - if (!originalOrder) throw new NotFoundError('采购订单不存在'); - - // 从原订单继承 currency + exchangeRate - const currency = originalOrder.currency; - const exchangeRate = originalOrder.exchangeRate; - const baseCurrency = originalOrder.baseCurrency; - - // 为退货明细计算 base_* 字段(使用原订单固化汇率) - const lines = props.lines.map((line) => { - const baseUnitPrice = Number((line.unitPrice * exchangeRate).toFixed(2)); - const baseAmount = Number((line.quantity * line.unitPrice * exchangeRate).toFixed(2)); - return { ...line, baseUnitPrice, baseAmount }; - }); - - const totalAmount = lines.reduce((sum, l) => sum + l.quantity * l.unitPrice, 0); - const baseTotalAmount = Number((totalAmount * exchangeRate).toFixed(2)); - - // 构建 PurchaseReturnProps - const returnProps: PurchaseReturnProps = { - ...props, - currency, exchangeRate, baseCurrency, baseTotalAmount, - lines, - totalAmount, - }; - - // ...原有持久化逻辑... -} -``` - -- [ ] **步骤 2:修改 PurchaseReconciliationApplicationService.createReconciliation** - -```typescript -async createReconciliation(props: ReconCreateInput): Promise<{ id: number }> { - // 1. 查询所有关联入库单(inv_inbound 暂无 currency 字段,使用 COALESCE 兼容) - const inbounds = await this.inboundRepo.findByIds(props.inboundIds); - - // 2. 校验同币种 - const currencies = new Set( - inbounds.map((i) => (i as any).currency ?? 'CNY') - ); - if (currencies.size > 1) { - throw new DomainError( - `对账失败:关联入库单币种不一致:${Array.from(currencies).join(', ')}` - ); - } - - // 3. 从首笔入库单继承 currency + exchangeRate - const currency = Array.from(currencies)[0] || 'CNY'; - const baseCurrency = await getSystemConfig('finance.base_currency', 'CNY'); - let exchangeRate = 1.0; - if (currency !== baseCurrency) { - exchangeRate = await this.currencyService.getLatestRate(currency, baseCurrency); - } - - // 4. 计算 base_* 字段 - const receiptAmount = inbounds.reduce((sum, i) => sum + (i.totalAmount || 0), 0); - const baseReceiptAmount = Number((receiptAmount * exchangeRate).toFixed(2)); - // ...其他 base_* 字段类似... - - // 5. 构建聚合根并持久化 - // ... -} -``` - -- [ ] **步骤 3:运行测试验证** - -运行:`npx vitest run tests/unit/application/services/` -预期:全部通过 - -- [ ] **步骤 4:Commit** - -```bash -git add src/application/services/PurchaseReturnApplicationService.ts src/application/services/PurchaseReconciliationApplicationService.ts -git commit -m "feat(currency): inherit currency in return and validate reconciliation currency consistency" -``` - ---- - -## 任务 13:采购订单 API 响应扩展 - -**文件:** -- 修改:`src/app/api/purchase/orders/route.ts` - -- [ ] **步骤 1:在 GET 列表响应的 serializedData 中追加 base_* 字段** - -```typescript -const serializedData = result.data.map((order) => ({ - // ...原有字段... - base_currency: order.baseCurrency, - base_total_amount: order.baseTotalAmount, - base_tax_amount: order.baseTaxAmount, - base_grand_total: order.baseGrandTotal, - lines: order.lines.map((line) => ({ - // ...原有字段... - base_unit_price: line.baseUnitPrice, - base_amount: line.baseAmount, - base_tax_amount: line.baseTaxAmount, - base_line_total: line.baseLineTotal, - })), -})); -``` - -- [ ] **步骤 2:在 POST 创建请求处理中拒绝 currency 修改(若有 id 表示更新)** - -POST 方法为创建,currency 可选传入。在 PUT 方法中增加 currency 不可变校验: - -```typescript -export const PUT = withPermission( - async (request: NextRequest, userInfo: UserInfo) => { - const body = await request.json(); - // ...原有逻辑... - - // 新增:currency 不可变校验(仅当 body 中包含 currency 字段时) - if (body.currency !== undefined) { - return errorResponse('币种创建后不可修改', 400, 400); - } - if (body.exchange_rate !== undefined) { - return errorResponse('汇率创建后不可修改', 400, 400); - } - - // ...原有 action 处理... - }, - { errorMessage: '操作失败' } -); -``` - -- [ ] **步骤 3:运行测试验证** - -运行:`npx vitest run tests/integration/api/purchase/` -预期:全部通过 - -- [ ] **步骤 4:Commit** - -```bash -git add src/app/api/purchase/orders/route.ts -git commit -m "feat(currency): expose base_* fields in purchase orders API and reject currency mutation" -``` - ---- - -## 任务 14:采购退货 + 对账 + 供应商 API 改造 - -**文件:** -- 修改:`src/app/api/purchase/return/route.ts` -- 修改:`src/app/api/purchase/reconciliation/route.ts` -- 修改:`src/app/api/purchase/suppliers/route.ts` - -- [ ] **步骤 1:采购退货 API 响应扩展** - -在 GET/POST 响应中追加 `currency`、`exchange_rate`、`base_currency`、`base_total_amount` 字段。POST 创建请求中不接收 currency/exchange_rate(由服务端从原订单继承)。 - -- [ ] **步骤 2:采购对账 API 响应扩展 + 同币种校验错误响应** - -在 GET/POST 响应中追加 currency + base_* 字段。在 POST 处理器中捕获 `DomainError`,当错误消息包含"币种不一致"时返回 400 + 明确错误消息。 - -```typescript -try { - const result = await service.createReconciliation(body); - return successResponse(result, '对账单创建成功'); -} catch (error) { - if (error instanceof DomainError) { - return errorResponse(error.message, 400, 400); - } - throw error; -} -``` - -- [ ] **步骤 3:供应商 API 扩展 default_currency 字段** - -在 GET/POST/PUT 请求中处理 `default_currency` 字段。GET 响应中包含该字段,POST/PUT 请求中接收并持久化。 - -- [ ] **步骤 4:运行测试验证** - -运行:`npx vitest run tests/integration/api/purchase/` -预期:全部通过 - -- [ ] **步骤 5:Commit** - -```bash -git add src/app/api/purchase/return/route.ts src/app/api/purchase/reconciliation/route.ts src/app/api/purchase/suppliers/route.ts -git commit -m "feat(currency): expose currency fields in return/reconciliation/suppliers APIs" -``` - ---- - -## 任务 15:i18n 扩展 - -**文件:** -- 修改:`messages/zh-CN.json`、`messages/zh-TW.json`、`messages/en.json`、`messages/vi.json` - -- [ ] **步骤 1:在 Common 块中追加 8 个新 key** - -zh-CN.json: -```json -{ - "Common": { - "baseCurrencyAmount": "本位币金额", - "originalCurrencyAmount": "原币金额", - "currencyImmutableWarning": "币种创建后不可修改", - "exchangeRateLocked": "汇率已固化", - "inboundCurrencyMismatch": "关联入库单币种不一致:{currencies}", - "supplierDefaultCurrency": "供应商默认币种", - "estimatedBaseAmount": "预估本位币金额", - "exchangeRateAtCreation": "创建时汇率" - } -} -``` - -zh-TW.json: -```json -{ - "Common": { - "baseCurrencyAmount": "本位幣金額", - "originalCurrencyAmount": "原幣金額", - "currencyImmutableWarning": "幣種創建後不可修改", - "exchangeRateLocked": "匯率已固化", - "inboundCurrencyMismatch": "關聯入庫單幣種不一致:{currencies}", - "supplierDefaultCurrency": "供應商預設幣種", - "estimatedBaseAmount": "預估本位幣金額", - "exchangeRateAtCreation": "創建時匯率" - } -} -``` - -en.json: -```json -{ - "Common": { - "baseCurrencyAmount": "Base Currency Amount", - "originalCurrencyAmount": "Original Currency Amount", - "currencyImmutableWarning": "Currency cannot be changed after creation", - "exchangeRateLocked": "Exchange rate locked", - "inboundCurrencyMismatch": "Inbound currency mismatch: {currencies}", - "supplierDefaultCurrency": "Supplier Default Currency", - "estimatedBaseAmount": "Estimated Base Amount", - "exchangeRateAtCreation": "Exchange Rate at Creation" - } -} -``` - -vi.json: -```json -{ - "Common": { - "baseCurrencyAmount": "Số tiền tiền tệ cơ sở", - "originalCurrencyAmount": "Số tiền nguyên tệ", - "currencyImmutableWarning": "Tiền tệ không thể thay đổi sau khi tạo", - "exchangeRateLocked": "Tỷ giá đã khóa", - "inboundCurrencyMismatch": "Tiền tệ phiếu nhập không nhất quán: {currencies}", - "supplierDefaultCurrency": "Tiền tệ mặc định của nhà cung cấp", - "estimatedBaseAmount": "Số tiền cơ sở ước tính", - "exchangeRateAtCreation": "Tỷ giá khi tạo" - } -} -``` - -- [ ] **步骤 2:验证 JSON 格式** - -运行:`node -e "JSON.parse(require('fs').readFileSync('messages/zh-CN.json', 'utf8')); console.log('OK')"`(4 个文件均验证) - -- [ ] **步骤 3:Commit** - -```bash -git add messages/zh-CN.json messages/zh-TW.json messages/en.json messages/vi.json -git commit -m "feat(currency): add i18n keys for purchase multi-currency pages" -``` - ---- - -## 任务 16:采购订单列表页改造 - -**文件:** -- 修改:`src/app/[locale]/purchase/orders/page.tsx` - -- [ ] **步骤 1:在 PurchaseOrder 接口追加 base_* 字段** - -```typescript -interface PurchaseOrder { - // ...原有字段... - base_currency?: string; - base_total_amount?: number; - base_tax_amount?: number; - base_grand_total?: number; - // 明细行 - lines?: (OrderItem & { - base_unit_price?: number; - base_amount?: number; - base_tax_amount?: number; - base_line_total?: number; - })[]; -} -``` - -- [ ] **步骤 2:在表头追加"币种"和"本位币金额"列** - -在 `金额` 之后追加: - -```tsx -{tc('baseCurrencyAmount')} -{tc('currency')} -``` - -- [ ] **步骤 3:在表体中使用 MoneyDisplay 组件展示双币种** - -替换硬编码 `¥{Number(order.grand_total...).toLocaleString()}`: - -```tsx -import { MoneyDisplay } from '@/components/ui/money-display'; - - - - -{order.currency || 'CNY'} -``` - -- [ ] **步骤 4:在展开的明细行中追加 base_* 列** - -在明细表头追加"本位币金额"列,表体中使用 `MoneyDisplay` 展示 `base_amount`。 - -- [ ] **步骤 5:在创建 Dialog 中增加 CurrencySelect 组件** - -```tsx -import { CurrencySelect } from '@/components/ui/currency-select'; - -// 在 newOrder state 中追加 currency 字段 -const [newOrder, setNewOrder] = useState({ - supplier_id: '', - delivery_date: '', - remark: '', - currency: '', // 新增 -}); - -// 在 Dialog 表单中追加币种选择器 -
- - setNewOrder((prev) => ({ ...prev, currency: v }))} - /> -
-``` - -- [ ] **步骤 6:在创建请求中传递 currency** - -```typescript -const data = await ApiClient.post('/api/purchase/orders', { - supplier_id: parseInt(newOrder.supplier_id), - // ... - currency: newOrder.currency || undefined, // 新增 - lines: validItems.map((item) => ({ ... })), -}); -``` - -- [ ] **步骤 7:运行前端开发服务器验证** - -运行:`npm run dev`,访问 `/purchase/orders` 页面 -预期:页面正常加载,表头显示"本位币金额"和"币种"列 - -- [ ] **步骤 8:Commit** - -```bash -git add src/app/[locale]/purchase/orders/page.tsx -git commit -m "feat(currency): display dual-currency in purchase orders list and create form" -``` - ---- - -## 任务 17:采购订单详情页 + 退货页 + 对账页改造 - -**文件:** -- 修改:`src/app/[locale]/purchase/orders/[id]/page.tsx`(若存在) -- 修改:`src/app/[locale]/purchase/return/page.tsx` -- 修改:`src/app/[locale]/purchase/reconciliation/page.tsx` - -- [ ] **步骤 1:采购订单详情页改造** - -在详情页头部展示 currency + exchangeRate + baseCurrency 信息卡片。明细表格每行展示原币 + 本位币金额。汇总区域展示双币种合计。 - -- [ ] **步骤 2:采购退货页改造** - -- 列表页增加 currency 列 -- 创建退货时展示原订单 currency(只读,从 API 响应中读取) -- 退货金额使用 `MoneyDisplay` 双币种展示 - -- [ ] **步骤 3:采购对账页改造** - -- 列表页增加 currency 列 -- 创建对账时展示从入库单继承的 currency(只读) -- 同币种校验失败时显示 i18n 错误消息 -- 对账金额双币种展示 - -- [ ] **步骤 4:运行前端验证** - -运行:`npm run dev`,访问各页面 -预期:页面正常加载,双币种展示 - -- [ ] **步骤 5:Commit** - -```bash -git add src/app/[locale]/purchase/orders/[id]/page.tsx src/app/[locale]/purchase/return/page.tsx src/app/[locale]/purchase/reconciliation/page.tsx -git commit -m "feat(currency): display dual-currency in purchase detail/return/reconciliation pages" -``` - ---- - -## 任务 18:供应商管理页面改造 - -**文件:** -- 修改:`src/app/[locale]/purchase/suppliers/page.tsx` - -- [ ] **步骤 1:在 Supplier 接口追加 default_currency 字段** - -```typescript -interface Supplier { - // ...原有字段... - default_currency?: string; -} -``` - -- [ ] **步骤 2:在表单中追加 CurrencySelect 组件** - -在创建/编辑供应商的 Dialog 中追加: - -```tsx -
- - setFormData((prev) => ({ ...prev, default_currency: v }))} - /> -
-``` - -- [ ] **步骤 3:在列表表格中展示默认币种列** - -在表头追加 `{tc('supplierDefaultCurrency')}`,表体中展示 `{supplier.default_currency || 'CNY'}`。 - -- [ ] **步骤 4:运行前端验证** - -运行:`npm run dev`,访问 `/purchase/suppliers` -预期:表单和列表展示 default_currency - -- [ ] **步骤 5:Commit** - -```bash -git add src/app/[locale]/purchase/suppliers/page.tsx -git commit -m "feat(currency): add default_currency field to supplier management page" -``` - ---- - -## 自检 - -### 规格覆盖度 - -| 规格章节 | 对应任务 | -|---------|---------| -| 2.1 表改造总览 | 任务 1(migration 064)| -| 2.2 Migration 064 | 任务 1 | -| 2.3 Migration 065 | 任务 2 | -| 2.4 Drizzle schema.ts | 任务 3 | -| 3.1 CurrencySnapshot | 任务 4 | -| 3.2 PurchaseOrder | 任务 6 | -| 3.3 PurchaseOrderLine | 任务 5 | -| 3.4 PurchaseReturn | 任务 7 | -| 3.5 PurchaseReconciliation | 任务 8 | -| 3.7 领域事件 | 任务 6/7/8 | -| 4.1-4.3 Repository 改造 | 任务 9/10 | -| 5.1 PurchaseApplicationService | 任务 11 | -| 5.2 PurchaseReturnApplicationService | 任务 12 | -| 5.3 PurchaseReconciliationApplicationService | 任务 12 | -| 6.1 采购订单 API | 任务 13 | -| 6.2 采购退货 API | 任务 14 | -| 6.3 采购对账 API | 任务 14 | -| 6.4 供应商 API | 任务 14 | -| 7.1 采购订单列表页 | 任务 16 | -| 7.2 采购订单详情页 | 任务 17 | -| 7.3 创建/编辑表单 | 任务 16 | -| 7.4 采购退货页面 | 任务 17 | -| 7.5 采购对账页面 | 任务 17 | -| 7.6 供应商管理页面 | 任务 18 | -| 7.7 i18n 扩展 | 任务 15 | - -无遗漏。 - -### 占位符扫描 - -- 无 "待定"、"TODO"、"后续实现" -- 每个步骤包含完整代码或精确路径 - -### 类型一致性 - -- `CurrencySnapshot.create(currency, exchangeRate, baseCurrency)` — 全任务统一 -- `PurchaseOrderProps.baseCurrency` / `baseTotalAmount` / `baseTaxAmount` / `baseGrandTotal` — 全任务统一 -- `PurchaseOrderLineProps.baseUnitPrice` / `baseAmount` / `baseTaxAmount` / `baseLineTotal` — 全任务统一 -- `PurchaseReturnProps.currency` / `exchangeRate` / `baseCurrency` / `baseTotalAmount` — 全任务统一 -- `PurchaseReconciliationProps.currency` / `exchangeRate` / 7 个 `base_*` — 全任务统一 -- API 响应字段使用 snake_case(`base_total_amount`),领域层使用 camelCase(`baseTotalAmount`)— 与现有代码风格一致 diff --git a/docs/superpowers/plans/2026-07-17-multi-currency-phase2b.md b/docs/superpowers/plans/2026-07-17-multi-currency-phase2b.md deleted file mode 100644 index 24186683..00000000 --- a/docs/superpowers/plans/2026-07-17-multi-currency-phase2b.md +++ /dev/null @@ -1,656 +0,0 @@ -# Phase 2b Multi-Currency Implementation Plan - -> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. - -**Goal:** Extend multi-currency support to sales, inventory/warehouse, and finance (AR/AP) modules. - -**Architecture:** Reuse Phase 1 infrastructure (CurrencySnapshot, Money, CurrencyApplicationService) and Phase 2a patterns (domain aggregate props, repository mapToProps, service injection, API response extension, frontend MoneyDisplay). Each module follows the same 6-layer pattern: DB migration → domain layer → repository → application service → API route → frontend. - -**Tech Stack:** Next.js 14, MySQL 8, Drizzle ORM, Vitest, next-intl - ---- - -### Task 1: Migration 066 — Inventory/Warehouse Currency Columns - -**Files:** -- Create: `database/migrations/066_inventory_multi_currency.sql` -- Test: `tests/integration/database/migration-066.test.ts` - -**Step 1: Write the migration SQL** - -```sql --- inv_inbound_order -ALTER TABLE `inv_inbound_order` - ADD COLUMN `currency` varchar(10) DEFAULT 'CNY' COMMENT '币种' AFTER `total_amount`, - ADD COLUMN `exchange_rate` decimal(18,6) DEFAULT 1.000000 COMMENT '汇率' AFTER `currency`, - ADD COLUMN `base_total_amount` decimal(18,4) DEFAULT 0.0000 COMMENT '本位币总金额' AFTER `exchange_rate`; - -ALTER TABLE `inv_inbound_item` - ADD COLUMN `base_unit_price` decimal(18,4) DEFAULT 0.0000 COMMENT '本位币单价' AFTER `unit_price`, - ADD COLUMN `base_amount` decimal(18,4) DEFAULT 0.0000 COMMENT '本位币金额' AFTER `amount`; - -ALTER TABLE `inv_outbound_order` - ADD COLUMN `exchange_rate` decimal(18,6) DEFAULT 1.000000 COMMENT '汇率' AFTER `currency`, - ADD COLUMN `base_total_amount` decimal(18,4) DEFAULT 0.0000 COMMENT '本位币总金额' AFTER `exchange_rate`; - -ALTER TABLE `inv_outbound_item` - ADD COLUMN `base_unit_price` decimal(18,4) DEFAULT 0.0000 COMMENT '本位币单价' AFTER `unit_price`, - ADD COLUMN `base_amount` decimal(18,4) DEFAULT 0.0000 COMMENT '本位币金额' AFTER `amount`; - -UPDATE `inv_inbound_order` SET `base_total_amount` = `total_amount` WHERE `base_total_amount` = 0; -UPDATE `inv_outbound_order` SET `base_total_amount` = `total_amount` WHERE `base_total_amount` = 0; -``` - -**Step 2: Write the migration SQL file** - -Create `database/migrations/066_inventory_multi_currency.sql` with the above SQL. - -**Step 3: Validate SQL syntax** - -Run: `node -e "require('child_process').execSync('mysql --help', {stdio:'inherit'})" 2>/dev/null || echo "MySQL CLI not available — manual review required"` - -**Step 4: Commit** - -```bash -git add database/migrations/066_inventory_multi_currency.sql -git commit -m "feat(currency): add migration 066 for inventory multi-currency fields" -``` - ---- - -### Task 2: Migration 067 — Sales Module Currency Columns - -**Files:** -- Create: `database/migrations/067_sales_multi_currency.sql` - -**Step 1: Write the migration SQL** - -```sql --- sal_order (已有 currency + exchange_rate) -ALTER TABLE `sal_order` - ADD COLUMN `base_total_amount` decimal(18,4) DEFAULT 0.0000 COMMENT '本位币金额' AFTER `total_amount`, - ADD COLUMN `base_tax_amount` decimal(18,4) DEFAULT 0.0000 COMMENT '本位币税额' AFTER `tax_amount`, - ADD COLUMN `base_grand_total` decimal(18,4) DEFAULT 0.0000 COMMENT '本位币含税总额' AFTER `total_with_tax`; - -ALTER TABLE `sal_order_detail` - ADD COLUMN `currency` varchar(10) DEFAULT 'CNY' COMMENT '币种' AFTER `material_spec`, - ADD COLUMN `exchange_rate` decimal(18,6) DEFAULT 1.000000 COMMENT '汇率' AFTER `currency`, - ADD COLUMN `base_unit_price` decimal(18,4) DEFAULT 0.0000 COMMENT '本位币单价' AFTER `unit_price`, - ADD COLUMN `base_amount` decimal(18,4) DEFAULT 0.0000 COMMENT '本位币金额' AFTER `amount`, - ADD COLUMN `base_tax_amount` decimal(18,4) DEFAULT 0.0000 COMMENT '本位币税额' AFTER `tax_amount`; - -ALTER TABLE `sal_delivery` - ADD COLUMN `currency` varchar(10) DEFAULT 'CNY' COMMENT '币种' AFTER `total_amount`, - ADD COLUMN `exchange_rate` decimal(18,6) DEFAULT 1.000000 COMMENT '汇率' AFTER `currency`, - ADD COLUMN `base_total_amount` decimal(18,4) DEFAULT 0.0000 COMMENT '本位币总金额' AFTER `exchange_rate`; - -ALTER TABLE `sal_delivery_detail` - ADD COLUMN `base_unit_price` decimal(18,4) DEFAULT 0.0000 COMMENT '本位币单价' AFTER `unit_price`, - ADD COLUMN `base_amount` decimal(18,4) DEFAULT 0.0000 COMMENT '本位币金额' AFTER `amount`; - -ALTER TABLE `sal_return` - ADD COLUMN `currency` varchar(10) DEFAULT 'CNY' COMMENT '币种' AFTER `total_amount`, - ADD COLUMN `exchange_rate` decimal(18,6) DEFAULT 1.000000 COMMENT '汇率' AFTER `currency`, - ADD COLUMN `base_total_amount` decimal(18,4) DEFAULT 0.0000 COMMENT '本位币总金额' AFTER `exchange_rate`; - -ALTER TABLE `sal_return_detail` - ADD COLUMN `base_unit_price` decimal(18,4) DEFAULT 0.0000 COMMENT '本位币单价' AFTER `unit_price`, - ADD COLUMN `base_amount` decimal(18,4) DEFAULT 0.0000 COMMENT '本位币金额' AFTER `amount`; - -ALTER TABLE `sal_reconciliation` - ADD COLUMN `currency` varchar(10) DEFAULT 'CNY' COMMENT '币种' AFTER `total_amount`, - ADD COLUMN `exchange_rate` decimal(18,6) DEFAULT 1.000000 COMMENT '汇率' AFTER `currency`, - ADD COLUMN `base_delivery_amount` decimal(18,4) DEFAULT 0.0000 COMMENT '本位币出库金额' AFTER `delivery_amount`, - ADD COLUMN `base_return_amount` decimal(18,4) DEFAULT 0.0000 COMMENT '本位币退货金额' AFTER `return_amount`, - ADD COLUMN `base_net_amount` decimal(18,4) DEFAULT 0.0000 COMMENT '本位币净额' AFTER `net_amount`, - ADD COLUMN `base_discount_amount` decimal(18,4) DEFAULT 0.0000 COMMENT '本位币折扣金额' AFTER `discount_amount`, - ADD COLUMN `base_received_amount` decimal(18,4) DEFAULT 0.0000 COMMENT '本位币实收金额' AFTER `received_amount`, - ADD COLUMN `base_balance_amount` decimal(18,4) DEFAULT 0.0000 COMMENT '本位币余额' AFTER `balance_amount`; - -UPDATE `sal_order` SET `base_total_amount` = `total_amount`, `base_tax_amount` = `tax_amount`, `base_grand_total` = `total_with_tax` WHERE `base_total_amount` = 0; -UPDATE `sal_delivery` SET `base_total_amount` = `total_amount` WHERE `base_total_amount` = 0; -UPDATE `sal_return` SET `base_total_amount` = `total_amount` WHERE `base_total_amount` = 0; -``` - -**Step 2: Write the migration SQL file** - -Create `database/migrations/067_sales_multi_currency.sql`. - -**Step 3: Commit** - -```bash -git add database/migrations/067_sales_multi_currency.sql -git commit -m "feat(currency): add migration 067 for sales multi-currency fields" -``` - ---- - -### Task 3: Migration 068 — Finance Module Currency Columns - -**Files:** -- Create: `database/migrations/068_finance_multi_currency.sql` - -**Step 1: Write the migration SQL** - -```sql -ALTER TABLE `fin_payable` - ADD COLUMN `currency` varchar(10) DEFAULT 'CNY' COMMENT '币种' AFTER `amount`, - ADD COLUMN `exchange_rate` decimal(18,6) DEFAULT 1.000000 COMMENT '汇率' AFTER `currency`, - ADD COLUMN `base_amount` decimal(18,4) DEFAULT 0.0000 COMMENT '本位币金额' AFTER `exchange_rate`; - -ALTER TABLE `fin_receivable` - ADD COLUMN `currency` varchar(10) DEFAULT 'CNY' COMMENT '币种' AFTER `amount`, - ADD COLUMN `exchange_rate` decimal(18,6) DEFAULT 1.000000 COMMENT '汇率' AFTER `currency`, - ADD COLUMN `base_amount` decimal(18,4) DEFAULT 0.0000 COMMENT '本位币金额' AFTER `exchange_rate`; - -ALTER TABLE `fin_payment_record` - ADD COLUMN `currency` varchar(10) DEFAULT 'CNY' COMMENT '币种' AFTER `amount`, - ADD COLUMN `exchange_rate` decimal(18,6) DEFAULT 1.000000 COMMENT '汇率' AFTER `currency`, - ADD COLUMN `base_amount` decimal(18,4) DEFAULT 0.0000 COMMENT '本位币金额' AFTER `exchange_rate`; - -ALTER TABLE `fin_receipt_record` - ADD COLUMN `currency` varchar(10) DEFAULT 'CNY' COMMENT '币种' AFTER `amount`, - ADD COLUMN `exchange_rate` decimal(18,6) DEFAULT 1.000000 COMMENT '汇率' AFTER `currency`, - ADD COLUMN `base_amount` decimal(18,4) DEFAULT 0.0000 COMMENT '本位币金额' AFTER `exchange_rate`; - -UPDATE `fin_payable` SET `base_amount` = `amount`, `currency` = 'CNY', `exchange_rate` = 1.0 WHERE `base_amount` = 0; -UPDATE `fin_receivable` SET `base_amount` = `amount`, `currency` = 'CNY', `exchange_rate` = 1.0 WHERE `base_amount` = 0; -UPDATE `fin_payment_record` SET `base_amount` = `amount`, `currency` = 'CNY', `exchange_rate` = 1.0 WHERE `base_amount` = 0; -UPDATE `fin_receipt_record` SET `base_amount` = `amount`, `currency` = 'CNY', `exchange_rate` = 1.0 WHERE `base_amount` = 0; -``` - -**Step 2: Write the file** - -**Step 3: Commit** - -```bash -git add database/migrations/068_finance_multi_currency.sql -git commit -m "feat(currency): add migration 068 for finance multi-currency fields" -``` - ---- - -### Task 4: Drizzle Schema Sync - -**Files:** -- Modify: `src/lib/db/schema.ts` - -**Step 1: Read the current schema** - -Read `src/lib/db/schema.ts` to find existing table definitions for `sal_order`, `sal_order_detail`, `sal_delivery`, `sal_delivery_detail`, `sal_return`, `sal_return_detail`, `sal_reconciliation`, `inv_inbound_order`, `inv_inbound_item`, `inv_outbound_order`, `inv_outbound_item`, `fin_payable`, `fin_receivable`, `fin_payment_record`, `fin_receipt_record`. - -**Step 2: Add new columns to each table definition** - -For each table, add the new column definitions using the same pattern as Phase 2a's `pur_purchase_order` entries. Example: -```typescript -baseTotalAmount: numeric('base_total_amount', { precision: 18, scale: 4 }).default('0.0000'), -``` - -**Step 3: Verify** - -Run: `npx tsc --noEmit` -Expected: No new type errors - -**Step 4: Commit** - -```bash -git add src/lib/db/schema.ts -git commit -m "feat(currency): sync Drizzle schema with Phase 2b multi-currency columns" -``` - ---- - -### Task 5: Domain — SalesOrder + SalesOrderLine - -**Files:** -- Modify: `src/domain/sales/aggregates/SalesOrder.ts` -- Modify: `src/domain/sales/entities/SalesOrderLine.ts` -- Modify: `src/domain/sales/events/SalesOrderEvents.ts` - -**Step 1: Read existing files** - -Read all 3 files to understand current structure. - -**Step 2: Extend SalesOrderLineProps interface** - -Add to `SalesOrderLineProps`: -```typescript -baseUnitPrice?: number; -baseAmount?: number; -baseTaxAmount?: number; -baseLineTotal?: number; -``` - -Add private fields, getters, and update `create`/`reconstitute` with `?? 0` defaults (exact same pattern as `PurchaseOrderLine` in Phase 2a). - -**Step 3: Extend SalesOrderProps interface** - -Add: -```typescript -currency?: string; -exchangeRate?: number; -baseCurrency?: string; -baseTotalAmount?: number; -baseTaxAmount?: number; -baseGrandTotal?: number; -``` - -**Step 4: Extend SalesOrder class** - -- Add constructor params: `currency`, `exchangeRate`, `baseCurrency`, `baseTotalAmount`, `baseTaxAmount`, `baseGrandTotal` -- Add getters for base_* fields -- Update `create` with `|| 'CNY'` for currency, `?? 0` for amounts -- Update `reconstitute` to read the fields -- In event creation, pass currency fields to `SalesOrderCreatedEvent` - -**Step 5: Extend SalesOrderEvents.ts** - -Add optional fields to `SalesOrderCreatedEvent` payload: -```typescript -currency?: string; -exchangeRate?: number; -baseCurrency?: string; -baseTotalAmount?: number; -baseGrandTotal?: number; -``` - -**Step 6: Verify** - -Run: `npx tsc --noEmit` — expect no new errors - -**Step 7: Commit** - -```bash -git add src/domain/sales/aggregates/SalesOrder.ts src/domain/sales/entities/SalesOrderLine.ts src/domain/sales/events/SalesOrderEvents.ts -git commit -m "feat(currency): extend SalesOrder aggregate with base_* fields and event payload" -``` - ---- - -### Task 6: Domain — Delivery + DeliveryLine - -**Files:** -- Modify: `src/domain/sales/aggregates/Delivery.ts` -- Modify: `src/domain/sales/entities/DeliveryLine.ts` -- Modify: `src/domain/sales/events/DeliveryEvents.ts` (if exists) - -**Step 1: Read existing files** - -**Step 2: Extend props and entities** - -- `DeliveryProps`: add `currency?`, `exchangeRate?`, `baseCurrency?`, `baseTotalAmount?` -- `DeliveryLineProps` (if exists as separate entity): add `baseUnitPrice?`, `baseAmount?` -- If DeliveryLine is inline, add base fields inline - -**Step 3: Extend Delivery class** - -Add constructor params, getters, update create/reconstitute (same pattern as `PurchaseReturn`). - -**Step 4: Extend events** - -Add optional currency fields to `DeliveryCreatedEvent` payload. - -**Step 5: Verify** - -Run: `npx tsc --noEmit` - -**Step 6: Commit** - -```bash -git add src/domain/sales/aggregates/Delivery.ts src/domain/sales/events/ -git commit -m "feat(currency): extend Delivery aggregate with base_* fields" -``` - ---- - -### Task 7: Domain — ReturnOrder + ReturnOrderLine - -**Files:** -- Modify: `src/domain/sales/aggregates/ReturnOrder.ts` -- Modify: `src/domain/sales/entities/ReturnOrderLine.ts` -- Modify: `src/domain/sales/events/ReturnOrderEvents.ts` (if exists) - -Same pattern as `PurchaseReturn` in Phase 2a. Add `currency`, `exchangeRate`, `baseCurrency`, `baseTotalAmount` to props, constructor, create/reconstitute, and events. - ---- - -### Task 8: Domain — Reconciliation + WriteOffRecord - -**Files:** -- Modify: `src/domain/sales/aggregates/Reconciliation.ts` -- Modify: `src/domain/sales/entities/WriteOffRecord.ts` (if exists) -- Modify: `src/domain/sales/events/ReconciliationEvents.ts` (if exists) - -Same pattern as `PurchaseReconciliation` in Phase 2a. Add `currency`, `exchangeRate`, `baseCurrency`, `baseDeliveryAmount`, `baseReturnAmount`, `baseNetAmount`, `baseDiscountAmount`, `baseReceivedAmount`, `baseBalanceAmount`. - ---- - -### Task 9: Domain — InboundOrder + OutboundOrder - -**Files:** -- Modify: `src/domain/warehouse/aggregates/InboundOrder.ts` -- Modify: `src/domain/warehouse/entities/InboundItem.ts` (if exists) -- Modify: `src/domain/warehouse/aggregates/OutboundOrder.ts` - -**InboundOrder:** -- Add `currency`, `exchangeRate`, `baseCurrency`, `baseTotalAmount` to props and class -- InboundItem (if entity): add `baseUnitPrice`, `baseAmount` - -**OutboundOrder:** -- Already has `currency` field — add `exchangeRate`, `baseTotalAmount` - ---- - -### Task 10: Domain — Payable + Receivable - -**Files:** -- Modify: `src/domain/finance/aggregates/Payable.ts` -- Modify: `src/domain/finance/aggregates/Receivable.ts` -- Modify: `src/domain/finance/events/PayableEvents.ts` (if exists) -- Modify: `src/domain/finance/events/ReceivableEvents.ts` (if exists) - -**Payable/Receivable:** -- Add `currency?`, `exchangeRate?`, `baseAmount?` to Props interface -- Add constructor params, getters, update create/reconstitute -- Add optional fields to CreatedEvent payload - ---- - -### Task 11: Repository — MysqlSalesOrderRepository - -**Files:** -- Modify: `src/infrastructure/repositories/MysqlSalesOrderRepository.ts` - -**Step 1: Read existing file** - -**Step 2: Extend row interfaces** - -Add `base_total_amount`, `base_tax_amount`, `base_grand_total` to header row; `base_unit_price`, `base_amount`, `base_tax_amount`, `base_line_total` to line row. - -**Step 3: Extend SQL** - -- Add columns to column constants (if they exist) or inline SELECTs -- Add columns to INSERT for save method -- Add columns to UPDATE if applicable - -**Step 4: Extend mapToProps** - -Add mapping for all new fields with `Number(col) || 0` pattern and `baseCurrency: 'CNY'`. - -**Step 5: Verify** - -Run: `npx tsc --noEmit` - -**Step 6: Commit** - ---- - -### Task 12: Repository — Delivery, Return, Reconciliation - -**Files:** -- Modify: `src/infrastructure/repositories/MysqlDeliveryRepository.ts` -- Modify: `src/infrastructure/repositories/MysqlReturnOrderRepository.ts` -- Modify: `src/infrastructure/repositories/MysqlReconciliationRepository.ts` - -Each follows the same pattern as Task 11: extend row interface → extend SQL → extend mapToProps. - ---- - -### Task 13: Repository — Inbound, Outbound, Payable, Receivable - -**Files:** -- Modify: `src/infrastructure/repositories/MysqlInboundOrderRepository.ts` -- Modify: `src/infrastructure/repositories/MysqlOutboundOrderRepository.ts` -- Modify: `src/infrastructure/repositories/MysqlPayableRepository.ts` -- Modify: `src/infrastructure/repositories/MysqlReceivableRepository.ts` - -For inbound: add `currency`, `exchange_rate`, `base_total_amount` columns. -For outbound: add `exchange_rate`, `base_total_amount` columns. -For payable/receivable: add `currency`, `exchange_rate`, `base_amount` columns. - ---- - -### Task 14: Application — SalesApplicationService - -**Files:** -- Modify: `src/application/services/SalesApplicationService.ts` -- Test: Create `tests/unit/application/services/sales-application-service.test.ts` - -**Step 1: Read existing file** - -**Step 2: Add imports** - -```typescript -import { CurrencyApplicationService } from './CurrencyApplicationService'; -import { CurrencySnapshot } from '@/domain/shared/value-objects/CurrencySnapshot'; -import { Money } from '@/domain/shared/value-objects/Money'; -import { getSystemConfig } from '@/lib/system-config'; -``` - -**Step 3: Inject CurrencyApplicationService** - -```typescript -constructor( - private readonly orderRepo: ISalesOrderRepository, - private readonly currencyService: CurrencyApplicationService, -) {} -``` - -**Step 4: Modify createOrder method** - -- After existing logic, determine currency (input > system default) -- Query base currency from system config -- Get exchange rate if currency ≠ baseCurrency -- Build CurrencySnapshot -- For each line, calculate base_* via snapshot.convert(Money.create(...)) -- Calculate header base totals - -**Step 5: Modify createDelivery** - -- From SalesOrder, inherit currency + exchangeRate (read-only) -- Calculate baseTotalAmount from delivery total * exchangeRate - -**Step 6: Modify createReturn** - -- From delivery or order, inherit currency -- Calculate base amounts - -**Step 7: Modify createReconciliation** - -- Query delivery orders, verify same currency -- Throw DomainError if mismatch -- Calculate base_* amounts - -**Step 8: Write unit tests** - -Test cases: -1. Default CNY currency (rate = 1.0, base = original) -2. USD conversion (base amounts = original * rate) -3. Explicit currency override -4. Line base amounts correctly calculated - -**Step 9: Verify** - -Run: `npx vitest run tests/unit/application/services/sales-application-service.test.ts` -Run: `npx tsc --noEmit` - -**Step 10: Commit** - ---- - -### Task 15: Application — InboundApplicationService - -**Files:** -- Modify: `src/application/services/InboundApplicationService.ts` - -- Inject CurrencyApplicationService + IPurchaseOrderRepository -- createInbound: if linked PO, inherit currency (allow override); no PO → CNY default -- createOutbound: if linked SO, inherit currency (allow override); no SO → CNY default -- Calculate base amounts for each line item - ---- - -### Task 16: Application — FinanceApplicationService - -**Files:** -- Modify: `src/application/services/FinanceApplicationService.ts` - -- Inject CurrencyApplicationService -- createPayable: inherit from source document (inbound/PO), allow manual override -- createReceivable: inherit from source document (delivery/SO), allow manual override -- recordPayment/recordReceipt: inherit from payable/receivable - ---- - -### Task 17: API — Sales Orders Route - -**Files:** -- Modify: `src/app/api/sales/orders/route.ts` - -**Step 1: Read existing file** - -**Step 2: Extend GET serialization** - -Add `base_currency`, `base_total_amount`, `base_tax_amount`, `base_grand_total` to response. For lines, add `base_unit_price`, `base_amount`, `base_tax_amount`, `base_line_total`. - -**Step 3: Add PUT currency immutability check** - -```typescript -if (body.currency !== undefined) { - return errorResponse('币种创建后不可修改', 400, 400); -} -if (body.exchange_rate !== undefined) { - return errorResponse('汇率创建后不可修改', 400, 400); -} -``` - -**Step 4: Verify** - -Run: `npx tsc --noEmit` - -**Step 5: Commit** - ---- - -### Task 18: API — Sales Delivery, Return, Reconciliation Routes - -**Files:** -- Modify: `src/app/api/sales/delivery/route.ts` -- Modify: `src/app/api/sales/return/route.ts` -- Modify: `src/app/api/sales/reconciliation/route.ts` - -Each: extend GET serialization with currency + base_* fields. Reconciliation POST wraps DomainError for currency mismatch. - ---- - -### Task 19: API — Inventory Inbound, Outbound Routes - -**Files:** -- Modify: `src/app/api/inventory/inbound/route.ts` -- Modify: `src/app/api/inventory/outbound/route.ts` - -Each: extend GET with currency fields; POST accepts optional currency. - ---- - -### Task 20: API — Finance Payable, Receivable, Payment, Receipt Routes - -**Files:** -- Modify: `src/app/api/finance/payable/route.ts` -- Modify: `src/app/api/finance/receivable/route.ts` -- Modify: `src/app/api/finance/payment/route.ts` -- Modify: `src/app/api/finance/receipt/route.ts` - -Each: extend GET with currency + exchange_rate + base_amount; POST accepts currency. - ---- - -### Task 21: i18n Extension - -**Files:** -- Modify: `messages/zh-CN.json` -- Modify: `messages/zh-TW.json` -- Modify: `messages/en.json` -- Modify: `messages/vi.json` - -Add to `Common` block: -```json -"baseCurrencyAmount": "本位币金额", -"inheritedCurrency": "继承币种", -"inboundCurrency": "入库币种", -"payableCurrency": "应付币种", -"receivableCurrency": "应收币种", -"allowOverride": "允许修改", -"currencyConsistencyError": "单据币种不一致" -``` - -Verify: `node -e "JSON.parse(require('fs').readFileSync('messages/zh-CN.json','utf8')); console.log('OK')"` - ---- - -### Task 22: Frontend — Sales Orders Page - -**Files:** -- Modify: `src/app/[locale]/sales/orders/page.tsx` - -1. Extend SalesOrder interface with base_* fields -2. Add `baseCurrencyAmount` and `currency` columns to table header -3. Use `` for amount rendering -4. Add `` in create dialog -5. Pass `currency` in API POST - ---- - -### Task 23: Frontend — Sales Delivery, Return, Reconciliation Pages - -**Files:** -- Modify: `src/app/[locale]/sales/delivery/page.tsx` -- Modify: `src/app/[locale]/sales/return/page.tsx` -- Modify: `src/app/[locale]/sales/reconciliation/page.tsx` - -Each: add currency column + MoneyDisplay. Return/reconciliation show inherited currency as read-only. - ---- - -### Task 24: Frontend — Inventory Inbound, Outbound Pages - -**Files:** -- Modify: `src/app/[locale]/inventory/inbound/page.tsx` -- Modify: `src/app/[locale]/inventory/outbound/page.tsx` - -Each: add currency column + MoneyDisplay. Inbound allows currency selection/override. - ---- - -### Task 25: Frontend — Finance Payable, Receivable Pages - -**Files:** -- Modify: `src/app/[locale]/finance/payable/page.tsx` -- Modify: `src/app/[locale]/finance/receivable/page.tsx` - -Each: add currency column + MoneyDisplay + source document link. - ---- - -### Full Verification - -**Step 1: TypeScript check** - -Run: `npx tsc --noEmit` -Expected: 0 new errors (pre-existing 4 errors in currency-snapshot.test.ts only) - -**Step 2: Run all tests** - -Run: `npx vitest run` -Expected: 1388+ pass, 6 pre-existing failures unchanged - -**Step 3: Build** - -Run: `npm run build` -Expected: exit 0 - -**Step 4: Push** - -```bash -git push origin feature/multi-currency-phase1 -``` diff --git a/docs/superpowers/plans/2026-07-20-hr-module-upgrade.md b/docs/superpowers/plans/2026-07-20-hr-module-upgrade.md deleted file mode 100644 index c95d09bb..00000000 --- a/docs/superpowers/plans/2026-07-20-hr-module-upgrade.md +++ /dev/null @@ -1,717 +0,0 @@ -# HR 模块深度升级 — 实施计划 - -> **目标**:将 VNERP HR 模块从"基础记录系统"升级为"制造业深度薪资计算引擎",支持计件/计时/绩效混合薪酬模式。 - -**架构**:Drizzle ORM + Next.js API Routes (REST) + shadcn/ui 前端,遵循现有 DDD 分层模式。 - -**技术栈**:Drizzle ORM (mysql-core) + Next.js 16 + next-intl + shadcn/ui + Recharts (报表) - ---- - -## Phase 1: 数据库与 ORM 基础 (1-2 周) - -### 任务 1.1:Drizzle Schema — HR 表映射 - -**文件**: -- 修改:`src/lib/db/schema.ts` — 追加 HR 表定义 -- 创建:`database/sql/hr_salary_standard.sql` — 新表 DDL -- 创建:`database/sql/hr_piece_rate.sql` -- 创建:`database/sql/hr_salary_profile.sql` -- 创建:`database/sql/hr_salary_calculation.sql` -- 创建:`database/sql/hr_piece_work_detail.sql` -- 创建:`database/sql/hr_attendance_exception.sql` - -现有 HR 表(`sys_employee`, `sys_salary`, `hr_attendance`, `hr_training`, `hr_training_participant`)迁移到 Drizzle ORM。新增 6 张核心表。 - -**Drizzle 表定义要点**(追加到 `schema.ts`): - -```typescript -// ==================== HR 模块 (Drizzle ORM) ==================== - -// 薪资标准表 -export const hrSalaryStandard = mysqlTable('hr_salary_standard', { - id: bigint('id', { mode: 'number', unsigned: true }).autoincrement().primaryKey(), - positionCode: varchar('position_code', { length: 50 }), - skillLevel: int('skill_level'), - baseSalary: decimal('base_salary', { precision: 10, scale: 2 }), - pieceRateType: varchar('piece_rate_type', { length: 20 }), - performanceBase: decimal('performance_base', { precision: 10, scale: 2 }), - allowanceNight: decimal('allowance_night', { precision: 10, scale: 2 }), - allowanceHighTemp: decimal('allowance_high_temp', { precision: 10, scale: 2 }), - effectiveDate: date('effective_date'), - factoryId: bigint('factory_id', { mode: 'number', unsigned: true }), - status: tinyint('status').default(1), - createTime: datetime('create_time').default(sql`CURRENT_TIMESTAMP`), - updateTime: datetime('update_time').default(sql`CURRENT_TIMESTAMP`), - deleted: tinyint('deleted').default(0), -}); - -// 工序单价表 -export const hrPieceRate = mysqlTable('hr_piece_rate', { - id: bigint('id', { mode: 'number', unsigned: true }).autoincrement().primaryKey(), - processCode: varchar('process_code', { length: 50 }), - productType: varchar('product_type', { length: 50 }), - unitPrice: decimal('unit_price', { precision: 10, scale: 4 }), - unit: varchar('unit', { length: 20 }), - qualityThreshold: decimal('quality_threshold', { precision: 5, scale: 2 }), - factoryId: bigint('factory_id', { mode: 'number', unsigned: true }), - status: tinyint('status').default(1), - createTime: datetime('create_time').default(sql`CURRENT_TIMESTAMP`), - updateTime: datetime('update_time').default(sql`CURRENT_TIMESTAMP`), - deleted: tinyint('deleted').default(0), -}); - -// 员工薪资档案表 -export const hrSalaryProfile = mysqlTable('hr_salary_profile', { - id: bigint('id', { mode: 'number', unsigned: true }).autoincrement().primaryKey(), - employeeId: bigint('employee_id', { mode: 'number', unsigned: true }), - salaryType: varchar('salary_type', { length: 20 }), // piece/time/mixed - baseSalary: decimal('base_salary', { precision: 10, scale: 2 }), - socialInsuranceBase: decimal('social_insurance_base', { precision: 10, scale: 2 }), - housingFundRate: decimal('housing_fund_rate', { precision: 5, scale: 2 }), - taxDeduction: decimal('tax_deduction', { precision: 10, scale: 2 }), - bankAccount: varchar('bank_account', { length: 50 }), - effectiveDate: date('effective_date'), - status: tinyint('status').default(1), - createTime: datetime('create_time').default(sql`CURRENT_TIMESTAMP`), - updateTime: datetime('update_time').default(sql`CURRENT_TIMESTAMP`), -}); - -// 月度薪资计算结果表 -export const hrSalaryCalculation = mysqlTable('hr_salary_calculation', { - id: bigint('id', { mode: 'number', unsigned: true }).autoincrement().primaryKey(), - employeeId: bigint('employee_id', { mode: 'number', unsigned: true }), - calcMonth: varchar('calc_month', { length: 7 }), - baseSalary: decimal('base_salary', { precision: 10, scale: 2 }), - pieceSalary: decimal('piece_salary', { precision: 10, scale: 2 }), - overtimeSalary: decimal('overtime_salary', { precision: 10, scale: 2 }), - performanceSalary: decimal('performance_salary', { precision: 10, scale: 2 }), - allowances: decimal('allowances', { precision: 10, scale: 2 }), - socialInsurancePersonal: decimal('social_insurance_personal', { precision: 10, scale: 2 }), - housingFundPersonal: decimal('housing_fund_personal', { precision: 10, scale: 2 }), - individualTax: decimal('individual_tax', { precision: 10, scale: 2 }), - attendanceDeduction: decimal('attendance_deduction', { precision: 10, scale: 2 }), - grossPay: decimal('gross_pay', { precision: 10, scale: 2 }), - totalDeduction: decimal('total_deduction', { precision: 10, scale: 2 }), - netPay: decimal('net_pay', { precision: 10, scale: 2 }), - status: varchar('status', { length: 20 }).default('draft'), // draft/confirmed/paid - createTime: datetime('create_time').default(sql`CURRENT_TIMESTAMP`), - updateTime: datetime('update_time').default(sql`CURRENT_TIMESTAMP`), -}); - -// 计件产量明细表 -export const hrPieceWorkDetail = mysqlTable('hr_piece_work_detail', { - id: bigint('id', { mode: 'number', unsigned: true }).autoincrement().primaryKey(), - employeeId: bigint('employee_id', { mode: 'number', unsigned: true }), - workDate: date('work_date'), - processCode: varchar('process_code', { length: 50 }), - productCode: varchar('product_code', { length: 50 }), - quantity: int('quantity'), - defectiveQuantity: int('defective_quantity'), - unitPrice: decimal('unit_price', { precision: 10, scale: 4 }), - amount: decimal('amount', { precision: 10, scale: 2 }), - mesSyncId: varchar('mes_sync_id', { length: 50 }), - createTime: datetime('create_time').default(sql`CURRENT_TIMESTAMP`), -}); - -// 考勤异常表 -export const hrAttendanceException = mysqlTable('hr_attendance_exception', { - id: bigint('id', { mode: 'number', unsigned: true }).autoincrement().primaryKey(), - employeeId: bigint('employee_id', { mode: 'number', unsigned: true }), - exceptionDate: date('exception_date'), - exceptionType: varchar('exception_type', { length: 20 }), // late/early_leave/absence/overtime - minutes: int('minutes'), - deductionAmount: decimal('deduction_amount', { precision: 10, scale: 2 }), - status: varchar('status', { length: 20 }).default('pending'), // pending/approved/rejected - createTime: datetime('create_time').default(sql`CURRENT_TIMESTAMP`), - updateTime: datetime('update_time').default(sql`CURRENT_TIMESTAMP`), -}); -``` - -**创建 SQL DDL 文件**(每个表一个文件,放在 `database/sql/`): -- 必须包含索引定义(employee_id, calc_month, work_date 等) -- 必须包含外键约束(employee_id → sys_employee.id) - -### 任务 1.2:基础设施层实现 - -**文件**: -- 创建:`src/domain/hr/infrastructure/EmployeeRepository.ts` -- 创建:`src/domain/hr/infrastructure/SalaryRepository.ts` -- 创建:`src/domain/hr/infrastructure/AttendanceRepository.ts` -- 创建:`src/domain/hr/infrastructure/PieceRateRepository.ts` -- 创建:`src/domain/hr/infrastructure/SalaryCalculationRepository.ts` - -### 任务 1.3:菜单与权限数据 - -**文件**: -- 创建:`database/sql/hr_menus_extended.sql` -- 创建:`database/sql/hr_permissions.sql` - -新增菜单条目:薪资标准、工序单价、计件产量、薪资计算、薪资报表、社保公积金、考勤异常、技能认证。 - ---- - -## Phase 2: 组织架构升级 (1 周) - -### 任务 2.1:六级组织架构表 - -**文件**: -- 创建:`database/sql/hr_organization.sql` - -```sql --- 集团 -CREATE TABLE org_group ( - id BIGINT PRIMARY KEY AUTO_INCREMENT, - code VARCHAR(50), name VARCHAR(100), - sort_order INT, status TINYINT DEFAULT 1 -); --- 法人 -CREATE TABLE org_legal_entity ( - id BIGINT PRIMARY KEY AUTO_INCREMENT, - group_id BIGINT, code VARCHAR(50), name VARCHAR(100) -); --- 工厂 -CREATE TABLE org_factory ( - id BIGINT PRIMARY KEY AUTO_INCREMENT, - legal_entity_id BIGINT, code VARCHAR(50), name VARCHAR(100) -); --- 车间 -CREATE TABLE org_workshop ( - id BIGINT PRIMARY KEY AUTO_INCREMENT, - factory_id BIGINT, code VARCHAR(50), name VARCHAR(100) -); --- 班组 -CREATE TABLE org_team ( - id BIGINT PRIMARY KEY AUTO_INCREMENT, - workshop_id BIGINT, code VARCHAR(50), name VARCHAR(100) -); --- 岗位 -CREATE TABLE org_position ( - id BIGINT PRIMARY KEY AUTO_INCREMENT, - team_id BIGINT, code VARCHAR(50), name VARCHAR(100), - skill_level INT DEFAULT 1 -); -``` - -### 任务 2.2:组织架构 API - -**文件**: -- 创建:`src/app/api/organization/group/route.ts` -- 创建:`src/app/api/organization/legal-entity/route.ts` -- 创建:`src/app/api/organization/factory/route.ts` -- 创建:`src/app/api/organization/workshop/route.ts` -- 创建:`src/app/api/organization/team/route.ts` -- 创建:`src/app/api/organization/position/route.ts` -- 修改:`src/app/api/organization/employee/route.ts` — 关联到新组织架构 - -### 任务 2.3:组织架构前端(树形控件) - -**文件**: -- 创建:`src/components/hr/OrganizationTree.tsx` — 可折叠的六级树 -- 创建:`src/app/[locale]/hr/organization/page.tsx` — 组织架构维护页面 -- 修改:`src/app/[locale]/hr/employee/page.tsx` — 部门选择器改用新组织树 - -### 任务 2.4:i18n 补充 - -**文件**: -- 修改:`messages/*.json` — 添加 Organization 命名空间翻译(group, legalEntity, factory, workshop, team, position) - ---- - -## Phase 3: 员工档案升级 (1 周) - -### 任务 3.1:员工表扩展字段 - -**文件**: -- 创建:`database/sql/hr_employee_ext.sql` — ALTER TABLE 添加字段 -- 修改:`src/app/api/organization/employee/route.ts` — 支持新字段 CRUD - -扩展字段:`skill_level`、`certificate_info`(JSON)、`contract_type`、`contract_start`、`contract_end`、`bank_account`、`emergency_contact`、`emergency_phone`、`blood_type`、`height`、`weight`、`marital_status`、`nationality` - -### 任务 3.2:资质证书与合同管理 - -**文件**: -- 创建:`database/sql/hr_certificate.sql` -- 创建:`database/sql/hr_contract.sql` -- 创建:`src/app/api/hr/certificates/route.ts` -- 创建:`src/app/api/hr/contracts/route.ts` -- 创建:`src/app/[locale]/hr/employee/components/dialogs/CertificateDialog.tsx` -- 创建:`src/app/[locale]/hr/employee/components/dialogs/ContractDialog.tsx` - -### 任务 3.3:员工 Tab 页重构 - -**文件**: -- 修改:`src/app/[locale]/hr/employee/page.tsx` — 增加 Tab 切换(基本信息/资质证书/合同/薪资档案) -- 修改:`src/app/[locale]/hr/employee/components/dialogs/EmployeeFormDialog.tsx` — 增加扩展字段 - ---- - -## Phase 4: 排班与考勤引擎 (2 周) - -### 任务 4.1:班次规则定义 - -**文件**: -- 创建:`database/sql/hr_shift.sql` -- 创建:`src/app/api/hr/shifts/route.ts` -- 创建:`src/app/[locale]/hr/shifts/page.tsx` - -```sql -CREATE TABLE hr_shift ( - id BIGINT PRIMARY KEY AUTO_INCREMENT, - shift_name VARCHAR(50), -- 早班/中班/夜班 - start_time VARCHAR(5), -- 08:00 - end_time VARCHAR(5), -- 17:00 - allow_overtime TINYINT DEFAULT 1, - overtime_rate DECIMAL(3,1) DEFAULT 1.5, - night_allowance DECIMAL(10,2) -- 夜班津贴 -); -``` - -### 任务 4.2:智能排班 - -**文件**: -- 创建:`database/sql/hr_schedule.sql` -- 创建:`src/app/api/hr/schedules/route.ts` -- 创建:`src/app/api/hr/schedules/auto-generate/route.ts` — 按生产计划自动排班 -- 创建:`src/app/[locale]/hr/schedules/page.tsx` -- 创建:`src/components/hr/ScheduleCalendar.tsx` — 日历视图组件 - -### 任务 4.3:考勤异常识别 - -**文件**: -- 创建:`src/app/api/hr/attendance-exceptions/route.ts` -- 创建:`src/app/api/hr/attendance-exceptions/detect/route.ts` — 自动检测异常 -- 修改:`src/app/api/hr/attendance/route.ts` — 打卡时自动检测异常 -- 修改:`src/app/[locale]/hr/attendance/page.tsx` — 异常标记与处理 UI - -### 任务 4.4:MES 工时同步接口 - -**文件**: -- 创建:`src/app/api/hr/mes-sync/work-hours/route.ts` — 接收 MES 推送的工时数据 -- 创建:`src/lib/hr/mes-sync.ts` — MES 同步服务 - -```typescript -// MES 工时同步数据格式 -interface MesWorkHoursSync { - employeeNo: string; - workDate: string; - processCode: string; - startTime: string; - endTime: string; - quantity: number; - defectiveQty: number; - machineId: string; -} -``` - ---- - -## Phase 5: 薪资计算引擎 (3-4 周) ⭐ 核心 - -### 任务 5.1:计件工资模块 - -**文件**: -- 创建:`src/app/api/hr/salary/piece-rate/route.ts` — 工序单价 CRUD -- 创建:`src/app/api/hr/salary/piece-work/route.ts` — 计件产量录入 -- 创建:`src/app/api/hr/salary/calculate-piece/route.ts` — 计件工资计算 -- 创建:`src/app/[locale]/hr/salary/piece-rate/page.tsx` — 单价管理页面 -- 创建:`src/app/[locale]/hr/salary/piece-work/page.tsx` — 产量录入页面 - -**计算逻辑**(`calculate-piece/route.ts`): - -```typescript -async function calculatePieceSalary(employeeId: number, month: string) { - // 1. 查询当月计件产量明细 - const details = await db.select().from(hrPieceWorkDetail) - .where(and( - eq(hrPieceWorkDetail.employeeId, employeeId), - like(hrPieceWorkDetail.workDate, `${month}-%`) - )); - - // 2. 按工序分组计算 - let totalPieceSalary = 0; - for (const detail of details) { - const qualifiedRate = detail.quantity > 0 - ? (detail.quantity - detail.defectiveQuantity) / detail.quantity - : 0; - const pieceAmount = detail.quantity * Number(detail.unitPrice) * qualifiedRate; - totalPieceSalary += pieceAmount; - } - - // 3. 最低工资保障检查 - const minWage = await getLocalMinWage(); - return Math.max(totalPieceSalary, minWage); -} -``` - -### 任务 5.2:加班工资计算 - -**文件**: -- 创建:`src/app/api/hr/salary/calculate-overtime/route.ts` -- 创建:`src/lib/hr/overtime-calculator.ts` - -```typescript -function calculateOvertimeSalary( - baseSalary: number, - attendanceRecords: AttendanceRecord[] -) { - const hourlyRate = baseSalary / 21.75 / 8; - let total = 0; - - for (const record of attendanceRecords) { - switch (record.overtimeType) { - case 'weekday': // 平时 1.5 倍 - total += record.hours * hourlyRate * 1.5; - break; - case 'weekend': // 周末 2 倍 - total += record.hours * hourlyRate * 2; - break; - case 'holiday': // 法定 3 倍 - total += record.hours * hourlyRate * 3; - break; - } - } - - // 加班上限校验 (每月不超过 36 小时) - const totalOvertimeHours = attendanceRecords.reduce((s, r) => s + r.hours, 0); - if (totalOvertimeHours > 36) { - throw new Error('加班时长超过法定上限 (36小时/月)'); - } - - return total; -} -``` - -### 任务 5.3:绩效奖金计算 - -**文件**: -- 创建:`src/app/api/hr/salary/calculate-performance/route.ts` -- 创建:`src/lib/hr/performance-calculator.ts` -- 创建:`src/app/api/hr/performance/route.ts` — KPI 评分 CRUD -- 创建:`src/app/[locale]/hr/performance/page.tsx` — 绩效评分页面 - -```typescript -function calculatePerformanceBonus( - baseAmount: number, - kpiScore: number, - qualityRate: number -) { - // 产量达成率 40%, 质量合格率 30%, 设备稼动率 15%, 5S 15% - const scoreCoefficient = kpiScore / 100; - return baseAmount * scoreCoefficient * qualityRate; -} -``` - -### 任务 5.4:社保公积金计算 - -**文件**: -- 创建:`src/app/api/hr/salary/calculate-insurance/route.ts` -- 创建:`src/lib/hr/insurance-calculator.ts` - -```typescript -function calculateSocialInsurance(base: number) { - return { - personal: { - pension: base * 0.08, // 养老 8% - medical: base * 0.02, // 医疗 2% - unemployment: base * 0.005, // 失业 0.5% - total: base * 0.105, - }, - enterprise: { - pension: base * 0.16, // 养老 16% - medical: base * 0.08, // 医疗 8% - unemployment: base * 0.005, // 失业 0.5% - injury: base * 0.005, // 工伤 0.5% - maternity: base * 0.005, // 生育 0.5% - total: base * 0.255, - }, - }; -} - -function calculateHousingFund(base: number, rate: number) { - return { - personal: base * rate / 100, - enterprise: base * rate / 100, - }; -} -``` - -### 任务 5.5:个税计算(累计预扣法) - -**文件**: -- 创建:`src/lib/hr/tax-calculator.ts` - -```typescript -// 2026 年个税税率表(年度) -const TAX_BRACKETS = [ - { threshold: 0, rate: 0.03, deduction: 0 }, - { threshold: 36000, rate: 0.1, deduction: 2520 }, - { threshold: 144000, rate: 0.2, deduction: 16920 }, - { threshold: 300000, rate: 0.25, deduction: 31920 }, - { threshold: 420000, rate: 0.3, deduction: 52920 }, - { threshold: 660000, rate: 0.35, deduction: 85920 }, - { threshold: 960000, rate: 0.45, deduction: 181920 }, -]; - -function calculateCumulativeTax( - cumulativeIncome: number, // 累计收入 - cumulativeDeduction: number, // 累计专项附加扣除 - cumulativeBase: number, // 累计减除费用 (5000 × 月数) - monthCount: number // 当前月份 -) { - const taxableIncome = cumulativeIncome - cumulativeBase - cumulativeDeduction; - if (taxableIncome <= 0) return 0; - - let tax = 0; - for (let i = TAX_BRACKETS.length - 1; i >= 0; i--) { - if (taxableIncome > TAX_BRACKETS[i].threshold) { - tax = taxableIncome * TAX_BRACKETS[i].rate - TAX_BRACKETS[i].deduction; - break; - } - } - - return Math.max(0, tax); -} -``` - -### 任务 5.6:薪资计算主引擎 - -**文件**: -- 创建:`src/app/api/hr/salary/calculate/route.ts` — 总计算入口 -- 创建:`src/lib/hr/salary-engine.ts` — 计算引擎协调器 - -```typescript -// 薪资计算引擎主入口 -async function calculateMonthlySalary( - employeeId: number, - month: string, - options: { - includeOvertime?: boolean; - includePerformance?: boolean; - includeInsurance?: boolean; - includeTax?: boolean; - } = {} -) { - // 1. 获取员工薪资档案 - const profile = await getSalaryProfile(employeeId); - - // 2. 并行计算各子项 - const [pieceSalary, overtimeSalary, performanceBonus, allowances] = await Promise.all([ - options.includeOvertime !== false ? calculatePieceSalary(employeeId, month) : 0, - options.includeOvertime !== false ? calculateOvertimeSalary(profile.baseSalary, month) : 0, - options.includePerformance !== false ? calculatePerformanceBonus(employeeId, month) : 0, - calculateAllowances(employeeId, month, profile), - ]); - - // 3. 应发合计 - const grossPay = Number(profile.baseSalary) - + Number(pieceSalary) - + Number(overtimeSalary) - + Number(performanceBonus) - + Number(allowances); - - // 4. 计算应扣款项 - const [insurance, fund, tax, attendanceDeduction] = await Promise.all([ - options.includeInsurance !== false - ? calculateSocialInsurance(profile.socialInsuranceBase) - : { personal: { total: 0 } }, - options.includeInsurance !== false - ? calculateHousingFund(profile.socialInsuranceBase, profile.housingFundRate) - : { personal: 0 }, - options.includeTax !== false && grossPay > 5000 - ? calculateCumulativeTax(employeeId, month, grossPay) - : 0, - calculateAttendanceDeduction(employeeId, month), - ]); - - const totalDeduction = insurance.personal.total - + Number(fund.personal) - + Number(tax) - + Number(attendanceDeduction); - - // 5. 实发工资 - const netPay = grossPay - totalDeduction; - - // 6. 保存计算结果 - return saveCalculation(employeeId, month, { - grossPay, totalDeduction, netPay, - pieceSalary, overtimeSalary, performanceBonus, allowances, - socialInsurance: insurance.personal.total, - housingFund: fund.personal, - individualTax: tax, - attendanceDeduction, - }); -} -``` - -### 任务 5.7:薪资计算前端 - -**文件**: -- 创建:`src/app/[locale]/hr/salary/calculate/page.tsx` — 计算页面 -- 创建:`src/components/hr/SalaryCalculationWizard.tsx` — 分步计算向导 -- 创建:`src/components/hr/SalaryDetailTable.tsx` — 详细工资表明细 -- 创建:`src/components/hr/SalaryBatchActions.tsx` — 批量确认/发放 -- 修改:`src/app/[locale]/hr/salary/page.tsx` — 集成新计算引擎 - -### 任务 5.8:银行报盘与工资条 - -**文件**: -- 创建:`src/app/api/hr/salary/bank-report/route.ts` — 生成银行代发文件 -- 创建:`src/app/api/hr/salary/payslips/route.ts` — 电子工资条 -- 创建:`src/app/[locale]/hr/salary/bank-report/page.tsx` -- 创建:`src/app/[locale]/hr/salary/payslips/page.tsx` -- 创建:`src/components/hr/PayslipCard.tsx` — 工资条展示组件 - ---- - -## Phase 6: 培训与技能认证 (1 周) - -### 任务 6.1:技能矩阵 - -**文件**: -- 创建:`database/sql/hr_skill_matrix.sql` -- 创建:`src/app/api/hr/skills/route.ts` -- 创建:`src/app/[locale]/hr/skills/page.tsx` -- 创建:`src/components/hr/SkillMatrixGrid.tsx` — 四维矩阵(岗位×技能×等级×认证) - -### 任务 6.2:证书有效期预警 - -**文件**: -- 创建:`src/app/api/hr/certificates/expiring/route.ts` — 即将到期证书列表 -- 修改:`src/app/[locale]/hr/employee/page.tsx` — 增加证书过期预警标记 - -### 任务 6.3:培训效果评估 - -**文件**: -- 修改:`src/app/api/hr/training/route.ts` — 增加效果评估字段 -- 修改:`src/app/[locale]/hr/training/page.tsx` — 增加评分/合格率统计 -- 修改:`src/app/[locale]/hr/employee/components/dialogs/EmployeeFormDialog.tsx` — 技能关联 - ---- - -## Phase 7: HR 分析报表 (1 周) - -### 任务 7.1:人力成本分析 - -**文件**: -- 创建:`src/app/api/hr/reports/labor-cost/route.ts` -- 创建:`src/app/[locale]/hr/reports/labor-cost/page.tsx` -- 创建:`src/components/hr/charts/LaborCostChart.tsx` — Recharts 堆叠柱状图 - -```typescript -// 人力成本分析维度 -interface LaborCostReport { - totalCost: number; - byDepartment: { deptName: string; cost: number; headcount: number }[]; - byType: { type: string; amount: number; percentage: number }[]; - trend: { month: string; cost: number; headcount: number }[]; - kpi: { - avgCostPerHead: number; - costRatio: number; // 人力成本占总成本比例 - productivity: number; // 人均产值 - }; -} -``` - -### 任务 7.2:薪资结构分析 - -**文件**: -- 创建:`src/app/api/hr/reports/salary-structure/route.ts` -- 创建:`src/app/[locale]/hr/reports/salary-structure/page.tsx` -- 创建:`src/components/hr/charts/SalaryStructureChart.tsx` — 饼图/环形图 - -### 任务 7.3:离职率与人员流动 - -**文件**: -- 创建:`src/app/api/hr/reports/turnover/route.ts` -- 创建:`src/app/[locale]/hr/reports/turnover/page.tsx` - ---- - -## Phase 8: 集成与部署 (1 周) - -### 任务 8.1:MES 产量数据对接 - -**文件**: -- 创建:`src/app/api/hr/mes-sync/piece-work/route.ts` — MES 推送计件产量 -- 创建:`src/lib/hr/piece-work-sync.ts` - -### 任务 8.2:质量系统对接 - -**文件**: -- 创建:`src/app/api/hr/quality-sync/defect-rate/route.ts` — 获取员工次品率 -- 修改:`src/lib/hr/performance-calculator.ts` — 质量系数挂钩 - -### 任务 8.3:财务系统对接 - -**文件**: -- 创建:`src/app/api/hr/finance-sync/salary-transfer/route.ts` — 工资发放凭证 -- 创建:`src/app/api/hr/finance-sync/insurance/route.ts` — 社保缴纳凭证 - -### 任务 8.4:数据库迁移与 CI - -**文件**: -- 修改:`package.json` — 添加 `db:push:hr` 脚本 -- 创建:`database/sql/hr_all_tables.sql` — 全量 DDL 汇总 -- 创建:`database/sql/hr_all_seed.sql` — 种子数据(示例员工、岗位、单价) - -### 任务 8.5:菜单注册 - -**文件**: -- 创建:`database/sql/hr_menu_registration.sql` - -```sql --- HR 模块完整菜单结构 -INSERT INTO sys_menu (name, permission, path, parent_id, sort_order) VALUES -('HR管理', 'hr:manage', '/hr', NULL, 40), -('├ 员工管理', 'hr:employee', '/hr/employee', LAST_INSERT_ID(), 1), -('├ 组织架构', 'hr:organization', '/hr/organization', @hr_id, 2), -('├ 班次管理', 'hr:shift', '/hr/shifts', @hr_id, 3), -('├ 排班管理', 'hr:schedule', '/hr/schedules', @hr_id, 4), -('├ 考勤管理', 'hr:attendance', '/hr/attendance', @hr_id, 5), -('├ 工序单价', 'hr:piece-rate', '/hr/salary/piece-rate', @hr_id, 6), -('├ 计件产量', 'hr:piece-work', '/hr/salary/piece-work', @hr_id, 7), -('├ 薪资计算', 'hr:salary-calc', '/hr/salary/calculate', @hr_id, 8), -('├ 薪资管理', 'hr:salary', '/hr/salary', @hr_id, 9), -('├ 银行报盘', 'hr:bank-report', '/hr/salary/bank-report', @hr_id, 10), -('├ 工资条', 'hr:payslip', '/hr/salary/payslips', @hr_id, 11), -('├ 绩效评分', 'hr:performance', '/hr/performance', @hr_id, 12), -('├ 培训管理', 'hr:training', '/hr/training', @hr_id, 13), -('├ 技能认证', 'hr:skill', '/hr/skills', @hr_id, 14), -('├ 人力报表', 'hr:report', '/hr/reports', @hr_id, 15); -``` - ---- - -## 实施顺序建议 - -``` -Phase 1 (DB+ORM) ──→ Phase 2 (组织架构) ──→ Phase 3 (员工档案) - │ - ↓ -Phase 4 (排班考勤) ──→ Phase 5 (薪资引擎) ←── 依赖 Phase 3 薪资档案 - │ - ┌────────────────────────┼────────────────────────┐ - ↓ ↓ ↓ - Phase 6 (培训认证) Phase 7 (分析报表) Phase 8 (集成部署) -``` - -**关键依赖路径**: -- Phase 5 薪资引擎需要 Phase 1 (DB) + Phase 3 (员工薪资档案) + Phase 4 (考勤/排班) -- Phase 7 报表依赖 Phase 5 (薪资数据) + Phase 6 (培训数据) -- Phase 8 集成本质上是"胶水代码",可在各阶段并行 - ---- - -## 文件变更总清单 - -| 类型 | 数量 | 说明 | -|------|------|------| -| 创建 Drizzle Schema | 12 表 | 6 张新表 + 6 张现有表迁移 | -| 创建 API 路由 | ~25 个 | 涵盖所有新功能端点 | -| 创建前端页面 | ~20 个 | 新功能页面 + 组件 | -| 修改前端页面 | ~8 个 | 现有页面集成升级 | -| 创建工具库 | ~8 个 | 计算/同步/校验 | -| 创建 SQL 脚本 | ~15 个 | DDL + 种子 + 菜单 | -| 修改 i18n | 4 文件 | 新增命名空间翻译 | -| **总计** | **~80 个文件** | | diff --git a/docs/superpowers/plans/2026-08-12-purchase-inbound-linkage.md b/docs/superpowers/plans/2026-08-12-purchase-inbound-linkage.md deleted file mode 100644 index 53026f69..00000000 --- a/docs/superpowers/plans/2026-08-12-purchase-inbound-linkage.md +++ /dev/null @@ -1,394 +0,0 @@ -# 采购入库单与采购订单联动改造 — 实施计划 - -> **目标**:实现采购入库强制关联有效采购订单、信息自动带入、数量强校验(含超收容差)、订单状态双向回写(入库审核累加 / 作废·反审核回补),打通「采购 → 入库 → 应付」核心链路。 -> -> **约束铁律**:先计划后落地(本文档);最小改动(仅动采购入库链路,不碰生产入库/其他入库);向下兼容(历史无来源入库单可正常查询编辑,仅新增单据生效);类型隔离(仅「采购入库」强制关联订单);每步必验(`pnpm lint` / `pnpm ts-check`,核心逻辑补单元测试)。 - -**架构**:DDD 分层(domain / application / infrastructure / api)+ Drizzle ORM + 事件驱动(DomainEventOutbox + EventBus)。 - -**技术栈**:Next.js App Router + TypeScript + Drizzle ORM (mysql-core) + MySQL + Zod + pnpm + Vitest。 - ---- - -## 0. 现状基线(改造起点) - -| 已有基础 | 缺口 | -|---|---| -| 入库单已含 `poId/poNo/supplierId/orderType` 字段 | 缺标准化 `source_type/source_order_id`(入库单)、`purchase_order_item_id`(明细行) | -| `PurchaseOrder.receive()` 已含容差校验 + `partially_received` 状态机 | 缺 `reverseReceive()`(作废/反审核回补)与 `PurchaseOrderReceiveReversedEvent` | -| `InboundApplicationService.createInboundFromPO()` 已存在 | 数量校验未含容差;未写入 source 字段与行级 PO 行 id | -| `PurchaseInboundSyncHandler`(inbound.approved → SQL 直写累加)已存在 | 未走领域 `receive()`;`inbound.unapproved/cancelled` 无 PO 回补 | -| `over_receipt_tolerance` 订单级字段已存在 | 创建订单时 `body.over_receipt_tolerance \|\| 0` 导致默认 0;系统级默认配置缺失 | -| 前端 AddDialog 已有 PO 搜索下拉、选单填充 | 缺入库类型第一步、物料/单价/规格只读、实时容差校验 | -| PO 详情页已有「入库记录」Tab | 过滤逻辑有缺陷(`order_type==='purchase'` 会把所有采购入库列出),且无 PO 详情/入库记录 API | - ---- - -## 1. 影响文件清单(全量) - -### 修改(16 个) -| 文件 | 改动 | -|---|---| -| `src/lib/db/schemas/warehouse.ts` | `invInboundOrders` + `source_type/source_order_id`;`invInboundItems` + `purchase_order_item_id/purchase_order_line_no` | -| `src/lib/db/schemas/procurement.ts` | `purPurchaseOrder` + `received_quantity` 汇总字段 | -| `src/domain/warehouse/aggregates/InboundOrder.ts` | Props + `sourceType/sourceOrderId`;create 校验;cancel/unapprove 事件 payload 补 poId+items | -| `src/domain/warehouse/entities/InboundItem.ts` | + `purchaseOrderItemId/purchaseOrderLineNo` | -| `src/domain/warehouse/events/InboundOrderEvents.ts` | `InboundOrderUnapprovedEvent/InboundOrderCancelledEvent` payload + `poId/items` | -| `src/domain/purchase/aggregates/PurchaseOrder.ts` | + `reverseReceive()`、`canReverseReceive()`;修复 reconstitute 容差 `\|\| 0` → `?? 0` | -| `src/domain/purchase/entities/PurchaseOrderLine.ts` | + `reverseReceive(quantity)` 反向扣减 | -| `src/domain/purchase/value-objects/PurchaseOrderStatus.ts` | transitions/operations + 回退(completed/partially_received → approved / partially_received)+ `canReverseReceive()` | -| `src/domain/purchase/events/PurchaseOrderEvents.ts` | + `PurchaseOrderReceiveReversedEvent` | -| `src/application/services/InboundApplicationService.ts` | `createInboundFromPO` 含容差强校验 + source 字段透传 | -| `src/application/handlers/PurchaseInboundSyncHandler.ts` | 重构为「加载聚合根 → receive() → 事务内持久化 + outbox」+ 维护 `received_quantity` | -| `src/application/handlers/PurchaseInboundReversalHandler.ts` | **新增**:inbound.unapproved / inbound.cancelled → `reverseReceive()` 回补 | -| `src/application/EventRegistry.ts` | 注册 ReversalHandler;`purchase.receive_reversed` 注册 AuditLog | -| `src/app/api/purchase/orders/route.ts` | GET 支持 `status` 逗号分隔(待入库列表);POST 容差默认值读系统配置 | -| `src/app/api/warehouse/inbound/route.ts` | POST schema + `source_type/source_order_id`;GET 支持 `poId` 参数 + 序列化 source 字段 | -| `src/app/api/warehouse/inbound/from-po/route.ts` | 透传容差校验信息、返回 source 信息 | - -### 新增(6 个) -| 文件 | 用途 | -|---|---| -| `database/migrations/20260812090000_purchase_inbound_linkage.ts` | 加字段 + 历史数据回填 + sys_config 默认值 | -| `src/app/api/purchase/orders/[id]/route.ts` | 采购订单详情(含行可入库上限) | -| `src/app/api/purchase/orders/[id]/inbound-records/route.ts` | 按 po_id 精确查入库记录 | -| `src/application/handlers/PurchaseInboundReversalHandler.ts` | 作废/反审核回补处理器 | -| `tests/unit/purchase/purchase-order-receive-reverse.test.ts` | 领域层回补单测 | -| `tests/unit/warehouse/inbound-from-po.test.ts` | 应用层容差校验单测 | - -### 前端(4 个) -| 文件 | 改动 | -|---|---| -| `src/app/[locale]/warehouse/inbound/types.ts` | + `inboundType`;PO 类型补 `status_label/over_receipt_tolerance/total_received_qty`;行补 `max_receivable_qty` | -| `src/app/[locale]/warehouse/inbound/hooks/usePurchaseOrderSearch.ts` | 只查可入库单(status 30,40)+ 返回容差上限 | -| `src/app/[locale]/warehouse/inbound/components/dialogs/AddDialog.tsx` | 入库类型第一步;采购入库只读字段 + 实时容差校验标红 | -| `src/app/[locale]/purchase/orders/[id]/page.tsx` | 入库记录 Tab 改按 po_id 精确匹配(走新 API) | - ---- - -## 2. 风险点与控制措施 - -| 风险 | 等级 | 控制措施 | -|---|---|---| -| 入库审核事件处理器改为领域 `receive()` 后,极端并发超收会抛 DomainError 导致 outbox 重试阻塞 | 中 | 创建时应用层强校验(含容差)+ `FOR UPDATE` 行锁;receive() 抛错即事务回滚并告警,人工介入;不影响库存(InventorySync 独立事务) | -| 作废/反审核回补重复执行(事件重放) | 高 | `IdempotentHandler` 幂等 + 回补前校验 PO 状态 ∈ {40,50} 且行已收量 > 0,否则跳过 | -| 历史无来源入库单(po_id 为空)在事件链中误处理 | 中 | 事件 payload 增加 poId 后,handler 判空跳过;回补 SQL 仅处理 po_id 非空单 | -| `over_receipt_tolerance \|\| 0` 使默认容差恒为 0 | 中 | API 改 `?? `(null/undefined 才用系统默认 5,显式 0 表示不允许超收) | -| 状态机回退(completed→approved)破坏既有流转约束 | 中 | 仅在 `reverseReceive` 专用转换中加入,`canReverseReceive` 仅 completed/partially_received 为 true;补单测锁定 | -| 列表查询性能(received_quantity 冗余) | 低 | 主表字段由 handler 事务内维护,领域 getter 仍以 lines 求和为准(单一事实来源) | -| 其他入库类型(生产入库/其他入库)被误伤 | 中 | 全部校验以 `orderType==='purchase'` 为前提;普通创建路径不动 | - ---- - -## 子任务 1:数据库结构改造与迁移脚本 - -### 1.1 修改 `src/lib/db/schemas/warehouse.ts` - -`invInboundOrders` 追加(放在 `poNo` 之后): - -```typescript -sourceType: varchar('source_type', { length: 20 }), // 'purchase_order' | NULL -sourceOrderId: bigint('source_order_id', { mode: 'number', unsigned: true }), -``` - -索引块追加:`sourceIdx: index('idx_source_order').on(table.sourceType, table.sourceOrderId)` - -`invInboundItems` 追加: - -```typescript -purchaseOrderItemId: bigint('purchase_order_item_id', { mode: 'number', unsigned: true }), -purchaseOrderLineNo: int('purchase_order_line_no', { unsigned: true }), -``` - -### 1.2 修改 `src/lib/db/schemas/procurement.ts` - -`purPurchaseOrder` 追加(放在 `overReceiptTolerance` 之后): - -```typescript -receivedQuantity: decimal('received_quantity', { precision: 18, scale: 4 }).default('0.0000'), -``` - -### 1.3 新增迁移 `database/migrations/20260812090000_purchase_inbound_linkage.ts` - -导出 `up(conn)` / `down(conn)`,内容: -1. `ALTER TABLE inv_inbound_order ADD COLUMN source_type ... , ADD COLUMN source_order_id ... , ADD INDEX idx_source_order(source_type, source_order_id)` -2. `ALTER TABLE inv_inbound_item ADD COLUMN purchase_order_item_id ..., ADD COLUMN purchase_order_line_no ...` -3. `ALTER TABLE pur_purchase_order ADD COLUMN received_quantity DECIMAL(18,4) NOT NULL DEFAULT 0.0000` -4. 历史数据回填: - - `UPDATE inv_inbound_order SET source_type='purchase_order', source_order_id=po_id WHERE po_id IS NOT NULL` - - `UPDATE pur_purchase_order o SET received_quantity = (SELECT IFNULL(SUM(l.received_qty),0) FROM pur_purchase_order_line l WHERE l.po_id = o.id)` -5. 系统配置默认值:`INSERT INTO sys_config(config_key, config_value, description) VALUES ('purchase.over_receipt_tolerance','5','采购入库超收比例(%),范围0~20') ON DUPLICATE KEY UPDATE description=VALUES(description)` - (先确认 `sys_config` 表真实列名,以 `src/lib/system-config.ts` 读取逻辑为准) -6. `down(conn)` 按相反顺序 DROP COLUMN / DELETE 配置(保守起见 down 仅 DROP 新增列)。 - -**验证**:`pnpm migrate`(`npx tsx scripts/migrate.ts up`)后 `pnpm migrate` status 显示该迁移已应用;用 SQL 确认列存在与回填数据正确。 - ---- - -## 子任务 2:领域层核心逻辑改造 - -### 2.1 `InboundItem.ts`(实体) - -`InboundItemProps` + `purchaseOrderItemId?: number; purchaseOrderLineNo?: number`;构造函数与 create/reconstitute 透传两个 getter:`purchaseOrderItemId`、`purchaseOrderLineNo`。 - -### 2.2 `InboundOrder.ts`(聚合根) - -- `InboundOrderProps` + `sourceType?: string; sourceOrderId?: number`;构造函数新增只读属性 `sourceType`、`sourceOrderId`。 -- `create()` 来源合法性校验(新增,仅采购入库生效): - ```typescript - const sourceType = props.sourceType || (props.poId ? 'purchase_order' : undefined); - if (sourceType === 'purchase_order') { - if (!props.poId) throw new DomainError('采购入库必须关联采购订单'); - if (!props.poNo) throw new DomainError('采购入库缺少采购订单号'); - if (!props.supplierId) throw new DomainError('采购入库缺少供应商'); - if (props.orderType !== 'purchase') { /* 允许:orderType 统一由上层传 purchase */ } - } - ``` -- `cancel()` / `unapprove()`:事件 payload 补 `poId: this.poId, items: this._items.map(i => ({ materialId, materialCode, materialName, quantity, unitPrice, batchNo }))`(供回补 handler 精确扣减,不再依赖回查库)。 - -### 2.3 `InboundOrderEvents.ts` - -`InboundOrderUnapprovedEvent` / `InboundOrderCancelledEvent` payload 增加: -```typescript -poId?: number; -items?: Array<{ materialId: number; materialCode: string; materialName: string; quantity: number; unitPrice: number; batchNo: string }>; -``` - -### 2.4 `PurchaseOrderLine.ts` - -新增反向方法(与 `receive` 对称): -```typescript -reverseReceive(quantity: number): void { - if (quantity <= 0) throw new DomainError('回补数量必须大于0'); - if (quantity > this._receivedQty) { - throw new DomainError(`回补数量${quantity}超过已收数量${this._receivedQty}`); - } - this._receivedQty = Math.round((this._receivedQty - quantity) * 10000) / 10000; -} -``` - -### 2.5 `PurchaseOrderStatus.ts` - -- `transitions` 增加回退:`completed: ['partially_received', 'approved', 'closed']`、`partially_received: ['approved', 'completed', 'closed', 'voided']` -- `operations` 增加:`completed: ['view', 'close', 'reverse_receive']`、`partially_received: ['receive', 'close', 'void', 'reverse_receive']` -- 新增 `canReverseReceive(): boolean`(operations 含 'reverse_receive') - -### 2.6 `PurchaseOrder.ts`(聚合根) - -- 新增方法 `reverseReceive(lineReceives: Array<{ lineNo: number; quantity: number }>): void`: - ```typescript - if (!this._status.canReverseReceive()) { - throw new DomainError(`当前状态"${this._status.label()}"不允许回补`); - } - for (const recv of lineReceives) { - const line = this._lines.find((l) => l.lineNo === recv.lineNo); - if (!line) throw new DomainError(`行号${recv.lineNo}不存在`); - line.reverseReceive(recv.quantity); - } - // 重算状态 - const allZero = this._lines.every((l) => l.receivedQty <= 0); - if (allZero) this._status = this._status.transitionTo('approved'); - else if (this.isFullyReceived) this._status = this._status.transitionTo('completed'); - else this._status = this._status.transitionTo('partially_received'); - this._domainEvents.push(new PurchaseOrderReceiveReversedEvent({ ... })); - ``` -- 新增 `canReverseReceive(): boolean` 透传。 -- 修复 `reconstitute()` 中 `props.overReceiptTolerance || 0` → `props.overReceiptTolerance ?? 0`(避免 NaN,保持持久化值语义)。 - -### 2.7 `PurchaseOrderEvents.ts` - -新增 `PurchaseOrderReceiveReversedEvent`(`eventType = 'purchase.receive_reversed'`),payload:`orderId/orderNo/supplierId/supplierName/reversedItems{lineNo,materialId,materialName,quantity,unitPrice}/totalReversedAmount`。 - -**验证**:`pnpm ts-check` + 后续单测(子任务 7)。 - ---- - -## 子任务 3:应用服务与事件处理器 - -### 3.1 `InboundApplicationService.createInboundFromPO()` 增强 - -1. 容差强校验(替代现有 `item.quantity > line.remainingQty`): - ```typescript - const tolerance = purchaseOrder.overReceiptTolerance; // DB 默认 5,0 表示不允许超收 - const maxAllowed = line.orderQty * (1 + tolerance / 100); - const newReceived = line.receivedQty + item.quantity; - if (newReceived > maxAllowed) { - throw new DomainError(`行${item.lineNo}入库将超限:订购${line.orderQty}、已收${line.receivedQty}、本次${item.quantity}、容差${tolerance}%、上限${maxAllowed}`); - } - ``` - (同一入库单内同物料多行也需累加:按 lineNo 聚合 items 后再逐行校验) -2. `props` 增加 `sourceType: 'purchase_order'`、`sourceOrderId: purchaseOrder.id`; -3. `items` 映射增加 `purchaseOrderItemId: line.id`、`purchaseOrderLineNo: line.lineNo`。 - -### 3.2 `PurchaseInboundSyncHandler` 重构(审核回写走领域) - -`handle()` 逻辑改为: -1. `poId` 为空 → 跳过(兼容非采购入库); -2. 事务内: - - `SELECT * FROM pur_purchase_order WHERE id=? AND deleted=0 AND status IN (30,40) FOR UPDATE`(锁单,防并发); - - 加载 `PurchaseOrder` 聚合根(`MysqlPurchaseOrderRepository.findById` 语义,但需同连接锁行 —— 用 SQL 锁行后自行 `reconstitute`); - - 由 event.items 构造 `lineReceives`(按 materialId 匹配 lineNo;优先用 item 行的 `purchaseOrderLineNo`); - - 调用 `order.receive(lineReceives)`(领域容差校验 + 状态机 + 事件); - - SQL 更新各行 `received_qty`、订单 `status`(`order.status.toDbCode()`)、`received_quantity = SUM(lines)`; - - `getDomainEventOutbox().saveEvents(conn, 'PurchaseOrder', poId, order.getDomainEvents())`(保存 `PurchaseOrderReceivedEvent`)。 -3. `receive()` 抛 `DomainError`(并发超收)→ 整个事务回滚 + `logger.error` 告警 + 抛错进 outbox 重试(人工介入)。 - -> 备选(若验证时发现重试阻塞风险不可控):回退为原 SQL 直写 + 仅补 `received_quantity` 汇总,领域 `receive()` 仍保留供 from-po 创建后自检使用。**实现顺序:先做聚合根版,单测 + 集成验证通过则保留;不通过则切换备选并记录。** - -### 3.3 新增 `PurchaseInboundReversalHandler`(作废/反审核回补) - -- 监听 `inbound.unapproved`、`inbound.cancelled`; -- `handle(event)`: - 1. `event.payload.poId` 为空 → 跳过; - 2. 事务内: - - `SELECT ... FOR UPDATE` 锁 PO(`status IN (40,50)` 才处理,否则跳过并 info 日志); - - 加载聚合根,构造 `lineReceives`(用 payload.items 按 materialId 匹配行;若 payload 无 items 则查 `inv_inbound_item` 按 `purchase_order_item_id` 精确匹配); - - 调用 `order.reverseReceive(lineReceives)`; - - SQL 更新行 `received_qty`、订单 `status`(回退到 approved/partially_received/completed)、`received_quantity`; - - outbox 保存 `PurchaseOrderReceiveReversedEvent`; - 3. 全程包 `IdempotentHandler`(事件重放安全)。 - -### 3.4 `EventRegistry.ts` - -```typescript -eventBus.subscribe('inbound.unapproved', new IdempotentHandler(new PurchaseInboundReversalHandler())); -eventBus.subscribe('inbound.cancelled', new IdempotentHandler(new PurchaseInboundReversalHandler())); -eventBus.subscribe('purchase.receive_reversed', new AuditLogHandler()); -``` - -**验证**:`pnpm lint`、`pnpm ts-check`。 - ---- - -## 子任务 4:API 接口改造 - -### 4.1 `src/app/api/purchase/orders/route.ts`(GET 增强) - -- `status` 支持逗号分隔(如 `status=30,40`)→ 传 `statusList` 给 service/仓储 → SQL `WHERE status IN (...)`。**待入库列表即 `?status=30,40&pageSize=20`**(approved + partially_received)。 -- 序列化行数据补:`max_receivable_qty = round(orderQty*(1+tol/100) - receivedQty, 4)`、`over_receipt_tolerance`(订单级)。 - -### 4.2 `src/app/api/purchase/orders/[id]/route.ts`(新增) - -- `GET`:按 id 查聚合根,返回订单头 + lines(含 `remaining_qty`、`max_receivable_qty`、`received_qty`);复用 GET 列表的序列化结构。 -- `PUT`:如需操作(如重新审核)可后续补充,本次仅 GET。 - -### 4.3 `src/app/api/purchase/orders/[id]/inbound-records/route.ts`(新增) - -- `GET`:`SELECT ... FROM inv_inbound_order WHERE po_id = ? AND deleted = 0 ORDER BY create_time DESC`,序列化 `id/order_no/inbound_date/status/supplier_name/total_quantity/total_amount`。 - -### 4.4 `src/app/api/warehouse/inbound/route.ts` - -- `createInboundOrderSchema`(`src/lib/validations/inbound.ts`)补 `source_type`(枚举 `['purchase_order']` 可选)、`source_order_id`(number 可选);校验:`source_type === 'purchase_order'` 时 `source_order_id` 必填。 -- POST 透传 `sourceType/sourceOrderId` 进 `InboundOrderProps`;普通创建(非 from-po)若 `source_type` 缺失且 `order_type==='purchase'` 则强制要求关联(由 `InboundApplicationService.createOrder` 或 route 校验决定——**默认:普通 POST 走原有逻辑,强制关联仅由 from-po 路径 + 前端类型选择保证**,满足"最小改动 + 类型隔离")。 -- GET 支持 `poId` 参数(`o.po_id = ?` 精确匹配,供 PO 详情页入库记录使用);列表序列化补 `source_type/source_order_id`。 - -### 4.5 `src/app/api/warehouse/inbound/from-po/route.ts` - -- 透传 `over_receipt_tolerance` 至响应(或由前端从所选 PO 行数据获得,本步仅确保校验一致);返回体补 `source_type/source_order_id`。 - -**验证**:`pnpm lint`、`pnpm ts-check`;curl 验证:`GET /api/purchase/orders?status=30,40`、`GET /api/purchase/orders/1`、`GET /api/purchase/orders/1/inbound-records`、`GET /api/warehouse/inbound?poId=1`。 - ---- - -## 子任务 5:前端页面交互改造 - -### 5.1 `types.ts` - -- `InboundFormData` + `inboundType: 'purchase' | 'other'`(默认 `'purchase'`);+ `sourceType/sourceOrderId`;`INITIAL_FORM_DATA` 同步。 -- `PurchaseOrder` 类型补:`status_label/over_receipt_tolerance/total_received_qty/po_no`;行类型补 `max_receivable_qty`。 - -### 5.2 `usePurchaseOrderSearch.ts` - -- `searchPurchaseOrders(keyword)` 改调 `GET /api/purchase/orders?status=30,40&keyword=...&pageSize=20`(只返回可入库单)。 -- 选行填充补:`maxReceivableQty = max_receivable_qty`、`tolerance = over_receipt_tolerance`;填充 `sourceType='purchase_order'`、`sourceOrderId=po.id`。 - -### 5.3 `AddDialog.tsx` - -- **第一步**:入库类型 radio(采购入库 / 其他入库);「采购入库」强制显示订单选择器且 `sourceType` 必填;「其他入库」隐藏订单选择器、字段可编辑(完全保留现逻辑)。 -- **选单后**:供应商、物料编码/名称、规格、单价、单位 → `disabled` 只读;数量预填 `remaining_qty`;显示「可入库上限 = max_receivable_qty」。 -- **实时校验**:`quantity > maxReceivableQty` 时输入框红框 + 错误提示 + 阻止提交;提交时再次校验(双保险)。 -- **提交**:`inboundType==='purchase'` 且 `poId && lineNo` → 走 `/api/warehouse/inbound/from-po`(body 已含 source 信息);否则走原 `/api/warehouse/inbound`。 -- 详情/列表行:采购入库单展示来源订单号,点击跳转 `/purchase/orders/[poId]`。 - -### 5.4 `purchase/orders/[id]/page.tsx` - -- 「入库记录」Tab 改调 `GET /api/purchase/orders/[id]/inbound-records`(精确按 po_id),删除原 `r.po_no === order.po_no || r.order_type === 'purchase'` 缺陷过滤。 - -**验证**:`pnpm lint`、`pnpm ts-check`;浏览器手工验证采购入库流程(选单→带出→超量标红→提交)与 PO 详情入库记录。 - ---- - -## 子任务 6:系统配置与管控参数 - -- 新增配置项:`purchase.over_receipt_tolerance`(默认 `5`,取值 0~20;0 表示不允许超收)。迁移脚本已 INSERT 默认值。 -- `src/app/api/purchase/orders/route.ts` POST 改: - ```typescript - overReceiptTolerance: - body.over_receipt_tolerance === undefined || body.over_receipt_tolerance === null - ? await getSystemConfigNumber('purchase.over_receipt_tolerance', 5) - : Number(body.over_receipt_tolerance), - ``` - (领域层不再硬编码 5;显式 0 保留) -- 入库侧容差一律取订单自身 `over_receipt_tolerance`(DB 默认值由创建时系统配置注入),领域/应用层不读库硬编码。 -- `getSystemConfigNumber` 已有 60s 缓存,无需新增缓存机制。 - -**验证**:`pnpm lint`、`pnpm ts-check`;单测覆盖默认值注入。 - ---- - -## 子任务 7:单元测试与全流程回归 - -### 7.1 `tests/unit/purchase/purchase-order-receive-reverse.test.ts` - -- 正常回补:partially_received 单 reverseReceive 后状态回退 approved(全行归零); -- 分批回补:部分回补后 completed → partially_received; -- 超量回补拦截:reverseReceive 数量 > 已收 → 抛 DomainError; -- 非法状态拦截:draft/approved(未收过)调用 reverseReceive → 抛错; -- 事件断言:receive 后 reverseReceive 产生 `PurchaseOrderReceiveReversedEvent`。 - -### 7.2 `tests/unit/warehouse/inbound-from-po.test.ts` - -- 容差内通过:`quantity <= maxAllowed - receivedQty` 成功; -- 超容差拦截:`quantity > maxAllowed - receivedQty` 抛 DomainError(mock 仓储); -- source 透传:`sourceType/sourceOrderId/purchaseOrderItemId` 写入 props; -- 非法订单状态拦截:draft/completed 单拒绝创建。 - -### 7.3 集成/回归 - -- 事件链冒烟:inbound.unapproved → ReversalHandler → PO 行回退 + 状态回退(mock EventBus/Outbox 断言调用序列); -- 全量回归:`pnpm lint`、`pnpm ts-check`、`pnpm test:unit:run`; -- 手工回归:历史无来源入库单查询/编辑不受影响;其他入库类型创建流程不变;生产入库/完工入库不受影响。 - -**验收标准映射**: -- 管控生效:采购入库从 from-po 路径强制关联(`source_order_id` 非空); -- 数据一致:审核后 PO `received_qty/status/received_quantity` 正确;作废/反审核回补后一致; -- 超量拦截:> 容差上限提交被拒(应用层 + 领域层双重); -- 自动带入:供应商/物料/单价/规格只读自动带出; -- 向下兼容:历史单可查可编辑; -- 类型隔离:非采购入库不受影响; -- 质量达标:lint / ts-check / 单测全绿。 - ---- - -## 3. 执行顺序与验证节奏 - -1. 子任务 1(schema + 迁移)→ `pnpm migrate` 验证 -2. 子任务 2(领域层)→ `pnpm ts-check` + 子任务 7.1 单测 -3. 子任务 6(系统配置)→ `pnpm ts-check` -4. 子任务 3(应用服务 + 处理器)→ `pnpm lint` + `pnpm ts-check` + 7.2 单测 -5. 子任务 4(API)→ 接口 curl 冒烟 -6. 子任务 5(前端)→ 浏览器手工验证 -7. 子任务 7(回归)→ 全量命令 + 手工回归 - -每完成一个子任务运行对应验证命令,全部完成后跑一次完整回归(`pnpm lint` + `pnpm ts-check` + `pnpm test:unit:run`)。 - -## 4. 自检清单(提交前) - -- [ ] `pnpm lint` 通过 -- [ ] `pnpm ts-check` 通过 -- [ ] 新增单测全部通过(receive-reverse / inbound-from-po) -- [ ] 迁移已应用且 down 脚本可用 -- [ ] 历史无来源入库单查询/编辑不受影响 -- [ ] 非采购入库类型创建流程不变 -- [ ] 采购入库:强制选单、自动带出、超量标红、提交成功 -- [ ] 入库审核 → PO 已收/状态更新;入库作废/反审核 → PO 回补 diff --git a/docs/superpowers/specs/2026-07-16-multi-currency-design.md b/docs/superpowers/specs/2026-07-16-multi-currency-design.md deleted file mode 100644 index 75308a33..00000000 --- a/docs/superpowers/specs/2026-07-16-multi-currency-design.md +++ /dev/null @@ -1,431 +0,0 @@ -# 多币种支持设计文档 - -> 日期:2026-07-16 -> 状态:已批准(待实现) -> 范围:跨境采购 + 跨境销售 + 财务收付款多币种核算 - -## 1. 背景与目标 - -### 1.1 业务背景 -公司有跨境采购(向国外供应商用美元/越南盾付款)和跨境销售(向海外客户用美元结算)需求。现有系统金额字段以人民币(CNY)为主,部分表(`pur_purchase_order`、`sal_order`)已有 `currency` + `exchange_rate` 字段但未启用,财务表(`fin_payable`/`fin_receivable`/`fin_payment_record`/`fin_receipt_record`)完全缺失多币种字段。 - -### 1.2 目标 -- 支持多币种业务单据(CNY/USD/VND,可扩展) -- 按公司设置本位币(如中国公司 CNY,越南公司 VND) -- 实时汇率换算,业务单据创建时计算并存储本位币金额快照 -- 财务报表按本位币汇总,无需实时换算 - -### 1.3 非目标(YAGNI) -- 汇率 API 自动获取(先用手动录入) -- 多公司主体合并报表 -- 期货套期保值 -- 历史汇率追溯调整 - -## 2. 现状分析 - -### 2.1 已有基础设施 -| 资产 | 位置 | 状态 | -|------|------|------| -| `Money` 值对象 | `src/domain/shared/value-objects/Money.ts` | 已有 currency 字段 + add/subtract/multiply,缺 convertTo | -| `pur_purchase_order` 表 | `database/vnerpdacahng_schema.sql:2827` | 已有 `currency` + `exchange_rate`,缺 `base_amount` | -| `sal_order` 表 | `database/vnerpdacahng_schema.sql:3372` | 已有 `currency` + `exchange_rate`,缺 `base_amount` | -| `sys_company` 表 | `database/vnerpdacahng_schema.sql:3741` | 已有公司主体,缺 `base_currency` 字段 | -| 配置项 | `settings/config/page.tsx:312-337` | 已有 `finance.default_currency` 和 `finance.multi_currency` | -| `audit_system_tables.sql:133` | 已有 `exchange_rate DECIMAL(10,6)` | 审计表已有汇率字段 | - -### 2.2 缺失部分 -- 无 `sys_currency`(币种主数据)表 -- 无 `sys_exchange_rate`(汇率表)表 -- `sys_company` 缺 `base_currency` 字段 -- `fin_payable`/`fin_receivable`/`fin_payment_record`/`fin_receipt_record` 缺 `currency` + `exchange_rate` + `base_amount` 字段 -- `Money` 值对象缺 `convertTo`/`format` 方法 -- 无 `ICurrencyService` 领域服务接口 -- 无统一的货币格式化工具和 `` 组件 - -## 3. 架构设计 - -### 3.1 分层架构 -``` -┌─────────────────────────────────────────────┐ -│ 前端层 │ -│ 组件 │ -│ formatMoney() 工具 + │ -├─────────────────────────────────────────────┤ -│ API 路由层(Thin Controller) │ -│ /api/system/currency /api/system/exchange-rate │ -├─────────────────────────────────────────────┤ -│ 应用服务层 │ -│ CurrencyApplicationService │ -│ 采购/销售/财务服务调用 Money.convertTo() │ -├─────────────────────────────────────────────┤ -│ 领域层 │ -│ Money 值对象(扩展 convertTo/format) │ -│ ICurrencyService 接口 │ -├─────────────────────────────────────────────┤ -│ 基础设施层 │ -│ MysqlCurrencyRepository(实现 ICurrencyService)│ -├─────────────────────────────────────────────┤ -│ 数据库层 │ -│ sys_currency / sys_exchange_rate (新建) │ -│ sys_company.base_currency (新字段) │ -│ fin_*.{currency,exchange_rate,base_amount} │ -└─────────────────────────────────────────────┘ -``` - -### 3.2 核心数据流(以采购单为例) -1. 创建采购单时,用户选币种 USD -2. `CurrencyApplicationService` 查 `sys_exchange_rate` 取最新 USD→CNY 汇率(带 Redis 缓存) -3. 存入 `pur_purchase_order`:`currency=USD`, `exchange_rate=7.25`, `total_amount=1000`(原币), `base_amount=7250`(本位币) -4. 生成应付 `fin_payable` 时携带 currency+exchange_rate+base_amount -5. 报表查询时直接 SUM(base_amount) 得到本位币汇总,无需实时换算 - -### 3.3 关键设计决策 -虽然采用"实时汇率换算",但每笔业务单据**创建时**会按当时汇率计算 `base_amount` 并存储。这保证报表数据稳定(不因后续汇率波动改变已发生的历史金额),同时汇率表保持最新汇率供新单据使用。这比纯实时换算更符合财务规范。 - -## 4. 数据库设计 - -### 4.1 新建表 - -#### sys_currency(币种主数据) -```sql -CREATE TABLE `sys_currency` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT, - `code` varchar(10) NOT NULL COMMENT '币种代码 ISO 4217 (CNY/USD/VND)', - `name` varchar(50) NOT NULL COMMENT '币种名称', - `symbol` varchar(10) DEFAULT NULL COMMENT '符号 (¥/$/₫)', - `decimal_places` tinyint DEFAULT 2 COMMENT '小数位 (CNY=2, USD=2, VND=0)', - `status` tinyint DEFAULT 1 COMMENT '1启用 0停用', - `sort` int DEFAULT 0, - `create_time` datetime DEFAULT CURRENT_TIMESTAMP, - `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - `create_by` bigint unsigned DEFAULT NULL, - `update_by` bigint unsigned DEFAULT NULL, - `deleted` tinyint DEFAULT 0, - PRIMARY KEY (`id`), - UNIQUE KEY `uk_code` (`code`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='币种主数据'; - --- 预置数据 -INSERT INTO `sys_currency` (`code`, `name`, `symbol`, `decimal_places`, `sort`) VALUES - ('CNY', '人民币', '¥', 2, 1), - ('USD', '美元', '$', 2, 2), - ('VND', '越南盾', '₫', 0, 3); -``` - -#### sys_exchange_rate(汇率表) -```sql -CREATE TABLE `sys_exchange_rate` ( - `id` bigint unsigned NOT NULL AUTO_INCREMENT, - `from_currency` varchar(10) NOT NULL COMMENT '源币种', - `to_currency` varchar(10) NOT NULL COMMENT '目标币种', - `rate` decimal(18,6) NOT NULL COMMENT '汇率', - `rate_date` date NOT NULL COMMENT '汇率日期', - `source` varchar(50) DEFAULT 'manual' COMMENT '汇率来源 manual/api', - `remark` varchar(200) DEFAULT NULL, - `create_time` datetime DEFAULT CURRENT_TIMESTAMP, - `create_by` bigint unsigned DEFAULT NULL, - PRIMARY KEY (`id`), - KEY `idx_from_to_date` (`from_currency`,`to_currency`,`rate_date`), - KEY `idx_date` (`rate_date`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='汇率表'; -``` - -### 4.2 修改表 - -#### sys_company 加本位币字段 -```sql -ALTER TABLE `sys_company` - ADD COLUMN `base_currency` varchar(10) DEFAULT 'CNY' COMMENT '本位币' AFTER `tax_no`; -``` - -#### 财务表补多币种字段 -涉及表:`fin_payable`、`fin_receivable`、`fin_payment_record`、`fin_receipt_record` - -```sql --- 以 fin_payable 为例,其余三表同构 -ALTER TABLE `fin_payable` - ADD COLUMN `currency` varchar(10) DEFAULT 'CNY' COMMENT '币种' AFTER `amount`, - ADD COLUMN `exchange_rate` decimal(18,6) DEFAULT '1.000000' COMMENT '汇率' AFTER `currency`, - ADD COLUMN `base_amount` decimal(18,4) DEFAULT '0.0000' COMMENT '本位币金额' AFTER `exchange_rate`; - --- 旧数据回填:currency 默认 CNY,exchange_rate 默认 1,base_amount = amount -UPDATE `fin_payable` SET `base_amount` = `amount` WHERE `base_amount` = 0; --- 对 fin_receivable、fin_payment_record、fin_receipt_record 同样回填 -``` - -#### 采购/销售主表补 base_amount -```sql -ALTER TABLE `pur_purchase_order` - ADD COLUMN `base_amount` decimal(18,4) DEFAULT '0.0000' COMMENT '本位币总金额' AFTER `grand_total`; - -ALTER TABLE `sal_order` - ADD COLUMN `base_amount` decimal(18,4) DEFAULT '0.0000' COMMENT '本位币总金额' AFTER `total_with_tax`; - --- 旧数据回填 -UPDATE `pur_purchase_order` SET `base_amount` = `total_amount` WHERE `base_amount` = 0; -UPDATE `sal_order` SET `base_amount` = `total_amount` WHERE `base_amount` = 0; -``` - -### 4.3 精度规范 -- 金额字段:`decimal(18,4)` — 兼容 VND(无小数但金额大)和 USD/CNY -- 汇率字段:`decimal(18,6)` — 容纳 6 位小数精度 -- `base_amount` = 原币金额 × 汇率,创建时计算存储 - -## 5. 领域层设计 - -### 5.1 Money 值对象扩展 -在现有 `src/domain/shared/value-objects/Money.ts` 基础上添加: - -```typescript -/** - * 按汇率转换为目标币种(返回新的 Money 对象) - * @param rate 汇率(from→to) - * @param targetCurrency 目标币种代码 - * @param decimalPlaces 目标币种小数位(VND=0, CNY/USD=2) - */ -convertTo(rate: number, targetCurrency: string, decimalPlaces: number = 2): Money { - if (this.currency === targetCurrency) { - return new Money(this.amount, this.currency); - } - const converted = this.amount * rate; - const factor = Math.pow(10, decimalPlaces); - const rounded = Math.round(converted * factor) / factor; - return new Money(rounded, targetCurrency); -} - -/** - * 按目标币种小数位格式化金额 - */ -format(decimalPlaces: number = 2): string { - return this.amount.toFixed(decimalPlaces); -} -``` - -### 5.2 ICurrencyService 领域服务接口 -```typescript -// src/domain/shared/CurrencyService.ts (新建) - -export interface CurrencyInfo { - code: string; - name: string; - symbol: string; - decimalPlaces: number; -} - -export interface ExchangeRate { - fromCurrency: string; - toCurrency: string; - rate: number; - rateDate: Date; -} - -export interface ICurrencyService { - getCurrency(code: string): Promise; - getLatestRate(fromCurrency: string, toCurrency: string): Promise; - getRateOnDate(fromCurrency: string, toCurrency: string, date: Date): Promise; - listActiveCurrencies(): Promise; -} -``` - -**原则:** -- 领域层只定义接口,不依赖 MySQL/Drizzle -- `Money.convertTo` 是纯函数,汇率由调用方传入 -- 应用服务层负责调用 `ICurrencyService` 获取汇率 - -## 6. 应用层与基础设施层 - -### 6.1 MysqlCurrencyRepository -```typescript -// src/infrastructure/repositories/MysqlCurrencyRepository.ts (新建) -export class MysqlCurrencyRepository implements ICurrencyService { - async getCurrency(code: string): Promise { - // SELECT * FROM sys_currency WHERE code = ? AND status = 1 AND deleted = 0 - } - async getLatestRate(from: string, to: string): Promise { - // SELECT * FROM sys_exchange_rate - // WHERE from_currency=? AND to_currency=? - // ORDER BY rate_date DESC LIMIT 1 - } - // ...其他方法 -} -``` - -### 6.2 CurrencyApplicationService -```typescript -// src/application/services/CurrencyApplicationService.ts (新建) -export class CurrencyApplicationService { - constructor(private currencyService: ICurrencyService) {} - - /** 获取最新汇率,带 Redis 缓存(TTL 5分钟) */ - async getLatestRate(from: string, to: string): Promise { - if (from === to) return 1; - const cacheKey = `rate:${from}:${to}`; - const cached = await cache.get(cacheKey); - if (cached) return parseFloat(cached); - const rate = await this.currencyService.getLatestRate(from, to); - if (!rate) throw new Error(`汇率未配置: ${from}→${to}`); - await cache.set(cacheKey, rate.rate, 300); - return rate.rate; - } - - /** 换算金额为本位币 */ - async convertToBaseCurrency(money: Money, baseCurrency: string): Promise { - if (money.currency === baseCurrency) return money; - const rate = await this.getLatestRate(money.currency, baseCurrency); - const target = await this.currencyService.getCurrency(baseCurrency); - return money.convertTo(rate, baseCurrency, target?.decimalPlaces ?? 2); - } -} -``` - -**缓存降级:** 无 Redis 时降级为内存 Map 缓存,复用项目现有的 Redis 降级模式。 - -### 6.3 业务服务集成(以采购订单为例) -```typescript -// src/application/services/PurchaseApplicationService.ts (修改) -async createPurchaseOrder(dto: CreatePurchaseOrderDTO): Promise { - const baseCurrency = await this.getCompanyBaseCurrency(); - const totalAmount = Money.create(dto.totalAmount, dto.currency); - const baseAmount = await this.currencyAppService.convertToBaseCurrency(totalAmount, baseCurrency); - const rate = await this.currencyAppService.getLatestRate(dto.currency, baseCurrency); - - return this.purchaseOrderRepo.create({ - ...dto, - exchange_rate: rate, - base_amount: baseAmount.amount, - }); -} -``` - -### 6.4 API 路由 -```typescript -// src/app/api/system/currency/route.ts (新建) -// GET - 币种列表 -// POST - 新建币种 -// PUT - 更新币种 -// DELETE - 删除币种 - -// src/app/api/system/exchange-rate/route.ts (新建) -// GET /api/system/exchange-rate?from=USD&to=CNY → 最新汇率 -// GET /api/system/exchange-rate?date=2026-07-16 → 指定日期汇率 -// POST /api/system/exchange-rate → 录入汇率 -``` - -## 7. 前端设计 - -### 7.1 新建组件 - -#### `` 组件 -```tsx -// src/components/ui/money-display.tsx -interface MoneyDisplayProps { - amount: number; - currency?: string; // 原币代码,默认 CNY - baseAmount?: number; // 本位币金额(可选,提供则双行显示) - baseCurrency?: string; // 本位币代码 - showSymbol?: boolean; // 是否显示符号 ¥/$/₫ -} -// 渲染:$1,000.00 (≈¥7,250.00) 或仅 ¥7,250.00 -``` - -#### `formatMoney()` 工具函数 -```typescript -// src/lib/money-format.ts (新建) -export function formatMoney(amount: number, currency: string, decimalPlaces = 2): string { - return new Intl.NumberFormat(getLocaleByCurrency(currency), { - style: 'currency', - currency, - minimumFractionDigits: decimalPlaces, - maximumFractionDigits: decimalPlaces, - }).format(amount); -} -// VND 用 0 位小数,CNY/USD 用 2 位 -``` - -#### `` 币种选择器 -```tsx -// src/components/ui/currency-select.tsx -// 下拉选 sys_currency 表数据,显示 "¥ CNY 人民币" 格式 -``` - -### 7.2 新建页面 -- **汇率管理页面** `settings/exchange-rate`:汇率列表(筛选 from/to 币种)、录入/更新汇率表单 -- **币种管理页面** `settings/currency`:币种 CRUD(启用/停用、设置小数位) - -### 7.3 现有页面改造 - -#### 采购订单页面(已有 currency 字段,改造小) -- 表单加币种选择器 + 汇率自动填充(选币种后自动查最新汇率) -- 列表/详情用 `` 展示原币+本位币 -- 总金额区域显示"USD 1,000.00 (≈CNY 7,250.00)" - -#### 销售订单页面(已有 currency 字段,改造小) -- 同采购订单 - -#### 财务页面(fin_payable/receivable 等) -- 列表加币种列 -- 付款/收款表单加币种选择 -- 报表按本位币汇总(SUM(base_amount)) - -### 7.4 i18n -新增 Common 块 key(4 语言文件同步): -- `currency`、`exchangeRate`、`baseCurrency`、`originalCurrency`、`convertedAmount`、`exchangeRateManagement`、`currencyManagement` - -## 8. 分阶段实施计划 - -### Phase 1:基础设施 -**交付物:** -- 数据库迁移:新建 `sys_currency`、`sys_exchange_rate` 表 -- `sys_company` 加 `base_currency` 字段 -- `Money` 值对象扩展 `convertTo`/`format` -- `ICurrencyService` 接口 + `MysqlCurrencyRepository` 实现 -- `CurrencyApplicationService`(含 Redis 缓存) -- 预置数据:CNY/USD/VND 三币种 + 初始汇率 -- API:`/api/system/currency`、`/api/system/exchange-rate` -- 前端:`formatMoney()` 工具、``、`` 组件 -- 设置页面:币种管理、汇率管理 - -**验证:** 能查询币种、录入汇率、调用 `convertTo` 正确换算 - -### Phase 2:采购模块改造 -**交付物:** -- `pur_purchase_order` 加 `base_amount` 字段 -- `PurchaseApplicationService` 集成 `CurrencyApplicationService` -- 创建采购单时自动查汇率、计算 base_amount -- 采购订单页面:币种选择器 + 汇率自动填充 + 双币展示 -- `fin_payable` 加 currency+exchange_rate+base_amount 字段 -- 生成应付时携带多币种信息 -- 旧数据回填:currency=CNY, exchange_rate=1, base_amount=amount - -**验证:** 创建 USD 采购单 → 应付生成正确本位币金额 - -### Phase 3:销售模块改造 -**交付物:** -- `sal_order` 加 `base_amount` 字段 -- `SalesApplicationService` 集成 `CurrencyApplicationService` -- 销售订单页面:币种选择器 + 双币展示 -- `fin_receivable` 加 currency+exchange_rate+base_amount 字段 -- 生成应收时携带多币种信息 -- 旧数据回填 - -**验证:** 创建 USD 销售单 → 应收生成正确本位币金额 - -### Phase 4:财务收付款 + 报表 -**交付物:** -- `fin_payment_record`、`fin_receipt_record` 加 currency+exchange_rate+base_amount -- 付款/收款表单:币种选择 + 汇率显示 + 本位币金额 -- 财务报表:按 `base_amount` 汇总(SUM 不再需要实时换算) -- 对账单:原币+本位币双列展示 -- 汇率损益计算(收付时汇率与应收应付时汇率的差异) - -**验证:** 跨币种收付款 → 报表正确汇总本位币 + 汇兑损益正确 - -## 9. 错误处理 -- 汇率未配置时:`CurrencyApplicationService.getLatestRate` 抛出明确错误,前端 toast 提示"汇率未配置,请先在汇率管理中录入" -- Redis 缓存不可用时:降级为内存 Map 缓存,不影响业务 -- 币种停用时:已存在的单据保留原币种,新建单据不能选择已停用币种 - -## 10. 测试策略 -- 单元测试:`Money.convertTo` 各种币种组合(含 VND 0 位小数)、`formatMoney` 格式化 -- 集成测试:`MysqlCurrencyRepository` CRUD、`CurrencyApplicationService` 缓存命中/失效 -- E2E 测试:创建 USD 采购单 → 生成应付 → 付款 → 报表汇总本位币金额正确 diff --git a/docs/superpowers/specs/2026-07-16-purchase-multi-currency-design.md b/docs/superpowers/specs/2026-07-16-purchase-multi-currency-design.md deleted file mode 100644 index 74235ccb..00000000 --- a/docs/superpowers/specs/2026-07-16-purchase-multi-currency-design.md +++ /dev/null @@ -1,460 +0,0 @@ -# 采购模块多币种改造(Phase 2a)设计规格 - -> **状态:** 已批准 -> **作者:** brainstorming 流程产出 -> **日期:** 2026-07-16 -> **依赖:** Phase 1 多币种基础设施(已完成) - -## 1. 概述 - -### 1.1 目标 - -为采购模块接入多币种支持,实现采购订单、采购退货、采购对账、供应商主数据的多币种金额管理。核心能力: - -- 支持人民币(CNY)、美元(USD)、越南盾(VND)等多币种采购 -- 创建订单时固化汇率,本位币金额随之固化 -- 明细表冗余本位币金额字段,避免查询时计算 -- 采购退货强制跟随原订单 currency + rate -- 采购对账强制关联入库单同币种 -- 前端双币种展示 - -### 1.2 核心决策 - -| 决策项 | 选择 | 理由 | -|--------|------|------| -| 汇率换算时机 | 创建订单时固化 | 财务确定性,避免历史订单汇率波动 | -| 明细表设计 | 冗余本位币金额字段 | 查询性能 + currency/rate 不可变前提下的冗余安全 | -| 退货币种 | 跟随原订单 currency + rate | 退货是订单的逆向业务,避免汇兑损益 | -| 对账币种 | 强制同币种才能对账 | 财务对账要求同币种汇总 | -| 历史数据 | 按历史日期查询汇率填充 | 反映真实历史成本 | -| 供应商主数据 | 增加 default_currency | 供应商稳定使用某一币种结算 | -| currency 可变性 | 创建后不可变 | 保证数据完整性 | - -### 1.3 实现方案 - -方案 B:完整 DDD 改造。领域模型所有金额字段统一用 `Money` 值对象,引入 `CurrencySnapshot` 值对象封装 currency + rate + baseAmount,事件层携带 currency 信息。 - -## 2. 数据模型 - -### 2.1 表改造总览 - -| 表 | 改造内容 | -|----|---------| -| `pur_supplier` | 新增 `default_currency` VARCHAR(10) DEFAULT 'CNY' | -| `pur_purchase_order` | 新增 `base_total_amount`、`base_tax_amount`、`base_grand_total` DECIMAL(18,4) NOT NULL DEFAULT 0.0000(currency、exchange_rate 已有) | -| `pur_purchase_order_line` | 新增 `base_unit_price`、`base_amount`、`base_tax_amount`、`base_line_total` DECIMAL(18,4) NOT NULL DEFAULT 0.0000 | -| `pur_purchase_return` | 新增 `currency` VARCHAR(10) NOT NULL DEFAULT 'CNY'、`exchange_rate` DECIMAL(18,4) NOT NULL DEFAULT 1.0000、`base_total_amount` DECIMAL(18,4) NOT NULL DEFAULT 0.0000 | -| `pur_purchase_return_line` | 新增 `base_unit_price`、`base_amount` DECIMAL(18,4) NOT NULL DEFAULT 0.0000 | -| `pur_purchase_reconciliation` | 新增 `currency`、`exchange_rate`、`base_receipt_amount`、`base_return_amount`、`base_net_amount`、`base_discount_amount`、`base_paid_amount`、`base_balance_amount` | - -### 2.2 Migration 064:表结构变更 - -幂等性通过 `INFORMATION_SCHEMA` 检查列是否存在实现(参考 migration 027/028/042-044 的模式)。 - -字段位置通过 `AFTER` 子句保持本位币字段紧邻原币字段。所有新金额字段使用 `NOT NULL DEFAULT 0.0000`,新币种字段使用 `NOT NULL DEFAULT 'CNY'`,新汇率字段使用 `NOT NULL DEFAULT 1.0000`。字符集与排序规则跟随表(utf8mb4_0900_ai_ci)。 - -### 2.3 Migration 065:历史数据回填 - -策略:按历史订单创建日期查询当时汇率,查不到时 fallback 到 1.0000。 - -实现:使用存储过程分批处理(每批 1000 条),避免长事务锁表。 - -回填范围: -- `pur_purchase_order`:currency、exchange_rate、base_total_amount、base_tax_amount、base_grand_total -- `pur_purchase_order_line`:base_unit_price、base_amount、base_tax_amount、base_line_total -- `pur_supplier`:default_currency -- `pur_purchase_return` / `pur_purchase_reconciliation`:历史记录按 1.0 回填(旧记录未接入入库流程) - -幂等性:仅处理 `base_grand_total = 0 AND grand_total > 0` 的记录,避免重复回填。 - -未找到汇率的记录在日志中记录 orderId + currency,供人工核对。 - -### 2.4 Drizzle schema.ts 同步 - -所有新字段使用 `decimal({ precision: 18, scale: 4 }).default('0.0000').notNull()`,与现有金额字段精度一致(migration 024 统一为 18,4)。币种字段使用 `varchar('currency', { length: 10 }).default('CNY').notNull()`,汇率字段使用 `decimal('exchange_rate', { precision: 18, scale: 4 }).default('1.0000').notNull()`。 - -## 3. 领域层设计 - -### 3.1 新增值对象:CurrencySnapshot - -文件:`src/domain/shared/value-objects/CurrencySnapshot.ts` - -不可变值对象,封装创建订单时的 currency + exchange_rate + baseCurrency。 - -**关键方法**: -- `create(currency, exchangeRate, baseCurrency)` — 工厂方法,校验 currency 长度 3、exchangeRate 正数 -- `isSameCurrency` — 判断同币种短路 -- `convert(money, decimalPlaces=2)` — 复用 `Money.convertTo`,使用固化的 exchangeRate - -### 3.2 PurchaseOrder 聚合根 - -文件:`src/domain/purchase/aggregates/PurchaseOrder.ts` - -**新增属性**: -- `baseCurrency: string` -- `baseTotalAmount: number` -- `baseTaxAmount: number` -- `baseGrandTotal: number` -- `currencySnapshot: CurrencySnapshot` - -**改造方法**: -- `updateAmounts()` — 同时计算原币和本位币金额 -- `updateLine(line)` — 同时更新明细本位币金额 -- `isCurrencyImmutable()` — 返回 true - -### 3.3 PurchaseOrderLine 实体 - -文件:`src/domain/purchase/entities/PurchaseOrderLine.ts` - -**新增属性**:`baseUnitPrice`、`baseAmount`、`baseTaxAmount`、`baseLineTotal` - -**改造方法**:`recompute(snapshot: CurrencySnapshot)` — 同时计算原币和本位币金额 - -### 3.4 PurchaseReturn 聚合根 - -文件:`src/domain/purchase/aggregates/PurchaseReturn.ts` - -**新增属性**:`currency`、`exchangeRate`、`baseCurrency`、`baseTotalAmount`、`currencySnapshot` - -**关键约束**:静态工厂 `createFromOrder(order: PurchaseOrder, ...)` 强制从原订单继承 currency + exchangeRate + baseCurrency。不提供独立的 `setCurrency()` 方法。 - -### 3.5 PurchaseReturnLine 实体 - -文件:`src/domain/purchase/entities/PurchaseReturnLine.ts` - -**新增属性**:`baseUnitPrice`、`baseAmount` - -### 3.6 PurchaseReconciliation 聚合根 - -文件:`src/domain/purchase/aggregates/PurchaseReconciliation.ts` - -**新增属性**:`currency`、`exchangeRate`、`baseCurrency`、`baseReceiptAmount`、`baseReturnAmount`、`baseNetAmount`、`baseDiscountAmount`、`basePaidAmount`、`baseBalanceAmount` - -**关键约束**:静态工厂 `create(props)` 在构造时遍历所有关联入库单,校验 currency 必须一致,不一致抛出 `DomainError`。汇率从首笔入库单继承。 - -### 3.7 领域事件改造 - -文件:`src/domain/purchase/events/PurchaseOrderEvents.ts` 等 - -事件 payload 新增字段:`currency`、`exchangeRate`、`baseCurrency`、`baseTotalAmount`、`baseTaxAmount`、`baseGrandTotal` - -涉及事件: -- `PurchaseOrderCreated` -- `PurchaseOrderApproved` -- `PurchaseReturnCreated` -- `PurchaseReconciliationCreated` - -## 4. 基础设施层设计 - -### 4.1 MysqlPurchaseOrderRepository - -文件:`src/infrastructure/repositories/MysqlPurchaseOrderRepository.ts` - -**改造点**: -- INSERT:包含 currency、exchange_rate、base_total_amount、base_tax_amount、base_grand_total -- SELECT:包含本位币字段 + 供应商 default_currency -- UPDATE:明细行同步更新 base_* 字段 -- `toDomain` 映射:decimal → number 转换,null 通过 NOT NULL 约束避免 - -**防御性校验**:Repository 层不做业务校验,依赖应用服务层。 - -### 4.2 MysqlPurchaseReturnRepository - -文件:`src/infrastructure/repositories/MysqlPurchaseReturnRepository.ts` - -**改造点**: -- INSERT:包含 currency、exchange_rate、base_total_amount -- 明细行 INSERT/UPDATE:包含 base_unit_price、base_amount -- `toDomain` 映射:构建 currencySnapshot - -### 4.3 MysqlPurchaseReconciliationRepository - -文件:`src/infrastructure/repositories/MysqlPurchaseReconciliationRepository.ts` - -**改造点**: -- INSERT:包含 currency、exchange_rate、所有 base_* 字段 -- SELECT:包含 currency + base_* 字段 -- 对账单创建前的 currency 一致性校验使用 `COALESCE(i.currency, 'CNY')` 兼容 `inv_inbound` 暂无 currency 字段的情况 - -### 4.4 DrizzlePurchaseOrderRepository - -备用实现,通过 `REPOSITORY_IMPL=drizzle` 切换。使用 Drizzle ORM 链式 API,字段访问通过 camelCase 属性。 - -### 4.5 数据映射策略 - -- `DECIMAL(18,4)` → 读取时 `Number(row.field_name)`,写入时直接传 number -- 所有新字段 `NOT NULL DEFAULT`,避免 null 判断 -- Repository 层做 Money ↔ number 转换,领域层只感知 Money 对象 - -## 5. 应用服务层设计 - -### 5.1 PurchaseApplicationService - -文件:`src/application/services/PurchaseApplicationService.ts` - -**构造注入扩展**:新增 `CurrencyApplicationService` - -**createOrder 方法**(核心改造): -1. 确定 currency:优先级 = 输入 > 供应商 default_currency > 系统默认 `finance.default_currency` -2. 查询当前汇率:同币种短路(rate=1),否则调用 `CurrencyApplicationService.getLatestRate` -3. 无汇率记录时抛 `NotFoundError` -4. 创建 `CurrencySnapshot` -5. 构建明细:同时计算原币 + 本位币金额(通过 `snapshot.convert`) -6. 汇总主表金额:原币 + 本位币 -7. 构建聚合根并持久化 -8. 发布事件(携带 currency 字段) - -**updateOrder 方法**: -- 不可变性约束:拒绝修改 currency 和 exchangeRate,抛 `DomainError` -- 修改明细行时使用固化的 `currencySnapshot` 重新计算本位币金额 - -### 5.2 PurchaseReturnApplicationService - -**createReturn 方法**: -- 查询原订单 -- 调用 `PurchaseReturn.createFromOrder(originalOrder, ...)`,强制继承 currency + rate + baseCurrency -- 本位币退货金额按原订单固化汇率计算 -- 发布事件 - -### 5.3 PurchaseReconciliationApplicationService - -**createReconciliation 方法**: -1. 查询所有关联入库单 -2. 校验同币种(`COALESCE(i.currency, 'CNY')`) -3. 不一致抛 `DomainError`,错误消息列出所有涉及的 currency -4. 从首笔入库单继承 currency + exchangeRate -5. 构建 `CurrencySnapshot` -6. 汇总金额(原币 + 本位币) -7. 持久化并发布事件 - -### 5.4 事件处理器 - -`PurchasePayableHandler`:从事件 payload 读取 currency + rate + baseAmount,传递给 `FinanceApplicationService.createPayable`。 - -**向后兼容**:Phase 2a 期间,财务侧暂不消费 currency 字段,仅记录到日志,Phase 2c 改造时同步更新。 - -## 6. API 层设计 - -### 6.1 采购订单 API - -文件:`src/app/api/purchase/orders/route.ts` - -**GET 列表响应**:新增 currency、exchangeRate、baseCurrency、baseTotalAmount、baseTaxAmount、baseGrandTotal 字段 - -**GET 详情响应**:包含主表 + 明细行,每行携带原币与本位币金额 - -**POST 创建请求**:currency 可选(默认取供应商 default_currency),exchangeRate 不传(服务端查询固化) - -**PUT 更新约束**:拒绝修改 currency 和 exchangeRate,返回 400 - -### 6.2 采购退货 API - -文件:`src/app/api/purchase/return/route.ts` - -**POST 创建请求**:不传 currency/exchangeRate,强制从原订单继承 - -### 6.3 采购对账 API - -文件:`src/app/api/purchase/reconciliation/route.ts` - -**POST 创建请求**:不传 currency/exchangeRate,由入库单决定 - -**校验失败响应**:返回 400 + 错误消息 "对账失败:关联入库单币种不一致:USD, VND" - -### 6.4 供应商 API - -文件:`src/app/api/purchase/suppliers/route.ts` - -**POST/PUT 请求**:新增 `defaultCurrency` 字段 - -### 6.5 API 权限 - -`/api/purchase/*` 路径权限已在 `api-permissions.ts` 注册,无需新增。 - -## 7. 前端改造 - -### 7.1 采购订单列表页 - -文件:`src/app/[locale]/purchase/orders/page.tsx` - -- 表头新增"币种"、"价税合计(本位币)"列 -- 金额列使用 `MoneyDisplay` 组件展示双币种 -- 筛选条件新增 `CurrencySelect` 下拉 - -### 7.2 采购订单详情页 - -文件:`src/app/[locale]/purchase/orders/[id]/page.tsx` - -- 头部展示 currency + exchangeRate + baseCurrency 信息卡片 -- 明细表格每行展示原币 + 本位币金额 -- 汇总区域展示双币种合计 -- 不可变提示 - -### 7.3 采购订单创建/编辑表单 - -- 新增 `CurrencySelect` 币种选择器 -- 选择供应商后自动填充 `defaultCurrency` -- 实时预览本位币金额(前端调用 `/api/system/exchange-rate?from=...&to=...&latest=true`) -- 提交后服务端固化真实汇率 - -### 7.4 采购退货页面 - -文件:`src/app/[locale]/purchase/return/page.tsx` - -- 创建退货时展示原订单 currency(只读) -- 退货金额自动按原订单汇率换算本位币 -- 列表页增加 currency 列 - -### 7.5 采购对账页面 - -文件:`src/app/[locale]/purchase/reconciliation/page.tsx` - -- 创建对账时展示从入库单继承的 currency(只读) -- 同币种校验失败时显示明确提示 -- 列表页增加 currency 列 -- 对账金额双币种展示 - -### 7.6 供应商管理页面 - -文件:`src/app/[locale]/purchase/suppliers/page.tsx` - -- 表单新增 `defaultCurrency` 字段(`CurrencySelect` 组件) -- 列表页展示默认币种列 - -### 7.7 i18n 扩展 - -新增 key 到 4 个语言文件(zh-CN / zh-TW / en / vi)的 Common 块: - -- `baseCurrencyAmount` — 本位币金额 -- `originalCurrencyAmount` — 原币金额 -- `currencyImmutableWarning` — 币种创建后不可修改 -- `exchangeRateLocked` — 汇率已固化 -- `inboundCurrencyMismatch` — 关联入库单币种不一致:{currencies} -- `supplierDefaultCurrency` — 供应商默认币种 -- `estimatedBaseAmount` — 预估本位币金额 -- `exchangeRateAtCreation` — 创建时汇率 - -## 8. 测试策略 - -### 8.1 单元测试 - -**CurrencySnapshot 值对象**(新增 `tests/unit/domain/shared/value-objects/currency-snapshot.test.ts`): -- `create` 工厂方法:有效输入、无效 currency(长度非 3)、无效 exchangeRate(≤0、NaN) -- `isSameCurrency`:同币种、不同币种 -- `convert`:同币种短路、跨币种换算、VND 0 位小数、负数金额 - -**PurchaseOrder 聚合根**(扩展测试): -- `create`:携带 currencySnapshot 创建 -- `updateLine`:修改明细时本位币金额同步更新 -- `updateAmounts`:汇总计算本位币金额 -- 不可变性:创建后 currency 不可修改 - -**PurchaseReturn 聚合根**(扩展测试): -- `createFromOrder`:从原订单继承 currency + rate -- 非法输入:原订单 currency 与传入 currency 不一致时抛错 - -**PurchaseReconciliation 聚合根**(扩展测试): -- `create`:同币种入库单创建成功 -- `create`:不同币种入库单抛 DomainError -- `create`:从首笔入库单继承 currency + rate - -**PurchaseApplicationService**(扩展测试): -- `createOrder`:currency 优先级(输入 > 供应商 > 系统默认) -- `createOrder`:汇率查询 + 固化 -- `createOrder`:同币种短路(exchangeRate=1) -- `createOrder`:无汇率记录时抛 NotFoundError -- `updateOrder`:拒绝修改 currency -- `updateOrder`:修改明细行时本位币金额重新计算 - -**PurchaseReturnApplicationService**(扩展测试): -- `createReturn`:从原订单继承 currency + rate -- `createReturn`:本位币金额按原订单汇率计算 - -**PurchaseReconciliationApplicationService**(扩展测试): -- `createReconciliation`:同币种入库单创建成功 -- `createReconciliation`:不同币种入库单抛错 -- `createReconciliation`:从首笔入库单继承 currency - -### 8.2 集成测试 - -**API 集成测试**(`tests/integration/api/purchase/*.test.ts`): -- POST /api/purchase/orders:创建多币种订单,验证响应包含 currency + base_* 字段 -- PUT /api/purchase/orders/{id}:尝试修改 currency,验证返回 400 -- POST /api/purchase/return:创建退货,验证 currency 继承自原订单 -- POST /api/purchase/reconciliation:不同币种入库单,验证返回 400 - -### 8.3 测试 Mock 策略 - -- `CurrencyApplicationService`:Mock `getLatestRate` 返回固定汇率 -- `IPurchaseOrderRepository`:内存实现或 Mock -- `IEventBus`:Mock 验证事件 payload 包含 currency 字段 -- `SystemConfigService`:Mock 返回固定 base_currency - -## 9. 向后兼容与迁移策略 - -### 9.1 向后兼容保证 - -1. **API 响应**:新增字段为可选,旧客户端忽略即可 -2. **数据库字段**:所有新字段 `NOT NULL DEFAULT`,旧数据迁移后填充 -3. **事件 payload**:新增字段,旧消费者忽略未消费的字段 -4. **前端**:新字段展示为可选,无数据时不显示本位币列 - -### 9.2 迁移阶段 - -**阶段 1:数据库迁移(migration 064 + 065)** -- 064:表结构变更 -- 065:历史数据回填 -- 建议迁移期间系统只读 - -**阶段 2:代码部署** -- 部署新版本代码 -- 新创建订单自动接入多币种 -- 旧订单已迁移,读取时返回 base_* 字段 - -**阶段 3:验证** -- 单元测试 + 集成测试 -- 抽样验证历史订单的 base_* 金额 -- 监控事件消费者 - -### 9.3 风险与缓解 - -1. **历史数据回填查不到汇率** → fallback 到 1.0,日志记录供人工核对 -2. **事件消费者未更新** → Phase 2a 财务侧暂不消费 currency 字段,Phase 2c 同步更新 -3. **前端缓存旧版** → Next.js 构建哈希自动失效 -4. **对账单 `inv_inbound` 无 currency** → `COALESCE(i.currency, 'CNY')` 兼容 - -## 10. 范围边界 - -### 10.1 Phase 2a 范围内 - -- 4 张采购核心表(order / order_line / return / return_line)+ reconciliation + supplier -- 3 个聚合根 + 3 个实体 + 1 个值对象(CurrencySnapshot) -- 3 个 Application Service -- 6 个 API 路由 -- 5 个前端页面 -- 2 个 migration(064 + 065) -- i18n 扩展 - -### 10.2 Phase 2a 范围外 - -- `inv_inbound` 表的 currency 改造(仓库模块 Phase 2.x) -- `fin_payable` 表的 currency 改造(财务模块 Phase 2c) -- `pur_request` 采购申请的 currency 扩展(已有字段,仅小调整) -- 已废弃表(`pur_receipt_deprecated` 等) - -### 10.3 依赖关系 - -- **依赖 Phase 1**:`CurrencyApplicationService`、`Money.convertTo`、`sys_currency` / `sys_exchange_rate` 表 -- **被 Phase 2c 依赖**:采购事件 payload 携带 currency 字段 -- **被仓库模块 Phase 2.x 依赖**:采购订单 currency 信息传递给入库单 - -## 11. 验收标准 - -1. 创建 USD 采购订单,验证 currency=USD、exchangeRate=当日汇率、base_* 字段正确计算 -2. 创建 VND 采购订单,验证 decimalPlaces=0 的本位币金额为整数 -3. 尝试 PUT 修改 currency,验证返回 400 -4. 基于 USD 订单创建退货,验证 currency 继承为 USD、exchangeRate 继承为原值 -5. 创建对账单时关联不同 currency 的入库单,验证返回 400 + 明确错误消息 -6. 历史订单迁移后 base_* 字段非零且合理 -7. 前端采购订单列表正确展示双币种 -8. 所有单元测试和集成测试通过 diff --git a/docs/superpowers/specs/2026-07-17-multi-currency-phase2b-design.md b/docs/superpowers/specs/2026-07-17-multi-currency-phase2b-design.md deleted file mode 100644 index 20eac41e..00000000 --- a/docs/superpowers/specs/2026-07-17-multi-currency-phase2b-design.md +++ /dev/null @@ -1,479 +0,0 @@ -# 多币种支持 Phase 2b 设计文档 - -> 日期:2026-07-17 -> 状态:草稿 -> 范围:销售模块 + 库存/入库衔接 + 财务应收/应付 -> 基于:Phase 1(基础设施)+ Phase 2a(采购多币种)已完成 - -## 1. 范围总览 - -### 1.1 包含模块 - -| 模块 | 子范围 | 优先级 | -|------|--------|--------| -| 销售 | `sal_order`, `sal_order_detail`, `sal_delivery`, `sal_delivery_detail`, `sal_return`, `sal_return_detail`, `sal_reconciliation`, 相关聚合根/实体 | 高 | -| 库存 | `inv_inbound_order`, `inv_inbound_item`, `inv_outbound_order`, `inv_outbound_item` | 高 | -| 财务 | `fin_payable`, `fin_receivable`, `fin_payment_record`, `fin_receipt_record` | 高 | - -### 1.2 排除模块(延后处理) - -| 模块 | 原因 | -|------|------| -| 生产成本 (`prod_work_order` 等) | 成本核算通常只用本位币,无需多币种 | - -### 1.3 关键设计决策 - -| 决策 | 选项 | 结论 | -|------|------|------| -| 入库单币种来源 | 继承但允许覆盖 | 默认从 PO 继承 currency+exchange_rate,用户可手动修改 | -| 应收/应付币种来源 | 继承+手动调整 | 默认从来源单据继承,允许用户在特殊场景手动调整 | - -## 2. 数据库设计 - -### 2.1 精度规范 - -- 金额字段:`DECIMAL(18,4)` — 兼容 VND(大金额无小数)和 USD/CNY -- 汇率字段:`DECIMAL(18,6)` — 6 位小数精度 -- `base_amount` = 原币金额 × 汇率,创建时计算存储 -- 旧数据回填策略:currency=CNY, exchange_rate=1.0, base_amount=原金额 - -### 2.2 Migration 066 — 库存/入库衔接 - -```sql --- inv_inbound_order -ALTER TABLE `inv_inbound_order` - ADD COLUMN `currency` varchar(10) DEFAULT 'CNY' COMMENT '币种' AFTER `total_amount`, - ADD COLUMN `exchange_rate` decimal(18,6) DEFAULT 1.000000 COMMENT '汇率' AFTER `currency`, - ADD COLUMN `base_total_amount` decimal(18,4) DEFAULT 0.0000 COMMENT '本位币总金额' AFTER `exchange_rate`; - --- inv_inbound_item -ALTER TABLE `inv_inbound_item` - ADD COLUMN `base_unit_price` decimal(18,4) DEFAULT 0.0000 COMMENT '本位币单价' AFTER `unit_price`, - ADD COLUMN `base_amount` decimal(18,4) DEFAULT 0.0000 COMMENT '本位币金额' AFTER `amount`; - --- inv_outbound_order -ALTER TABLE `inv_outbound_order` - ADD COLUMN `exchange_rate` decimal(18,6) DEFAULT 1.000000 COMMENT '汇率' AFTER `currency`, - ADD COLUMN `base_total_amount` decimal(18,4) DEFAULT 0.0000 COMMENT '本位币总金额' AFTER `exchange_rate`; - --- inv_outbound_item -ALTER TABLE `inv_outbound_item` - ADD COLUMN `base_unit_price` decimal(18,4) DEFAULT 0.0000 COMMENT '本位币单价' AFTER `unit_price`, - ADD COLUMN `base_amount` decimal(18,4) DEFAULT 0.0000 COMMENT '本位币金额' AFTER `amount`; - --- 旧数据回填 -UPDATE `inv_inbound_order` SET `base_total_amount` = `total_amount` WHERE `base_total_amount` = 0; -UPDATE `inv_outbound_order` SET `base_total_amount` = `total_amount` WHERE `base_total_amount` = 0; -``` - -### 2.3 Migration 067 — 销售模块 - -```sql --- sal_order(已有 currency + exchange_rate) -ALTER TABLE `sal_order` - ADD COLUMN `base_total_amount` decimal(18,4) DEFAULT 0.0000 COMMENT '本位币金额' AFTER `total_amount`, - ADD COLUMN `base_tax_amount` decimal(18,4) DEFAULT 0.0000 COMMENT '本位币税额' AFTER `tax_amount`, - ADD COLUMN `base_grand_total` decimal(18,4) DEFAULT 0.0000 COMMENT '本位币含税总额' AFTER `total_with_tax`; - --- sal_order_detail -ALTER TABLE `sal_order_detail` - ADD COLUMN `currency` varchar(10) DEFAULT 'CNY' COMMENT '币种' AFTER `material_spec`, - ADD COLUMN `exchange_rate` decimal(18,6) DEFAULT 1.000000 COMMENT '汇率' AFTER `currency`, - ADD COLUMN `base_unit_price` decimal(18,4) DEFAULT 0.0000 COMMENT '本位币单价' AFTER `unit_price`, - ADD COLUMN `base_amount` decimal(18,4) DEFAULT 0.0000 COMMENT '本位币金额' AFTER `amount`, - ADD COLUMN `base_tax_amount` decimal(18,4) DEFAULT 0.0000 COMMENT '本位币税额' AFTER `tax_amount`; - --- sal_delivery -ALTER TABLE `sal_delivery` - ADD COLUMN `currency` varchar(10) DEFAULT 'CNY' COMMENT '币种' AFTER `total_amount`, - ADD COLUMN `exchange_rate` decimal(18,6) DEFAULT 1.000000 COMMENT '汇率' AFTER `currency`, - ADD COLUMN `base_total_amount` decimal(18,4) DEFAULT 0.0000 COMMENT '本位币总金额' AFTER `exchange_rate`; - --- sal_delivery_detail -ALTER TABLE `sal_delivery_detail` - ADD COLUMN `base_unit_price` decimal(18,4) DEFAULT 0.0000 COMMENT '本位币单价' AFTER `unit_price`, - ADD COLUMN `base_amount` decimal(18,4) DEFAULT 0.0000 COMMENT '本位币金额' AFTER `amount`; - --- sal_return -ALTER TABLE `sal_return` - ADD COLUMN `currency` varchar(10) DEFAULT 'CNY' COMMENT '币种' AFTER `total_amount`, - ADD COLUMN `exchange_rate` decimal(18,6) DEFAULT 1.000000 COMMENT '汇率' AFTER `currency`, - ADD COLUMN `base_total_amount` decimal(18,4) DEFAULT 0.0000 COMMENT '本位币总金额' AFTER `exchange_rate`; - --- sal_return_detail -ALTER TABLE `sal_return_detail` - ADD COLUMN `base_unit_price` decimal(18,4) DEFAULT 0.0000 COMMENT '本位币单价' AFTER `unit_price`, - ADD COLUMN `base_amount` decimal(18,4) DEFAULT 0.0000 COMMENT '本位币金额' AFTER `amount`; - --- sal_reconciliation(7 个 base_*) -ALTER TABLE `sal_reconciliation` - ADD COLUMN `currency` varchar(10) DEFAULT 'CNY' COMMENT '币种' AFTER `total_amount`, - ADD COLUMN `exchange_rate` decimal(18,6) DEFAULT 1.000000 COMMENT '汇率' AFTER `currency`, - ADD COLUMN `base_delivery_amount` decimal(18,4) DEFAULT 0.0000 COMMENT '本位币出库金额' AFTER `delivery_amount`, - ADD COLUMN `base_return_amount` decimal(18,4) DEFAULT 0.0000 COMMENT '本位币退货金额' AFTER `return_amount`, - ADD COLUMN `base_net_amount` decimal(18,4) DEFAULT 0.0000 COMMENT '本位币净额' AFTER `net_amount`, - ADD COLUMN `base_discount_amount` decimal(18,4) DEFAULT 0.0000 COMMENT '本位币折扣金额' AFTER `discount_amount`, - ADD COLUMN `base_received_amount` decimal(18,4) DEFAULT 0.0000 COMMENT '本位币实收金额' AFTER `received_amount`, - ADD COLUMN `base_balance_amount` decimal(18,4) DEFAULT 0.0000 COMMENT '本位币余额' AFTER `balance_amount`; - --- 旧数据回填 -UPDATE `sal_order` SET `base_total_amount` = `total_amount`, `base_tax_amount` = `tax_amount`, `base_grand_total` = `total_with_tax` WHERE `base_total_amount` = 0; -UPDATE `sal_delivery` SET `base_total_amount` = `total_amount` WHERE `base_total_amount` = 0; -UPDATE `sal_return` SET `base_total_amount` = `total_amount` WHERE `base_total_amount` = 0; -``` - -### 2.4 Migration 068 — 财务模块 - -```sql --- fin_payable -ALTER TABLE `fin_payable` - ADD COLUMN `currency` varchar(10) DEFAULT 'CNY' COMMENT '币种' AFTER `amount`, - ADD COLUMN `exchange_rate` decimal(18,6) DEFAULT 1.000000 COMMENT '汇率' AFTER `currency`, - ADD COLUMN `base_amount` decimal(18,4) DEFAULT 0.0000 COMMENT '本位币金额' AFTER `exchange_rate`; - --- fin_receivable -ALTER TABLE `fin_receivable` - ADD COLUMN `currency` varchar(10) DEFAULT 'CNY' COMMENT '币种' AFTER `amount`, - ADD COLUMN `exchange_rate` decimal(18,6) DEFAULT 1.000000 COMMENT '汇率' AFTER `currency`, - ADD COLUMN `base_amount` decimal(18,4) DEFAULT 0.0000 COMMENT '本位币金额' AFTER `exchange_rate`; - --- fin_payment_record -ALTER TABLE `fin_payment_record` - ADD COLUMN `currency` varchar(10) DEFAULT 'CNY' COMMENT '币种' AFTER `amount`, - ADD COLUMN `exchange_rate` decimal(18,6) DEFAULT 1.000000 COMMENT '汇率' AFTER `currency`, - ADD COLUMN `base_amount` decimal(18,4) DEFAULT 0.0000 COMMENT '本位币金额' AFTER `exchange_rate`; - --- fin_receipt_record -ALTER TABLE `fin_receipt_record` - ADD COLUMN `currency` varchar(10) DEFAULT 'CNY' COMMENT '币种' AFTER `amount`, - ADD COLUMN `exchange_rate` decimal(18,6) DEFAULT 1.000000 COMMENT '汇率' AFTER `currency`, - ADD COLUMN `base_amount` decimal(18,4) DEFAULT 0.0000 COMMENT '本位币金额' AFTER `exchange_rate`; - --- 旧数据回填 -UPDATE `fin_payable` SET `base_amount` = `amount`, `currency` = 'CNY', `exchange_rate` = 1.0 WHERE `base_amount` = 0; -UPDATE `fin_receivable` SET `base_amount` = `amount`, `currency` = 'CNY', `exchange_rate` = 1.0 WHERE `base_amount` = 0; -UPDATE `fin_payment_record` SET `base_amount` = `amount`, `currency` = 'CNY', `exchange_rate` = 1.0 WHERE `base_amount` = 0; -UPDATE `fin_receipt_record` SET `base_amount` = `amount`, `currency` = 'CNY', `exchange_rate` = 1.0 WHERE `base_amount` = 0; -``` - -### 2.5 Drizzle Schema 同步 - -新建/修改 `src/lib/db/schema.ts` 中对应表定义,追加新字段列,保持与 Phase 2a 模式一致。 - -## 3. 领域层设计 - -### 3.1 复用已有资产 - -| 资产 | 位置 | 状态 | -|------|------|------| -| `Money` 值对象(含 `convertTo`/`format`) | `src/domain/shared/value-objects/Money.ts` | Phase 1 完成 | -| `CurrencySnapshot` 值对象 | `src/domain/shared/value-objects/CurrencySnapshot.ts` | Phase 2a 完成 | -| `ICurrencyService` 接口 | `src/domain/shared/CurrencyService.ts` | Phase 1 完成 | -| `CurrencyApplicationService`(含缓存) | `src/application/services/CurrencyApplicationService.ts` | Phase 1 完成 | -| `MysqlCurrencyRepository` | `src/infrastructure/repositories/MysqlCurrencyRepository.ts` | Phase 1 完成 | - -### 3.2 SalesOrder 聚合根 - -**Props 接口修改:** -```typescript -export interface SalesOrderProps { - // ...原有字段... - currency?: string; - exchangeRate?: number; - baseCurrency?: string; - baseTotalAmount?: number; - baseTaxAmount?: number; - baseGrandTotal?: number; -} -``` - -**构造函数追加参数:** 与 `PurchaseOrder` 模式一致(7 个新参数:`currency`, `exchangeRate`, `baseCurrency`, `baseTotalAmount`, `baseTaxAmount`, `baseGrandTotal`)。 - -**getter / create / reconstitute:** 与 `PurchaseOrder` 完全一致。 - -### 3.3 SalesOrderLine 实体 - -```typescript -export interface SalesOrderLineProps { - // ...原有字段... - baseUnitPrice?: number; - baseAmount?: number; - baseTaxAmount?: number; - baseLineTotal?: number; -} -``` - -### 3.4 Delivery 聚合根 - -```typescript -export interface DeliveryProps { - // ...原有字段... - currency?: string; - exchangeRate?: number; - baseCurrency?: string; - baseTotalAmount?: number; -} -``` - -### 3.5 ReturnOrder 聚合根 - -```typescript -export interface ReturnOrderProps { - // ...原有字段... - currency?: string; - exchangeRate?: number; - baseCurrency?: string; - baseTotalAmount?: number; -} -``` - -### 3.6 Reconciliation 聚合根 - -```typescript -export interface ReconciliationProps { - // ...原有字段... - currency?: string; - exchangeRate?: number; - baseCurrency?: string; - baseDeliveryAmount?: number; - baseReturnAmount?: number; - baseNetAmount?: number; - baseDiscountAmount?: number; - baseReceivedAmount?: number; - baseBalanceAmount?: number; -} -``` - -### 3.7 InboundOrder 聚合根 - -```typescript -export interface InboundOrderProps { - // ...原有字段... - currency?: string; - exchangeRate?: number; - baseCurrency?: string; - baseTotalAmount?: number; -} -``` - -### 3.8 Payable 聚合根 - -```typescript -export interface PayableProps { - // ...原有字段... - currency?: string; - exchangeRate?: number; - baseAmount?: number; -} -``` - -### 3.9 Receivable 聚合根 - -```typescript -export interface ReceivableProps { - // ...原有字段... - currency?: string; - exchangeRate?: number; - baseAmount?: number; -} -``` - -### 3.10 领域事件扩展 - -每个聚合根的 `CreatedEvent` 追加对应可选字段(与 Phase 2a 模式一致,向后兼容): -- `SalesOrderCreatedEvent` → 追加 `currency`, `exchangeRate`, `baseCurrency`, `baseTotalAmount`, `baseGrandTotal` -- `DeliveryCreatedEvent` → 追加 `currency`, `exchangeRate`, `baseCurrency`, `baseTotalAmount` -- `ReturnOrderCreatedEvent` → 追加 `currency`, `exchangeRate`, `baseCurrency`, `baseTotalAmount` -- `ReconciliationCreatedEvent` → 追加 `currency`, `exchangeRate`, `baseCurrency`, 3-5 个 base_* 字段 -- `PayableCreatedEvent` → 追加 `currency`, `exchangeRate`, `baseAmount` - -## 4. 应用服务层设计 - -### 4.1 SalesApplicationService - -**构造函数注入:** -```typescript -constructor( - private readonly orderRepo: ISalesOrderRepository, - private readonly currencyService: CurrencyApplicationService, -) {} -``` - -**createOrder 方法:** -- 确定 currency:输入 > 系统默认(`finance.default_currency`) -- 查询本位币(`finance.base_currency`) -- 若 currency ≠ baseCurrency,调用 `currencyService.getLatestRate` 获取汇率 -- 构建 `CurrencySnapshot` 固化汇率 -- 为每个明细行计算 base_* 字段 -- 计算主表 base_total_amount / base_tax_amount / base_grand_total - -**createDelivery:** -- 从 `sal_order` 继承 currency + exchangeRate(只读,不允许覆盖),与采购退货相同模式 - -**createReturn:** -- 从原 `sal_delivery` 或 `sal_order` 继承 currency + exchangeRate(只读) - -**createReconciliation:** -- 查询关联出库单 -- 校验同币种一致性(与 `PurchaseReconciliationApplicationService` 相同模式) - -### 4.2 InboundApplicationService - -**构造函数注入 `CurrencyApplicationService`、`IPurchaseOrderRepository`(可选):** - -**createInbound:** -- 若关联 PO(`purchase_order_id`),从 PO 继承 currency + exchangeRate,允许用户覆盖 -- 若无 PO,默认 CNY -- 为明细行计算 base_* 字段 - -**createOutbound:** -- 若关联 SO(`sales_order_id`),从 SO 继承 currency + exchangeRate,允许覆盖 -- 若无 SO,默认 CNY - -### 4.3 FinanceApplicationService - -**构造函数注入 `CurrencyApplicationService`:** - -**createPayable(来源 = 采购入库单 + 采购单):** -- 从来源单继承 currency + exchangeRate -- 允许用户手动调整(应用服务层覆盖) -- 若手动调整 currency,重新计算 exchangeRate 和 baseAmount - -**createReceivable(来源 = 销售出库单 + 销售单):** -- 同上 - -**recordPayment / recordReceipt:** -- 继承对应应付/应收的 currency + exchangeRate -- 计算 baseAmount - -## 5. 基础设施层设计 - -### 5.1 Repository 改造列表 - -| Repository 文件 | 改造内容 | -|---|---| -| `MysqlSalesOrderRepository.ts` | INSERT/SELECT/mapToProps 追加 base_* 字段 | -| `MysqlDeliveryRepository.ts` | 同上 | -| `MysqlReturnOrderRepository.ts` | 同上 | -| `MysqlReconciliationRepository.ts` | 同上(7 个 base_*) | -| `MysqlInboundOrderRepository.ts` | 新增 currency + exchangeRate + base_total_amount | -| `MysqlOutboundOrderRepository.ts` | 新增 exchange_rate + base_total_amount | -| `MysqlPayableRepository.ts` | 新增 currency + exchangeRate + base_amount | -| `MysqlReceivableRepository.ts` | 新增 currency + exchangeRate + base_amount | - -### 5.2 改造模式 - -每个 Repository 的改造分 3 步(与 Phase 2a 完全一致): -1. Row 接口追加新字段类型定义 -2. INSERT/SELECT SQL 追加新列 -3. `mapToProps` 方法读取新字段 - -## 6. API 路由设计 - -### 6.1 销售模块 - -| 路由文件 | 操作 | -|---|---| -| `src/app/api/sales/orders/route.ts` | GET 响应追加 base_* 字段;POST 接收 currency;PUT 拒绝 currency 修改 | -| `src/app/api/sales/delivery/route.ts` | GET 响应追加 currency+exchange_rate+base_total_amount;POST 不接收 currency(从 SO 继承) | -| `src/app/api/sales/return/route.ts` | GET 响应追加;POST 不接收 currency(从源单据继承) | -| `src/app/api/sales/reconciliation/route.ts` | GET 响应追加;POST 包装 DomainError 同币种校验 | - -### 6.2 库存模块 - -| 路由文件 | 操作 | -|---|---| -| `src/app/api/inventory/inbound/route.ts` | GET 响应追加;POST 接收可选 currency(默认从 PO 继承) | -| `src/app/api/inventory/outbound/route.ts` | GET 响应追加;POST 接收可选 currency | - -### 6.3 财务模块 - -| 路由文件 | 操作 | -|---|---| -| `src/app/api/finance/payable/route.ts` | GET 响应追加 currency+exchange_rate+base_amount;POST 接收 currency | -| `src/app/api/finance/receivable/route.ts` | GET 响应追加;POST 接收 currency | -| `src/app/api/finance/payment/route.ts` | GET 响应追加;POST 继承应付 currency | -| `src/app/api/finance/receipt/route.ts` | GET 响应追加;POST 继承应收 currency | - -## 7. 前端设计 - -### 7.1 复用组件 - -| 组件 | 用途 | -|---|---| -| `` | 双币种金额展示 | -| `` | 币种选择器 | -| `formatMoney()` | 金额格式化工具 | - -### 7.2 页面改造列表 - -| 页面 | 改造内容 | -|---|---| -| 销售订单列表/创建页 | 加币种列 + `MoneyDisplay` + `CurrencySelect` | -| 销售订单详情页 | 加币种信息卡 + 明细双币列 | -| 销售出库页 | 显示继承币种(只读)+ `MoneyDisplay` | -| 销售退货页 | 继承币种显示 + 双币金额 | -| 销售对账页 | 同币种校验提示 + 双币金额 | -| 入库管理页 | 从 PO 获取 currency 展示 + 可选覆盖 | -| 应付/应收页 | 币种列 + `MoneyDisplay` + 来源单号链接 | - -### 7.3 i18n 扩展(约 12 个新 key) - -| Key | 中文 | English | -|---|---|---| -| `sales.baseCurrencyAmount` | 本位币金额 | Base Currency Amount | -| `sales.inheritedCurrency` | 继承币种 | Inherited Currency | -| `inventory.inboundCurrency` | 入库币种 | Inbound Currency | -| `finance.payableCurrency` | 应付币种 | Payable Currency | -| `finance.receivableCurrency` | 应收币种 | Receivable Currency | -| `common.allowOverride` | 允许修改 | Allow Override | -| `common.currencyConsistencyError` | 单据币种不一致 | Document Currency Mismatch | - -## 8. 错误处理 - -| 场景 | 处理方式 | -|---|---| -| 汇率未配置 | `CurrencyApplicationService.getLatestRate` 抛出明确错误,前端 toast 提示 | -| Redis 不可用 | 降级为内存 Map 缓存(复用现有降级模式) | -| 币种停用 | 已存在单据保留原币种,新建单据不能选择已停用币种 | -| 对账币种不一致 | `DomainError` + 400 HTTP + 明确错误消息(同 Phase 2a) | -| 币种创建后修改 | PUT 处理程序拒绝 `currency`/`exchange_rate` 字段(同 Phase 2a) | - -## 9. 测试策略 - -### 9.1 单元测试 - -- `CurrencySnapshot` 值对象 — 已有 10 测试 -- `SalesOrder` 聚合根 — base_* 字段创建/读取 -- `SalesApplicationService.createOrder` — 多币种换算 - -### 9.2 集成测试 - -- 各 Repository — INSERT/SELECT 新字段正确读写 -- `CurrencyApplicationService` — 缓存命中/失效 - -### 9.3 回归测试 - -- 现有 1382 测试 — 确认无新失败(已知 6 个预存失败不受影响) - -## 10. 旧数据迁移说明 - -所有历史数据按原金额视为 CNY(exchange_rate=1.0),base_amount = 原金额: -- 这保证了财务汇总的一致性(历史数据本位币 = 原金额) -- 新增业务按实时汇率换算 -- 不改已有数据,仅追加新列并设默认值 - -## 11. 估算 - -| 工作项 | 文件数 | 估算 | -|--------|--------|------| -| DB migrations (3 个) | 3 | 小 | -| Drizzle schema sync | 1 | 小 | -| 领域层 (7 聚合根 + 6 实体 + 7 事件文件) | ~20 | 中 | -| Repository (8 个) | 8 | 中 | -| Application Service (3 个) | 3 | 中 | -| API routes (9 个) | 9 | 小 | -| 前端页面 (7 个) | 7 | 中 | -| i18n | 4 | 小 | -| 测试 | ~10 | 中 | -| **合计** | **~65** | **大(约 2-3 倍 Phase 2a 工作量)** | diff --git a/docs/warehouse-upgrade-execution-plan.md b/docs/warehouse-upgrade-execution-plan.md deleted file mode 100644 index 5d062ef0..00000000 --- a/docs/warehouse-upgrade-execution-plan.md +++ /dev/null @@ -1,187 +0,0 @@ -# vnerp 仓储体系升级 · 全量执行计划 - -> 制定日期:2026-08-16 -> 依据:用户贴出的「vnerp 仓储体系升级全量 To-Do 清单」(源自 github.com/fjykTec/ModernWMS),结合**真实库 `vnerpdacahng` 实测状态**编制。 -> 执行原则:**基础先行 → 核心落地 → 执行适配 → 进阶扩展** -> 技术栈:Next.js 16 + Drizzle ORM 0.45 + MySQL 8.0 + TypeScript(DDD 分层:domain / application / api / page) - ---- - -## 0. 既有约定(必须遵循,否则会踩已知坑) - -| 约定 | 说明 | 来源 | -|------|------|------| -| **迁移走裸 SQL,不用 drizzle-kit** | 库由 init 脚本建,`scripts/migrate.ts up` 执行 `database/migrations/*.sql` 并记录 `sys_migration`。**严禁跑 `drizzle-kit push/generate`** —— Drizzle 不知情真实库 129 个外键,会全部 DROP。 | 先前唯一约束整改复盘 | -| **软删除 = `deleted TINYINT NOT NULL DEFAULT 0`** | 复合唯一键必须含 `deleted` 列(NULL 语义不适用)。 | 全项目统一 | -| **唯一冲突统一转译** | `src/lib/db/errors.ts` 的 `isUniqueViolation` + 中央 handler 已把 1062 → 409 友好中文,业务层无需各自处理。 | 1-7 已完成 | -| **live 表名单数,Drizzle 注册复数** | 真实表 `inv_inbound_order`;Drizzle 里 `invInboundOrders`。很多真实表 Drizzle 根本未建模(见 Phase 风险)。 | 先前核对 | -| **四件套 + `version` 乐观锁** | 高并发写用 `transactionWithRetry`。 | 全项目统一 | -| **事件总线已就绪** | `DomainEventOutbox → OutboxPoller → EventBus → Handler`,5s 轮询。跨模块联动优先走事件,别写同步 RPC。 | 采购入库联动已验证 | - ---- - -## 1. 真实进度审计(连 live 库实测,非假设) - -| 阶段 | 整体状态 | 关键实测事实 | -|------|---------|-------------| -| **一、唯一约束(P0)** | **≈78% 完成** | 1-1/1-4/1-5/1-6/1-7/1-8/1-9 已在 8/13 通过 7 个幂等迁移 + `errors.ts` + `check-duplicate-data.mjs` 落地。全库现有 **123 个唯一索引**。仅剩 **1-2、1-3**。 | -| **二、FIFO + 效期(P0)** | 基础有 / 业务 TODO | FIFO 路由 `/api/warehouse/outbound/fifo` 已存在;批次表 `inv_inventory_batch` 已有 `produce_date / expire_date / inbound_date / status / alert_level`(**缺「保质期天数」列**)。2-2~2-9 全未做。 | -| **三、分切(P1)** | TODO | `inv_cutting_record` 表已存在(有 `uk_record_no`),母子码/分切事务/追溯需新建。 | -| **四、二维码 + 标签(P1)** | 部分 | `QRCodeScanner` 组件 + `/api/dcprint/scan` + 标签打印已有基础;全环节闭环 TODO。 | -| **五、Pad 端(P2)** | TODO | — | -| **六、ModernWMS 整合(P3)** | TODO | 外部系统,需定接口契约。 | -| **七、印前整合(P1)** | TODO | `prd_ink`/`base_ink`/`ink_mixed_batch`/`prd_die`/`prd_screen_plate`/`dcprint_tool` 等表已有基础。 | - ---- - -## 2. 阶段一:基础数据治理(唯一约束整改)收尾 - -> P0 前置必做。除 1-2、1-3 外全部已完成,本阶段只需补完这两块。 - -### 1-3 库存流水唯一索引 —— **可立即安全执行** -- **实测**:`inv_inventory_transaction` 当前 **0 行、0 重复**;列 `source_type(varchar20) / source_id(bigint) / source_line_id(bigint)` 齐全。 -- **做法**:幂等 SQL 加 `uk_inv_transaction_source(source_type, source_id, source_line_id)`,记 `sys_migration`。 -- ⚠️ **建索引前需核查写入路径**:确认业务不会在同一 `(source_type, source_id, source_line_id)` 写多行(如「数量行 + 成本行」分开记)。若存在,索引需加判别列(如 `trans_sub_type`)或改为「单来源单行」。 -- **验收**:同单据同类型同号无法重复生成流水;账实可追溯。 - -### 1-2 扫码出库记录唯一索引 —— **已定方案:扩展 `inv_scan_log`** -- **实测**:真实库**无专用「扫码出库记录表」**。`inv_scan_log` 现有 `scan_type / qr_content / label_no / operation / operator_id`,**无出库单 ID 列**;`qrcode_record` 是通用记录表。 -- **已选方案**(用户确认):给 `inv_scan_log` 加 `outbound_order_id BIGINT UNSIGNED` 列 + 唯一索引 `uk_scan_outbound(outbound_order_id, label_no)`。 -- **配套改造**(非纯 DB): - 1. `/api/dcprint/scan`(scanType='outbound')及出库扫码闭环需把当前**出库单 ID** 写入 `inv_scan_log.outbound_order_id`。 - 2. 写入前查重:同 `(outbound_order_id, label_no)` 已存在则拦截(复用 `errors.ts` 1062→409)。 -- **验收**:同一出库单同一二维码无法重复扫码扣库,从 DB 层杜绝重复扣减。 - ---- - -## 3. 阶段二:FIFO 先进先出 + 批次效期全管控(P0 核心) - -| 任务 | 做法要点 | 当前状态 | -|------|---------|---------| -| 2-1 批次效期字段 | `inv_inventory_batch` 补 `shelf_life_days INT`(保质期天数,用于自动算过期日);`produce/expire/inbound_date/status` 已有 | 部分(缺天数) | -| 2-2 采购入库强制效期 | 入库 API 校验 `produce_date + shelf_life_days` 必填,自动算 `expire_date`;缺失禁止入库、禁止生成二维码 | TODO | -| 2-3 FIFO 引擎重构 | `planFIFOAllocation` 统一按 `inbound_date` 升序,过滤 `expire_date<今天` 与 `status=冻结/隔离`;支持母/子批次混合(见阶段三) | 基础有,需增强 | -| 2-4 生产领料 FIFO 推荐 | 建领料单时调 FIFO 路由,拆分最早批次,展示 批次号/入库日期/库位/应领量 | TODO | -| 2-5 扫码出库 FIFO 合规 | 扫非最早批次弹黄色提醒;强制模式无授权不可领 | TODO | -| 2-6 过期批次强拦截 | 扫过期批次直接拦截,禁止任何出库 | TODO | -| 2-7 效期状态定时任务 | 每日巡检,按规则更新 `status`/`alert_level`(正常/临期/过期) | TODO | -| 2-8 效期预警 | 首页看板统计卡片 + 定时推送临期清单 | TODO | -| 2-9 报表 | 效期库存报表、FIFO 执行合规报表(非 FIFO 领用记录) | TODO | - -**依赖**:2-1 先于 2-2/2-3;2-3 是 2-4/2-5/2-6 的基础。 - ---- - -## 4. 阶段三:分切作业全流程 + 母子码 FIFO 继承(P1 行业核心) - -| 任务 | 做法要点 | -|------|---------| -| 3-1 批次扩字段 | `inv_inventory_batch` 加 `batch_type(母/子) / parent_batch_id / original_inbound_date / cutting_date` | -| 3-2 分切工单/明细表 | 新建 `inv_cutting_order` + `inv_cutting_detail`,配套唯一约束(分切单号全局唯一、母子关联可查) | -| 3-3 子批次继承 | 分切时 100% 继承母材 `original_inbound_date / produce_date / expire_date / supplier_id`,不重置保质期、不改变 FIFO 基准 | -| 3-4 FIFO 引擎适配 | 母+子按 `original_inbound_date` 混合升序参与扣减 | -| 3-5 分切全事务 | 单事务包裹:母材扣减 + 子材入库 + 损耗记账 + 流水 + 子码生成,任一步失败全回滚 | -| 3-6 作废逆向回滚 | 子材作废 + 母材数量恢复 + 流水冲销,原路还原留痕 | -| 3-7 数量校验 | `Σ子材量 + 损耗 = 母材分切量`,不匹配禁止完工 | -| 3-8 全链路追溯 | 母码查所有子卷流向,子码溯源母批次与采购单 | -| 3-9 报表 | 分切损耗率、完工统计 | - -**依赖**:3-1/3-2 → 3-3/3-4 → 3-5/3-6/3-7 → 3-8/3-9。需阶段二 2-3 FIFO 引擎先支持母子混合。 - ---- - -## 5. 阶段四:二维码全环节落地 + 标准化标签打印(P1 执行落地) - -| 任务 | 做法要点 | -|------|---------| -| 4-1 统一编码规则 | `批次类型+批次ID+物料ID+校验位`,全局唯一,复用阶段一唯一约束成果 | -| 4-2/4-3 标签模板 | 母材入库标签、分切子材标签(明确标注母批次号+原始入库日期+FIFO 基准) | -| 4-4~4-7 扫码闭环 | 采购入库 / 分切 / 生产领料 / 盘点调拨 四环节,复用 `QRCodeScanner`+`/api/dcprint/scan`,扫码自动校验 FIFO 与效期 | -| 4-8 打印能力 | 自动/单张补打/批量打印,适配热敏标签机 | - -**已有基础**:`QRCodeScanner`(@zxing/browser,camera 模式已修可用)、`/api/dcprint/scan`、标签打印模块。 - ---- - -## 6. 阶段五:Pad + 扫描枪移动作业端(P2 体验优化) - -- 5-1 Pad 响应式(大按钮/大字体/高对比/三色状态);5-2 扫描枪键盘模拟输入兼容;5-3~5-6 Pad 端采购入库/分切/领料/盘点调拨页;5-7 三色交互(绿合规/黄提醒/红拦截)。 -- **依赖**:阶段二~四的业务能力先就位,Pad 端只是适配层。 - ---- - -## 7. 阶段六:ModernWMS 整合(P3 长期) - -- 6-1 双层定位:vnerp=业务财务中枢,ModernWMS=仓储执行层。 -- 6-2 主数据同步(物料/供应商/客户/仓库/库位由 vnerp 下发)。 -- 6-3 核心单据对接(采购入库/销售出库/生产领料审核后下发,执行结果回传)。 -- 6-4 事件总线替换同步接口(已有 OutboxPoller 可直接复用)。 -- 6-5 库位四级体系(仓-区-架-位)+ 上架策略。 -- 6-6 波次拣选/智能上架;6-7 可视化看板+热力图;6-8 ABC 分类/呆滞料分析。 - -**注意**:外部系统,需先定接口契约与鉴权,建议 Phase 五完成后启动。 - ---- - -## 8. 阶段七:印前模块(网板/油墨/刀模)整合 ModernWMS(P1) - -沿用「vnerp 管工艺 + WMS 管实物」双层(详见用户原方案)。 - -| 任务 | 要点 | -|------|------| -| 7-1 WMS 档案扩展 | 油墨/刀模/网板专属属性字段 | -| 7-2 编号唯一 | 三类编号全局唯一 + 唯一索引(复用阶段一模式) | -| 7-3 油墨 | 完全复用原材料 FIFO+效期(最成熟,先做) | -| 7-4 刀模 | 一物一码 + 寿命累计(设计寿命/累计次数,80%黄提醒/100%红拦截)+ 修模报废 | -| 7-5 网板 | 一物一码 + 张力+次数双管控(张力阈值拦截、张检记录) | -| 7-6 三套标签模板 | 油墨/刀模/网板,支持批量+补打 | -| 7-7 Pad 三作业页 | 油墨/刀模/网板各作业入口 | -| 7-8/7-9 双向对接 | 主数据同步 + 单据对接回传 | -| 7-10 印前工装看板 | 在库/寿命预警/临期油墨/待报废统计 | - -**执行顺序建议**:油墨(复用原材料)→ 刀模(工装通用逻辑)→ 网板(张力扩展,最复杂)。 - ---- - -## 9. 跨阶段依赖与建议排期 - -``` -阶段一(收尾 1-2/1-3) ──► 阶段二(FIFO+效期) ──┐ - │ ├─► 阶段四(二维码+标签) ─► 阶段五(Pad) - └─► 阶段三(分切, 依赖 2-3) ◄───────────────┘ - │ - 阶段六(ModernWMS) ─ 阶段七(印前) -``` - -- **第一阶段(1~2 周)**:收尾阶段一 + 阶段二(数据一致性 + 核心业务规则)。 -- **第二阶段(2~3 周)**:阶段三(分切)+ 阶段四(二维码+标签)。 -- **第三阶段(1 周)**:阶段五(Pad 端)。 -- **第四阶段(长期)**:阶段六 + 阶段七(ModernWMS 整合 + 印前)。 - ---- - -## 10. 风险与护栏 - -1. **🔴 drizzle-kit 误删外键**:任何 schema 改动走裸 SQL 迁移,**绝不**跑 `drizzle-kit push/generate`(会删 129 个 FK)。 -2. **数据孤儿**:加唯一索引前先跑重复检测(参考 `scripts/check-duplicate-data.mjs`)。1-3 已实测 0 行安全;1-2 为新列不涉及历史重复。 -3. **并发扣减**:库存写用 `transactionWithRetry` + 行锁,FIFO 扣减需 `SELECT ... FOR UPDATE` 锁批次行。 -4. **Drizzle 建模缺口**:财务 `fin_voucher*`、印前 `prd_ink/die` 等表 Drizzle 未建模,涉及这些表的查询仍走 `db.query` raw SQL,新增逻辑优先复用既有仓储而非强行 Drizzle 化。 -5. **扫码幂等**:出库扫码除 DB 唯一索引外,应用层对「同单同码」请求做前置拦截(参考阶段一 1-7 模式)。 - ---- - -## 11. 验收与回归门禁 - -- **类型门**:每次改动后 `npx tsc --noEmit` 必须 EXIT 0(`next build` 本机 hang,用 tsc 兜底)。 -- **数据门**:涉及唯一索引的迁移,先 dry-run 脏数据脚本确认 0 重复再执行。 -- **端到端门**:核心链路(采购入库→FIFO 推荐→扫码出库→库存流水)用真实标签 + 真事件总线冒烟,轮询 >5s 再断言,DECIMAL 用 `Number()` 比较(先前踩过的坑)。 -- **迁移幂等**:所有 `.sql` 用 `information_schema` 探测 + `IF NOT EXISTS`/重复忽略,可复跑。 - ---- - -## 12. 待确认/开放项 - -- [ ] 1-3 写入路径核查(是否存在单来源多行)— 决定索引是否加判别列。 -- [ ] 阶段二 2-1「保质期天数」列命名与单位(天?月?)待定。 -- [ ] 阶段六 ModernWMS 接口契约与鉴权方案(启动前定)。 -- [ ] 印前三类物资是否全部下沉 WMS,还是仅油墨先行(原方案建议油墨先做)。 diff --git "a/docs/\345\205\250\345\272\223-Drizzle-\350\246\206\347\233\226\347\216\207\344\270\223\351\241\271.md" "b/docs/\345\205\250\345\272\223-Drizzle-\350\246\206\347\233\226\347\216\207\344\270\223\351\241\271.md" deleted file mode 100644 index 5bee7587..00000000 --- "a/docs/\345\205\250\345\272\223-Drizzle-\350\246\206\347\233\226\347\216\207\344\270\223\351\241\271.md" +++ /dev/null @@ -1,265 +0,0 @@ -# 全库 Drizzle 覆盖率专项 - -> 建立日期:2026-08-26 | 状态:基线已建立,等待执行策略确认 -> 关联提交:`2b2b309d`(safe-db-push 护栏 + 配置注释校正,已推 origin/main) -> 发现脚本:`scripts/_audit/coverage_discovery.cjs`(只读,可复跑) -> 基线数据:`scripts/_audit/coverage_discovery.json` - -## 1. 背景与目标 - -`db:push` 已被 `scripts/safe-db-push.cjs` 包裹:任何会 **DROP 未建模表/列/外键** 的操作都会直接 `ABORT(exit 1)`。 -护栏只是"止血"——根本问题是 **Drizzle schema 未覆盖全库**,导致 `db:push`(以及残缺 schema 上的 `db:generate`)始终存在把真实结构改写/误删的高危。 - -本专项目标:**让 db:push 未来"可能安全"**——要么把 Drizzle 覆盖到所有真实核心表,要么清掉所有无关残留表,使覆盖率分母收敛到"只含业务表"。 - -## 2. 基线(2026-08-26 只读发现) - -| 指标 | 值 | -|------|-----| -| 真实库总表 | 327 | -| Drizzle 已建模 | 133 | -| **整体覆盖率** | **41%** | -| 残留备份表(_bak* / _ghost_backup_*) | 88 | -| **未建模核心表(真正缺口)** | **106** | - -### 域覆盖率矩阵(按 total 降序,备份表已剔除出核心缺口) - -| 域 | total | modeled | 覆盖率 | 未建模核心 | 备份表 | -|----|------:|--------:|------:|----------:|------:| -| inv | 82 | 38 | 46% | 0 | 44 | -| prd | 37 | 24 | 65% | 4 | 9 | -| sys | 28 | 14 | 50% | 13 | 1 | -| sal | 27 | 9 | 33% | 11 | 7 | -| pur | 25 | 6 | 24% | 9 | 10 | -| hr | 24 | 13 | 54% | 6 | 5 | -| fin | 14 | 2 | 14% | 9 | 3 | -| qc | 8 | 0 | 0% | 5 | 3 | -| eqp | 6 | 0 | 0% | 6 | 0 | -| crm | 5 | 0 | 0% | 5 | 0 | -| ink | 5 | 0 | 0% | 5 | 0 | -| material | 5 | 0 | 0% | 5 | 0 | -| outsource | 5 | 0 | 0% | 5 | 0 | -| …其余 19 个域 | 1–3 | 0–2 | 0–100% | 1–3 | 0–2 | - -- `dcprint / org / qrcode / split / label / saga` 已 100% 覆盖。 -- `qc / eqp / crm / ink / material / outsource / qms / eng / eq / plm / bom / srm / base / biz / domain / mdm / qr / work` 覆盖率 0%,多为未接入 Drizzle 的独立小模块。 -- 88 张备份表命名规律:`_bak_20260816_gen` / `_bak_20260817` / `_bak_chain_20260817` / `_bak_pur_20260817` / `_bak_inv_20260817b` / `_ghost_backup_inv_*`,均为 2026-08 多次"重建采购链路 / 库存回填 / 分切重建"产生的临时快照,业务代码不再读写。完整清单见 `coverage_discovery.json.backupTables`。 - -## 3. 两条路径 - -### 路径 A:建模补齐(增覆盖,安全,可逆) -- 复用已验证流程:**live `information_schema` → 生成 `_gen_.ts` → `schema.ts` 重导出 → `npx tsc --noEmit` 校验**。 -- 每张表处理:列对齐、索引(唯一键/普通索引)、内部 FK;**跨域 FK 暂缓声明**(避免前向引用 TDZ 与 `fk_` 命名冲突,与仓库域做法一致)。 -- 优先顺序(业务价值 + FK 依赖度):`sys`(系统参数/基础数据)→ `pur`(采购)→ `sal`(销售)→ `fin`(财务)→ 其余小域。 -- 工作量:约 **106 张核心表** + 后续跨域 FK 校准(约 81 条跨域 FK 待补,见历史记忆)。 - -### 路径 B:清理残留备份表(减真实表数,破坏性) -- 88 张 `_bak*` / `_ghost_backup_*` 表清理后,真实表数 327 → 239,且这些表永不进入 Drizzle,覆盖率分母显著缩小。 -- **执行前置条件(缺一不可)**: - 1. 全库 `mysqldump` 物理备份; - 2. 确认这些表无外键被任何核心表引用(FK 反向检查); - 3. 逐域 `DROP` 或 `RENAME` 到独立归档库。 -- ⚠️ **破坏性操作,必须用户显式确认并先完成备份**,不可自动执行。 - -## 4. 推荐执行顺序 - -1. **路径 B 安全预检(只读,无破坏)**:列出 88 张备份表的行数 + 是否被核心表 FK 引用,产出风险评级清单。→ 见任务 T1。 -2. 用户确认后 **执行路径 B 清理**(带备份)。→ 见任务 T2。 -3. 并行/随后 **推进路径 A 按域建模**,从 `sys` 起步。→ 见任务 T3–T6。 -4. 跨域 FK 校准。→ 见任务 T7。 -5. 覆盖率复测 + 评估"能否解除 db:push 护栏"。→ 见任务 T8。 - -## 5. 风险与护栏约束 - -- `scripts/safe-db-push.cjs` **保持常驻**,任何路径下都不可移除,直到确认无 DROP 风险。 -- `db:push-raw`(裸 `drizzle-kit push`)仅在专项显式声明"已全量覆盖 / 无 DROP 风险"时方可使用。 -- 路径 A 生成的 `_gen_*.ts` 属脚手架代码,命名/类型与 live 对齐前不得用于 `db:generate`,否则仍可能产出 DROP 语句。 - -## 6. 任务清单(见项目 TaskList) - -- T1 路径B 安全预检(88 张备份表引用关系 + 行数,产出清理清单) -- T2 路径B 清理执行(备份后 DROP / 归档,需用户确认) -- T3 路径A 建模 sys_ 域(13 张未建模核心) -- T4 路径A 建模 pur_ 域(9 张) -- T5 路径A 建模 sal_ 域(11 张) -- T6 路径A 建模 fin_ + 其余小域(约 73 张) -- T7 跨域 FK 校准(约 81 条) -- T8 覆盖率复测 + 护栏解除评估 - -## 7. T1 预检结果(2026-08-26,结论:100% 可安全清理) - -只读核查脚本:`scripts/_audit/backup_precheck.cjs` | 证据:`scripts/_audit/backup_precheck.json` - -**三重核查全部通过:** - -| 核查项 | 方法 | 结果 | -|--------|------|------| -| DB 反向外键 | `KEY_COLUMN_USAGE` 查 REFERENCED_TABLE_NAME=备份表 | **0 张**被核心表引用(88 张全 SAFE,CAUTION=0 / LOW=0) | -| 应用代码引用 | `src/` Grep `_ghost_backup`/`_bak_2026`/`_bak_chain`/`_bak_pur`/`_bak_inv`/`_bak_gen` | **0 命中** | -| 触发器写入 | `information_schema.TRIGGERS` 全库扫描 | **0 个**触发器(任何表都不会被触发写入) | -| 数据量 | `TABLE_ROWS` 聚合 | 88 张合计 **~1595 行**,最大 127 行,30 张为空 —— 纯小快照 | - -**按域分布(共 88 张):** inv 44 · pur 10 · prd 9 · sal 7 · hr 5 · fin 3 · qc 3 · prod 3 · sys 1 · eng 1 · `_ghost_backup_` 2 - -**结论:** 88 张备份表是 2026-08 多次"重建采购链路 / 库存回填 / 分切重建"操作产生的临时快照,**完全孤立**(无 FK、无代码、无触发器、几乎无数据)。DROP / 归档不会破坏任何业务结构或数据,路径 B 可在「**全库 mysqldump 物理备份 + 用户显式确认**」后执行。 - -**T2 推荐做法(可逆优先):** 先做全库 `mysqldump` 备份,再将 88 张 `RENAME` 到独立归档库(如 `vnerp_archive`),从活动库移除但随时可恢复;若确认无碍再考虑硬 `DROP`。 - -## 8. T2 清理执行结果(2026-08-26,✅ 完成) - -- **物理备份**:`D:/dcprint/db_backups/vnerpdacahng_20260826_0853_before_bak_cleanup.sql`(3.8MB,全库 `mysqldump`,置于仓库外目录,**不入库**)。 -- **执行**:`CREATE DATABASE vnerp_archive` + 单条 `RENAME TABLE` 原子移动 88 张备份表(任一失败全回滚,未动任何源表)。 -- **校验**:归档库 `vnerp_archive` 88 张 / 源库 `vnerpdacahng` 残留 0 张 ✅。 -- **效果(重跑覆盖率发现 `coverage_discovery.cjs`)**: - -| 指标 | 清理前 | 清理后 | -|------|------:|------:| -| 真实库总表 | 327 | **239** | -| 残留备份表 | 88 | **0** | -| Drizzle 整体覆盖率 | 41% | **56%** | -| 未建模核心表(真实缺口) | 106 | 106(不变) | - -- `inv` 域因移除 44 张 `_bak*`,覆盖率 **46% → 100%**。 -- **完全可逆**:恢复只需 `RENAME TABLE vnerp_archive.x TO vnerpdacahng.x`,或用备份文件 `mysql` 还原。 -- **下一步**:路径 A 按域建模补齐 106 张未建模核心表(优先级 `sys`→`pur`→`sal`→`fin`→小域),见 T3–T6。 - -## 9. T3 sys_ 域建模结果(2026-08-26,✅ 完成) - -生成器:`scripts/_audit/gen_sys.cjs`(复用 `gen_warehouse_missing.cjs` 的列/类型/索引/拓扑排序逻辑,针对 13 张未建模核心表)| 产物:`src/lib/db/schemas/_gen_sys.ts` + `schema.ts` 接入。 - -**13 张表**(均来自 live `information_schema`,列数与 live 逐一核对一致): -`sys_announcement` · `sys_announcement_read` · `sys_calc_param` · `sys_company` · `sys_dict_data` · `sys_dict_type` · `sys_event_processed` · `sys_migration` · `sys_notice` · `sys_oper_log` · `sys_operation_log` · `sys_scheduled_task` · `sys_task_execution_log` - -- 内部 FK 实写 1 条:`sys_dict_data.dictTypeId → sys_dict_type.id`(拓扑排序保证父表在前)。 -- 跨域 FK 0 跳过(这些表无对外的强制外键约束)。 -- `sys_calc_param` 便是此前迁移 074 已补列、由 `CalcParamService` 消费的分类编码参数表——现正式进入 Drizzle 建模。 -- **校验**:`npx tsc --noEmit` 通过(exit 0);13 张表列数与 live 100% 对齐。 - -**效果(重跑覆盖率发现):** - -| 指标 | T2 后 | T3 后 | -|------|------:|------:| -| 真实库总表 | 239 | 239 | -| Drizzle 已建模 | 133 | **146** | -| 整体覆盖率 | 56% | **61%** | -| 未建模核心表 | 106 | **93** | -| `sys` 域覆盖率 | 50% | **100%**(27/27) | - -- **下一步**:T4 建模 `pur_` 域(9 张未建模核心);随后 T5 `sal_`(11)、T6 `fin_`+小域(约 73)。 - -## 10. T4 pur_ 域建模结果(2026-08-26,✅ 完成) - -生成器:`scripts/_audit/gen_pur.cjs`(复用列/类型/索引/拓扑排序逻辑)| 产物:`src/lib/db/schemas/_gen_pur.ts` + `schema.ts` 接入。 - -**9 张表**(来自 live,列数逐一核对一致): -`pur_request` · `pur_request_detail`(FK→pur_request)· `pur_request_item` · `pur_supplier_material` · `pur_purchase_reconciliation_writeoff` · 以及 **5 张 `*_deprecated` 废弃表**(`pur_order_deprecated` + `pur_order_detail_deprecated`[FK→purOrderDeprecated]、`pur_receipt_deprecated` + `pur_receipt_detail_deprecated`、`pur_receipt_detail_deprecated` 独立)。 - -> ⚠️ 5 张 `*_deprecated` 是历史废弃表,业务代码不读写,但**真实存在**于 live。建模它们是为避免 safe-db-push 护栏把它们判为"应 DROP 的未建模表"而误 abort;同时避免将来真要清理时漏判。属于"防御性建模"。 - -- 内部 FK 实写 2 条:`purOrderDetailDeprecated.orderId → purOrderDeprecated.id`、`purRequestDetail.requestId → purRequest.id`(拓扑排序保证父表在前)。 -- 跨域 FK 0 跳过(pur 域对 sys_user/inv_material 等引用在 live 未建强制外键约束)。 -- **校验**:`npx tsc --noEmit` 通过;9 张表列数与 live 100% 对齐。 - -**效果(重跑覆盖率发现):** - -| 指标 | T3 后 | T4 后 | -|------|------:|------:| -| Drizzle 已建模 | 146 | **155** | -| 整体覆盖率 | 61% | **65%** | -| 未建模核心表 | 93 | **84** | -| `pur` 域覆盖率 | 40% | **100%**(15/15) | - -- **下一步**:T5 建模 `sal_` 域(11 张未建模核心);随后 T6 `fin_`+小域。 - -## 11. T5 sal_ 域建模结果(2026-08-26,✅ 完成) - -生成器:`scripts/_audit/gen_sal.cjs` | 产物:`src/lib/db/schemas/_gen_sal.ts` + `schema.ts` 接入。 - -**11 张表**(来自 live,列数逐一核对一致):`sal_delivery` · `sal_delivery_detail` · `sal_delivery_order_item` · `sal_order_item` · `sal_reconciliation_detail` · `sal_reconciliation_line` · `sal_reconciliation_writeoff` · `sal_return` · `sal_return_detail` · `sal_return_order_item` · `sal_sample_inventory` - -> ⚠️ **命名冲突规避**:`sal_delivery_order` 已占用导出名 `salDelivery`(sales.ts),故 `sal_delivery` 导出名改为 **`salDeliveryHdr`**。`schema.ts` 接入处已加注释说明。 - -- 已建模 sal 表分散在 3 文件:sales.ts 5 张(`sal_order`/`sal_order_detail`/`sal_delivery_order`/`sal_return_order`/`sal_reconciliation`)+ sample.ts 2 张(`sal_sample_feedback`/`sal_sample_quotation`)+ quote.ts 2 张(`sal_quote`/`sal_quote_item`)= 9,+11 生成 = **sal 域 20/20 = 100%**。 -- 内部 FK 实写 **10 条**(如 `salOrderItem.orderId→salOrder`、`salReturnDetail.returnId→salReturn`、`salDeliveryDetail.deliveryId→salDeliveryHdr` 等,拓扑排序保证父表在前);跨域 FK 跳过 **7 条**(`sal_delivery.customerId→crm_customer`、`sal_delivery.warehouseId→inv_warehouse` 等,留待 T7 跨域校准)。 -- **校验**:`npx tsc --noEmit` 通过;11 张表列数与 live 100% 对齐。 - -**效果(重跑覆盖率发现):** - -| 指标 | T4 后 | T5 后 | -|------|------:|------:| -| Drizzle 已建模 | 155 | **166** | -| 整体覆盖率 | 65% | **69%** | -| 未建模核心表 | 84 | **73** | -| `sal` 域覆盖率 | 45% | **100%**(20/20) | - -- 路径 A 已连下 **sys / pur / sal 三域达 100%**,累计覆盖率 **69%**。 -- **下一步**:T6 建模 `fin_` 域(9 张未建模核心)+ 其余 0% 小域(qc/eqp/crm/ink/material/outsource/qms/eng/eq/plm/bom/srm 等,约 64 张)。 - -## 12. T6 fin_ 域建模结果(2026-08-26,✅ 完成) - -生成器:`scripts/_audit/gen_fin.cjs` | 产物:`src/lib/db/schemas/_gen_fin.ts` + `schema.ts` 接入。 - -**9 张表**(来自 live,列数逐一核对一致):`fin_account` · `fin_account_balance` · `fin_cost_record` · `fin_payment_record` · `fin_period` · `fin_receipt_record` · `fin_receivable_line` · `fin_voucher` · `fin_voucher_line` - -- 已建模 fin 表:finance.ts 2 张(`fin_receivable`/`fin_payable`)+ 9 生成 = **fin 域 11/11 = 100%**。 -- 内部 FK 实写 **5 条**(`finAccountBalance.accountId→finAccount`、`finAccountBalance.periodCode→finPeriod`、`finVoucher.periodCode→finPeriod`、`finVoucherLine.accountId→finAccount`、`finVoucherLine.voucherId→finVoucher`,拓扑排序保证父表在前);跨域 FK 跳过 **2 条**(`fin_voucher_line.customerId→crm_customer`、`fin_voucher_line.supplierId→pur_supplier`,留待 T7)。 -- **校验**:`npx tsc --noEmit` 通过;9 张表列数与 live 100% 对齐。 - -**效果(重跑覆盖率发现):** - -| 指标 | T5 后 | T6 后 | -|------|------:|------:| -| Drizzle 已建模 | 166 | **175** | -| 整体覆盖率 | 69% | **73%** | -| 未建模核心表 | 73 | **64** | -| `fin` 域覆盖率 | 18% | **100%**(11/11) | - -- 路径 A 已连下 **sys / pur / sal / fin 四域达 100%**,累计覆盖率 **73%**。 -- **下一步**:T6 续——补齐其余 0% 小域(qc/eqp/crm/ink/material/outsource/qms/eng/eq/plm/bom/srm 等,约 64 张未建模核心)。这些域多为独立小模块,可一次性批量生成。 - -## 13. T6 续:剩余 18 个 0% 覆盖域批量建模(2026-08-26,✅ 完成) - -**目标**:补齐路径 A 剩余全部 0% 覆盖小域,使全库 Drizzle 覆盖率达到 **100%**。 - -**通用工具链**(均落库 `scripts/_audit/`): -- `gen_misc.cjs`:用法 `node gen_misc.cjs `。扫描全部 `schemas/*.ts` 构建全局「导出名→表名→文件」注册表,用于 FK import 与冲突检测;自动规避导出名冲突(派生名已占用则追加 `Gen` 序号后缀,如 `prdPickOrderGen2`);内部/已建模 FK 实写并自动 import,跨域 FK 以注释保留;拓扑排序保证父表在前。列/索引/FK 数据均直接来自 live `information_schema`。 -- `wire_misc.cjs`:将生成的 `_gen_*.ts` 幂等接入 `schema.ts`(带 `AUTO-WIRED-MISC` 锚点块,重复执行先清后写)。 -- `verify_columns.cjs`:逐表比对「生成文件列数」与「live `information_schema` 列数」。 - -**新增 20 个域的 `_gen_*.ts`**(含 18 个原 0% 域 + 复用通用器补的 prd/eqp 等): - -| 文件 | 域 | 建模表(节选) | -|------|------|------| -| `_gen_prd.ts` | prd | prd_pick_order / prd_pick_order_item / prd_return_order / prd_return_order_item | -| `_gen_eqp.ts` | eqp | equipment_maintenance / equipment_repair / equipment_inspection … | -| `_gen_hr.ts` | hr | hr_employee / hr_attendance / hr_leave … | -| `_gen_crm.ts` | crm | crm_customer / crm_contact / crm_customer_analysis … | -| `_gen_ink.ts` | ink | ink_* 油墨/版库相关 | -| `_gen_material.ts` | material | material_* 物料主数据相关 | -| `_gen_outsource.ts` | outsource | outsource_* 委外相关 | -| `_gen_qc.ts` / `_gen_qms.ts` | qc/qms | 质检/质量体系 | -| `_gen_eng.ts` / `_gen_plm.ts` / `_gen_bom.ts` | eng/plm/bom | 工程/PLM/BOM | -| `_gen_srm.ts` | srm | 供应商关系 | -| `_gen_eq.ts` | eq | 设备 | -| `_gen_base.ts` / `_gen_biz.ts` / `_gen_domain.ts` / `_gen_mdm.ts` / `_gen_qr.ts` / `_gen_work.ts` | base/biz/domain/mdm/qr/work | 基础/业务/域/主数据/二维码/工单 | - -**校验结果**: -- `verify_columns.cjs` 比对 **134 张**生成表(含 sys/pur/sal/fin/warehouse_missing 及本批 64 张)与 live 列数:**全部一致**(`✅ 所有生成表列数与 live 100% 一致`)。 -- `npx tsc --noEmit` 通过(exit 0)。 -- `coverage_discovery.json` 刷新:**239/239 = 100%**,未建模核心 **0**。 - -**提交**:`01cffebd`(`feat(db)+T6续: ...`),已推送 `origin/main`(`760adcef..01cffebd`,25 文件 +2171/−130)。 - -**效果(覆盖率发现重跑)**: - -| 指标 | T6 后 | T6 续后 | -|------|------:|------:| -| Drizzle 已建模 | 175 | **239** | -| 整体覆盖率 | 73% | **100%** | -| 未建模核心表 | 64 | **0** | - -## 14. 全库覆盖率达标后的待办(T7 / T8) - -- **T7 跨域 FK 校准**:当前所有跨域外键(→ `sys_user` / `crm_customer` / `inv_*` / `pur_*` / `sal_*` / `prd_work_order` 等,约 81 处)以 `// SKIP-FK` 注释保留,未实写。需逐域把 `foreignKey()` + `relations()` 补回 Drizzle,且列/类型必须与 live 精确匹配(参考 §0 的「FK 充足但 Drizzle 不知情」警告——**绝不可直接 `drizzle-kit push`**,必须经 `safe-db-push.cjs` 守卫)。 -- **T8 覆盖率复测 + 守卫评估**:在 real 库上重跑 `drizzle-kit push --dry-run` 或 `safe-db-push.cjs`,确认无「DROP 未建模表/列/键」触发;评估是否可解除/收紧 `safe-db-push` 拦截策略。 -- **已知遗留(非阻断)**:部分 `sal_delivery` 等多选/回填历史债(V2 回填不完整)与 split-order 母料 version 静默不扣减 bug,详见工作记忆,与本专项解耦。 diff --git "a/docs/\346\225\260\346\215\256\350\256\276\350\256\241\346\225\264\344\275\223\346\200\235\350\267\257.MD" "b/docs/\346\225\260\346\215\256\350\256\276\350\256\241\346\225\264\344\275\223\346\200\235\350\267\257.MD" deleted file mode 100644 index 74053f9b..00000000 --- "a/docs/\346\225\260\346\215\256\350\256\276\350\256\241\346\225\264\344\275\223\346\200\235\350\267\257.MD" +++ /dev/null @@ -1,354 +0,0 @@ -## 一、数据设计整体思路 - -### 闭环逻辑 - -**销售订单 → MRP计算 → 采购申请/生产工单 → 采购入库/领料出库 → 生产完工入库 → 销售发货 → 财务对账** - -### 与 /settings 的关联 - -所有数据引用的基础定义均来自 `/settings` 模块: - -* **物料/产品** :引用 `/settings/material` 定义 -* **客户/供应商** :引用 `/settings/customer` 和 `/settings/supplier` 定义 -* **币种/汇率** :引用 `/settings/currency` 定义(本币为 CNY) -* **仓库/库位** :引用 `/settings/warehouse` 定义 -* **工序/工艺** :引用 `/settings/process` 定义 -* **计量单位** :引用 `/settings/unit` 定义 - ---- - -## 二、各模块演示数据 - -### 1. 仓储管理 /warehouse - -#### 1.1 入库单 (warehouse_inbound) - -| 入库单号 | 入库类型 | 来源单号 | 仓库 | 物料编码 | 物料名称 | 批次号 | 数量 | 单位 | 入库日期 | -| ----------- | -------- | ----------- | ------ | ----------- | ---------------- | ------------ | ---- | ---- | ---------- | -| IN-2026-001 | 采购入库 | PO-2026-001 | 原料仓 | RAW-PVC-001 | PVC薄膜(透明) | B20260701-01 | 1000 | M | 2026-07-01 | -| IN-2026-002 | 采购入库 | PO-2026-002 | 原料仓 | RAW-PE-001 | PE薄膜(白色) | B20260703-01 | 800 | M | 2026-07-03 | -| IN-2026-003 | 生产入库 | MO-2026-001 | 成品仓 | FIN-LBL-001 | 不干胶标签(A4) | B20260710-01 | 5000 | PCS | 2026-07-10 | - -> **说明** :IN-2026-001 和 IN-2026-002 为后续**大料分切**提供原料批次追溯。 - -#### 1.2 出库单 (warehouse_outbound) - -| 出库单号 | 出库类型 | 关联单号 | 仓库 | 物料编码 | 物料名称 | 批次号 | 数量 | 单位 | 出库日期 | -| ------------ | -------- | ----------- | ------ | ----------- | ---------------- | ------------ | ---- | ---- | ---------- | -| OUT-2026-001 | 生产领料 | MO-2026-001 | 原料仓 | RAW-PVC-001 | PVC薄膜(透明) | B20260701-01 | 200 | M | 2026-07-05 | -| OUT-2026-002 | 生产领料 | MO-2026-001 | 原料仓 | RAW-PE-001 | PE薄膜(白色) | B20260703-01 | 150 | M | 2026-07-05 | -| OUT-2026-003 | 销售发货 | SO-2026-001 | 成品仓 | FIN-LBL-001 | 不干胶标签(A4) | B20260710-01 | 3000 | PCS | 2026-07-12 | - -> **说明** :OUT-2026-001/002 对应工单 MO-2026-001 的领料,形成**批次追溯**链条。 - -#### 1.3 库存台账 (warehouse_stock) - -| 物料编码 | 物料名称 | 仓库 | 批次号 | 当前库存 | 单位 | 安全库存 | -| ----------- | ---------------- | ------ | ------------ | -------- | ---- | -------- | -| RAW-PVC-001 | PVC薄膜(透明) | 原料仓 | B20260701-01 | 800 | M | 100 | -| RAW-PE-001 | PE薄膜(白色) | 原料仓 | B20260703-01 | 650 | M | 80 | -| FIN-LBL-001 | 不干胶标签(A4) | 成品仓 | B20260710-01 | 2000 | PCS | 500 | - -> **说明** :库存数据由入库/出库自动更新,库存台账为自动生成页面。 - -#### 1.4 调拨单 (warehouse_transfer) - -| 调拨单号 | 调出仓库 | 调入仓库 | 物料编码 | 物料名称 | 批次号 | 数量 | 单位 | 调拨日期 | -| ----------- | -------- | ---------- | ----------- | --------------- | ------------ | ---- | ---- | ---------- | -| TF-2026-001 | 原料仓 | 车间中转仓 | RAW-PVC-001 | PVC薄膜(透明) | B20260701-01 | 300 | M | 2026-07-06 | - -#### 1.5 盘点单 (warehouse_count) - -| 盘点单号 | 仓库 | 物料编码 | 物料名称 | 账面数量 | 实盘数量 | 差异 | 单位 | 盘点日期 | -| ----------- | ------ | ----------- | ---------------- | -------- | -------- | ---- | ---- | ---------- | -| CT-2026-001 | 原料仓 | RAW-PVC-001 | PVC薄膜(透明) | 800 | 798 | -2 | M | 2026-07-15 | -| CT-2026-002 | 成品仓 | FIN-LBL-001 | 不干胶标签(A4) | 2000 | 2005 | +5 | PCS | 2026-07-15 | - ---- - -### 2. 大料分切场景(PVC/PE 分切) - -这是你特别提到的场景: **1卷 1000M 大料分切生成小料** 。 - -#### 分切单 (warehouse_splitting) - -| 分切单号 | 母卷物料 | 母卷批次 | 母卷数量 | 子料物料 | 子料规格 | 子料数量 | 单位 | 分切日期 | -| ----------- | --------------- | ------------ | -------- | ----------------- | -------- | -------- | ---- | ---------- | -| SP-2026-001 | PVC薄膜(透明) | B20260701-01 | 300 | PVC薄膜(窄幅) | 500mm宽 | 600 | M | 2026-07-06 | -| SP-2026-001 | PVC薄膜(透明) | B20260701-01 | 300 | PVC薄膜(边角料) | 200mm宽 | 300 | M | 2026-07-06 | -| SP-2026-002 | PE薄膜(白色) | B20260703-01 | 200 | PE薄膜(窄幅) | 600mm宽 | 400 | M | 2026-07-07 | - -> **说明** : -> -> * 母卷 300M 分切成 600M(窄幅)+ 300M(边角料),存在合理损耗 -> * 分切后 **母卷库存减少** , **子料库存增加** ,自动生成入库/出库记录 -> * 子料继承母卷批次号,实现**批次追溯** - ---- - -### 3. 生产管理 /production - -#### 3.1 工单 (production_order) - -| 工单号 | 产品名称 | 计划数量 | 单位 | 工单状态 | 计划开始 | 计划完工 | 关联销售单 | -| ----------- | ---------------- | -------- | ---- | -------- | ---------- | ---------- | ----------- | -| MO-2026-001 | 不干胶标签(A4) | 5000 | PCS | 已完工 | 2026-07-05 | 2026-07-10 | SO-2026-001 | -| MO-2026-002 | 包装盒(小号) | 2000 | PCS | 生产中 | 2026-07-12 | 2026-07-18 | SO-2026-002 | -| MO-2026-003 | 说明书(A5) | 10000 | PCS | 计划中 | 2026-07-20 | 2026-07-25 | SO-2026-003 | - -#### 3.2 BOM清单 (production_bom) - -| BOM编号 | 产品名称 | 子件物料 | 子件名称 | 用量 | 单位 | 损耗率 | -| ------- | ---------------- | ----------- | --------------- | ----- | ------ | ------ | -| BOM-001 | 不干胶标签(A4) | RAW-PVC-001 | PVC薄膜(透明) | 0.04 | M/PCS | 3% | -| BOM-001 | 不干胶标签(A4) | RAW-PE-001 | PE薄膜(白色) | 0.03 | M/PCS | 2% | -| BOM-001 | 不干胶标签(A4) | RAW-INK-001 | 油墨(黑色) | 0.001 | KG/PCS | 5% | - -> **说明** :BOM定义来自 `/settings/bom`,是MRP计算的核心依据。 - -#### 3.3 领料单 (production_pick) - -| 领料单号 | 工单号 | 物料编码 | 物料名称 | 批次号 | 应发数量 | 实发数量 | 单位 | 领料日期 | -| ----------- | ----------- | ----------- | --------------- | ------------ | -------- | -------- | ---- | ---------- | -| PK-2026-001 | MO-2026-001 | RAW-PVC-001 | PVC薄膜(透明) | B20260701-01 | 200 | 200 | M | 2026-07-05 | -| PK-2026-002 | MO-2026-001 | RAW-PE-001 | PE薄膜(白色) | B20260703-01 | 150 | 150 | M | 2026-07-05 | - -> **说明** :领料单由工单自动生成,领料后自动创建出库单。 - -#### 3.4 退料单 (production_return) - -| 退料单号 | 工单号 | 物料编码 | 物料名称 | 批次号 | 数量 | 单位 | 退料日期 | 退料原因 | -| ----------- | ----------- | ----------- | --------------- | ------------ | ---- | ---- | ---------- | -------- | -| RT-2026-001 | MO-2026-001 | RAW-PVC-001 | PVC薄膜(透明) | B20260701-01 | 10 | M | 2026-07-10 | 余料退回 | - -#### 3.5 MRP计算 (production_mrp) - -| MRP编号 | 计算日期 | 产品 | 需求数量 | 可用库存 | 净需求 | 建议采购/生产 | -| ------------ | ---------- | ---------------- | -------- | -------- | ------ | -------------------- | -| MRP-2026-001 | 2026-07-02 | 不干胶标签(A4) | 5000 | 0 | 5000 | 生产工单 MO-2026-001 | -| MRP-2026-002 | 2026-07-02 | PVC薄膜(透明) | 200 | 1000 | 0 | 无需采购 | -| MRP-2026-003 | 2026-07-02 | PE薄膜(白色) | 150 | 800 | 0 | 无需采购 | - -> **说明** :MRP为 **自动生成页面** ,根据销售订单和BOM计算物料需求。 - ---- - -### 4. 销售管理 /sales - -#### 4.1 销售订单 (sales_order) - -| 订单号 | 客户名称 | 产品名称 | 数量 | 单位 | 单价(USD) | 总金额(USD) | 币种 | 订单日期 | 交货日期 | 状态 | -| ----------- | ----------- | ---------------- | ----- | ---- | --------- | ----------- | ---- | ---------- | ---------- | ------ | -| SO-2026-001 | 深圳ABC印刷 | 不干胶标签(A4) | 5000 | PCS | 0.50 | 2500.00 | USD | 2026-07-01 | 2026-07-15 | 已完成 | -| SO-2026-002 | 广州XYZ包装 | 包装盒(小号) | 2000 | PCS | 1.20 | 2400.00 | USD | 2026-07-08 | 2026-07-20 | 发货中 | -| SO-2026-003 | 东莞LMN电子 | 说明书(A5) | 10000 | PCS | 0.15 | 1500.00 | USD | 2026-07-14 | 2026-07-28 | 待生产 | - -> **说明** :支持 **多币种** (USD),汇率定义来自 `/settings/currency`[](https://github.com/snqig/vnerp)。 - -#### 4.2 发货单 (sales_delivery) - -| 发货单号 | 关联订单 | 客户 | 产品 | 发货数量 | 单位 | 批次号 | 发货日期 | -| ----------- | ----------- | ----------- | ---------------- | -------- | ---- | ------------ | ---------- | -| DL-2026-001 | SO-2026-001 | 深圳ABC印刷 | 不干胶标签(A4) | 3000 | PCS | B20260710-01 | 2026-07-12 | -| DL-2026-002 | SO-2026-001 | 深圳ABC印刷 | 不干胶标签(A4) | 2000 | PCS | B20260710-01 | 2026-07-14 | - -#### 4.3 销售退货单 (sales_return) - -| 退货单号 | 关联订单 | 客户 | 产品 | 退货数量 | 单位 | 批次号 | 退货日期 | 退货原因 | -| ----------- | ----------- | ----------- | ---------------- | -------- | ---- | ------------ | ---------- | -------- | -| SR-2026-001 | SO-2026-001 | 深圳ABC印刷 | 不干胶标签(A4) | 100 | PCS | B20260710-01 | 2026-07-16 | 印刷模糊 | - -#### 4.4 客户对账单 (sales_reconciliation) - -| 对账单号 | 客户 | 对账期间 | 订单总额 | 已收款 | 未收款 | 币种 | 对账日期 | -| ------------ | ----------- | -------- | -------- | ------- | ------- | ---- | ---------- | -| REC-2026-001 | 深圳ABC印刷 | 2026-07 | 2500.00 | 1500.00 | 1000.00 | USD | 2026-07-31 | - -> **说明** :对账单根据销售订单和收款记录 **自动生成** 。 - ---- - -### 5. 采购管理 /purchase - -#### 5.1 采购申请 (purchase_request) - -| 申请单号 | 申请人 | 物料编码 | 物料名称 | 需求数量 | 单位 | 需求日期 | 用途说明 | 状态 | -| ----------- | ------ | ----------- | --------------- | -------- | ---- | ---------- | --------------- | ------ | -| PR-2026-001 | 生产部 | RAW-INK-001 | 油墨(黑色) | 50 | KG | 2026-07-10 | 工单MO-2026-001 | 已批准 | -| PR-2026-002 | 生产部 | RAW-PVC-001 | PVC薄膜(透明) | 500 | M | 2026-07-20 | 工单MO-2026-002 | 待批 | - -#### 5.2 采购订单 (purchase_order) - -| 采购单号 | 供应商 | 物料编码 | 物料名称 | 数量 | 单位 | 单价(CNY) | 总金额(CNY) | 币种 | 下单日期 | 交货日期 | 状态 | -| ----------- | ------------ | ----------- | --------------- | ---- | ---- | --------- | ----------- | ---- | ---------- | ---------- | ------ | -| PO-2026-001 | 上海塑胶科技 | RAW-PVC-001 | PVC薄膜(透明) | 1000 | M | 8.50 | 8500.00 | CNY | 2026-06-28 | 2026-07-01 | 已收货 | -| PO-2026-002 | 天津PE材料 | RAW-PE-001 | PE薄膜(白色) | 800 | M | 6.20 | 4960.00 | CNY | 2026-06-30 | 2026-07-03 | 已收货 | -| PO-2026-003 | 广州油墨厂 | RAW-INK-001 | 油墨(黑色) | 50 | KG | 35.00 | 1750.00 | CNY | 2026-07-05 | 2026-07-12 | 待收货 | - -#### 5.3 采购退货单 (purchase_return) - -| 退货单号 | 关联采购单 | 供应商 | 物料 | 退货数量 | 单位 | 退货日期 | 退货原因 | -| ------------ | ----------- | ------------ | --------------- | -------- | ---- | ---------- | -------- | -| PRT-2026-001 | PO-2026-001 | 上海塑胶科技 | PVC薄膜(透明) | 50 | M | 2026-07-02 | 品质不良 | - ---- - -### 6. 印前管理 /dcprint - -#### 6.1 刀模管理 (dcprint_die) - -| 刀模编号 | 刀模名称 | 适用产品 | 材质 | 尺寸 | 状态 | 创建日期 | -| -------- | -------------- | ---------------- | ---- | -------------- | ---- | ---------- | -| DIE-001 | A4标签刀模 | 不干胶标签(A4) | 钢 | 210×297mm | 可用 | 2026-06-15 | -| DIE-002 | 小号包装盒刀模 | 包装盒(小号) | 钢 | 150×100×50mm | 可用 | 2026-06-20 | - -#### 6.2 油墨管理 (dcprint_ink) - -| 油墨编码 | 油墨名称 | 颜色 | 品牌 | 当前库存 | 单位 | 安全库存 | -| ----------- | ------------ | ---- | ---- | -------- | ---- | -------- | -| INK-BLK-001 | 油墨(黑色) | 黑 | 东洋 | 80 | KG | 20 | -| INK-CYN-001 | 油墨(青色) | 青 | 东洋 | 45 | KG | 15 | - -#### 6.3 网版管理 (dcprint_screen) - -| 网版编号 | 网版名称 | 目数 | 适用产品 | 状态 | 创建日期 | -| -------- | ---------- | ----- | ---------------- | ------ | ---------- | -| SCR-001 | A4标签网版 | 350目 | 不干胶标签(A4) | 可用 | 2026-06-18 | -| SCR-002 | 包装盒网版 | 300目 | 包装盒(小号) | 维修中 | 2026-06-22 | - -#### 6.4 工艺卡 (dcprint_process_card) - -| 工艺卡编号 | 产品名称 | 工序序列 | 设备要求 | 油墨要求 | 刀模要求 | 版本 | -| ---------- | ---------------- | ---------------------- | -------- | ----------- | -------- | ---- | -| PC-001 | 不干胶标签(A4) | 印刷→模切→检验→包装 | 丝印机 | INK-BLK-001 | DIE-001 | V1.0 | - -> **说明** :工艺卡关联刀模、油墨、网版,实现**印前追溯**[](https://github.com/snqig/vnerp)。 - ---- - -### 7. 质量管理 /quality - -#### 7.1 来料检验 (quality_iqc) - -| 检验单号 | 关联采购单 | 物料 | 批次号 | 检验项目 | 检验结果 | 合格数量 | 不合格数量 | 检验日期 | -| ------------ | ----------- | --------------- | ------------ | -------------- | -------- | -------- | ---------- | ---------- | -| IQC-2026-001 | PO-2026-001 | PVC薄膜(透明) | B20260701-01 | 厚度/宽度/外观 | 合格 | 950 | 50 | 2026-07-01 | -| IQC-2026-002 | PO-2026-002 | PE薄膜(白色) | B20260703-01 | 厚度/宽度/外观 | 合格 | 800 | 0 | 2026-07-03 | - -#### 7.2 过程检验 (quality_ipqc) - -| 检验单号 | 工单号 | 产品 | 检验工序 | 检验项目 | 结果 | 检验日期 | -| ------------- | ----------- | ---------------- | -------- | ------------- | ---- | ---------- | -| IPQC-2026-001 | MO-2026-001 | 不干胶标签(A4) | 印刷 | 色差/套印精度 | 合格 | 2026-07-06 | - -#### 7.3 成品检验 (quality_fqc) - -| 检验单号 | 工单号 | 产品 | 批次号 | 检验数量 | 合格数量 | 不合格数量 | 检验日期 | -| ------------ | ----------- | ---------------- | ------------ | -------- | -------- | ---------- | ---------- | -| FQC-2026-001 | MO-2026-001 | 不干胶标签(A4) | B20260710-01 | 5000 | 4950 | 50 | 2026-07-10 | - -#### 7.4 客诉管理 (quality_complaint) - -| 客诉编号 | 关联订单 | 客户 | 投诉内容 | 严重程度 | 处理状态 | 处理日期 | -| ------------ | ----------- | ----------- | ------------ | -------- | -------- | ---------- | -| CMP-2026-001 | SO-2026-001 | 深圳ABC印刷 | 标签印刷模糊 | 严重 | 已处理 | 2026-07-17 | - ---- - -### 8. 设备管理 /equipment - -#### 8.1 设备台账 (equipment_list) - -| 设备编码 | 设备名称 | 型号 | 所在车间 | 状态 | 启用日期 | -| -------- | -------- | ------- | -------- | ------ | ---------- | -| EQ-001 | 丝印机 | SP-2000 | 印刷车间 | 运行中 | 2024-01-15 | -| EQ-002 | 模切机 | MQ-1500 | 后道车间 | 运行中 | 2024-03-20 | - -#### 8.2 设备校准 (equipment_calibration) - -| 校准单号 | 设备 | 校准类型 | 校准日期 | 下次校准日期 | 校准结果 | -| ------------ | -------------- | -------- | ---------- | ------------ | -------- | -| CAL-2026-001 | 丝印机 SP-2000 | 年度校准 | 2026-01-10 | 2027-01-10 | 合格 | - -#### 8.3 设备维修 (equipment_maintenance) - -| 维修单号 | 设备 | 维修类型 | 维修日期 | 维修描述 | 状态 | -| ------------ | -------------- | -------- | ---------- | -------- | ------ | -| MTN-2026-001 | 模切机 MQ-1500 | 故障维修 | 2026-07-08 | 刀片更换 | 已完成 | - ---- - -### 9. 财务管理 /finance - -#### 9.1 应收账款 (finance_ar) - -| 凭证号 | 客户 | 关联订单 | 应收金额(USD) | 已收金额(USD) | 未收金额(USD) | 到期日 | 状态 | -| ----------- | ----------- | ----------- | ------------- | ------------- | ------------- | ---------- | ------ | -| AR-2026-001 | 深圳ABC印刷 | SO-2026-001 | 2500.00 | 1500.00 | 1000.00 | 2026-08-15 | 催收中 | - -#### 9.2 应付账款 (finance_ap) - -| 凭证号 | 供应商 | 关联采购单 | 应付金额(CNY) | 已付金额(CNY) | 未付金额(CNY) | 到期日 | 状态 | -| ----------- | ------------ | ----------- | ------------- | ------------- | ------------- | ---------- | ---- | -| AP-2026-001 | 上海塑胶科技 | PO-2026-001 | 8500.00 | 8500.00 | 0.00 | 2026-07-31 | 已付 | - -#### 9.3 成本报表 (finance_cost) - -| 报表编号 | 期间 | 产品 | 直接材料成本 | 直接人工成本 | 制造费用 | 总成本 | 单位成本 | -| ------------- | ------- | ---------------- | ------------ | ------------ | -------- | ------- | -------- | -| COST-2026-001 | 2026-07 | 不干胶标签(A4) | 1250.00 | 800.00 | 450.00 | 2500.00 | 0.50 | - -> **说明** :成本报表根据工单领料、人工、费用 **自动生成** 。 - ---- - -### 10. 打样管理 /sample - -#### 10.1 样品订单 (sample_order) - -| 样品单号 | 客户 | 产品名称 | 样品数量 | 单位 | 打样要求 | 打样日期 | 状态 | -| ------------ | ----------- | ---------------- | -------- | ---- | -------- | ---------- | ------ | -| SPL-2026-001 | 深圳ABC印刷 | 不干胶标签(A4) | 50 | PCS | 标准色卡 | 2026-06-20 | 已确认 | -| SPL-2026-002 | 广州XYZ包装 | 包装盒(小号) | 30 | PCS | 专色打样 | 2026-07-05 | 打样中 | - -#### 10.2 标准色卡 (sample_color_card) - -| 色卡编号 | 色卡名称 | CMYK值 | Pantone号 | 适用产品 | 创建日期 | -| -------- | -------- | --------------- | --------- | ---------------- | ---------- | -| CC-001 | 企业蓝 | C100 M50 Y0 K0 | 2935C | 不干胶标签(A4) | 2026-06-15 | -| CC-002 | 专色红 | C0 M100 Y100 K0 | 185C | 包装盒(小号) | 2026-06-20 | - ---- - -## 三、数据流转路径总结 - -**text** - -``` -销售订单 SO-2026-001 (USD 2500.00) - │ - ▼ -MRP 自动计算 → 生产工单 MO-2026-001 - │ │ - │ ▼ - │ BOM展开 → 领料单 PK-2026-001/002 - │ │ - │ ▼ - │ 原料出库 OUT-2026-001/002 - │ │ - │ ▼ - │ 生产完工 → 成品入库 IN-2026-003 - │ │ - ▼ ▼ -采购申请 PR-2026-001 → 采购订单 PO-2026-003 - │ - ▼ -来料检验 IQC-2026-003 → 采购入库 -``` - - **关键闭环验证** : - -1. **批次追溯** :母卷批次 B20260701-01 → 分切子料 → 领料 → 成品批次 B20260710-01 → 发货 -2. **多币种** :销售 USD ↔ 采购/成本 CNY,汇率来自 `/settings/currency`[](https://github.com/snqig/vnerp) -3. **自动生成页面** :MRP、库存台账、对账单、成本报表 -4. **物料转化** :大料(1000M PVC卷)→ 分切 → 小料(600M窄幅 + 300M边角料) diff --git a/drizzle.config.ts b/drizzle.config.ts index 413d7dd9..e7f53067 100644 --- a/drizzle.config.ts +++ b/drizzle.config.ts @@ -1,20 +1,7 @@ import { defineConfig } from 'drizzle-kit'; -/** - * Drizzle Kit 配置(MySQL dialect) - * - * 作用域与约束(2026-08-26 校正): - * - Drizzle schema(src/lib/db/schema.ts)仍在逐步补全以覆盖真实库表; - * 截至本提交仅建模约 140 / 327 张表,远未全覆盖。 - * - db:push 已被 scripts/safe-db-push.cjs 包裹:任何会 DROP 未建模 - * 表 / 列 / 外键的操作都会直接 ABORT(exit 1)。 - * ⚠️ 严禁 db:push-raw / 直接 drizzle-kit push:会 DROP 真实库 129 外键。schema 变更走 pnpm migrate。 - * - db:generate / db:migrate 仍可直接运行,但同样基于(不完整的) - * Drizzle schema;从残缺 schema 生成迁移亦可能产出 DROP 语句,需人工复核。 - * - db:studio 仅用于可视化查看,安全。 - * - * 全库覆盖率提升见 docs/ 中的「全库 Drizzle 覆盖率」专项计划。 - */ +// Drizzle Kit 配置(MySQL dialect) +// 使用: pnpm db:generate 生成迁移 / pnpm db:push 推送 schema / pnpm db:studio 可视化 export default defineConfig({ schema: './src/lib/db/schema.ts', out: './drizzle', diff --git a/drizzle/0000_chilly_retro_girl.sql b/drizzle/0000_chilly_retro_girl.sql new file mode 100644 index 00000000..2b12f83b --- /dev/null +++ b/drizzle/0000_chilly_retro_girl.sql @@ -0,0 +1,743 @@ +CREATE TABLE `bom_items` ( + `id` serial AUTO_INCREMENT NOT NULL, + `bom_id` int NOT NULL, + `material_id` int NOT NULL, + `quantity` decimal(15,6) NOT NULL, + `unit` varchar(20), + `loss_rate` decimal(5,2) DEFAULT '0', + `sequence` int DEFAULT 0, + `remarks` text, + `created_at` datetime DEFAULT 'CURRENT_TIMESTAMP', + CONSTRAINT `bom_items_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `boms` ( + `id` serial AUTO_INCREMENT NOT NULL, + `product_id` int NOT NULL, + `version` varchar(20) DEFAULT 'V1.0', + `status` varchar(20) DEFAULT 'active', + `effective_date` datetime, + `remarks` text, + `created_at` datetime DEFAULT 'CURRENT_TIMESTAMP', + `updated_at` datetime DEFAULT 'CURRENT_TIMESTAMP', + CONSTRAINT `boms_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `customers` ( + `id` serial AUTO_INCREMENT NOT NULL, + `code` varchar(50) NOT NULL, + `name` varchar(200) NOT NULL, + `short_name` varchar(100), + `contact` varchar(50), + `phone` varchar(50), + `address` varchar(500), + `credit_limit` decimal(15,2) DEFAULT '0', + `credit_used` decimal(15,2) DEFAULT '0', + `status` varchar(20) DEFAULT 'active', + `remarks` text, + `created_at` datetime DEFAULT 'CURRENT_TIMESTAMP', + `updated_at` datetime DEFAULT 'CURRENT_TIMESTAMP', + CONSTRAINT `customers_id` PRIMARY KEY(`id`), + CONSTRAINT `customers_code_unique` UNIQUE(`code`) +); +--> statement-breakpoint +CREATE TABLE `cutting_details` ( + `id` serial AUTO_INCREMENT NOT NULL, + `record_id` int NOT NULL, + `new_label_id` int NOT NULL, + `new_label_no` varchar(50) NOT NULL, + `cut_width` decimal(18,2), + `sequence` int DEFAULT 0, + `created_at` datetime DEFAULT 'CURRENT_TIMESTAMP', + CONSTRAINT `cutting_details_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `cutting_records` ( + `id` serial AUTO_INCREMENT NOT NULL, + `record_no` varchar(50) NOT NULL, + `source_label_id` int NOT NULL, + `source_label_no` varchar(50) NOT NULL, + `cut_width_str` varchar(200), + `original_width` decimal(18,2), + `cut_total_width` decimal(18,2), + `remain_width` decimal(18,2), + `operator_id` int, + `operator_name` varchar(50), + `cut_time` datetime DEFAULT 'CURRENT_TIMESTAMP', + `remark` text, + `status` varchar(20) DEFAULT 'active', + `created_at` datetime DEFAULT 'CURRENT_TIMESTAMP', + CONSTRAINT `cutting_records_id` PRIMARY KEY(`id`), + CONSTRAINT `cutting_records_record_no_unique` UNIQUE(`record_no`) +); +--> statement-breakpoint +CREATE TABLE `defect_records` ( + `id` serial AUTO_INCREMENT NOT NULL, + `inspection_id` int, + `batch_id` int, + `defect_type` varchar(50), + `defect_qty` decimal(15,4), + `disposition` varchar(20), + `status` varchar(20) DEFAULT 'pending', + `handler_id` int, + `handled_at` datetime, + `remarks` text, + `created_at` datetime DEFAULT 'CURRENT_TIMESTAMP', + CONSTRAINT `defect_records_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `delivery_items` ( + `id` serial AUTO_INCREMENT NOT NULL, + `delivery_id` int NOT NULL, + `batch_id` int NOT NULL, + `quantity` decimal(15,4) NOT NULL, + `pallet_no` varchar(50), + `loaded_at` datetime, + `confirmed_at` datetime, + `status` varchar(20) DEFAULT 'pending', + `remarks` text, + `created_at` datetime DEFAULT 'CURRENT_TIMESTAMP', + CONSTRAINT `delivery_items_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `delivery_orders` ( + `id` serial AUTO_INCREMENT NOT NULL, + `delivery_no` varchar(50) NOT NULL, + `vehicle_id` int, + `sales_order_id` int, + `customer_id` int, + `delivery_address` varchar(500), + `delivery_window` varchar(50), + `plan_date` datetime, + `actual_date` datetime, + `status` varchar(20) DEFAULT 'planned', + `total_weight` decimal(10,2), + `total_volume` decimal(10,2), + `remarks` text, + `created_at` datetime DEFAULT 'CURRENT_TIMESTAMP', + `updated_at` datetime DEFAULT 'CURRENT_TIMESTAMP', + CONSTRAINT `delivery_orders_id` PRIMARY KEY(`id`), + CONSTRAINT `delivery_orders_delivery_no_unique` UNIQUE(`delivery_no`) +); +--> statement-breakpoint +CREATE TABLE `delivery_receipts` ( + `id` serial AUTO_INCREMENT NOT NULL, + `delivery_id` int NOT NULL, + `receiver_name` varchar(50), + `receiver_phone` varchar(20), + `received_at` datetime, + `signature` text, + `photos` json, + `remarks` text, + `created_at` datetime DEFAULT 'CURRENT_TIMESTAMP', + CONSTRAINT `delivery_receipts_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `employees` ( + `id` serial AUTO_INCREMENT NOT NULL, + `code` varchar(50) NOT NULL, + `name` varchar(50) NOT NULL, + `department` varchar(50), + `position` varchar(50), + `phone` varchar(50), + `status` varchar(20) DEFAULT 'active', + `created_at` datetime DEFAULT 'CURRENT_TIMESTAMP', + `updated_at` datetime DEFAULT 'CURRENT_TIMESTAMP', + CONSTRAINT `employees_id` PRIMARY KEY(`id`), + CONSTRAINT `employees_code_unique` UNIQUE(`code`) +); +--> statement-breakpoint +CREATE TABLE `equipments` ( + `id` serial AUTO_INCREMENT NOT NULL, + `code` varchar(50) NOT NULL, + `name` varchar(200) NOT NULL, + `model` varchar(100), + `workshop` varchar(50), + `commission_date` datetime, + `maintenance_cycle` int, + `last_maintenance` datetime, + `next_maintenance` datetime, + `status` varchar(20) DEFAULT 'active', + `created_at` datetime DEFAULT 'CURRENT_TIMESTAMP', + `updated_at` datetime DEFAULT 'CURRENT_TIMESTAMP', + CONSTRAINT `equipments_id` PRIMARY KEY(`id`), + CONSTRAINT `equipments_code_unique` UNIQUE(`code`) +); +--> statement-breakpoint +CREATE TABLE `inspection_records` ( + `id` serial AUTO_INCREMENT NOT NULL, + `inspection_no` varchar(50) NOT NULL, + `type` varchar(20) NOT NULL, + `standard_id` int, + `batch_id` int, + `work_order_id` int, + `product_id` int, + `material_id` int, + `sample_qty` int, + `pass_qty` int, + `fail_qty` int, + `result` varchar(20), + `inspector_id` int, + `inspected_at` datetime DEFAULT 'CURRENT_TIMESTAMP', + `inspection_data` json, + `remarks` text, + `created_at` datetime DEFAULT 'CURRENT_TIMESTAMP', + CONSTRAINT `inspection_records_id` PRIMARY KEY(`id`), + CONSTRAINT `inspection_records_inspection_no_unique` UNIQUE(`inspection_no`) +); +--> statement-breakpoint +CREATE TABLE `inspection_standards` ( + `id` serial AUTO_INCREMENT NOT NULL, + `code` varchar(50) NOT NULL, + `name` varchar(200) NOT NULL, + `type` varchar(20) NOT NULL, + `product_id` int, + `material_id` int, + `inspection_items` json, + `status` varchar(20) DEFAULT 'active', + `created_at` datetime DEFAULT 'CURRENT_TIMESTAMP', + CONSTRAINT `inspection_standards_id` PRIMARY KEY(`id`), + CONSTRAINT `inspection_standards_code_unique` UNIQUE(`code`) +); +--> statement-breakpoint +CREATE TABLE `inventory_batches` ( + `id` serial AUTO_INCREMENT NOT NULL, + `batch_no` varchar(50) NOT NULL, + `qr_code` varchar(100), + `material_id` int, + `product_id` int, + `warehouse_id` int NOT NULL, + `location_id` int, + `quantity` decimal(15,4) NOT NULL, + `available_qty` decimal(15,4) NOT NULL, + `reserved_qty` decimal(15,4) DEFAULT '0', + `unit` varchar(20), + `source_type` varchar(50), + `source_no` varchar(50), + `parent_batch_no` varchar(50), + `expiry_date` datetime, + `production_date` datetime, + `status` varchar(20) DEFAULT 'available', + `alert_level` varchar(20) DEFAULT 'normal', + `last_alert_time` datetime, + `inspection_status` varchar(20) DEFAULT 'pending', + `quarantine_status` varchar(20) DEFAULT 'none', + `created_at` datetime DEFAULT 'CURRENT_TIMESTAMP', + `updated_at` datetime DEFAULT 'CURRENT_TIMESTAMP', + CONSTRAINT `inventory_batches_id` PRIMARY KEY(`id`), + CONSTRAINT `inventory_batches_batch_no_unique` UNIQUE(`batch_no`), + CONSTRAINT `inventory_batches_qr_code_unique` UNIQUE(`qr_code`) +); +--> statement-breakpoint +CREATE TABLE `inventory_transactions` ( + `id` serial AUTO_INCREMENT NOT NULL, + `trans_no` varchar(50) NOT NULL, + `trans_type` varchar(50) NOT NULL, + `batch_id` int NOT NULL, + `warehouse_id` int NOT NULL, + `location_id` int, + `quantity` decimal(15,4) NOT NULL, + `before_qty` decimal(15,4), + `after_qty` decimal(15,4), + `source_type` varchar(50), + `source_no` varchar(50), + `operator_id` int, + `operated_at` datetime DEFAULT 'CURRENT_TIMESTAMP', + `remarks` text, + `created_at` datetime DEFAULT 'CURRENT_TIMESTAMP', + CONSTRAINT `inventory_transactions_id` PRIMARY KEY(`id`), + CONSTRAINT `inventory_transactions_trans_no_unique` UNIQUE(`trans_no`) +); +--> statement-breakpoint +CREATE TABLE `locations` ( + `id` serial AUTO_INCREMENT NOT NULL, + `code` varchar(50) NOT NULL, + `warehouse_id` int NOT NULL, + `name` varchar(100) NOT NULL, + `zone` varchar(50), + `shelf` varchar(50), + `layer` varchar(50), + `status` varchar(20) DEFAULT 'active', + `created_at` datetime DEFAULT 'CURRENT_TIMESTAMP', + CONSTRAINT `locations_id` PRIMARY KEY(`id`), + CONSTRAINT `locations_code_unique` UNIQUE(`code`) +); +--> statement-breakpoint +CREATE TABLE `maintenance_plans` ( + `id` serial AUTO_INCREMENT NOT NULL, + `equipment_id` int NOT NULL, + `plan_date` datetime NOT NULL, + `maintenance_type` varchar(20), + `status` varchar(20) DEFAULT 'planned', + `executor_id` int, + `executed_at` datetime, + `remarks` text, + `created_at` datetime DEFAULT 'CURRENT_TIMESTAMP', + CONSTRAINT `maintenance_plans_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `material_labels` ( + `id` serial AUTO_INCREMENT NOT NULL, + `label_no` varchar(50) NOT NULL, + `qr_code` text, + `purchase_order_no` varchar(50), + `supplier_name` varchar(200), + `receive_date` datetime, + `material_code` varchar(50) NOT NULL, + `material_name` varchar(200), + `specification` varchar(200), + `unit` varchar(20), + `batch_no` varchar(50), + `quantity` decimal(18,4) DEFAULT '0', + `package_qty` decimal(18,4) DEFAULT '0', + `width` decimal(18,2), + `length_per_roll` decimal(18,2), + `remark` text, + `color_code` varchar(50), + `mix_remark` text, + `warehouse_id` int, + `location_id` int, + `is_main_material` boolean DEFAULT false, + `is_used` boolean DEFAULT false, + `is_cut` boolean DEFAULT false, + `parent_label_id` int, + `status` varchar(20) DEFAULT 'active', + `created_at` datetime DEFAULT 'CURRENT_TIMESTAMP', + `updated_at` datetime DEFAULT 'CURRENT_TIMESTAMP', + CONSTRAINT `material_labels_id` PRIMARY KEY(`id`), + CONSTRAINT `material_labels_label_no_unique` UNIQUE(`label_no`) +); +--> statement-breakpoint +CREATE TABLE `materials` ( + `id` serial AUTO_INCREMENT NOT NULL, + `code` varchar(50) NOT NULL, + `name` varchar(200) NOT NULL, + `specification` varchar(200), + `unit` varchar(20), + `category` varchar(50), + `safety_stock` decimal(15,4) DEFAULT '0', + `status` varchar(20) DEFAULT 'active', + `created_at` datetime DEFAULT 'CURRENT_TIMESTAMP', + `updated_at` datetime DEFAULT 'CURRENT_TIMESTAMP', + CONSTRAINT `materials_id` PRIMARY KEY(`id`), + CONSTRAINT `materials_code_unique` UNIQUE(`code`) +); +--> statement-breakpoint +CREATE TABLE `outsource_orders` ( + `id` serial AUTO_INCREMENT NOT NULL, + `order_no` varchar(50) NOT NULL, + `qr_code` varchar(100), + `supplier_id` int NOT NULL, + `work_order_id` int, + `process_id` int, + `send_qty` decimal(15,4) NOT NULL, + `return_qty` decimal(15,4) DEFAULT '0', + `scrap_qty` decimal(15,4) DEFAULT '0', + `allowed_loss_rate` decimal(5,2) DEFAULT '0', + `send_date` datetime NOT NULL, + `expected_return_date` datetime, + `actual_return_date` datetime, + `status` varchar(20) DEFAULT 'sent', + `amount` decimal(15,2), + `payment_status` varchar(20) DEFAULT 'unpaid', + `remarks` text, + `created_at` datetime DEFAULT 'CURRENT_TIMESTAMP', + `updated_at` datetime DEFAULT 'CURRENT_TIMESTAMP', + CONSTRAINT `outsource_orders_id` PRIMARY KEY(`id`), + CONSTRAINT `outsource_orders_order_no_unique` UNIQUE(`order_no`), + CONSTRAINT `outsource_orders_qr_code_unique` UNIQUE(`qr_code`) +); +--> statement-breakpoint +CREATE TABLE `process_card_materials` ( + `id` serial AUTO_INCREMENT NOT NULL, + `card_id` int NOT NULL, + `card_no` varchar(50), + `label_id` int NOT NULL, + `label_no` varchar(50) NOT NULL, + `material_type` varchar(20) DEFAULT 'auxiliary', + `material_code` varchar(50), + `material_name` varchar(200), + `specification` varchar(200), + `batch_no` varchar(50), + `quantity` decimal(18,4) DEFAULT '0', + `unit` varchar(20), + `remark` text, + `created_at` datetime DEFAULT 'CURRENT_TIMESTAMP', + CONSTRAINT `process_card_materials_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `process_cards` ( + `id` serial AUTO_INCREMENT NOT NULL, + `card_no` varchar(50) NOT NULL, + `qr_code` text, + `work_order_id` int, + `work_order_no` varchar(50), + `product_code` varchar(50), + `product_name` varchar(200), + `material_spec` varchar(200), + `work_order_date` datetime, + `plan_qty` decimal(18,4) DEFAULT '0', + `main_label_id` int, + `main_label_no` varchar(50), + `burdening_status` varchar(20) DEFAULT 'pending', + `lock_status` varchar(20) DEFAULT 'unlocked', + `create_user_id` int, + `create_user_name` varchar(50), + `created_at` datetime DEFAULT 'CURRENT_TIMESTAMP', + `updated_at` datetime DEFAULT 'CURRENT_TIMESTAMP', + CONSTRAINT `process_cards_id` PRIMARY KEY(`id`), + CONSTRAINT `process_cards_card_no_unique` UNIQUE(`card_no`) +); +--> statement-breakpoint +CREATE TABLE `processes` ( + `id` serial AUTO_INCREMENT NOT NULL, + `code` varchar(50) NOT NULL, + `name` varchar(100) NOT NULL, + `sequence` int NOT NULL, + `workcenter` varchar(50), + `standard_time` decimal(10,2), + `setup_time` decimal(10,2), + `status` varchar(20) DEFAULT 'active', + `created_at` datetime DEFAULT 'CURRENT_TIMESTAMP', + CONSTRAINT `processes_id` PRIMARY KEY(`id`), + CONSTRAINT `processes_code_unique` UNIQUE(`code`) +); +--> statement-breakpoint +CREATE TABLE `production_reports` ( + `id` serial AUTO_INCREMENT NOT NULL, + `report_no` varchar(50) NOT NULL, + `work_order_id` int NOT NULL, + `work_order_process_id` int, + `process_id` int NOT NULL, + `equipment_id` int, + `employee_id` int NOT NULL, + `good_qty` decimal(15,4) NOT NULL, + `scrap_qty` decimal(15,4) DEFAULT '0', + `batch_no` varchar(50), + `work_date` datetime NOT NULL, + `start_time` datetime, + `end_time` datetime, + `work_minutes` int, + `efficiency` decimal(5,2), + `status` varchar(20) DEFAULT 'normal', + `remarks` text, + `reported_at` datetime DEFAULT 'CURRENT_TIMESTAMP', + `created_at` datetime DEFAULT 'CURRENT_TIMESTAMP', + CONSTRAINT `production_reports_id` PRIMARY KEY(`id`), + CONSTRAINT `production_reports_report_no_unique` UNIQUE(`report_no`) +); +--> statement-breakpoint +CREATE TABLE `products` ( + `id` serial AUTO_INCREMENT NOT NULL, + `code` varchar(50) NOT NULL, + `name` varchar(200) NOT NULL, + `specification` varchar(200), + `unit` varchar(20), + `category` varchar(50), + `bom_version` varchar(20) DEFAULT 'V1.0', + `customer_id` int, + `status` varchar(20) DEFAULT 'active', + `created_at` datetime DEFAULT 'CURRENT_TIMESTAMP', + `updated_at` datetime DEFAULT 'CURRENT_TIMESTAMP', + CONSTRAINT `products_id` PRIMARY KEY(`id`), + CONSTRAINT `products_code_unique` UNIQUE(`code`) +); +--> statement-breakpoint +CREATE TABLE `purchase_order_items` ( + `id` serial AUTO_INCREMENT NOT NULL, + `order_id` int NOT NULL, + `material_id` int NOT NULL, + `quantity` decimal(15,4) NOT NULL, + `received_qty` decimal(15,4) DEFAULT '0', + `unit` varchar(20), + `unit_price` decimal(15,4), + `amount` decimal(15,4), + `status` varchar(20) DEFAULT 'pending', + `remarks` text, + `created_at` datetime DEFAULT 'CURRENT_TIMESTAMP', + CONSTRAINT `purchase_order_items_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `purchase_orders` ( + `id` serial AUTO_INCREMENT NOT NULL, + `order_no` varchar(50) NOT NULL, + `supplier_id` int NOT NULL, + `order_date` datetime NOT NULL, + `expected_date` datetime, + `status` varchar(20) DEFAULT 'draft', + `total_amount` decimal(15,2) DEFAULT '0', + `remarks` text, + `created_by` int, + `created_at` datetime DEFAULT 'CURRENT_TIMESTAMP', + `updated_at` datetime DEFAULT 'CURRENT_TIMESTAMP', + CONSTRAINT `purchase_orders_id` PRIMARY KEY(`id`), + CONSTRAINT `purchase_orders_order_no_unique` UNIQUE(`order_no`) +); +--> statement-breakpoint +CREATE TABLE `purchase_request_items` ( + `id` serial AUTO_INCREMENT NOT NULL, + `request_id` int NOT NULL, + `material_id` int NOT NULL, + `quantity` decimal(15,4) NOT NULL, + `unit` varchar(20), + `required_date` datetime, + `status` varchar(20) DEFAULT 'pending', + `remarks` text, + `created_at` datetime DEFAULT 'CURRENT_TIMESTAMP', + CONSTRAINT `purchase_request_items_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `purchase_requests` ( + `id` serial AUTO_INCREMENT NOT NULL, + `request_no` varchar(50) NOT NULL, + `request_type` varchar(20), + `requester_id` int, + `request_date` datetime NOT NULL, + `status` varchar(20) DEFAULT 'draft', + `remarks` text, + `created_at` datetime DEFAULT 'CURRENT_TIMESTAMP', + CONSTRAINT `purchase_requests_id` PRIMARY KEY(`id`), + CONSTRAINT `purchase_requests_request_no_unique` UNIQUE(`request_no`) +); +--> statement-breakpoint +CREATE TABLE `sales_order_items` ( + `id` serial AUTO_INCREMENT NOT NULL, + `order_id` int NOT NULL, + `product_id` int NOT NULL, + `quantity` decimal(15,4) NOT NULL, + `unit` varchar(20), + `unit_price` decimal(15,4), + `amount` decimal(15,4), + `delivery_date` datetime, + `status` varchar(20) DEFAULT 'pending', + `remarks` text, + `created_at` datetime DEFAULT 'CURRENT_TIMESTAMP', + CONSTRAINT `sales_order_items_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `sales_orders` ( + `id` serial AUTO_INCREMENT NOT NULL, + `order_no` varchar(50) NOT NULL, + `customer_id` int NOT NULL, + `order_date` datetime NOT NULL, + `delivery_date` datetime, + `status` varchar(20) DEFAULT 'draft', + `total_amount` decimal(15,2) DEFAULT '0', + `remarks` text, + `created_by` int, + `created_at` datetime DEFAULT 'CURRENT_TIMESTAMP', + `updated_at` datetime DEFAULT 'CURRENT_TIMESTAMP', + CONSTRAINT `sales_orders_id` PRIMARY KEY(`id`), + CONSTRAINT `sales_orders_order_no_unique` UNIQUE(`order_no`) +); +--> statement-breakpoint +CREATE TABLE `sample_requests` ( + `id` serial AUTO_INCREMENT NOT NULL, + `request_no` varchar(50) NOT NULL, + `customer_id` int NOT NULL, + `product_id` int, + `product_name` varchar(200), + `specification` varchar(200), + `quantity` decimal(15,4), + `unit` varchar(20), + `request_date` datetime NOT NULL, + `required_date` datetime, + `status` varchar(20) DEFAULT 'draft', + `cost` decimal(15,2), + `qr_code` varchar(100), + `remarks` text, + `requester_id` int, + `created_at` datetime DEFAULT 'CURRENT_TIMESTAMP', + `updated_at` datetime DEFAULT 'CURRENT_TIMESTAMP', + CONSTRAINT `sample_requests_id` PRIMARY KEY(`id`), + CONSTRAINT `sample_requests_request_no_unique` UNIQUE(`request_no`), + CONSTRAINT `sample_requests_qr_code_unique` UNIQUE(`qr_code`) +); +--> statement-breakpoint +CREATE TABLE `scan_logs` ( + `id` serial AUTO_INCREMENT NOT NULL, + `scan_type` varchar(50) NOT NULL, + `qr_content` text, + `label_no` varchar(50), + `operation` varchar(50), + `result` varchar(20) DEFAULT 'success', + `message` text, + `operator_id` int, + `operator_name` varchar(50), + `scan_time` datetime DEFAULT 'CURRENT_TIMESTAMP', + `ip_address` varchar(50), + CONSTRAINT `scan_logs_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `suppliers` ( + `id` serial AUTO_INCREMENT NOT NULL, + `code` varchar(50) NOT NULL, + `name` varchar(200) NOT NULL, + `contact` varchar(50), + `phone` varchar(50), + `address` varchar(500), + `status` varchar(20) DEFAULT 'active', + `created_at` datetime DEFAULT 'CURRENT_TIMESTAMP', + `updated_at` datetime DEFAULT 'CURRENT_TIMESTAMP', + CONSTRAINT `suppliers_id` PRIMARY KEY(`id`), + CONSTRAINT `suppliers_code_unique` UNIQUE(`code`) +); +--> statement-breakpoint +CREATE TABLE `trace_records` ( + `id` serial AUTO_INCREMENT NOT NULL, + `trace_no` varchar(50) NOT NULL, + `card_id` int, + `card_no` varchar(50), + `work_order_no` varchar(50), + `product_code` varchar(50), + `main_label_id` int, + `trace_type` varchar(20) DEFAULT 'forward', + `operator_id` int, + `operator_name` varchar(50), + `trace_time` datetime DEFAULT 'CURRENT_TIMESTAMP', + `remark` text, + `created_at` datetime DEFAULT 'CURRENT_TIMESTAMP', + CONSTRAINT `trace_records_id` PRIMARY KEY(`id`), + CONSTRAINT `trace_records_trace_no_unique` UNIQUE(`trace_no`) +); +--> statement-breakpoint +CREATE TABLE `vehicles` ( + `id` serial AUTO_INCREMENT NOT NULL, + `plate_no` varchar(20) NOT NULL, + `vehicle_type` varchar(50), + `volume` decimal(10,2), + `load_weight` decimal(10,2), + `driver` varchar(50), + `driver_phone` varchar(20), + `status` varchar(20) DEFAULT 'available', + `created_at` datetime DEFAULT 'CURRENT_TIMESTAMP', + CONSTRAINT `vehicles_id` PRIMARY KEY(`id`), + CONSTRAINT `vehicles_plate_no_unique` UNIQUE(`plate_no`) +); +--> statement-breakpoint +CREATE TABLE `warehouses` ( + `id` serial AUTO_INCREMENT NOT NULL, + `code` varchar(50) NOT NULL, + `name` varchar(100) NOT NULL, + `type` varchar(20) NOT NULL, + `manager` varchar(50), + `status` varchar(20) DEFAULT 'active', + `created_at` datetime DEFAULT 'CURRENT_TIMESTAMP', + CONSTRAINT `warehouses_id` PRIMARY KEY(`id`), + CONSTRAINT `warehouses_code_unique` UNIQUE(`code`) +); +--> statement-breakpoint +CREATE TABLE `work_order_processes` ( + `id` serial AUTO_INCREMENT NOT NULL, + `work_order_id` int NOT NULL, + `process_id` int NOT NULL, + `equipment_id` int, + `plan_qty` decimal(15,4) NOT NULL, + `completed_qty` decimal(15,4) DEFAULT '0', + `scrap_qty` decimal(15,4) DEFAULT '0', + `status` varchar(20) DEFAULT 'pending', + `start_time` datetime, + `end_time` datetime, + `created_at` datetime DEFAULT 'CURRENT_TIMESTAMP', + CONSTRAINT `work_order_processes_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `work_orders` ( + `id` serial AUTO_INCREMENT NOT NULL, + `order_no` varchar(50) NOT NULL, + `qr_code` varchar(100), + `sales_order_id` int, + `sales_order_item_id` int, + `product_id` int NOT NULL, + `bom_id` int, + `quantity` decimal(15,4) NOT NULL, + `completed_qty` decimal(15,4) DEFAULT '0', + `scrap_qty` decimal(15,4) DEFAULT '0', + `plan_start_date` datetime, + `plan_end_date` datetime, + `actual_start_date` datetime, + `actual_end_date` datetime, + `status` varchar(20) DEFAULT 'created', + `priority` int DEFAULT 5, + `workshop` varchar(50), + `remarks` text, + `created_by` int, + `created_at` datetime DEFAULT 'CURRENT_TIMESTAMP', + `updated_at` datetime DEFAULT 'CURRENT_TIMESTAMP', + CONSTRAINT `work_orders_id` PRIMARY KEY(`id`), + CONSTRAINT `work_orders_order_no_unique` UNIQUE(`order_no`), + CONSTRAINT `work_orders_qr_code_unique` UNIQUE(`qr_code`) +); +--> statement-breakpoint +ALTER TABLE `bom_items` ADD CONSTRAINT `bom_items_bom_id_boms_id_fk` FOREIGN KEY (`bom_id`) REFERENCES `boms`(`id`) ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE `bom_items` ADD CONSTRAINT `bom_items_material_id_materials_id_fk` FOREIGN KEY (`material_id`) REFERENCES `materials`(`id`) ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE `boms` ADD CONSTRAINT `boms_product_id_products_id_fk` FOREIGN KEY (`product_id`) REFERENCES `products`(`id`) ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE `cutting_details` ADD CONSTRAINT `cutting_details_record_id_cutting_records_id_fk` FOREIGN KEY (`record_id`) REFERENCES `cutting_records`(`id`) ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE `cutting_details` ADD CONSTRAINT `cutting_details_new_label_id_material_labels_id_fk` FOREIGN KEY (`new_label_id`) REFERENCES `material_labels`(`id`) ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE `cutting_records` ADD CONSTRAINT `cutting_records_source_label_id_material_labels_id_fk` FOREIGN KEY (`source_label_id`) REFERENCES `material_labels`(`id`) ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE `cutting_records` ADD CONSTRAINT `cutting_records_operator_id_employees_id_fk` FOREIGN KEY (`operator_id`) REFERENCES `employees`(`id`) ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE `defect_records` ADD CONSTRAINT `defect_records_inspection_id_inspection_records_id_fk` FOREIGN KEY (`inspection_id`) REFERENCES `inspection_records`(`id`) ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE `defect_records` ADD CONSTRAINT `defect_records_batch_id_inventory_batches_id_fk` FOREIGN KEY (`batch_id`) REFERENCES `inventory_batches`(`id`) ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE `defect_records` ADD CONSTRAINT `defect_records_handler_id_employees_id_fk` FOREIGN KEY (`handler_id`) REFERENCES `employees`(`id`) ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE `delivery_items` ADD CONSTRAINT `delivery_items_delivery_id_delivery_orders_id_fk` FOREIGN KEY (`delivery_id`) REFERENCES `delivery_orders`(`id`) ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE `delivery_items` ADD CONSTRAINT `delivery_items_batch_id_inventory_batches_id_fk` FOREIGN KEY (`batch_id`) REFERENCES `inventory_batches`(`id`) ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE `delivery_orders` ADD CONSTRAINT `delivery_orders_vehicle_id_vehicles_id_fk` FOREIGN KEY (`vehicle_id`) REFERENCES `vehicles`(`id`) ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE `delivery_orders` ADD CONSTRAINT `delivery_orders_sales_order_id_sales_orders_id_fk` FOREIGN KEY (`sales_order_id`) REFERENCES `sales_orders`(`id`) ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE `delivery_orders` ADD CONSTRAINT `delivery_orders_customer_id_customers_id_fk` FOREIGN KEY (`customer_id`) REFERENCES `customers`(`id`) ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE `delivery_receipts` ADD CONSTRAINT `delivery_receipts_delivery_id_delivery_orders_id_fk` FOREIGN KEY (`delivery_id`) REFERENCES `delivery_orders`(`id`) ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE `inspection_records` ADD CONSTRAINT `inspection_records_standard_id_inspection_standards_id_fk` FOREIGN KEY (`standard_id`) REFERENCES `inspection_standards`(`id`) ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE `inspection_records` ADD CONSTRAINT `inspection_records_batch_id_inventory_batches_id_fk` FOREIGN KEY (`batch_id`) REFERENCES `inventory_batches`(`id`) ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE `inspection_records` ADD CONSTRAINT `inspection_records_work_order_id_work_orders_id_fk` FOREIGN KEY (`work_order_id`) REFERENCES `work_orders`(`id`) ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE `inspection_records` ADD CONSTRAINT `inspection_records_product_id_products_id_fk` FOREIGN KEY (`product_id`) REFERENCES `products`(`id`) ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE `inspection_records` ADD CONSTRAINT `inspection_records_material_id_materials_id_fk` FOREIGN KEY (`material_id`) REFERENCES `materials`(`id`) ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE `inspection_records` ADD CONSTRAINT `inspection_records_inspector_id_employees_id_fk` FOREIGN KEY (`inspector_id`) REFERENCES `employees`(`id`) ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE `inspection_standards` ADD CONSTRAINT `inspection_standards_product_id_products_id_fk` FOREIGN KEY (`product_id`) REFERENCES `products`(`id`) ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE `inspection_standards` ADD CONSTRAINT `inspection_standards_material_id_materials_id_fk` FOREIGN KEY (`material_id`) REFERENCES `materials`(`id`) ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE `inventory_batches` ADD CONSTRAINT `inventory_batches_material_id_materials_id_fk` FOREIGN KEY (`material_id`) REFERENCES `materials`(`id`) ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE `inventory_batches` ADD CONSTRAINT `inventory_batches_product_id_products_id_fk` FOREIGN KEY (`product_id`) REFERENCES `products`(`id`) ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE `inventory_batches` ADD CONSTRAINT `inventory_batches_warehouse_id_warehouses_id_fk` FOREIGN KEY (`warehouse_id`) REFERENCES `warehouses`(`id`) ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE `inventory_batches` ADD CONSTRAINT `inventory_batches_location_id_locations_id_fk` FOREIGN KEY (`location_id`) REFERENCES `locations`(`id`) ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE `inventory_transactions` ADD CONSTRAINT `inventory_transactions_batch_id_inventory_batches_id_fk` FOREIGN KEY (`batch_id`) REFERENCES `inventory_batches`(`id`) ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE `inventory_transactions` ADD CONSTRAINT `inventory_transactions_operator_id_employees_id_fk` FOREIGN KEY (`operator_id`) REFERENCES `employees`(`id`) ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE `locations` ADD CONSTRAINT `locations_warehouse_id_warehouses_id_fk` FOREIGN KEY (`warehouse_id`) REFERENCES `warehouses`(`id`) ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE `maintenance_plans` ADD CONSTRAINT `maintenance_plans_equipment_id_equipments_id_fk` FOREIGN KEY (`equipment_id`) REFERENCES `equipments`(`id`) ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE `maintenance_plans` ADD CONSTRAINT `maintenance_plans_executor_id_employees_id_fk` FOREIGN KEY (`executor_id`) REFERENCES `employees`(`id`) ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE `material_labels` ADD CONSTRAINT `material_labels_warehouse_id_warehouses_id_fk` FOREIGN KEY (`warehouse_id`) REFERENCES `warehouses`(`id`) ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE `material_labels` ADD CONSTRAINT `material_labels_location_id_locations_id_fk` FOREIGN KEY (`location_id`) REFERENCES `locations`(`id`) ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE `outsource_orders` ADD CONSTRAINT `outsource_orders_supplier_id_suppliers_id_fk` FOREIGN KEY (`supplier_id`) REFERENCES `suppliers`(`id`) ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE `outsource_orders` ADD CONSTRAINT `outsource_orders_work_order_id_work_orders_id_fk` FOREIGN KEY (`work_order_id`) REFERENCES `work_orders`(`id`) ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE `outsource_orders` ADD CONSTRAINT `outsource_orders_process_id_processes_id_fk` FOREIGN KEY (`process_id`) REFERENCES `processes`(`id`) ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE `process_card_materials` ADD CONSTRAINT `process_card_materials_card_id_process_cards_id_fk` FOREIGN KEY (`card_id`) REFERENCES `process_cards`(`id`) ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE `process_card_materials` ADD CONSTRAINT `process_card_materials_label_id_material_labels_id_fk` FOREIGN KEY (`label_id`) REFERENCES `material_labels`(`id`) ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE `process_cards` ADD CONSTRAINT `process_cards_work_order_id_work_orders_id_fk` FOREIGN KEY (`work_order_id`) REFERENCES `work_orders`(`id`) ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE `process_cards` ADD CONSTRAINT `process_cards_main_label_id_material_labels_id_fk` FOREIGN KEY (`main_label_id`) REFERENCES `material_labels`(`id`) ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE `process_cards` ADD CONSTRAINT `process_cards_create_user_id_employees_id_fk` FOREIGN KEY (`create_user_id`) REFERENCES `employees`(`id`) ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE `production_reports` ADD CONSTRAINT `production_reports_work_order_id_work_orders_id_fk` FOREIGN KEY (`work_order_id`) REFERENCES `work_orders`(`id`) ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE `production_reports` ADD CONSTRAINT `production_reports_work_order_process_id_work_order_processes_id_fk` FOREIGN KEY (`work_order_process_id`) REFERENCES `work_order_processes`(`id`) ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE `production_reports` ADD CONSTRAINT `production_reports_process_id_processes_id_fk` FOREIGN KEY (`process_id`) REFERENCES `processes`(`id`) ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE `production_reports` ADD CONSTRAINT `production_reports_equipment_id_equipments_id_fk` FOREIGN KEY (`equipment_id`) REFERENCES `equipments`(`id`) ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE `production_reports` ADD CONSTRAINT `production_reports_employee_id_employees_id_fk` FOREIGN KEY (`employee_id`) REFERENCES `employees`(`id`) ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE `products` ADD CONSTRAINT `products_customer_id_customers_id_fk` FOREIGN KEY (`customer_id`) REFERENCES `customers`(`id`) ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE `purchase_order_items` ADD CONSTRAINT `purchase_order_items_order_id_purchase_orders_id_fk` FOREIGN KEY (`order_id`) REFERENCES `purchase_orders`(`id`) ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE `purchase_order_items` ADD CONSTRAINT `purchase_order_items_material_id_materials_id_fk` FOREIGN KEY (`material_id`) REFERENCES `materials`(`id`) ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE `purchase_orders` ADD CONSTRAINT `purchase_orders_supplier_id_suppliers_id_fk` FOREIGN KEY (`supplier_id`) REFERENCES `suppliers`(`id`) ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE `purchase_request_items` ADD CONSTRAINT `purchase_request_items_request_id_purchase_requests_id_fk` FOREIGN KEY (`request_id`) REFERENCES `purchase_requests`(`id`) ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE `purchase_request_items` ADD CONSTRAINT `purchase_request_items_material_id_materials_id_fk` FOREIGN KEY (`material_id`) REFERENCES `materials`(`id`) ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE `purchase_requests` ADD CONSTRAINT `purchase_requests_requester_id_employees_id_fk` FOREIGN KEY (`requester_id`) REFERENCES `employees`(`id`) ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE `sales_order_items` ADD CONSTRAINT `sales_order_items_order_id_sales_orders_id_fk` FOREIGN KEY (`order_id`) REFERENCES `sales_orders`(`id`) ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE `sales_order_items` ADD CONSTRAINT `sales_order_items_product_id_products_id_fk` FOREIGN KEY (`product_id`) REFERENCES `products`(`id`) ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE `sales_orders` ADD CONSTRAINT `sales_orders_customer_id_customers_id_fk` FOREIGN KEY (`customer_id`) REFERENCES `customers`(`id`) ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE `sample_requests` ADD CONSTRAINT `sample_requests_customer_id_customers_id_fk` FOREIGN KEY (`customer_id`) REFERENCES `customers`(`id`) ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE `sample_requests` ADD CONSTRAINT `sample_requests_product_id_products_id_fk` FOREIGN KEY (`product_id`) REFERENCES `products`(`id`) ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE `sample_requests` ADD CONSTRAINT `sample_requests_requester_id_employees_id_fk` FOREIGN KEY (`requester_id`) REFERENCES `employees`(`id`) ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE `scan_logs` ADD CONSTRAINT `scan_logs_operator_id_employees_id_fk` FOREIGN KEY (`operator_id`) REFERENCES `employees`(`id`) ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE `trace_records` ADD CONSTRAINT `trace_records_card_id_process_cards_id_fk` FOREIGN KEY (`card_id`) REFERENCES `process_cards`(`id`) ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE `trace_records` ADD CONSTRAINT `trace_records_main_label_id_material_labels_id_fk` FOREIGN KEY (`main_label_id`) REFERENCES `material_labels`(`id`) ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE `trace_records` ADD CONSTRAINT `trace_records_operator_id_employees_id_fk` FOREIGN KEY (`operator_id`) REFERENCES `employees`(`id`) ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE `work_order_processes` ADD CONSTRAINT `work_order_processes_work_order_id_work_orders_id_fk` FOREIGN KEY (`work_order_id`) REFERENCES `work_orders`(`id`) ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE `work_order_processes` ADD CONSTRAINT `work_order_processes_process_id_processes_id_fk` FOREIGN KEY (`process_id`) REFERENCES `processes`(`id`) ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE `work_order_processes` ADD CONSTRAINT `work_order_processes_equipment_id_equipments_id_fk` FOREIGN KEY (`equipment_id`) REFERENCES `equipments`(`id`) ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE `work_orders` ADD CONSTRAINT `work_orders_sales_order_id_sales_orders_id_fk` FOREIGN KEY (`sales_order_id`) REFERENCES `sales_orders`(`id`) ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE `work_orders` ADD CONSTRAINT `work_orders_sales_order_item_id_sales_order_items_id_fk` FOREIGN KEY (`sales_order_item_id`) REFERENCES `sales_order_items`(`id`) ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE `work_orders` ADD CONSTRAINT `work_orders_product_id_products_id_fk` FOREIGN KEY (`product_id`) REFERENCES `products`(`id`) ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE `work_orders` ADD CONSTRAINT `work_orders_bom_id_boms_id_fk` FOREIGN KEY (`bom_id`) REFERENCES `boms`(`id`) ON DELETE no action ON UPDATE no action; \ No newline at end of file diff --git a/drizzle/0001_ordinary_valkyrie.sql b/drizzle/0001_ordinary_valkyrie.sql new file mode 100644 index 00000000..bfa0629e --- /dev/null +++ b/drizzle/0001_ordinary_valkyrie.sql @@ -0,0 +1,34 @@ +CREATE TABLE `inv_inbound_item` ( + `id` serial AUTO_INCREMENT NOT NULL, + `order_id` int NOT NULL, + `material_id` int, + `material_name` varchar(200), + `material_spec` varchar(200), + `batch_no` varchar(50), + `quantity` decimal(15,3), + `unit` varchar(20), + `unit_price` decimal(15,4), + `total_price` decimal(15,4), + `warehouse_location` varchar(50), + `produce_date` datetime, + `create_time` datetime DEFAULT 'CURRENT_TIMESTAMP', + CONSTRAINT `inv_inbound_item_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `inv_inbound_order` ( + `id` serial AUTO_INCREMENT NOT NULL, + `order_no` varchar(30) NOT NULL, + `order_type` varchar(20) DEFAULT 'purchase', + `warehouse_id` int NOT NULL, + `supplier_name` varchar(100), + `inbound_date` datetime, + `total_quantity` decimal(15,3) DEFAULT '0', + `total_amount` decimal(15,2), + `status` varchar(20) DEFAULT 'pending', + `qc_status` varchar(20) DEFAULT 'pending', + `remark` varchar(500), + `deleted` boolean DEFAULT false, + `create_time` datetime DEFAULT 'CURRENT_TIMESTAMP', + `update_time` datetime DEFAULT 'CURRENT_TIMESTAMP', + CONSTRAINT `inv_inbound_order_id` PRIMARY KEY(`id`) +); diff --git a/drizzle/meta/0000_snapshot.json b/drizzle/meta/0000_snapshot.json new file mode 100644 index 00000000..cffff1d5 --- /dev/null +++ b/drizzle/meta/0000_snapshot.json @@ -0,0 +1,5376 @@ +{ + "version": "5", + "dialect": "mysql", + "id": "f4c9546c-b8bc-4070-9697-ef8d9185b2ed", + "prevId": "00000000-0000-0000-0000-000000000000", + "tables": { + "bom_items": { + "name": "bom_items", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "bom_id": { + "name": "bom_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "material_id": { + "name": "material_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "quantity": { + "name": "quantity", + "type": "decimal(15,6)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "unit": { + "name": "unit", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "loss_rate": { + "name": "loss_rate", + "type": "decimal(5,2)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'0'" + }, + "sequence": { + "name": "sequence", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": 0 + }, + "remarks": { + "name": "remarks", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + } + }, + "indexes": {}, + "foreignKeys": { + "bom_items_bom_id_boms_id_fk": { + "name": "bom_items_bom_id_boms_id_fk", + "tableFrom": "bom_items", + "tableTo": "boms", + "columnsFrom": [ + "bom_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "bom_items_material_id_materials_id_fk": { + "name": "bom_items_material_id_materials_id_fk", + "tableFrom": "bom_items", + "tableTo": "materials", + "columnsFrom": [ + "material_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "bom_items_id": { + "name": "bom_items_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "boms": { + "name": "boms", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "product_id": { + "name": "product_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "version": { + "name": "version", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'V1.0'" + }, + "status": { + "name": "status", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'active'" + }, + "effective_date": { + "name": "effective_date", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "remarks": { + "name": "remarks", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + }, + "updated_at": { + "name": "updated_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + } + }, + "indexes": {}, + "foreignKeys": { + "boms_product_id_products_id_fk": { + "name": "boms_product_id_products_id_fk", + "tableFrom": "boms", + "tableTo": "products", + "columnsFrom": [ + "product_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "boms_id": { + "name": "boms_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "customers": { + "name": "customers", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "code": { + "name": "code", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "varchar(200)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "short_name": { + "name": "short_name", + "type": "varchar(100)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "contact": { + "name": "contact", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "phone": { + "name": "phone", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "address": { + "name": "address", + "type": "varchar(500)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "credit_limit": { + "name": "credit_limit", + "type": "decimal(15,2)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'0'" + }, + "credit_used": { + "name": "credit_used", + "type": "decimal(15,2)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'0'" + }, + "status": { + "name": "status", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'active'" + }, + "remarks": { + "name": "remarks", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + }, + "updated_at": { + "name": "updated_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "customers_id": { + "name": "customers_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": { + "customers_code_unique": { + "name": "customers_code_unique", + "columns": [ + "code" + ] + } + }, + "checkConstraint": {} + }, + "cutting_details": { + "name": "cutting_details", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "record_id": { + "name": "record_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "new_label_id": { + "name": "new_label_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "new_label_no": { + "name": "new_label_no", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "cut_width": { + "name": "cut_width", + "type": "decimal(18,2)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "sequence": { + "name": "sequence", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + } + }, + "indexes": {}, + "foreignKeys": { + "cutting_details_record_id_cutting_records_id_fk": { + "name": "cutting_details_record_id_cutting_records_id_fk", + "tableFrom": "cutting_details", + "tableTo": "cutting_records", + "columnsFrom": [ + "record_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "cutting_details_new_label_id_material_labels_id_fk": { + "name": "cutting_details_new_label_id_material_labels_id_fk", + "tableFrom": "cutting_details", + "tableTo": "material_labels", + "columnsFrom": [ + "new_label_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "cutting_details_id": { + "name": "cutting_details_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "cutting_records": { + "name": "cutting_records", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "record_no": { + "name": "record_no", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "source_label_id": { + "name": "source_label_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "source_label_no": { + "name": "source_label_no", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "cut_width_str": { + "name": "cut_width_str", + "type": "varchar(200)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "original_width": { + "name": "original_width", + "type": "decimal(18,2)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "cut_total_width": { + "name": "cut_total_width", + "type": "decimal(18,2)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "remain_width": { + "name": "remain_width", + "type": "decimal(18,2)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "operator_id": { + "name": "operator_id", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "operator_name": { + "name": "operator_name", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "cut_time": { + "name": "cut_time", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + }, + "remark": { + "name": "remark", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'active'" + }, + "created_at": { + "name": "created_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + } + }, + "indexes": {}, + "foreignKeys": { + "cutting_records_source_label_id_material_labels_id_fk": { + "name": "cutting_records_source_label_id_material_labels_id_fk", + "tableFrom": "cutting_records", + "tableTo": "material_labels", + "columnsFrom": [ + "source_label_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "cutting_records_operator_id_employees_id_fk": { + "name": "cutting_records_operator_id_employees_id_fk", + "tableFrom": "cutting_records", + "tableTo": "employees", + "columnsFrom": [ + "operator_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "cutting_records_id": { + "name": "cutting_records_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": { + "cutting_records_record_no_unique": { + "name": "cutting_records_record_no_unique", + "columns": [ + "record_no" + ] + } + }, + "checkConstraint": {} + }, + "defect_records": { + "name": "defect_records", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "inspection_id": { + "name": "inspection_id", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "batch_id": { + "name": "batch_id", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "defect_type": { + "name": "defect_type", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "defect_qty": { + "name": "defect_qty", + "type": "decimal(15,4)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "disposition": { + "name": "disposition", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'pending'" + }, + "handler_id": { + "name": "handler_id", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "handled_at": { + "name": "handled_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "remarks": { + "name": "remarks", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + } + }, + "indexes": {}, + "foreignKeys": { + "defect_records_inspection_id_inspection_records_id_fk": { + "name": "defect_records_inspection_id_inspection_records_id_fk", + "tableFrom": "defect_records", + "tableTo": "inspection_records", + "columnsFrom": [ + "inspection_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "defect_records_batch_id_inventory_batches_id_fk": { + "name": "defect_records_batch_id_inventory_batches_id_fk", + "tableFrom": "defect_records", + "tableTo": "inventory_batches", + "columnsFrom": [ + "batch_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "defect_records_handler_id_employees_id_fk": { + "name": "defect_records_handler_id_employees_id_fk", + "tableFrom": "defect_records", + "tableTo": "employees", + "columnsFrom": [ + "handler_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "defect_records_id": { + "name": "defect_records_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "delivery_items": { + "name": "delivery_items", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "delivery_id": { + "name": "delivery_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "batch_id": { + "name": "batch_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "quantity": { + "name": "quantity", + "type": "decimal(15,4)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "pallet_no": { + "name": "pallet_no", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "loaded_at": { + "name": "loaded_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "confirmed_at": { + "name": "confirmed_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'pending'" + }, + "remarks": { + "name": "remarks", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + } + }, + "indexes": {}, + "foreignKeys": { + "delivery_items_delivery_id_delivery_orders_id_fk": { + "name": "delivery_items_delivery_id_delivery_orders_id_fk", + "tableFrom": "delivery_items", + "tableTo": "delivery_orders", + "columnsFrom": [ + "delivery_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "delivery_items_batch_id_inventory_batches_id_fk": { + "name": "delivery_items_batch_id_inventory_batches_id_fk", + "tableFrom": "delivery_items", + "tableTo": "inventory_batches", + "columnsFrom": [ + "batch_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "delivery_items_id": { + "name": "delivery_items_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "delivery_orders": { + "name": "delivery_orders", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "delivery_no": { + "name": "delivery_no", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "vehicle_id": { + "name": "vehicle_id", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "sales_order_id": { + "name": "sales_order_id", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "customer_id": { + "name": "customer_id", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "delivery_address": { + "name": "delivery_address", + "type": "varchar(500)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "delivery_window": { + "name": "delivery_window", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "plan_date": { + "name": "plan_date", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "actual_date": { + "name": "actual_date", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'planned'" + }, + "total_weight": { + "name": "total_weight", + "type": "decimal(10,2)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "total_volume": { + "name": "total_volume", + "type": "decimal(10,2)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "remarks": { + "name": "remarks", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + }, + "updated_at": { + "name": "updated_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + } + }, + "indexes": {}, + "foreignKeys": { + "delivery_orders_vehicle_id_vehicles_id_fk": { + "name": "delivery_orders_vehicle_id_vehicles_id_fk", + "tableFrom": "delivery_orders", + "tableTo": "vehicles", + "columnsFrom": [ + "vehicle_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "delivery_orders_sales_order_id_sales_orders_id_fk": { + "name": "delivery_orders_sales_order_id_sales_orders_id_fk", + "tableFrom": "delivery_orders", + "tableTo": "sales_orders", + "columnsFrom": [ + "sales_order_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "delivery_orders_customer_id_customers_id_fk": { + "name": "delivery_orders_customer_id_customers_id_fk", + "tableFrom": "delivery_orders", + "tableTo": "customers", + "columnsFrom": [ + "customer_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "delivery_orders_id": { + "name": "delivery_orders_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": { + "delivery_orders_delivery_no_unique": { + "name": "delivery_orders_delivery_no_unique", + "columns": [ + "delivery_no" + ] + } + }, + "checkConstraint": {} + }, + "delivery_receipts": { + "name": "delivery_receipts", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "delivery_id": { + "name": "delivery_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "receiver_name": { + "name": "receiver_name", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "receiver_phone": { + "name": "receiver_phone", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "received_at": { + "name": "received_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "signature": { + "name": "signature", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "photos": { + "name": "photos", + "type": "json", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "remarks": { + "name": "remarks", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + } + }, + "indexes": {}, + "foreignKeys": { + "delivery_receipts_delivery_id_delivery_orders_id_fk": { + "name": "delivery_receipts_delivery_id_delivery_orders_id_fk", + "tableFrom": "delivery_receipts", + "tableTo": "delivery_orders", + "columnsFrom": [ + "delivery_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "delivery_receipts_id": { + "name": "delivery_receipts_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "employees": { + "name": "employees", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "code": { + "name": "code", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "department": { + "name": "department", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "position": { + "name": "position", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "phone": { + "name": "phone", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'active'" + }, + "created_at": { + "name": "created_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + }, + "updated_at": { + "name": "updated_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "employees_id": { + "name": "employees_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": { + "employees_code_unique": { + "name": "employees_code_unique", + "columns": [ + "code" + ] + } + }, + "checkConstraint": {} + }, + "equipments": { + "name": "equipments", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "code": { + "name": "code", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "varchar(200)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "model": { + "name": "model", + "type": "varchar(100)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "workshop": { + "name": "workshop", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "commission_date": { + "name": "commission_date", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "maintenance_cycle": { + "name": "maintenance_cycle", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "last_maintenance": { + "name": "last_maintenance", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "next_maintenance": { + "name": "next_maintenance", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'active'" + }, + "created_at": { + "name": "created_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + }, + "updated_at": { + "name": "updated_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "equipments_id": { + "name": "equipments_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": { + "equipments_code_unique": { + "name": "equipments_code_unique", + "columns": [ + "code" + ] + } + }, + "checkConstraint": {} + }, + "inspection_records": { + "name": "inspection_records", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "inspection_no": { + "name": "inspection_no", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "type": { + "name": "type", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "standard_id": { + "name": "standard_id", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "batch_id": { + "name": "batch_id", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "work_order_id": { + "name": "work_order_id", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "product_id": { + "name": "product_id", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "material_id": { + "name": "material_id", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "sample_qty": { + "name": "sample_qty", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "pass_qty": { + "name": "pass_qty", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "fail_qty": { + "name": "fail_qty", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "result": { + "name": "result", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "inspector_id": { + "name": "inspector_id", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "inspected_at": { + "name": "inspected_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + }, + "inspection_data": { + "name": "inspection_data", + "type": "json", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "remarks": { + "name": "remarks", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + } + }, + "indexes": {}, + "foreignKeys": { + "inspection_records_standard_id_inspection_standards_id_fk": { + "name": "inspection_records_standard_id_inspection_standards_id_fk", + "tableFrom": "inspection_records", + "tableTo": "inspection_standards", + "columnsFrom": [ + "standard_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "inspection_records_batch_id_inventory_batches_id_fk": { + "name": "inspection_records_batch_id_inventory_batches_id_fk", + "tableFrom": "inspection_records", + "tableTo": "inventory_batches", + "columnsFrom": [ + "batch_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "inspection_records_work_order_id_work_orders_id_fk": { + "name": "inspection_records_work_order_id_work_orders_id_fk", + "tableFrom": "inspection_records", + "tableTo": "work_orders", + "columnsFrom": [ + "work_order_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "inspection_records_product_id_products_id_fk": { + "name": "inspection_records_product_id_products_id_fk", + "tableFrom": "inspection_records", + "tableTo": "products", + "columnsFrom": [ + "product_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "inspection_records_material_id_materials_id_fk": { + "name": "inspection_records_material_id_materials_id_fk", + "tableFrom": "inspection_records", + "tableTo": "materials", + "columnsFrom": [ + "material_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "inspection_records_inspector_id_employees_id_fk": { + "name": "inspection_records_inspector_id_employees_id_fk", + "tableFrom": "inspection_records", + "tableTo": "employees", + "columnsFrom": [ + "inspector_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "inspection_records_id": { + "name": "inspection_records_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": { + "inspection_records_inspection_no_unique": { + "name": "inspection_records_inspection_no_unique", + "columns": [ + "inspection_no" + ] + } + }, + "checkConstraint": {} + }, + "inspection_standards": { + "name": "inspection_standards", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "code": { + "name": "code", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "varchar(200)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "type": { + "name": "type", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "product_id": { + "name": "product_id", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "material_id": { + "name": "material_id", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "inspection_items": { + "name": "inspection_items", + "type": "json", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'active'" + }, + "created_at": { + "name": "created_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + } + }, + "indexes": {}, + "foreignKeys": { + "inspection_standards_product_id_products_id_fk": { + "name": "inspection_standards_product_id_products_id_fk", + "tableFrom": "inspection_standards", + "tableTo": "products", + "columnsFrom": [ + "product_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "inspection_standards_material_id_materials_id_fk": { + "name": "inspection_standards_material_id_materials_id_fk", + "tableFrom": "inspection_standards", + "tableTo": "materials", + "columnsFrom": [ + "material_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "inspection_standards_id": { + "name": "inspection_standards_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": { + "inspection_standards_code_unique": { + "name": "inspection_standards_code_unique", + "columns": [ + "code" + ] + } + }, + "checkConstraint": {} + }, + "inventory_batches": { + "name": "inventory_batches", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "batch_no": { + "name": "batch_no", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "qr_code": { + "name": "qr_code", + "type": "varchar(100)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "material_id": { + "name": "material_id", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "product_id": { + "name": "product_id", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "warehouse_id": { + "name": "warehouse_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "location_id": { + "name": "location_id", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "quantity": { + "name": "quantity", + "type": "decimal(15,4)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "available_qty": { + "name": "available_qty", + "type": "decimal(15,4)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "reserved_qty": { + "name": "reserved_qty", + "type": "decimal(15,4)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'0'" + }, + "unit": { + "name": "unit", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "source_type": { + "name": "source_type", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "source_no": { + "name": "source_no", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "parent_batch_no": { + "name": "parent_batch_no", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "expiry_date": { + "name": "expiry_date", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "production_date": { + "name": "production_date", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'available'" + }, + "alert_level": { + "name": "alert_level", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'normal'" + }, + "last_alert_time": { + "name": "last_alert_time", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "inspection_status": { + "name": "inspection_status", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'pending'" + }, + "quarantine_status": { + "name": "quarantine_status", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'none'" + }, + "created_at": { + "name": "created_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + }, + "updated_at": { + "name": "updated_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + } + }, + "indexes": {}, + "foreignKeys": { + "inventory_batches_material_id_materials_id_fk": { + "name": "inventory_batches_material_id_materials_id_fk", + "tableFrom": "inventory_batches", + "tableTo": "materials", + "columnsFrom": [ + "material_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "inventory_batches_product_id_products_id_fk": { + "name": "inventory_batches_product_id_products_id_fk", + "tableFrom": "inventory_batches", + "tableTo": "products", + "columnsFrom": [ + "product_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "inventory_batches_warehouse_id_warehouses_id_fk": { + "name": "inventory_batches_warehouse_id_warehouses_id_fk", + "tableFrom": "inventory_batches", + "tableTo": "warehouses", + "columnsFrom": [ + "warehouse_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "inventory_batches_location_id_locations_id_fk": { + "name": "inventory_batches_location_id_locations_id_fk", + "tableFrom": "inventory_batches", + "tableTo": "locations", + "columnsFrom": [ + "location_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "inventory_batches_id": { + "name": "inventory_batches_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": { + "inventory_batches_batch_no_unique": { + "name": "inventory_batches_batch_no_unique", + "columns": [ + "batch_no" + ] + }, + "inventory_batches_qr_code_unique": { + "name": "inventory_batches_qr_code_unique", + "columns": [ + "qr_code" + ] + } + }, + "checkConstraint": {} + }, + "inventory_transactions": { + "name": "inventory_transactions", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "trans_no": { + "name": "trans_no", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "trans_type": { + "name": "trans_type", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "batch_id": { + "name": "batch_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "warehouse_id": { + "name": "warehouse_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "location_id": { + "name": "location_id", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "quantity": { + "name": "quantity", + "type": "decimal(15,4)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "before_qty": { + "name": "before_qty", + "type": "decimal(15,4)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "after_qty": { + "name": "after_qty", + "type": "decimal(15,4)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "source_type": { + "name": "source_type", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "source_no": { + "name": "source_no", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "operator_id": { + "name": "operator_id", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "operated_at": { + "name": "operated_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + }, + "remarks": { + "name": "remarks", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + } + }, + "indexes": {}, + "foreignKeys": { + "inventory_transactions_batch_id_inventory_batches_id_fk": { + "name": "inventory_transactions_batch_id_inventory_batches_id_fk", + "tableFrom": "inventory_transactions", + "tableTo": "inventory_batches", + "columnsFrom": [ + "batch_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "inventory_transactions_operator_id_employees_id_fk": { + "name": "inventory_transactions_operator_id_employees_id_fk", + "tableFrom": "inventory_transactions", + "tableTo": "employees", + "columnsFrom": [ + "operator_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "inventory_transactions_id": { + "name": "inventory_transactions_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": { + "inventory_transactions_trans_no_unique": { + "name": "inventory_transactions_trans_no_unique", + "columns": [ + "trans_no" + ] + } + }, + "checkConstraint": {} + }, + "locations": { + "name": "locations", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "code": { + "name": "code", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "warehouse_id": { + "name": "warehouse_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "zone": { + "name": "zone", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "shelf": { + "name": "shelf", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "layer": { + "name": "layer", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'active'" + }, + "created_at": { + "name": "created_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + } + }, + "indexes": {}, + "foreignKeys": { + "locations_warehouse_id_warehouses_id_fk": { + "name": "locations_warehouse_id_warehouses_id_fk", + "tableFrom": "locations", + "tableTo": "warehouses", + "columnsFrom": [ + "warehouse_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "locations_id": { + "name": "locations_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": { + "locations_code_unique": { + "name": "locations_code_unique", + "columns": [ + "code" + ] + } + }, + "checkConstraint": {} + }, + "maintenance_plans": { + "name": "maintenance_plans", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "equipment_id": { + "name": "equipment_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "plan_date": { + "name": "plan_date", + "type": "datetime", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "maintenance_type": { + "name": "maintenance_type", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'planned'" + }, + "executor_id": { + "name": "executor_id", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "executed_at": { + "name": "executed_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "remarks": { + "name": "remarks", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + } + }, + "indexes": {}, + "foreignKeys": { + "maintenance_plans_equipment_id_equipments_id_fk": { + "name": "maintenance_plans_equipment_id_equipments_id_fk", + "tableFrom": "maintenance_plans", + "tableTo": "equipments", + "columnsFrom": [ + "equipment_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "maintenance_plans_executor_id_employees_id_fk": { + "name": "maintenance_plans_executor_id_employees_id_fk", + "tableFrom": "maintenance_plans", + "tableTo": "employees", + "columnsFrom": [ + "executor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "maintenance_plans_id": { + "name": "maintenance_plans_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "material_labels": { + "name": "material_labels", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "label_no": { + "name": "label_no", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "qr_code": { + "name": "qr_code", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "purchase_order_no": { + "name": "purchase_order_no", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "supplier_name": { + "name": "supplier_name", + "type": "varchar(200)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "receive_date": { + "name": "receive_date", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "material_code": { + "name": "material_code", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "material_name": { + "name": "material_name", + "type": "varchar(200)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "specification": { + "name": "specification", + "type": "varchar(200)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "unit": { + "name": "unit", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "batch_no": { + "name": "batch_no", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "quantity": { + "name": "quantity", + "type": "decimal(18,4)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'0'" + }, + "package_qty": { + "name": "package_qty", + "type": "decimal(18,4)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'0'" + }, + "width": { + "name": "width", + "type": "decimal(18,2)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "length_per_roll": { + "name": "length_per_roll", + "type": "decimal(18,2)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "remark": { + "name": "remark", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "color_code": { + "name": "color_code", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "mix_remark": { + "name": "mix_remark", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "warehouse_id": { + "name": "warehouse_id", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "location_id": { + "name": "location_id", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "is_main_material": { + "name": "is_main_material", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": false + }, + "is_used": { + "name": "is_used", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": false + }, + "is_cut": { + "name": "is_cut", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": false + }, + "parent_label_id": { + "name": "parent_label_id", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'active'" + }, + "created_at": { + "name": "created_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + }, + "updated_at": { + "name": "updated_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + } + }, + "indexes": {}, + "foreignKeys": { + "material_labels_warehouse_id_warehouses_id_fk": { + "name": "material_labels_warehouse_id_warehouses_id_fk", + "tableFrom": "material_labels", + "tableTo": "warehouses", + "columnsFrom": [ + "warehouse_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "material_labels_location_id_locations_id_fk": { + "name": "material_labels_location_id_locations_id_fk", + "tableFrom": "material_labels", + "tableTo": "locations", + "columnsFrom": [ + "location_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "material_labels_id": { + "name": "material_labels_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": { + "material_labels_label_no_unique": { + "name": "material_labels_label_no_unique", + "columns": [ + "label_no" + ] + } + }, + "checkConstraint": {} + }, + "materials": { + "name": "materials", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "code": { + "name": "code", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "varchar(200)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "specification": { + "name": "specification", + "type": "varchar(200)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "unit": { + "name": "unit", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "category": { + "name": "category", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "safety_stock": { + "name": "safety_stock", + "type": "decimal(15,4)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'0'" + }, + "status": { + "name": "status", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'active'" + }, + "created_at": { + "name": "created_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + }, + "updated_at": { + "name": "updated_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "materials_id": { + "name": "materials_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": { + "materials_code_unique": { + "name": "materials_code_unique", + "columns": [ + "code" + ] + } + }, + "checkConstraint": {} + }, + "outsource_orders": { + "name": "outsource_orders", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "order_no": { + "name": "order_no", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "qr_code": { + "name": "qr_code", + "type": "varchar(100)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "supplier_id": { + "name": "supplier_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "work_order_id": { + "name": "work_order_id", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "process_id": { + "name": "process_id", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "send_qty": { + "name": "send_qty", + "type": "decimal(15,4)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "return_qty": { + "name": "return_qty", + "type": "decimal(15,4)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'0'" + }, + "scrap_qty": { + "name": "scrap_qty", + "type": "decimal(15,4)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'0'" + }, + "allowed_loss_rate": { + "name": "allowed_loss_rate", + "type": "decimal(5,2)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'0'" + }, + "send_date": { + "name": "send_date", + "type": "datetime", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "expected_return_date": { + "name": "expected_return_date", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "actual_return_date": { + "name": "actual_return_date", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'sent'" + }, + "amount": { + "name": "amount", + "type": "decimal(15,2)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "payment_status": { + "name": "payment_status", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'unpaid'" + }, + "remarks": { + "name": "remarks", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + }, + "updated_at": { + "name": "updated_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + } + }, + "indexes": {}, + "foreignKeys": { + "outsource_orders_supplier_id_suppliers_id_fk": { + "name": "outsource_orders_supplier_id_suppliers_id_fk", + "tableFrom": "outsource_orders", + "tableTo": "suppliers", + "columnsFrom": [ + "supplier_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "outsource_orders_work_order_id_work_orders_id_fk": { + "name": "outsource_orders_work_order_id_work_orders_id_fk", + "tableFrom": "outsource_orders", + "tableTo": "work_orders", + "columnsFrom": [ + "work_order_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "outsource_orders_process_id_processes_id_fk": { + "name": "outsource_orders_process_id_processes_id_fk", + "tableFrom": "outsource_orders", + "tableTo": "processes", + "columnsFrom": [ + "process_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "outsource_orders_id": { + "name": "outsource_orders_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": { + "outsource_orders_order_no_unique": { + "name": "outsource_orders_order_no_unique", + "columns": [ + "order_no" + ] + }, + "outsource_orders_qr_code_unique": { + "name": "outsource_orders_qr_code_unique", + "columns": [ + "qr_code" + ] + } + }, + "checkConstraint": {} + }, + "process_card_materials": { + "name": "process_card_materials", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "card_id": { + "name": "card_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "card_no": { + "name": "card_no", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "label_id": { + "name": "label_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "label_no": { + "name": "label_no", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "material_type": { + "name": "material_type", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'auxiliary'" + }, + "material_code": { + "name": "material_code", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "material_name": { + "name": "material_name", + "type": "varchar(200)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "specification": { + "name": "specification", + "type": "varchar(200)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "batch_no": { + "name": "batch_no", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "quantity": { + "name": "quantity", + "type": "decimal(18,4)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'0'" + }, + "unit": { + "name": "unit", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "remark": { + "name": "remark", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + } + }, + "indexes": {}, + "foreignKeys": { + "process_card_materials_card_id_process_cards_id_fk": { + "name": "process_card_materials_card_id_process_cards_id_fk", + "tableFrom": "process_card_materials", + "tableTo": "process_cards", + "columnsFrom": [ + "card_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "process_card_materials_label_id_material_labels_id_fk": { + "name": "process_card_materials_label_id_material_labels_id_fk", + "tableFrom": "process_card_materials", + "tableTo": "material_labels", + "columnsFrom": [ + "label_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "process_card_materials_id": { + "name": "process_card_materials_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "process_cards": { + "name": "process_cards", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "card_no": { + "name": "card_no", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "qr_code": { + "name": "qr_code", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "work_order_id": { + "name": "work_order_id", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "work_order_no": { + "name": "work_order_no", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "product_code": { + "name": "product_code", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "product_name": { + "name": "product_name", + "type": "varchar(200)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "material_spec": { + "name": "material_spec", + "type": "varchar(200)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "work_order_date": { + "name": "work_order_date", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "plan_qty": { + "name": "plan_qty", + "type": "decimal(18,4)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'0'" + }, + "main_label_id": { + "name": "main_label_id", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "main_label_no": { + "name": "main_label_no", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "burdening_status": { + "name": "burdening_status", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'pending'" + }, + "lock_status": { + "name": "lock_status", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'unlocked'" + }, + "create_user_id": { + "name": "create_user_id", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "create_user_name": { + "name": "create_user_name", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + }, + "updated_at": { + "name": "updated_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + } + }, + "indexes": {}, + "foreignKeys": { + "process_cards_work_order_id_work_orders_id_fk": { + "name": "process_cards_work_order_id_work_orders_id_fk", + "tableFrom": "process_cards", + "tableTo": "work_orders", + "columnsFrom": [ + "work_order_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "process_cards_main_label_id_material_labels_id_fk": { + "name": "process_cards_main_label_id_material_labels_id_fk", + "tableFrom": "process_cards", + "tableTo": "material_labels", + "columnsFrom": [ + "main_label_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "process_cards_create_user_id_employees_id_fk": { + "name": "process_cards_create_user_id_employees_id_fk", + "tableFrom": "process_cards", + "tableTo": "employees", + "columnsFrom": [ + "create_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "process_cards_id": { + "name": "process_cards_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": { + "process_cards_card_no_unique": { + "name": "process_cards_card_no_unique", + "columns": [ + "card_no" + ] + } + }, + "checkConstraint": {} + }, + "processes": { + "name": "processes", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "code": { + "name": "code", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "sequence": { + "name": "sequence", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "workcenter": { + "name": "workcenter", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "standard_time": { + "name": "standard_time", + "type": "decimal(10,2)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "setup_time": { + "name": "setup_time", + "type": "decimal(10,2)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'active'" + }, + "created_at": { + "name": "created_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "processes_id": { + "name": "processes_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": { + "processes_code_unique": { + "name": "processes_code_unique", + "columns": [ + "code" + ] + } + }, + "checkConstraint": {} + }, + "production_reports": { + "name": "production_reports", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "report_no": { + "name": "report_no", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "work_order_id": { + "name": "work_order_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "work_order_process_id": { + "name": "work_order_process_id", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "process_id": { + "name": "process_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "equipment_id": { + "name": "equipment_id", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "employee_id": { + "name": "employee_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "good_qty": { + "name": "good_qty", + "type": "decimal(15,4)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "scrap_qty": { + "name": "scrap_qty", + "type": "decimal(15,4)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'0'" + }, + "batch_no": { + "name": "batch_no", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "work_date": { + "name": "work_date", + "type": "datetime", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "start_time": { + "name": "start_time", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "end_time": { + "name": "end_time", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "work_minutes": { + "name": "work_minutes", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "efficiency": { + "name": "efficiency", + "type": "decimal(5,2)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'normal'" + }, + "remarks": { + "name": "remarks", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "reported_at": { + "name": "reported_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + }, + "created_at": { + "name": "created_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + } + }, + "indexes": {}, + "foreignKeys": { + "production_reports_work_order_id_work_orders_id_fk": { + "name": "production_reports_work_order_id_work_orders_id_fk", + "tableFrom": "production_reports", + "tableTo": "work_orders", + "columnsFrom": [ + "work_order_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "production_reports_work_order_process_id_work_order_processes_id_fk": { + "name": "production_reports_work_order_process_id_work_order_processes_id_fk", + "tableFrom": "production_reports", + "tableTo": "work_order_processes", + "columnsFrom": [ + "work_order_process_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "production_reports_process_id_processes_id_fk": { + "name": "production_reports_process_id_processes_id_fk", + "tableFrom": "production_reports", + "tableTo": "processes", + "columnsFrom": [ + "process_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "production_reports_equipment_id_equipments_id_fk": { + "name": "production_reports_equipment_id_equipments_id_fk", + "tableFrom": "production_reports", + "tableTo": "equipments", + "columnsFrom": [ + "equipment_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "production_reports_employee_id_employees_id_fk": { + "name": "production_reports_employee_id_employees_id_fk", + "tableFrom": "production_reports", + "tableTo": "employees", + "columnsFrom": [ + "employee_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "production_reports_id": { + "name": "production_reports_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": { + "production_reports_report_no_unique": { + "name": "production_reports_report_no_unique", + "columns": [ + "report_no" + ] + } + }, + "checkConstraint": {} + }, + "products": { + "name": "products", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "code": { + "name": "code", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "varchar(200)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "specification": { + "name": "specification", + "type": "varchar(200)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "unit": { + "name": "unit", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "category": { + "name": "category", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "bom_version": { + "name": "bom_version", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'V1.0'" + }, + "customer_id": { + "name": "customer_id", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'active'" + }, + "created_at": { + "name": "created_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + }, + "updated_at": { + "name": "updated_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + } + }, + "indexes": {}, + "foreignKeys": { + "products_customer_id_customers_id_fk": { + "name": "products_customer_id_customers_id_fk", + "tableFrom": "products", + "tableTo": "customers", + "columnsFrom": [ + "customer_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "products_id": { + "name": "products_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": { + "products_code_unique": { + "name": "products_code_unique", + "columns": [ + "code" + ] + } + }, + "checkConstraint": {} + }, + "purchase_order_items": { + "name": "purchase_order_items", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "order_id": { + "name": "order_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "material_id": { + "name": "material_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "quantity": { + "name": "quantity", + "type": "decimal(15,4)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "received_qty": { + "name": "received_qty", + "type": "decimal(15,4)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'0'" + }, + "unit": { + "name": "unit", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "unit_price": { + "name": "unit_price", + "type": "decimal(15,4)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "amount": { + "name": "amount", + "type": "decimal(15,4)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'pending'" + }, + "remarks": { + "name": "remarks", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + } + }, + "indexes": {}, + "foreignKeys": { + "purchase_order_items_order_id_purchase_orders_id_fk": { + "name": "purchase_order_items_order_id_purchase_orders_id_fk", + "tableFrom": "purchase_order_items", + "tableTo": "purchase_orders", + "columnsFrom": [ + "order_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "purchase_order_items_material_id_materials_id_fk": { + "name": "purchase_order_items_material_id_materials_id_fk", + "tableFrom": "purchase_order_items", + "tableTo": "materials", + "columnsFrom": [ + "material_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "purchase_order_items_id": { + "name": "purchase_order_items_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "purchase_orders": { + "name": "purchase_orders", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "order_no": { + "name": "order_no", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "supplier_id": { + "name": "supplier_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "order_date": { + "name": "order_date", + "type": "datetime", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "expected_date": { + "name": "expected_date", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'draft'" + }, + "total_amount": { + "name": "total_amount", + "type": "decimal(15,2)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'0'" + }, + "remarks": { + "name": "remarks", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_by": { + "name": "created_by", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + }, + "updated_at": { + "name": "updated_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + } + }, + "indexes": {}, + "foreignKeys": { + "purchase_orders_supplier_id_suppliers_id_fk": { + "name": "purchase_orders_supplier_id_suppliers_id_fk", + "tableFrom": "purchase_orders", + "tableTo": "suppliers", + "columnsFrom": [ + "supplier_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "purchase_orders_id": { + "name": "purchase_orders_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": { + "purchase_orders_order_no_unique": { + "name": "purchase_orders_order_no_unique", + "columns": [ + "order_no" + ] + } + }, + "checkConstraint": {} + }, + "purchase_request_items": { + "name": "purchase_request_items", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "request_id": { + "name": "request_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "material_id": { + "name": "material_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "quantity": { + "name": "quantity", + "type": "decimal(15,4)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "unit": { + "name": "unit", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "required_date": { + "name": "required_date", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'pending'" + }, + "remarks": { + "name": "remarks", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + } + }, + "indexes": {}, + "foreignKeys": { + "purchase_request_items_request_id_purchase_requests_id_fk": { + "name": "purchase_request_items_request_id_purchase_requests_id_fk", + "tableFrom": "purchase_request_items", + "tableTo": "purchase_requests", + "columnsFrom": [ + "request_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "purchase_request_items_material_id_materials_id_fk": { + "name": "purchase_request_items_material_id_materials_id_fk", + "tableFrom": "purchase_request_items", + "tableTo": "materials", + "columnsFrom": [ + "material_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "purchase_request_items_id": { + "name": "purchase_request_items_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "purchase_requests": { + "name": "purchase_requests", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "request_no": { + "name": "request_no", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "request_type": { + "name": "request_type", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "requester_id": { + "name": "requester_id", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "request_date": { + "name": "request_date", + "type": "datetime", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'draft'" + }, + "remarks": { + "name": "remarks", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + } + }, + "indexes": {}, + "foreignKeys": { + "purchase_requests_requester_id_employees_id_fk": { + "name": "purchase_requests_requester_id_employees_id_fk", + "tableFrom": "purchase_requests", + "tableTo": "employees", + "columnsFrom": [ + "requester_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "purchase_requests_id": { + "name": "purchase_requests_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": { + "purchase_requests_request_no_unique": { + "name": "purchase_requests_request_no_unique", + "columns": [ + "request_no" + ] + } + }, + "checkConstraint": {} + }, + "sales_order_items": { + "name": "sales_order_items", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "order_id": { + "name": "order_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "product_id": { + "name": "product_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "quantity": { + "name": "quantity", + "type": "decimal(15,4)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "unit": { + "name": "unit", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "unit_price": { + "name": "unit_price", + "type": "decimal(15,4)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "amount": { + "name": "amount", + "type": "decimal(15,4)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "delivery_date": { + "name": "delivery_date", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'pending'" + }, + "remarks": { + "name": "remarks", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + } + }, + "indexes": {}, + "foreignKeys": { + "sales_order_items_order_id_sales_orders_id_fk": { + "name": "sales_order_items_order_id_sales_orders_id_fk", + "tableFrom": "sales_order_items", + "tableTo": "sales_orders", + "columnsFrom": [ + "order_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "sales_order_items_product_id_products_id_fk": { + "name": "sales_order_items_product_id_products_id_fk", + "tableFrom": "sales_order_items", + "tableTo": "products", + "columnsFrom": [ + "product_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "sales_order_items_id": { + "name": "sales_order_items_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "sales_orders": { + "name": "sales_orders", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "order_no": { + "name": "order_no", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "customer_id": { + "name": "customer_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "order_date": { + "name": "order_date", + "type": "datetime", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "delivery_date": { + "name": "delivery_date", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'draft'" + }, + "total_amount": { + "name": "total_amount", + "type": "decimal(15,2)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'0'" + }, + "remarks": { + "name": "remarks", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_by": { + "name": "created_by", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + }, + "updated_at": { + "name": "updated_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + } + }, + "indexes": {}, + "foreignKeys": { + "sales_orders_customer_id_customers_id_fk": { + "name": "sales_orders_customer_id_customers_id_fk", + "tableFrom": "sales_orders", + "tableTo": "customers", + "columnsFrom": [ + "customer_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "sales_orders_id": { + "name": "sales_orders_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": { + "sales_orders_order_no_unique": { + "name": "sales_orders_order_no_unique", + "columns": [ + "order_no" + ] + } + }, + "checkConstraint": {} + }, + "sample_requests": { + "name": "sample_requests", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "request_no": { + "name": "request_no", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "customer_id": { + "name": "customer_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "product_id": { + "name": "product_id", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "product_name": { + "name": "product_name", + "type": "varchar(200)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "specification": { + "name": "specification", + "type": "varchar(200)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "quantity": { + "name": "quantity", + "type": "decimal(15,4)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "unit": { + "name": "unit", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "request_date": { + "name": "request_date", + "type": "datetime", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "required_date": { + "name": "required_date", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'draft'" + }, + "cost": { + "name": "cost", + "type": "decimal(15,2)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "qr_code": { + "name": "qr_code", + "type": "varchar(100)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "remarks": { + "name": "remarks", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "requester_id": { + "name": "requester_id", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + }, + "updated_at": { + "name": "updated_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + } + }, + "indexes": {}, + "foreignKeys": { + "sample_requests_customer_id_customers_id_fk": { + "name": "sample_requests_customer_id_customers_id_fk", + "tableFrom": "sample_requests", + "tableTo": "customers", + "columnsFrom": [ + "customer_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "sample_requests_product_id_products_id_fk": { + "name": "sample_requests_product_id_products_id_fk", + "tableFrom": "sample_requests", + "tableTo": "products", + "columnsFrom": [ + "product_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "sample_requests_requester_id_employees_id_fk": { + "name": "sample_requests_requester_id_employees_id_fk", + "tableFrom": "sample_requests", + "tableTo": "employees", + "columnsFrom": [ + "requester_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "sample_requests_id": { + "name": "sample_requests_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": { + "sample_requests_request_no_unique": { + "name": "sample_requests_request_no_unique", + "columns": [ + "request_no" + ] + }, + "sample_requests_qr_code_unique": { + "name": "sample_requests_qr_code_unique", + "columns": [ + "qr_code" + ] + } + }, + "checkConstraint": {} + }, + "scan_logs": { + "name": "scan_logs", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "scan_type": { + "name": "scan_type", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "qr_content": { + "name": "qr_content", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "label_no": { + "name": "label_no", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "operation": { + "name": "operation", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "result": { + "name": "result", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'success'" + }, + "message": { + "name": "message", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "operator_id": { + "name": "operator_id", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "operator_name": { + "name": "operator_name", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "scan_time": { + "name": "scan_time", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + }, + "ip_address": { + "name": "ip_address", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "scan_logs_operator_id_employees_id_fk": { + "name": "scan_logs_operator_id_employees_id_fk", + "tableFrom": "scan_logs", + "tableTo": "employees", + "columnsFrom": [ + "operator_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "scan_logs_id": { + "name": "scan_logs_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "suppliers": { + "name": "suppliers", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "code": { + "name": "code", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "varchar(200)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "contact": { + "name": "contact", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "phone": { + "name": "phone", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "address": { + "name": "address", + "type": "varchar(500)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'active'" + }, + "created_at": { + "name": "created_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + }, + "updated_at": { + "name": "updated_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "suppliers_id": { + "name": "suppliers_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": { + "suppliers_code_unique": { + "name": "suppliers_code_unique", + "columns": [ + "code" + ] + } + }, + "checkConstraint": {} + }, + "trace_records": { + "name": "trace_records", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "trace_no": { + "name": "trace_no", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "card_id": { + "name": "card_id", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "card_no": { + "name": "card_no", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "work_order_no": { + "name": "work_order_no", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "product_code": { + "name": "product_code", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "main_label_id": { + "name": "main_label_id", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "trace_type": { + "name": "trace_type", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'forward'" + }, + "operator_id": { + "name": "operator_id", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "operator_name": { + "name": "operator_name", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "trace_time": { + "name": "trace_time", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + }, + "remark": { + "name": "remark", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + } + }, + "indexes": {}, + "foreignKeys": { + "trace_records_card_id_process_cards_id_fk": { + "name": "trace_records_card_id_process_cards_id_fk", + "tableFrom": "trace_records", + "tableTo": "process_cards", + "columnsFrom": [ + "card_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "trace_records_main_label_id_material_labels_id_fk": { + "name": "trace_records_main_label_id_material_labels_id_fk", + "tableFrom": "trace_records", + "tableTo": "material_labels", + "columnsFrom": [ + "main_label_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "trace_records_operator_id_employees_id_fk": { + "name": "trace_records_operator_id_employees_id_fk", + "tableFrom": "trace_records", + "tableTo": "employees", + "columnsFrom": [ + "operator_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "trace_records_id": { + "name": "trace_records_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": { + "trace_records_trace_no_unique": { + "name": "trace_records_trace_no_unique", + "columns": [ + "trace_no" + ] + } + }, + "checkConstraint": {} + }, + "vehicles": { + "name": "vehicles", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "plate_no": { + "name": "plate_no", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "vehicle_type": { + "name": "vehicle_type", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "volume": { + "name": "volume", + "type": "decimal(10,2)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "load_weight": { + "name": "load_weight", + "type": "decimal(10,2)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "driver": { + "name": "driver", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "driver_phone": { + "name": "driver_phone", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'available'" + }, + "created_at": { + "name": "created_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "vehicles_id": { + "name": "vehicles_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": { + "vehicles_plate_no_unique": { + "name": "vehicles_plate_no_unique", + "columns": [ + "plate_no" + ] + } + }, + "checkConstraint": {} + }, + "warehouses": { + "name": "warehouses", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "code": { + "name": "code", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "type": { + "name": "type", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "manager": { + "name": "manager", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'active'" + }, + "created_at": { + "name": "created_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "warehouses_id": { + "name": "warehouses_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": { + "warehouses_code_unique": { + "name": "warehouses_code_unique", + "columns": [ + "code" + ] + } + }, + "checkConstraint": {} + }, + "work_order_processes": { + "name": "work_order_processes", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "work_order_id": { + "name": "work_order_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "process_id": { + "name": "process_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "equipment_id": { + "name": "equipment_id", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "plan_qty": { + "name": "plan_qty", + "type": "decimal(15,4)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "completed_qty": { + "name": "completed_qty", + "type": "decimal(15,4)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'0'" + }, + "scrap_qty": { + "name": "scrap_qty", + "type": "decimal(15,4)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'0'" + }, + "status": { + "name": "status", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'pending'" + }, + "start_time": { + "name": "start_time", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "end_time": { + "name": "end_time", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + } + }, + "indexes": {}, + "foreignKeys": { + "work_order_processes_work_order_id_work_orders_id_fk": { + "name": "work_order_processes_work_order_id_work_orders_id_fk", + "tableFrom": "work_order_processes", + "tableTo": "work_orders", + "columnsFrom": [ + "work_order_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "work_order_processes_process_id_processes_id_fk": { + "name": "work_order_processes_process_id_processes_id_fk", + "tableFrom": "work_order_processes", + "tableTo": "processes", + "columnsFrom": [ + "process_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "work_order_processes_equipment_id_equipments_id_fk": { + "name": "work_order_processes_equipment_id_equipments_id_fk", + "tableFrom": "work_order_processes", + "tableTo": "equipments", + "columnsFrom": [ + "equipment_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "work_order_processes_id": { + "name": "work_order_processes_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "work_orders": { + "name": "work_orders", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "order_no": { + "name": "order_no", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "qr_code": { + "name": "qr_code", + "type": "varchar(100)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "sales_order_id": { + "name": "sales_order_id", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "sales_order_item_id": { + "name": "sales_order_item_id", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "product_id": { + "name": "product_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "bom_id": { + "name": "bom_id", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "quantity": { + "name": "quantity", + "type": "decimal(15,4)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "completed_qty": { + "name": "completed_qty", + "type": "decimal(15,4)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'0'" + }, + "scrap_qty": { + "name": "scrap_qty", + "type": "decimal(15,4)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'0'" + }, + "plan_start_date": { + "name": "plan_start_date", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "plan_end_date": { + "name": "plan_end_date", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "actual_start_date": { + "name": "actual_start_date", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "actual_end_date": { + "name": "actual_end_date", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'created'" + }, + "priority": { + "name": "priority", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": 5 + }, + "workshop": { + "name": "workshop", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "remarks": { + "name": "remarks", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_by": { + "name": "created_by", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + }, + "updated_at": { + "name": "updated_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + } + }, + "indexes": {}, + "foreignKeys": { + "work_orders_sales_order_id_sales_orders_id_fk": { + "name": "work_orders_sales_order_id_sales_orders_id_fk", + "tableFrom": "work_orders", + "tableTo": "sales_orders", + "columnsFrom": [ + "sales_order_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "work_orders_sales_order_item_id_sales_order_items_id_fk": { + "name": "work_orders_sales_order_item_id_sales_order_items_id_fk", + "tableFrom": "work_orders", + "tableTo": "sales_order_items", + "columnsFrom": [ + "sales_order_item_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "work_orders_product_id_products_id_fk": { + "name": "work_orders_product_id_products_id_fk", + "tableFrom": "work_orders", + "tableTo": "products", + "columnsFrom": [ + "product_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "work_orders_bom_id_boms_id_fk": { + "name": "work_orders_bom_id_boms_id_fk", + "tableFrom": "work_orders", + "tableTo": "boms", + "columnsFrom": [ + "bom_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "work_orders_id": { + "name": "work_orders_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": { + "work_orders_order_no_unique": { + "name": "work_orders_order_no_unique", + "columns": [ + "order_no" + ] + }, + "work_orders_qr_code_unique": { + "name": "work_orders_qr_code_unique", + "columns": [ + "qr_code" + ] + } + }, + "checkConstraint": {} + } + }, + "views": {}, + "_meta": { + "schemas": {}, + "tables": {}, + "columns": {} + }, + "internal": { + "tables": {}, + "indexes": {} + } +} \ No newline at end of file diff --git a/drizzle/meta/0001_snapshot.json b/drizzle/meta/0001_snapshot.json new file mode 100644 index 00000000..1770077b --- /dev/null +++ b/drizzle/meta/0001_snapshot.json @@ -0,0 +1,5607 @@ +{ + "version": "5", + "dialect": "mysql", + "id": "c01e351c-5642-4b86-965b-c9e8f89909fe", + "prevId": "f4c9546c-b8bc-4070-9697-ef8d9185b2ed", + "tables": { + "bom_items": { + "name": "bom_items", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "bom_id": { + "name": "bom_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "material_id": { + "name": "material_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "quantity": { + "name": "quantity", + "type": "decimal(15,6)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "unit": { + "name": "unit", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "loss_rate": { + "name": "loss_rate", + "type": "decimal(5,2)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'0'" + }, + "sequence": { + "name": "sequence", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": 0 + }, + "remarks": { + "name": "remarks", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + } + }, + "indexes": {}, + "foreignKeys": { + "bom_items_bom_id_boms_id_fk": { + "name": "bom_items_bom_id_boms_id_fk", + "tableFrom": "bom_items", + "tableTo": "boms", + "columnsFrom": [ + "bom_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "bom_items_material_id_materials_id_fk": { + "name": "bom_items_material_id_materials_id_fk", + "tableFrom": "bom_items", + "tableTo": "materials", + "columnsFrom": [ + "material_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "bom_items_id": { + "name": "bom_items_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "boms": { + "name": "boms", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "product_id": { + "name": "product_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "version": { + "name": "version", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'V1.0'" + }, + "status": { + "name": "status", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'active'" + }, + "effective_date": { + "name": "effective_date", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "remarks": { + "name": "remarks", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + }, + "updated_at": { + "name": "updated_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + } + }, + "indexes": {}, + "foreignKeys": { + "boms_product_id_products_id_fk": { + "name": "boms_product_id_products_id_fk", + "tableFrom": "boms", + "tableTo": "products", + "columnsFrom": [ + "product_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "boms_id": { + "name": "boms_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "customers": { + "name": "customers", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "code": { + "name": "code", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "varchar(200)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "short_name": { + "name": "short_name", + "type": "varchar(100)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "contact": { + "name": "contact", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "phone": { + "name": "phone", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "address": { + "name": "address", + "type": "varchar(500)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "credit_limit": { + "name": "credit_limit", + "type": "decimal(15,2)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'0'" + }, + "credit_used": { + "name": "credit_used", + "type": "decimal(15,2)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'0'" + }, + "status": { + "name": "status", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'active'" + }, + "remarks": { + "name": "remarks", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + }, + "updated_at": { + "name": "updated_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "customers_id": { + "name": "customers_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": { + "customers_code_unique": { + "name": "customers_code_unique", + "columns": [ + "code" + ] + } + }, + "checkConstraint": {} + }, + "cutting_details": { + "name": "cutting_details", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "record_id": { + "name": "record_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "new_label_id": { + "name": "new_label_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "new_label_no": { + "name": "new_label_no", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "cut_width": { + "name": "cut_width", + "type": "decimal(18,2)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "sequence": { + "name": "sequence", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + } + }, + "indexes": {}, + "foreignKeys": { + "cutting_details_record_id_cutting_records_id_fk": { + "name": "cutting_details_record_id_cutting_records_id_fk", + "tableFrom": "cutting_details", + "tableTo": "cutting_records", + "columnsFrom": [ + "record_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "cutting_details_new_label_id_material_labels_id_fk": { + "name": "cutting_details_new_label_id_material_labels_id_fk", + "tableFrom": "cutting_details", + "tableTo": "material_labels", + "columnsFrom": [ + "new_label_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "cutting_details_id": { + "name": "cutting_details_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "cutting_records": { + "name": "cutting_records", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "record_no": { + "name": "record_no", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "source_label_id": { + "name": "source_label_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "source_label_no": { + "name": "source_label_no", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "cut_width_str": { + "name": "cut_width_str", + "type": "varchar(200)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "original_width": { + "name": "original_width", + "type": "decimal(18,2)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "cut_total_width": { + "name": "cut_total_width", + "type": "decimal(18,2)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "remain_width": { + "name": "remain_width", + "type": "decimal(18,2)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "operator_id": { + "name": "operator_id", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "operator_name": { + "name": "operator_name", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "cut_time": { + "name": "cut_time", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + }, + "remark": { + "name": "remark", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'active'" + }, + "created_at": { + "name": "created_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + } + }, + "indexes": {}, + "foreignKeys": { + "cutting_records_source_label_id_material_labels_id_fk": { + "name": "cutting_records_source_label_id_material_labels_id_fk", + "tableFrom": "cutting_records", + "tableTo": "material_labels", + "columnsFrom": [ + "source_label_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "cutting_records_operator_id_employees_id_fk": { + "name": "cutting_records_operator_id_employees_id_fk", + "tableFrom": "cutting_records", + "tableTo": "employees", + "columnsFrom": [ + "operator_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "cutting_records_id": { + "name": "cutting_records_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": { + "cutting_records_record_no_unique": { + "name": "cutting_records_record_no_unique", + "columns": [ + "record_no" + ] + } + }, + "checkConstraint": {} + }, + "defect_records": { + "name": "defect_records", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "inspection_id": { + "name": "inspection_id", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "batch_id": { + "name": "batch_id", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "defect_type": { + "name": "defect_type", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "defect_qty": { + "name": "defect_qty", + "type": "decimal(15,4)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "disposition": { + "name": "disposition", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'pending'" + }, + "handler_id": { + "name": "handler_id", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "handled_at": { + "name": "handled_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "remarks": { + "name": "remarks", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + } + }, + "indexes": {}, + "foreignKeys": { + "defect_records_inspection_id_inspection_records_id_fk": { + "name": "defect_records_inspection_id_inspection_records_id_fk", + "tableFrom": "defect_records", + "tableTo": "inspection_records", + "columnsFrom": [ + "inspection_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "defect_records_batch_id_inventory_batches_id_fk": { + "name": "defect_records_batch_id_inventory_batches_id_fk", + "tableFrom": "defect_records", + "tableTo": "inventory_batches", + "columnsFrom": [ + "batch_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "defect_records_handler_id_employees_id_fk": { + "name": "defect_records_handler_id_employees_id_fk", + "tableFrom": "defect_records", + "tableTo": "employees", + "columnsFrom": [ + "handler_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "defect_records_id": { + "name": "defect_records_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "delivery_items": { + "name": "delivery_items", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "delivery_id": { + "name": "delivery_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "batch_id": { + "name": "batch_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "quantity": { + "name": "quantity", + "type": "decimal(15,4)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "pallet_no": { + "name": "pallet_no", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "loaded_at": { + "name": "loaded_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "confirmed_at": { + "name": "confirmed_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'pending'" + }, + "remarks": { + "name": "remarks", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + } + }, + "indexes": {}, + "foreignKeys": { + "delivery_items_delivery_id_delivery_orders_id_fk": { + "name": "delivery_items_delivery_id_delivery_orders_id_fk", + "tableFrom": "delivery_items", + "tableTo": "delivery_orders", + "columnsFrom": [ + "delivery_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "delivery_items_batch_id_inventory_batches_id_fk": { + "name": "delivery_items_batch_id_inventory_batches_id_fk", + "tableFrom": "delivery_items", + "tableTo": "inventory_batches", + "columnsFrom": [ + "batch_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "delivery_items_id": { + "name": "delivery_items_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "delivery_orders": { + "name": "delivery_orders", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "delivery_no": { + "name": "delivery_no", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "vehicle_id": { + "name": "vehicle_id", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "sales_order_id": { + "name": "sales_order_id", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "customer_id": { + "name": "customer_id", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "delivery_address": { + "name": "delivery_address", + "type": "varchar(500)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "delivery_window": { + "name": "delivery_window", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "plan_date": { + "name": "plan_date", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "actual_date": { + "name": "actual_date", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'planned'" + }, + "total_weight": { + "name": "total_weight", + "type": "decimal(10,2)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "total_volume": { + "name": "total_volume", + "type": "decimal(10,2)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "remarks": { + "name": "remarks", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + }, + "updated_at": { + "name": "updated_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + } + }, + "indexes": {}, + "foreignKeys": { + "delivery_orders_vehicle_id_vehicles_id_fk": { + "name": "delivery_orders_vehicle_id_vehicles_id_fk", + "tableFrom": "delivery_orders", + "tableTo": "vehicles", + "columnsFrom": [ + "vehicle_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "delivery_orders_sales_order_id_sales_orders_id_fk": { + "name": "delivery_orders_sales_order_id_sales_orders_id_fk", + "tableFrom": "delivery_orders", + "tableTo": "sales_orders", + "columnsFrom": [ + "sales_order_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "delivery_orders_customer_id_customers_id_fk": { + "name": "delivery_orders_customer_id_customers_id_fk", + "tableFrom": "delivery_orders", + "tableTo": "customers", + "columnsFrom": [ + "customer_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "delivery_orders_id": { + "name": "delivery_orders_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": { + "delivery_orders_delivery_no_unique": { + "name": "delivery_orders_delivery_no_unique", + "columns": [ + "delivery_no" + ] + } + }, + "checkConstraint": {} + }, + "delivery_receipts": { + "name": "delivery_receipts", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "delivery_id": { + "name": "delivery_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "receiver_name": { + "name": "receiver_name", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "receiver_phone": { + "name": "receiver_phone", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "received_at": { + "name": "received_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "signature": { + "name": "signature", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "photos": { + "name": "photos", + "type": "json", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "remarks": { + "name": "remarks", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + } + }, + "indexes": {}, + "foreignKeys": { + "delivery_receipts_delivery_id_delivery_orders_id_fk": { + "name": "delivery_receipts_delivery_id_delivery_orders_id_fk", + "tableFrom": "delivery_receipts", + "tableTo": "delivery_orders", + "columnsFrom": [ + "delivery_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "delivery_receipts_id": { + "name": "delivery_receipts_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "employees": { + "name": "employees", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "code": { + "name": "code", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "department": { + "name": "department", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "position": { + "name": "position", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "phone": { + "name": "phone", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'active'" + }, + "created_at": { + "name": "created_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + }, + "updated_at": { + "name": "updated_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "employees_id": { + "name": "employees_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": { + "employees_code_unique": { + "name": "employees_code_unique", + "columns": [ + "code" + ] + } + }, + "checkConstraint": {} + }, + "equipments": { + "name": "equipments", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "code": { + "name": "code", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "varchar(200)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "model": { + "name": "model", + "type": "varchar(100)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "workshop": { + "name": "workshop", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "commission_date": { + "name": "commission_date", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "maintenance_cycle": { + "name": "maintenance_cycle", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "last_maintenance": { + "name": "last_maintenance", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "next_maintenance": { + "name": "next_maintenance", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'active'" + }, + "created_at": { + "name": "created_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + }, + "updated_at": { + "name": "updated_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "equipments_id": { + "name": "equipments_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": { + "equipments_code_unique": { + "name": "equipments_code_unique", + "columns": [ + "code" + ] + } + }, + "checkConstraint": {} + }, + "inspection_records": { + "name": "inspection_records", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "inspection_no": { + "name": "inspection_no", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "type": { + "name": "type", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "standard_id": { + "name": "standard_id", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "batch_id": { + "name": "batch_id", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "work_order_id": { + "name": "work_order_id", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "product_id": { + "name": "product_id", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "material_id": { + "name": "material_id", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "sample_qty": { + "name": "sample_qty", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "pass_qty": { + "name": "pass_qty", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "fail_qty": { + "name": "fail_qty", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "result": { + "name": "result", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "inspector_id": { + "name": "inspector_id", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "inspected_at": { + "name": "inspected_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + }, + "inspection_data": { + "name": "inspection_data", + "type": "json", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "remarks": { + "name": "remarks", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + } + }, + "indexes": {}, + "foreignKeys": { + "inspection_records_standard_id_inspection_standards_id_fk": { + "name": "inspection_records_standard_id_inspection_standards_id_fk", + "tableFrom": "inspection_records", + "tableTo": "inspection_standards", + "columnsFrom": [ + "standard_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "inspection_records_batch_id_inventory_batches_id_fk": { + "name": "inspection_records_batch_id_inventory_batches_id_fk", + "tableFrom": "inspection_records", + "tableTo": "inventory_batches", + "columnsFrom": [ + "batch_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "inspection_records_work_order_id_work_orders_id_fk": { + "name": "inspection_records_work_order_id_work_orders_id_fk", + "tableFrom": "inspection_records", + "tableTo": "work_orders", + "columnsFrom": [ + "work_order_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "inspection_records_product_id_products_id_fk": { + "name": "inspection_records_product_id_products_id_fk", + "tableFrom": "inspection_records", + "tableTo": "products", + "columnsFrom": [ + "product_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "inspection_records_material_id_materials_id_fk": { + "name": "inspection_records_material_id_materials_id_fk", + "tableFrom": "inspection_records", + "tableTo": "materials", + "columnsFrom": [ + "material_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "inspection_records_inspector_id_employees_id_fk": { + "name": "inspection_records_inspector_id_employees_id_fk", + "tableFrom": "inspection_records", + "tableTo": "employees", + "columnsFrom": [ + "inspector_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "inspection_records_id": { + "name": "inspection_records_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": { + "inspection_records_inspection_no_unique": { + "name": "inspection_records_inspection_no_unique", + "columns": [ + "inspection_no" + ] + } + }, + "checkConstraint": {} + }, + "inspection_standards": { + "name": "inspection_standards", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "code": { + "name": "code", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "varchar(200)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "type": { + "name": "type", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "product_id": { + "name": "product_id", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "material_id": { + "name": "material_id", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "inspection_items": { + "name": "inspection_items", + "type": "json", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'active'" + }, + "created_at": { + "name": "created_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + } + }, + "indexes": {}, + "foreignKeys": { + "inspection_standards_product_id_products_id_fk": { + "name": "inspection_standards_product_id_products_id_fk", + "tableFrom": "inspection_standards", + "tableTo": "products", + "columnsFrom": [ + "product_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "inspection_standards_material_id_materials_id_fk": { + "name": "inspection_standards_material_id_materials_id_fk", + "tableFrom": "inspection_standards", + "tableTo": "materials", + "columnsFrom": [ + "material_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "inspection_standards_id": { + "name": "inspection_standards_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": { + "inspection_standards_code_unique": { + "name": "inspection_standards_code_unique", + "columns": [ + "code" + ] + } + }, + "checkConstraint": {} + }, + "inv_inbound_item": { + "name": "inv_inbound_item", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "order_id": { + "name": "order_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "material_id": { + "name": "material_id", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "material_name": { + "name": "material_name", + "type": "varchar(200)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "material_spec": { + "name": "material_spec", + "type": "varchar(200)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "batch_no": { + "name": "batch_no", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "quantity": { + "name": "quantity", + "type": "decimal(15,3)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "unit": { + "name": "unit", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "unit_price": { + "name": "unit_price", + "type": "decimal(15,4)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "total_price": { + "name": "total_price", + "type": "decimal(15,4)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "warehouse_location": { + "name": "warehouse_location", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "produce_date": { + "name": "produce_date", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "create_time": { + "name": "create_time", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "inv_inbound_item_id": { + "name": "inv_inbound_item_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "inv_inbound_order": { + "name": "inv_inbound_order", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "order_no": { + "name": "order_no", + "type": "varchar(30)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "order_type": { + "name": "order_type", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'purchase'" + }, + "warehouse_id": { + "name": "warehouse_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "supplier_name": { + "name": "supplier_name", + "type": "varchar(100)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "inbound_date": { + "name": "inbound_date", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "total_quantity": { + "name": "total_quantity", + "type": "decimal(15,3)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'0'" + }, + "total_amount": { + "name": "total_amount", + "type": "decimal(15,2)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'pending'" + }, + "qc_status": { + "name": "qc_status", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'pending'" + }, + "remark": { + "name": "remark", + "type": "varchar(500)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "deleted": { + "name": "deleted", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": false + }, + "create_time": { + "name": "create_time", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + }, + "update_time": { + "name": "update_time", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "inv_inbound_order_id": { + "name": "inv_inbound_order_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "inventory_batches": { + "name": "inventory_batches", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "batch_no": { + "name": "batch_no", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "qr_code": { + "name": "qr_code", + "type": "varchar(100)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "material_id": { + "name": "material_id", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "product_id": { + "name": "product_id", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "warehouse_id": { + "name": "warehouse_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "location_id": { + "name": "location_id", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "quantity": { + "name": "quantity", + "type": "decimal(15,4)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "available_qty": { + "name": "available_qty", + "type": "decimal(15,4)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "reserved_qty": { + "name": "reserved_qty", + "type": "decimal(15,4)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'0'" + }, + "unit": { + "name": "unit", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "source_type": { + "name": "source_type", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "source_no": { + "name": "source_no", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "parent_batch_no": { + "name": "parent_batch_no", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "expiry_date": { + "name": "expiry_date", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "production_date": { + "name": "production_date", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'available'" + }, + "alert_level": { + "name": "alert_level", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'normal'" + }, + "last_alert_time": { + "name": "last_alert_time", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "inspection_status": { + "name": "inspection_status", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'pending'" + }, + "quarantine_status": { + "name": "quarantine_status", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'none'" + }, + "created_at": { + "name": "created_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + }, + "updated_at": { + "name": "updated_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + } + }, + "indexes": {}, + "foreignKeys": { + "inventory_batches_material_id_materials_id_fk": { + "name": "inventory_batches_material_id_materials_id_fk", + "tableFrom": "inventory_batches", + "tableTo": "materials", + "columnsFrom": [ + "material_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "inventory_batches_product_id_products_id_fk": { + "name": "inventory_batches_product_id_products_id_fk", + "tableFrom": "inventory_batches", + "tableTo": "products", + "columnsFrom": [ + "product_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "inventory_batches_warehouse_id_warehouses_id_fk": { + "name": "inventory_batches_warehouse_id_warehouses_id_fk", + "tableFrom": "inventory_batches", + "tableTo": "warehouses", + "columnsFrom": [ + "warehouse_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "inventory_batches_location_id_locations_id_fk": { + "name": "inventory_batches_location_id_locations_id_fk", + "tableFrom": "inventory_batches", + "tableTo": "locations", + "columnsFrom": [ + "location_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "inventory_batches_id": { + "name": "inventory_batches_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": { + "inventory_batches_batch_no_unique": { + "name": "inventory_batches_batch_no_unique", + "columns": [ + "batch_no" + ] + }, + "inventory_batches_qr_code_unique": { + "name": "inventory_batches_qr_code_unique", + "columns": [ + "qr_code" + ] + } + }, + "checkConstraint": {} + }, + "inventory_transactions": { + "name": "inventory_transactions", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "trans_no": { + "name": "trans_no", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "trans_type": { + "name": "trans_type", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "batch_id": { + "name": "batch_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "warehouse_id": { + "name": "warehouse_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "location_id": { + "name": "location_id", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "quantity": { + "name": "quantity", + "type": "decimal(15,4)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "before_qty": { + "name": "before_qty", + "type": "decimal(15,4)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "after_qty": { + "name": "after_qty", + "type": "decimal(15,4)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "source_type": { + "name": "source_type", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "source_no": { + "name": "source_no", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "operator_id": { + "name": "operator_id", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "operated_at": { + "name": "operated_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + }, + "remarks": { + "name": "remarks", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + } + }, + "indexes": {}, + "foreignKeys": { + "inventory_transactions_batch_id_inventory_batches_id_fk": { + "name": "inventory_transactions_batch_id_inventory_batches_id_fk", + "tableFrom": "inventory_transactions", + "tableTo": "inventory_batches", + "columnsFrom": [ + "batch_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "inventory_transactions_operator_id_employees_id_fk": { + "name": "inventory_transactions_operator_id_employees_id_fk", + "tableFrom": "inventory_transactions", + "tableTo": "employees", + "columnsFrom": [ + "operator_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "inventory_transactions_id": { + "name": "inventory_transactions_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": { + "inventory_transactions_trans_no_unique": { + "name": "inventory_transactions_trans_no_unique", + "columns": [ + "trans_no" + ] + } + }, + "checkConstraint": {} + }, + "locations": { + "name": "locations", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "code": { + "name": "code", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "warehouse_id": { + "name": "warehouse_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "zone": { + "name": "zone", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "shelf": { + "name": "shelf", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "layer": { + "name": "layer", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'active'" + }, + "created_at": { + "name": "created_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + } + }, + "indexes": {}, + "foreignKeys": { + "locations_warehouse_id_warehouses_id_fk": { + "name": "locations_warehouse_id_warehouses_id_fk", + "tableFrom": "locations", + "tableTo": "warehouses", + "columnsFrom": [ + "warehouse_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "locations_id": { + "name": "locations_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": { + "locations_code_unique": { + "name": "locations_code_unique", + "columns": [ + "code" + ] + } + }, + "checkConstraint": {} + }, + "maintenance_plans": { + "name": "maintenance_plans", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "equipment_id": { + "name": "equipment_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "plan_date": { + "name": "plan_date", + "type": "datetime", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "maintenance_type": { + "name": "maintenance_type", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'planned'" + }, + "executor_id": { + "name": "executor_id", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "executed_at": { + "name": "executed_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "remarks": { + "name": "remarks", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + } + }, + "indexes": {}, + "foreignKeys": { + "maintenance_plans_equipment_id_equipments_id_fk": { + "name": "maintenance_plans_equipment_id_equipments_id_fk", + "tableFrom": "maintenance_plans", + "tableTo": "equipments", + "columnsFrom": [ + "equipment_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "maintenance_plans_executor_id_employees_id_fk": { + "name": "maintenance_plans_executor_id_employees_id_fk", + "tableFrom": "maintenance_plans", + "tableTo": "employees", + "columnsFrom": [ + "executor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "maintenance_plans_id": { + "name": "maintenance_plans_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "material_labels": { + "name": "material_labels", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "label_no": { + "name": "label_no", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "qr_code": { + "name": "qr_code", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "purchase_order_no": { + "name": "purchase_order_no", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "supplier_name": { + "name": "supplier_name", + "type": "varchar(200)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "receive_date": { + "name": "receive_date", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "material_code": { + "name": "material_code", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "material_name": { + "name": "material_name", + "type": "varchar(200)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "specification": { + "name": "specification", + "type": "varchar(200)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "unit": { + "name": "unit", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "batch_no": { + "name": "batch_no", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "quantity": { + "name": "quantity", + "type": "decimal(18,4)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'0'" + }, + "package_qty": { + "name": "package_qty", + "type": "decimal(18,4)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'0'" + }, + "width": { + "name": "width", + "type": "decimal(18,2)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "length_per_roll": { + "name": "length_per_roll", + "type": "decimal(18,2)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "remark": { + "name": "remark", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "color_code": { + "name": "color_code", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "mix_remark": { + "name": "mix_remark", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "warehouse_id": { + "name": "warehouse_id", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "location_id": { + "name": "location_id", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "is_main_material": { + "name": "is_main_material", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": false + }, + "is_used": { + "name": "is_used", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": false + }, + "is_cut": { + "name": "is_cut", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": false + }, + "parent_label_id": { + "name": "parent_label_id", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'active'" + }, + "created_at": { + "name": "created_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + }, + "updated_at": { + "name": "updated_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + } + }, + "indexes": {}, + "foreignKeys": { + "material_labels_warehouse_id_warehouses_id_fk": { + "name": "material_labels_warehouse_id_warehouses_id_fk", + "tableFrom": "material_labels", + "tableTo": "warehouses", + "columnsFrom": [ + "warehouse_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "material_labels_location_id_locations_id_fk": { + "name": "material_labels_location_id_locations_id_fk", + "tableFrom": "material_labels", + "tableTo": "locations", + "columnsFrom": [ + "location_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "material_labels_id": { + "name": "material_labels_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": { + "material_labels_label_no_unique": { + "name": "material_labels_label_no_unique", + "columns": [ + "label_no" + ] + } + }, + "checkConstraint": {} + }, + "materials": { + "name": "materials", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "code": { + "name": "code", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "varchar(200)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "specification": { + "name": "specification", + "type": "varchar(200)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "unit": { + "name": "unit", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "category": { + "name": "category", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "safety_stock": { + "name": "safety_stock", + "type": "decimal(15,4)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'0'" + }, + "status": { + "name": "status", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'active'" + }, + "created_at": { + "name": "created_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + }, + "updated_at": { + "name": "updated_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "materials_id": { + "name": "materials_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": { + "materials_code_unique": { + "name": "materials_code_unique", + "columns": [ + "code" + ] + } + }, + "checkConstraint": {} + }, + "outsource_orders": { + "name": "outsource_orders", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "order_no": { + "name": "order_no", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "qr_code": { + "name": "qr_code", + "type": "varchar(100)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "supplier_id": { + "name": "supplier_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "work_order_id": { + "name": "work_order_id", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "process_id": { + "name": "process_id", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "send_qty": { + "name": "send_qty", + "type": "decimal(15,4)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "return_qty": { + "name": "return_qty", + "type": "decimal(15,4)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'0'" + }, + "scrap_qty": { + "name": "scrap_qty", + "type": "decimal(15,4)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'0'" + }, + "allowed_loss_rate": { + "name": "allowed_loss_rate", + "type": "decimal(5,2)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'0'" + }, + "send_date": { + "name": "send_date", + "type": "datetime", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "expected_return_date": { + "name": "expected_return_date", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "actual_return_date": { + "name": "actual_return_date", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'sent'" + }, + "amount": { + "name": "amount", + "type": "decimal(15,2)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "payment_status": { + "name": "payment_status", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'unpaid'" + }, + "remarks": { + "name": "remarks", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + }, + "updated_at": { + "name": "updated_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + } + }, + "indexes": {}, + "foreignKeys": { + "outsource_orders_supplier_id_suppliers_id_fk": { + "name": "outsource_orders_supplier_id_suppliers_id_fk", + "tableFrom": "outsource_orders", + "tableTo": "suppliers", + "columnsFrom": [ + "supplier_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "outsource_orders_work_order_id_work_orders_id_fk": { + "name": "outsource_orders_work_order_id_work_orders_id_fk", + "tableFrom": "outsource_orders", + "tableTo": "work_orders", + "columnsFrom": [ + "work_order_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "outsource_orders_process_id_processes_id_fk": { + "name": "outsource_orders_process_id_processes_id_fk", + "tableFrom": "outsource_orders", + "tableTo": "processes", + "columnsFrom": [ + "process_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "outsource_orders_id": { + "name": "outsource_orders_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": { + "outsource_orders_order_no_unique": { + "name": "outsource_orders_order_no_unique", + "columns": [ + "order_no" + ] + }, + "outsource_orders_qr_code_unique": { + "name": "outsource_orders_qr_code_unique", + "columns": [ + "qr_code" + ] + } + }, + "checkConstraint": {} + }, + "process_card_materials": { + "name": "process_card_materials", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "card_id": { + "name": "card_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "card_no": { + "name": "card_no", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "label_id": { + "name": "label_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "label_no": { + "name": "label_no", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "material_type": { + "name": "material_type", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'auxiliary'" + }, + "material_code": { + "name": "material_code", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "material_name": { + "name": "material_name", + "type": "varchar(200)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "specification": { + "name": "specification", + "type": "varchar(200)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "batch_no": { + "name": "batch_no", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "quantity": { + "name": "quantity", + "type": "decimal(18,4)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'0'" + }, + "unit": { + "name": "unit", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "remark": { + "name": "remark", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + } + }, + "indexes": {}, + "foreignKeys": { + "process_card_materials_card_id_process_cards_id_fk": { + "name": "process_card_materials_card_id_process_cards_id_fk", + "tableFrom": "process_card_materials", + "tableTo": "process_cards", + "columnsFrom": [ + "card_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "process_card_materials_label_id_material_labels_id_fk": { + "name": "process_card_materials_label_id_material_labels_id_fk", + "tableFrom": "process_card_materials", + "tableTo": "material_labels", + "columnsFrom": [ + "label_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "process_card_materials_id": { + "name": "process_card_materials_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "process_cards": { + "name": "process_cards", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "card_no": { + "name": "card_no", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "qr_code": { + "name": "qr_code", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "work_order_id": { + "name": "work_order_id", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "work_order_no": { + "name": "work_order_no", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "product_code": { + "name": "product_code", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "product_name": { + "name": "product_name", + "type": "varchar(200)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "material_spec": { + "name": "material_spec", + "type": "varchar(200)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "work_order_date": { + "name": "work_order_date", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "plan_qty": { + "name": "plan_qty", + "type": "decimal(18,4)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'0'" + }, + "main_label_id": { + "name": "main_label_id", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "main_label_no": { + "name": "main_label_no", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "burdening_status": { + "name": "burdening_status", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'pending'" + }, + "lock_status": { + "name": "lock_status", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'unlocked'" + }, + "create_user_id": { + "name": "create_user_id", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "create_user_name": { + "name": "create_user_name", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + }, + "updated_at": { + "name": "updated_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + } + }, + "indexes": {}, + "foreignKeys": { + "process_cards_work_order_id_work_orders_id_fk": { + "name": "process_cards_work_order_id_work_orders_id_fk", + "tableFrom": "process_cards", + "tableTo": "work_orders", + "columnsFrom": [ + "work_order_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "process_cards_main_label_id_material_labels_id_fk": { + "name": "process_cards_main_label_id_material_labels_id_fk", + "tableFrom": "process_cards", + "tableTo": "material_labels", + "columnsFrom": [ + "main_label_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "process_cards_create_user_id_employees_id_fk": { + "name": "process_cards_create_user_id_employees_id_fk", + "tableFrom": "process_cards", + "tableTo": "employees", + "columnsFrom": [ + "create_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "process_cards_id": { + "name": "process_cards_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": { + "process_cards_card_no_unique": { + "name": "process_cards_card_no_unique", + "columns": [ + "card_no" + ] + } + }, + "checkConstraint": {} + }, + "processes": { + "name": "processes", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "code": { + "name": "code", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "sequence": { + "name": "sequence", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "workcenter": { + "name": "workcenter", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "standard_time": { + "name": "standard_time", + "type": "decimal(10,2)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "setup_time": { + "name": "setup_time", + "type": "decimal(10,2)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'active'" + }, + "created_at": { + "name": "created_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "processes_id": { + "name": "processes_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": { + "processes_code_unique": { + "name": "processes_code_unique", + "columns": [ + "code" + ] + } + }, + "checkConstraint": {} + }, + "production_reports": { + "name": "production_reports", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "report_no": { + "name": "report_no", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "work_order_id": { + "name": "work_order_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "work_order_process_id": { + "name": "work_order_process_id", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "process_id": { + "name": "process_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "equipment_id": { + "name": "equipment_id", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "employee_id": { + "name": "employee_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "good_qty": { + "name": "good_qty", + "type": "decimal(15,4)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "scrap_qty": { + "name": "scrap_qty", + "type": "decimal(15,4)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'0'" + }, + "batch_no": { + "name": "batch_no", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "work_date": { + "name": "work_date", + "type": "datetime", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "start_time": { + "name": "start_time", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "end_time": { + "name": "end_time", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "work_minutes": { + "name": "work_minutes", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "efficiency": { + "name": "efficiency", + "type": "decimal(5,2)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'normal'" + }, + "remarks": { + "name": "remarks", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "reported_at": { + "name": "reported_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + }, + "created_at": { + "name": "created_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + } + }, + "indexes": {}, + "foreignKeys": { + "production_reports_work_order_id_work_orders_id_fk": { + "name": "production_reports_work_order_id_work_orders_id_fk", + "tableFrom": "production_reports", + "tableTo": "work_orders", + "columnsFrom": [ + "work_order_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "production_reports_work_order_process_id_work_order_processes_id_fk": { + "name": "production_reports_work_order_process_id_work_order_processes_id_fk", + "tableFrom": "production_reports", + "tableTo": "work_order_processes", + "columnsFrom": [ + "work_order_process_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "production_reports_process_id_processes_id_fk": { + "name": "production_reports_process_id_processes_id_fk", + "tableFrom": "production_reports", + "tableTo": "processes", + "columnsFrom": [ + "process_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "production_reports_equipment_id_equipments_id_fk": { + "name": "production_reports_equipment_id_equipments_id_fk", + "tableFrom": "production_reports", + "tableTo": "equipments", + "columnsFrom": [ + "equipment_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "production_reports_employee_id_employees_id_fk": { + "name": "production_reports_employee_id_employees_id_fk", + "tableFrom": "production_reports", + "tableTo": "employees", + "columnsFrom": [ + "employee_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "production_reports_id": { + "name": "production_reports_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": { + "production_reports_report_no_unique": { + "name": "production_reports_report_no_unique", + "columns": [ + "report_no" + ] + } + }, + "checkConstraint": {} + }, + "products": { + "name": "products", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "code": { + "name": "code", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "varchar(200)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "specification": { + "name": "specification", + "type": "varchar(200)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "unit": { + "name": "unit", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "category": { + "name": "category", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "bom_version": { + "name": "bom_version", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'V1.0'" + }, + "customer_id": { + "name": "customer_id", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'active'" + }, + "created_at": { + "name": "created_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + }, + "updated_at": { + "name": "updated_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + } + }, + "indexes": {}, + "foreignKeys": { + "products_customer_id_customers_id_fk": { + "name": "products_customer_id_customers_id_fk", + "tableFrom": "products", + "tableTo": "customers", + "columnsFrom": [ + "customer_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "products_id": { + "name": "products_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": { + "products_code_unique": { + "name": "products_code_unique", + "columns": [ + "code" + ] + } + }, + "checkConstraint": {} + }, + "purchase_order_items": { + "name": "purchase_order_items", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "order_id": { + "name": "order_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "material_id": { + "name": "material_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "quantity": { + "name": "quantity", + "type": "decimal(15,4)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "received_qty": { + "name": "received_qty", + "type": "decimal(15,4)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'0'" + }, + "unit": { + "name": "unit", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "unit_price": { + "name": "unit_price", + "type": "decimal(15,4)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "amount": { + "name": "amount", + "type": "decimal(15,4)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'pending'" + }, + "remarks": { + "name": "remarks", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + } + }, + "indexes": {}, + "foreignKeys": { + "purchase_order_items_order_id_purchase_orders_id_fk": { + "name": "purchase_order_items_order_id_purchase_orders_id_fk", + "tableFrom": "purchase_order_items", + "tableTo": "purchase_orders", + "columnsFrom": [ + "order_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "purchase_order_items_material_id_materials_id_fk": { + "name": "purchase_order_items_material_id_materials_id_fk", + "tableFrom": "purchase_order_items", + "tableTo": "materials", + "columnsFrom": [ + "material_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "purchase_order_items_id": { + "name": "purchase_order_items_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "purchase_orders": { + "name": "purchase_orders", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "order_no": { + "name": "order_no", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "supplier_id": { + "name": "supplier_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "order_date": { + "name": "order_date", + "type": "datetime", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "expected_date": { + "name": "expected_date", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'draft'" + }, + "total_amount": { + "name": "total_amount", + "type": "decimal(15,2)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'0'" + }, + "remarks": { + "name": "remarks", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_by": { + "name": "created_by", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + }, + "updated_at": { + "name": "updated_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + } + }, + "indexes": {}, + "foreignKeys": { + "purchase_orders_supplier_id_suppliers_id_fk": { + "name": "purchase_orders_supplier_id_suppliers_id_fk", + "tableFrom": "purchase_orders", + "tableTo": "suppliers", + "columnsFrom": [ + "supplier_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "purchase_orders_id": { + "name": "purchase_orders_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": { + "purchase_orders_order_no_unique": { + "name": "purchase_orders_order_no_unique", + "columns": [ + "order_no" + ] + } + }, + "checkConstraint": {} + }, + "purchase_request_items": { + "name": "purchase_request_items", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "request_id": { + "name": "request_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "material_id": { + "name": "material_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "quantity": { + "name": "quantity", + "type": "decimal(15,4)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "unit": { + "name": "unit", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "required_date": { + "name": "required_date", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'pending'" + }, + "remarks": { + "name": "remarks", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + } + }, + "indexes": {}, + "foreignKeys": { + "purchase_request_items_request_id_purchase_requests_id_fk": { + "name": "purchase_request_items_request_id_purchase_requests_id_fk", + "tableFrom": "purchase_request_items", + "tableTo": "purchase_requests", + "columnsFrom": [ + "request_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "purchase_request_items_material_id_materials_id_fk": { + "name": "purchase_request_items_material_id_materials_id_fk", + "tableFrom": "purchase_request_items", + "tableTo": "materials", + "columnsFrom": [ + "material_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "purchase_request_items_id": { + "name": "purchase_request_items_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "purchase_requests": { + "name": "purchase_requests", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "request_no": { + "name": "request_no", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "request_type": { + "name": "request_type", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "requester_id": { + "name": "requester_id", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "request_date": { + "name": "request_date", + "type": "datetime", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'draft'" + }, + "remarks": { + "name": "remarks", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + } + }, + "indexes": {}, + "foreignKeys": { + "purchase_requests_requester_id_employees_id_fk": { + "name": "purchase_requests_requester_id_employees_id_fk", + "tableFrom": "purchase_requests", + "tableTo": "employees", + "columnsFrom": [ + "requester_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "purchase_requests_id": { + "name": "purchase_requests_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": { + "purchase_requests_request_no_unique": { + "name": "purchase_requests_request_no_unique", + "columns": [ + "request_no" + ] + } + }, + "checkConstraint": {} + }, + "sales_order_items": { + "name": "sales_order_items", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "order_id": { + "name": "order_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "product_id": { + "name": "product_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "quantity": { + "name": "quantity", + "type": "decimal(15,4)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "unit": { + "name": "unit", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "unit_price": { + "name": "unit_price", + "type": "decimal(15,4)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "amount": { + "name": "amount", + "type": "decimal(15,4)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "delivery_date": { + "name": "delivery_date", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'pending'" + }, + "remarks": { + "name": "remarks", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + } + }, + "indexes": {}, + "foreignKeys": { + "sales_order_items_order_id_sales_orders_id_fk": { + "name": "sales_order_items_order_id_sales_orders_id_fk", + "tableFrom": "sales_order_items", + "tableTo": "sales_orders", + "columnsFrom": [ + "order_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "sales_order_items_product_id_products_id_fk": { + "name": "sales_order_items_product_id_products_id_fk", + "tableFrom": "sales_order_items", + "tableTo": "products", + "columnsFrom": [ + "product_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "sales_order_items_id": { + "name": "sales_order_items_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "sales_orders": { + "name": "sales_orders", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "order_no": { + "name": "order_no", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "customer_id": { + "name": "customer_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "order_date": { + "name": "order_date", + "type": "datetime", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "delivery_date": { + "name": "delivery_date", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'draft'" + }, + "total_amount": { + "name": "total_amount", + "type": "decimal(15,2)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'0'" + }, + "remarks": { + "name": "remarks", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_by": { + "name": "created_by", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + }, + "updated_at": { + "name": "updated_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + } + }, + "indexes": {}, + "foreignKeys": { + "sales_orders_customer_id_customers_id_fk": { + "name": "sales_orders_customer_id_customers_id_fk", + "tableFrom": "sales_orders", + "tableTo": "customers", + "columnsFrom": [ + "customer_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "sales_orders_id": { + "name": "sales_orders_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": { + "sales_orders_order_no_unique": { + "name": "sales_orders_order_no_unique", + "columns": [ + "order_no" + ] + } + }, + "checkConstraint": {} + }, + "sample_requests": { + "name": "sample_requests", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "request_no": { + "name": "request_no", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "customer_id": { + "name": "customer_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "product_id": { + "name": "product_id", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "product_name": { + "name": "product_name", + "type": "varchar(200)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "specification": { + "name": "specification", + "type": "varchar(200)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "quantity": { + "name": "quantity", + "type": "decimal(15,4)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "unit": { + "name": "unit", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "request_date": { + "name": "request_date", + "type": "datetime", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "required_date": { + "name": "required_date", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'draft'" + }, + "cost": { + "name": "cost", + "type": "decimal(15,2)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "qr_code": { + "name": "qr_code", + "type": "varchar(100)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "remarks": { + "name": "remarks", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "requester_id": { + "name": "requester_id", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + }, + "updated_at": { + "name": "updated_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + } + }, + "indexes": {}, + "foreignKeys": { + "sample_requests_customer_id_customers_id_fk": { + "name": "sample_requests_customer_id_customers_id_fk", + "tableFrom": "sample_requests", + "tableTo": "customers", + "columnsFrom": [ + "customer_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "sample_requests_product_id_products_id_fk": { + "name": "sample_requests_product_id_products_id_fk", + "tableFrom": "sample_requests", + "tableTo": "products", + "columnsFrom": [ + "product_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "sample_requests_requester_id_employees_id_fk": { + "name": "sample_requests_requester_id_employees_id_fk", + "tableFrom": "sample_requests", + "tableTo": "employees", + "columnsFrom": [ + "requester_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "sample_requests_id": { + "name": "sample_requests_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": { + "sample_requests_request_no_unique": { + "name": "sample_requests_request_no_unique", + "columns": [ + "request_no" + ] + }, + "sample_requests_qr_code_unique": { + "name": "sample_requests_qr_code_unique", + "columns": [ + "qr_code" + ] + } + }, + "checkConstraint": {} + }, + "scan_logs": { + "name": "scan_logs", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "scan_type": { + "name": "scan_type", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "qr_content": { + "name": "qr_content", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "label_no": { + "name": "label_no", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "operation": { + "name": "operation", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "result": { + "name": "result", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'success'" + }, + "message": { + "name": "message", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "operator_id": { + "name": "operator_id", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "operator_name": { + "name": "operator_name", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "scan_time": { + "name": "scan_time", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + }, + "ip_address": { + "name": "ip_address", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "scan_logs_operator_id_employees_id_fk": { + "name": "scan_logs_operator_id_employees_id_fk", + "tableFrom": "scan_logs", + "tableTo": "employees", + "columnsFrom": [ + "operator_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "scan_logs_id": { + "name": "scan_logs_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "suppliers": { + "name": "suppliers", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "code": { + "name": "code", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "varchar(200)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "contact": { + "name": "contact", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "phone": { + "name": "phone", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "address": { + "name": "address", + "type": "varchar(500)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'active'" + }, + "created_at": { + "name": "created_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + }, + "updated_at": { + "name": "updated_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "suppliers_id": { + "name": "suppliers_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": { + "suppliers_code_unique": { + "name": "suppliers_code_unique", + "columns": [ + "code" + ] + } + }, + "checkConstraint": {} + }, + "trace_records": { + "name": "trace_records", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "trace_no": { + "name": "trace_no", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "card_id": { + "name": "card_id", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "card_no": { + "name": "card_no", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "work_order_no": { + "name": "work_order_no", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "product_code": { + "name": "product_code", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "main_label_id": { + "name": "main_label_id", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "trace_type": { + "name": "trace_type", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'forward'" + }, + "operator_id": { + "name": "operator_id", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "operator_name": { + "name": "operator_name", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "trace_time": { + "name": "trace_time", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + }, + "remark": { + "name": "remark", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + } + }, + "indexes": {}, + "foreignKeys": { + "trace_records_card_id_process_cards_id_fk": { + "name": "trace_records_card_id_process_cards_id_fk", + "tableFrom": "trace_records", + "tableTo": "process_cards", + "columnsFrom": [ + "card_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "trace_records_main_label_id_material_labels_id_fk": { + "name": "trace_records_main_label_id_material_labels_id_fk", + "tableFrom": "trace_records", + "tableTo": "material_labels", + "columnsFrom": [ + "main_label_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "trace_records_operator_id_employees_id_fk": { + "name": "trace_records_operator_id_employees_id_fk", + "tableFrom": "trace_records", + "tableTo": "employees", + "columnsFrom": [ + "operator_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "trace_records_id": { + "name": "trace_records_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": { + "trace_records_trace_no_unique": { + "name": "trace_records_trace_no_unique", + "columns": [ + "trace_no" + ] + } + }, + "checkConstraint": {} + }, + "vehicles": { + "name": "vehicles", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "plate_no": { + "name": "plate_no", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "vehicle_type": { + "name": "vehicle_type", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "volume": { + "name": "volume", + "type": "decimal(10,2)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "load_weight": { + "name": "load_weight", + "type": "decimal(10,2)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "driver": { + "name": "driver", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "driver_phone": { + "name": "driver_phone", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'available'" + }, + "created_at": { + "name": "created_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "vehicles_id": { + "name": "vehicles_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": { + "vehicles_plate_no_unique": { + "name": "vehicles_plate_no_unique", + "columns": [ + "plate_no" + ] + } + }, + "checkConstraint": {} + }, + "warehouses": { + "name": "warehouses", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "code": { + "name": "code", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "type": { + "name": "type", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "manager": { + "name": "manager", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'active'" + }, + "created_at": { + "name": "created_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "warehouses_id": { + "name": "warehouses_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": { + "warehouses_code_unique": { + "name": "warehouses_code_unique", + "columns": [ + "code" + ] + } + }, + "checkConstraint": {} + }, + "work_order_processes": { + "name": "work_order_processes", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "work_order_id": { + "name": "work_order_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "process_id": { + "name": "process_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "equipment_id": { + "name": "equipment_id", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "plan_qty": { + "name": "plan_qty", + "type": "decimal(15,4)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "completed_qty": { + "name": "completed_qty", + "type": "decimal(15,4)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'0'" + }, + "scrap_qty": { + "name": "scrap_qty", + "type": "decimal(15,4)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'0'" + }, + "status": { + "name": "status", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'pending'" + }, + "start_time": { + "name": "start_time", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "end_time": { + "name": "end_time", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + } + }, + "indexes": {}, + "foreignKeys": { + "work_order_processes_work_order_id_work_orders_id_fk": { + "name": "work_order_processes_work_order_id_work_orders_id_fk", + "tableFrom": "work_order_processes", + "tableTo": "work_orders", + "columnsFrom": [ + "work_order_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "work_order_processes_process_id_processes_id_fk": { + "name": "work_order_processes_process_id_processes_id_fk", + "tableFrom": "work_order_processes", + "tableTo": "processes", + "columnsFrom": [ + "process_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "work_order_processes_equipment_id_equipments_id_fk": { + "name": "work_order_processes_equipment_id_equipments_id_fk", + "tableFrom": "work_order_processes", + "tableTo": "equipments", + "columnsFrom": [ + "equipment_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "work_order_processes_id": { + "name": "work_order_processes_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "work_orders": { + "name": "work_orders", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "order_no": { + "name": "order_no", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "qr_code": { + "name": "qr_code", + "type": "varchar(100)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "sales_order_id": { + "name": "sales_order_id", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "sales_order_item_id": { + "name": "sales_order_item_id", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "product_id": { + "name": "product_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "bom_id": { + "name": "bom_id", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "quantity": { + "name": "quantity", + "type": "decimal(15,4)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "completed_qty": { + "name": "completed_qty", + "type": "decimal(15,4)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'0'" + }, + "scrap_qty": { + "name": "scrap_qty", + "type": "decimal(15,4)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'0'" + }, + "plan_start_date": { + "name": "plan_start_date", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "plan_end_date": { + "name": "plan_end_date", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "actual_start_date": { + "name": "actual_start_date", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "actual_end_date": { + "name": "actual_end_date", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'created'" + }, + "priority": { + "name": "priority", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": 5 + }, + "workshop": { + "name": "workshop", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "remarks": { + "name": "remarks", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_by": { + "name": "created_by", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + }, + "updated_at": { + "name": "updated_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'CURRENT_TIMESTAMP'" + } + }, + "indexes": {}, + "foreignKeys": { + "work_orders_sales_order_id_sales_orders_id_fk": { + "name": "work_orders_sales_order_id_sales_orders_id_fk", + "tableFrom": "work_orders", + "tableTo": "sales_orders", + "columnsFrom": [ + "sales_order_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "work_orders_sales_order_item_id_sales_order_items_id_fk": { + "name": "work_orders_sales_order_item_id_sales_order_items_id_fk", + "tableFrom": "work_orders", + "tableTo": "sales_order_items", + "columnsFrom": [ + "sales_order_item_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "work_orders_product_id_products_id_fk": { + "name": "work_orders_product_id_products_id_fk", + "tableFrom": "work_orders", + "tableTo": "products", + "columnsFrom": [ + "product_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "work_orders_bom_id_boms_id_fk": { + "name": "work_orders_bom_id_boms_id_fk", + "tableFrom": "work_orders", + "tableTo": "boms", + "columnsFrom": [ + "bom_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "work_orders_id": { + "name": "work_orders_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": { + "work_orders_order_no_unique": { + "name": "work_orders_order_no_unique", + "columns": [ + "order_no" + ] + }, + "work_orders_qr_code_unique": { + "name": "work_orders_qr_code_unique", + "columns": [ + "qr_code" + ] + } + }, + "checkConstraint": {} + } + }, + "views": {}, + "_meta": { + "schemas": {}, + "tables": {}, + "columns": {} + }, + "internal": { + "tables": {}, + "indexes": {} + } +} \ No newline at end of file diff --git a/drizzle/meta/_journal.json b/drizzle/meta/_journal.json new file mode 100644 index 00000000..8bebdd0e --- /dev/null +++ b/drizzle/meta/_journal.json @@ -0,0 +1,20 @@ +{ + "version": "7", + "dialect": "mysql", + "entries": [ + { + "idx": 0, + "version": "5", + "when": 1782702988126, + "tag": "0000_chilly_retro_girl", + "breakpoints": true + }, + { + "idx": 1, + "version": "5", + "when": 1782703493354, + "tag": "0001_ordinary_valkyrie", + "breakpoints": true + } + ] +} \ No newline at end of file diff --git a/eslint-baseline-output.txt b/eslint-baseline-output.txt deleted file mode 100644 index e3cc87ea..00000000 --- a/eslint-baseline-output.txt +++ /dev/null @@ -1,11842 +0,0 @@ - -D:\dcprint\erp-project\src\app\[locale]\advanced\cost-analysis\page.tsx - 9:10 warning 'activeTab' is assigned a value but never used. Allowed unused elements of array destructuring must match /^_/u @typescript-eslint/no-unused-vars - 9:21 warning 'setActiveTab' is assigned a value but never used. Allowed unused elements of array destructuring must match /^_/u @typescript-eslint/no-unused-vars - 10:48 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 40:26 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "A 绫荤墿鏂?銆傚缓璁娇鐢? {tc('text_1j3gks')} i18n/no-chinese-hardcode - 48:26 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "B 绫荤墿鏂?銆傚缓璁娇鐢? {tc('text_1jn965')} i18n/no-chinese-hardcode - 56:26 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "C 绫荤墿鏂?銆傚缓璁娇鐢? {tc('text_1k71ri')} i18n/no-chinese-hardcode - 66:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "浜у搧鍒╂鼎鍒嗘瀽"銆傚缓璁娇鐢? {tc('text_2azgrl')} i18n/no-chinese-hardcode - 72:49 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "浜у搧ID"銆傚缓璁娇鐢? {tc('text_a9jn9x')} i18n/no-chinese-hardcode - 73:50 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏀跺叆"銆傚缓璁娇鐢? {tc('text_hnu7')} i18n/no-chinese-hardcode - 74:50 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鐩存帴鎴愭湰"銆傚缓璁娇鐢? {tc('text_ff70nh')} i18n/no-chinese-hardcode - 75:50 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "闂存帴璐圭敤"銆傚缓璁娇鐢? {tc('text_jc5rls')} i18n/no-chinese-hardcode - 76:50 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "姣涘埄鐜?銆傚缓璁娇鐢? {tc('text_g7btl')} i18n/no-chinese-hardcode - 77:50 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍑€鍒╃巼"銆傚缓璁娇鐢? {tc('text_cdoam')} i18n/no-chinese-hardcode - 81:37 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - -D:\dcprint\erp-project\src\app\[locale]\advanced\dashboard\page.tsx - 50:46 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏈堥攢鍞敹鍏?銆傚缓璁娇鐢? {tc('text_46k8d7')} i18n/no-chinese-hardcode - 57:48 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "涓鍗?銆傚缓璁娇鐢? {tc('text_c4d5p')} i18n/no-chinese-hardcode - 63:46 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "璁㈠崟瀹屾垚鐜?銆傚缓璁娇鐢? {tc('text_be14uo')} i18n/no-chinese-hardcode - 75:46 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "搴撳瓨鍛ㄨ浆鐜?銆傚缓璁娇鐢? {tc('text_qifwzi')} i18n/no-chinese-hardcode - 87:46 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "搴撳瓨浠峰€?銆傚缓璁娇鐢? {tc('text_cb6ubu')} i18n/no-chinese-hardcode - 99:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "6 涓湀鏀跺叆瓒嬪娍"銆傚缓璁娇鐢? {tc('text_gw5r05')} i18n/no-chinese-hardcode - 125:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏅鸿兘寤鸿"銆傚缓璁娇鐢? {tc('text_dgo4jb')} i18n/no-chinese-hardcode - 128:45 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "褰撳墠鏃犻璀︼紝杩愯鐘舵€佽壇濂?銆傚缓璁娇鐢? {tc('text_ardo13')} i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\[locale]\analysis\db-relations\page.tsx - 16:10 warning 'useToast' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 17:10 warning 'useReactFlow' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 17:24 warning 'useNodesState' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 17:39 warning 'useEdgesState' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 87:15 warning 'err' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars - 363:40 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "涓?銆傚缓璁娇鐢? {tc('text_ffu')} i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\[locale]\base-data\material-category\page.tsx - 89:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\base-data\material-category\page.tsx:89:5 - 87 | }; - 88 | useEffect(() => { -> 89 | fetchData(); - | ^^^^^^^^^ Avoid calling setState() directly within an effect - 90 | }, [page]); - 91 | - 92 | const handleSave = async () => { react-hooks/set-state-in-effect - 90:6 warning React Hook useEffect has a missing dependency: 'fetchData'. Either include it or remove the dependency array react-hooks/exhaustive-deps - 106:24 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "澶辫触"銆傚缓璁娇鐢? tc('text_fy1g') i18n/no-chinese-hardcode - 109:22 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "澶辫触"銆傚缓璁娇鐢? tc('text_fy1g') i18n/no-chinese-hardcode - 113:18 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "纭畾鍒犻櫎锛?銆傚缓璁娇鐢? tc('text_efhx5d') i18n/no-chinese-hardcode - 118:24 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍒犻櫎鎴愬姛"銆傚缓璁娇鐢? tc('text_azeh8z') i18n/no-chinese-hardcode - 122:22 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "澶辫触"銆傚缓璁娇鐢? tc('text_fy1g') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\[locale]\business\contract-review\page.tsx - 147:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\business\contract-review\page.tsx:147:5 - 145 | - 146 | useEffect(() => { -> 147 | fetchData(); - | ^^^^^^^^^ Avoid calling setState() directly within an effect - 148 | }, [fetchData]); - 149 | - 150 | const toggleSelect = (id: number) => { react-hooks/set-state-in-effect - 174:9 warning 'handlePrint' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 178:9 warning 'handleExportPDF' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 182:9 warning 'handleExportXLS' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 186:9 warning 'handleExportWORD' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 204:24 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "澶辫触"銆傚缓璁娇鐢? tc('text_fy1g') i18n/no-chinese-hardcode - 207:22 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "澶辫触"銆傚缓璁娇鐢? tc('text_fy1g') i18n/no-chinese-hardcode - 220:24 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "璇勫鎰忚淇濆瓨鎴愬姛"銆傚缓璁娇鐢? tc('text_id55ax') i18n/no-chinese-hardcode - 223:24 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "澶辫触"銆傚缓璁娇鐢? tc('text_fy1g') i18n/no-chinese-hardcode - 226:22 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "澶辫触"銆傚缓璁娇鐢? tc('text_fy1g') i18n/no-chinese-hardcode - 231:18 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "纭畾鍒犻櫎姝よ瘎瀹¤褰曪紵"銆傚缓璁娇鐢? tc('text_kuhz4n') i18n/no-chinese-hardcode - 236:24 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍒犻櫎鎴愬姛"銆傚缓璁娇鐢? tc('text_azeh8z') i18n/no-chinese-hardcode - 240:22 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "澶辫触"銆傚缓璁娇鐢? tc('text_fy1g') i18n/no-chinese-hardcode - 261:24 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鏂囦欢涓婁紶鎴愬姛"銆傚缓璁娇鐢? tc('text_ij2qj0') i18n/no-chinese-hardcode - 263:24 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "涓婁紶澶辫触"銆傚缓璁娇鐢? tc('text_a6dm22') i18n/no-chinese-hardcode - 266:22 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "涓婁紶澶辫触"銆傚缓璁娇鐢? tc('text_a6dm22') i18n/no-chinese-hardcode - 306:45 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍏ㄩ儴鐘舵€?銆傚缓璁娇鐢? {tc('text_avez63')} i18n/no-chinese-hardcode - 320:28 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍚堝悓璇勫绠$悊"銆傚缓璁娇鐢? {tc('text_jfm8py')} i18n/no-chinese-hardcode - 410:94 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "璇勫"銆傚缓璁娇鐢? {tc('text_o9y5')} i18n/no-chinese-hardcode - 432:96 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏆傛棤鏁版嵁"銆傚缓璁娇鐢? {tc('text_dcv57g')} i18n/no-chinese-hardcode - 441:63 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍏?銆傚缓璁娇鐢? {tc('text_g35')} i18n/no-chinese-hardcode - 442:25 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏉?銆傚缓璁娇鐢? {tc('text_kf5')} i18n/no-chinese-hardcode - 450:18 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "涓婁竴椤?銆傚缓璁娇鐢? {tc('text_btlof')} i18n/no-chinese-hardcode - 458:18 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "涓嬩竴椤?銆傚缓璁娇鐢? {tc('text_btmf4')} i18n/no-chinese-hardcode - 473:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "璁㈠崟鍙?銆傚缓璁娇鐢? {tc('text_kuwys')} i18n/no-chinese-hardcode - 480:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "瀹㈡埛鍚嶇О *"銆傚缓璁娇鐢? {tc('text_555b6m')} i18n/no-chinese-hardcode - 487:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "浜у搧缂栫爜"銆傚缓璁娇鐢? {tc('text_aa5vdx')} i18n/no-chinese-hardcode - 517:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "浜よ揣鏃ユ湡"銆傚缓璁娇鐢? {tc('text_ai8ysd')} i18n/no-chinese-hardcode - 525:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏍峰搧鐘舵€?銆傚缓璁娇鐢? {tc('text_di6511')} i18n/no-chinese-hardcode - 543:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "璐ㄩ噺瑕佹眰"銆傚缓璁娇鐢? {tc('text_ieyjt4')} i18n/no-chinese-hardcode - 562:78 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍙栨秷"銆傚缓璁娇鐢? {tc('text_ev02')} i18n/no-chinese-hardcode - 580:57 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "瀹㈡埛锛?銆傚缓璁娇鐢? {tc('text_dxa79')} i18n/no-chinese-hardcode - 588:57 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏁伴噺锛?銆傚缓璁娇鐢? {tc('text_fl2u3')} i18n/no-chinese-hardcode - 594:42 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "涓氬姟閮?銆傚缓璁娇鐢? {tc('text_buoe9')} i18n/no-chinese-hardcode - 595:42 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "宸ョ▼鎶€鏈儴"銆傚缓璁娇鐢? {tc('text_rs1ifn')} i18n/no-chinese-hardcode - 596:46 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍝佽川閮?銆傚缓璁娇鐢? {tc('text_d3pkx')} i18n/no-chinese-hardcode - 597:43 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鐢熶骇閮?銆傚缓璁娇鐢? {tc('text_hjr0g')} i18n/no-chinese-hardcode - 598:47 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "閲囪喘閮?銆傚缓璁娇鐢? {tc('text_m1hlu')} i18n/no-chinese-hardcode - 613:47 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "宸ョ▼鎶€鏈儴璇勫鎰忚"銆傚缓璁娇鐢? {tc('text_b0b1s2')} i18n/no-chinese-hardcode - 636:47 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍝佽川閮ㄨ瘎瀹℃剰瑙?銆傚缓璁娇鐢? {tc('text_xfmrgw')} i18n/no-chinese-hardcode - 659:47 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鐢熶骇閮ㄨ瘎瀹℃剰瑙?銆傚缓璁娇鐢? {tc('text_58biun')} i18n/no-chinese-hardcode - 661:26 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "浜ц兘璇勪及"銆傚缓璁娇鐢? {tc('text_agp1uq')} i18n/no-chinese-hardcode - 682:47 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "閲囪喘閮ㄨ瘎瀹℃剰瑙?銆傚缓璁娇鐢? {tc('text_nzjk6n')} i18n/no-chinese-hardcode - 707:47 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "闄勪欢涓婁紶"銆傚缓璁娇鐢? {tc('text_ja8tu0')} i18n/no-chinese-hardcode - 755:84 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍏抽棴"銆傚缓璁娇鐢? {tc('text_eod6')} i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\[locale]\crm\analysis\page.tsx - 152:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\crm\analysis\page.tsx:152:5 - 150 | - 151 | useEffect(() => { -> 152 | fetchData(); - | ^^^^^^^^^ Avoid calling setState() directly within an effect - 153 | }, [page]); - 154 | - 155 | const handleSave = async () => { react-hooks/set-state-in-effect - 153:6 warning React Hook useEffect has a missing dependency: 'fetchData'. Either include it or remove the dependency array react-hooks/exhaustive-deps - -D:\dcprint\erp-project\src\app\[locale]\crm\follow\page.tsx - 79:10 warning 'loading' is assigned a value but never used. Allowed unused elements of array destructuring must match /^_/u @typescript-eslint/no-unused-vars - 116:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\crm\follow\page.tsx:116:5 - 114 | - 115 | useEffect(() => { -> 116 | fetchData(); - | ^^^^^^^^^ Avoid calling setState() directly within an effect - 117 | }, [page]); - 118 | - 119 | const handleSave = async () => { react-hooks/set-state-in-effect - 117:6 warning React Hook useEffect has a missing dependency: 'fetchData'. Either include it or remove the dependency array react-hooks/exhaustive-deps - -D:\dcprint\erp-project\src\app\[locale]\dashboard\ceo\page.tsx - 183:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\dashboard\ceo\page.tsx:183:5 - 181 | - 182 | useEffect(() => { -> 183 | setCurrentTime(new Date()); - | ^^^^^^^^^^^^^^ Avoid calling setState() directly within an effect - 184 | const fetchData = async () => { - 185 | try { - 186 | const res = await authFetch('/api/dashboard/ceo'); react-hooks/set-state-in-effect - 601:16 warning Error: Cannot create components during render - -Components created during render will reset their state each time they are created. Declare components outside of render. - -D:\dcprint\erp-project\src\app\[locale]\dashboard\ceo\page.tsx:601:16 - 599 | {/* Left Column */} - 600 |
-> 601 | - | ^^^^^ This component is created during render - 602 |
- 603 | {workshopDaily.length > 0 ? ( - 604 | workshopDaily.slice(0, 8).map((item, i) => ( - -D:\dcprint\erp-project\src\app\[locale]\dashboard\ceo\page.tsx:398:17 - 396 | }; - 397 | -> 398 | const Panel = ({ - | ^^ -> 399 | title, - | ^^^^^^^^^^ -> 400 | icon: Icon, - 鈥? | ^^^^^^^^^^ -> 426 |
- | ^^^^^^^^^^ -> 427 | ); - | ^^^^ The component is created during render here - 428 | - 429 | function AutoScroll({ - 430 | children, react-hooks/static-components - 634:16 warning Error: Cannot create components during render - -Components created during render will reset their state each time they are created. Declare components outside of render. - -D:\dcprint\erp-project\src\app\[locale]\dashboard\ceo\page.tsx:634:16 - 632 |
- 633 | -> 634 | - | ^^^^^ This component is created during render - 635 |
- 636 | {materialConsumption.length > 0 ? ( - 637 | materialConsumption.map((item, i) => ( - -D:\dcprint\erp-project\src\app\[locale]\dashboard\ceo\page.tsx:398:17 - 396 | }; - 397 | -> 398 | const Panel = ({ - | ^^ -> 399 | title, - | ^^^^^^^^^^ -> 400 | icon: Icon, - 鈥? | ^^^^^^^^^^ -> 426 |
- | ^^^^^^^^^^ -> 427 | ); - | ^^^^ The component is created during render here - 428 | - 429 | function AutoScroll({ - 430 | children, react-hooks/static-components - 652:20 warning Error: Cannot create components during render - -Components created during render will reset their state each time they are created. Declare components outside of render. - -D:\dcprint\erp-project\src\app\[locale]\dashboard\ceo\page.tsx:652:20 - 650 |
- 651 |
-> 652 | 228 | const RingChart = ({ - | ^^ -> 229 | percent, - | ^^^^^^^^^^^^ -> 230 | label, - 鈥? | ^^^^^^^^^^^^ -> 277 | ); - | ^^^^^^^^^^^^ -> 278 | }; - | ^^^^ The component is created during render here - 279 | - 280 | const LineChart = ({ - 281 | data, react-hooks/static-components - 657:20 warning Error: Cannot create components during render - -Components created during render will reset their state each time they are created. Declare components outside of render. - -D:\dcprint\erp-project\src\app\[locale]\dashboard\ceo\page.tsx:657:20 - 655 | color="cyan" - 656 | /> -> 657 | 228 | const RingChart = ({ - | ^^ -> 229 | percent, - | ^^^^^^^^^^^^ -> 230 | label, - 鈥? | ^^^^^^^^^^^^ -> 277 | ); - | ^^^^^^^^^^^^ -> 278 | }; - | ^^^^ The component is created during render here - 279 | - 280 | const LineChart = ({ - 281 | data, react-hooks/static-components - 665:16 warning Error: Cannot create components during render - -Components created during render will reset their state each time they are created. Declare components outside of render. - -D:\dcprint\erp-project\src\app\[locale]\dashboard\ceo\page.tsx:665:16 - 663 | - 664 | -> 665 | - | ^^^^^ This component is created during render - 666 |
- 667 | {monthlyMaterialConsumption.length > 0 ? ( - 668 | monthlyMaterialConsumption.map((item, i) => { - -D:\dcprint\erp-project\src\app\[locale]\dashboard\ceo\page.tsx:398:17 - 396 | }; - 397 | -> 398 | const Panel = ({ - | ^^ -> 399 | title, - | ^^^^^^^^^^ -> 400 | icon: Icon, - 鈥? | ^^^^^^^^^^ -> 426 |
- | ^^^^^^^^^^ -> 427 | ); - | ^^^^ The component is created during render here - 428 | - 429 | function AutoScroll({ - 430 | children, react-hooks/static-components - 696:16 warning Error: Cannot create components during render - -Components created during render will reset their state each time they are created. Declare components outside of render. - -D:\dcprint\erp-project\src\app\[locale]\dashboard\ceo\page.tsx:696:16 - 694 |
- 695 | -> 696 | - | ^^^^^ This component is created during render - 697 |
- 698 | {workshopHistory.length > 0 ? ( - 699 | workshopHistory.map((item, i) => ( - -D:\dcprint\erp-project\src\app\[locale]\dashboard\ceo\page.tsx:398:17 - 396 | }; - 397 | -> 398 | const Panel = ({ - | ^^ -> 399 | title, - | ^^^^^^^^^^ -> 400 | icon: Icon, - 鈥? | ^^^^^^^^^^ -> 426 |
- | ^^^^^^^^^^ -> 427 | ); - | ^^^^ The component is created during render here - 428 | - 429 | function AutoScroll({ - 430 | children, react-hooks/static-components - 720:16 warning Error: Cannot create components during render - -Components created during render will reset their state each time they are created. Declare components outside of render. - -D:\dcprint\erp-project\src\app\[locale]\dashboard\ceo\page.tsx:720:16 - 718 | {/* Center Column */} - 719 |
-> 720 | - | ^^^^^ This component is created during render - 721 |
- 722 |
- 723 |

- -D:\dcprint\erp-project\src\app\[locale]\dashboard\ceo\page.tsx:398:17 - 396 | }; - 397 | -> 398 | const Panel = ({ - | ^^ -> 399 | title, - | ^^^^^^^^^^ -> 400 | icon: Icon, - 鈥? | ^^^^^^^^^^ -> 426 |

- | ^^^^^^^^^^ -> 427 | ); - | ^^^^ The component is created during render here - 428 | - 429 | function AutoScroll({ - 430 | children, react-hooks/static-components - 876:16 warning Error: Cannot create components during render - -Components created during render will reset their state each time they are created. Declare components outside of render. - -D:\dcprint\erp-project\src\app\[locale]\dashboard\ceo\page.tsx:876:16 - 874 | - 875 | -> 876 | - | ^^^^^ This component is created during render - 877 |
- 878 | 0 ? data.orderTrend.map((d) => d.count) : []} - -D:\dcprint\erp-project\src\app\[locale]\dashboard\ceo\page.tsx:398:17 - 396 | }; - 397 | -> 398 | const Panel = ({ - | ^^ -> 399 | title, - | ^^^^^^^^^^ -> 400 | icon: Icon, - 鈥? | ^^^^^^^^^^ -> 426 |
- | ^^^^^^^^^^ -> 427 | ); - | ^^^^ The component is created during render here - 428 | - 429 | function AutoScroll({ - 430 | children, react-hooks/static-components - 878:20 warning Error: Cannot create components during render - -Components created during render will reset their state each time they are created. Declare components outside of render. - -D:\dcprint\erp-project\src\app\[locale]\dashboard\ceo\page.tsx:878:20 - 876 | - 877 |
-> 878 | 0 ? data.orderTrend.map((d) => d.count) : []} - 880 | color="#22d3ee" - 881 | height={150} - -D:\dcprint\erp-project\src\app\[locale]\dashboard\ceo\page.tsx:280:21 - 278 | }; - 279 | -> 280 | const LineChart = ({ - | ^^ -> 281 | data, - | ^^^^^^^^^ -> 282 | color = '#22d3ee', - 鈥? | ^^^^^^^^^ -> 325 | ); - | ^^^^^^^^^ -> 326 | }; - | ^^^^ The component is created during render here - 327 | - 328 | const DualLineChart = ({ - 329 | data1, react-hooks/static-components - 897:16 warning Error: Cannot create components during render - -Components created during render will reset their state each time they are created. Declare components outside of render. - -D:\dcprint\erp-project\src\app\[locale]\dashboard\ceo\page.tsx:897:16 - 895 | - 896 | -> 897 | - | ^^^^^ This component is created during render - 898 |
- 899 | 398 | const Panel = ({ - | ^^ -> 399 | title, - | ^^^^^^^^^^ -> 400 | icon: Icon, - 鈥? | ^^^^^^^^^^ -> 426 |
- | ^^^^^^^^^^ -> 427 | ); - | ^^^^ The component is created during render here - 428 | - 429 | function AutoScroll({ - 430 | children, react-hooks/static-components - 899:20 warning Error: Cannot create components during render - -Components created during render will reset their state each time they are created. Declare components outside of render. - -D:\dcprint\erp-project\src\app\[locale]\dashboard\ceo\page.tsx:899:20 - 897 | - 898 |
-> 899 | 0 - 902 | ? data.powerConsumption.map((d) => d.power) - -D:\dcprint\erp-project\src\app\[locale]\dashboard\ceo\page.tsx:328:25 - 326 | }; - 327 | -> 328 | const DualLineChart = ({ - | ^^ -> 329 | data1, - | ^^^^^^^^^^ -> 330 | data2, - 鈥? | ^^^^^^^^^^ -> 395 | ); - | ^^^^^^^^^^ -> 396 | }; - | ^^^^ The component is created during render here - 397 | - 398 | const Panel = ({ - 399 | title, react-hooks/static-components - 928:16 warning Error: Cannot create components during render - -Components created during render will reset their state each time they are created. Declare components outside of render. - -D:\dcprint\erp-project\src\app\[locale]\dashboard\ceo\page.tsx:928:16 - 926 | {/* Right Column */} - 927 |
-> 928 | - | ^^^^^ This component is created during render - 929 |
- 930 |
- 931 |
- -D:\dcprint\erp-project\src\app\[locale]\dashboard\ceo\page.tsx:398:17 - 396 | }; - 397 | -> 398 | const Panel = ({ - | ^^ -> 399 | title, - | ^^^^^^^^^^ -> 400 | icon: Icon, - 鈥? | ^^^^^^^^^^ -> 426 |
- | ^^^^^^^^^^ -> 427 | ); - | ^^^^ The component is created during render here - 428 | - 429 | function AutoScroll({ - 430 | children, react-hooks/static-components - 988:16 warning Error: Cannot create components during render - -Components created during render will reset their state each time they are created. Declare components outside of render. - -D:\dcprint\erp-project\src\app\[locale]\dashboard\ceo\page.tsx:988:16 - 986 | - 987 | -> 988 | - | ^^^^^ This component is created during render - 989 |
- 990 |
- 991 |

{t('inProgressOrders')}

- -D:\dcprint\erp-project\src\app\[locale]\dashboard\ceo\page.tsx:398:17 - 396 | }; - 397 | -> 398 | const Panel = ({ - | ^^ -> 399 | title, - | ^^^^^^^^^^ -> 400 | icon: Icon, - 鈥? | ^^^^^^^^^^ -> 426 |
- | ^^^^^^^^^^ -> 427 | ); - | ^^^^ The component is created during render here - 428 | - 429 | function AutoScroll({ - 430 | children, react-hooks/static-components - 1005:25 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "瀹炴椂鐢熶骇宸ュ崟婊氬姩"銆傚缓璁娇鐢? {tc('text_cz0sda')} i18n/no-chinese-hardcode - 1007:18 warning Error: Cannot create components during render - -Components created during render will reset their state each time they are created. Declare components outside of render. - -D:\dcprint\erp-project\src\app\[locale]\dashboard\ceo\page.tsx:1007:18 - 1005 | 瀹炴椂鐢熶骇宸ュ崟婊氬姩 - 1006 |
-> 1007 | - | ^^^^^^^^^^ This component is created during render - 1008 |
- 1009 | {activeWorkOrders.length > 0 ? ( - 1010 | activeWorkOrders.map((wo, i) => ( - -D:\dcprint\erp-project\src\app\[locale]\dashboard\ceo\page.tsx:429:3 - 427 | ); - 428 | -> 429 | function AutoScroll({ - | ^^^^^^^^^^^^^^^^^^^^^ -> 430 | children, - | ^^^^^^^^^^^^^ -> 431 | maxHeight = 200, - 鈥? | ^^^^^^^^^^^^^ -> 484 | ); - | ^^^^^^^^^^^^^ -> 485 | } - | ^^^^ The component is created during render here - 486 | - 487 | const workshopDaily = data.workshopDaily.length > 0 ? data.workshopDaily : []; - 488 | const materialConsumption = data.materialConsumption.length > 0 ? data.materialConsumption : []; react-hooks/static-components - 1025:109 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "绱ф€?銆傚缓璁娇鐢? {tc('text_ltcu')} i18n/no-chinese-hardcode - 1028:115 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "楂?銆傚缓璁娇鐢? {tc('text_ul4')} i18n/no-chinese-hardcode - 1055:16 warning Error: Cannot create components during render - -Components created during render will reset their state each time they are created. Declare components outside of render. - -D:\dcprint\erp-project\src\app\[locale]\dashboard\ceo\page.tsx:1055:16 - 1053 | - 1054 | -> 1055 | - | ^^^^^ This component is created during render - 1056 | - 1057 |
- 1058 | {data.production.equipmentStatus.length > 0 ? ( - -D:\dcprint\erp-project\src\app\[locale]\dashboard\ceo\page.tsx:398:17 - 396 | }; - 397 | -> 398 | const Panel = ({ - | ^^ -> 399 | title, - | ^^^^^^^^^^ -> 400 | icon: Icon, - 鈥? | ^^^^^^^^^^ -> 426 |
- | ^^^^^^^^^^ -> 427 | ); - | ^^^^ The component is created during render here - 428 | - 429 | function AutoScroll({ - 430 | children, react-hooks/static-components - 1056:18 warning Error: Cannot create components during render - -Components created during render will reset their state each time they are created. Declare components outside of render. - -D:\dcprint\erp-project\src\app\[locale]\dashboard\ceo\page.tsx:1056:18 - 1054 | - 1055 | -> 1056 | - | ^^^^^^^^^^ This component is created during render - 1057 |
- 1058 | {data.production.equipmentStatus.length > 0 ? ( - 1059 | data.production.equipmentStatus.map((eq, i) => ( - -D:\dcprint\erp-project\src\app\[locale]\dashboard\ceo\page.tsx:429:3 - 427 | ); - 428 | -> 429 | function AutoScroll({ - | ^^^^^^^^^^^^^^^^^^^^^ -> 430 | children, - | ^^^^^^^^^^^^^ -> 431 | maxHeight = 200, - 鈥? | ^^^^^^^^^^^^^ -> 484 | ); - | ^^^^^^^^^^^^^ -> 485 | } - | ^^^^ The component is created during render here - 486 | - 487 | const workshopDaily = data.workshopDaily.length > 0 ? data.workshopDaily : []; - 488 | const materialConsumption = data.materialConsumption.length > 0 ? data.materialConsumption : []; react-hooks/static-components - 1090:16 warning Error: Cannot create components during render - -Components created during render will reset their state each time they are created. Declare components outside of render. - -D:\dcprint\erp-project\src\app\[locale]\dashboard\ceo\page.tsx:1090:16 - 1088 | - 1089 | -> 1090 | - | ^^^^^ This component is created during render - 1091 |
- 1092 |
- 1093 | {t('totalReceivable')} - -D:\dcprint\erp-project\src\app\[locale]\dashboard\ceo\page.tsx:398:17 - 396 | }; - 397 | -> 398 | const Panel = ({ - | ^^ -> 399 | title, - | ^^^^^^^^^^ -> 400 | icon: Icon, - 鈥? | ^^^^^^^^^^ -> 426 |
- | ^^^^^^^^^^ -> 427 | ); - | ^^^^ The component is created during render here - 428 | - 429 | function AutoScroll({ - 430 | children, react-hooks/static-components - 1123:16 warning Error: Cannot create components during render - -Components created during render will reset their state each time they are created. Declare components outside of render. - -D:\dcprint\erp-project\src\app\[locale]\dashboard\ceo\page.tsx:1123:16 - 1121 |
- 1122 |
-> 1123 | - | ^^^^^ This component is created during render - 1124 |
- 1125 |
398 | const Panel = ({ - | ^^ -> 399 | title, - | ^^^^^^^^^^ -> 400 | icon: Icon, - 鈥? | ^^^^^^^^^^ -> 426 |
- | ^^^^^^^^^^ -> 427 | ); - | ^^^^ The component is created during render here - 428 | - 429 | function AutoScroll({ - 430 | children, react-hooks/static-components - 1148:16 warning Error: Cannot create components during render - -Components created during render will reset their state each time they are created. Declare components outside of render. - -D:\dcprint\erp-project\src\app\[locale]\dashboard\ceo\page.tsx:1148:16 - 1146 |
- 1147 |
-> 1148 | - | ^^^^^ This component is created during render - 1149 |
- 1150 |
398 | const Panel = ({ - | ^^ -> 399 | title, - | ^^^^^^^^^^ -> 400 | icon: Icon, - 鈥? | ^^^^^^^^^^ -> 426 |
- | ^^^^^^^^^^ -> 427 | ); - | ^^^^ The component is created during render here - 428 | - 429 | function AutoScroll({ - 430 | children, react-hooks/static-components - 1173:16 warning Error: Cannot create components during render - -Components created during render will reset their state each time they are created. Declare components outside of render. - -D:\dcprint\erp-project\src\app\[locale]\dashboard\ceo\page.tsx:1173:16 - 1171 |
- 1172 |
-> 1173 | - | ^^^^^ This component is created during render - 1174 |
- 1175 |
398 | const Panel = ({ - | ^^ -> 399 | title, - | ^^^^^^^^^^ -> 400 | icon: Icon, - 鈥? | ^^^^^^^^^^ -> 426 |
- | ^^^^^^^^^^ -> 427 | ); - | ^^^^ The component is created during render here - 428 | - 429 | function AutoScroll({ - 430 | children, react-hooks/static-components - 1198:16 warning Error: Cannot create components during render - -Components created during render will reset their state each time they are created. Declare components outside of render. - -D:\dcprint\erp-project\src\app\[locale]\dashboard\ceo\page.tsx:1198:16 - 1196 |
- 1197 |
-> 1198 | - | ^^^^^ This component is created during render - 1199 |
- 1200 |
398 | const Panel = ({ - | ^^ -> 399 | title, - | ^^^^^^^^^^ -> 400 | icon: Icon, - 鈥? | ^^^^^^^^^^ -> 426 |
- | ^^^^^^^^^^ -> 427 | ); - | ^^^^ The component is created during render here - 428 | - 429 | function AutoScroll({ - 430 | children, react-hooks/static-components - -D:\dcprint\erp-project\src\app\[locale]\dashboard\finance\page.tsx - 276:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\dashboard\finance\page.tsx:276:5 - 274 | }; - 275 | fetchData(); -> 276 | setCurrentTime(new Date()); - | ^^^^^^^^^^^^^^ Avoid calling setState() directly within an effect - 277 | const timer1 = setInterval(fetchData, 60000); - 278 | const timer2 = setInterval(() => setCurrentTime(new Date()), 1000); - 279 | return () => { react-hooks/set-state-in-effect - -D:\dcprint\erp-project\src\app\[locale]\dashboard\flow\page.tsx - 34:3 warning 'Box' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 222:36 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鐢ㄦ埛琛?銆傚缓璁娇鐢? tc('text_hn7sp') i18n/no-chinese-hardcode - 223:42 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "閮ㄩ棬琛?銆傚缓璁娇鐢? tc('text_lyc14') i18n/no-chinese-hardcode - 224:36 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "瑙掕壊琛?銆傚缓璁娇鐢? tc('text_ktnrc') i18n/no-chinese-hardcode - 227:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鐢ㄦ埛瑙掕壊鍏宠仈"銆傚缓璁娇鐢? tc('text_tzwh1s') i18n/no-chinese-hardcode - 230:36 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鑿滃崟琛?銆傚缓璁娇鐢? tc('text_jq7pr') i18n/no-chinese-hardcode - 231:41 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "瑙掕壊鑿滃崟鍏宠仈"銆傚缓璁娇鐢? tc('text_sdy5uu') i18n/no-chinese-hardcode - 232:45 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鎿嶄綔鏃ュ織"銆傚缓璁娇鐢? tc('text_d1tfy9') i18n/no-chinese-hardcode - 233:41 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鐧诲綍鏃ュ織"銆傚缓璁娇鐢? tc('text_fcfmmk') i18n/no-chinese-hardcode - 234:41 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "瀛楀吀绫诲瀷"銆傚缓璁娇鐢? tc('text_bv9usx') i18n/no-chinese-hardcode - 235:41 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "瀛楀吀鏁版嵁"銆傚缓璁娇鐢? tc('text_bv601r') i18n/no-chinese-hardcode - 236:38 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "绯荤粺閰嶇疆"銆傚缓璁娇鐢? tc('text_garzt1') i18n/no-chinese-hardcode - 245:40 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "瀹㈡埛琛?銆傚缓璁娇鐢? tc('text_dwmr7') i18n/no-chinese-hardcode - 248:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "瀹㈡埛鑱旂郴浜?銆傚缓璁娇鐢? tc('text_g1y9ha') i18n/no-chinese-hardcode - 253:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "瀹㈡埛璺熻繘"銆傚缓璁娇鐢? tc('text_bz5bap') i18n/no-chinese-hardcode - 264:40 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "渚涘簲鍟嗚〃"銆傚缓璁娇鐢? tc('text_afqgaj') i18n/no-chinese-hardcode - 267:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "渚涘簲鍟嗙墿鏂?銆傚缓璁娇鐢? tc('text_vlvffn') i18n/no-chinese-hardcode - 278:49 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鐗╂枡鍒嗙被"銆傚缓璁娇鐢? tc('text_eus3np') i18n/no-chinese-hardcode - 281:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鐗╂枡琛?銆傚缓璁娇鐢? tc('text_h9b88') i18n/no-chinese-hardcode - 284:41 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "浠撳簱琛?銆傚缓璁娇鐢? tc('text_c0h1k') i18n/no-chinese-hardcode - 287:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "搴撳瓨琛?銆傚缓璁娇鐢? tc('text_eaz1f') i18n/no-chinese-hardcode - 292:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "搴撳瓨鏃ュ織"銆傚缓璁娇鐢? tc('text_cbattj') i18n/no-chinese-hardcode - 303:39 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "閲囪喘鐢宠"銆傚缓璁娇鐢? tc('text_iz67sa') i18n/no-chinese-hardcode - 306:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "閲囪喘鐢宠鏄庣粏"銆傚缓璁娇鐢? tc('text_i0i7tq') i18n/no-chinese-hardcode - 309:37 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "閲囪喘璁㈠崟"銆傚缓璁娇鐢? tc('text_iz9pyx') i18n/no-chinese-hardcode - 312:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "閲囪喘璁㈠崟鏄庣粏"銆傚缓璁娇鐢? tc('text_fexslr') i18n/no-chinese-hardcode - 317:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "閲囪喘鏀惰揣"銆傚缓璁娇鐢? tc('text_iz3i47') i18n/no-chinese-hardcode - 320:46 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏀惰揣鏄庣粏"銆傚缓璁娇鐢? tc('text_dcqind') i18n/no-chinese-hardcode - 329:37 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "閿€鍞鍗?銆傚缓璁娇鐢? tc('text_j5p8kh') i18n/no-chinese-hardcode - 332:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "閿€鍞鍗曟槑缁?銆傚缓璁娇鐢? tc('text_e71kux') i18n/no-chinese-hardcode - 337:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "閿€鍞彂璐?銆傚缓璁娇鐢? tc('text_j5g278') i18n/no-chinese-hardcode - 340:47 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍙戣揣鏄庣粏"銆傚缓璁娇鐢? tc('text_b5r626') i18n/no-chinese-hardcode - 349:45 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏍囧噯宸ヨ壓鍗?銆傚缓璁娇鐢? tc('text_8q6reb') i18n/no-chinese-hardcode - 352:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鐢熶骇宸ュ崟"銆傚缓璁娇鐢? tc('text_f3s1h4') i18n/no-chinese-hardcode - 355:35 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "BOM琛?銆傚缓璁娇鐢? tc('text_18ki0') i18n/no-chinese-hardcode - 358:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "BOM鏄庣粏"銆傚缓璁娇鐢? tc('text_128i6w') i18n/no-chinese-hardcode - 369:45 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍏ュ簱鍗?銆傚缓璁娇鐢? tc('text_cdqh3') i18n/no-chinese-hardcode - 370:46 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍑哄簱鍗?銆傚缓璁娇鐢? tc('text_cgsyk') i18n/no-chinese-hardcode - 371:46 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "璋冩嫧鍗?銆傚缓璁娇鐢? tc('text_kzk4w') i18n/no-chinese-hardcode - 372:46 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鐗╂枡鏍囩"銆傚缓璁娇鐢? tc('text_euvu7b') i18n/no-chinese-hardcode - 373:46 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍒嗗垏璁板綍"銆傚缓璁娇鐢? tc('text_ap4js6') i18n/no-chinese-hardcode - 374:40 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鎵爜鏃ュ織"銆傚缓璁娇鐢? tc('text_cwy9e0') i18n/no-chinese-hardcode - 375:43 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鐩樼偣璁板綍"銆傚缓璁娇鐢? tc('text_fgt4xy') i18n/no-chinese-hardcode - 384:41 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "妫€楠岃褰?銆傚缓璁娇鐢? tc('text_duxvn5') i18n/no-chinese-hardcode - 385:42 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "涓嶅悎鏍艰褰?銆傚缓璁娇鐢? tc('text_w3eqna') i18n/no-chinese-hardcode - 386:44 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "杩芥函璁板綍"銆傚缓璁娇鐢? tc('text_imokfr') i18n/no-chinese-hardcode - 397:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "搴旀敹娆?銆傚缓璁娇鐢? tc('text_ecifw') i18n/no-chinese-hardcode - 402:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "搴斾粯娆?銆傚缓璁娇鐢? tc('text_e8ph6') i18n/no-chinese-hardcode - 405:46 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏀舵璁板綍"銆傚缓璁娇鐢? tc('text_d7xxt9') i18n/no-chinese-hardcode - 414:39 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍛樺伐琛?銆傚缓璁娇鐢? tc('text_ctgmz') i18n/no-chinese-hardcode - 415:41 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鑰冨嫟璁板綍"銆傚缓璁娇鐢? tc('text_gi2g86') i18n/no-chinese-hardcode - 416:39 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍩硅璁板綍"銆傚缓璁娇鐢? tc('text_bol01l') i18n/no-chinese-hardcode - 425:41 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "璁惧琛?銆傚缓璁娇鐢? tc('text_kwqyn') i18n/no-chinese-hardcode - 426:50 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "淇濆吇璁板綍"銆傚缓璁娇鐢? tc('text_af8k83') i18n/no-chinese-hardcode - 427:38 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "缁翠慨璁板綍"銆傚缓璁娇鐢? tc('text_gctspr') i18n/no-chinese-hardcode - 465:49 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏍峰紡绯荤粺"銆傚缓璁娇鐢? tc('text_djqe4c') i18n/no-chinese-hardcode - 478:46 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鎺ュ彛瑙勮寖"銆傚缓璁娇鐢? tc('text_cxelul') i18n/no-chinese-hardcode - 479:43 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "璁よ瘉涓棿浠?銆傚缓璁娇鐢? tc('text_4agksu') i18n/no-chinese-hardcode - 481:46 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏁版嵁鏍¢獙"銆傚缓璁娇鐢? tc('text_d7o1zd') i18n/no-chinese-hardcode - 492:49 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "宸ュ崟鐘舵€佹満"銆傚缓璁娇鐢? tc('text_mylri7') i18n/no-chinese-hardcode - 493:44 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "搴撳瓨鍒嗛厤绛栫暐"銆傚缓璁娇鐢? tc('text_us4vgl') i18n/no-chinese-hardcode - 495:46 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鐢熶骇鎺掔▼寮曟搸"銆傚缓璁娇鐢? tc('text_qggta2') i18n/no-chinese-hardcode - 496:42 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鎿嶄綔鏃ュ織鏈嶅姟"銆傚缓璁娇鐢? tc('text_uxgwzv') i18n/no-chinese-hardcode - 507:38 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏁版嵁搴撻┍鍔?銆傚缓璁娇鐢? tc('text_gmckgk') i18n/no-chinese-hardcode - 510:40 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "杩炴帴姹犵鐞?銆傚缓璁娇鐢? tc('text_7aneny') i18n/no-chinese-hardcode - 522:43 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "涓氬姟琛?銆傚缓璁娇鐢? tc('text_bumpt') i18n/no-chinese-hardcode - 523:38 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "瀛樺偍寮曟搸"銆傚缓璁娇鐢? tc('text_bv1a5l') i18n/no-chinese-hardcode - 524:40 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "瀛楃闆?銆傚缓璁娇鐢? tc('text_dzenr') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\[locale]\dashboard\page.tsx - 100:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\dashboard\page.tsx:100:5 - 98 | - 99 | useEffect(() => { -> 100 | fetchDashboard(); - | ^^^^^^^^^^^^^^ Avoid calling setState() directly within an effect - 101 | const updateTime = () => { - 102 | const now = new Date(); - 103 | setCurrentTime( react-hooks/set-state-in-effect - 117:6 warning React Hook useEffect has a missing dependency: 'locale'. Either include it or remove the dependency array react-hooks/exhaustive-deps - -D:\dcprint\erp-project\src\app\[locale]\dashboard\production\page.tsx - 89:3 warning 'speed' is assigned a value but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars - 253:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\dashboard\production\page.tsx:253:5 - 251 | }; - 252 | fetchData(); -> 253 | setCurrentTime(new Date()); - | ^^^^^^^^^^^^^^ Avoid calling setState() directly within an effect - 254 | const timer1 = setInterval(fetchData, 60000); - 255 | const timer2 = setInterval(() => setCurrentTime(new Date()), 1000); - 256 | return () => { react-hooks/set-state-in-effect - 260:6 warning React Hook useEffect has a missing dependency: 't'. Either include it or remove the dependency array react-hooks/exhaustive-deps - -D:\dcprint\erp-project\src\app\[locale]\dashboard\quality\page.tsx - 160:21 warning 'arr' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars - 289:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\dashboard\quality\page.tsx:289:5 - 287 | }; - 288 | fetchData(); -> 289 | setCurrentTime(new Date()); - | ^^^^^^^^^^^^^^ Avoid calling setState() directly within an effect - 290 | const timer1 = setInterval(fetchData, 60000); - 291 | const timer2 = setInterval(() => setCurrentTime(new Date()), 1000); - 292 | return () => { react-hooks/set-state-in-effect - -D:\dcprint\erp-project\src\app\[locale]\dashboard\sales\page.tsx - 214:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\dashboard\sales\page.tsx:214:5 - 212 | }; - 213 | fetchData(); -> 214 | setCurrentTime(new Date()); - | ^^^^^^^^^^^^^^ Avoid calling setState() directly within an effect - 215 | const timer1 = setInterval(fetchData, 60000); - 216 | const timer2 = setInterval(() => setCurrentTime(new Date()), 1000); - 217 | return () => { react-hooks/set-state-in-effect - -D:\dcprint\erp-project\src\app\[locale]\dashboard\warehouse\page.tsx - 229:28 warning 'setWarehouseHistory' is assigned a value but never used. Allowed unused elements of array destructuring must match /^_/u @typescript-eslint/no-unused-vars - 243:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\dashboard\warehouse\page.tsx:243:5 - 241 | }; - 242 | fetchData(); -> 243 | setCurrentTime(new Date()); - | ^^^^^^^^^^^^^^ Avoid calling setState() directly within an effect - 244 | const timer1 = setInterval(fetchData, 60000); - 245 | const timer2 = setInterval(() => setCurrentTime(new Date()), 1000); - 246 | return () => { react-hooks/set-state-in-effect - 306:9 warning 'occupancyChartUrls' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - -D:\dcprint\erp-project\src\app\[locale]\dcprint\die\page.tsx - 50:6 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "妯″垏鍒€"銆傚缓璁娇鐢? tc('text_fy0my') i18n/no-chinese-hardcode - 51:6 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍒嗗垏鍒€"銆傚缓璁娇鐢? tc('text_cewrj') i18n/no-chinese-hardcode - 52:6 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍘嬬棔鍒€"銆傚缓璁娇鐢? tc('text_ct0gm') i18n/no-chinese-hardcode - 53:6 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍐插瓟鍒€"銆傚缓璁娇鐢? tc('text_cerfi') i18n/no-chinese-hardcode - 59:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍦ㄧ敤"銆傚缓璁娇鐢? tc('text_fgu8') i18n/no-chinese-hardcode - 60:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "寰呬繚鍏?銆傚缓璁娇鐢? tc('text_edpc3') i18n/no-chinese-hardcode - 61:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "淇濆吇涓?銆傚缓璁娇鐢? tc('text_c3elr') i18n/no-chinese-hardcode - 62:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "宸叉姤搴?銆傚缓璁娇鐢? tc('text_e8o3w') i18n/no-chinese-hardcode - 95:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\dcprint\die\page.tsx:95:5 - 93 | }; - 94 | useEffect(() => { -> 95 | fetchData(); - | ^^^^^^^^^ Avoid calling setState() directly within an effect - 96 | }, [page]); - 97 | - 98 | const handleSave = async () => { react-hooks/set-state-in-effect - 96:6 warning React Hook useEffect has a missing dependency: 'fetchData'. Either include it or remove the dependency array react-hooks/exhaustive-deps - 162:48 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏂板鍒€鍏?銆傚缓璁娇鐢? {tc('text_d73qp1')} i18n/no-chinese-hardcode - 172:50 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍒€鍏风紪鐮?銆傚缓璁娇鐢? {tc('text_aovqsi')} i18n/no-chinese-hardcode - 173:50 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍒€鍏峰悕绉?銆傚缓璁娇鐢? {tc('text_aoofne')} i18n/no-chinese-hardcode - 175:50 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "灏哄瑙勬牸"銆傚缓璁娇鐢? {tc('text_c0winq')} i18n/no-chinese-hardcode - 178:50 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "宸茬敤"銆傚缓璁娇鐢? {tc('text_gmeu')} i18n/no-chinese-hardcode - 237:88 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏆傛棤璁板綍"銆傚缓璁娇鐢? {tc('text_dd1mmb')} i18n/no-chinese-hardcode - 247:51 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍏?銆傚缓璁娇鐢? {tc('text_g35')} i18n/no-chinese-hardcode - 247:59 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏉?銆傚缓璁娇鐢? {tc('text_kf5')} i18n/no-chinese-hardcode - 254:14 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "涓婁竴椤?銆傚缓璁娇鐢? {tc('text_btlof')} i18n/no-chinese-hardcode - 262:14 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "涓嬩竴椤?銆傚缓璁娇鐢? {tc('text_btmf4')} i18n/no-chinese-hardcode - 274:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍒€鍏风紪鐮?銆傚缓璁娇鐢? {tc('text_aovqsi')} i18n/no-chinese-hardcode - 281:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍒€鍏峰悕绉?銆傚缓璁娇鐢? {tc('text_aoofne')} i18n/no-chinese-hardcode - 297:43 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "妯″垏鍒€"銆傚缓璁娇鐢? {tc('text_fy0my')} i18n/no-chinese-hardcode - 298:43 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍒嗗垏鍒€"銆傚缓璁娇鐢? {tc('text_cewrj')} i18n/no-chinese-hardcode - 299:43 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍘嬬棔鍒€"銆傚缓璁娇鐢? {tc('text_ct0gm')} i18n/no-chinese-hardcode - 300:43 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍐插瓟鍒€"銆傚缓璁娇鐢? {tc('text_cerfi')} i18n/no-chinese-hardcode - 305:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "灏哄瑙勬牸"銆傚缓璁娇鐢? {tc('text_c0winq')} i18n/no-chinese-hardcode - 312:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "浜у搧鍚嶇О"銆傚缓璁娇鐢? {tc('text_a9yk8t')} i18n/no-chinese-hardcode - 319:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏈€澶т娇鐢ㄦ鏁?銆傚缓璁娇鐢? {tc('text_cxnt0h')} i18n/no-chinese-hardcode - 330:78 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍙栨秷"銆傚缓璁娇鐢? {tc('text_ev02')} i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\[locale]\dcprint\ink-formula\page.tsx - 28:3 warning 'Select' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 29:3 warning 'SelectContent' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 30:3 warning 'SelectItem' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 31:3 warning 'SelectTrigger' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 32:3 warning 'SelectValue' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 34:10 warning 'Tabs' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 34:16 warning 'TabsContent' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 34:29 warning 'TabsList' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 34:39 warning 'TabsTrigger' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 43:3 warning 'Trash2' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 85:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鑽夌"銆傚缓璁娇鐢? tc('text_n02e') i18n/no-chinese-hardcode - 86:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "宸茬敓鏁?銆傚缓璁娇鐢? tc('text_ebukb') i18n/no-chinese-hardcode - 87:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "宸蹭綔搴?銆傚缓璁娇鐢? tc('text_e5e0l') i18n/no-chinese-hardcode - 121:19 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍔犺浇鑹插彿澶辫触"銆傚缓璁娇鐢? tc('text_95luk6') i18n/no-chinese-hardcode - 135:19 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍔犺浇閰嶆柟鐗堟湰澶辫触"銆傚缓璁娇鐢? tc('text_h42xi9') i18n/no-chinese-hardcode - 140:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\dcprint\ink-formula\page.tsx:140:5 - 138 | - 139 | useEffect(() => { -> 140 | fetchColors(); - | ^^^^^^^^^^^ Avoid calling setState() directly within an effect - 141 | }, [fetchColors]); - 142 | useEffect(() => { - 143 | if (selectedColor) fetchVersions(selectedColor.id); react-hooks/set-state-in-effect - 143:24 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\dcprint\ink-formula\page.tsx:143:24 - 141 | }, [fetchColors]); - 142 | useEffect(() => { -> 143 | if (selectedColor) fetchVersions(selectedColor.id); - | ^^^^^^^^^^^^^ Avoid calling setState() directly within an effect - 144 | }, [selectedColor, fetchVersions]); - 145 | - 146 | const handleSaveColor = async () => { react-hooks/set-state-in-effect - 148:21 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鑹插彿缂栫爜鍜屽悕绉颁笉鑳戒负绌?銆傚缓璁娇鐢? tc('text_pcjjhr') i18n/no-chinese-hardcode - 178:19 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鎿嶄綔澶辫触"銆傚缓璁娇鐢? tc('text_d1rj43') i18n/no-chinese-hardcode - 189:23 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "閰嶆柟鐗堟湰宸茬敓鏁?銆傚缓璁娇鐢? tc('text_qu6w7f') i18n/no-chinese-hardcode - 195:19 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鎿嶄綔澶辫触"銆傚缓璁娇鐢? tc('text_d1rj43') i18n/no-chinese-hardcode - 206:23 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "閰嶆柟鐗堟湰宸蹭綔搴?銆傚缓璁娇鐢? tc('text_qu0fnp') i18n/no-chinese-hardcode - 212:19 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鎿嶄綔澶辫触"銆傚缓璁娇鐢? tc('text_d1rj43') i18n/no-chinese-hardcode - 223:23 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鏂扮増鏈凡鍒涘缓"銆傚缓璁娇鐢? tc('text_5f4il') i18n/no-chinese-hardcode - 229:19 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鎿嶄綔澶辫触"銆傚缓璁娇鐢? tc('text_d1rj43') i18n/no-chinese-hardcode - 271:28 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鑹插彿"銆傚缓璁娇鐢? {tc('text_mnd1')} i18n/no-chinese-hardcode - 286:32 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鑹插彿缂栫爜"銆傚缓璁娇鐢? {tc('text_gt7wog')} i18n/no-chinese-hardcode - 287:32 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鑹插彿鍚嶇О"銆傚缓璁娇鐢? {tc('text_gt0ljc')} i18n/no-chinese-hardcode - 288:32 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鑹茬郴"銆傚缓璁娇鐢? {tc('text_mvgp')} i18n/no-chinese-hardcode - 289:32 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍩哄ⅷ绫诲瀷"銆傚缓璁娇鐢? {tc('text_bh1zha')} i18n/no-chinese-hardcode - 291:32 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鐘舵€?銆傚缓璁娇鐢? {tc('text_k1e3')} i18n/no-chinese-hardcode - 352:49 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "- 閰嶆柟鐗堟湰"銆傚缓璁娇鐢? {tc('text_uha29p')} i18n/no-chinese-hardcode - 358:52 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏂板缓鐗堟湰"銆傚缓璁娇鐢? {tc('text_d86vu6')} i18n/no-chinese-hardcode - 365:73 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏆傛棤閰嶆柟鐗堟湰"銆傚缓璁娇鐢? {tc('text_elxunm')} i18n/no-chinese-hardcode - 370:34 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鐗堟湰鍙?銆傚缓璁娇鐢? {tc('text_h8m1f')} i18n/no-chinese-hardcode - 371:34 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鐗堟湰鍚嶇О"銆傚缓璁娇鐢? {tc('text_eufntz')} i18n/no-chinese-hardcode - 372:34 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鎬婚噸閲?銆傚缓璁娇鐢? {tc('text_et0rh')} i18n/no-chinese-hardcode - 373:34 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鐞嗚鎴愭湰"銆傚缓璁娇鐢? {tc('text_f7rh5c')} i18n/no-chinese-hardcode - 374:34 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鐘舵€?銆傚缓璁娇鐢? {tc('text_k1e3')} i18n/no-chinese-hardcode - 375:34 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鐢熸晥鏃堕棿"銆傚缓璁娇鐢? {tc('text_f753lz')} i18n/no-chinese-hardcode - 446:32 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "濉啓鑹插彿鍩虹淇℃伅"銆傚缓璁娇鐢? {tc('text_nqohdl')} i18n/no-chinese-hardcode - 450:22 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鑹插彿缂栫爜"銆傚缓璁娇鐢? {tc('text_gt7wog')} i18n/no-chinese-hardcode - 459:22 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鑹插彿鍚嶇О"銆傚缓璁娇鐢? {tc('text_gt0ljc')} i18n/no-chinese-hardcode - 468:22 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鑹茬郴"銆傚缓璁娇鐢? {tc('text_mvgp')} i18n/no-chinese-hardcode - 475:22 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍩哄ⅷ绫诲瀷"銆傚缓璁娇鐢? {tc('text_bh1zha')} i18n/no-chinese-hardcode - 482:22 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "Pantone鑹插彿"銆傚缓璁娇鐢? {tc('text_fsw5f8')} i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\[locale]\dcprint\ink-mixed\page.tsx - 65:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "宸插叆搴?銆傚缓璁娇鐢? tc('text_e5qgw') i18n/no-chinese-hardcode - 66:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "宸蹭娇鐢?銆傚缓璁娇鐢? tc('text_e5jaz') i18n/no-chinese-hardcode - 67:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "宸茶繃鏈?銆傚缓璁娇鐢? tc('text_ege5m') i18n/no-chinese-hardcode - 101:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\dcprint\ink-mixed\page.tsx:101:5 - 99 | - 100 | useEffect(() => { -> 101 | fetchData(); - | ^^^^^^^^^ Avoid calling setState() directly within an effect - 102 | }, [page]); - 103 | - 104 | const handleSave = async () => { react-hooks/set-state-in-effect - 102:6 warning React Hook useEffect has a missing dependency: 'fetchData'. Either include it or remove the dependency array react-hooks/exhaustive-deps - 186:48 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏂板鍏ュ簱"銆傚缓璁娇鐢? {tc('text_d73pks')} i18n/no-chinese-hardcode - 197:50 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "璁板綍鍗曞彿"銆傚缓璁娇鐢? {tc('text_i0myef')} i18n/no-chinese-hardcode - 199:50 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "璋冭壊姣斾緥"銆傚缓璁娇鐢? {tc('text_i7d8w6')} i18n/no-chinese-hardcode - 200:50 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鑹插僵鍚嶇О"銆傚缓璁娇鐢? {tc('text_guoy62')} i18n/no-chinese-hardcode - 203:50 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鎿嶄綔鍛?銆傚缓璁娇鐢? {tc('text_f5hc9')} i18n/no-chinese-hardcode - 204:50 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "璋冭壊鏃堕棿"銆傚缓璁娇鐢? {tc('text_i7cmvh')} i18n/no-chinese-hardcode - 205:50 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "杩囨湡鏃堕棿"銆傚缓璁娇鐢? {tc('text_ikg3cm')} i18n/no-chinese-hardcode - 256:32 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "宸蹭娇鐢?銆傚缓璁娇鐢? {tc('text_e5jaz')} i18n/no-chinese-hardcode - 264:32 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "宸茶繃鏈?銆傚缓璁娇鐢? {tc('text_ege5m')} i18n/no-chinese-hardcode - 295:88 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏆傛棤璋冭壊娌瑰ⅷ璁板綍"銆傚缓璁娇鐢? {tc('text_7va1sv')} i18n/no-chinese-hardcode - 306:51 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍏?銆傚缓璁娇鐢? {tc('text_g35')} i18n/no-chinese-hardcode - 316:14 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "涓婁竴椤?銆傚缓璁娇鐢? {tc('text_btlof')} i18n/no-chinese-hardcode - 324:14 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "涓嬩竴椤?銆傚缓璁娇鐢? {tc('text_btmf4')} i18n/no-chinese-hardcode - 337:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍘熸补澧ㄧ紪鍙?銆傚缓璁娇鐢? {tc('text_e3uxo1')} i18n/no-chinese-hardcode - 344:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍘熸补澧ㄥ悕绉?銆傚缓璁娇鐢? {tc('text_e421ov')} i18n/no-chinese-hardcode - 351:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "璋冭壊姣斾緥"銆傚缓璁娇鐢? {tc('text_i7d8w6')} i18n/no-chinese-hardcode - 359:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鑹插僵鍚嶇О"銆傚缓璁娇鐢? {tc('text_guoy62')} i18n/no-chinese-hardcode - 366:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鑹插僵缂栫爜"銆傚缓璁娇鐢? {tc('text_guw9b6')} i18n/no-chinese-hardcode - 374:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "瀹㈡埛鍚嶇О"銆傚缓璁娇鐢? {tc('text_byvcwo')} i18n/no-chinese-hardcode - 381:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "璋冭壊鏃堕棿"銆傚缓璁娇鐢? {tc('text_i7cmvh')} i18n/no-chinese-hardcode - 389:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鎿嶄綔鍛?銆傚缓璁娇鐢? {tc('text_f5hc9')} i18n/no-chinese-hardcode - 422:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "杩囨湡鏃堕棿"銆傚缓璁娇鐢? {tc('text_ikg3cm')} i18n/no-chinese-hardcode - 438:78 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍙栨秷"銆傚缓璁娇鐢? {tc('text_ev02')} i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\[locale]\dcprint\ink-opening\page.tsx - 85:23 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "24灏忔椂"銆傚缓璁娇鐢? tc('text_1d7rd') i18n/no-chinese-hardcode - 86:23 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "48灏忔椂"銆傚缓璁娇鐢? tc('text_1ekp7') i18n/no-chinese-hardcode - 87:23 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "72灏忔椂"銆傚缓璁娇鐢? tc('text_1gd7m') i18n/no-chinese-hardcode - 88:23 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "96灏忔椂"銆傚缓璁娇鐢? tc('text_1hq5g') i18n/no-chinese-hardcode - 89:24 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "120灏忔椂"銆傚缓璁娇鐢? tc('text_sb1va') i18n/no-chinese-hardcode - 90:24 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "168灏忔椂(7澶?"銆傚缓璁娇鐢? tc('text_upeg2f') i18n/no-chinese-hardcode - 91:24 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "336灏忔椂(14澶?"銆傚缓璁娇鐢? tc('text_3alp3m') i18n/no-chinese-hardcode - 92:24 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "720灏忔椂(30澶?"銆傚缓璁娇鐢? tc('text_h9z2vr') i18n/no-chinese-hardcode - 102:10 warning 'loading' is assigned a value but never used. Allowed unused elements of array destructuring must match /^_/u @typescript-eslint/no-unused-vars - 131:10 warning 'materials' is assigned a value but never used. Allowed unused elements of array destructuring must match /^_/u @typescript-eslint/no-unused-vars - 149:22 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鑾峰彇娌瑰ⅷ寮€缃愯褰曞け璐?銆傚缓璁娇鐢? tc('text_q23b6x') i18n/no-chinese-hardcode - 153:6 warning React Hook useCallback has a missing dependency: 'toast'. Either include it or remove the dependency array react-hooks/exhaustive-deps - 166:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\dcprint\ink-opening\page.tsx:166:5 - 164 | - 165 | useEffect(() => { -> 166 | fetchRecords(); - | ^^^^^^^^^^^^ Avoid calling setState() directly within an effect - 167 | fetchMaterials(); - 168 | }, [fetchRecords]); - 169 | react-hooks/set-state-in-effect - 172:22 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "璇峰~鍐欏繀濉瓧娈?銆傚缓璁娇鐢? tc('text_stv1kn') i18n/no-chinese-hardcode - 195:24 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "娌瑰ⅷ寮€缃愯褰曞垱寤烘垚鍔?銆傚缓璁娇鐢? tc('text_toikya') i18n/no-chinese-hardcode - 215:22 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍒涘缓娌瑰ⅷ寮€缃愯褰曞け璐?銆傚缓璁娇鐢? tc('text_z7ufc9') i18n/no-chinese-hardcode - 228:24 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鐘舵€佹洿鏂版垚鍔?銆傚缓璁娇鐢? tc('text_flsaxi') i18n/no-chinese-hardcode - 234:22 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鏇存柊澶辫触"銆傚缓璁娇鐢? tc('text_det3dc') i18n/no-chinese-hardcode - 238:9 warning 'handleDelete' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 239:18 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "纭畾鍒犻櫎姝よ褰曪紵"銆傚缓璁娇鐢? tc('text_x5zasm') i18n/no-chinese-hardcode - 244:24 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍒犻櫎鎴愬姛"銆傚缓璁娇鐢? tc('text_azeh8z') i18n/no-chinese-hardcode - 250:22 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍒犻櫎澶辫触"銆傚缓璁娇鐢? tc('text_azdahk') i18n/no-chinese-hardcode - 276:58 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "浣跨敤涓?銆傚缓璁娇鐢? {tc('text_c7jd0')} i18n/no-chinese-hardcode - 288:58 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "宸茶繃鏈?銆傚缓璁娇鐢? {tc('text_ege5m')} i18n/no-chinese-hardcode - 300:58 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍗冲皢杩囨湡"銆傚缓璁娇鐢? {tc('text_ax323v')} i18n/no-chinese-hardcode - 312:58 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "宸叉姤搴?銆傚缓璁娇鐢? {tc('text_e8o3w')} i18n/no-chinese-hardcode - 328:54 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "杩囨湡棰勮"銆傚缓璁娇鐢? {tc('text_ikomu2')} i18n/no-chinese-hardcode - 331:75 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "浠ヤ笅娌瑰ⅷ宸茶秴杩囨湁鏁堜娇鐢ㄦ椂闂达紝璇峰強鏃跺鐞嗭紒"銆傚缓璁娇鐢? {tc('text_4skp25')} i18n/no-chinese-hardcode - 339:32 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "璁板綍鍗曞彿"銆傚缓璁娇鐢? {tc('text_i0myef')} i18n/no-chinese-hardcode - 340:32 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "娌瑰ⅷ鍚嶇О"銆傚缓璁娇鐢? {tc('text_e32i1e')} i18n/no-chinese-hardcode - 341:32 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "娌瑰ⅷ绫诲瀷"銆傚缓璁娇鐢? {tc('text_e396tb')} i18n/no-chinese-hardcode - 342:32 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "寮€缃愭椂闂?銆傚缓璁娇鐢? {tc('text_ciieby')} i18n/no-chinese-hardcode - 343:32 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "杩囨湡鏃堕棿"銆傚缓璁娇鐢? {tc('text_ikg3cm')} i18n/no-chinese-hardcode - 366:26 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏍囪杩囨湡"銆傚缓璁娇鐢? {tc('text_dpi4xt')} i18n/no-chinese-hardcode - 374:26 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏍囪鎶ュ簾"銆傚缓璁娇鐢? {tc('text_dpaew3')} i18n/no-chinese-hardcode - 390:28 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "娌瑰ⅷ寮€缃愯褰?銆傚缓璁娇鐢? {tc('text_tjkjgc')} i18n/no-chinese-hardcode - 409:45 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍏ㄩ儴鐘舵€?銆傚缓璁娇鐢? {tc('text_avez63')} i18n/no-chinese-hardcode - 410:43 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "浣跨敤涓?銆傚缓璁娇鐢? {tc('text_c7jd0')} i18n/no-chinese-hardcode - 411:43 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "宸茶繃鏈?銆傚缓璁娇鐢? {tc('text_ege5m')} i18n/no-chinese-hardcode - 412:43 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "宸叉姤搴?銆傚缓璁娇鐢? {tc('text_e8o3w')} i18n/no-chinese-hardcode - 420:45 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍏ㄩ儴绫诲瀷"銆傚缓璁娇鐢? {tc('text_avglbk')} i18n/no-chinese-hardcode - 421:49 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "婧跺墏鍨?銆傚缓璁娇鐢? {tc('text_gm8xr')} i18n/no-chinese-hardcode - 422:44 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "UV鍨?銆傚缓璁娇鐢? {tc('text_2adm')} i18n/no-chinese-hardcode - 423:47 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "姘存€?銆傚缓璁娇鐢? {tc('text_ixkj')} i18n/no-chinese-hardcode - 427:57 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍒锋柊"銆傚缓璁娇鐢? {tc('text_ejix')} i18n/no-chinese-hardcode - 431:52 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏂板寮€缃?銆傚缓璁娇鐢? {tc('text_d767cu')} i18n/no-chinese-hardcode - 441:30 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "璁板綍鍗曞彿"銆傚缓璁娇鐢? {tc('text_i0myef')} i18n/no-chinese-hardcode - 442:30 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "娌瑰ⅷ鍚嶇О"銆傚缓璁娇鐢? {tc('text_e32i1e')} i18n/no-chinese-hardcode - 443:30 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "娌瑰ⅷ绫诲瀷"銆傚缓璁娇鐢? {tc('text_e396tb')} i18n/no-chinese-hardcode - 444:30 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鎵瑰彿"銆傚缓璁娇鐢? {tc('text_h7ku')} i18n/no-chinese-hardcode - 445:30 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "寮€缃愭椂闂?銆傚缓璁娇鐢? {tc('text_ciieby')} i18n/no-chinese-hardcode - 447:30 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "杩囨湡鏃堕棿"銆傚缓璁娇鐢? {tc('text_ikg3cm')} i18n/no-chinese-hardcode - 449:30 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍓╀綑鏁伴噺"銆傚缓璁娇鐢? {tc('text_aqbejz')} i18n/no-chinese-hardcode - 457:96 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏆傛棤娌瑰ⅷ寮€缃愯褰?銆傚缓璁娇鐢? {tc('text_cd9ztu')} i18n/no-chinese-hardcode - 487:43 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "灏忔椂"銆傚缓璁娇鐢? {tc('text_g7uv')} i18n/no-chinese-hardcode - 570:26 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鐗╂枡缂栫爜"銆傚缓璁娇鐢? {tc('text_euzqpn')} i18n/no-chinese-hardcode - 580:26 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鐗╂枡鍚嶇О"銆傚缓璁娇鐢? {tc('text_eusfkj')} i18n/no-chinese-hardcode - 592:26 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "娌瑰ⅷ绫诲瀷"銆傚缓璁娇鐢? {tc('text_e396tb')} i18n/no-chinese-hardcode - 601:51 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "婧跺墏鍨?銆傚缓璁娇鐢? {tc('text_gm8xr')} i18n/no-chinese-hardcode - 602:46 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "UV鍨?銆傚缓璁娇鐢? {tc('text_2adm')} i18n/no-chinese-hardcode - 603:49 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "姘存€?銆傚缓璁娇鐢? {tc('text_ixkj')} i18n/no-chinese-hardcode - 608:26 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鎵瑰彿"銆傚缓璁娇鐢? {tc('text_h7ku')} i18n/no-chinese-hardcode - 648:26 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍓╀綑鏁伴噺"銆傚缓璁娇鐢? {tc('text_aqbejz')} i18n/no-chinese-hardcode - 671:41 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "缃?銆傚缓璁娇鐢? {tc('text_p5c')} i18n/no-chinese-hardcode - 671:45 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "缃?銆傚缓璁娇鐢? {tc('text_p5c')} i18n/no-chinese-hardcode - 677:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鎿嶄綔鍛?銆傚缓璁娇鐢? {tc('text_f5hc9')} i18n/no-chinese-hardcode - 693:78 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍙栨秷"銆傚缓璁娇鐢? {tc('text_ev02')} i18n/no-chinese-hardcode - 718:61 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鐗╂枡鍚嶇О锛?銆傚缓璁娇鐢? {tc('text_ybuh7r')} i18n/no-chinese-hardcode - 733:46 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "灏忔椂"銆傚缓璁娇鐢? {tc('text_g7uv')} i18n/no-chinese-hardcode - 755:61 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鐘舵€侊細"銆傚缓璁娇鐢? {tc('text_halin')} i18n/no-chinese-hardcode - 763:61 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "澶囨敞锛?銆傚缓璁娇鐢? {tc('text_dld2x')} i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\[locale]\dcprint\ink-usage\page.tsx - 102:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\dcprint\ink-usage\page.tsx:102:5 - 100 | - 101 | useEffect(() => { -> 102 | fetchData(); - | ^^^^^^^^^ Avoid calling setState() directly within an effect - 103 | }, [page]); - 104 | useEffect(() => { - 105 | fetchInks(); react-hooks/set-state-in-effect - 103:6 warning React Hook useEffect has a missing dependency: 'fetchData'. Either include it or remove the dependency array react-hooks/exhaustive-deps - 105:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\dcprint\ink-usage\page.tsx:105:5 - 103 | }, [page]); - 104 | useEffect(() => { -> 105 | fetchInks(); - | ^^^^^^^^^ Avoid calling setState() directly within an effect - 106 | }, []); - 107 | - 108 | const handleSave = async () => { react-hooks/set-state-in-effect - 188:48 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏂板鑰楃敤"銆傚缓璁娇鐢? {tc('text_d7brz3')} i18n/no-chinese-hardcode - 199:50 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鑰楃敤鏃ユ湡"銆傚缓璁娇鐢? {tc('text_gn9imj')} i18n/no-chinese-hardcode - 200:50 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "缃戠増缂栫爜"銆傚缓璁娇鐢? {tc('text_gjgb2a')} i18n/no-chinese-hardcode - 201:50 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "娌瑰ⅷ缂栫爜"銆傚缓璁娇鐢? {tc('text_e39t6i')} i18n/no-chinese-hardcode - 202:50 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "娌瑰ⅷ鍚嶇О"銆傚缓璁娇鐢? {tc('text_e32i1e')} i18n/no-chinese-hardcode - 203:50 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鑰楃敤鏁伴噺"銆傚缓璁娇鐢? {tc('text_gn9o9c')} i18n/no-chinese-hardcode - 205:50 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鎿嶄綔浜?銆傚缓璁娇鐢? {tc('text_f5g8b')} i18n/no-chinese-hardcode - 235:87 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏆傛棤璁板綍"銆傚缓璁娇鐢? {tc('text_dd1mmb')} i18n/no-chinese-hardcode - 246:51 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍏?銆傚缓璁娇鐢? {tc('text_g35')} i18n/no-chinese-hardcode - 246:59 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏉?銆傚缓璁娇鐢? {tc('text_kf5')} i18n/no-chinese-hardcode - 253:14 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "涓婁竴椤?銆傚缓璁娇鐢? {tc('text_btlof')} i18n/no-chinese-hardcode - 261:14 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "涓嬩竴椤?銆傚缓璁娇鐢? {tc('text_btmf4')} i18n/no-chinese-hardcode - 274:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "娌瑰ⅷ"銆傚缓璁娇鐢? {tc('text_iz9r')} i18n/no-chinese-hardcode - 301:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鑰楃敤鏁伴噺"銆傚缓璁娇鐢? {tc('text_gn9o9c')} i18n/no-chinese-hardcode - 329:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鑰楃敤鏃ユ湡"銆傚缓璁娇鐢? {tc('text_gn9imj')} i18n/no-chinese-hardcode - 337:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鎿嶄綔浜?銆傚缓璁娇鐢? {tc('text_f5g8b')} i18n/no-chinese-hardcode - 352:78 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍙栨秷"銆傚缓璁娇鐢? {tc('text_ev02')} i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\[locale]\dcprint\ink\page.tsx - 131:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\dcprint\ink\page.tsx:131:5 - 129 | - 130 | useEffect(() => { -> 131 | fetchData(); - | ^^^^^^^^^ Avoid calling setState() directly within an effect - 132 | }, [fetchData]); - 133 | - 134 | const handleSort = (field: SortField) => { react-hooks/set-state-in-effect - 186:9 warning 'handlePrint' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 190:9 warning 'handleExportPDF' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 194:9 warning 'handleExportXLS' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 198:9 warning 'handleExportWORD' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 312:28 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "娌瑰ⅷ绠$悊"銆傚缓璁娇鐢? {tc('text_e39784')} i18n/no-chinese-hardcode - 361:24 warning Error: Cannot create components during render - -Components created during render will reset their state each time they are created. Declare components outside of render. - -D:\dcprint\erp-project\src\app\[locale]\dcprint\ink\page.tsx:361:24 - 359 | - 360 | {t('inkCode')} -> 361 | - | ^^^^^^^^ This component is created during render - 362 | - 363 | - 364 | 170 | const SortIcon = ({ field }: { field: SortField }) => { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 171 | if (sortField !== field) return ; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 172 | return sortDir === 'asc' ? ( - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 173 | - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 174 | ) : ( - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 175 | - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 176 | ); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 177 | }; - | ^^^^ The component is created during render here - 178 | - 179 | const getExportData = () => - 180 | sortedList.map((item) => ({ react-hooks/static-components - 370:24 warning Error: Cannot create components during render - -Components created during render will reset their state each time they are created. Declare components outside of render. - -D:\dcprint\erp-project\src\app\[locale]\dcprint\ink\page.tsx:370:24 - 368 | - 369 | {t('inkName')} -> 370 | - | ^^^^^^^^ This component is created during render - 371 | - 372 | - 373 | 170 | const SortIcon = ({ field }: { field: SortField }) => { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 171 | if (sortField !== field) return ; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 172 | return sortDir === 'asc' ? ( - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 173 | - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 174 | ) : ( - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 175 | - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 176 | ); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 177 | }; - | ^^^^ The component is created during render here - 178 | - 179 | const getExportData = () => - 180 | sortedList.map((item) => ({ react-hooks/static-components - 379:24 warning Error: Cannot create components during render - -Components created during render will reset their state each time they are created. Declare components outside of render. - -D:\dcprint\erp-project\src\app\[locale]\dcprint\ink\page.tsx:379:24 - 377 | - 378 | {tc('type')} -> 379 | - | ^^^^^^^^ This component is created during render - 380 | - 381 | - 382 | {tc('color')} - -D:\dcprint\erp-project\src\app\[locale]\dcprint\ink\page.tsx:170:20 - 168 | }; - 169 | -> 170 | const SortIcon = ({ field }: { field: SortField }) => { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 171 | if (sortField !== field) return ; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 172 | return sortDir === 'asc' ? ( - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 173 | - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 174 | ) : ( - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 175 | - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 176 | ); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 177 | }; - | ^^^^ The component is created during render here - 178 | - 179 | const getExportData = () => - 180 | sortedList.map((item) => ({ react-hooks/static-components - 392:24 warning Error: Cannot create components during render - -Components created during render will reset their state each time they are created. Declare components outside of render. - -D:\dcprint\erp-project\src\app\[locale]\dcprint\ink\page.tsx:392:24 - 390 | - 391 | {t('stock')} -> 392 | - | ^^^^^^^^ This component is created during render - 393 | - 394 | - 395 | 170 | const SortIcon = ({ field }: { field: SortField }) => { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 171 | if (sortField !== field) return ; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 172 | return sortDir === 'asc' ? ( - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 173 | - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 174 | ) : ( - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 175 | - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 176 | ); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 177 | }; - | ^^^^ The component is created during render here - 178 | - 179 | const getExportData = () => - 180 | sortedList.map((item) => ({ react-hooks/static-components - 401:24 warning Error: Cannot create components during render - -Components created during render will reset their state each time they are created. Declare components outside of render. - -D:\dcprint\erp-project\src\app\[locale]\dcprint\ink\page.tsx:401:24 - 399 | - 400 | {t('safetyStock')} -> 401 | - | ^^^^^^^^ This component is created during render - 402 | - 403 | - 404 | 170 | const SortIcon = ({ field }: { field: SortField }) => { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 171 | if (sortField !== field) return ; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 172 | return sortDir === 'asc' ? ( - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 173 | - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 174 | ) : ( - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 175 | - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 176 | ); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 177 | }; - | ^^^^ The component is created during render here - 178 | - 179 | const getExportData = () => - 180 | sortedList.map((item) => ({ react-hooks/static-components - 410:24 warning Error: Cannot create components during render - -Components created during render will reset their state each time they are created. Declare components outside of render. - -D:\dcprint\erp-project\src\app\[locale]\dcprint\ink\page.tsx:410:24 - 408 | - 409 | {tc('status')} -> 410 | - | ^^^^^^^^ This component is created during render - 411 | - 412 | - 413 | {tc('actions')} - -D:\dcprint\erp-project\src\app\[locale]\dcprint\ink\page.tsx:170:20 - 168 | }; - 169 | -> 170 | const SortIcon = ({ field }: { field: SortField }) => { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 171 | if (sortField !== field) return ; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 172 | return sortDir === 'asc' ? ( - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 173 | - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 174 | ) : ( - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 175 | - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 176 | ); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 177 | }; - | ^^^^ The component is created during render here - 178 | - 179 | const getExportData = () => - 180 | sortedList.map((item) => ({ react-hooks/static-components - -D:\dcprint\erp-project\src\app\[locale]\dcprint\labels\page.tsx - 141:69 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "浣跨敤 mock 鏍囩鏁版嵁"銆傚缓璁娇鐢? tc('text_zhs964') i18n/no-chinese-hardcode - 169:69 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鏍囩鏁版嵁鑾峰彇鎴愬姛"銆傚缓璁娇鐢? tc('text_fiuf4t') i18n/no-chinese-hardcode - 175:70 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鑾峰彇鏍囩鏁版嵁澶辫触"銆傚缓璁娇鐢? tc('text_appazc') i18n/no-chinese-hardcode - 187:6 warning React Hook useEffect has a missing dependency: 'pageSize'. Either include it or remove the dependency array react-hooks/exhaustive-deps - -D:\dcprint\erp-project\src\app\[locale]\dcprint\page.tsx - 14:12 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鐗╂枡鏍囩绠$悊"銆傚缓璁娇鐢? tc('text_18vvxo') i18n/no-chinese-hardcode - 15:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "绠$悊鐗╂枡鏍囩锛屾敮鎸佷簩缁寸爜杩芥函"銆傚缓璁娇鐢? tc('text_q4gmqv') i18n/no-chinese-hardcode - 19:12 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏍囩鎬绘暟: 0"銆傚缓璁娇鐢? tc('text_d7sljy') i18n/no-chinese-hardcode - 22:12 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鐗╂枡鍒嗗垏"銆傚缓璁娇鐢? tc('text_eurv9t') i18n/no-chinese-hardcode - 23:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鎵弿鏍囩杩涜鍒嗗垏鎿嶄綔"銆傚缓璁娇鐢? tc('text_mxiu5g') i18n/no-chinese-hardcode - 27:12 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "浠婃棩鍒嗗垏: 0"銆傚缓璁娇鐢? tc('text_u9u8z2') i18n/no-chinese-hardcode - 30:12 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鐢熶骇娴佺▼鍗?銆傚缓璁娇鐢? tc('text_sw72y9') i18n/no-chinese-hardcode - 31:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鐢熸垚鍜岀鐞嗙敓浜ф祦绋嬪崱"銆傚缓璁娇鐢? tc('text_g5tro1') i18n/no-chinese-hardcode - 35:12 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "娴佺▼鍗℃暟: 0"銆傚缓璁娇鐢? tc('text_ym8o7l') i18n/no-chinese-hardcode - 38:12 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鐗╂枡杩芥函"銆傚缓璁娇鐢? tc('text_ev2kde') i18n/no-chinese-hardcode - 39:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍏ㄧ▼浜岀淮鐮佽拷婧煡璇?銆傚缓璁娇鐢? tc('text_h8mb5x') i18n/no-chinese-hardcode - 43:12 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "杩芥函璁板綍: 0"銆傚缓璁娇鐢? tc('text_r5mxot') i18n/no-chinese-hardcode - 49:12 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鎵弿鍒嗗垏"銆傚缓璁娇鐢? tc('text_ctwbd1') i18n/no-chinese-hardcode - 50:12 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鐢熸垚娴佺▼鍗?銆傚缓璁娇鐢? tc('text_qg294q') i18n/no-chinese-hardcode - 51:12 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "杩芥函鏌ヨ"銆傚缓璁娇鐢? tc('text_imiq27') i18n/no-chinese-hardcode - 52:12 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏍囩绠$悊"銆傚缓璁娇鐢? tc('text_dn1dss') i18n/no-chinese-hardcode - 57:12 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "浠婃棩鍒€鍏?銆傚缓璁娇鐢? tc('text_ad2pzm') i18n/no-chinese-hardcode - 58:12 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "浠婃棩娴佺▼鍗?銆傚缓璁娇鐢? tc('text_xs0tqc') i18n/no-chinese-hardcode - 59:12 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "浠婃棩杩芥函"銆傚缓璁娇鐢? tc('text_addfcd') i18n/no-chinese-hardcode - 60:12 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏍囩鎬绘暟"銆傚缓璁娇鐢? tc('text_dmwn58') i18n/no-chinese-hardcode - 75:57 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍏ㄧ▼浜岀淮鐮佽拷婧郴缁?銆傚缓璁娇鐢? {tc('text_h8ptpo')} i18n/no-chinese-hardcode - 81:58 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "寮€濮嬪垎鍒?銆傚缓璁娇鐢? {tc('text_ccwslo')} i18n/no-chinese-hardcode - 90:58 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鐢熸垚娴佺▼鍗?銆傚缓璁娇鐢? {tc('text_qg294q')} i18n/no-chinese-hardcode - 146:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "蹇嵎鎿嶄綔"銆傚缓璁娇鐢? {tc('text_cil0yj')} i18n/no-chinese-hardcode - 166:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "绯荤粺璇存槑"銆傚缓璁娇鐢? {tc('text_gaqy1q')} i18n/no-chinese-hardcode - 177:64 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "閫氳繃浜岀淮鐮佹爣绛剧鐞嗙墿鏂欏叆搴撱€佸嚭搴擄紝瀹炵幇鍏堣繘鍏堝嚭鐨勫簱瀛樼鐞?銆傚缓璁娇鐢? {tc('text_r0lwcf')} i18n/no-chinese-hardcode - 187:47 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鐗╂枡鍒嗗垏"銆傚缓璁娇鐢? {tc('text_eurv9t')} i18n/no-chinese-hardcode - 188:64 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏀寔姣嶆潗鍒嗗垏鎿嶄綔锛岃嚜鍔ㄧ敓鎴愬垎鍒囧悗鐨勬柊鏍囩锛屼繚鎸佽拷婧摼瀹屾暣"銆傚缓璁娇鐢? {tc('text_kg3hvm')} i18n/no-chinese-hardcode - 198:47 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鐢熶骇娴佺▼鍗?銆傚缓璁娇鐢? {tc('text_sw72y9')} i18n/no-chinese-hardcode - 199:64 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鎵弿宸ュ崟鍜岀墿鏂欑敓鎴愭祦绋嬪崱锛屽叧鑱斾富鏉愬拰杈呮枡锛屾敮鎸侀厤鏂欑鐞?銆傚缓璁娇鐢? {tc('text_4gbp8t')} i18n/no-chinese-hardcode - 210:64 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "閫氳繃娴佺▼鍗′簩缁寸爜杩芥函浜у搧浣跨敤鐨勬墍鏈夌墿鏂欎俊鎭紝鏀寔姝e悜鍜屽弽鍚戣拷婧?銆傚缓璁娇鐢? {tc('text_t6539f')} i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\[locale]\dcprint\process-card\page.tsx - 21:3 warning 'Select' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 22:3 warning 'SelectContent' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 23:3 warning 'SelectItem' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 24:3 warning 'SelectTrigger' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 25:3 warning 'SelectValue' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 27:10 warning 'Tabs' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 27:16 warning 'TabsContent' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 27:29 warning 'TabsList' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 27:39 warning 'TabsTrigger' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 40:3 warning 'Copy' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 112:19 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍔犺浇宸ヨ壓鍗″垪琛ㄥけ璐?銆傚缓璁娇鐢? tc('text_vfyes4') i18n/no-chinese-hardcode - 119:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\dcprint\process-card\page.tsx:119:5 - 117 | - 118 | useEffect(() => { -> 119 | fetchCards(); - | ^^^^^^^^^^ Avoid calling setState() directly within an effect - 120 | }, [fetchCards]); - 121 | - 122 | const handleSubmit = async (id: number) => { react-hooks/set-state-in-effect - 127:23 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "宸ヨ壓鍗″凡鎻愪氦"銆傚缓璁娇鐢? tc('text_v89pmu') i18n/no-chinese-hardcode - 133:19 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鎿嶄綔澶辫触"銆傚缓璁娇鐢? tc('text_d1rj43') i18n/no-chinese-hardcode - 142:23 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "宸ヨ壓鍗″凡纭"銆傚缓璁娇鐢? tc('text_v85vc4') i18n/no-chinese-hardcode - 148:19 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鎿嶄綔澶辫触"銆傚缓璁娇鐢? tc('text_d1rj43') i18n/no-chinese-hardcode - 153:18 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "纭浣滃簾姝ゅ伐鑹哄崱锛?銆傚缓璁娇鐢? tc('text_eohzmm') i18n/no-chinese-hardcode - 158:23 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "宸ヨ壓鍗″凡浣滃簾"銆傚缓璁娇鐢? tc('text_v8d3pz') i18n/no-chinese-hardcode - 164:19 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鎿嶄綔澶辫触"銆傚缓璁娇鐢? tc('text_d1rj43') i18n/no-chinese-hardcode - 169:18 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "纭鍒犻櫎姝ゅ伐鑹哄崱锛?銆傚缓璁娇鐢? tc('text_n7k18j') i18n/no-chinese-hardcode - 174:23 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍒犻櫎鎴愬姛"銆傚缓璁娇鐢? tc('text_azeh8z') i18n/no-chinese-hardcode - 180:19 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍒犻櫎澶辫触"銆傚缓璁娇鐢? tc('text_azdahk') i18n/no-chinese-hardcode - 191:62 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍏ㄩ儴"銆傚缓璁娇鐢? {tc('text_en40')} i18n/no-chinese-hardcode - 197:62 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鑽夌"銆傚缓璁娇鐢? {tc('text_n02e')} i18n/no-chinese-hardcode - 203:62 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鎵撴牱涓?銆傚缓璁娇鐢? {tc('text_ewm7d')} i18n/no-chinese-hardcode - 209:62 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "宸茬‘璁?銆傚缓璁娇鐢? {tc('text_ecmeg')} i18n/no-chinese-hardcode - 252:32 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鎵撴牱缂栧彿"銆傚缓璁娇鐢? {tc('text_cubwf9')} i18n/no-chinese-hardcode - 253:32 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鎵撴牱鍚嶇О"銆傚缓璁娇鐢? {tc('text_cu4sef')} i18n/no-chinese-hardcode - 254:32 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "瀹㈡埛"銆傚缓璁娇鐢? {tc('text_g4id')} i18n/no-chinese-hardcode - 255:32 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "浜у搧"銆傚缓璁娇鐢? {tc('text_dud6')} i18n/no-chinese-hardcode - 256:32 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鐗堟湰"銆傚缓璁娇鐢? {tc('text_k06c')} i18n/no-chinese-hardcode - 257:32 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍩烘潗"銆傚缓璁娇鐢? {tc('text_fj4m')} i18n/no-chinese-hardcode - 258:32 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍗板埛鑹?銆傚缓璁娇鐢? {tc('text_cmnwr')} i18n/no-chinese-hardcode - 259:32 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "棰勪及宸ユ椂"銆傚缓璁娇鐢? {tc('text_jkkmvx')} i18n/no-chinese-hardcode - 260:32 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鎬绘垚鏈?銆傚缓璁娇鐢? {tc('text_eko0n')} i18n/no-chinese-hardcode - 261:32 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鐘舵€?銆傚缓璁娇鐢? {tc('text_k1e3')} i18n/no-chinese-hardcode - 293:63 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏌ョ湅"銆傚缓璁娇鐢? {tc('text_ibpi')} i18n/no-chinese-hardcode - 307:68 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鎻愪氦"銆傚缓璁娇鐢? {tc('text_heqc')} i18n/no-chinese-hardcode - 319:90 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "纭"銆傚缓璁娇鐢? {tc('text_l912')} i18n/no-chinese-hardcode - 323:84 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "浣滃簾"銆傚缓璁娇鐢? {tc('text_e0n7')} i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\[locale]\dcprint\process-cards\page.tsx - 89:10 warning 'showDetail' is assigned a value but never used. Allowed unused elements of array destructuring must match /^_/u @typescript-eslint/no-unused-vars - 90:10 warning 'selectedCard' is assigned a value but never used. Allowed unused elements of array destructuring must match /^_/u @typescript-eslint/no-unused-vars - 94:5 warning Error: Cannot access variable before it is declared - -`fetchCards` is accessed before it is declared, which prevents the earlier access from updating when this value changes over time. - -D:\dcprint\erp-project\src\app\[locale]\dcprint\process-cards\page.tsx:94:5 - 92 | - 93 | useEffect(() => { -> 94 | fetchCards(); - | ^^^^^^^^^^ `fetchCards` accessed before it is declared - 95 | qrInputRef.current?.focus(); - 96 | }, []); - 97 | - -D:\dcprint\erp-project\src\app\[locale]\dcprint\process-cards\page.tsx:98:3 - 96 | }, []); - 97 | -> 98 | const fetchCards = async () => { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 99 | try { - | ^^^^^^^^^ -> 100 | if (USE_MOCK) { - 鈥? | ^^^^^^^^^ -> 118 | } - | ^^^^^^^^^ -> 119 | }; - | ^^^^^ `fetchCards` is declared here - 120 | - 121 | const handleScanQRCode = async () => { - 122 | if (!qrCode.trim()) return; react-hooks/immutability - 101:73 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "浣跨敤 mock 娴佺▼鍗℃暟鎹?銆傚缓璁娇鐢? tc('text_eo5kx6') i18n/no-chinese-hardcode - 110:73 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "娴佺▼鍗℃暟鎹幏鍙栨垚鍔?銆傚缓璁娇鐢? tc('text_3w3hvn') i18n/no-chinese-hardcode - 115:72 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鑾峰彇娴佺▼鍗℃暟鎹け璐?銆傚缓璁娇鐢? tc('text_oco9nu') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\[locale]\dcprint\screen-plate\page.tsx - 81:6 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "涓濈綉鐗?銆傚缓璁娇鐢? tc('text_c267o') i18n/no-chinese-hardcode - 82:6 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鑳跺嵃鐗?銆傚缓璁娇鐢? tc('text_jatse') i18n/no-chinese-hardcode - 83:6 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏌旂増"銆傚缓璁娇鐢? tc('text_iad0') i18n/no-chinese-hardcode - 84:6 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍑圭増"銆傚缓璁娇鐢? tc('text_ekj3') i18n/no-chinese-hardcode - 90:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏂板埗"銆傚缓璁娇鐢? tc('text_hqx2') i18n/no-chinese-hardcode - 91:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍙敤"銆傚缓璁娇鐢? tc('text_ex3t') i18n/no-chinese-hardcode - 92:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "宸叉洕鍏?銆傚缓璁娇鐢? tc('text_e9bb2') i18n/no-chinese-hardcode - 93:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鐢熶骇涓?銆傚缓璁娇鐢? tc('text_hjdtx') i18n/no-chinese-hardcode - 94:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "娓呮礂涓?銆傚缓璁娇鐢? tc('text_gn457') i18n/no-chinese-hardcode - 95:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "宸插啀鐢?銆傚缓璁娇鐢? tc('text_e5vvo') i18n/no-chinese-hardcode - 96:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鎹熷潖"銆傚缓璁娇鐢? tc('text_hdqo') i18n/no-chinese-hardcode - 97:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "宸叉姤搴?銆傚缓璁娇鐢? tc('text_e8o3w') i18n/no-chinese-hardcode - 100:12 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍒涘缓"銆傚缓璁娇鐢? tc('text_ehj3') i18n/no-chinese-hardcode - 101:12 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏇濆厜"銆傚缓璁娇鐢? tc('text_hxxo') i18n/no-chinese-hardcode - 102:12 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍗板埛"銆傚缓璁娇鐢? tc('text_en5z') i18n/no-chinese-hardcode - 103:12 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "娓呮礂"銆傚缓璁娇鐢? tc('text_jb8y') i18n/no-chinese-hardcode - 104:14 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍐嶇敓"銆傚缓璁娇鐢? tc('text_eiia') i18n/no-chinese-hardcode - 105:13 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鎶ュ簾"銆傚缓璁娇鐢? tc('text_haqi') i18n/no-chinese-hardcode - 106:20 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "寮犲姏璋冩暣"銆傚缓璁娇鐢? tc('text_ccpaq4') i18n/no-chinese-hardcode - 144:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\dcprint\screen-plate\page.tsx:144:5 - 142 | - 143 | useEffect(() => { -> 144 | fetchData(); - | ^^^^^^^^^ Avoid calling setState() directly within an effect - 145 | }, [page]); - 146 | - 147 | const fetchHistory = async (plateId: number) => { react-hooks/set-state-in-effect - 145:6 warning React Hook useEffect has a missing dependency: 'fetchData'. Either include it or remove the dependency array react-hooks/exhaustive-deps - 171:24 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "澶辫触"銆傚缓璁娇鐢? tc('text_fy1g') i18n/no-chinese-hardcode - 174:22 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "澶辫触"銆傚缓璁娇鐢? tc('text_fy1g') i18n/no-chinese-hardcode - 179:18 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "纭畾鍒犻櫎锛?銆傚缓璁娇鐢? tc('text_efhx5d') i18n/no-chinese-hardcode - 184:24 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍒犻櫎鎴愬姛"銆傚缓璁娇鐢? tc('text_azeh8z') i18n/no-chinese-hardcode - 188:22 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "澶辫触"銆傚缓璁娇鐢? tc('text_fy1g') i18n/no-chinese-hardcode - 200:22 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "璇烽€夋嫨鎿嶄綔绫诲瀷"銆傚缓璁娇鐢? tc('text_wuj0ye') i18n/no-chinese-hardcode - 218:24 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "璁板綍鎴愬姛"銆傚缓璁娇鐢? tc('text_i0pgc4') i18n/no-chinese-hardcode - 226:24 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "澶辫触"銆傚缓璁娇鐢? tc('text_fy1g') i18n/no-chinese-hardcode - 229:22 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "澶辫触"銆傚缓璁娇鐢? tc('text_fy1g') i18n/no-chinese-hardcode - 237:46 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "缃戠増绠$悊"銆傚缓璁娇鐢? {tc('text_gjfp3w')} i18n/no-chinese-hardcode - 270:48 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏂板缃戠増"銆傚缓璁娇鐢? {tc('text_d7bmo5')} i18n/no-chinese-hardcode - 281:50 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "缃戠増缂栫爜"銆傚缓璁娇鐢? {tc('text_gjgb2a')} i18n/no-chinese-hardcode - 282:50 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "缃戠増鍚嶇О"銆傚缓璁娇鐢? {tc('text_gj8zx6')} i18n/no-chinese-hardcode - 284:50 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鐩暟"銆傚缓璁娇鐢? {tc('text_ksaq')} i18n/no-chinese-hardcode - 363:88 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏆傛棤璁板綍"銆傚缓璁娇鐢? {tc('text_dd1mmb')} i18n/no-chinese-hardcode - 374:51 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍏?銆傚缓璁娇鐢? {tc('text_g35')} i18n/no-chinese-hardcode - 374:59 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏉?銆傚缓璁娇鐢? {tc('text_kf5')} i18n/no-chinese-hardcode - 381:14 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "涓婁竴椤?銆傚缓璁娇鐢? {tc('text_btlof')} i18n/no-chinese-hardcode - 389:14 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "涓嬩竴椤?銆傚缓璁娇鐢? {tc('text_btmf4')} i18n/no-chinese-hardcode - 402:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "缃戠増缂栫爜"銆傚缓璁娇鐢? {tc('text_gjgb2a')} i18n/no-chinese-hardcode - 409:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "缃戠増鍚嶇О"銆傚缓璁娇鐢? {tc('text_gj8zx6')} i18n/no-chinese-hardcode - 425:43 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "涓濈綉鐗?銆傚缓璁娇鐢? {tc('text_c267o')} i18n/no-chinese-hardcode - 426:43 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鑳跺嵃鐗?銆傚缓璁娇鐢? {tc('text_jatse')} i18n/no-chinese-hardcode - 427:43 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏌旂増"銆傚缓璁娇鐢? {tc('text_iad0')} i18n/no-chinese-hardcode - 428:43 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍑圭増"銆傚缓璁娇鐢? {tc('text_ekj3')} i18n/no-chinese-hardcode - 433:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鐩暟"銆傚缓璁娇鐢? {tc('text_ksaq')} i18n/no-chinese-hardcode - 440:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "涓濈綉鏉愯川"銆傚缓璁娇鐢? {tc('text_adu9bg')} i18n/no-chinese-hardcode - 454:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "妗嗙被鍨?銆傚缓璁娇鐢? {tc('text_fvhh2')} i18n/no-chinese-hardcode - 461:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "寮犲姏鍊?N/cm)"銆傚缓璁娇鐢? {tc('text_tt1rt5')} i18n/no-chinese-hardcode - 471:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏈€澶т娇鐢ㄦ鏁?銆傚缓璁娇鐢? {tc('text_cxnt0h')} i18n/no-chinese-hardcode - 499:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "瀛樻斁浣嶇疆"銆傚缓璁娇鐢? {tc('text_bxzajr')} i18n/no-chinese-hardcode - 514:78 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍙栨秷"銆傚缓璁娇鐢? {tc('text_ev02')} i18n/no-chinese-hardcode - 525:28 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "缃戠増鐢熷懡鍛ㄦ湡璁板綍"銆傚缓璁娇鐢? {tc('text_p82rm7')} i18n/no-chinese-hardcode - 529:43 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍘嗗彶璁板綍"銆傚缓璁娇鐢? {tc('text_aw7utt')} i18n/no-chinese-hardcode - 536:54 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鎿嶄綔绫诲瀷"銆傚缓璁娇鐢? {tc('text_d1x8m7')} i18n/no-chinese-hardcode - 540:54 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鎿嶄綔浜?銆傚缓璁娇鐢? {tc('text_f5g8b')} i18n/no-chinese-hardcode - 563:91 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏆傛棤璁板綍"銆傚缓璁娇鐢? {tc('text_dd1mmb')} i18n/no-chinese-hardcode - 574:28 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鎿嶄綔绫诲瀷"銆傚缓璁娇鐢? {tc('text_d1x8m7')} i18n/no-chinese-hardcode - 609:58 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鎻愪氦璁板綍"銆傚缓璁娇鐢? {tc('text_cxej5l')} i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\[locale]\dcprint\tool-manage\page.tsx - 34:10 warning 'Tabs' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 34:16 warning 'TabsContent' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 34:29 warning 'TabsList' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 34:39 warning 'TabsTrigger' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 41:3 warning 'Eye' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 43:3 warning 'Trash2' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 47:3 warning 'Settings' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 126:19 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍔犺浇宸ヨ鍒楄〃澶辫触"銆傚缓璁娇鐢? tc('text_ryok2q') i18n/no-chinese-hardcode - 133:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\dcprint\tool-manage\page.tsx:133:5 - 131 | - 132 | useEffect(() => { -> 133 | fetchTools(); - | ^^^^^^^^^^ Avoid calling setState() directly within an effect - 134 | }, [fetchTools]); - 135 | - 136 | const handleSave = async () => { react-hooks/set-state-in-effect - 138:21 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "宸ヨ缂栧彿鍜屽悕绉颁笉鑳戒负绌?銆傚缓璁娇鐢? tc('text_2zlxnm') i18n/no-chinese-hardcode - 159:19 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鎿嶄綔澶辫触"銆傚缓璁娇鐢? tc('text_d1rj43') i18n/no-chinese-hardcode - 168:23 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "宸ヨ宸插惎鐢?銆傚缓璁娇鐢? tc('text_tm5cjv') i18n/no-chinese-hardcode - 174:19 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鎿嶄綔澶辫触"銆傚缓璁娇鐢? tc('text_d1rj43') i18n/no-chinese-hardcode - 179:18 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "纭鎶ュ簾姝ゅ伐瑁咃紵"銆傚缓璁娇鐢? tc('text_cvmmgl') i18n/no-chinese-hardcode - 188:23 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "宸ヨ宸叉姤搴?銆傚缓璁娇鐢? tc('text_tm7ong') i18n/no-chinese-hardcode - 194:19 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鎿嶄綔澶辫触"銆傚缓璁娇鐢? tc('text_d1rj43') i18n/no-chinese-hardcode - 230:43 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍏ㄩ儴绫诲瀷"銆傚缓璁娇鐢? {tc('text_avglbk')} i18n/no-chinese-hardcode - 231:41 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍒€妯?銆傚缓璁娇鐢? {tc('text_ej35')} i18n/no-chinese-hardcode - 232:41 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "缃戠増"銆傚缓璁娇鐢? {tc('text_ma6v')} i18n/no-chinese-hardcode - 240:43 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍏ㄩ儴鐘舵€?銆傚缓璁娇鐢? {tc('text_avez63')} i18n/no-chinese-hardcode - 241:41 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "寰呯敤"銆傚缓璁娇鐢? {tc('text_gw1v')} i18n/no-chinese-hardcode - 242:41 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍦ㄧ敤"銆傚缓璁娇鐢? {tc('text_fgu8')} i18n/no-chinese-hardcode - 243:41 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "缁翠慨"銆傚缓璁娇鐢? {tc('text_m16i')} i18n/no-chinese-hardcode - 244:41 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "棰勮"銆傚缓璁娇鐢? {tc('text_qpgi')} i18n/no-chinese-hardcode - 245:41 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鎶ュ簾"銆傚缓璁娇鐢? {tc('text_haqi')} i18n/no-chinese-hardcode - 286:32 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "宸ヨ缂栧彿"銆傚缓璁娇鐢? {tc('text_cezh29')} i18n/no-chinese-hardcode - 287:32 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "宸ヨ鍚嶇О"銆傚缓璁娇鐢? {tc('text_cesd1f')} i18n/no-chinese-hardcode - 288:32 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "绫诲瀷"銆傚缓璁娇鐢? {tc('text_lnjk')} i18n/no-chinese-hardcode - 289:32 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "瑙勬牸"銆傚缓璁娇鐢? {tc('text_o06w')} i18n/no-chinese-hardcode - 290:32 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "棰濆畾瀵垮懡"銆傚缓璁娇鐢? {tc('text_jmtn0b')} i18n/no-chinese-hardcode - 291:32 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "宸茬敤/鍓╀綑"銆傚缓璁娇鐢? {tc('text_qyhqa1')} i18n/no-chinese-hardcode - 292:32 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍗曟鎴愭湰"銆傚缓璁娇鐢? {tc('text_ayjs08')} i18n/no-chinese-hardcode - 293:32 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鐘舵€?銆傚缓璁娇鐢? {tc('text_k1e3')} i18n/no-chinese-hardcode - 294:32 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "瀵垮懡鐘舵€?銆傚缓璁娇鐢? {tc('text_bynfmx')} i18n/no-chinese-hardcode - 309:53 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "娆?銆傚缓璁娇鐢? {tc('text_l5t')} i18n/no-chinese-hardcode - 358:75 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍚敤"銆傚缓璁娇鐢? {tc('text_eymx')} i18n/no-chinese-hardcode - 364:84 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鎶ュ簾"銆傚缓璁娇鐢? {tc('text_haqi')} i18n/no-chinese-hardcode - 385:32 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "濉啓宸ヨ鍩虹淇℃伅"銆傚缓璁娇鐢? {tc('text_ea7yv6')} i18n/no-chinese-hardcode - 389:22 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "宸ヨ绫诲瀷"銆傚缓璁娇鐢? {tc('text_cez1tc')} i18n/no-chinese-hardcode - 400:41 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍒€妯?銆傚缓璁娇鐢? {tc('text_ej35')} i18n/no-chinese-hardcode - 401:41 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "缃戠増"銆傚缓璁娇鐢? {tc('text_ma6v')} i18n/no-chinese-hardcode - 406:22 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "宸ヨ缂栧彿"銆傚缓璁娇鐢? {tc('text_cezh29')} i18n/no-chinese-hardcode - 415:22 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "宸ヨ鍚嶇О"銆傚缓璁娇鐢? {tc('text_cesd1f')} i18n/no-chinese-hardcode - 424:22 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "瑙勬牸"銆傚缓璁娇鐢? {tc('text_o06w')} i18n/no-chinese-hardcode - 431:22 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "棰濆畾瀵垮懡锛堟锛?銆傚缓璁娇鐢? {tc('text_mjgakr')} i18n/no-chinese-hardcode - 439:22 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍘熷鎴愭湰"銆傚缓璁娇鐢? {tc('text_axbm7c')} i18n/no-chinese-hardcode - 450:26 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鐩暟"銆傚缓璁娇鐢? {tc('text_ksaq')} i18n/no-chinese-hardcode - 457:26 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "缃戞潗"銆傚缓璁娇鐢? {tc('text_m80v')} i18n/no-chinese-hardcode - 464:26 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "灏哄"銆傚缓璁娇鐢? {tc('text_g6wu')} i18n/no-chinese-hardcode - 471:26 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "寮犲姏鍊?銆傚缓璁娇鐢? {tc('text_ec2zl')} i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\[locale]\dcprint\tool\page.tsx - 128:10 warning 'total' is assigned a value but never used. Allowed unused elements of array destructuring must match /^_/u @typescript-eslint/no-unused-vars - 129:16 warning 'setPage' is assigned a value but never used. Allowed unused elements of array destructuring must match /^_/u @typescript-eslint/no-unused-vars - 131:10 warning 'loading' is assigned a value but never used. Allowed unused elements of array destructuring must match /^_/u @typescript-eslint/no-unused-vars - 223:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\dcprint\tool\page.tsx:223:5 - 221 | - 222 | useEffect(() => { -> 223 | fetchTools(); - | ^^^^^^^^^^ Avoid calling setState() directly within an effect - 224 | }, [fetchTools]); - 225 | useEffect(() => { - 226 | fetchDashboard(); react-hooks/set-state-in-effect - 226:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\dcprint\tool\page.tsx:226:5 - 224 | }, [fetchTools]); - 225 | useEffect(() => { -> 226 | fetchDashboard(); - | ^^^^^^^^^^^^^^ Avoid calling setState() directly within an effect - 227 | }, [fetchDashboard]); - 228 | - 229 | const openCreate = () => { react-hooks/set-state-in-effect - 334:22 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鎿嶄綔澶辫触"銆傚缓璁娇鐢? tc('text_d1rj43') i18n/no-chinese-hardcode - 358:22 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "浣跨敤璁板綍宸茬櫥璁?銆傚缓璁娇鐢? tc('text_59m4pl') i18n/no-chinese-hardcode - 364:22 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鎿嶄綔澶辫触"銆傚缓璁娇鐢? tc('text_d1rj43') i18n/no-chinese-hardcode - 398:22 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鎿嶄綔澶辫触"銆傚缓璁娇鐢? tc('text_d1rj43') i18n/no-chinese-hardcode - 411:22 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "宸ヨ宸叉姤搴?銆傚缓璁娇鐢? tc('text_tm7ong') i18n/no-chinese-hardcode - 417:22 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鎿嶄綔澶辫触"銆傚缓璁娇鐢? tc('text_d1rj43') i18n/no-chinese-hardcode - 425:22 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "宸ヨ宸叉縺娲?銆傚缓璁娇鐢? tc('text_tm9zsd') i18n/no-chinese-hardcode - 429:22 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鎿嶄綔澶辫触"銆傚缓璁娇鐢? tc('text_d1rj43') i18n/no-chinese-hardcode - 438:22 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍒犻櫎鎴愬姛"銆傚缓璁娇鐢? tc('text_azeh8z') i18n/no-chinese-hardcode - 442:22 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鎿嶄綔澶辫触"銆傚缓璁娇鐢? tc('text_d1rj43') i18n/no-chinese-hardcode - 457:46 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏂板宸ヨ"銆傚缓璁娇鐢? {tc('text_d762ge')} i18n/no-chinese-hardcode - 472:60 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍦ㄧ敤"銆傚缓璁娇鐢? {tc('text_fgu8')} i18n/no-chinese-hardcode - 478:60 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "棰勮"銆傚缓璁娇鐢? {tc('text_qpgi')} i18n/no-chinese-hardcode - 484:60 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "缁翠慨涓?銆傚缓璁娇鐢? {tc('text_izg1f')} i18n/no-chinese-hardcode - 490:60 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "宸叉姤搴?銆傚缓璁娇鐢? {tc('text_e8o3w')} i18n/no-chinese-hardcode - 506:37 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍏ㄩ儴"銆傚缓璁娇鐢? {tc('text_en40')} i18n/no-chinese-hardcode - 507:38 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍒€妯?銆傚缓璁娇鐢? {tc('text_ej35')} i18n/no-chinese-hardcode - 508:38 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "缃戠増"銆傚缓璁娇鐢? {tc('text_ma6v')} i18n/no-chinese-hardcode - 516:36 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍏ㄩ儴鐘舵€?銆傚缓璁娇鐢? {tc('text_avez63')} i18n/no-chinese-hardcode - 518:37 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍦ㄧ敤"銆傚缓璁娇鐢? {tc('text_fgu8')} i18n/no-chinese-hardcode - 519:37 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "缁翠慨涓?銆傚缓璁娇鐢? {tc('text_izg1f')} i18n/no-chinese-hardcode - 520:37 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "棰勮"銆傚缓璁娇鐢? {tc('text_qpgi')} i18n/no-chinese-hardcode - 521:37 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "宸叉姤搴?銆傚缓璁娇鐢? {tc('text_e8o3w')} i18n/no-chinese-hardcode - 542:30 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "绫诲瀷"銆傚缓璁娇鐢? {tc('text_lnjk')} i18n/no-chinese-hardcode - 543:30 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "缂栫爜"銆傚缓璁娇鐢? {tc('text_m9wr')} i18n/no-chinese-hardcode - 544:30 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍚嶇О"銆傚缓璁娇鐢? {tc('text_eyrn')} i18n/no-chinese-hardcode - 546:30 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍑€鍊?銆傚缓璁娇鐢? {tc('text_ecfw')} i18n/no-chinese-hardcode - 547:30 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鐘舵€?銆傚缓璁娇鐢? {tc('text_k1e3')} i18n/no-chinese-hardcode - 548:30 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鎿嶄綔"銆傚缓璁娇鐢? {tc('text_hkxb')} i18n/no-chinese-hardcode - 554:95 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏆傛棤鏁版嵁"銆傚缓璁娇鐢? {tc('text_dcv57g')} i18n/no-chinese-hardcode - 676:49 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍒€妯?銆傚缓璁娇鐢? {tc('text_ej35')} i18n/no-chinese-hardcode - 677:49 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "缃戠増"銆傚缓璁娇鐢? {tc('text_ma6v')} i18n/no-chinese-hardcode - 699:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "瑙勬牸"銆傚缓璁娇鐢? {tc('text_o06w')} i18n/no-chinese-hardcode - 737:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "瀛樻斁浣嶇疆"銆傚缓璁娇鐢? {tc('text_bxzajr')} i18n/no-chinese-hardcode - 842:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "澶囨敞"銆傚缓璁娇鐢? {tc('text_fqo1')} i18n/no-chinese-hardcode - 850:78 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍙栨秷"銆傚缓璁娇鐢? {tc('text_ev02')} i18n/no-chinese-hardcode - 853:44 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "纭畾"銆傚缓璁娇鐢? {tc('text_kzjg')} i18n/no-chinese-hardcode - 921:48 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "浣跨敤璁板綍"銆傚缓璁娇鐢? {tc('text_aisnou')} i18n/no-chinese-hardcode - 922:54 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "缁翠慨璁板綍"銆傚缓璁娇鐢? {tc('text_gctspr')} i18n/no-chinese-hardcode - 928:38 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏃堕棿"銆傚缓璁娇鐢? {tc('text_i5z2')} i18n/no-chinese-hardcode - 929:38 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "宸ュ崟鍙?銆傚缓璁娇鐢? {tc('text_e5qlj')} i18n/no-chinese-hardcode - 930:38 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "宸ュ簭"銆傚缓璁娇鐢? {tc('text_ghmy')} i18n/no-chinese-hardcode - 933:38 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鎿嶄綔浜?銆傚缓璁娇鐢? {tc('text_f5g8b')} i18n/no-chinese-hardcode - 942:30 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏆傛棤璁板綍"銆傚缓璁娇鐢? {tc('text_dd1mmb')} i18n/no-chinese-hardcode - 965:38 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "绫诲瀷"銆傚缓璁娇鐢? {tc('text_lnjk')} i18n/no-chinese-hardcode - 966:38 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "璐圭敤"銆傚缓璁娇鐢? {tc('text_onwv')} i18n/no-chinese-hardcode - 968:38 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鐘舵€?銆傚缓璁娇鐢? {tc('text_k1e3')} i18n/no-chinese-hardcode - 969:38 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏃堕棿"銆傚缓璁娇鐢? {tc('text_i5z2')} i18n/no-chinese-hardcode - 970:38 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鎻忚堪"銆傚缓璁娇鐢? {tc('text_hrlt')} i18n/no-chinese-hardcode - 979:30 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏆傛棤璁板綍"銆傚缓璁娇鐢? {tc('text_dd1mmb')} i18n/no-chinese-hardcode - 995:60 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "杩涜涓?銆傚缓璁娇鐢? {tc('text_lq5q4')} i18n/no-chinese-hardcode - 997:42 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "宸插畬鎴?銆傚缓璁娇鐢? {tc('text_e7hbq')} i18n/no-chinese-hardcode - 1045:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "宸ュ簭鍚嶇О"銆傚缓璁娇鐢? {tc('text_c8ls99')} i18n/no-chinese-hardcode - 1052:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "澶囨敞"銆傚缓璁娇鐢? {tc('text_fqo1')} i18n/no-chinese-hardcode - 1060:82 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍙栨秷"銆傚缓璁娇鐢? {tc('text_ev02')} i18n/no-chinese-hardcode - 1063:45 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "纭畾"銆傚缓璁娇鐢? {tc('text_kzjg')} i18n/no-chinese-hardcode - 1095:28 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "缁翠慨绫诲瀷"銆傚缓璁娇鐢? {tc('text_gcr622')} i18n/no-chinese-hardcode - 1105:49 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "缁翠慨"銆傚缓璁娇鐢? {tc('text_m16i')} i18n/no-chinese-hardcode - 1106:49 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "淇濆吇"銆傚缓璁娇鐢? {tc('text_e14u')} i18n/no-chinese-hardcode - 1132:28 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "缁翠慨璐圭敤"銆傚缓璁娇鐢? {tc('text_gcu6fd')} i18n/no-chinese-hardcode - 1163:82 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍙栨秷"銆傚缓璁娇鐢? {tc('text_ev02')} i18n/no-chinese-hardcode - 1166:51 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "纭畾"銆傚缓璁娇鐢? {tc('text_kzjg')} i18n/no-chinese-hardcode - 1186:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鎶ュ簾鍘熷洜"銆傚缓璁娇鐢? {tc('text_cu6am3')} i18n/no-chinese-hardcode - 1194:82 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍙栨秷"銆傚缓璁娇鐢? {tc('text_ev02')} i18n/no-chinese-hardcode - 1197:67 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "纭鎶ュ簾"銆傚缓璁娇鐢? {tc('text_frrbww')} i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\[locale]\dcprint\trace\page.tsx - 79:5 warning Error: Cannot access variable before it is declared - -`fetchRecords` is accessed before it is declared, which prevents the earlier access from updating when this value changes over time. - -D:\dcprint\erp-project\src\app\[locale]\dcprint\trace\page.tsx:79:5 - 77 | - 78 | useEffect(() => { -> 79 | fetchRecords(); - | ^^^^^^^^^^^^ `fetchRecords` accessed before it is declared - 80 | qrInputRef.current?.focus(); - 81 | }, []); - 82 | - -D:\dcprint\erp-project\src\app\[locale]\dcprint\trace\page.tsx:83:3 - 81 | }, []); - 82 | -> 83 | const fetchRecords = async () => { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 84 | try { - | ^^^^^^^^^ -> 85 | const response = await authFetch('/api/dcprint/trace'); - | ^^^^^^^^^ -> 86 | const result = await response.json(); - | ^^^^^^^^^ -> 87 | if (result.success) { - | ^^^^^^^^^ -> 88 | setRecords(result.data.list || []); - | ^^^^^^^^^ -> 89 | } - | ^^^^^^^^^ -> 90 | } catch {} - | ^^^^^^^^^ -> 91 | }; - | ^^^^^ `fetchRecords` is declared here - 92 | - 93 | const handleScanQRCode = async () => { - 94 | if (!qrCode.trim()) return; react-hooks/immutability - 151:11 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鎵撳嵃鍔熻兘寰呭疄鐜?銆傚缓璁娇鐢? tc('text_vx4p8c') i18n/no-chinese-hardcode - 161:45 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鎵爜杩芥函"銆傚缓璁娇鐢? {tc('text_cx5i2w')} i18n/no-chinese-hardcode - 183:59 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "閲嶇疆"銆傚缓璁娇鐢? {tc('text_phz5')} i18n/no-chinese-hardcode - 187:56 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "杩芥函"銆傚缓璁娇鐢? {tc('text_p3ki')} i18n/no-chinese-hardcode - 218:32 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "杩芥函缁撴灉"銆傚缓璁娇鐢? {tc('text_immfaz')} i18n/no-chinese-hardcode - 225:57 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鎵撳嵃杩芥函鍗?銆傚缓璁娇鐢? {tc('text_um5lcq')} i18n/no-chinese-hardcode - 235:54 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "娴佺▼鍗′俊鎭?銆傚缓璁娇鐢? {tc('text_gqkiw5')} i18n/no-chinese-hardcode - 240:67 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "娴佺▼鍗″崱鍙?銆傚缓璁娇鐢? {tc('text_gql1v1')} i18n/no-chinese-hardcode - 244:67 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "宸ュ崟鍙?銆傚缓璁娇鐢? {tc('text_e5qlj')} i18n/no-chinese-hardcode - 248:67 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鎴愬搧鏂欏彿"銆傚缓璁娇鐢? {tc('text_cq660f')} i18n/no-chinese-hardcode - 252:67 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鎴愬搧鍚嶇О"銆傚缓璁娇鐢? {tc('text_cq3e2c')} i18n/no-chinese-hardcode - 262:54 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "涓绘潗淇℃伅"銆傚缓璁娇鐢? {tc('text_aaqm03')} i18n/no-chinese-hardcode - 267:67 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏍囩缂栧彿"銆傚缓璁娇鐢? {tc('text_dn1smw')} i18n/no-chinese-hardcode - 271:67 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鐗╂枡浠e彿"銆傚缓璁娇鐢? {tc('text_eurcg4')} i18n/no-chinese-hardcode - 277:67 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鐗╂枡鍚嶇О"銆傚缓璁娇鐢? {tc('text_eusfkj')} i18n/no-chinese-hardcode - 289:67 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鎵瑰彿"銆傚缓璁娇鐢? {tc('text_h7ku')} i18n/no-chinese-hardcode - 299:67 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "杩涙枡鏃ユ湡"銆傚缓璁娇鐢? {tc('text_ikkk8o')} i18n/no-chinese-hardcode - 331:74 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鐗╂枡鎬绘暟"銆傚缓璁娇鐢? {tc('text_euue3p')} i18n/no-chinese-hardcode - 344:28 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鐗╂枡鏄庣粏"銆傚缓璁娇鐢? {tc('text_euvirs')} i18n/no-chinese-hardcode - 352:36 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏍囩缂栧彿"銆傚缓璁娇鐢? {tc('text_dn1smw')} i18n/no-chinese-hardcode - 354:36 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鐗╂枡浠e彿"銆傚缓璁娇鐢? {tc('text_eurcg4')} i18n/no-chinese-hardcode - 355:36 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鐗╂枡鍚嶇О"銆傚缓璁娇鐢? {tc('text_eusfkj')} i18n/no-chinese-hardcode - 357:36 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鎵瑰彿"銆傚缓璁娇鐢? {tc('text_h7ku')} i18n/no-chinese-hardcode - 359:36 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "杩涙枡鏃ユ湡"銆傚缓璁娇鐢? {tc('text_ikkk8o')} i18n/no-chinese-hardcode - 365:79 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏆傛棤鐗╂枡鏁版嵁"銆傚缓璁娇鐢? {tc('text_iia61g')} i18n/no-chinese-hardcode - 375:80 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "涓绘潗"銆傚缓璁娇鐢? {tc('text_dvg5')} i18n/no-chinese-hardcode - 377:78 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "杈呮枡"銆傚缓璁娇鐢? {tc('text_oywk')} i18n/no-chinese-hardcode - 400:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "杩芥函璁板綍"銆傚缓璁娇鐢? {tc('text_imokfr')} i18n/no-chinese-hardcode - 409:32 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "娴佺▼鍗″崱鍙?銆傚缓璁娇鐢? {tc('text_gql1v1')} i18n/no-chinese-hardcode - 410:32 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "宸ュ崟鍙?銆傚缓璁娇鐢? {tc('text_e5qlj')} i18n/no-chinese-hardcode - 411:32 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鎴愬搧鏂欏彿"銆傚缓璁娇鐢? {tc('text_cq660f')} i18n/no-chinese-hardcode - 413:32 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鎿嶄綔鍛?銆傚缓璁娇鐢? {tc('text_f5hc9')} i18n/no-chinese-hardcode - 414:32 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "杩芥函鏃堕棿"銆傚缓璁娇鐢? {tc('text_imig7k')} i18n/no-chinese-hardcode - 420:75 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏆傛棤鏁版嵁"銆傚缓璁娇鐢? {tc('text_dcv57g')} i18n/no-chinese-hardcode - 433:74 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "姝e悜杩芥函"銆傚缓璁娇鐢? {tc('text_dwm21s')} i18n/no-chinese-hardcode - 435:78 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍙嶅悜杩芥函"銆傚缓璁娇鐢? {tc('text_axin92')} i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\[locale]\delivery\vehicles\page.tsx - 70:5 warning Error: Cannot access variable before it is declared - -`fetchVehicles` is accessed before it is declared, which prevents the earlier access from updating when this value changes over time. - -D:\dcprint\erp-project\src\app\[locale]\delivery\vehicles\page.tsx:70:5 - 68 | - 69 | useEffect(() => { -> 70 | fetchVehicles(); - | ^^^^^^^^^^^^^ `fetchVehicles` accessed before it is declared - 71 | }, [page, status, keyword]); - 72 | - 73 | const fetchVehicles = async () => { - -D:\dcprint\erp-project\src\app\[locale]\delivery\vehicles\page.tsx:73:3 - 71 | }, [page, status, keyword]); - 72 | -> 73 | const fetchVehicles = async () => { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 74 | try { - | ^^^^^^^^^ -> 75 | setLoading(true); - 鈥? | ^^^^^^^^^ -> 95 | } - | ^^^^^^^^^ -> 96 | }; - | ^^^^^ `fetchVehicles` is declared here - 97 | - 98 | const handleDelete = async (id: number) => { - 99 | if (!confirm(t('confirmDeleteVehicle'))) return; react-hooks/immutability - 71:6 warning React Hook useEffect has a missing dependency: 'fetchVehicles'. Either include it or remove the dependency array react-hooks/exhaustive-deps - -D:\dcprint\erp-project\src\app\[locale]\engineering\sample-to-mass\page.tsx - 125:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\engineering\sample-to-mass\page.tsx:125:5 - 123 | - 124 | useEffect(() => { -> 125 | fetchData(); - | ^^^^^^^^^ Avoid calling setState() directly within an effect - 126 | fetchCustomers(); - 127 | }, [page]); - 128 | react-hooks/set-state-in-effect - 127:6 warning React Hook useEffect has a missing dependency: 'fetchData'. Either include it or remove the dependency array react-hooks/exhaustive-deps - -D:\dcprint\erp-project\src\app\[locale]\engineering\sop\page.tsx - 148:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\engineering\sop\page.tsx:148:5 - 146 | - 147 | useEffect(() => { -> 148 | fetchData(); - | ^^^^^^^^^ Avoid calling setState() directly within an effect - 149 | }, [page]); - 150 | - 151 | const handleSave = async () => { react-hooks/set-state-in-effect - 149:6 warning React Hook useEffect has a missing dependency: 'fetchData'. Either include it or remove the dependency array react-hooks/exhaustive-deps - -D:\dcprint\erp-project\src\app\[locale]\equipment\calibration\page.tsx - 46:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "寰呮瀹?銆傚缓璁娇鐢? tc('text_ehzq7') i18n/no-chinese-hardcode - 47:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "妫€瀹氫腑"銆傚缓璁娇鐢? tc('text_fscr7') i18n/no-chinese-hardcode - 48:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "宸插悎鏍?銆傚缓璁娇鐢? tc('text_e68iu') i18n/no-chinese-hardcode - 49:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "涓嶅悎鏍?銆傚缓璁娇鐢? tc('text_bufb5') i18n/no-chinese-hardcode - 80:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\equipment\calibration\page.tsx:80:5 - 78 | }; - 79 | useEffect(() => { -> 80 | fetchData(); - | ^^^^^^^^^ Avoid calling setState() directly within an effect - 81 | }, [page]); - 82 | - 83 | const handleSave = async () => { react-hooks/set-state-in-effect - 81:6 warning React Hook useEffect has a missing dependency: 'fetchData'. Either include it or remove the dependency array react-hooks/exhaustive-deps - 140:46 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "璁惧妫€瀹?銆傚缓璁娇鐢? {tc('text_i02c2r')} i18n/no-chinese-hardcode - 160:48 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏂板妫€瀹?銆傚缓璁娇鐢? {tc('text_d77o08')} i18n/no-chinese-hardcode - 170:50 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "妫€瀹氬崟鍙?銆傚缓璁娇鐢? {tc('text_dlgbks')} i18n/no-chinese-hardcode - 171:50 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "璁惧缂栫爜"銆傚缓璁娇鐢? {tc('text_i06agk')} i18n/no-chinese-hardcode - 172:50 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "璁惧鍚嶇О"銆傚缓璁娇鐢? {tc('text_hzyzbg')} i18n/no-chinese-hardcode - 173:50 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "妫€瀹氭棩鏈?銆傚缓璁娇鐢? {tc('text_dljl10')} i18n/no-chinese-hardcode - 175:50 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "妫€瀹氭満鏋?銆傚缓璁娇鐢? {tc('text_dljt9g')} i18n/no-chinese-hardcode - 206:30 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "寮€濮嬫瀹?銆傚缓璁娇鐢? {tc('text_cd0pnp')} i18n/no-chinese-hardcode - 216:30 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍚堟牸"銆傚缓璁娇鐢? {tc('text_ev5g')} i18n/no-chinese-hardcode - 246:87 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏆傛棤璁板綍"銆傚缓璁娇鐢? {tc('text_dd1mmb')} i18n/no-chinese-hardcode - 256:51 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍏?銆傚缓璁娇鐢? {tc('text_g35')} i18n/no-chinese-hardcode - 256:59 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏉?銆傚缓璁娇鐢? {tc('text_kf5')} i18n/no-chinese-hardcode - 263:14 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "涓婁竴椤?銆傚缓璁娇鐢? {tc('text_btlof')} i18n/no-chinese-hardcode - 271:14 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "涓嬩竴椤?銆傚缓璁娇鐢? {tc('text_btmf4')} i18n/no-chinese-hardcode - 283:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "璁惧缂栫爜"銆傚缓璁娇鐢? {tc('text_i06agk')} i18n/no-chinese-hardcode - 290:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "璁惧鍚嶇О"銆傚缓璁娇鐢? {tc('text_hzyzbg')} i18n/no-chinese-hardcode - 297:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "妫€瀹氭棩鏈?銆傚缓璁娇鐢? {tc('text_dljl10')} i18n/no-chinese-hardcode - 305:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "涓嬫妫€瀹氭棩鏈?銆傚缓璁娇鐢? {tc('text_ipgqzu')} i18n/no-chinese-hardcode - 315:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "妫€瀹氭満鏋?銆傚缓璁娇鐢? {tc('text_dljt9g')} i18n/no-chinese-hardcode - 330:78 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍙栨秷"銆傚缓璁娇鐢? {tc('text_ev02')} i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\[locale]\equipment\maintenance\page.tsx - 167:6 warning React Hook useCallback has missing dependencies: 'tc' and 'toast'. Either include them or remove the dependency array react-hooks/exhaustive-deps - 189:6 warning React Hook useCallback has missing dependencies: 'tc' and 'toast'. Either include them or remove the dependency array react-hooks/exhaustive-deps - 192:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\equipment\maintenance\page.tsx:192:5 - 190 | - 191 | useEffect(() => { -> 192 | fetchEquipment(); - | ^^^^^^^^^^^^^^ Avoid calling setState() directly within an effect - 193 | }, [fetchEquipment]); - 194 | useEffect(() => { - 195 | if (activeTab === 'plan') fetchPlans(); react-hooks/set-state-in-effect - 195:31 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\equipment\maintenance\page.tsx:195:31 - 193 | }, [fetchEquipment]); - 194 | useEffect(() => { -> 195 | if (activeTab === 'plan') fetchPlans(); - | ^^^^^^^^^^ Avoid calling setState() directly within an effect - 196 | else fetchRecords(); - 197 | }, [activeTab, fetchPlans, fetchRecords]); - 198 | react-hooks/set-state-in-effect - 286:54 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "淇濆吇璁″垝"銆傚缓璁娇鐢? {tc('text_af8h8v')} i18n/no-chinese-hardcode - 290:47 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "淇濆吇璁板綍"銆傚缓璁娇鐢? {tc('text_af8k83')} i18n/no-chinese-hardcode - 312:52 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏂板淇濆吇璁″垝"銆傚缓璁娇鐢? {tc('text_w5b743')} i18n/no-chinese-hardcode - 317:52 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏂板淇濆吇璁板綍"銆傚缓璁娇鐢? {tc('text_w5b44v')} i18n/no-chinese-hardcode - 327:28 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "淇濆吇璁″垝"銆傚缓璁娇鐢? {tc('text_af8h8v')} i18n/no-chinese-hardcode - 339:36 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "璁″垝缂栧彿"銆傚缓璁娇鐢? {tc('text_hymw36')} i18n/no-chinese-hardcode - 340:36 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "璁惧缂栫爜"銆傚缓璁娇鐢? {tc('text_i06agk')} i18n/no-chinese-hardcode - 341:36 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "璁惧鍚嶇О"銆傚缓璁娇鐢? {tc('text_hzyzbg')} i18n/no-chinese-hardcode - 379:36 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "寮€濮嬫墽琛?銆傚缓璁娇鐢? {tc('text_cczvm8')} i18n/no-chinese-hardcode - 389:36 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "濉啓璁板綍"銆傚缓璁娇鐢? {tc('text_bi3jqr')} i18n/no-chinese-hardcode - 420:93 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏆傛棤淇濆吇璁″垝"銆傚缓璁娇鐢? {tc('text_mxwydv')} i18n/no-chinese-hardcode - 429:59 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍏?銆傚缓璁娇鐢? {tc('text_g35')} i18n/no-chinese-hardcode - 429:71 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏉?銆傚缓璁娇鐢? {tc('text_kf5')} i18n/no-chinese-hardcode - 436:22 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "涓婁竴椤?銆傚缓璁娇鐢? {tc('text_btlof')} i18n/no-chinese-hardcode - 444:22 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "涓嬩竴椤?銆傚缓璁娇鐢? {tc('text_btmf4')} i18n/no-chinese-hardcode - 456:28 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "淇濆吇璁板綍"銆傚缓璁娇鐢? {tc('text_af8k83')} i18n/no-chinese-hardcode - 468:36 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "璁板綍缂栧彿"銆傚缓璁娇鐢? {tc('text_i0uebq')} i18n/no-chinese-hardcode - 469:36 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "璁惧缂栫爜"銆傚缓璁娇鐢? {tc('text_i06agk')} i18n/no-chinese-hardcode - 470:36 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "璁惧鍚嶇О"銆傚缓璁娇鐢? {tc('text_hzyzbg')} i18n/no-chinese-hardcode - 472:36 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "寮€濮嬫椂闂?銆傚缓璁娇鐢? {tc('text_cd0k3t')} i18n/no-chinese-hardcode - 473:36 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "缁撴潫鏃堕棿"銆傚缓璁娇鐢? {tc('text_gfi7qi')} i18n/no-chinese-hardcode - 474:36 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍋滄満鏃堕暱"銆傚缓璁娇鐢? {tc('text_aki78n')} i18n/no-chinese-hardcode - 475:36 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "璐圭敤"銆傚缓璁娇鐢? {tc('text_onwv')} i18n/no-chinese-hardcode - 529:94 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏆傛棤淇濆吇璁板綍"銆傚缓璁娇鐢? {tc('text_mxwven')} i18n/no-chinese-hardcode - 538:59 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍏?銆傚缓璁娇鐢? {tc('text_g35')} i18n/no-chinese-hardcode - 538:73 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏉?銆傚缓璁娇鐢? {tc('text_kf5')} i18n/no-chinese-hardcode - 545:22 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "涓婁竴椤?銆傚缓璁娇鐢? {tc('text_btlof')} i18n/no-chinese-hardcode - 553:22 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "涓嬩竴椤?銆傚缓璁娇鐢? {tc('text_btmf4')} i18n/no-chinese-hardcode - 585:28 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "璁惧"銆傚缓璁娇鐢? {tc('text_o9ah')} i18n/no-chinese-hardcode - 663:28 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "璁″垝鏃ユ湡"銆傚缓璁娇鐢? {tc('text_hyipm3')} i18n/no-chinese-hardcode - 693:28 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "璁惧"銆傚缓璁娇鐢? {tc('text_o9ah')} i18n/no-chinese-hardcode - 743:28 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "寮€濮嬫椂闂?銆傚缓璁娇鐢? {tc('text_cd0k3t')} i18n/no-chinese-hardcode - 751:28 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "缁撴潫鏃堕棿"銆傚缓璁娇鐢? {tc('text_gfi7qi')} i18n/no-chinese-hardcode - 800:26 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏁呴殰鎻忚堪"銆傚缓璁娇鐢? {tc('text_dedoag')} i18n/no-chinese-hardcode - 829:76 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍙栨秷"銆傚缓璁娇鐢? {tc('text_ev02')} i18n/no-chinese-hardcode - 832:84 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "淇濆瓨"銆傚缓璁娇鐢? {tc('text_e32z')} i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\[locale]\equipment\page.tsx - 95:19 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鑾峰彇璁惧鍒楄〃澶辫触"銆傚缓璁娇鐢? tc('text_9m2enn') i18n/no-chinese-hardcode - 102:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\equipment\page.tsx:102:5 - 100 | - 101 | useEffect(() => { -> 102 | fetchData(); - | ^^^^^^^^^ Avoid calling setState() directly within an effect - 103 | }, [fetchData]); - 104 | - 105 | const saveEquipment = async () => { react-hooks/set-state-in-effect - 107:19 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "璇峰~鍐欒澶囩紪鐮佸拰鍚嶇О"銆傚缓璁娇鐢? tc('text_dx7gwa') i18n/no-chinese-hardcode - 126:19 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "淇濆瓨璁惧澶辫触"銆傚缓璁娇鐢? tc('text_v2brew') i18n/no-chinese-hardcode - 136:23 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "璁惧鍒犻櫎鎴愬姛"銆傚缓璁娇鐢? tc('text_yes810') i18n/no-chinese-hardcode - 142:19 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍒犻櫎璁惧澶辫触"銆傚缓璁娇鐢? tc('text_n9mxnj') i18n/no-chinese-hardcode - 170:44 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "璁惧鍙拌处"銆傚缓璁娇鐢? {tc('text_hzz2f3')} i18n/no-chinese-hardcode - 183:48 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏂板璁惧"銆傚缓璁娇鐢? {tc('text_d7dlrr')} i18n/no-chinese-hardcode - 203:43 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍏ㄩ儴绫诲瀷"銆傚缓璁娇鐢? {tc('text_avglbk')} i18n/no-chinese-hardcode - 224:32 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "璁惧缂栫爜"銆傚缓璁娇鐢? {tc('text_i06agk')} i18n/no-chinese-hardcode - 225:32 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "璁惧鍚嶇О"銆傚缓璁娇鐢? {tc('text_hzyzbg')} i18n/no-chinese-hardcode - 228:32 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "浣嶇疆"銆傚缓璁娇鐢? {tc('text_e6rl')} i18n/no-chinese-hardcode - 229:32 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "浜ц兘"銆傚缓璁娇鐢? {tc('text_e33q')} i18n/no-chinese-hardcode - 300:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "璁惧缂栫爜"銆傚缓璁娇鐢? {tc('text_i06agk')} i18n/no-chinese-hardcode - 312:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "璁惧鍚嶇О"銆傚缓璁娇鐢? {tc('text_hzyzbg')} i18n/no-chinese-hardcode - 325:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "璁惧绫诲瀷"銆傚缓璁娇鐢? {tc('text_i05o3d')} i18n/no-chinese-hardcode - 353:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍨嬪彿"銆傚缓璁娇鐢? {tc('text_fcng')} i18n/no-chinese-hardcode - 361:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "瀹夎浣嶇疆"銆傚缓璁娇鐢? {tc('text_c41wyl')} i18n/no-chinese-hardcode - 371:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "棰濆畾浜ц兘"銆傚缓璁娇鐢? {tc('text_jmrm37')} i18n/no-chinese-hardcode - 382:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "杩愯鐘舵€?銆傚缓璁娇鐢? {tc('text_ipinbb')} i18n/no-chinese-hardcode - 411:76 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍙栨秷"銆傚缓璁娇鐢? {tc('text_ev02')} i18n/no-chinese-hardcode - 414:87 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "淇濆瓨"銆傚缓璁娇鐢? {tc('text_e32z')} i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\[locale]\equipment\repair\page.tsx - 50:6 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "棰勯槻鎬х淮淇?銆傚缓璁娇鐢? tc('text_nld3bh') i18n/no-chinese-hardcode - 51:6 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏁呴殰缁翠慨"銆傚缓璁娇鐢? tc('text_dehxv5') i18n/no-chinese-hardcode - 52:6 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "绱ф€ョ淮淇?銆傚缓璁娇鐢? tc('text_g6yorc') i18n/no-chinese-hardcode - 93:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\equipment\repair\page.tsx:93:5 - 91 | }; - 92 | useEffect(() => { -> 93 | fetchData(); - | ^^^^^^^^^ Avoid calling setState() directly within an effect - 94 | }, [page]); - 95 | - 96 | const handleSave = async () => { react-hooks/set-state-in-effect - 94:6 warning React Hook useEffect has a missing dependency: 'fetchData'. Either include it or remove the dependency array react-hooks/exhaustive-deps - 153:46 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "璁惧缁翠慨"銆傚缓璁娇鐢? {tc('text_i061qb')} i18n/no-chinese-hardcode - 173:48 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏂板缁翠慨鍗?銆傚缓璁娇鐢? {tc('text_gvv3wz')} i18n/no-chinese-hardcode - 183:50 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "缁翠慨鍗曞彿"銆傚缓璁娇鐢? {tc('text_gck5do')} i18n/no-chinese-hardcode - 184:50 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "璁惧缂栫爜"銆傚缓璁娇鐢? {tc('text_i06agk')} i18n/no-chinese-hardcode - 185:50 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "璁惧鍚嶇О"銆傚缓璁娇鐢? {tc('text_hzyzbg')} i18n/no-chinese-hardcode - 186:50 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏁呴殰鏃ユ湡"銆傚缓璁娇鐢? {tc('text_dedt01')} i18n/no-chinese-hardcode - 187:50 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏁呴殰鎻忚堪"銆傚缓璁娇鐢? {tc('text_dedoag')} i18n/no-chinese-hardcode - 188:50 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "缁翠慨绫诲瀷"銆傚缓璁娇鐢? {tc('text_gcr622')} i18n/no-chinese-hardcode - 189:50 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "缁翠慨浜?銆傚缓璁娇鐢? {tc('text_izg5c')} i18n/no-chinese-hardcode - 221:30 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "寮€濮嬬淮淇?銆傚缓璁娇鐢? {tc('text_cd4fb9')} i18n/no-chinese-hardcode - 231:30 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "瀹屾垚"銆傚缓璁娇鐢? {tc('text_g3yc')} i18n/no-chinese-hardcode - 261:87 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏆傛棤璁板綍"銆傚缓璁娇鐢? {tc('text_dd1mmb')} i18n/no-chinese-hardcode - 271:51 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍏?銆傚缓璁娇鐢? {tc('text_g35')} i18n/no-chinese-hardcode - 271:59 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏉?銆傚缓璁娇鐢? {tc('text_kf5')} i18n/no-chinese-hardcode - 278:14 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "涓婁竴椤?銆傚缓璁娇鐢? {tc('text_btlof')} i18n/no-chinese-hardcode - 286:14 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "涓嬩竴椤?銆傚缓璁娇鐢? {tc('text_btmf4')} i18n/no-chinese-hardcode - 294:28 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏂板缁翠慨鍗?銆傚缓璁娇鐢? {tc('text_gvv3wz')} i18n/no-chinese-hardcode - 298:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "璁惧缂栫爜"銆傚缓璁娇鐢? {tc('text_i06agk')} i18n/no-chinese-hardcode - 305:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "璁惧鍚嶇О"銆傚缓璁娇鐢? {tc('text_hzyzbg')} i18n/no-chinese-hardcode - 312:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏁呴殰鏃ユ湡"銆傚缓璁娇鐢? {tc('text_dedt01')} i18n/no-chinese-hardcode - 320:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "缁翠慨绫诲瀷"銆傚缓璁娇鐢? {tc('text_gcr622')} i18n/no-chinese-hardcode - 329:43 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "棰勯槻鎬х淮淇?銆傚缓璁娇鐢? {tc('text_nld3bh')} i18n/no-chinese-hardcode - 330:43 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏁呴殰缁翠慨"銆傚缓璁娇鐢? {tc('text_dehxv5')} i18n/no-chinese-hardcode - 331:43 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "绱ф€ョ淮淇?銆傚缓璁娇鐢? {tc('text_g6yorc')} i18n/no-chinese-hardcode - 336:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "缁翠慨浜?銆傚缓璁娇鐢? {tc('text_izg5c')} i18n/no-chinese-hardcode - 343:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏁呴殰鎻忚堪"銆傚缓璁娇鐢? {tc('text_dedoag')} i18n/no-chinese-hardcode - 351:78 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍙栨秷"銆傚缓璁娇鐢? {tc('text_ev02')} i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\[locale]\equipment\scrap\page.tsx - 75:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\equipment\scrap\page.tsx:75:5 - 73 | }; - 74 | useEffect(() => { -> 75 | fetchData(); - | ^^^^^^^^^ Avoid calling setState() directly within an effect - 76 | }, [page]); - 77 | - 78 | const handleSave = async () => { react-hooks/set-state-in-effect - 76:6 warning React Hook useEffect has a missing dependency: 'fetchData'. Either include it or remove the dependency array react-hooks/exhaustive-deps - 135:46 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "璁惧鎶ュ簾"銆傚缓璁娇鐢? {tc('text_i01bab')} i18n/no-chinese-hardcode - 155:48 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏂板鎶ュ簾"銆傚缓璁娇鐢? {tc('text_d76n7s')} i18n/no-chinese-hardcode - 165:50 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鎶ュ簾鍗曞彿"銆傚缓璁娇鐢? {tc('text_cu689o')} i18n/no-chinese-hardcode - 166:50 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "璁惧缂栫爜"銆傚缓璁娇鐢? {tc('text_i06agk')} i18n/no-chinese-hardcode - 167:50 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "璁惧鍚嶇О"銆傚缓璁娇鐢? {tc('text_hzyzbg')} i18n/no-chinese-hardcode - 168:50 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鎶ュ簾鏃ユ湡"銆傚缓璁娇鐢? {tc('text_cu9hpw')} i18n/no-chinese-hardcode - 169:50 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鎶ュ簾鍘熷洜"銆傚缓璁娇鐢? {tc('text_cu6am3')} i18n/no-chinese-hardcode - 170:50 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍘熷€?銆傚缓璁娇鐢? {tc('text_enwd')} i18n/no-chinese-hardcode - 171:50 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍑€鍊?銆傚缓璁娇鐢? {tc('text_ecfw')} i18n/no-chinese-hardcode - 209:30 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "瀹℃壒"銆傚缓璁娇鐢? {tc('text_g4jc')} i18n/no-chinese-hardcode - 219:30 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "纭鎶ュ簾"銆傚缓璁娇鐢? {tc('text_frrbww')} i18n/no-chinese-hardcode - 249:88 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏆傛棤璁板綍"銆傚缓璁娇鐢? {tc('text_dd1mmb')} i18n/no-chinese-hardcode - 259:51 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍏?銆傚缓璁娇鐢? {tc('text_g35')} i18n/no-chinese-hardcode - 259:59 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏉?銆傚缓璁娇鐢? {tc('text_kf5')} i18n/no-chinese-hardcode - 266:14 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "涓婁竴椤?銆傚缓璁娇鐢? {tc('text_btlof')} i18n/no-chinese-hardcode - 274:14 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "涓嬩竴椤?銆傚缓璁娇鐢? {tc('text_btmf4')} i18n/no-chinese-hardcode - 286:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "璁惧缂栫爜"銆傚缓璁娇鐢? {tc('text_i06agk')} i18n/no-chinese-hardcode - 293:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "璁惧鍚嶇О"銆傚缓璁娇鐢? {tc('text_hzyzbg')} i18n/no-chinese-hardcode - 300:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鎶ュ簾鏃ユ湡"銆傚缓璁娇鐢? {tc('text_cu9hpw')} i18n/no-chinese-hardcode - 315:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍘熷€?銆傚缓璁娇鐢? {tc('text_enwd')} i18n/no-chinese-hardcode - 325:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍑€鍊?銆傚缓璁娇鐢? {tc('text_ecfw')} i18n/no-chinese-hardcode - 333:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鎶ュ簾鍘熷洜"銆傚缓璁娇鐢? {tc('text_cu6am3')} i18n/no-chinese-hardcode - 341:78 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍙栨秷"銆傚缓璁娇鐢? {tc('text_ev02')} i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\[locale]\finance\cost\page.tsx - 92:6 warning React Hook useCallback has a missing dependency: 'summary'. Either include it or remove the dependency array. You can also do a functional update 'setSummary(s => ...)' if you only need 'summary' in the 'setSummary' call react-hooks/exhaustive-deps - 95:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\finance\cost\page.tsx:95:5 - 93 | - 94 | useEffect(() => { -> 95 | fetchData(); - | ^^^^^^^^^ Avoid calling setState() directly within an effect - 96 | fetchSummary(); - 97 | }, [fetchData, fetchSummary]); - 98 | react-hooks/set-state-in-effect - -D:\dcprint\erp-project\src\app\[locale]\finance\costs\page.tsx - 50:16 warning 'setPage' is assigned a value but never used. Allowed unused elements of array destructuring must match /^_/u @typescript-eslint/no-unused-vars - 51:10 warning 'total' is assigned a value but never used. Allowed unused elements of array destructuring must match /^_/u @typescript-eslint/no-unused-vars - 74:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\finance\costs\page.tsx:74:5 - 72 | - 73 | useEffect(() => { -> 74 | loadCosts(); - | ^^^^^^^^^ Avoid calling setState() directly within an effect - 75 | }, [page]); - 76 | - 77 | const handleCalculate = async () => { react-hooks/set-state-in-effect - 75:6 warning React Hook useEffect has a missing dependency: 'loadCosts'. Either include it or remove the dependency array react-hooks/exhaustive-deps - -D:\dcprint\erp-project\src\app\[locale]\finance\page.tsx - 44:3 warning 'AlertTriangle' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 135:10 warning 'loading' is assigned a value but never used. Allowed unused elements of array destructuring must match /^_/u @typescript-eslint/no-unused-vars - 204:6 warning React Hook useCallback has a missing dependency: 'tc'. Either include it or remove the dependency array react-hooks/exhaustive-deps - 226:6 warning React Hook useCallback has a missing dependency: 'tc'. Either include it or remove the dependency array react-hooks/exhaustive-deps - 241:6 warning React Hook useCallback has a missing dependency: 'tc'. Either include it or remove the dependency array react-hooks/exhaustive-deps - 256:6 warning React Hook useCallback has a missing dependency: 'tc'. Either include it or remove the dependency array react-hooks/exhaustive-deps - 279:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\finance\page.tsx:279:5 - 277 | - 278 | useEffect(() => { -> 279 | fetchCustomers(); - | ^^^^^^^^^^^^^^ Avoid calling setState() directly within an effect - 280 | fetchSuppliers(); - 281 | }, []); - 282 | react-hooks/set-state-in-effect - 284:37 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\finance\page.tsx:284:37 - 282 | - 283 | useEffect(() => { -> 284 | if (activeTab === 'receivable') fetchReceivables(); - | ^^^^^^^^^^^^^^^^ Avoid calling setState() directly within an effect - 285 | else if (activeTab === 'payable') fetchPayables(); - 286 | else if (activeTab === 'receipt') fetchReceipts(); - 287 | else if (activeTab === 'payment') fetchPayments(); react-hooks/set-state-in-effect - 440:19 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍒犻櫎澶辫触"銆傚缓璁娇鐢? tc('text_azdahk') i18n/no-chinese-hardcode - 456:19 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍒犻櫎澶辫触"銆傚缓璁娇鐢? tc('text_azdahk') i18n/no-chinese-hardcode - 844:99 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏆傛棤浠樻璁板綍"銆傚缓璁娇鐢? {tc('text_myropj')} i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\[locale]\finance\payables\page.tsx - 53:16 warning 'setPage' is assigned a value but never used. Allowed unused elements of array destructuring must match /^_/u @typescript-eslint/no-unused-vars - 54:10 warning 'total' is assigned a value but never used. Allowed unused elements of array destructuring must match /^_/u @typescript-eslint/no-unused-vars - 80:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\finance\payables\page.tsx:80:5 - 78 | - 79 | useEffect(() => { -> 80 | loadPayables(); - | ^^^^^^^^^^^^ Avoid calling setState() directly within an effect - 81 | }, [page]); - 82 | - 83 | const getStatusBadge = (status: number) => { react-hooks/set-state-in-effect - 81:6 warning React Hook useEffect has a missing dependency: 'loadPayables'. Either include it or remove the dependency array react-hooks/exhaustive-deps - -D:\dcprint\erp-project\src\app\[locale]\finance\receivable\page.tsx - 138:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\finance\receivable\page.tsx:138:5 - 136 | - 137 | useEffect(() => { -> 138 | fetchData(); - | ^^^^^^^^^ Avoid calling setState() directly within an effect - 139 | }, [fetchData]); - 140 | - 141 | const handleViewDetail = async (id: number) => { react-hooks/set-state-in-effect - 150:22 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鑾峰彇璇︽儏澶辫触"銆傚缓璁娇鐢? tc('text_q73iq6') i18n/no-chinese-hardcode - 168:24 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鏀舵鎴愬姛"銆傚缓璁娇鐢? tc('text_d7qsev') i18n/no-chinese-hardcode - 172:24 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鏀舵澶辫触"銆傚缓璁娇鐢? tc('text_d7plng') i18n/no-chinese-hardcode - 179:9 warning 'formatAmount' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - -D:\dcprint\erp-project\src\app\[locale]\finance\receivables\page.tsx - 49:16 warning 'setPage' is assigned a value but never used. Allowed unused elements of array destructuring must match /^_/u @typescript-eslint/no-unused-vars - 50:10 warning 'total' is assigned a value but never used. Allowed unused elements of array destructuring must match /^_/u @typescript-eslint/no-unused-vars - 76:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\finance\receivables\page.tsx:76:5 - 74 | - 75 | useEffect(() => { -> 76 | loadReceivables(); - | ^^^^^^^^^^^^^^^ Avoid calling setState() directly within an effect - 77 | }, [page]); - 78 | - 79 | const getStatusBadge = (status: number) => { react-hooks/set-state-in-effect - 77:6 warning React Hook useEffect has a missing dependency: 'loadReceivables'. Either include it or remove the dependency array react-hooks/exhaustive-deps - -D:\dcprint\erp-project\src\app\[locale]\finance\report\page.tsx - 89:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\finance\report\page.tsx:89:5 - 87 | - 88 | useEffect(() => { -> 89 | fetchData(); - | ^^^^^^^^^ Avoid calling setState() directly within an effect - 90 | fetchSummary(); - 91 | }, [fetchData, fetchSummary]); - 92 | react-hooks/set-state-in-effect - -D:\dcprint\erp-project\src\app\[locale]\hr\attendance\page.tsx - 12:3 warning 'User' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 17:3 warning 'Download' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 23:3 warning 'BarChart2' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 24:3 warning 'Users' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 25:3 warning 'Briefcase' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 26:3 warning 'Award' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 27:3 warning 'X' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 28:3 warning 'Check' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 75:12 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "绠$悊閮?銆傚缓璁娇鐢? tc('text_iof7n') i18n/no-chinese-hardcode - 76:12 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "涓氬姟閮?銆傚缓璁娇鐢? tc('text_buoe9') i18n/no-chinese-hardcode - 77:12 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鐢熶骇閮?銆傚缓璁娇鐢? tc('text_hjr0g') i18n/no-chinese-hardcode - 78:12 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鎵撴牱涓績"銆傚缓璁娇鐢? tc('text_cu3n96') i18n/no-chinese-hardcode - 79:12 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "閲囪喘閮?銆傚缓璁娇鐢? tc('text_m1hlu') i18n/no-chinese-hardcode - 80:12 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍝佽川閮?銆傚缓璁娇鐢? tc('text_d3pkx') i18n/no-chinese-hardcode - 81:12 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "妯″垏"銆傚缓璁娇鐢? tc('text_ii2u') i18n/no-chinese-hardcode - 82:12 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍟嗘爣"銆傚缓璁娇鐢? tc('text_f2pt') i18n/no-chinese-hardcode - 83:12 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍏朵粬"銆傚缓璁娇鐢? tc('text_eae8') i18n/no-chinese-hardcode - 84:12 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "閲囪喘"銆傚缓璁娇鐢? tc('text_pkjq') i18n/no-chinese-hardcode - 195:5 warning Error: Cannot access variable before it is declared - -`fetchAttendanceRecords` is accessed before it is declared, which prevents the earlier access from updating when this value changes over time. - -D:\dcprint\erp-project\src\app\[locale]\hr\attendance\page.tsx:195:5 - 193 | - 194 | useEffect(() => { -> 195 | fetchAttendanceRecords(); - | ^^^^^^^^^^^^^^^^^^^^^^ `fetchAttendanceRecords` accessed before it is declared - 196 | }, []); - 197 | - 198 | const fetchAttendanceRecords = async () => { - -D:\dcprint\erp-project\src\app\[locale]\hr\attendance\page.tsx:198:3 - 196 | }, []); - 197 | -> 198 | const fetchAttendanceRecords = async () => { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 199 | try { - | ^^^^^^^^^ -> 200 | setIsLoading(true); - 鈥? | ^^^^^^^^^ -> 238 | } - | ^^^^^^^^^ -> 239 | }; - | ^^^^^ `fetchAttendanceRecords` is declared here - 240 | - 241 | // 鍒锋柊鏁版嵁 - 242 | const handleRefresh = useCallback(async () => { react-hooks/immutability - 196:6 warning React Hook useEffect has a missing dependency: 'fetchAttendanceRecords'. Either include it or remove the dependency array react-hooks/exhaustive-deps - 245:6 warning React Hook useCallback has missing dependencies: 'fetchAttendanceRecords' and 't'. Either include them or remove the dependency array react-hooks/exhaustive-deps - 255:6 warning React Hook useCallback has a missing dependency: 't'. Either include it or remove the dependency array react-hooks/exhaustive-deps - 775:22 warning Error: Cannot create components during render - -Components created during render will reset their state each time they are created. Declare components outside of render. - -D:\dcprint\erp-project\src\app\[locale]\hr\attendance\page.tsx:775:22 - 773 | /> - 774 | -> 775 | {tc('date')} - | ^^^^^^^^^^^^^^ This component is created during render - 776 | {tc('employeeNo')} - 777 | {tc('name')} - 778 | {tc('department')} - -D:\dcprint\erp-project\src\app\[locale]\hr\attendance\page.tsx:274:26 - 272 | }; - 273 | -> 274 | const SortableHeader = ({ field, children }: { field: string; children: React.ReactNode }) => ( - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 275 | 276 | className="cursor-pointer select-none border border-border bg-muted text-center whitespace-nowrap hover:bg-muted/80 transition-colors" - 鈥? | ^^^^^^^^^^^^^^ -> 291 | - | ^^^^^^^^^^^^^^ -> 292 | ); - | ^^^^ The component is created during render here - 293 | - 294 | // 绛涢€夎€冨嫟璁板綍 - 295 | const filteredRecords = useMemo(() => { react-hooks/static-components - 776:22 warning Error: Cannot create components during render - -Components created during render will reset their state each time they are created. Declare components outside of render. - -D:\dcprint\erp-project\src\app\[locale]\hr\attendance\page.tsx:776:22 - 774 | - 775 | {tc('date')} -> 776 | {tc('employeeNo')} - | ^^^^^^^^^^^^^^ This component is created during render - 777 | {tc('name')} - 778 | {tc('department')} - 779 | {tc('checkIn')} - -D:\dcprint\erp-project\src\app\[locale]\hr\attendance\page.tsx:274:26 - 272 | }; - 273 | -> 274 | const SortableHeader = ({ field, children }: { field: string; children: React.ReactNode }) => ( - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 275 | 276 | className="cursor-pointer select-none border border-border bg-muted text-center whitespace-nowrap hover:bg-muted/80 transition-colors" - 鈥? | ^^^^^^^^^^^^^^ -> 291 | - | ^^^^^^^^^^^^^^ -> 292 | ); - | ^^^^ The component is created during render here - 293 | - 294 | // 绛涢€夎€冨嫟璁板綍 - 295 | const filteredRecords = useMemo(() => { react-hooks/static-components - 777:22 warning Error: Cannot create components during render - -Components created during render will reset their state each time they are created. Declare components outside of render. - -D:\dcprint\erp-project\src\app\[locale]\hr\attendance\page.tsx:777:22 - 775 | {tc('date')} - 776 | {tc('employeeNo')} -> 777 | {tc('name')} - | ^^^^^^^^^^^^^^ This component is created during render - 778 | {tc('department')} - 779 | {tc('checkIn')} - 780 | {tc('checkOut')} - -D:\dcprint\erp-project\src\app\[locale]\hr\attendance\page.tsx:274:26 - 272 | }; - 273 | -> 274 | const SortableHeader = ({ field, children }: { field: string; children: React.ReactNode }) => ( - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 275 | 276 | className="cursor-pointer select-none border border-border bg-muted text-center whitespace-nowrap hover:bg-muted/80 transition-colors" - 鈥? | ^^^^^^^^^^^^^^ -> 291 | - | ^^^^^^^^^^^^^^ -> 292 | ); - | ^^^^ The component is created during render here - 293 | - 294 | // 绛涢€夎€冨嫟璁板綍 - 295 | const filteredRecords = useMemo(() => { react-hooks/static-components - 778:22 warning Error: Cannot create components during render - -Components created during render will reset their state each time they are created. Declare components outside of render. - -D:\dcprint\erp-project\src\app\[locale]\hr\attendance\page.tsx:778:22 - 776 | {tc('employeeNo')} - 777 | {tc('name')} -> 778 | {tc('department')} - | ^^^^^^^^^^^^^^ This component is created during render - 779 | {tc('checkIn')} - 780 | {tc('checkOut')} - 781 | {tc('workingHours')} - -D:\dcprint\erp-project\src\app\[locale]\hr\attendance\page.tsx:274:26 - 272 | }; - 273 | -> 274 | const SortableHeader = ({ field, children }: { field: string; children: React.ReactNode }) => ( - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 275 | 276 | className="cursor-pointer select-none border border-border bg-muted text-center whitespace-nowrap hover:bg-muted/80 transition-colors" - 鈥? | ^^^^^^^^^^^^^^ -> 291 | - | ^^^^^^^^^^^^^^ -> 292 | ); - | ^^^^ The component is created during render here - 293 | - 294 | // 绛涢€夎€冨嫟璁板綍 - 295 | const filteredRecords = useMemo(() => { react-hooks/static-components - 779:22 warning Error: Cannot create components during render - -Components created during render will reset their state each time they are created. Declare components outside of render. - -D:\dcprint\erp-project\src\app\[locale]\hr\attendance\page.tsx:779:22 - 777 | {tc('name')} - 778 | {tc('department')} -> 779 | {tc('checkIn')} - | ^^^^^^^^^^^^^^ This component is created during render - 780 | {tc('checkOut')} - 781 | {tc('workingHours')} - 782 | {tc('overtimeHours')} - -D:\dcprint\erp-project\src\app\[locale]\hr\attendance\page.tsx:274:26 - 272 | }; - 273 | -> 274 | const SortableHeader = ({ field, children }: { field: string; children: React.ReactNode }) => ( - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 275 | 276 | className="cursor-pointer select-none border border-border bg-muted text-center whitespace-nowrap hover:bg-muted/80 transition-colors" - 鈥? | ^^^^^^^^^^^^^^ -> 291 | - | ^^^^^^^^^^^^^^ -> 292 | ); - | ^^^^ The component is created during render here - 293 | - 294 | // 绛涢€夎€冨嫟璁板綍 - 295 | const filteredRecords = useMemo(() => { react-hooks/static-components - 780:22 warning Error: Cannot create components during render - -Components created during render will reset their state each time they are created. Declare components outside of render. - -D:\dcprint\erp-project\src\app\[locale]\hr\attendance\page.tsx:780:22 - 778 | {tc('department')} - 779 | {tc('checkIn')} -> 780 | {tc('checkOut')} - | ^^^^^^^^^^^^^^ This component is created during render - 781 | {tc('workingHours')} - 782 | {tc('overtimeHours')} - 783 | {tc('status')} - -D:\dcprint\erp-project\src\app\[locale]\hr\attendance\page.tsx:274:26 - 272 | }; - 273 | -> 274 | const SortableHeader = ({ field, children }: { field: string; children: React.ReactNode }) => ( - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 275 | 276 | className="cursor-pointer select-none border border-border bg-muted text-center whitespace-nowrap hover:bg-muted/80 transition-colors" - 鈥? | ^^^^^^^^^^^^^^ -> 291 | - | ^^^^^^^^^^^^^^ -> 292 | ); - | ^^^^ The component is created during render here - 293 | - 294 | // 绛涢€夎€冨嫟璁板綍 - 295 | const filteredRecords = useMemo(() => { react-hooks/static-components - 781:22 warning Error: Cannot create components during render - -Components created during render will reset their state each time they are created. Declare components outside of render. - -D:\dcprint\erp-project\src\app\[locale]\hr\attendance\page.tsx:781:22 - 779 | {tc('checkIn')} - 780 | {tc('checkOut')} -> 781 | {tc('workingHours')} - | ^^^^^^^^^^^^^^ This component is created during render - 782 | {tc('overtimeHours')} - 783 | {tc('status')} - 784 | {tc('remark')} - -D:\dcprint\erp-project\src\app\[locale]\hr\attendance\page.tsx:274:26 - 272 | }; - 273 | -> 274 | const SortableHeader = ({ field, children }: { field: string; children: React.ReactNode }) => ( - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 275 | 276 | className="cursor-pointer select-none border border-border bg-muted text-center whitespace-nowrap hover:bg-muted/80 transition-colors" - 鈥? | ^^^^^^^^^^^^^^ -> 291 | - | ^^^^^^^^^^^^^^ -> 292 | ); - | ^^^^ The component is created during render here - 293 | - 294 | // 绛涢€夎€冨嫟璁板綍 - 295 | const filteredRecords = useMemo(() => { react-hooks/static-components - 782:22 warning Error: Cannot create components during render - -Components created during render will reset their state each time they are created. Declare components outside of render. - -D:\dcprint\erp-project\src\app\[locale]\hr\attendance\page.tsx:782:22 - 780 | {tc('checkOut')} - 781 | {tc('workingHours')} -> 782 | {tc('overtimeHours')} - | ^^^^^^^^^^^^^^ This component is created during render - 783 | {tc('status')} - 784 | {tc('remark')} - 785 | {tc('actions')} - -D:\dcprint\erp-project\src\app\[locale]\hr\attendance\page.tsx:274:26 - 272 | }; - 273 | -> 274 | const SortableHeader = ({ field, children }: { field: string; children: React.ReactNode }) => ( - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 275 | 276 | className="cursor-pointer select-none border border-border bg-muted text-center whitespace-nowrap hover:bg-muted/80 transition-colors" - 鈥? | ^^^^^^^^^^^^^^ -> 291 | - | ^^^^^^^^^^^^^^ -> 292 | ); - | ^^^^ The component is created during render here - 293 | - 294 | // 绛涢€夎€冨嫟璁板綍 - 295 | const filteredRecords = useMemo(() => { react-hooks/static-components - 783:22 warning Error: Cannot create components during render - -Components created during render will reset their state each time they are created. Declare components outside of render. - -D:\dcprint\erp-project\src\app\[locale]\hr\attendance\page.tsx:783:22 - 781 | {tc('workingHours')} - 782 | {tc('overtimeHours')} -> 783 | {tc('status')} - | ^^^^^^^^^^^^^^ This component is created during render - 784 | {tc('remark')} - 785 | {tc('actions')} - 786 | - -D:\dcprint\erp-project\src\app\[locale]\hr\attendance\page.tsx:274:26 - 272 | }; - 273 | -> 274 | const SortableHeader = ({ field, children }: { field: string; children: React.ReactNode }) => ( - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 275 | 276 | className="cursor-pointer select-none border border-border bg-muted text-center whitespace-nowrap hover:bg-muted/80 transition-colors" - 鈥? | ^^^^^^^^^^^^^^ -> 291 | - | ^^^^^^^^^^^^^^ -> 292 | ); - | ^^^^ The component is created during render here - 293 | - 294 | // 绛涢€夎€冨嫟璁板綍 - 295 | const filteredRecords = useMemo(() => { react-hooks/static-components - 1138:44 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍦?銆傚缓璁娇鐢? {tc('text_h7s')} i18n/no-chinese-hardcode - 1138:66 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鐨勮€冨嫟璁板綍鍚楋紵姝ゆ搷浣滀笉鍙挙閿€銆?銆傚缓璁娇鐢? {tc('text_ybol37')} i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\[locale]\hr\certificates\expiring\page.tsx - 87:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\hr\certificates\expiring\page.tsx:87:5 - 85 | - 86 | useEffect(() => { -> 87 | fetchData(); - | ^^^^^^^^^ Avoid calling setState() directly within an effect - 88 | }, []); - 89 | - 90 | const handleRenew = async () => { react-hooks/set-state-in-effect - 88:6 warning React Hook useEffect has a missing dependency: 'fetchData'. Either include it or remove the dependency array react-hooks/exhaustive-deps - -D:\dcprint\erp-project\src\app\[locale]\hr\certificates\page.tsx - 35:38 warning 'Eye' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 128:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\hr\certificates\page.tsx:128:5 - 126 | - 127 | useEffect(() => { -> 128 | fetchData(); - | ^^^^^^^^^ Avoid calling setState() directly within an effect - 129 | }, [page]); - 130 | - 131 | const handleSave = async () => { react-hooks/set-state-in-effect - 129:6 warning React Hook useEffect has a missing dependency: 'fetchData'. Either include it or remove the dependency array react-hooks/exhaustive-deps - 502:61 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "闄勪欢锛?銆傚缓璁娇鐢? {tc('text_mf6eg')} i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\[locale]\hr\employee\components\dialogs\BatchPrintDialog.tsx - 43:13 warning Using `` could result in slower LCP and higher bandwidth. Consider using `` from `next/image` or a custom image loader to automatically optimize images. This may incur additional usage or cost from your provider. See: https://nextjs.org/docs/messages/no-img-element @next/next/no-img-element - -D:\dcprint\erp-project\src\app\[locale]\hr\employee\components\dialogs\EmployeeFormDialog.tsx - 319:35 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍒濅腑"銆傚缓璁娇鐢? {tc('text_ee9c')} i18n/no-chinese-hardcode - 320:35 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "涓笓"銆傚缓璁娇鐢? {tc('text_dq4m')} i18n/no-chinese-hardcode - 321:35 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "楂樹腑"銆傚缓璁娇鐢? {tc('text_qrmd')} i18n/no-chinese-hardcode - 322:35 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "澶т笓"銆傚缓璁娇鐢? {tc('text_flcc')} i18n/no-chinese-hardcode - 323:35 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏈"銆傚缓璁娇鐢? {tc('text_i7tx')} i18n/no-chinese-hardcode - 324:35 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "纭曞+"銆傚缓璁娇鐢? {tc('text_kyeu')} i18n/no-chinese-hardcode - 325:35 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍗氬+"銆傚缓璁娇鐢? {tc('text_enyp')} i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\[locale]\hr\employee\components\dialogs\PrintDialog.tsx - 55:19 warning Using `` could result in slower LCP and higher bandwidth. Consider using `` from `next/image` or a custom image loader to automatically optimize images. This may incur additional usage or cost from your provider. See: https://nextjs.org/docs/messages/no-img-element @next/next/no-img-element - -D:\dcprint\erp-project\src\app\[locale]\hr\employee\page.tsx - 54:15 warning 'ExportColumn' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 62:11 warning 'hasPermission' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 98:61 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "寮€濮嬭绠楀憳宸ョ粺璁℃暟鎹?銆傚缓璁娇鐢? tc('text_pldi1a') i18n/no-chinese-hardcode - 114:61 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍛樺伐缁熻鏁版嵁璁$畻瀹屾垚"銆傚缓璁娇鐢? tc('text_1cb9o9') i18n/no-chinese-hardcode - 121:6 warning React Hook useCallback has a missing dependency: 't'. Either include it or remove the dependency array react-hooks/exhaustive-deps - 159:61 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "寮€濮嬭幏鍙栧憳宸ュ垪琛?銆傚缓璁娇鐢? tc('text_5us70o') i18n/no-chinese-hardcode - 168:65 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "浣跨敤 mock 鏁版嵁"銆傚缓璁娇鐢? tc('text_us9wlf') i18n/no-chinese-hardcode - 169:15 warning 'mockResponse' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 186:63 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍛樺伐鍒楄〃鑾峰彇鎴愬姛"銆傚缓璁娇鐢? tc('text_r2w30s') i18n/no-chinese-hardcode - 190:64 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鑾峰彇鍛樺伐鍒楄〃澶辫触"銆傚缓璁娇鐢? tc('text_shdpgv') i18n/no-chinese-hardcode - 197:6 warning React Hook useCallback has a missing dependency: 't'. Either include it or remove the dependency array react-hooks/exhaustive-deps - 201:63 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "寮€濮嬭幏鍙栭儴闂ㄥ垪琛?銆傚缓璁娇鐢? tc('text_dpz86z') i18n/no-chinese-hardcode - 206:67 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "浣跨敤 mock 鏁版嵁"銆傚缓璁娇鐢? tc('text_us9wlf') i18n/no-chinese-hardcode - 217:65 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "閮ㄩ棬鍒楄〃鑾峰彇鎴愬姛"銆傚缓璁娇鐢? tc('text_uee9vl') i18n/no-chinese-hardcode - 221:66 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鑾峰彇閮ㄩ棬鍒楄〃澶辫触"銆傚缓璁娇鐢? tc('text_1zeuh0') i18n/no-chinese-hardcode - 229:57 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "寮€濮嬭幏鍙栬鑹插垪琛?銆傚缓璁娇鐢? tc('text_cqybrv') i18n/no-chinese-hardcode - 234:61 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "浣跨敤 mock 鏁版嵁"銆傚缓璁娇鐢? tc('text_us9wlf') i18n/no-chinese-hardcode - 245:59 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "瑙掕壊鍒楄〃鑾峰彇鎴愬姛"銆傚缓璁娇鐢? tc('text_17c6zj') i18n/no-chinese-hardcode - 249:60 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鑾峰彇瑙掕壊鍒楄〃澶辫触"銆傚缓璁娇鐢? tc('text_9l5v8c') i18n/no-chinese-hardcode - 289:61 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "寮€濮嬩笂浼犲憳宸ョ収鐗?銆傚缓璁娇鐢? tc('text_ga1sku') i18n/no-chinese-hardcode - 304:63 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鐓х墖涓婁紶鎴愬姛"銆傚缓璁娇鐢? tc('text_3rgyqj') i18n/no-chinese-hardcode - 315:62 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "涓婁紶鐓х墖寮傚父"銆傚缓璁娇鐢? tc('text_j1p6r8') i18n/no-chinese-hardcode - 330:64 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍒犻櫎鍛樺伐鐓х墖"銆傚缓璁娇鐢? tc('text_u8cur3') i18n/no-chinese-hardcode - 381:62 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "淇濆瓨鍛樺伐寮傚父"銆傚缓璁娇鐢? tc('text_wzz4v2') i18n/no-chinese-hardcode - 391:61 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "璇锋眰鍒犻櫎鍛樺伐"銆傚缓璁娇鐢? tc('text_39iy6c') i18n/no-chinese-hardcode - 396:63 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鐢ㄦ埛鍙栨秷鍒犻櫎"銆傚缓璁娇鐢? tc('text_y514n9') i18n/no-chinese-hardcode - 405:65 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍛樺伐鍒犻櫎鎴愬姛"銆傚缓璁娇鐢? tc('text_hr0j4w') i18n/no-chinese-hardcode - 419:64 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍒犻櫎鍛樺伐寮傚父"銆傚缓璁娇鐢? tc('text_u8g3cp') i18n/no-chinese-hardcode - 449:62 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "寮€濮嬫墦鍗板憳宸ュ垪琛?銆傚缓璁娇鐢? tc('text_5sywp2') i18n/no-chinese-hardcode - 468:65 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鏃犳硶鎵撳紑鎵撳嵃绐楀彛"銆傚缓璁娇鐢? tc('text_4vacdn') i18n/no-chinese-hardcode - 518:62 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鎵撳嵃鍒楄〃绐楀彛宸叉墦寮€"銆傚缓璁娇鐢? tc('text_7rej8b') i18n/no-chinese-hardcode - 526:63 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鎵撳紑鎵归噺鎵撳嵃瀵硅瘽妗?銆傚缓璁娇鐢? tc('text_olqn5u') i18n/no-chinese-hardcode - 544:60 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "寮€濮嬪鍑篍xcel"銆傚缓璁娇鐢? tc('text_9di4pe') i18n/no-chinese-hardcode - 609:60 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "Excel瀵煎嚭鎴愬姛"銆傚缓璁娇鐢? tc('text_1k1sv0') i18n/no-chinese-hardcode - 622:58 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "寮€濮嬪鍑篜DF"銆傚缓璁娇鐢? tc('text_93muzb') i18n/no-chinese-hardcode - 635:61 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鏃犳硶鎵撳紑PDF寮瑰嚭绐楀彛"銆傚缓璁娇鐢? tc('text_hpgsir') i18n/no-chinese-hardcode - 717:58 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "PDF瀵煎嚭鎴愬姛"銆傚缓璁娇鐢? tc('text_bwj1wf') i18n/no-chinese-hardcode - 727:66 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "寮€濮嬫壒閲忔墦鍗板憳宸ヤ笂宀楄瘉"銆傚缓璁娇鐢? tc('text_sy7j6x') i18n/no-chinese-hardcode - 733:69 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鏃犳硶鎵撳紑鎵归噺鎵撳嵃绐楀彛"銆傚缓璁娇鐢? tc('text_y5edi7') i18n/no-chinese-hardcode - 843:66 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鎵归噺鎵撳嵃绐楀彛宸叉墦寮€"銆傚缓璁娇鐢? tc('text_r7pvsw') i18n/no-chinese-hardcode - 851:65 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "寮€濮嬬敓鎴愬憳宸ヤ簩缁寸爜"銆傚缓璁娇鐢? tc('text_bxm2nk') i18n/no-chinese-hardcode - 868:67 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍛樺伐浜岀淮鐮佺敓鎴愭垚鍔?銆傚缓璁娇鐢? tc('text_mmv2b8') i18n/no-chinese-hardcode - 873:68 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鐢熸垚浜岀淮鐮佸け璐?銆傚缓璁娇鐢? tc('text_xyx1dw') i18n/no-chinese-hardcode - 883:58 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "寮€濮嬫墦鍗板崟寮犱笂宀楄瘉"銆傚缓璁娇鐢? tc('text_y28f5t') i18n/no-chinese-hardcode - 891:61 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鏃犳硶鎵撳紑鎵撳嵃绐楀彛"銆傚缓璁娇鐢? tc('text_4vacdn') i18n/no-chinese-hardcode - 1074:58 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍗曞紶涓婂矖璇佹墦鍗扮獥鍙e凡鎵撳紑"銆傚缓璁娇鐢? tc('text_i1cgkz') i18n/no-chinese-hardcode - 1079:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\hr\employee\page.tsx:1079:5 - 1077 | // 鍒濆鍖栧姞杞? 1078 | useEffect(() => { -> 1079 | fetchEmployees(); - | ^^^^^^^^^^^^^^ Avoid calling setState() directly within an effect - 1080 | fetchDepartments(); - 1081 | fetchRoles(); - 1082 | }, [fetchEmployees, fetchDepartments, fetchRoles]); react-hooks/set-state-in-effect - 1146:26 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍛樺伐鍒楄〃"銆傚缓璁娇鐢? {tc('text_b14u4e')} i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\[locale]\hr\employee\query\page.tsx - 52:7 warning Error: Cannot access variable before it is declared - -`fetchEmployee` is accessed before it is declared, which prevents the earlier access from updating when this value changes over time. - -D:\dcprint\erp-project\src\app\[locale]\hr\employee\query\page.tsx:52:7 - 50 | useEffect(() => { - 51 | if (employeeId) { -> 52 | fetchEmployee(parseInt(employeeId)); - | ^^^^^^^^^^^^^ `fetchEmployee` accessed before it is declared - 53 | } else { - 54 | setError(tc('invalidEmployeeId')); - 55 | setLoading(false); - -D:\dcprint\erp-project\src\app\[locale]\hr\employee\query\page.tsx:59:3 - 57 | }, [employeeId]); - 58 | -> 59 | const fetchEmployee = async (id: number) => { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 60 | try { - | ^^^^^^^^^ -> 61 | const response = await authFetch(`/api/organization/employee?id=${id}`); - 鈥? | ^^^^^^^^^ -> 72 | } - | ^^^^^^^^^ -> 73 | }; - | ^^^^^ `fetchEmployee` is declared here - 74 | - 75 | const getStatusBadge = (status: number) => { - 76 | const styles: Record = { react-hooks/immutability - 54:7 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\hr\employee\query\page.tsx:54:7 - 52 | fetchEmployee(parseInt(employeeId)); - 53 | } else { -> 54 | setError(tc('invalidEmployeeId')); - | ^^^^^^^^ Avoid calling setState() directly within an effect - 55 | setLoading(false); - 56 | } - 57 | }, [employeeId]); react-hooks/set-state-in-effect - 57:6 warning React Hook useEffect has missing dependencies: 'fetchEmployee' and 'tc'. Either include them or remove the dependency array react-hooks/exhaustive-deps - -D:\dcprint\erp-project\src\app\[locale]\hr\mes-sync\page.tsx - 13:45 warning 'AlertCircle' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 29:116 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "杩炴帴瓒呮椂"銆傚缓璁娇鐢? tc('text_ikiwuw') i18n/no-chinese-hardcode - 38:53 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 84:21 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\hr\mes-sync\page.tsx:84:21 - 82 | }; - 83 | -> 84 | useEffect(() => { fetchSyncData(); }, []); - | ^^^^^^^^^^^^^ Avoid calling setState() directly within an effect - 85 | - 86 | const handleManualSync = async () => { - 87 | setSyncing(true); react-hooks/set-state-in-effect - -D:\dcprint\erp-project\src\app\[locale]\hr\organization\page.tsx - 9:9 warning 't' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - -D:\dcprint\erp-project\src\app\[locale]\hr\performance\page.tsx - 7:41 warning 'CardTitle' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 13:10 warning 'Badge' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 37:10 warning 'loading' is assigned a value but never used. Allowed unused elements of array destructuring must match /^_/u @typescript-eslint/no-unused-vars - 47:35 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 67:21 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\hr\performance\page.tsx:67:21 - 65 | }; - 66 | -> 67 | useEffect(() => { fetchScores(); }, []); - | ^^^^^^^^^^^ Avoid calling setState() directly within an effect - 68 | - 69 | const updateScore = (id: number, field: keyof ScoreRow, value: number) => { - 70 | setScores((prev) => react-hooks/set-state-in-effect - 184:26 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "寮犱笁"銆傚缓璁娇鐢? tc('text_glwp') i18n/no-chinese-hardcode - 185:26 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏉庡洓"銆傚缓璁娇鐢? tc('text_i1ql') i18n/no-chinese-hardcode - 186:26 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鐜嬩簲"銆傚缓璁娇鐢? tc('text_k31l') i18n/no-chinese-hardcode - 187:26 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "璧靛叚"銆傚缓璁娇鐢? tc('text_oiag') i18n/no-chinese-hardcode - 188:26 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "瀛欎竷"銆傚缓璁娇鐢? tc('text_fyru') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\[locale]\hr\reports\labor-cost\page.tsx - 19:18 warning 'subMonths' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 54:63 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "椤甸潰鍔犺浇澶辫触"銆傚缓璁娇鐢? {tc('text_p6lute')} i18n/no-chinese-hardcode - 56:47 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍙戠敓鏈煡閿欒"銆傚缓璁娇鐢? tc('text_nuijq9') i18n/no-chinese-hardcode - 62:16 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "閲嶈瘯"銆傚缓璁娇鐢? {tc('text_pkfc')} i18n/no-chinese-hardcode - 100:5 warning Error: Cannot access variable before it is declared - -`fetchData` is accessed before it is declared, which prevents the earlier access from updating when this value changes over time. - -D:\dcprint\erp-project\src\app\[locale]\hr\reports\labor-cost\page.tsx:100:5 - 98 | - 99 | useEffect(() => { -> 100 | fetchData(); - | ^^^^^^^^^ `fetchData` accessed before it is declared - 101 | }, [month]); - 102 | - 103 | const fetchData = async () => { - -D:\dcprint\erp-project\src\app\[locale]\hr\reports\labor-cost\page.tsx:103:3 - 101 | }, [month]); - 102 | -> 103 | const fetchData = async () => { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 104 | setLoading(true); - | ^^^^^^^^^^^^^^^^^^^^^ -> 105 | try { - 鈥? | ^^^^^^^^^^^^^^^^^^^^^ -> 116 | } - | ^^^^^^^^^^^^^^^^^^^^^ -> 117 | }; - | ^^^^^ `fetchData` is declared here - 118 | - 119 | const changeMonth = (offset: number) => { - 120 | const date = new Date(month + '-01'); react-hooks/immutability - 101:6 warning React Hook useEffect has a missing dependency: 'fetchData'. Either include it or remove the dependency array react-hooks/exhaustive-deps - -D:\dcprint\erp-project\src\app\[locale]\hr\reports\salary-structure\page.tsx - 31:9 warning 'tc' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 37:5 warning Error: Cannot access variable before it is declared - -`fetchData` is accessed before it is declared, which prevents the earlier access from updating when this value changes over time. - -D:\dcprint\erp-project\src\app\[locale]\hr\reports\salary-structure\page.tsx:37:5 - 35 | - 36 | useEffect(() => { -> 37 | fetchData(); - | ^^^^^^^^^ `fetchData` accessed before it is declared - 38 | }, [month]); - 39 | - 40 | const fetchData = async () => { - -D:\dcprint\erp-project\src\app\[locale]\hr\reports\salary-structure\page.tsx:40:3 - 38 | }, [month]); - 39 | -> 40 | const fetchData = async () => { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 41 | setLoading(true); - | ^^^^^^^^^^^^^^^^^^^^^ -> 42 | try { - 鈥? | ^^^^^^^^^^^^^^^^^^^^^ -> 54 | } - | ^^^^^^^^^^^^^^^^^^^^^ -> 55 | }; - | ^^^^^ `fetchData` is declared here - 56 | - 57 | const changeMonth = (offset: number) => { - 58 | const date = new Date(month + '-01'); react-hooks/immutability - 38:6 warning React Hook useEffect has a missing dependency: 'fetchData'. Either include it or remove the dependency array react-hooks/exhaustive-deps - -D:\dcprint\erp-project\src\app\[locale]\hr\reports\turnover\page.tsx - 40:9 warning 'tc' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 45:5 warning Error: Cannot access variable before it is declared - -`fetchData` is accessed before it is declared, which prevents the earlier access from updating when this value changes over time. - -D:\dcprint\erp-project\src\app\[locale]\hr\reports\turnover\page.tsx:45:5 - 43 | - 44 | useEffect(() => { -> 45 | fetchData(); - | ^^^^^^^^^ `fetchData` accessed before it is declared - 46 | }, []); - 47 | - 48 | const fetchData = async () => { - -D:\dcprint\erp-project\src\app\[locale]\hr\reports\turnover\page.tsx:48:3 - 46 | }, []); - 47 | -> 48 | const fetchData = async () => { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 49 | setLoading(true); - | ^^^^^^^^^^^^^^^^^^^^^ -> 50 | try { - 鈥? | ^^^^^^^^^^^^^^^^^^^^^ -> 59 | } - | ^^^^^^^^^^^^^^^^^^^^^ -> 60 | }; - | ^^^^^ `fetchData` is declared here - 61 | - 62 | if (loading) { - 63 | return ( react-hooks/immutability - -D:\dcprint\erp-project\src\app\[locale]\hr\salary\bank-report\page.tsx - 7:41 warning 'CardTitle' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 14:10 warning 'Badge' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 18:30 warning 'DollarSign' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 28:19 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "寮犱笁"銆傚缓璁娇鐢? tc('text_glwp') i18n/no-chinese-hardcode - 29:19 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏉庡洓"銆傚缓璁娇鐢? tc('text_i1ql') i18n/no-chinese-hardcode - 30:19 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鐜嬩簲"銆傚缓璁娇鐢? tc('text_k31l') i18n/no-chinese-hardcode - 31:19 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "璧靛叚"銆傚缓璁娇鐢? tc('text_oiag') i18n/no-chinese-hardcode - 32:19 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "瀛欎竷"銆傚缓璁娇鐢? tc('text_fyru') i18n/no-chinese-hardcode - 33:19 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍛ㄥ叓"銆傚缓璁娇鐢? tc('text_esxv') i18n/no-chinese-hardcode - 52:40 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 68:21 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\hr\salary\bank-report\page.tsx:68:21 - 66 | }; - 67 | -> 68 | useEffect(() => { fetchData(); }, []); - | ^^^^^^^^^ Avoid calling setState() directly within an effect - 69 | - 70 | const totalAmount = data.reduce((s, item) => s + item.netPay, 0); - 71 | react-hooks/set-state-in-effect - 68:37 warning React Hook useEffect has a missing dependency: 'fetchData'. Either include it or remove the dependency array react-hooks/exhaustive-deps - -D:\dcprint\erp-project\src\app\[locale]\hr\salary\calculate\page.tsx - 14:22 warning 'CheckCircle' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 14:35 warning 'AlertCircle' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 50:5 warning Unexpected console statement. Only these console methods are allowed: warn, error no-console - 51:5 warning Unexpected console statement. Only these console methods are allowed: warn, error no-console - 52:5 warning Unexpected console statement. Only these console methods are allowed: warn, error no-console - 53:5 warning Unexpected console statement. Only these console methods are allowed: warn, error no-console - 54:5 warning Unexpected console statement. Only these console methods are allowed: warn, error no-console - 55:5 warning Unexpected console statement. Only these console methods are allowed: warn, error no-console - 59:5 warning Unexpected console statement. Only these console methods are allowed: warn, error no-console - 60:6 warning React Hook useEffect has a missing dependency: 'month'. Either include it or remove the dependency array react-hooks/exhaustive-deps - 64:5 warning Unexpected console statement. Only these console methods are allowed: warn, error no-console - 72:7 warning Unexpected console statement. Only these console methods are allowed: warn, error no-console - 80:21 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "[SalaryCalculatePage] 璁$畻璇锋眰澶辫触:"銆傚缓璁娇鐢? tc('text_lhcsjy') i18n/no-chinese-hardcode - 88:5 warning Unexpected console statement. Only these console methods are allowed: warn, error no-console - 96:7 warning Unexpected console statement. Only these console methods are allowed: warn, error no-console - 104:21 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "[SalaryCalculatePage] 鎵归噺璁$畻璇锋眰澶辫触:"銆傚缓璁娇鐢? tc('text_vij75o') i18n/no-chinese-hardcode - 200:35 warning Error: Cannot create components during render - -Components created during render will reset their state each time they are created. Declare components outside of render. - -D:\dcprint\erp-project\src\app\[locale]\hr\salary\calculate\page.tsx:200:35 - 198 | - 199 | {/* 鍗曞憳宸ョ粨鏋?*/} -> 200 | {result && !batchMode && } - | ^^^^^^^^^^^^^^^^ This component is created during render - 201 | - 202 | {/* 鎵归噺缁撴灉 */} - 203 | {batchResults.length > 0 && batchMode && ( - -D:\dcprint\erp-project\src\app\[locale]\hr\salary\calculate\page.tsx:110:28 - 108 | }; - 109 | -> 110 | const SalaryDetailCard = ({ data }: { data: SalaryResult }) => ( - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 111 | - | ^^^^^^^^^^ -> 112 | - 鈥? | ^^^^^^^^^^ -> 153 | - | ^^^^^^^^^^ -> 154 | ); - | ^^^^ The component is created during render here - 155 | - 156 | return ( - 157 | react-hooks/static-components - -D:\dcprint\erp-project\src\app\[locale]\hr\salary\page.tsx - 118:7 warning 'mockSalaries' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 122:11 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "寮犱笁"銆傚缓璁娇鐢? tc('text_glwp') i18n/no-chinese-hardcode - 125:16 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鐢熶骇閮?銆傚缓璁娇鐢? tc('text_hjr0g') i18n/no-chinese-hardcode - 126:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鐢熶骇涓荤"銆傚缓璁娇鐢? tc('text_f3plim') i18n/no-chinese-hardcode - 141:13 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "姝e父鍙戞斁"銆傚缓璁娇鐢? tc('text_dxtawy') i18n/no-chinese-hardcode - 146:11 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏉庡洓"銆傚缓璁娇鐢? tc('text_i1ql') i18n/no-chinese-hardcode - 149:16 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鐢熶骇閮?銆傚缓璁娇鐢? tc('text_hjr0g') i18n/no-chinese-hardcode - 150:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鎿嶄綔宸?銆傚缓璁娇鐢? tc('text_f5j86') i18n/no-chinese-hardcode - 165:13 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "姝e父鍙戞斁"銆傚缓璁娇鐢? tc('text_dxtawy') i18n/no-chinese-hardcode - 170:11 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鐜嬩簲"銆傚缓璁娇鐢? tc('text_k31l') i18n/no-chinese-hardcode - 173:16 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍝佽川閮?銆傚缓璁娇鐢? tc('text_d3pkx') i18n/no-chinese-hardcode - 174:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "璐ㄦ鍛?銆傚缓璁娇鐢? tc('text_l6lds') i18n/no-chinese-hardcode - 189:13 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "姝e父鍙戞斁"銆傚缓璁娇鐢? tc('text_dxtawy') i18n/no-chinese-hardcode - 194:11 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "璧靛叚"銆傚缓璁娇鐢? tc('text_oiag') i18n/no-chinese-hardcode - 197:16 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鎶€鏈儴"銆傚缓璁娇鐢? tc('text_exqft') i18n/no-chinese-hardcode - 198:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "宸ョ▼甯?銆傚缓璁娇鐢? tc('text_ecdmq') i18n/no-chinese-hardcode - 213:13 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "姝e父鍙戞斁"銆傚缓璁娇鐢? tc('text_dxtawy') i18n/no-chinese-hardcode - 218:11 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "瀛欎竷"銆傚缓璁娇鐢? tc('text_fyru') i18n/no-chinese-hardcode - 221:16 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "閿€鍞儴"銆傚缓璁娇鐢? tc('text_m8ygq') i18n/no-chinese-hardcode - 222:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "閿€鍞粡鐞?銆傚缓璁娇鐢? tc('text_j5n8hx') i18n/no-chinese-hardcode - 237:13 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍚攢鍞彁鎴?銆傚缓璁娇鐢? tc('text_6wmt0n') i18n/no-chinese-hardcode - 242:11 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍛ㄥ叓"銆傚缓璁娇鐢? tc('text_esxv') i18n/no-chinese-hardcode - 245:16 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "璐㈠姟閮?銆傚缓璁娇鐢? tc('text_l31ft') i18n/no-chinese-hardcode - 246:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "浼氳"銆傚缓璁娇鐢? tc('text_e7yf') i18n/no-chinese-hardcode - 261:13 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "姝e父鍙戞斁"銆傚缓璁娇鐢? tc('text_dxtawy') i18n/no-chinese-hardcode - 266:7 warning 'mockDepartments' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 267:23 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鐢熶骇閮?銆傚缓璁娇鐢? tc('text_hjr0g') i18n/no-chinese-hardcode - 268:23 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍝佽川閮?銆傚缓璁娇鐢? tc('text_d3pkx') i18n/no-chinese-hardcode - 269:23 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鎶€鏈儴"銆傚缓璁娇鐢? tc('text_exqft') i18n/no-chinese-hardcode - 270:23 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "閿€鍞儴"銆傚缓璁娇鐢? tc('text_m8ygq') i18n/no-chinese-hardcode - 271:23 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "璐㈠姟閮?銆傚缓璁娇鐢? tc('text_l31ft') i18n/no-chinese-hardcode - 272:23 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "浜轰簨閮?銆傚缓璁娇鐢? tc('text_bxa0n') i18n/no-chinese-hardcode - 380:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\hr\salary\page.tsx:380:5 - 378 | - 379 | useEffect(() => { -> 380 | fetchSalaryData(); - | ^^^^^^^^^^^^^^^ Avoid calling setState() directly within an effect - 381 | }, []); - 382 | - 383 | // 钖祫琛ㄥ崟 react-hooks/set-state-in-effect - 616:9 warning 'handleExport' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 891:20 warning Error: Cannot create components during render - -Components created during render will reset their state each time they are created. Declare components outside of render. - -D:\dcprint\erp-project\src\app\[locale]\hr\salary\page.tsx:891:20 - 889 | - 890 | {tc('serialNo')} -> 891 | {tc('employeeInfo')} - | ^^^^^^^^^^^^^^ This component is created during render - 892 | {tc('deptPosition')} - 893 | - 894 | {tc('baseSalary')} - -D:\dcprint\erp-project\src\app\[locale]\hr\salary\page.tsx:484:26 - 482 | }; - 483 | -> 484 | const SortableHeader = ({ - | ^^ -> 485 | field, - | ^^^^^^^^^^ -> 486 | children, - 鈥? | ^^^^^^^^^^ -> 509 | - | ^^^^^^^^^^ -> 510 | ); - | ^^^^ The component is created during render here - 511 | - 512 | // 鏌ョ湅璇︽儏 - 513 | const handleViewDetail = (salary: Salary) => { react-hooks/static-components - 892:20 warning Error: Cannot create components during render - -Components created during render will reset their state each time they are created. Declare components outside of render. - -D:\dcprint\erp-project\src\app\[locale]\hr\salary\page.tsx:892:20 - 890 | {tc('serialNo')} - 891 | {tc('employeeInfo')} -> 892 | {tc('deptPosition')} - | ^^^^^^^^^^^^^^ This component is created during render - 893 | - 894 | {tc('baseSalary')} - 895 | - -D:\dcprint\erp-project\src\app\[locale]\hr\salary\page.tsx:484:26 - 482 | }; - 483 | -> 484 | const SortableHeader = ({ - | ^^ -> 485 | field, - | ^^^^^^^^^^ -> 486 | children, - 鈥? | ^^^^^^^^^^ -> 509 | - | ^^^^^^^^^^ -> 510 | ); - | ^^^^ The component is created during render here - 511 | - 512 | // 鏌ョ湅璇︽儏 - 513 | const handleViewDetail = (salary: Salary) => { react-hooks/static-components - 893:20 warning Error: Cannot create components during render - -Components created during render will reset their state each time they are created. Declare components outside of render. - -D:\dcprint\erp-project\src\app\[locale]\hr\salary\page.tsx:893:20 - 891 | {tc('employeeInfo')} - 892 | {tc('deptPosition')} -> 893 | - | ^^^^^^^^^^^^^^ This component is created during render - 894 | {tc('baseSalary')} - 895 | - 896 | {tc('allowanceBonus')} - -D:\dcprint\erp-project\src\app\[locale]\hr\salary\page.tsx:484:26 - 482 | }; - 483 | -> 484 | const SortableHeader = ({ - | ^^ -> 485 | field, - | ^^^^^^^^^^ -> 486 | children, - 鈥? | ^^^^^^^^^^ -> 509 | - | ^^^^^^^^^^ -> 510 | ); - | ^^^^ The component is created during render here - 511 | - 512 | // 鏌ョ湅璇︽儏 - 513 | const handleViewDetail = (salary: Salary) => { react-hooks/static-components - 898:20 warning Error: Cannot create components during render - -Components created during render will reset their state each time they are created. Declare components outside of render. - -D:\dcprint\erp-project\src\app\[locale]\hr\salary\page.tsx:898:20 - 896 | {tc('allowanceBonus')} - 897 | {tc('deductionItems')} -> 898 | - | ^^^^^^^^^^^^^^ This component is created during render - 899 | {tc('netSalary')} - 900 | - 901 | {tc('status')} - -D:\dcprint\erp-project\src\app\[locale]\hr\salary\page.tsx:484:26 - 482 | }; - 483 | -> 484 | const SortableHeader = ({ - | ^^ -> 485 | field, - | ^^^^^^^^^^ -> 486 | children, - 鈥? | ^^^^^^^^^^ -> 509 | - | ^^^^^^^^^^ -> 510 | ); - | ^^^^ The component is created during render here - 511 | - 512 | // 鏌ョ湅璇︽儏 - 513 | const handleViewDetail = (salary: Salary) => { react-hooks/static-components - 1038:35 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "钖祫鏄庣粏"銆傚缓璁娇鐢? {tc('text_hg3z6a')} i18n/no-chinese-hardcode - 1249:35 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "钖祫鏄庣粏"銆傚缓璁娇鐢? {tc('text_hg3z6a')} i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\[locale]\hr\salary\payslips\page.tsx - 11:10 warning 'Badge' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 14:3 warning 'Select' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 14:11 warning 'SelectContent' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 14:26 warning 'SelectItem' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 14:38 warning 'SelectTrigger' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 14:53 warning 'SelectValue' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 41:17 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "寮犱笁"銆傚缓璁娇鐢? tc('text_glwp') i18n/no-chinese-hardcode - 43:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鐢熶骇閮?銆傚缓璁娇鐢? tc('text_hjr0g') i18n/no-chinese-hardcode - 44:13 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鐢熶骇涓荤"銆傚缓璁娇鐢? tc('text_f3plim') i18n/no-chinese-hardcode - 71:5 warning Unexpected console statement. Only these console methods are allowed: warn, error no-console - 72:5 warning Unexpected console statement. Only these console methods are allowed: warn, error no-console - 73:5 warning Unexpected console statement. Only these console methods are allowed: warn, error no-console - 74:5 warning Unexpected console statement. Only these console methods are allowed: warn, error no-console - 75:5 warning Unexpected console statement. Only these console methods are allowed: warn, error no-console - 76:5 warning Unexpected console statement. Only these console methods are allowed: warn, error no-console - 84:5 warning Unexpected console statement. Only these console methods are allowed: warn, error no-console - 89:7 warning Unexpected console statement. Only these console methods are allowed: warn, error no-console - 93:9 warning Unexpected console statement. Only these console methods are allowed: warn, error no-console - 97:21 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "[PayslipsPage] 鏌ヨ璇锋眰澶辫触:"銆傚缓璁娇鐢? tc('text_z9k3t2') i18n/no-chinese-hardcode - 97:54 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "锛屼娇鐢ㄦā鎷熸暟鎹?銆傚缓璁娇鐢? tc('text_7scak1') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\[locale]\hr\salary\piece-rate\page.tsx - 6:41 warning 'CardTitle' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 8:10 warning 'Label' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 21:38 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 37:21 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\hr\salary\piece-rate\page.tsx:37:21 - 35 | }; - 36 | -> 37 | useEffect(() => { fetchRates(); }, []); - | ^^^^^^^^^^ Avoid calling setState() directly within an effect - 38 | - 39 | return ( - 40 | react-hooks/set-state-in-effect - 37:38 warning React Hook useEffect has a missing dependency: 'fetchRates'. Either include it or remove the dependency array react-hooks/exhaustive-deps - -D:\dcprint\erp-project\src\app\[locale]\hr\salary\piece-work\page.tsx - 5:10 warning 'toast' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 7:41 warning 'CardTitle' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 32:46 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "寮犱笁"銆傚缓璁娇鐢? tc('text_glwp') i18n/no-chinese-hardcode - 33:46 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏉庡洓"銆傚缓璁娇鐢? tc('text_i1ql') i18n/no-chinese-hardcode - 34:46 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鐜嬩簲"銆傚缓璁娇鐢? tc('text_k31l') i18n/no-chinese-hardcode - 35:46 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "寮犱笁"銆傚缓璁娇鐢? tc('text_glwp') i18n/no-chinese-hardcode - 72:21 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\hr\salary\piece-work\page.tsx:72:21 - 70 | }; - 71 | -> 72 | useEffect(() => { fetchRecords(); }, []); - | ^^^^^^^^^^^^ Avoid calling setState() directly within an effect - 73 | - 74 | const totalQuantity = records.reduce((s, r) => s + r.quantity, 0); - 75 | const totalAmount = records.reduce((s, r) => s + r.amount, 0); react-hooks/set-state-in-effect - 72:40 warning React Hook useEffect has a missing dependency: 'fetchRecords'. Either include it or remove the dependency array react-hooks/exhaustive-deps - -D:\dcprint\erp-project\src\app\[locale]\hr\schedules\page.tsx - 7:41 warning 'CardTitle' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 52:10 warning 'employees' is assigned a value but never used. Allowed unused elements of array destructuring must match /^_/u @typescript-eslint/no-unused-vars - 52:21 warning 'setEmployees' is assigned a value but never used. Allowed unused elements of array destructuring must match /^_/u @typescript-eslint/no-unused-vars - 82:21 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\hr\schedules\page.tsx:82:21 - 80 | }; - 81 | -> 82 | useEffect(() => { fetchSchedules(); }, []); - | ^^^^^^^^^^^^^^ Avoid calling setState() directly within an effect - 83 | - 84 | const changeMonth = (offset: number) => { - 85 | const d = new Date(currentMonth + '-01'); react-hooks/set-state-in-effect - -D:\dcprint\erp-project\src\app\[locale]\hr\shifts\page.tsx - 7:41 warning 'CardTitle' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 58:10 warning 'loading' is assigned a value but never used. Allowed unused elements of array destructuring must match /^_/u @typescript-eslint/no-unused-vars - 80:21 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\hr\shifts\page.tsx:80:21 - 78 | }; - 79 | -> 80 | useEffect(() => { fetchShifts(); }, []); - | ^^^^^^^^^^^ Avoid calling setState() directly within an effect - 81 | - 82 | const handleSave = async () => { - 83 | if (!form.shiftName) { react-hooks/set-state-in-effect - 80:39 warning React Hook useEffect has a missing dependency: 'fetchShifts'. Either include it or remove the dependency array react-hooks/exhaustive-deps - -D:\dcprint\erp-project\src\app\[locale]\hr\skills\page.tsx - 109:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\hr\skills\page.tsx:109:5 - 107 | - 108 | useEffect(() => { -> 109 | fetchData(); - | ^^^^^^^^^ Avoid calling setState() directly within an effect - 110 | }, [page]); - 111 | - 112 | const handleSave = async () => { react-hooks/set-state-in-effect - 110:6 warning React Hook useEffect has a missing dependency: 'fetchData'. Either include it or remove the dependency array react-hooks/exhaustive-deps - -D:\dcprint\erp-project\src\app\[locale]\hr\training\page.tsx - 99:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\hr\training\page.tsx:99:5 - 97 | }; - 98 | useEffect(() => { -> 99 | fetchData(); - | ^^^^^^^^^ Avoid calling setState() directly within an effect - 100 | }, [page]); - 101 | - 102 | const handleSave = async () => { react-hooks/set-state-in-effect - 100:6 warning React Hook useEffect has a missing dependency: 'fetchData'. Either include it or remove the dependency array react-hooks/exhaustive-deps - -D:\dcprint\erp-project\src\app\[locale]\layout.tsx - 45:43 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍏徃鍚嶇О"銆傚缓璁娇鐢? tc('text_amf4wf') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\[locale]\login\page.tsx - 16:32 warning 'Sparkles' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 65:25 warning Error: Cannot access refs during render - -React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef). - -D:\dcprint\erp-project\src\app\[locale]\login\page.tsx:65:25 - 63 | }; - 64 | -> 65 | const pupilPosition = calculatePupilPosition(); - | ^^^^^^^^^^^^^^^^^^^^^^ This function accesses a ref value - 66 | - 67 | return ( - 68 |
131 | const pupilPosition = calculatePupilPosition(); - | ^^^^^^^^^^^^^^^^^^^^^^ This function accesses a ref value - 132 | - 133 | return ( - 134 |
{ -> 169 | setMounted(true); - | ^^^^^^^^^^ Avoid calling setState() directly within an effect - 170 | if (!isLoading && isAuthenticated) { - 171 | router.replace('/dashboard'); - 172 | } react-hooks/set-state-in-effect - 253:7 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\login\page.tsx:253:7 - 251 | useEffect(() => { - 252 | if (isTyping) { -> 253 | setIsLookingAtEachOther(true); - | ^^^^^^^^^^^^^^^^^^^^^^^ Avoid calling setState() directly within an effect - 254 | const timer = setTimeout(() => { - 255 | setIsLookingAtEachOther(false); - 256 | }, 800); react-hooks/set-state-in-effect - 280:7 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\login\page.tsx:280:7 - 278 | return () => clearTimeout(firstPeek); - 279 | } else { -> 280 | setIsPurplePeeking(false); - | ^^^^^^^^^^^^^^^^^^ Avoid calling setState() directly within an effect - 281 | } - 282 | }, [loginForm.password, showPassword, isPurplePeeking]); - 283 | react-hooks/set-state-in-effect - 297:21 warning Error: Cannot access refs during render - -React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef). - -D:\dcprint\erp-project\src\app\[locale]\login\page.tsx:297:21 - 295 | }; - 296 | -> 297 | const purplePos = calculatePosition(purpleRef); - | ^^^^^^^^^^^^^^^^^ This function accesses a ref value - 298 | const blackPos = calculatePosition(blackRef); - 299 | const yellowPos = calculatePosition(yellowRef); - 300 | const orangePos = calculatePosition(orangeRef); react-hooks/refs - 298:20 warning Error: Cannot access refs during render - -React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef). - -D:\dcprint\erp-project\src\app\[locale]\login\page.tsx:298:20 - 296 | - 297 | const purplePos = calculatePosition(purpleRef); -> 298 | const blackPos = calculatePosition(blackRef); - | ^^^^^^^^^^^^^^^^^ This function accesses a ref value - 299 | const yellowPos = calculatePosition(yellowRef); - 300 | const orangePos = calculatePosition(orangeRef); - 301 | react-hooks/refs - 299:21 warning Error: Cannot access refs during render - -React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef). - -D:\dcprint\erp-project\src\app\[locale]\login\page.tsx:299:21 - 297 | const purplePos = calculatePosition(purpleRef); - 298 | const blackPos = calculatePosition(blackRef); -> 299 | const yellowPos = calculatePosition(yellowRef); - | ^^^^^^^^^^^^^^^^^ This function accesses a ref value - 300 | const orangePos = calculatePosition(orangeRef); - 301 | - 302 | const handleLogin = async (e: React.FormEvent) => { react-hooks/refs - 300:21 warning Error: Cannot access refs during render - -React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef). - -D:\dcprint\erp-project\src\app\[locale]\login\page.tsx:300:21 - 298 | const blackPos = calculatePosition(blackRef); - 299 | const yellowPos = calculatePosition(yellowRef); -> 300 | const orangePos = calculatePosition(orangeRef); - | ^^^^^^^^^^^^^^^^^ This function accesses a ref value - 301 | - 302 | const handleLogin = async (e: React.FormEvent) => { - 303 | e.preventDefault(); react-hooks/refs - -D:\dcprint\erp-project\src\app\[locale]\material-requisitions\page.tsx - 52:16 warning 'setPage' is assigned a value but never used. Allowed unused elements of array destructuring must match /^_/u @typescript-eslint/no-unused-vars - 53:10 warning 'total' is assigned a value but never used. Allowed unused elements of array destructuring must match /^_/u @typescript-eslint/no-unused-vars - 54:10 warning 'searchNo' is assigned a value but never used. Allowed unused elements of array destructuring must match /^_/u @typescript-eslint/no-unused-vars - 54:20 warning 'setSearchNo' is assigned a value but never used. Allowed unused elements of array destructuring must match /^_/u @typescript-eslint/no-unused-vars - 95:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\material-requisitions\page.tsx:95:5 - 93 | - 94 | useEffect(() => { -> 95 | loadRequisitions(); - | ^^^^^^^^^^^^^^^^ Avoid calling setState() directly within an effect - 96 | }, [page, activeTab]); - 97 | - 98 | const getStatusBadge = (status: number) => { react-hooks/set-state-in-effect - 96:6 warning React Hook useEffect has a missing dependency: 'loadRequisitions'. Either include it or remove the dependency array react-hooks/exhaustive-deps - -D:\dcprint\erp-project\src\app\[locale]\modules\page.tsx - 29:12 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "璁㈠崟绠$悊"銆傚缓璁娇鐢? tc('text_hytrqw') i18n/no-chinese-hardcode - 33:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "閿€鍞鍗曘€佸鎴锋。妗堛€佷骇鍝佹。妗堛€丅OM绠$悊"銆傚缓璁娇鐢? tc('text_5ov82e') i18n/no-chinese-hardcode - 34:16 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "璁㈠崟褰曞叆涓庤窡韪?銆傚缓璁娇鐢? tc('text_5rdb06') i18n/no-chinese-hardcode - 34:27 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "瀹㈡埛淇$敤绠℃帶"銆傚缓璁娇鐢? tc('text_5o6wv2') i18n/no-chinese-hardcode - 34:37 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "浜у搧BOM鐗堟湰绠$悊"銆傚缓璁娇鐢? tc('text_bm9j2n') i18n/no-chinese-hardcode - 34:50 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "搴旀敹璐︽璺熻釜"銆傚缓璁娇鐢? tc('text_fksd2d') i18n/no-chinese-hardcode - 36:16 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "閿€鍞鍗?銆傚缓璁娇鐢? tc('text_j5p8kh') i18n/no-chinese-hardcode - 37:16 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "瀹㈡埛妗f"銆傚缓璁娇鐢? tc('text_byypy2') i18n/no-chinese-hardcode - 38:16 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "浜у搧妗f"銆傚缓璁娇鐢? tc('text_aa1xa7') i18n/no-chinese-hardcode - 39:16 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "BOM绠$悊"銆傚缓璁娇鐢? tc('text_12c46d') i18n/no-chinese-hardcode - 41:13 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "宸插疄鐜?銆傚缓璁娇鐢? tc('text_e7l8k') i18n/no-chinese-hardcode - 44:12 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鎵撴牱涓績"銆傚缓璁娇鐢? tc('text_cu3n96') i18n/no-chinese-hardcode - 48:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鎵撴牱鐢宠銆佹墦鏍峰伐鍗曘€佹牱鍝佺鐞?銆傚缓璁娇鐢? tc('text_43db31') i18n/no-chinese-hardcode - 49:16 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鎵撴牱鐢宠娴佺▼"銆傚缓璁娇鐢? tc('text_iq2kz2') i18n/no-chinese-hardcode - 49:26 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鎵撴牱宸ュ崟绠$悊"銆傚缓璁娇鐢? tc('text_lw48br') i18n/no-chinese-hardcode - 49:36 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鎴愭湰褰掗泦"銆傚缓璁娇鐢? tc('text_csul00') i18n/no-chinese-hardcode - 49:44 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏍峰搧杞噺浜?銆傚缓璁娇鐢? tc('text_7expc6') i18n/no-chinese-hardcode - 51:16 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鎵撴牱鐢宠"銆傚缓璁娇鐢? tc('text_cuaiy0') i18n/no-chinese-hardcode - 52:16 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鎵撴牱宸ュ崟"銆傚缓璁娇鐢? tc('text_cu691w') i18n/no-chinese-hardcode - 54:13 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "妗嗘灦宸叉惌寤?銆傚缓璁娇鐢? tc('text_502lap') i18n/no-chinese-hardcode - 57:12 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "浠撳簱绠$悊"銆傚缓璁娇鐢? tc('text_acd50l') i18n/no-chinese-hardcode - 61:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍥涗粨鍒嗙銆佹壒娆¤拷婧€佸厛杩涘厛鍑恒€佸簱瀛橀璀?銆傚缓璁娇鐢? tc('text_pr07hs') i18n/no-chinese-hardcode - 62:16 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍥涗粨鍒嗙绠$悊"銆傚缓璁娇鐢? tc('text_kza5f2') i18n/no-chinese-hardcode - 62:26 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鎵规鏁堟湡绠$悊"銆傚缓璁娇鐢? tc('text_8i7q04') i18n/no-chinese-hardcode - 62:36 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鎵规琛€缂樼户鎵?銆傚缓璁娇鐢? tc('text_d0intk') i18n/no-chinese-hardcode - 62:46 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "瀹夊叏搴撳瓨棰勮"銆傚缓璁娇鐢? tc('text_ag2yii') i18n/no-chinese-hardcode - 64:16 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "搴撳瓨鏌ヨ"銆傚缓璁娇鐢? tc('text_cbberm') i18n/no-chinese-hardcode - 65:16 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍏ュ簱绠$悊"銆傚缓璁娇鐢? tc('text_ao1adv') i18n/no-chinese-hardcode - 66:16 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍑哄簱绠$悊"銆傚缓璁娇鐢? tc('text_aqoffi') i18n/no-chinese-hardcode - 67:16 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "浠撳簱璁剧疆"銆傚缓璁娇鐢? tc('text_acfxxs') i18n/no-chinese-hardcode - 69:13 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "宸插疄鐜?銆傚缓璁娇鐢? tc('text_e7l8k') i18n/no-chinese-hardcode - 72:12 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鐢熶骇绠$悊"銆傚缓璁娇鐢? tc('text_f3xa0d') i18n/no-chinese-hardcode - 76:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "宸ュ崟绠$悊銆佺敓浜ф姤宸ャ€佹晥鐜囬璀︺€佽澶囩鐞?銆傚缓璁娇鐢? tc('text_ftt4ox') i18n/no-chinese-hardcode - 77:16 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "宸ュ崟鎺掍骇璋冨害"銆傚缓璁娇鐢? tc('text_hhi3c') i18n/no-chinese-hardcode - 77:26 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鎶ュ伐杩涘害璺熻釜"銆傚缓璁娇鐢? tc('text_h2i0ei') i18n/no-chinese-hardcode - 77:36 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏁堢巼瀹炴椂鐩戞帶"銆傚缓璁娇鐢? tc('text_pe4mvh') i18n/no-chinese-hardcode - 77:46 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "璁惧OEE鍒嗘瀽"銆傚缓璁娇鐢? tc('text_enl2s0') i18n/no-chinese-hardcode - 79:16 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鐢熶骇宸ュ崟"銆傚缓璁娇鐢? tc('text_f3s1h4') i18n/no-chinese-hardcode - 80:16 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鐢熶骇鎶ュ伐"銆傚缓璁娇鐢? tc('text_f3swnc') i18n/no-chinese-hardcode - 81:16 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "璁惧绠$悊"銆傚缓璁娇鐢? tc('text_i05oi6') i18n/no-chinese-hardcode - 82:16 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍛樺伐绠$悊"銆傚缓璁娇鐢? tc('text_b1bsgi') i18n/no-chinese-hardcode - 84:13 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "宸插疄鐜?銆傚缓璁娇鐢? tc('text_e7l8k') i18n/no-chinese-hardcode - 87:12 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍝佽川绠$悊"銆傚缓璁娇鐢? tc('text_ba41n0') i18n/no-chinese-hardcode - 91:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏉ユ枡妫€楠屻€侀浠剁‘璁ゃ€佸贰妫€SPC銆佸敭鍚庤拷婧?銆傚缓璁娇鐢? tc('text_9xmiui') i18n/no-chinese-hardcode - 92:16 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍏ㄦ祦绋嬫嫤鎴?銆傚缓璁娇鐢? tc('text_mkg3ze') i18n/no-chinese-hardcode - 92:25 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "SPC缁熻鍒嗘瀽"銆傚缓璁娇鐢? tc('text_4tq0pa') i18n/no-chinese-hardcode - 92:36 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鑶滃帤/鑹插樊璁板綍"銆傚缓璁娇鐢? tc('text_b8mcsy') i18n/no-chinese-hardcode - 92:47 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍞悗杩芥函"銆傚缓璁娇鐢? tc('text_b3rzea') i18n/no-chinese-hardcode - 94:16 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "杩芥函鏌ヨ"銆傚缓璁娇鐢? tc('text_imiq27') i18n/no-chinese-hardcode - 95:16 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏉ユ枡妫€楠?銆傚缓璁娇鐢? tc('text_dgvhr4') i18n/no-chinese-hardcode - 96:16 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "宸℃璁板綍"銆傚缓璁娇鐢? tc('text_caaack') i18n/no-chinese-hardcode - 97:16 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "涓嶈壇澶勭悊"銆傚缓璁娇鐢? tc('text_adxwqs') i18n/no-chinese-hardcode - 99:13 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "宸插疄鐜?銆傚缓璁娇鐢? tc('text_e7l8k') i18n/no-chinese-hardcode - 102:12 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "閲囪喘绠$悊"銆傚缓璁娇鐢? tc('text_iz76ff') i18n/no-chinese-hardcode - 106:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "閲囪喘鐢宠銆侀噰璐鍗曘€佷緵搴斿晢鍗忓悓銆丳DF閫佽揣鐮?銆傚缓璁娇鐢? tc('text_rx4tn3') i18n/no-chinese-hardcode - 107:16 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍙屾潵婧愯璐?銆傚缓璁娇鐢? tc('text_e014ib') i18n/no-chinese-hardcode - 107:25 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "渚涘簲鍟嗚瘎绾?銆傚缓璁娇鐢? tc('text_vlqz80') i18n/no-chinese-hardcode - 107:34 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "PDF鎵爜閫佽揣"銆傚缓璁娇鐢? tc('text_cvjcsu') i18n/no-chinese-hardcode - 107:45 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "瀵硅处浠樻"銆傚缓璁娇鐢? tc('text_c6obxv') i18n/no-chinese-hardcode - 109:16 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "閲囪喘璁㈠崟"銆傚缓璁娇鐢? tc('text_iz9pyx') i18n/no-chinese-hardcode - 110:16 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "閲囪喘鐢宠"銆傚缓璁娇鐢? tc('text_iz67sa') i18n/no-chinese-hardcode - 111:16 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "渚涘簲鍟嗙鐞?銆傚缓璁娇鐢? tc('text_vlts4u') i18n/no-chinese-hardcode - 112:16 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鐗╂枡妗f"銆傚缓璁娇鐢? tc('text_euvslx') i18n/no-chinese-hardcode - 114:13 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "宸插疄鐜?銆傚缓璁娇鐢? tc('text_e7l8k') i18n/no-chinese-hardcode - 117:12 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "濮斿绠$悊"銆傚缓璁娇鐢? tc('text_bpix8n') i18n/no-chinese-hardcode - 121:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "濮斿宸ュ崟銆佸澶栬窡韪€佹崯鑰楃鐞嗐€佸簲浠橀攣瀹?銆傚缓璁娇鐢? tc('text_vgs7pg') i18n/no-chinese-hardcode - 122:16 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "濮斿浜岀淮鐮佽窡韪?銆傚缓璁娇鐢? tc('text_u3yf7m') i18n/no-chinese-hardcode - 122:27 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "濮斿鎹熻€楃鐞?銆傚缓璁娇鐢? tc('text_qikpgf') i18n/no-chinese-hardcode - 122:37 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "澶栧崗鍘傛墜鏈哄洖璐?銆傚缓璁娇鐢? tc('text_xq7whr') i18n/no-chinese-hardcode - 122:48 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鎴愭湰褰掗泦"銆傚缓璁娇鐢? tc('text_csul00') i18n/no-chinese-hardcode - 124:16 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "濮斿宸ュ崟"銆傚缓璁娇鐢? tc('text_bpdope') i18n/no-chinese-hardcode - 125:16 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "濮斿璺熻釜"銆傚缓璁娇鐢? tc('text_bpm63x') i18n/no-chinese-hardcode - 127:13 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "妗嗘灦宸叉惌寤?銆傚缓璁娇鐢? tc('text_502lap') i18n/no-chinese-hardcode - 130:12 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "杞﹁締娲鹃€?銆傚缓璁娇鐢? tc('text_iooehv') i18n/no-chinese-hardcode - 134:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏅鸿兘鎺掕溅銆佽杞︾‘璁ゃ€佺數瀛愬洖鍗曘€佸璐︽敮鎸?銆傚缓璁娇鐢? tc('text_5j21ny') i18n/no-chinese-hardcode - 135:16 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏅鸿兘杞﹀瀷鎺ㄨ崘"銆傚缓璁娇鐢? tc('text_ga90gg') i18n/no-chinese-hardcode - 135:26 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "瑁呰溅纭"銆傚缓璁娇鐢? tc('text_hum1o7') i18n/no-chinese-hardcode - 135:34 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鐢靛瓙鍥炲崟涓婁紶"銆傚缓璁娇鐢? tc('text_dky0p4') i18n/no-chinese-hardcode - 135:44 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "瀵硅处鏀寔"銆傚缓璁娇鐢? tc('text_c6s33z') i18n/no-chinese-hardcode - 137:16 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "娲捐溅璁″垝"銆傚缓璁娇鐢? tc('text_edpbax') i18n/no-chinese-hardcode - 138:16 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "瑁呰溅绠$悊"銆傚缓璁娇鐢? tc('text_humgli') i18n/no-chinese-hardcode - 139:16 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍥炲崟绠$悊"銆傚缓璁娇鐢? tc('text_bb8kxo') i18n/no-chinese-hardcode - 141:13 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "妗嗘灦宸叉惌寤?銆傚缓璁娇鐢? tc('text_502lap') i18n/no-chinese-hardcode - 144:12 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "璁惧绠$悊"銆傚缓璁娇鐢? tc('text_i05oi6') i18n/no-chinese-hardcode - 148:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "璁惧妗f銆佷繚鍏昏鍒掋€佸浠剁鐞嗐€佹晠闅滄姤淇?銆傚缓璁娇鐢? tc('text_aubvbm') i18n/no-chinese-hardcode - 149:16 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "涓€鏈轰竴妗d竴鐮?銆傚缓璁娇鐢? tc('text_530qaq') i18n/no-chinese-hardcode - 149:26 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "淇濆吇璁″垝绠$悊"銆傚缓璁娇鐢? tc('text_1d2az8') i18n/no-chinese-hardcode - 149:36 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏈繚鍏婚攣瀹氬伐鍗?銆傚缓璁娇鐢? tc('text_oo429d') i18n/no-chinese-hardcode - 149:47 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "澶囦欢绠$悊"銆傚缓璁娇鐢? tc('text_bkemxg') i18n/no-chinese-hardcode - 151:16 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "璁惧妗f"銆傚缓璁娇鐢? tc('text_i02ccu') i18n/no-chinese-hardcode - 152:16 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "淇濆吇璁″垝"銆傚缓璁娇鐢? tc('text_af8h8v') i18n/no-chinese-hardcode - 154:13 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "妗嗘灦宸叉惌寤?銆傚缓璁娇鐢? tc('text_502lap') i18n/no-chinese-hardcode - 157:12 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏁版嵁鐪嬫澘"銆傚缓璁娇鐢? tc('text_d7qb82') i18n/no-chinese-hardcode - 161:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鑰佹澘椹鹃┒鑸便€佺敓浜х湅鏉裤€佷粨搴撶湅鏉裤€佸搧璐ㄧ湅鏉?銆傚缓璁娇鐢? tc('text_ajqmnr') i18n/no-chinese-hardcode - 162:16 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "瀹炴椂鏁版嵁澶у睆"銆傚缓璁娇鐢? tc('text_9q1xni') i18n/no-chinese-hardcode - 162:26 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鐢熶骇鐪嬫澘"銆傚缓璁娇鐢? tc('text_f3wfgc') i18n/no-chinese-hardcode - 162:34 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "搴撳瓨鍛ㄨ浆鍒嗘瀽"銆傚缓璁娇鐢? tc('text_uhsdu5') i18n/no-chinese-hardcode - 162:44 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍝佽川鐪嬫澘"銆傚缓璁娇鐢? tc('text_ba372z') i18n/no-chinese-hardcode - 164:16 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鑰佹澘椹鹃┒鑸?銆傚缓璁娇鐢? tc('text_gz91y3') i18n/no-chinese-hardcode - 165:16 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鐢熶骇鐪嬫澘"銆傚缓璁娇鐢? tc('text_f3wfgc') i18n/no-chinese-hardcode - 166:16 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "浠撳簱鐪嬫澘"銆傚缓璁娇鐢? tc('text_accagk') i18n/no-chinese-hardcode - 168:13 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "宸插疄鐜?銆傚缓璁娇鐢? tc('text_e7l8k') i18n/no-chinese-hardcode - 174:28 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "浠撶鍛?銆傚缓璁娇鐢? tc('text_c54oa') i18n/no-chinese-hardcode - 174:42 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏀惰揣涓婃灦"銆傚缓璁娇鐢? tc('text_dcmb19') i18n/no-chinese-hardcode - 174:58 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鎵弿閫佽揣鍗曠爜+瀹炵墿鐮侊紝涓婃灦"銆傚缓璁娇鐢? tc('text_fgm6vd') i18n/no-chinese-hardcode - 175:28 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鐗╂枡鍛?銆傚缓璁娇鐢? tc('text_h90y0') i18n/no-chinese-hardcode - 175:42 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鐢熶骇棰嗘枡"銆傚缓璁娇鐢? tc('text_f4243f') i18n/no-chinese-hardcode - 175:58 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鎵伐鍗曠爜锛屾寜鍏堣繘鍏堝嚭鍙栬揣"銆傚缓璁娇鐢? tc('text_72zcqv') i18n/no-chinese-hardcode - 177:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鎵嬫満"銆傚缓璁娇鐢? tc('text_haa7') i18n/no-chinese-hardcode - 178:11 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "涓氬姟鍛?銆傚缓璁娇鐢? tc('text_bucfl') i18n/no-chinese-hardcode - 179:12 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "杩涘害鏌ヨ"銆傚缓璁娇鐢? tc('text_ijkglk') i18n/no-chinese-hardcode - 180:13 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鎵鍗昉DF瀛樺浘锛岀湅瀹屾暣鑺傜偣"銆傚缓璁娇鐢? tc('text_16jx4o') i18n/no-chinese-hardcode - 183:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鎵嬫満"銆傚缓璁娇鐢? tc('text_haa7') i18n/no-chinese-hardcode - 184:11 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鑰佹澘"銆傚缓璁娇鐢? tc('text_mc9q') i18n/no-chinese-hardcode - 185:12 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "宸″巶杩芥函"銆傚缓璁娇鐢? tc('text_c773df') i18n/no-chinese-hardcode - 186:13 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鎵换鎰忓湪鍒跺搧鐮侊紝绌块€忚拷婧?銆傚缓璁娇鐢? tc('text_2stggv') i18n/no-chinese-hardcode - 189:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鎵嬫満"銆傚缓璁娇鐢? tc('text_haa7') i18n/no-chinese-hardcode - 190:11 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "渚涘簲鍟?銆傚缓璁娇鐢? tc('text_c4b9p') i18n/no-chinese-hardcode - 191:12 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "閫佽揣鍗忓悓"銆傚缓璁娇鐢? tc('text_iqy4pv') i18n/no-chinese-hardcode - 192:13 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鎵玃DF鐮侊紝濉彂璐т俊鎭?銆傚缓璁娇鐢? tc('text_jxfbh9') i18n/no-chinese-hardcode - 195:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鎵嬫満"銆傚缓璁娇鐢? tc('text_haa7') i18n/no-chinese-hardcode - 196:11 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "澶栧崗鍘?銆傚缓璁娇鐢? tc('text_dgdk9') i18n/no-chinese-hardcode - 197:12 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "濮斿鍥炶揣"銆傚缓璁娇鐢? tc('text_bpct3f') i18n/no-chinese-hardcode - 198:13 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鎵澶栫爜锛岃緭鏁伴噺鎷嶇収"銆傚缓璁娇鐢? tc('text_vjb5as') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\[locale]\monitoring\consistency\page.tsx - 171:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\monitoring\consistency\page.tsx:171:5 - 169 | - 170 | useEffect(() => { -> 171 | fetchData(); - | ^^^^^^^^^ Avoid calling setState() directly within an effect - 172 | const interval = setInterval(fetchData, 30000); - 173 | return () => clearInterval(interval); - 174 | }, []); react-hooks/set-state-in-effect - -D:\dcprint\erp-project\src\app\[locale]\not-found.tsx - 14:85 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "椤甸潰涓嶅瓨鍦?銆傚缓璁娇鐢? {tc('text_nr1d00')} i18n/no-chinese-hardcode - 15:63 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鎮ㄨ闂殑椤甸潰鍙兘宸茶绉诲姩鎴栧垹闄?銆傚缓璁娇鐢? {tc('text_liwqgc')} i18n/no-chinese-hardcode - 22:39 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "杩斿洖棣栭〉"銆傚缓璁娇鐢? {tc('text_iijhd5')} i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\[locale]\orders\bom\create\page.tsx - 66:12 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "浠?銆傚缓璁娇鐢? tc('text_fli') i18n/no-chinese-hardcode - 67:12 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "涓?銆傚缓璁娇鐢? tc('text_ffu') i18n/no-chinese-hardcode - 68:12 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "濂?銆傚缓璁娇鐢? tc('text_hnb') i18n/no-chinese-hardcode - 69:12 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "绫?銆傚缓璁娇鐢? tc('text_okz') i18n/no-chinese-hardcode - 70:12 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍗冨厠"銆傚缓璁娇鐢? tc('text_elwo') i18n/no-chinese-hardcode - 71:12 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍏?銆傚缓璁娇鐢? tc('text_g23') i18n/no-chinese-hardcode - 72:12 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "姣崌"銆傚缓璁娇鐢? tc('text_ita4') i18n/no-chinese-hardcode - 73:12 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍗?銆傚缓璁娇鐢? tc('text_gg7') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\[locale]\orders\bom\page.tsx - 206:18 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "閿欒"銆傚缓璁娇鐢? tc('text_q4mu') i18n/no-chinese-hardcode - 213:16 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "閿欒"銆傚缓璁娇鐢? tc('text_q4mu') i18n/no-chinese-hardcode - 226:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\orders\bom\page.tsx:226:5 - 224 | - 225 | useEffect(() => { -> 226 | fetchBOMList(); - | ^^^^^^^^^^^^ Avoid calling setState() directly within an effect - 227 | }, [currentPage]); - 228 | - 229 | return ( react-hooks/set-state-in-effect - 227:6 warning React Hook useEffect has a missing dependency: 'fetchBOMList'. Either include it or remove the dependency array react-hooks/exhaustive-deps - -D:\dcprint\erp-project\src\app\[locale]\orders\customers\page.tsx - 132:5 warning Error: Cannot access variable before it is declared - -`fetchCustomers` is accessed before it is declared, which prevents the earlier access from updating when this value changes over time. - -D:\dcprint\erp-project\src\app\[locale]\orders\customers\page.tsx:132:5 - 130 | // 浠庢暟鎹簱鍔犺浇瀹㈡埛鏁版嵁 - 131 | useEffect(() => { -> 132 | fetchCustomers(); - | ^^^^^^^^^^^^^^ `fetchCustomers` accessed before it is declared - 133 | }, [currentPage, statusFilter, customerTypeFilter, followUpStatusFilter]); - 134 | - 135 | // 闃叉姈鎼滅储锛氭悳绱㈣瘝鍙樺寲鏃惰嚜鍔ㄨЕ鍙戞悳绱? -D:\dcprint\erp-project\src\app\[locale]\orders\customers\page.tsx:149:3 - 147 | }, [searchTerm]); - 148 | -> 149 | const fetchCustomers = async () => { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 150 | try { - | ^^^^^^^^^ -> 151 | setLoading(true); - 鈥? | ^^^^^^^^^ -> 210 | } - | ^^^^^^^^^ -> 211 | }; - | ^^^^^ `fetchCustomers` is declared here - 212 | - 213 | // 鎼滅储澶勭悊 - 214 | const handleSearch = () => { react-hooks/immutability - 133:6 warning React Hook useEffect has a missing dependency: 'fetchCustomers'. Either include it or remove the dependency array react-hooks/exhaustive-deps - 147:6 warning React Hook useEffect has a missing dependency: 'fetchCustomers'. Either include it or remove the dependency array react-hooks/exhaustive-deps - 549:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "瀹㈡埛鍒楄〃"銆傚缓璁娇鐢? {tc('text_byv3ra')} i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\[locale]\orders\products\page.tsx - 237:6 warning React Hook useCallback has a missing dependency: 't'. Either include it or remove the dependency array react-hooks/exhaustive-deps - 250:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\orders\products\page.tsx:250:5 - 248 | - 249 | useEffect(() => { -> 250 | fetchProducts(); - | ^^^^^^^^^^^^^ Avoid calling setState() directly within an effect - 251 | }, [fetchProducts]); - 252 | - 253 | useEffect(() => { react-hooks/set-state-in-effect - 254:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\orders\products\page.tsx:254:5 - 252 | - 253 | useEffect(() => { -> 254 | fetchCategories(); - | ^^^^^^^^^^^^^^^ Avoid calling setState() directly within an effect - 255 | }, [fetchCategories]); - 256 | - 257 | useEffect(() => { react-hooks/set-state-in-effect - 753:41 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "寮?銆傚缓璁娇鐢? {tc('text_isg')} i18n/no-chinese-hardcode - 755:41 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍗?銆傚缓璁娇鐢? {tc('text_ghj')} i18n/no-chinese-hardcode - 756:41 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "浠?銆傚缓璁娇鐢? {tc('text_fli')} i18n/no-chinese-hardcode - 884:41 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "寮?銆傚缓璁娇鐢? {tc('text_isg')} i18n/no-chinese-hardcode - 886:41 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍗?銆傚缓璁娇鐢? {tc('text_ghj')} i18n/no-chinese-hardcode - 887:41 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "浠?銆傚缓璁娇鐢? {tc('text_fli')} i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\[locale]\orders\sales\page.tsx - 237:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\orders\sales\page.tsx:237:5 - 235 | - 236 | useEffect(() => { -> 237 | fetchOrders(); - | ^^^^^^^^^^^ Avoid calling setState() directly within an effect - 238 | fetchCustomers(); - 239 | fetchMaterials(); - 240 | }, []); react-hooks/set-state-in-effect - 240:6 warning React Hook useEffect has a missing dependency: 'fetchOrders'. Either include it or remove the dependency array react-hooks/exhaustive-deps - 245:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\orders\sales\page.tsx:245:5 - 243 | - 244 | useEffect(() => { -> 245 | fetchOrders(debouncedSearchKeyword, statusFilter); - | ^^^^^^^^^^^ Avoid calling setState() directly within an effect - 246 | }, [debouncedSearchKeyword, statusFilter, fetchOrders]); - 247 | - 248 | const handleSort = (field: SortField) => { react-hooks/set-state-in-effect - 485:9 warning 'handleExport' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 884:28 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "閿€鍞鍗?銆傚缓璁娇鐢? {tc('text_j5p8kh')} i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\[locale]\outsource\issue\page.tsx - 143:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\outsource\issue\page.tsx:143:5 - 141 | - 142 | useEffect(() => { -> 143 | fetchData(); - | ^^^^^^^^^ Avoid calling setState() directly within an effect - 144 | }, [page]); - 145 | useEffect(() => { - 146 | fetchOutsourceOrders(); react-hooks/set-state-in-effect - 144:6 warning React Hook useEffect has a missing dependency: 'fetchData'. Either include it or remove the dependency array react-hooks/exhaustive-deps - 146:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\outsource\issue\page.tsx:146:5 - 144 | }, [page]); - 145 | useEffect(() => { -> 146 | fetchOutsourceOrders(); - | ^^^^^^^^^^^^^^^^^^^^ Avoid calling setState() directly within an effect - 147 | fetchMaterials(); - 148 | }, []); - 149 | react-hooks/set-state-in-effect - 252:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "澶栧崗鍙戞枡"銆傚缓璁娇鐢? {tc('text_bl4ae9')} i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\[locale]\outsource\order\page.tsx - 115:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\outsource\order\page.tsx:115:5 - 113 | - 114 | useEffect(() => { -> 115 | fetchData(); - | ^^^^^^^^^ Avoid calling setState() directly within an effect - 116 | }, [page, searchStatus]); - 117 | useEffect(() => { - 118 | fetchSuppliers(); react-hooks/set-state-in-effect - 116:6 warning React Hook useEffect has a missing dependency: 'fetchData'. Either include it or remove the dependency array react-hooks/exhaustive-deps - 118:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\outsource\order\page.tsx:118:5 - 116 | }, [page, searchStatus]); - 117 | useEffect(() => { -> 118 | fetchSuppliers(); - | ^^^^^^^^^^^^^^ Avoid calling setState() directly within an effect - 119 | }, []); - 120 | - 121 | const handleSave = async () => { react-hooks/set-state-in-effect - 174:9 warning 'formatAmount' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - -D:\dcprint\erp-project\src\app\[locale]\outsource\receive\page.tsx - 113:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\outsource\receive\page.tsx:113:5 - 111 | - 112 | useEffect(() => { -> 113 | fetchData(); - | ^^^^^^^^^ Avoid calling setState() directly within an effect - 114 | }, [page]); - 115 | useEffect(() => { - 116 | fetchOutsourceOrders(); react-hooks/set-state-in-effect - 114:6 warning React Hook useEffect has a missing dependency: 'fetchData'. Either include it or remove the dependency array react-hooks/exhaustive-deps - 116:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\outsource\receive\page.tsx:116:5 - 114 | }, [page]); - 115 | useEffect(() => { -> 116 | fetchOutsourceOrders(); - | ^^^^^^^^^^^^^^^^^^^^ Avoid calling setState() directly within an effect - 117 | }, []); - 118 | - 119 | const handleSave = async () => { react-hooks/set-state-in-effect - -D:\dcprint\erp-project\src\app\[locale]\outsource\settlement\page.tsx - 113:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\outsource\settlement\page.tsx:113:5 - 111 | - 112 | useEffect(() => { -> 113 | fetchData(); - | ^^^^^^^^^ Avoid calling setState() directly within an effect - 114 | }, [page]); - 115 | useEffect(() => { - 116 | fetchOutsourceOrders(); react-hooks/set-state-in-effect - 114:6 warning React Hook useEffect has a missing dependency: 'fetchData'. Either include it or remove the dependency array react-hooks/exhaustive-deps - 116:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\outsource\settlement\page.tsx:116:5 - 114 | }, [page]); - 115 | useEffect(() => { -> 116 | fetchOutsourceOrders(); - | ^^^^^^^^^^^^^^^^^^^^ Avoid calling setState() directly within an effect - 117 | }, []); - 118 | - 119 | const handleSave = async () => { react-hooks/set-state-in-effect - -D:\dcprint\erp-project\src\app\[locale]\page.tsx - 200:6 warning React Hook useEffect has a missing dependency: 't'. Either include it or remove the dependency array react-hooks/exhaustive-deps - 203:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\page.tsx:203:5 - 201 | - 202 | useEffect(() => { -> 203 | setCurrentTime(new Date()); - | ^^^^^^^^^^^^^^ Avoid calling setState() directly within an effect - 204 | const timer = setInterval(() => setCurrentTime(new Date()), 1000); - 205 | return () => clearInterval(timer); - 206 | }, []); react-hooks/set-state-in-effect - -D:\dcprint\erp-project\src\app\[locale]\plm\eco\page.tsx - 88:10 warning 'loading' is assigned a value but never used. Allowed unused elements of array destructuring must match /^_/u @typescript-eslint/no-unused-vars - 127:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\plm\eco\page.tsx:127:5 - 125 | - 126 | useEffect(() => { -> 127 | fetchData(); - | ^^^^^^^^^ Avoid calling setState() directly within an effect - 128 | }, [page]); - 129 | - 130 | const handleSave = async () => { react-hooks/set-state-in-effect - 128:6 warning React Hook useEffect has a missing dependency: 'fetchData'. Either include it or remove the dependency array react-hooks/exhaustive-deps - -D:\dcprint\erp-project\src\app\[locale]\plm\lifecycle\page.tsx - 96:10 warning 'loading' is assigned a value but never used. Allowed unused elements of array destructuring must match /^_/u @typescript-eslint/no-unused-vars - 134:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\plm\lifecycle\page.tsx:134:5 - 132 | - 133 | useEffect(() => { -> 134 | fetchData(); - | ^^^^^^^^^ Avoid calling setState() directly within an effect - 135 | }, [page]); - 136 | - 137 | const handleSave = async () => { react-hooks/set-state-in-effect - 135:6 warning React Hook useEffect has a missing dependency: 'fetchData'. Either include it or remove the dependency array react-hooks/exhaustive-deps - -D:\dcprint\erp-project\src\app\[locale]\prepress\die-template\page.tsx - 41:3 warning 'Shield' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 48:3 warning 'QrCode' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 50:3 warning 'BarChart3' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 51:3 warning 'Clock' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 61:3 warning 'TableExportToolbar' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 62:3 warning 'printTable' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 63:3 warning 'exportTableToPDF' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 64:3 warning 'exportTableToXLS' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 65:3 warning 'exportTableToWORD' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 68:15 warning 'ExportColumn' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 72:52 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 73:63 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 74:41 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 75:32 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 127:10 warning 'loading' is assigned a value but never used. Allowed unused elements of array destructuring must match /^_/u @typescript-eslint/no-unused-vars - 130:24 warning 'setStatusFilter' is assigned a value but never used. Allowed unused elements of array destructuring must match /^_/u @typescript-eslint/no-unused-vars - 200:6 warning React Hook useCallback has a missing dependency: 'toast'. Either include it or remove the dependency array react-hooks/exhaustive-deps - 212:6 warning React Hook useCallback has a missing dependency: 'toast'. Either include it or remove the dependency array react-hooks/exhaustive-deps - 224:6 warning React Hook useCallback has a missing dependency: 'toast'. Either include it or remove the dependency array react-hooks/exhaustive-deps - 227:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\prepress\die-template\page.tsx:227:5 - 225 | - 226 | useEffect(() => { -> 227 | fetchList(); - | ^^^^^^^^^ Avoid calling setState() directly within an effect - 228 | }, [fetchList]); - 229 | - 230 | useEffect(() => { react-hooks/set-state-in-effect - 231:38 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\prepress\die-template\page.tsx:231:38 - 229 | - 230 | useEffect(() => { -> 231 | if (activeTab === 'maintenance') fetchMaintenanceList(); - | ^^^^^^^^^^^^^^^^^^^^ Avoid calling setState() directly within an effect - 232 | if (activeTab === 'usage') fetchUsageLogs(); - 233 | }, [activeTab, fetchMaintenanceList, fetchUsageLogs]); - 234 | react-hooks/set-state-in-effect - 281:9 warning 'exportColumns' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 292:9 warning 'getExportData' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 554:9 warning 'handleDelete' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 960:28 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍒€妯℃ā鏉垮垪琛?銆傚缓璁娇鐢? {tc('text_2kivqo')} i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\[locale]\production\material-issue\page.tsx - 69:9 warning 'locale' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 91:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\production\material-issue\page.tsx:91:5 - 89 | }; - 90 | useEffect(() => { -> 91 | fetchData(); - | ^^^^^^^^^ Avoid calling setState() directly within an effect - 92 | }, [page]); - 93 | - 94 | const handleSave = async () => { react-hooks/set-state-in-effect - 92:6 warning React Hook useEffect has a missing dependency: 'fetchData'. Either include it or remove the dependency array react-hooks/exhaustive-deps - -D:\dcprint\erp-project\src\app\[locale]\production\material-return\page.tsx - 56:9 warning 'locale' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 82:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\production\material-return\page.tsx:82:5 - 80 | }; - 81 | useEffect(() => { -> 82 | fetchData(); - | ^^^^^^^^^ Avoid calling setState() directly within an effect - 83 | }, [page]); - 84 | - 85 | const handleSave = async () => { react-hooks/set-state-in-effect - 83:6 warning React Hook useEffect has a missing dependency: 'fetchData'. Either include it or remove the dependency array react-hooks/exhaustive-deps - -D:\dcprint\erp-project\src\app\[locale]\production\mrp\page.tsx - 245:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\production\mrp\page.tsx:245:5 - 243 | - 244 | useEffect(() => { -> 245 | fetchWorkOrders(); - | ^^^^^^^^^^^^^^^ Avoid calling setState() directly within an effect - 246 | fetchMaterials(); - 247 | }, [fetchWorkOrders, fetchMaterials]); - 248 | react-hooks/set-state-in-effect - -D:\dcprint\erp-project\src\app\[locale]\production\orders\page.tsx - 52:14 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍖呰鑶?閫忔槑"銆傚缓璁娇鐢? tc('text_u9m7f4') i18n/no-chinese-hardcode - 53:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "娣卞湷浼熶笟"銆傚缓璁娇鐢? tc('text_e8cyil') i18n/no-chinese-hardcode - 61:21 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍗板埛"銆傚缓璁娇鐢? tc('text_en5z') i18n/no-chinese-hardcode - 68:14 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏍囩璐寸焊"銆傚缓璁娇鐢? tc('text_dn4fiz') i18n/no-chinese-hardcode - 69:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "骞垮窞鍗庤揪"銆傚缓璁娇鐢? tc('text_cb8gq7') i18n/no-chinese-hardcode - 73:11 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "寮?銆傚缓璁娇鐢? tc('text_isg') i18n/no-chinese-hardcode - 77:21 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "妯″垏"銆傚缓璁娇鐢? tc('text_ii2u') i18n/no-chinese-hardcode - 84:14 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "褰╁嵃鑶?钃?銆傚缓璁娇鐢? tc('text_sy5lk5') i18n/no-chinese-hardcode - 85:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "涓滆帪鎭掗€?銆傚缓璁娇鐢? tc('text_aef4i2') i18n/no-chinese-hardcode - 93:21 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "寰呭紑宸?銆傚缓璁娇鐢? tc('text_egch6') i18n/no-chinese-hardcode - 100:14 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鐑敹缂╄啘"銆傚缓璁娇鐢? tc('text_eo74n0') i18n/no-chinese-hardcode - 101:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "涓北鏂版潗"銆傚缓璁娇鐢? tc('text_a903gk') i18n/no-chinese-hardcode - 109:21 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "妫€楠?銆傚缓璁娇鐢? tc('text_inyk') i18n/no-chinese-hardcode - 116:14 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "闃查潤鐢佃啘"銆傚缓璁娇鐢? tc('text_jkpyum') i18n/no-chinese-hardcode - 117:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "浣涘北鍒╄揪"銆傚缓璁娇鐢? tc('text_ae9twr') i18n/no-chinese-hardcode - 125:21 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "宸插畬鎴?銆傚缓璁娇鐢? tc('text_e7hbq') i18n/no-chinese-hardcode - 132:24 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍒囨枡"銆傚缓璁娇鐢? tc('text_eicy') i18n/no-chinese-hardcode - 133:24 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "纾ㄥ垏"銆傚缓璁娇鐢? tc('text_l0kf') i18n/no-chinese-hardcode - 134:24 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍗板埛"銆傚缓璁娇鐢? tc('text_en5z') i18n/no-chinese-hardcode - 135:24 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鐑樺共"銆傚缓璁娇鐢? tc('text_jpne') i18n/no-chinese-hardcode - 136:24 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "妯″垏"銆傚缓璁娇鐢? tc('text_ii2u') i18n/no-chinese-hardcode - 137:24 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "妫€楠?銆傚缓璁娇鐢? tc('text_inyk') i18n/no-chinese-hardcode - 138:24 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍖呰"銆傚缓璁娇鐢? tc('text_evds') i18n/no-chinese-hardcode - 170:10 warning 'selectedOrder' is assigned a value but never used. Allowed unused elements of array destructuring must match /^_/u @typescript-eslint/no-unused-vars - 170:25 warning 'setSelectedOrder' is assigned a value but never used. Allowed unused elements of array destructuring must match /^_/u @typescript-eslint/no-unused-vars - 380:48 warning 'idx' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars - -D:\dcprint\erp-project\src\app\[locale]\production\process\page.tsx - 156:6 warning React Hook useCallback has a missing dependency: 't'. Either include it or remove the dependency array react-hooks/exhaustive-deps - 159:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\production\process\page.tsx:159:5 - 157 | - 158 | useEffect(() => { -> 159 | fetchProcesses(); - | ^^^^^^^^^^^^^^ Avoid calling setState() directly within an effect - 160 | }, [fetchProcesses]); - 161 | - 162 | const stats = { react-hooks/set-state-in-effect - -D:\dcprint\erp-project\src\app\[locale]\production\product-label\page.tsx - 36:3 warning 'TableExportToolbar' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 43:15 warning 'ExportColumn' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 74:9 warning 'locale' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 121:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\production\product-label\page.tsx:121:5 - 119 | - 120 | useEffect(() => { -> 121 | fetchData(); - | ^^^^^^^^^ Avoid calling setState() directly within an effect - 122 | }, [fetchData]); - 123 | - 124 | const toggleSelect = (id: number) => { react-hooks/set-state-in-effect - 272:9 warning 'handlePrint' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 273:9 warning 'handleExportPDF' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 280:9 warning 'handleExportXLS' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 282:9 warning 'handleExportWORD' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 321:65 warning 'v' is defined but never used. Allowed unused elements of array destructuring must match /^_/u @typescript-eslint/no-unused-vars - -D:\dcprint\erp-project\src\app\[locale]\production\report\page.tsx - 191:6 warning React Hook useCallback has a missing dependency: 't'. Either include it or remove the dependency array react-hooks/exhaustive-deps - 219:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\production\report\page.tsx:219:5 - 217 | - 218 | useEffect(() => { -> 219 | fetchData(); - | ^^^^^^^^^ Avoid calling setState() directly within an effect - 220 | }, [fetchData]); - 221 | useEffect(() => { - 222 | fetchWorkOrders(); react-hooks/set-state-in-effect - 222:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\production\report\page.tsx:222:5 - 220 | }, [fetchData]); - 221 | useEffect(() => { -> 222 | fetchWorkOrders(); - | ^^^^^^^^^^^^^^^ Avoid calling setState() directly within an effect - 223 | fetchEquipment(); - 224 | fetchDieTemplates(); - 225 | }, [fetchWorkOrders, fetchEquipment]); react-hooks/set-state-in-effect - 224:5 warning Error: Cannot access variable before it is declared - -`fetchDieTemplates` is accessed before it is declared, which prevents the earlier access from updating when this value changes over time. - -D:\dcprint\erp-project\src\app\[locale]\production\report\page.tsx:224:5 - 222 | fetchWorkOrders(); - 223 | fetchEquipment(); -> 224 | fetchDieTemplates(); - | ^^^^^^^^^^^^^^^^^ `fetchDieTemplates` accessed before it is declared - 225 | }, [fetchWorkOrders, fetchEquipment]); - 226 | - 227 | const fetchDieTemplates = useCallback(async () => { - -D:\dcprint\erp-project\src\app\[locale]\production\report\page.tsx:227:3 - 225 | }, [fetchWorkOrders, fetchEquipment]); - 226 | -> 227 | const fetchDieTemplates = useCallback(async () => { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 228 | try { - | ^^^^^^^^^ -> 229 | const res = await fetch('/api/prepress/die-template?pageSize=100&die_status=available'); - | ^^^^^^^^^ -> 230 | const result = await res.json(); - | ^^^^^^^^^ -> 231 | if (result.success) setDieTemplateList(result.data?.list || []); - | ^^^^^^^^^ -> 232 | } catch {} - | ^^^^^^^^^ -> 233 | }, []); - | ^^^^^^^^^^ `fetchDieTemplates` is declared here - 234 | - 235 | const handleSave = async () => { - 236 | if (!form.work_order_id) { react-hooks/immutability - 225:6 warning React Hook useEffect has a missing dependency: 'fetchDieTemplates'. Either include it or remove the dependency array react-hooks/exhaustive-deps - 793:41 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍗板埛"銆傚缓璁娇鐢? {tc('text_en5z')} i18n/no-chinese-hardcode - 794:41 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "瑕嗚啘"銆傚缓璁娇鐢? {tc('text_o3py')} i18n/no-chinese-hardcode - 795:41 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "妯″垏"銆傚缓璁娇鐢? {tc('text_ii2u')} i18n/no-chinese-hardcode - 796:41 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍒嗗垏"銆傚缓璁娇鐢? {tc('text_eegx')} i18n/no-chinese-hardcode - 797:41 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "妫€楠?銆傚缓璁娇鐢? {tc('text_inyk')} i18n/no-chinese-hardcode - 798:41 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍖呰"銆傚缓璁娇鐢? {tc('text_evds')} i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\[locale]\production\schedule\page.tsx - 52:3 warning 'Clock' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 58:3 warning 'Pause' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 59:3 warning 'Trash2' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 60:3 warning 'Filter' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 61:3 warning 'Download' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 62:3 warning 'Printer' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 63:3 warning 'ChevronLeft' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 64:3 warning 'ChevronRight' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 68:3 warning 'TrendingUp' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 69:3 warning 'Settings' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 349:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\production\schedule\page.tsx:349:5 - 347 | - 348 | useEffect(() => { -> 349 | fetchSchedules(); - | ^^^^^^^^^^^^^^ Avoid calling setState() directly within an effect - 350 | fetchCapacityData(); - 351 | fetchWorkOrders(); - 352 | }, []); react-hooks/set-state-in-effect - -D:\dcprint\erp-project\src\app\[locale]\production\workorder\page.tsx - 269:6 warning React Hook useCallback has missing dependencies: 't' and 'tc'. Either include them or remove the dependency array react-hooks/exhaustive-deps - 292:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\production\workorder\page.tsx:292:5 - 290 | - 291 | useEffect(() => { -> 292 | fetchWorkOrders(); - | ^^^^^^^^^^^^^^^ Avoid calling setState() directly within an effect - 293 | }, [fetchWorkOrders]); - 294 | - 295 | useEffect(() => { react-hooks/set-state-in-effect - 296:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\production\workorder\page.tsx:296:5 - 294 | - 295 | useEffect(() => { -> 296 | fetchSalesOrders(); - | ^^^^^^^^^^^^^^^^ Avoid calling setState() directly within an effect - 297 | fetchBomList(); - 298 | }, [fetchSalesOrders, fetchBomList]); - 299 | react-hooks/set-state-in-effect - 556:49 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "寮?銆傚缓璁娇鐢? {tc('text_isg')} i18n/no-chinese-hardcode - 557:49 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍗?銆傚缓璁娇鐢? {tc('text_ghj')} i18n/no-chinese-hardcode - 558:49 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "涓?銆傚缓璁娇鐢? {tc('text_ffu')} i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\[locale]\purchase\orders\[id]\page.tsx - 203:43 warning Compilation Skipped: Existing memoization could not be preserved - -React Compiler has skipped optimizing this component because the existing manual memoization could not be preserved. The inferred dependencies did not match the manually specified dependencies, which could cause the value to change more or less frequently than expected. The inferred dependency was `order`, but the source dependencies were [order?.po_no]. Inferred less specific property than source. - -D:\dcprint\erp-project\src\app\[locale]\purchase\orders\[id]\page.tsx:203:43 - 201 | }, [orderId]); - 202 | -> 203 | const fetchInboundRecords = useCallback(async () => { - | ^^^^^^^^^^^^^ -> 204 | if (!order?.po_no) return; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 205 | setTabLoading('inbound'); - 鈥? | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 226 | } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 227 | }, [order?.po_no]); - | ^^^^ Could not preserve existing manual memoization - 228 | - 229 | const fetchReturnRecords = useCallback(async () => { - 230 | if (!order?.id) return; react-hooks/preserve-manual-memoization - 229:42 warning Compilation Skipped: Existing memoization could not be preserved - -React Compiler has skipped optimizing this component because the existing manual memoization could not be preserved. The inferred dependencies did not match the manually specified dependencies, which could cause the value to change more or less frequently than expected. The inferred dependency was `order`, but the source dependencies were [order?.id]. Inferred less specific property than source. - -D:\dcprint\erp-project\src\app\[locale]\purchase\orders\[id]\page.tsx:229:42 - 227 | }, [order?.po_no]); - 228 | -> 229 | const fetchReturnRecords = useCallback(async () => { - | ^^^^^^^^^^^^^ -> 230 | if (!order?.id) return; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 231 | setTabLoading('return'); - 鈥? | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 249 | } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 250 | }, [order?.id]); - | ^^^^ Could not preserve existing manual memoization - 251 | - 252 | const fetchPayableRecords = useCallback(async () => { - 253 | if (!order?.po_no) return; react-hooks/preserve-manual-memoization - 252:43 warning Compilation Skipped: Existing memoization could not be preserved - -React Compiler has skipped optimizing this component because the existing manual memoization could not be preserved. The inferred dependencies did not match the manually specified dependencies, which could cause the value to change more or less frequently than expected. The inferred dependency was `order`, but the source dependencies were [order?.po_no]. Inferred less specific property than source. - -D:\dcprint\erp-project\src\app\[locale]\purchase\orders\[id]\page.tsx:252:43 - 250 | }, [order?.id]); - 251 | -> 252 | const fetchPayableRecords = useCallback(async () => { - | ^^^^^^^^^^^^^ -> 253 | if (!order?.po_no) return; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 254 | setTabLoading('payable'); - 鈥? | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 271 | } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 272 | }, [order?.po_no]); - | ^^^^ Could not preserve existing manual memoization - 273 | - 274 | useEffect(() => { - 275 | fetchOrder(); react-hooks/preserve-manual-memoization - 275:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\purchase\orders\[id]\page.tsx:275:5 - 273 | - 274 | useEffect(() => { -> 275 | fetchOrder(); - | ^^^^^^^^^^ Avoid calling setState() directly within an effect - 276 | }, [fetchOrder]); - 277 | - 278 | const handleTabChange = (value: string) => { react-hooks/set-state-in-effect - -D:\dcprint\erp-project\src\app\[locale]\purchase\orders\page.tsx - 214:66 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "寮€濮嬭幏鍙栭噰璐崟鍒楄〃"銆傚缓璁娇鐢? tc('text_1hksxi') i18n/no-chinese-hardcode - 224:70 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "浣跨敤 mock 鏁版嵁"銆傚缓璁娇鐢? tc('text_us9wlf') i18n/no-chinese-hardcode - 252:70 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "閲囪喘鍗曞垪琛ㄨ幏鍙栨垚鍔?銆傚缓璁娇鐢? tc('text_nv7ea6') i18n/no-chinese-hardcode - 261:69 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鑾峰彇閲囪喘鍗曞垪琛ㄥけ璐?銆傚缓璁娇鐢? tc('text_1lvaet') i18n/no-chinese-hardcode - 272:67 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "寮€濮嬭幏鍙栦緵搴斿晢鍒楄〃"銆傚缓璁娇鐢? tc('text_l0fj90') i18n/no-chinese-hardcode - 275:71 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "浣跨敤 mock 鏁版嵁"銆傚缓璁娇鐢? tc('text_us9wlf') i18n/no-chinese-hardcode - 283:71 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "渚涘簲鍟嗗垪琛ㄨ幏鍙栨垚鍔?銆傚缓璁娇鐢? tc('text_psnfm4') i18n/no-chinese-hardcode - 288:70 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鑾峰彇渚涘簲鍟嗗垪琛ㄥけ璐?銆傚缓璁娇鐢? tc('text_bqhdwz') i18n/no-chinese-hardcode - 295:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\purchase\orders\page.tsx:295:5 - 293 | - 294 | useEffect(() => { -> 295 | fetchOrders(); - | ^^^^^^^^^^^ Avoid calling setState() directly within an effect - 296 | }, []); - 297 | - 298 | const debouncedKeyword = useDebounce(keyword, 300); react-hooks/set-state-in-effect - 296:6 warning React Hook useEffect has a missing dependency: 'fetchOrders'. Either include it or remove the dependency array react-hooks/exhaustive-deps - 301:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\purchase\orders\page.tsx:301:5 - 299 | - 300 | useEffect(() => { -> 301 | fetchOrders(debouncedKeyword); - | ^^^^^^^^^^^ Avoid calling setState() directly within an effect - 302 | }, [debouncedKeyword, statusFilter, fetchOrders]); - 303 | - 304 | useEffect(() => { react-hooks/set-state-in-effect - 305:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\purchase\orders\page.tsx:305:5 - 303 | - 304 | useEffect(() => { -> 305 | fetchSuppliers(); - | ^^^^^^^^^^^^^^ Avoid calling setState() directly within an effect - 306 | }, [fetchSuppliers]); - 307 | - 308 | const handleViewDetail = (order: PurchaseOrder) => { react-hooks/set-state-in-effect - 326:22 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "閿欒"銆傚缓璁娇鐢? tc('text_q4mu') i18n/no-chinese-hardcode - 326:41 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "璇烽€夋嫨渚涘簲鍟?銆傚缓璁娇鐢? tc('text_3lv8ui') i18n/no-chinese-hardcode - 332:22 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "閿欒"銆傚缓璁娇鐢? tc('text_q4mu') i18n/no-chinese-hardcode - 332:41 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "璇锋坊鍔犺嚦灏戜竴椤归噰璐墿鏂?銆傚缓璁娇鐢? tc('text_kur2vd') i18n/no-chinese-hardcode - 356:24 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鎴愬姛"銆傚缓璁娇鐢? tc('text_h4sv') i18n/no-chinese-hardcode - 356:43 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "閲囪喘鍗曞垱寤烘垚鍔?銆傚缓璁娇鐢? tc('text_c4fzvh') i18n/no-chinese-hardcode - 364:24 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "閿欒"銆傚缓璁娇鐢? tc('text_q4mu') i18n/no-chinese-hardcode - 367:22 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "閿欒"銆傚缓璁娇鐢? tc('text_q4mu') i18n/no-chinese-hardcode - 367:41 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍒涘缓閲囪喘鍗曞け璐?銆傚缓璁娇鐢? tc('text_mzvr98') i18n/no-chinese-hardcode - 414:24 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鎴愬姛"銆傚缓璁娇鐢? tc('text_h4sv') i18n/no-chinese-hardcode - 417:24 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "閿欒"銆傚缓璁娇鐢? tc('text_q4mu') i18n/no-chinese-hardcode - 417:43 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍒犻櫎澶辫触"銆傚缓璁娇鐢? tc('text_azdahk') i18n/no-chinese-hardcode - 420:22 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "閿欒"銆傚缓璁娇鐢? tc('text_q4mu') i18n/no-chinese-hardcode - 420:41 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍒犻櫎澶辫触"銆傚缓璁娇鐢? tc('text_azdahk') i18n/no-chinese-hardcode - 429:36 warning 'newStatus' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 454:9 warning 'handleExport' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 490:22 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "瀵煎嚭鎴愬姛"銆傚缓璁娇鐢? tc('text_by5dal') i18n/no-chinese-hardcode - 490:43 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "宸插鍑轰负 Excel 鏂囦欢"銆傚缓璁娇鐢? tc('text_kh10ni') i18n/no-chinese-hardcode - 520:22 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "瀵煎嚭鎴愬姛"銆傚缓璁娇鐢? tc('text_by5dal') i18n/no-chinese-hardcode - 520:43 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "宸插鍑轰负 PDF锛堟墦鍗颁繚瀛橈級"銆傚缓璁娇鐢? tc('text_j2qaat') i18n/no-chinese-hardcode - 554:22 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "瀵煎嚭鎴愬姛"銆傚缓璁娇鐢? tc('text_by5dal') i18n/no-chinese-hardcode - 554:43 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "宸插鍑轰负 Word 鏂囦欢"銆傚缓璁娇鐢? tc('text_pkrj69') i18n/no-chinese-hardcode - 642:16 warning 'orderIndex' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars - 804:28 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "閲囪喘璁㈠崟"銆傚缓璁娇鐢? {tc('text_iz9pyx')} i18n/no-chinese-hardcode - 1349:62 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "閲囪喘鍗曞彿"銆傚缓璁娇鐢? {tc('text_iz05c8')} i18n/no-chinese-hardcode - 1403:38 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鐗╂枡缂栫爜"銆傚缓璁娇鐢? {tc('text_euzqpn')} i18n/no-chinese-hardcode - 1404:38 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鐗╂枡鍚嶇О"銆傚缓璁娇鐢? {tc('text_eusfkj')} i18n/no-chinese-hardcode - 1407:38 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍗曚环"銆傚缓璁娇鐢? {tc('text_elvm')} i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\[locale]\purchase\request\[id]\page.tsx - 109:7 warning Error: Cannot access variable before it is declared - -`fetchRequest` is accessed before it is declared, which prevents the earlier access from updating when this value changes over time. - -D:\dcprint\erp-project\src\app\[locale]\purchase\request\[id]\page.tsx:109:7 - 107 | useEffect(() => { - 108 | if (id) { -> 109 | fetchRequest(); - | ^^^^^^^^^^^^ `fetchRequest` accessed before it is declared - 110 | } - 111 | }, [id]); - 112 | - -D:\dcprint\erp-project\src\app\[locale]\purchase\request\[id]\page.tsx:113:3 - 111 | }, [id]); - 112 | -> 113 | const fetchRequest = async () => { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 114 | try { - | ^^^^^^^^^ -> 115 | setLoading(true); - 鈥? | ^^^^^^^^^ -> 128 | } - | ^^^^^^^^^ -> 129 | }; - | ^^^^^ `fetchRequest` is declared here - 130 | - 131 | const handleApprove = async () => { - 132 | toast.info('瀹℃壒鍔熻兘寮€鍙戜腑'); react-hooks/immutability - 111:6 warning React Hook useEffect has a missing dependency: 'fetchRequest'. Either include it or remove the dependency array react-hooks/exhaustive-deps - 125:19 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鑾峰彇閲囪喘鐢宠澶辫触"銆傚缓璁娇鐢? tc('text_6ue5vh') i18n/no-chinese-hardcode - 132:16 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "瀹℃壒鍔熻兘寮€鍙戜腑"銆傚缓璁娇鐢? tc('text_53oz96') i18n/no-chinese-hardcode - 136:16 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "瀹℃壒鍔熻兘寮€鍙戜腑"銆傚缓璁娇鐢? tc('text_53oz96') i18n/no-chinese-hardcode - 150:46 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍔犺浇涓?.."銆傚缓璁娇鐢? {tc('text_27k1ha')} i18n/no-chinese-hardcode - 160:68 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "閲囪喘鐢宠涓嶅瓨鍦ㄦ垨宸茶鍒犻櫎"銆傚缓璁娇鐢? {tc('text_rirxj4')} i18n/no-chinese-hardcode - 177:49 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "閲囪喘鐢宠璇︽儏"銆傚缓璁娇鐢? {tc('text_i0by0n')} i18n/no-chinese-hardcode - 188:51 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鎵撳嵃"銆傚缓璁娇鐢? {tc('text_h6kd')} i18n/no-chinese-hardcode - 193:50 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "缂栬緫"銆傚缓璁娇鐢? {tc('text_mekb')} i18n/no-chinese-hardcode - 200:59 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鎵瑰噯"銆傚缓璁娇鐢? {tc('text_h759')} i18n/no-chinese-hardcode - 204:55 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鎷掔粷"銆傚缓璁娇鐢? {tc('text_hi6j')} i18n/no-chinese-hardcode - 230:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍩烘湰淇℃伅"銆傚缓璁娇鐢? {tc('text_biyzkw')} i18n/no-chinese-hardcode - 235:64 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鐢宠鏃ユ湡"銆傚缓璁娇鐢? {tc('text_fd5kvy')} i18n/no-chinese-hardcode - 239:64 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鐢宠绫诲瀷"銆傚缓璁娇鐢? {tc('text_fd9c44')} i18n/no-chinese-hardcode - 243:64 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鐢宠閮ㄩ棬"銆傚缓璁娇鐢? {tc('text_fdd5ic')} i18n/no-chinese-hardcode - 255:64 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "寤鸿渚涘簲鍟?銆傚缓璁娇鐢? {tc('text_y5m1eh')} i18n/no-chinese-hardcode - 275:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "閲囪喘鐗╂枡鏄庣粏"銆傚缓璁娇鐢? {tc('text_iisfle')} i18n/no-chinese-hardcode - 281:30 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "琛屽彿"銆傚缓璁娇鐢? {tc('text_nn6z')} i18n/no-chinese-hardcode - 282:30 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鐗╂枡缂栫爜"銆傚缓璁娇鐢? {tc('text_euzqpn')} i18n/no-chinese-hardcode - 283:30 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鐗╂枡鍚嶇О"銆傚缓璁娇鐢? {tc('text_eusfkj')} i18n/no-chinese-hardcode - 284:30 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "瑙勬牸鍨嬪彿"銆傚缓璁娇鐢? {tc('text_ht8gjo')} i18n/no-chinese-hardcode - 287:53 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍗曚环"銆傚缓璁娇鐢? {tc('text_elvm')} i18n/no-chinese-hardcode - 315:50 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍚堣閲戦锛?銆傚缓璁娇鐢? {tc('text_8d9uh7')} i18n/no-chinese-hardcode - 329:26 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "瀹℃壒淇℃伅"銆傚缓璁娇鐢? {tc('text_byv89y')} i18n/no-chinese-hardcode - 342:66 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "瀹℃壒鎰忚"銆傚缓璁娇鐢? {tc('text_byydmy')} i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\[locale]\purchase\request\form\page.tsx - 37:10 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "骞?銆傚缓璁娇鐢? tc(`text_ino`) i18n/no-chinese-hardcode - 37:10 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏈?銆傚缓璁娇鐢? tc(`text_kco`) i18n/no-chinese-hardcode - 37:10 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏃?銆傚缓璁娇鐢? tc(`text_k4l`) i18n/no-chinese-hardcode - 40:22 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "涓?銆傚缓璁娇鐢? tc('text_ffu') i18n/no-chinese-hardcode - 40:27 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "浠?銆傚缓璁娇鐢? tc('text_fli') i18n/no-chinese-hardcode - 40:32 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "濂?銆傚缓璁娇鐢? tc('text_hnb') i18n/no-chinese-hardcode - 40:37 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "寮?銆傚缓璁娇鐢? tc('text_isg') i18n/no-chinese-hardcode - 40:42 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "绫?銆傚缓璁娇鐢? tc('text_okz') i18n/no-chinese-hardcode - 40:47 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍗冨厠"銆傚缓璁娇鐢? tc('text_elwo') i18n/no-chinese-hardcode - 40:53 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍗?銆傚缓璁娇鐢? tc('text_gg7') i18n/no-chinese-hardcode - 40:58 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍖?銆傚缓璁娇鐢? tc('text_ged') i18n/no-chinese-hardcode - 40:63 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "绠?銆傚缓璁娇鐢? tc('text_ofl') i18n/no-chinese-hardcode - 40:68 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍗?銆傚缓璁娇鐢? tc('text_ghj') i18n/no-chinese-hardcode - 57:11 warning 'MaterialRef' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 168:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\purchase\request\form\page.tsx:168:5 - 166 | - 167 | useEffect(() => { -> 168 | setPurchaseOrderNo(generateOrderNo()); - | ^^^^^^^^^^^^^^^^^^ Avoid calling setState() directly within an effect - 169 | setRequestDate(new Date().toISOString().split('T')[0]); - 170 | }, []); - 171 | react-hooks/set-state-in-effect - 237:24 warning Error: Cannot call impure function during render - -`Date.now` is an impure function. Calling an impure function can produce unstable results that update unpredictably when the component happens to re-render. (https://react.dev/reference/rules/components-and-hooks-must-be-pure#components-and-hooks-must-be-idempotent). - -D:\dcprint\erp-project\src\app\[locale]\purchase\request\form\page.tsx:237:24 - 235 | - 236 | if (record.items && record.items.length > 0) { -> 237 | let nextItemId = Date.now(); - | ^^^^^^^^^^ Cannot call impure function - 238 | const items = record.items.map((item: Loose) => ({ - 239 | id: nextItemId++, - 240 | material_id: item.material_id || 0, react-hooks/purity - 292:22 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鑷冲皯淇濈暀涓€琛岀墿鏂欐槑缁?銆傚缓璁娇鐢? tc('text_5mh1zm') i18n/no-chinese-hardcode - 320:22 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "璇疯嚦灏戝~鍐欎竴琛岀墿鏂欐槑缁?銆傚缓璁娇鐢? tc('text_ikztnb') i18n/no-chinese-hardcode - 324:22 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "璇烽€夋嫨鐢宠浜?銆傚缓璁娇鐢? tc('text_3g5bwh') i18n/no-chinese-hardcode - 328:22 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "璇烽€夋嫨閮ㄩ棬"銆傚缓璁娇鐢? tc('text_2ecyvd') i18n/no-chinese-hardcode - 396:22 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "淇濆瓨澶辫触"銆傚缓璁娇鐢? tc('text_agg8dr') i18n/no-chinese-hardcode - 542:48 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍘嗗彶璁板綍"銆傚缓璁娇鐢? {tc('text_aw7utt')} i18n/no-chinese-hardcode - 545:69 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏂板缓"銆傚缓璁娇鐢? {tc('text_htfu')} i18n/no-chinese-hardcode - 549:51 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鎵撳嵃(A5妯悜)"銆傚缓璁娇鐢? {tc('text_35em1p')} i18n/no-chinese-hardcode - 554:50 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "淇濆瓨鑽夌"銆傚缓璁娇鐢? {tc('text_agnaep')} i18n/no-chinese-hardcode - 560:50 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鎻愪氦瀹℃牎"銆傚缓璁娇鐢? {tc('text_cx6elw')} i18n/no-chinese-hardcode - 627:14 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "璇疯喘鍗?銆傚缓璁娇鐢? {tc('text_l6i2n')} i18n/no-chinese-hardcode - 643:68 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "閮ㄩ棬锛?銆傚缓璁娇鐢? {tc('text_lyzh6')} i18n/no-chinese-hardcode - 807:79 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "閫夋嫨鐗╂枡"銆傚缓璁娇鐢? {tc('text_il1vtc')} i18n/no-chinese-hardcode - 822:26 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏇存崲"銆傚缓璁娇鐢? {tc('text_i226')} i18n/no-chinese-hardcode - 1029:14 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "+ 娣诲姞鐗╂枡琛?銆傚缓璁娇鐢? {tc('text_lr02hu')} i18n/no-chinese-hardcode - 1032:65 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍚堣閲戦锛?銆傚缓璁娇鐢? {tc('text_8d9uh7')} i18n/no-chinese-hardcode - 1065:14 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "琛ㄥ崟缂栧彿锛欴C-A-03A"銆傚缓璁娇鐢? {tc('text_rssdsg')} i18n/no-chinese-hardcode - 1124:89 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍔犺浇涓?.."銆傚缓璁娇鐢? {tc('text_27k1ha')} i18n/no-chinese-hardcode - 1128:89 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏆傛棤璁板綍"銆傚缓璁娇鐢? {tc('text_dd1mmb')} i18n/no-chinese-hardcode - 1208:28 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "缂栬緫"銆傚缓璁娇鐢? {tc('text_mekb')} i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\[locale]\purchase\request\new\page.tsx - 125:19 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鑷冲皯闇€瑕佷繚鐣欎竴鏉℃槑缁?銆傚缓璁娇鐢? tc('text_77xwcs') i18n/no-chinese-hardcode - 142:19 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "璇疯緭鍏ョ敵璇蜂汉"銆傚缓璁娇鐢? tc('text_7gk73n') i18n/no-chinese-hardcode - 147:19 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "璇峰畬鍠勭墿鏂欎俊鎭紝鐗╂枡鍚嶇О鍜屾暟閲忎笉鑳戒负绌?銆傚缓璁娇鐢? tc('text_kvoi21') i18n/no-chinese-hardcode - 167:23 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "閲囪喘鐢宠鍒涘缓鎴愬姛"銆傚缓璁娇鐢? tc('text_hef0fs') i18n/no-chinese-hardcode - 173:19 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍒涘缓澶辫触"銆傚缓璁娇鐢? tc('text_ar5wgj') i18n/no-chinese-hardcode - 190:49 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏂板閲囪喘鐢宠"銆傚缓璁娇鐢? {tc('text_nldgko')} i18n/no-chinese-hardcode - 198:48 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "淇濆瓨鑽夌"銆傚缓璁娇鐢? {tc('text_agnaep')} i18n/no-chinese-hardcode - 212:26 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍩烘湰淇℃伅"銆傚缓璁娇鐢? {tc('text_biyzkw')} i18n/no-chinese-hardcode - 216:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鐢宠鏃ユ湡"銆傚缓璁娇鐢? {tc('text_fd5kvy')} i18n/no-chinese-hardcode - 229:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鐢宠绫诲瀷"銆傚缓璁娇鐢? {tc('text_fd9c44')} i18n/no-chinese-hardcode - 238:39 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍘熸潗鏂?銆傚缓璁娇鐢? {tc('text_cr294')} i18n/no-chinese-hardcode - 238:45 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍘熸潗鏂?銆傚缓璁娇鐢? {tc('text_cr294')} i18n/no-chinese-hardcode - 239:39 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "杈呮枡"銆傚缓璁娇鐢? {tc('text_oywk')} i18n/no-chinese-hardcode - 239:44 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "杈呮枡"銆傚缓璁娇鐢? {tc('text_oywk')} i18n/no-chinese-hardcode - 240:39 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "璁惧"銆傚缓璁娇鐢? {tc('text_o9ah')} i18n/no-chinese-hardcode - 241:39 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍔炲叕鐢ㄥ搧"銆傚缓璁娇鐢? {tc('text_armisn')} i18n/no-chinese-hardcode - 241:46 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍔炲叕鐢ㄥ搧"銆傚缓璁娇鐢? {tc('text_armisn')} i18n/no-chinese-hardcode - 242:39 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍏朵粬"銆傚缓璁娇鐢? {tc('text_eae8')} i18n/no-chinese-hardcode - 242:44 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍏朵粬"銆傚缓璁娇鐢? {tc('text_eae8')} i18n/no-chinese-hardcode - 247:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鐢宠閮ㄩ棬"銆傚缓璁娇鐢? {tc('text_fdd5ic')} i18n/no-chinese-hardcode - 265:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鐢宠浜?銆傚缓璁娇鐢? {tc('text_hu87q')} i18n/no-chinese-hardcode - 287:43 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "浣?銆傚缓璁娇鐢? {tc('text_fny')} i18n/no-chinese-hardcode - 288:43 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "涓?銆傚缓璁娇鐢? {tc('text_ffx')} i18n/no-chinese-hardcode - 289:43 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "楂?銆傚缓璁娇鐢? {tc('text_ul4')} i18n/no-chinese-hardcode - 290:43 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "绱ф€?銆傚缓璁娇鐢? {tc('text_ltcu')} i18n/no-chinese-hardcode - 305:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "寤鸿渚涘簲鍟?銆傚缓璁娇鐢? {tc('text_y5m1eh')} i18n/no-chinese-hardcode - 320:26 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "閲囪喘鐗╂枡鏄庣粏"銆傚缓璁娇鐢? {tc('text_iisfle')} i18n/no-chinese-hardcode - 322:50 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "娣诲姞鐗╂枡"銆傚缓璁娇鐢? {tc('text_e81ct1')} i18n/no-chinese-hardcode - 334:50 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "琛屽彿"銆傚缓璁娇鐢? {tc('text_nn6z')} i18n/no-chinese-hardcode - 340:50 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鐗╂枡缂栫爜"銆傚缓璁娇鐢? {tc('text_euzqpn')} i18n/no-chinese-hardcode - 348:50 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鐗╂枡鍚嶇О"銆傚缓璁娇鐢? {tc('text_eusfkj')} i18n/no-chinese-hardcode - 359:50 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "瑙勬牸鍨嬪彿"銆傚缓璁娇鐢? {tc('text_ht8gjo')} i18n/no-chinese-hardcode - 375:50 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏁伴噺"銆傚缓璁娇鐢? {tc('text_i1y7')} i18n/no-chinese-hardcode - 390:50 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍗曚环"銆傚缓璁娇鐢? {tc('text_elvm')} i18n/no-chinese-hardcode - 424:82 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍚堣閲戦锛?銆傚缓璁娇鐢? {tc('text_8d9uh7')} i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\[locale]\purchase\request\page.tsx - 175:5 warning Error: Cannot access variable before it is declared - -`fetchRequests` is accessed before it is declared, which prevents the earlier access from updating when this value changes over time. - -D:\dcprint\erp-project\src\app\[locale]\purchase\request\page.tsx:175:5 - 173 | - 174 | useEffect(() => { -> 175 | fetchRequests(); - | ^^^^^^^^^^^^^ `fetchRequests` accessed before it is declared - 176 | }, [page, status, debouncedKeyword]); - 177 | - 178 | const fetchRequests = async () => { - -D:\dcprint\erp-project\src\app\[locale]\purchase\request\page.tsx:178:3 - 176 | }, [page, status, debouncedKeyword]); - 177 | -> 178 | const fetchRequests = async () => { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 179 | try { - | ^^^^^^^^^ -> 180 | setLoading(true); - 鈥? | ^^^^^^^^^ -> 203 | } - | ^^^^^^^^^ -> 204 | }; - | ^^^^^ `fetchRequests` is declared here - 205 | - 206 | const handleDelete = async (id: number) => { - 207 | if (!confirm(tc('confirmDelete'))) return; react-hooks/immutability - 176:6 warning React Hook useEffect has a missing dependency: 'fetchRequests'. Either include it or remove the dependency array react-hooks/exhaustive-deps - 340:9 warning 'handleExportXLS' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 379:9 warning 'handleExportPDF' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 477:26 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "閲囪喘鐢宠"銆傚缓璁娇鐢? {tc('text_iz67sa')} i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\[locale]\purchase\return\page.tsx - 82:12 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "寰呭鏍?銆傚缓璁娇鐢? tc('text_eftvg') i18n/no-chinese-hardcode - 86:12 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "宸插鏍?銆傚缓璁娇鐢? tc('text_e7j1l') i18n/no-chinese-hardcode - 90:12 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "宸插畬鎴?銆傚缓璁娇鐢? tc('text_e7hbq') i18n/no-chinese-hardcode - 94:12 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "宸插彇娑?銆傚缓璁娇鐢? tc('text_e68dg') i18n/no-chinese-hardcode - 100:12 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "璐ㄩ噺闂"銆傚缓璁娇鐢? tc('text_if0wnl') i18n/no-chinese-hardcode - 101:13 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏁伴噺宸紓"銆傚缓璁娇鐢? tc('text_deejqr') i18n/no-chinese-hardcode - 102:10 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍏朵粬鍘熷洜"銆傚缓璁娇鐢? tc('text_alu6v5') i18n/no-chinese-hardcode - 130:10 warning 'detailItems' is assigned a value but never used. Allowed unused elements of array destructuring must match /^_/u @typescript-eslint/no-unused-vars - 168:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\purchase\return\page.tsx:168:5 - 166 | - 167 | useEffect(() => { -> 168 | fetchList(); - | ^^^^^^^^^ Avoid calling setState() directly within an effect - 169 | }, [fetchList]); - 170 | - 171 | const handleSelectOrder = (orderId: string) => { react-hooks/set-state-in-effect - 184:78 warning 'idx' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars - 220:22 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "璇疯嚦灏戝~鍐欎竴椤归€€璐ф暟閲?銆傚缓璁娇鐢? tc('text_eox6yq') i18n/no-chinese-hardcode - 224:22 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "璇烽€夋嫨閲囪喘璁㈠崟"銆傚缓璁娇鐢? tc('text_w98jo0') i18n/no-chinese-hardcode - 238:24 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "閲囪喘閫€璐у崟鍒涘缓鎴愬姛"銆傚缓璁娇鐢? tc('text_t10s9i') i18n/no-chinese-hardcode - 246:22 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍒涘缓澶辫触"銆傚缓璁娇鐢? tc('text_ar5wgj') i18n/no-chinese-hardcode - 267:22 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鎿嶄綔澶辫触"銆傚缓璁娇鐢? tc('text_d1rj43') i18n/no-chinese-hardcode - 314:44 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "閲囪喘閫€璐х鐞?銆傚缓璁娇鐢? {tc('text_emjff2')} i18n/no-chinese-hardcode - 332:41 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍏ㄩ儴鐘舵€?銆傚缓璁娇鐢? {tc('text_avez63')} i18n/no-chinese-hardcode - 343:48 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏂板缓閫€璐?銆傚缓璁娇鐢? {tc('text_d8c5bl')} i18n/no-chinese-hardcode - 354:30 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "閫€璐у崟鍙?銆傚缓璁娇鐢? {tc('text_iqxhux')} i18n/no-chinese-hardcode - 357:30 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "閫€璐ф棩鏈?銆傚缓璁娇鐢? {tc('text_ir0rb5')} i18n/no-chinese-hardcode - 358:30 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "閫€璐х被鍨?銆傚缓璁娇鐢? {tc('text_ir4ijb')} i18n/no-chinese-hardcode - 374:95 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏆傛棤閫€璐ц褰?銆傚缓璁娇鐢? {tc('text_elyafq')} i18n/no-chinese-hardcode - 411:61 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "璇︽儏"銆傚缓璁娇鐢? {tc('text_obrz')} i18n/no-chinese-hardcode - 422:73 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "瀹℃牳"銆傚缓璁娇鐢? {tc('text_g5o7')} i18n/no-chinese-hardcode - 431:69 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍙栨秷"銆傚缓璁娇鐢? {tc('text_ev02')} i18n/no-chinese-hardcode - 443:71 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "瀹屾垚閫€璐?銆傚缓璁娇鐢? {tc('text_byqt63')} i18n/no-chinese-hardcode - 458:59 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍏?銆傚缓璁娇鐢? {tc('text_g35')} i18n/no-chinese-hardcode - 458:67 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏉?銆傚缓璁娇鐢? {tc('text_kf5')} i18n/no-chinese-hardcode - 465:14 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "涓婁竴椤?銆傚缓璁娇鐢? {tc('text_btlof')} i18n/no-chinese-hardcode - 473:14 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "涓嬩竴椤?銆傚缓璁娇鐢? {tc('text_btmf4')} i18n/no-chinese-hardcode - 514:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "閫€璐ф棩鏈?銆傚缓璁娇鐢? {tc('text_ir0rb5')} i18n/no-chinese-hardcode - 522:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "閫€璐х被鍨?銆傚缓璁娇鐢? {tc('text_ir4ijb')} i18n/no-chinese-hardcode - 531:49 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "璐ㄩ噺闂"銆傚缓璁娇鐢? {tc('text_if0wnl')} i18n/no-chinese-hardcode - 532:50 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏁伴噺宸紓"銆傚缓璁娇鐢? {tc('text_deejqr')} i18n/no-chinese-hardcode - 533:47 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍏朵粬鍘熷洜"銆傚缓璁娇鐢? {tc('text_alu6v5')} i18n/no-chinese-hardcode - 552:55 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "閫€璐ф槑缁?銆傚缓璁娇鐢? {tc('text_ir0wyn')} i18n/no-chinese-hardcode - 557:52 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鐗╂枡缂栫爜"銆傚缓璁娇鐢? {tc('text_euzqpn')} i18n/no-chinese-hardcode - 558:52 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鐗╂枡鍚嶇О"銆傚缓璁娇鐢? {tc('text_eusfkj')} i18n/no-chinese-hardcode - 561:52 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍗曚环"銆傚缓璁娇鐢? {tc('text_elvm')} i18n/no-chinese-hardcode - 562:52 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "閫€璐ф暟閲?銆傚缓璁娇鐢? {tc('text_ir0wxy')} i18n/no-chinese-hardcode - 602:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "璇峰厛閫夋嫨閲囪喘璁㈠崟"銆傚缓璁娇鐢? {tc('text_kdl87q')} i18n/no-chinese-hardcode - 644:32 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鐘舵€侊細"銆傚缓璁娇鐢? {tc('text_halin')} i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\[locale]\purchase\suppliers\page.tsx - 93:22 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏈堢粨"銆傚缓璁娇鐢? tc('text_i7yj') i18n/no-chinese-hardcode - 94:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "30澶?銆傚缓璁娇鐢? tc('text_1kks') i18n/no-chinese-hardcode - 179:9 warning Unexpected console statement. Only these console methods are allowed: warn, error no-console - 210:9 warning Unexpected console statement. Only these console methods are allowed: warn, error no-console - 248:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\purchase\suppliers\page.tsx:248:5 - 246 | - 247 | useEffect(() => { -> 248 | fetchData(); - | ^^^^^^^^^ Avoid calling setState() directly within an effect - 249 | }, [fetchData]); - 250 | - 251 | const handleOpenAdd = () => { react-hooks/set-state-in-effect - 279:5 warning Unexpected console statement. Only these console methods are allowed: warn, error no-console - 288:7 warning Unexpected console statement. Only these console methods are allowed: warn, error no-console - 297:9 warning Unexpected console statement. Only these console methods are allowed: warn, error no-console - 299:9 warning Unexpected console statement. Only these console methods are allowed: warn, error no-console - 307:9 warning Unexpected console statement. Only these console methods are allowed: warn, error no-console - 316:9 warning Unexpected console statement. Only these console methods are allowed: warn, error no-console - 318:9 warning Unexpected console statement. Only these console methods are allowed: warn, error no-console - 320:11 warning Unexpected console statement. Only these console methods are allowed: warn, error no-console - 325:11 warning Unexpected console statement. Only these console methods are allowed: warn, error no-console - 338:5 warning Unexpected console statement. Only these console methods are allowed: warn, error no-console - 340:7 warning Unexpected console statement. Only these console methods are allowed: warn, error no-console - 345:9 warning Unexpected console statement. Only these console methods are allowed: warn, error no-console - 347:9 warning Unexpected console statement. Only these console methods are allowed: warn, error no-console - 352:9 warning Unexpected console statement. Only these console methods are allowed: warn, error no-console - 354:9 warning Unexpected console statement. Only these console methods are allowed: warn, error no-console - 356:9 warning Unexpected console statement. Only these console methods are allowed: warn, error no-console - 358:11 warning Unexpected console statement. Only these console methods are allowed: warn, error no-console - 362:11 warning Unexpected console statement. Only these console methods are allowed: warn, error no-console - 413:15 warning 'grade' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 452:9 warning 'handleExportXLS' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 501:9 warning 'handleExportPDF' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 530:15 warning 'grade' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 983:39 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏈堢粨"銆傚缓璁娇鐢? {tc('text_i7yj')} i18n/no-chinese-hardcode - 984:39 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鐜扮粨"銆傚缓璁娇鐢? {tc('text_kdgj')} i18n/no-chinese-hardcode - 985:39 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "棰勪粯"銆傚缓璁娇鐢? {tc('text_qdhw')} i18n/no-chinese-hardcode - 986:39 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "璐у埌浠樻"銆傚缓璁娇鐢? {tc('text_i5cgen')} i18n/no-chinese-hardcode - 987:39 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍒嗘湡浠樻"銆傚缓璁娇鐢? {tc('text_arxhq7')} i18n/no-chinese-hardcode - 1009:39 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "璐у埌浠樻"銆傚缓璁娇鐢? {tc('text_i5cgen')} i18n/no-chinese-hardcode - 1010:39 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "15澶?銆傚缓璁娇鐢? {tc('text_1j7p')} i18n/no-chinese-hardcode - 1011:39 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "30澶?銆傚缓璁娇鐢? {tc('text_1kks')} i18n/no-chinese-hardcode - 1012:39 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "60澶?銆傚缓璁娇鐢? {tc('text_1msv')} i18n/no-chinese-hardcode - 1013:39 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "90澶?銆傚缓璁娇鐢? {tc('text_1p0y')} i18n/no-chinese-hardcode - 1014:39 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "120澶?銆傚缓璁娇鐢? {tc('text_wu6y')} i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\[locale]\qrcode\page.tsx - 135:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\qrcode\page.tsx:135:5 - 133 | - 134 | useEffect(() => { -> 135 | fetchData(); - | ^^^^^^^^^ Avoid calling setState() directly within an effect - 136 | }, [fetchData]); - 137 | - 138 | const handleGenerate = async () => { react-hooks/set-state-in-effect - 265:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "浜岀淮鐮佽褰曞垪琛?銆傚缓璁娇鐢? {tc('text_u96lld')} i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\[locale]\quality\complaint\page.tsx - 153:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\quality\complaint\page.tsx:153:5 - 151 | - 152 | useEffect(() => { -> 153 | fetchData(); - | ^^^^^^^^^ Avoid calling setState() directly within an effect - 154 | }, [page]); - 155 | - 156 | const handleSave = async () => { react-hooks/set-state-in-effect - 154:6 warning React Hook useEffect has a missing dependency: 'fetchData'. Either include it or remove the dependency array react-hooks/exhaustive-deps - 271:26 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "瀹㈣瘔8D鎶ュ憡"銆傚缓璁娇鐢? {tc('text_4a1d88')} i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\[locale]\quality\final\page.tsx - 44:3 warning 'TableExportToolbar' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 45:3 warning 'exportTableToXLS' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 46:3 warning 'exportTableToPDF' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 47:3 warning 'exportTableToWORD' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 48:3 warning 'printTable' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 51:15 warning 'ExportColumn' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 69:3 warning 'Package' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 74:10 warning 'zhCN' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 110:11 warning 'FinalStats' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 185:63 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "寮€濮嬭幏鍙栫粓妫€鏁版嵁"銆傚缓璁娇鐢? tc('text_b95gcg') i18n/no-chinese-hardcode - 190:67 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "浣跨敤 mock 鏁版嵁"銆傚缓璁娇鐢? tc('text_us9wlf') i18n/no-chinese-hardcode - 260:67 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "缁堟鏁版嵁鑾峰彇鎴愬姛"銆傚缓璁娇鐢? tc('text_3pn3w') i18n/no-chinese-hardcode - 265:66 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鑾峰彇缁堟鏁版嵁澶辫触"銆傚缓璁娇鐢? tc('text_p6nq9j') i18n/no-chinese-hardcode - 274:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\quality\final\page.tsx:274:5 - 272 | - 273 | useEffect(() => { -> 274 | fetchFinals(); - | ^^^^^^^^^^^ Avoid calling setState() directly within an effect - 275 | }, []); - 276 | const printRef = useRef(null); - 277 | react-hooks/set-state-in-effect - 315:17 warning 'sortedFinalInspects' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 518:28 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鎴愬搧妫€楠屾姤鍛?銆傚缓璁娇鐢? {tc('text_e0dqv6')} i18n/no-chinese-hardcode - 827:60 warning 'arr' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars - -D:\dcprint\erp-project\src\app\[locale]\quality\incoming\page.tsx - 161:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\quality\incoming\page.tsx:161:5 - 159 | - 160 | useEffect(() => { -> 161 | fetchInspections(); - | ^^^^^^^^^^^^^^^^ Avoid calling setState() directly within an effect - 162 | }, [fetchInspections]); - 163 | const [selectedInspections, setSelectedInspections] = useState([]); - 164 | const [isAddDialogOpen, setIsAddDialogOpen] = useState(false); react-hooks/set-state-in-effect - 232:6 warning React Hook useCallback has a missing dependency: 'tc'. Either include it or remove the dependency array react-hooks/exhaustive-deps - -D:\dcprint\erp-project\src\app\[locale]\quality\lab-test\page.tsx - 122:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\quality\lab-test\page.tsx:122:5 - 120 | - 121 | useEffect(() => { -> 122 | fetchData(); - | ^^^^^^^^^ Avoid calling setState() directly within an effect - 123 | }, [page]); - 124 | - 125 | const handleSave = async () => { react-hooks/set-state-in-effect - 123:6 warning React Hook useEffect has a missing dependency: 'fetchData'. Either include it or remove the dependency array react-hooks/exhaustive-deps - 203:26 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "瀹為獙瀹ゆ楠屾姤鍛?銆傚缓璁娇鐢? {tc('text_w3j4jr')} i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\[locale]\quality\process\page.tsx - 34:10 warning 'Progress' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 45:3 warning 'TableExportToolbar' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 46:3 warning 'exportTableToXLS' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 47:3 warning 'exportTableToPDF' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 48:3 warning 'exportTableToWORD' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 49:3 warning 'printTable' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 52:15 warning 'ExportColumn' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 70:3 warning 'AlertCircle' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 73:10 warning 'zhCN' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 143:19 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "閫忔槑鍖呰鑶淎娆?銆傚缓璁娇鐢? tc('text_u8v7s6') i18n/no-chinese-hardcode - 144:20 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "PET閫忔槑鑶?0.1mm"銆傚缓璁娇鐢? tc('text_hxff67') i18n/no-chinese-hardcode - 149:23 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "寮犱笁"銆傚缓璁娇鐢? tc('text_glwp') i18n/no-chinese-hardcode - 152:20 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "娣卞湷绉戞妧鏈夐檺鍏徃"銆傚缓璁娇鐢? tc('text_4ncmjg') i18n/no-chinese-hardcode - 154:20 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍒囨枡-纾ㄥ垏-鍗板埛-鐑樺共"銆傚缓璁娇鐢? tc('text_kdl3cd') i18n/no-chinese-hardcode - 155:20 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "妯″垏-妫€楠?鍖呰"銆傚缓璁娇鐢? tc('text_bedugq') i18n/no-chinese-hardcode - 156:17 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "涓濈綉鍗板埛"銆傚缓璁娇鐢? tc('text_adqjyz') i18n/no-chinese-hardcode - 159:22 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "璐ㄩ噺涓荤A"銆傚缓璁娇鐢? tc('text_2e8oqc') i18n/no-chinese-hardcode - 167:19 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "闃查潤鐢佃啘B娆?銆傚缓璁娇鐢? tc('text_b1mljq') i18n/no-chinese-hardcode - 168:20 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "闃查潤鐢佃啘 0.08mm"銆傚缓璁娇鐢? tc('text_10xsuw') i18n/no-chinese-hardcode - 173:23 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏉庡洓"銆傚缓璁娇鐢? tc('text_i1ql') i18n/no-chinese-hardcode - 176:20 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "骞垮窞璐告槗鍙戝睍鏈夐檺鍏徃"銆傚缓璁娇鐢? tc('text_l725gx') i18n/no-chinese-hardcode - 178:20 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍒囨枡-纾ㄥ垏-鍗板埛-鐑樺共"銆傚缓璁娇鐢? tc('text_kdl3cd') i18n/no-chinese-hardcode - 179:20 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "妯″垏-妫€楠?鍖呰"銆傚缓璁娇鐢? tc('text_bedugq') i18n/no-chinese-hardcode - 180:17 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鑳跺嵃"銆傚缓璁娇鐢? tc('text_me62') i18n/no-chinese-hardcode - 183:22 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "璐ㄩ噺涓荤B"銆傚缓璁娇鐢? tc('text_2e8oqd') i18n/no-chinese-hardcode - 191:19 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏍囩璐寸焊C娆?銆傚缓璁娇鐢? tc('text_ugj16y') i18n/no-chinese-hardcode - 192:20 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "涓嶅共鑳剁焊 80g"銆傚缓璁娇鐢? tc('text_16bxue') i18n/no-chinese-hardcode - 197:23 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鐜嬩簲"銆傚缓璁娇鐢? tc('text_k31l') i18n/no-chinese-hardcode - 200:20 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "涓滆帪鍒堕€犳湁闄愬叕鍙?銆傚缓璁娇鐢? tc('text_b0el29') i18n/no-chinese-hardcode - 202:20 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍒囨枡-鍗板埛-妯″垏"銆傚缓璁娇鐢? tc('text_wbrqgh') i18n/no-chinese-hardcode - 203:20 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "妫€楠?鍖呰"銆傚缓璁娇鐢? tc('text_2w0xkx') i18n/no-chinese-hardcode - 204:17 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏁扮爜鍗板埛"銆傚缓璁娇鐢? tc('text_dakmjc') i18n/no-chinese-hardcode - 207:22 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "璐ㄩ噺涓荤A"銆傚缓璁娇鐢? tc('text_2e8oqc') i18n/no-chinese-hardcode - 215:19 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "褰╁嵃鑶淒娆?銆傚缓璁娇鐢? tc('text_sy5h1r') i18n/no-chinese-hardcode - 216:20 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "BOPP褰╁嵃鑶?0.12mm"銆傚缓璁娇鐢? tc('text_16bl3d') i18n/no-chinese-hardcode - 221:23 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "璧靛叚"銆傚缓璁娇鐢? tc('text_oiag') i18n/no-chinese-hardcode - 224:20 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "浣涘北瀹炰笟闆嗗洟鏈夐檺鍏徃"銆傚缓璁娇鐢? tc('text_xrk5s1') i18n/no-chinese-hardcode - 226:20 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍒囨枡-纾ㄥ垏-鍗板埛-鐑樺共"銆傚缓璁娇鐢? tc('text_kdl3cd') i18n/no-chinese-hardcode - 227:20 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "妯″垏-妫€楠?鍖呰"銆傚缓璁娇鐢? tc('text_bedugq') i18n/no-chinese-hardcode - 228:17 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍑瑰嵃"銆傚缓璁娇鐢? tc('text_eefr') i18n/no-chinese-hardcode - 231:22 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "璐ㄩ噺涓荤C"銆傚缓璁娇鐢? tc('text_2e8oqe') i18n/no-chinese-hardcode - 239:19 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鐑敹缂╄啘E娆?銆傚缓璁娇鐢? tc('text_z73ga3') i18n/no-chinese-hardcode - 240:20 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "POF鐑敹缂╄啘 0.15mm"銆傚缓璁娇鐢? tc('text_731pjz') i18n/no-chinese-hardcode - 245:23 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "瀛欎竷"銆傚缓璁娇鐢? tc('text_fyru') i18n/no-chinese-hardcode - 248:20 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "涓北鐢靛瓙绉戞妧鏈夐檺鍏徃"銆傚缓璁娇鐢? tc('text_q59j1b') i18n/no-chinese-hardcode - 250:20 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍒囨枡-纾ㄥ垏-鍗板埛-鐑樺共"銆傚缓璁娇鐢? tc('text_kdl3cd') i18n/no-chinese-hardcode - 251:20 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "妯″垏-妫€楠?鍖呰"銆傚缓璁娇鐢? tc('text_bedugq') i18n/no-chinese-hardcode - 252:17 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏌斿嵃"銆傚缓璁娇鐢? tc('text_i49o') i18n/no-chinese-hardcode - 255:22 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "璐ㄩ噺涓荤B"銆傚缓璁娇鐢? tc('text_2e8oqd') i18n/no-chinese-hardcode - 263:19 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "淇濇姢鑶淔娆?銆傚缓璁娇鐢? tc('text_tx6jjn') i18n/no-chinese-hardcode - 264:20 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "PE淇濇姢鑶?0.05mm"銆傚缓璁娇鐢? tc('text_1ell6l') i18n/no-chinese-hardcode - 269:23 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍛ㄥ叓"銆傚缓璁娇鐢? tc('text_esxv') i18n/no-chinese-hardcode - 272:20 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鎯犲窞鍖呰鏉愭枡鏈夐檺鍏徃"銆傚缓璁娇鐢? tc('text_9vnn6u') i18n/no-chinese-hardcode - 274:20 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍒囨枡-鍗板埛-妯″垏"銆傚缓璁娇鐢? tc('text_wbrqgh') i18n/no-chinese-hardcode - 275:20 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "妫€楠?鍖呰"銆傚缓璁娇鐢? tc('text_2w0xkx') i18n/no-chinese-hardcode - 276:17 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "涓濈綉鍗板埛"銆傚缓璁娇鐢? tc('text_adqjyz') i18n/no-chinese-hardcode - 279:22 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "璐ㄩ噺涓荤A"銆傚缓璁娇鐢? tc('text_2e8oqc') i18n/no-chinese-hardcode - 287:19 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "澶嶅悎鑶淕娆?銆傚缓璁娇鐢? tc('text_45ph4o') i18n/no-chinese-hardcode - 288:20 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "澶嶅悎鑶滄潗鏂?0.2mm"銆傚缓璁娇鐢? tc('text_q8bp0e') i18n/no-chinese-hardcode - 293:23 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍚翠節"銆傚缓璁娇鐢? tc('text_er3d') i18n/no-chinese-hardcode - 296:20 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鐝犳捣杩涘嚭鍙h锤鏄撴湁闄愬叕鍙?銆傚缓璁娇鐢? tc('text_45019h') i18n/no-chinese-hardcode - 298:20 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍒囨枡-澶嶅悎-鍗板埛-鐑樺共"銆傚缓璁娇鐢? tc('text_baxqjt') i18n/no-chinese-hardcode - 299:20 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "妯″垏-妫€楠?鍖呰"銆傚缓璁娇鐢? tc('text_bedugq') i18n/no-chinese-hardcode - 300:17 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鑳跺嵃"銆傚缓璁娇鐢? tc('text_me62') i18n/no-chinese-hardcode - 303:22 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "璐ㄩ噺涓荤C"銆傚缓璁娇鐢? tc('text_2e8oqe') i18n/no-chinese-hardcode - 311:19 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍗板埛鑶淗娆?銆傚缓璁娇鐢? tc('text_i0nset') i18n/no-chinese-hardcode - 312:20 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍗板埛涓撶敤鑶?0.1mm"銆傚缓璁娇鐢? tc('text_gcasvh') i18n/no-chinese-hardcode - 317:23 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "閮戝崄"銆傚缓璁娇鐢? tc('text_p380') i18n/no-chinese-hardcode - 320:20 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "姹熼棬鍗板埛鍖呰鏈夐檺鍏徃"銆傚缓璁娇鐢? tc('text_f6japp') i18n/no-chinese-hardcode - 322:20 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍒囨枡-纾ㄥ垏-鍗板埛-鐑樺共"銆傚缓璁娇鐢? tc('text_kdl3cd') i18n/no-chinese-hardcode - 323:20 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "妯″垏-妫€楠?鍖呰"銆傚缓璁娇鐢? tc('text_bedugq') i18n/no-chinese-hardcode - 324:17 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏁扮爜鍗板埛"銆傚缓璁娇鐢? tc('text_dakmjc') i18n/no-chinese-hardcode - 327:22 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "璐ㄩ噺涓荤A"銆傚缓璁娇鐢? tc('text_2e8oqc') i18n/no-chinese-hardcode - 335:19 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏂版潗鏂橧娆?銆傚缓璁娇鐢? tc('text_f583ea') i18n/no-chinese-hardcode - 336:20 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鐢熺墿闄嶈В鑶?0.1mm"銆傚缓璁娇鐢? tc('text_gu4b5b') i18n/no-chinese-hardcode - 341:23 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "閽卞崄涓€"銆傚缓璁娇鐢? tc('text_m6mvk') i18n/no-chinese-hardcode - 344:20 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鑲囧簡鏂版潗鏂欑鎶€鏈夐檺鍏徃"銆傚缓璁娇鐢? tc('text_q9dxac') i18n/no-chinese-hardcode - 346:20 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍒囨枡-纾ㄥ垏-鍗板埛"銆傚缓璁娇鐢? tc('text_yage7a') i18n/no-chinese-hardcode - 347:20 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "妯″垏-妫€楠?鍖呰"銆傚缓璁娇鐢? tc('text_bedugq') i18n/no-chinese-hardcode - 348:17 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏌斿嵃"銆傚缓璁娇鐢? tc('text_i49o') i18n/no-chinese-hardcode - 351:22 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "璐ㄩ噺涓荤B"銆傚缓璁娇鐢? tc('text_2e8oqd') i18n/no-chinese-hardcode - 359:19 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "濉戞枡鑶淛娆?銆傚缓璁娇鐢? tc('text_3i3lns') i18n/no-chinese-hardcode - 360:20 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "PVC濉戞枡鑶?0.12mm"銆傚缓璁娇鐢? tc('text_3hjec8') i18n/no-chinese-hardcode - 365:23 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍐崄浜?銆傚缓璁娇鐢? tc('text_cdb2y') i18n/no-chinese-hardcode - 368:20 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "姹曞ご濉戞枡鍒跺搧鏈夐檺鍏徃"銆傚缓璁娇鐢? tc('text_9dmqaj') i18n/no-chinese-hardcode - 370:20 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍒囨枡-纾ㄥ垏-鍗板埛-鐑樺共"銆傚缓璁娇鐢? tc('text_kdl3cd') i18n/no-chinese-hardcode - 371:20 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "妯″垏-妫€楠?鍖呰"銆傚缓璁娇鐢? tc('text_bedugq') i18n/no-chinese-hardcode - 372:17 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍑瑰嵃"銆傚缓璁娇鐢? tc('text_eefr') i18n/no-chinese-hardcode - 375:22 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "璐ㄩ噺涓荤C"銆傚缓璁娇鐢? tc('text_2e8oqe') i18n/no-chinese-hardcode - 430:66 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "寮€濮嬭幏鍙栧搧璐ㄨ繃绋嬫楠屾暟鎹?銆傚缓璁娇鐢? tc('text_qbjepr') i18n/no-chinese-hardcode - 435:70 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "浣跨敤 mock 鏁版嵁"銆傚缓璁娇鐢? tc('text_us9wlf') i18n/no-chinese-hardcode - 484:70 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍝佽川杩囩▼妫€楠屾暟鎹幏鍙栨垚鍔?銆傚缓璁娇鐢? tc('text_b34z25') i18n/no-chinese-hardcode - 489:69 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鑾峰彇鍝佽川杩囩▼妫€楠屾暟鎹け璐?銆傚缓璁娇鐢? tc('text_7vm0qw') i18n/no-chinese-hardcode - 498:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\quality\process\page.tsx:498:5 - 496 | - 497 | useEffect(() => { -> 498 | fetchProcesses(); - | ^^^^^^^^^^^^^^ Avoid calling setState() directly within an effect - 499 | }, []); - 500 | - 501 | // 妫€楠岃〃鍗曠姸鎬? react-hooks/set-state-in-effect - 577:17 warning 'sortedProcesses' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 615:13 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "妫€楠屾彁浜ゆ垚鍔?銆傚缓璁娇鐢? tc('text_v3ahin') i18n/no-chinese-hardcode - 617:13 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鎻愪氦澶辫触"銆傚缓璁娇鐢? tc('text_cx66zs') i18n/no-chinese-hardcode - 782:28 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "杩囩▼妫€楠屾姤鍛?銆傚缓璁娇鐢? {tc('text_iegdh1')} i18n/no-chinese-hardcode - 1079:84 warning 'arr' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars - -D:\dcprint\erp-project\src\app\[locale]\quality\sgs\page.tsx - 74:7 warning 'certTypeMap' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 79:7 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍏朵粬"銆傚缓璁娇鐢? tc('text_eae8') i18n/no-chinese-hardcode - 101:21 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "閾?Pb)"銆傚缓璁娇鐢? tc('text_dgj7n8') i18n/no-chinese-hardcode - 107:21 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "闀?Cd)"銆傚缓璁娇鐢? tc('text_fh3sdl') i18n/no-chinese-hardcode - 113:21 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "姹?Hg)"銆傚缓璁娇鐢? tc('text_2g8vds') i18n/no-chinese-hardcode - 119:21 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍏环閾?Cr6+)"銆傚缓璁娇鐢? tc('text_gewps1') i18n/no-chinese-hardcode - 125:21 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "澶氭捍鑱旇嫰(PBB)"銆傚缓璁娇鐢? tc('text_vhn6ac') i18n/no-chinese-hardcode - 131:21 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "澶氭捍浜岃嫰閱?PBDE)"銆傚缓璁娇鐢? tc('text_ji279x') i18n/no-chinese-hardcode - 140:21 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "SVHC鐗╄川"銆傚缓璁娇鐢? tc('text_ud7j0j') i18n/no-chinese-hardcode - 146:21 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "閭昏嫰浜岀敳閰搁叝"銆傚缓璁娇鐢? tc('text_n6fwoh') i18n/no-chinese-hardcode - 155:21 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "閽?Ba)"銆傚缓璁娇鐢? tc('text_cwqbvn') i18n/no-chinese-hardcode - 161:21 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "闀?Cd)"銆傚缓璁娇鐢? tc('text_fh3sdl') i18n/no-chinese-hardcode - 167:21 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "閾?Cr)"銆傚缓璁娇鐢? tc('text_e1yxj2') i18n/no-chinese-hardcode - 173:21 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "閾?Pb)"銆傚缓璁娇鐢? tc('text_dgj7n8') i18n/no-chinese-hardcode - 179:21 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "姹?Hg)"銆傚缓璁娇鐢? tc('text_2g8vds') i18n/no-chinese-hardcode - 185:21 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "纭?Se)"銆傚缓璁娇鐢? tc('text_qqu9jz') i18n/no-chinese-hardcode - 194:21 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鎬昏縼绉婚噺"銆傚缓璁娇鐢? tc('text_cqllzu') i18n/no-chinese-hardcode - 200:21 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "閲嶉噾灞?浠b璁?"銆傚缓璁娇鐢? tc('text_rgy1n3') i18n/no-chinese-hardcode - 284:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\quality\sgs\page.tsx:284:5 - 282 | - 283 | useEffect(() => { -> 284 | fetchData(); - | ^^^^^^^^^ Avoid calling setState() directly within an effect - 285 | }, [page]); - 286 | useEffect(() => { - 287 | fetchWarning(); react-hooks/set-state-in-effect - 285:6 warning React Hook useEffect has a missing dependency: 'fetchData'. Either include it or remove the dependency array react-hooks/exhaustive-deps - 287:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\quality\sgs\page.tsx:287:5 - 285 | }, [page]); - 286 | useEffect(() => { -> 287 | fetchWarning(); - | ^^^^^^^^^^^^ Avoid calling setState() directly within an effect - 288 | }, []); - 289 | - 290 | const handleSave = async () => { react-hooks/set-state-in-effect - 372:52 warning Error: Cannot call impure function during render - -`Date.now` is an impure function. Calling an impure function can produce unstable results that update unpredictably when the component happens to re-render. (https://react.dev/reference/rules/components-and-hooks-must-be-pure#components-and-hooks-must-be-idempotent). - -D:\dcprint\erp-project\src\app\[locale]\quality\sgs\page.tsx:372:52 - 370 | const isExpiring = (expireDate: string) => { - 371 | if (!expireDate) return false; -> 372 | const diff = (new Date(expireDate).getTime() - Date.now()) / (1000 * 60 * 60 * 24); - | ^^^^^^^^^^ Cannot call impure function - 373 | return diff <= 90 && diff > 0; - 374 | }; - 375 | react-hooks/purity - 378:45 warning Error: Cannot call impure function during render - -`Date.now` is an impure function. Calling an impure function can produce unstable results that update unpredictably when the component happens to re-render. (https://react.dev/reference/rules/components-and-hooks-must-be-pure#components-and-hooks-must-be-idempotent). - -D:\dcprint\erp-project\src\app\[locale]\quality\sgs\page.tsx:378:45 - 376 | const isExpired = (expireDate: string) => { - 377 | if (!expireDate) return false; -> 378 | return new Date(expireDate).getTime() < Date.now(); - | ^^^^^^^^^^ Cannot call impure function - 379 | }; - 380 | - 381 | return ( react-hooks/purity - 416:37 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍏朵粬"銆傚缓璁娇鐢? {tc('text_eae8')} i18n/no-chinese-hardcode - 455:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "SGS璇佷功鎶ュ憡"銆傚缓璁娇鐢? {tc('text_7gq793')} i18n/no-chinese-hardcode - 727:39 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍏朵粬"銆傚缓璁娇鐢? {tc('text_eae8')} i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\[locale]\quality\spc\page.tsx - 120:29 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "浼樼"銆傚缓璁娇鐢? tc('text_e4dk') i18n/no-chinese-hardcode - 121:28 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍙帴鍙?銆傚缓璁娇鐢? tc('text_crzlt') i18n/no-chinese-hardcode - 122:10 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "闇€鏀瑰杽"銆傚缓璁娇鐢? tc('text_mlwmj') i18n/no-chinese-hardcode - 184:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\quality\spc\page.tsx:184:5 - 182 | - 183 | useEffect(() => { -> 184 | fetchMaterials(); - | ^^^^^^^^^^^^^^ Avoid calling setState() directly within an effect - 185 | }, [fetchMaterials]); - 186 | - 187 | const handleGenerateXbarR = async () => { react-hooks/set-state-in-effect - 270:52 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "Xbar-R 鎺у埗鍥?銆傚缓璁娇鐢? {tc('text_1wfcvl')} i18n/no-chinese-hardcode - 274:53 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "甯曠疮鎵樺垎鏋?銆傚缓璁娇鐢? {tc('text_swkcyw')} i18n/no-chinese-hardcode - 278:52 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "P鎺у埗鍥?銆傚缓璁娇鐢? {tc('text_gf09r')} i18n/no-chinese-hardcode - 288:53 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "Xbar-R 鎺у埗鍥惧弬鏁?銆傚缓璁娇鐢? {tc('text_k7dh6l')} i18n/no-chinese-hardcode - 317:56 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏉ユ枡妫€楠?銆傚缓璁娇鐢? {tc('text_dgvhr4')} i18n/no-chinese-hardcode - 318:55 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "杩囩▼妫€楠?銆傚缓璁娇鐢? {tc('text_in8d80')} i18n/no-chinese-hardcode - 319:56 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鎴愬搧妫€楠?銆傚缓璁娇鐢? {tc('text_cq7399')} i18n/no-chinese-hardcode - 368:27 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鐢熸垚鍥捐〃"銆傚缓璁娇鐢? {tc('text_f6li9n')} i18n/no-chinese-hardcode - 455:66 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "寮傚父鐐硅鍛?銆傚缓璁娇鐢? {tc('text_sonhlz')} i18n/no-chinese-hardcode - 530:38 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "X鍧囧€?銆傚缓璁娇鐢? {tc('text_h3jh')} i18n/no-chinese-hardcode - 619:38 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏋佸樊R"銆傚缓璁娇鐢? {tc('text_flr39')} i18n/no-chinese-hardcode - 673:67 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "X鍧囧€?銆傚缓璁娇鐢? {tc('text_h3jh')} i18n/no-chinese-hardcode - 674:67 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏋佸樊R"銆傚缓璁娇鐢? {tc('text_flr39')} i18n/no-chinese-hardcode - 710:126 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "X寮傚父"銆傚缓璁娇鐢? {tc('text_ihn2')} i18n/no-chinese-hardcode - 715:121 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "R寮傚父"銆傚缓璁娇鐢? {tc('text_id6w')} i18n/no-chinese-hardcode - 723:42 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "姝e父"銆傚缓璁娇鐢? {tc('text_is6t')} i18n/no-chinese-hardcode - 747:54 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "甯曠疮鎵樺垎鏋愬弬鏁?銆傚缓璁娇鐢? {tc('text_48vjx2')} i18n/no-chinese-hardcode - 781:51 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍏ㄩ儴鐗╂枡"銆傚缓璁娇鐢? {tc('text_aveyfk')} i18n/no-chinese-hardcode - 800:27 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍒嗘瀽"銆傚缓璁娇鐢? {tc('text_eiq2')} i18n/no-chinese-hardcode - 885:38 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "涓嶈壇鏁伴噺"銆傚缓璁娇鐢? {tc('text_ae06ox')} i18n/no-chinese-hardcode - 893:38 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "绱鐧惧垎姣?銆傚缓璁娇鐢? {tc('text_acpofu')} i18n/no-chinese-hardcode - 913:44 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "涓嶈壇绫诲瀷"銆傚缓璁娇鐢? {tc('text_ae3saa')} i18n/no-chinese-hardcode - 917:44 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍒嗙被"銆傚缓璁娇鐢? {tc('text_emut')} i18n/no-chinese-hardcode - 940:119 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "A绫?鍏抽敭)"銆傚缓璁娇鐢? {tc('text_kn94tc')} i18n/no-chinese-hardcode - 944:131 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "B绫?閲嶈)"銆傚缓璁娇鐢? {tc('text_ldo3dy')} i18n/no-chinese-hardcode - 948:127 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "C绫?涓€鑸?"銆傚缓璁娇鐢? {tc('text_lkqygd')} i18n/no-chinese-hardcode - 971:53 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "P鎺у埗鍥惧弬鏁?銆傚缓璁娇鐢? {tc('text_c2cpcd')} i18n/no-chinese-hardcode - 1005:51 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍏ㄩ儴鐗╂枡"銆傚缓璁娇鐢? {tc('text_aveyfk')} i18n/no-chinese-hardcode - 1024:27 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鐢熸垚"銆傚缓璁娇鐢? {tc('text_kgk1')} i18n/no-chinese-hardcode - 1044:66 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "寮傚父鐐硅鍛?銆傚缓璁娇鐢? {tc('text_sonhlz')} i18n/no-chinese-hardcode - 1135:38 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "涓嶈壇鐜?%)"銆傚缓璁娇鐢? {tc('text_ub9xjt')} i18n/no-chinese-hardcode - 1186:44 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏃舵"銆傚缓璁娇鐢? {tc('text_hxmn')} i18n/no-chinese-hardcode - 1189:67 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "涓嶈壇鐜?銆傚缓璁娇鐢? {tc('text_c2dol')} i18n/no-chinese-hardcode - 1217:85 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "寮傚父"銆傚缓璁娇鐢? {tc('text_gody')} i18n/no-chinese-hardcode - 1224:42 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "姝e父"銆傚缓璁娇鐢? {tc('text_is6t')} i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\[locale]\quality\supplier-audit\page.tsx - 78:7 warning 'statusMap' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 121:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\quality\supplier-audit\page.tsx:121:5 - 119 | - 120 | useEffect(() => { -> 121 | fetchData(); - | ^^^^^^^^^ Avoid calling setState() directly within an effect - 122 | }, [page]); - 123 | - 124 | const handleSave = async () => { react-hooks/set-state-in-effect - 122:6 warning React Hook useEffect has a missing dependency: 'fetchData'. Either include it or remove the dependency array react-hooks/exhaustive-deps - 220:26 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "渚涘簲鍟嗗鏍告姤鍛?銆傚缓璁娇鐢? {tc('text_nhnnpl')} i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\[locale]\quality\trace\page.tsx - 117:19 warning 'setKeyword' is assigned a value but never used. Allowed unused elements of array destructuring must match /^_/u @typescript-eslint/no-unused-vars - 156:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\quality\trace\page.tsx:156:5 - 154 | - 155 | useEffect(() => { -> 156 | fetchRecords(); - | ^^^^^^^^^^^^ Avoid calling setState() directly within an effect - 157 | }, [fetchRecords]); - 158 | - 159 | const handleSearch = async () => { react-hooks/set-state-in-effect - 505:28 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "杩芥函璁板綍"銆傚缓璁娇鐢? {tc('text_imokfr')} i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\[locale]\quality\unqualified\page.tsx - 139:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\quality\unqualified\page.tsx:139:5 - 137 | }; - 138 | useEffect(() => { -> 139 | fetchData(); - | ^^^^^^^^^ Avoid calling setState() directly within an effect - 140 | }, [page]); - 141 | - 142 | const openCreateDialog = () => { react-hooks/set-state-in-effect - 140:6 warning React Hook useEffect has a missing dependency: 'fetchData'. Either include it or remove the dependency array react-hooks/exhaustive-deps - 283:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "涓嶅悎鏍煎搧澶勭悊鍗?銆傚缓璁娇鐢? {tc('text_8nstgz')} i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\[locale]\reports\page.tsx - 97:5 warning Error: Cannot access variable before it is declared - -`fetchDashboardData` is accessed before it is declared, which prevents the earlier access from updating when this value changes over time. - -D:\dcprint\erp-project\src\app\[locale]\reports\page.tsx:97:5 - 95 | - 96 | useEffect(() => { -> 97 | fetchDashboardData(); - | ^^^^^^^^^^^^^^^^^^ `fetchDashboardData` accessed before it is declared - 98 | }, [period]); - 99 | - 100 | const fetchDashboardData = async () => { - -D:\dcprint\erp-project\src\app\[locale]\reports\page.tsx:100:3 - 98 | }, [period]); - 99 | -> 100 | const fetchDashboardData = async () => { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 101 | setLoading(true); - | ^^^^^^^^^^^^^^^^^^^^^ -> 102 | try { - 鈥? | ^^^^^^^^^^^^^^^^^^^^^ -> 111 | } - | ^^^^^^^^^^^^^^^^^^^^^ -> 112 | }; - | ^^^^^ `fetchDashboardData` is declared here - 113 | - 114 | const fetchSupplierStats = async () => { - 115 | try { react-hooks/immutability - 98:6 warning React Hook useEffect has a missing dependency: 'fetchDashboardData'. Either include it or remove the dependency array react-hooks/exhaustive-deps - -D:\dcprint\erp-project\src\app\[locale]\sales\delivery\page.tsx - 165:9 warning 'SHIPMENT_TYPE_MAP' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 188:22 warning 'setTypeFilter' is assigned a value but never used. Allowed unused elements of array destructuring must match /^_/u @typescript-eslint/no-unused-vars - 191:10 warning 'shipDialogOpen' is assigned a value but never used. Allowed unused elements of array destructuring must match /^_/u @typescript-eslint/no-unused-vars - 192:10 warning 'editing' is assigned a value but never used. Allowed unused elements of array destructuring must match /^_/u @typescript-eslint/no-unused-vars - 207:10 warning 'detailItems' is assigned a value but never used. Allowed unused elements of array destructuring must match /^_/u @typescript-eslint/no-unused-vars - 223:63 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "寮€濮嬭幏鍙栧彂璐у崟鍒楄〃"銆傚缓璁娇鐢? tc('text_rorf0a') i18n/no-chinese-hardcode - 230:67 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "浣跨敤 mock 鏁版嵁"銆傚缓璁娇鐢? tc('text_us9wlf') i18n/no-chinese-hardcode - 250:67 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍙戣揣鍗曞垪琛ㄨ幏鍙栨垚鍔?銆傚缓璁娇鐢? tc('text_8gy7dq') i18n/no-chinese-hardcode - 255:66 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鑾峰彇鍙戣揣鍗曞垪琛ㄥけ璐?銆傚缓璁娇鐢? tc('text_kl51z9') i18n/no-chinese-hardcode - 262:6 warning React Hook useCallback has a missing dependency: 't'. Either include it or remove the dependency array react-hooks/exhaustive-deps - 265:64 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "寮€濮嬭幏鍙栧鎴峰垪琛?銆傚缓璁娇鐢? tc('text_6signk') i18n/no-chinese-hardcode - 268:68 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "浣跨敤 mock 鏁版嵁"銆傚缓璁娇鐢? tc('text_us9wlf') i18n/no-chinese-hardcode - 277:68 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "瀹㈡埛鍒楄〃鑾峰彇鎴愬姛"銆傚缓璁娇鐢? tc('text_7nkdqs') i18n/no-chinese-hardcode - 282:67 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鑾峰彇瀹㈡埛鍒楄〃澶辫触"銆傚缓璁娇鐢? tc('text_jjguu1') i18n/no-chinese-hardcode - 296:69 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鑾峰彇閿€鍞鍗曞垪琛ㄥけ璐?銆傚缓璁娇鐢? tc('text_pnkobp') i18n/no-chinese-hardcode - 303:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\sales\delivery\page.tsx:303:5 - 301 | - 302 | useEffect(() => { -> 303 | fetchData(); - | ^^^^^^^^^ Avoid calling setState() directly within an effect - 304 | fetchCustomers(); - 305 | fetchSalesOrders(); - 306 | }, [fetchData, fetchCustomers, fetchSalesOrders]); react-hooks/set-state-in-effect - 380:9 warning 'openShipDialog' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 396:9 warning 'addShipItem' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 403:9 warning 'updateShipItem' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 411:9 warning 'removeShipItem' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 418:9 warning 'executeShipping' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 447:9 warning 'submitPartialShipment' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 479:9 warning 'submitReShip' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - -D:\dcprint\erp-project\src\app\[locale]\sales\orders\[id]\page.tsx - 255:46 warning Compilation Skipped: Existing memoization could not be preserved - -React Compiler has skipped optimizing this component because the existing manual memoization could not be preserved. The inferred dependencies did not match the manually specified dependencies, which could cause the value to change more or less frequently than expected. The inferred dependency was `order`, but the source dependencies were [order?.order_no]. Inferred less specific property than source. - -D:\dcprint\erp-project\src\app\[locale]\sales\orders\[id]\page.tsx:255:46 - 253 | }, [orderId]); - 254 | -> 255 | const fetchReceivableRecords = useCallback(async () => { - | ^^^^^^^^^^^^^ -> 256 | if (!order?.order_no) return; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 257 | setTabLoading('receivable'); - 鈥? | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 273 | } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 274 | }, [order?.order_no]); - | ^^^^ Could not preserve existing manual memoization - 275 | - 276 | useEffect(() => { - 277 | fetchOrder(); react-hooks/preserve-manual-memoization - 277:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\sales\orders\[id]\page.tsx:277:5 - 275 | - 276 | useEffect(() => { -> 277 | fetchOrder(); - | ^^^^^^^^^^ Avoid calling setState() directly within an effect - 278 | }, [fetchOrder]); - 279 | - 280 | const handleTabChange = (value: string) => { react-hooks/set-state-in-effect - -D:\dcprint\erp-project\src\app\[locale]\sales\reconciliation\page.tsx - 118:10 warning 'total' is assigned a value but never used. Allowed unused elements of array destructuring must match /^_/u @typescript-eslint/no-unused-vars - 162:6 warning React Hook useCallback has a missing dependency: 't'. Either include it or remove the dependency array react-hooks/exhaustive-deps - 175:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\sales\reconciliation\page.tsx:175:5 - 173 | - 174 | useEffect(() => { -> 175 | fetchData(); - | ^^^^^^^^^ Avoid calling setState() directly within an effect - 176 | fetchCustomers(); - 177 | }, [fetchData, fetchCustomers]); - 178 | react-hooks/set-state-in-effect - -D:\dcprint\erp-project\src\app\[locale]\sales\return\page.tsx - 161:6 warning React Hook useCallback has a missing dependency: 't'. Either include it or remove the dependency array react-hooks/exhaustive-deps - 174:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\sales\return\page.tsx:174:5 - 172 | - 173 | useEffect(() => { -> 174 | fetchData(); - | ^^^^^^^^^ Avoid calling setState() directly within an effect - 175 | fetchCustomers(); - 176 | }, [fetchData, fetchCustomers]); - 177 | react-hooks/set-state-in-effect - -D:\dcprint\erp-project\src\app\[locale]\sample\management\page.tsx - 89:7 warning 'deliveryStatusLabelMap' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 205:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\sample\management\page.tsx:205:5 - 203 | - 204 | useEffect(() => { -> 205 | fetchData(); - | ^^^^^^^^^ Avoid calling setState() directly within an effect - 206 | }, [fetchData]); - 207 | - 208 | const handleViewDetail = (item: SampleOrder) => { react-hooks/set-state-in-effect - 347:28 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏍峰搧鍒楄〃"銆傚缓璁娇鐢? {tc('text_di0t97')} i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\[locale]\sample\orders\[id]\edit\page.tsx - 102:7 warning Error: Cannot access variable before it is declared - -`fetchOrder` is accessed before it is declared, which prevents the earlier access from updating when this value changes over time. - -D:\dcprint\erp-project\src\app\[locale]\sample\orders\[id]\edit\page.tsx:102:7 - 100 | useEffect(() => { - 101 | if (id) { -> 102 | fetchOrder(); - | ^^^^^^^^^^ `fetchOrder` accessed before it is declared - 103 | } - 104 | }, [id]); - 105 | - -D:\dcprint\erp-project\src\app\[locale]\sample\orders\[id]\edit\page.tsx:106:3 - 104 | }, [id]); - 105 | -> 106 | const fetchOrder = async () => { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 107 | try { - | ^^^^^^^^^ -> 108 | setLoading(true); - 鈥? | ^^^^^^^^^ -> 151 | } - | ^^^^^^^^^ -> 152 | }; - | ^^^^^ `fetchOrder` is declared here - 153 | - 154 | const handleChange = (field: string, value: Loose) => { - 155 | setFormData((prev) => ({ ...prev, [field]: value })); react-hooks/immutability - 104:6 warning React Hook useEffect has a missing dependency: 'fetchOrder'. Either include it or remove the dependency array react-hooks/exhaustive-deps - 148:19 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鑾峰彇鎵撴牱鍗曡鎯呭け璐?銆傚缓璁娇鐢? tc('text_db0o2j') i18n/no-chinese-hardcode - 160:19 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "璇疯緭鍏ュ鎴峰悕绉?銆傚缓璁娇鐢? tc('text_lk7ny7') i18n/no-chinese-hardcode - 182:23 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鎵撴牱鍗曟洿鏂版垚鍔?銆傚缓璁娇鐢? tc('text_79hag4') i18n/no-chinese-hardcode - 188:19 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鏇存柊澶辫触"銆傚缓璁娇鐢? tc('text_det3dc') i18n/no-chinese-hardcode - 198:46 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍔犺浇涓?.."銆傚缓璁娇鐢? {tc('text_27k1ha')} i18n/no-chinese-hardcode - 215:67 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "缂栬緫鎵撴牱鍗?銆傚缓璁娇鐢? {tc('text_i7iet2')} i18n/no-chinese-hardcode - 231:48 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍩烘湰淇℃伅"銆傚缓璁娇鐢? {tc('text_biyzkw')} i18n/no-chinese-hardcode - 236:26 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏈堜唤"銆傚缓璁娇鐢? {tc('text_hyid')} i18n/no-chinese-hardcode - 254:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "瀹㈡埛鍚嶇О *"銆傚缓璁娇鐢? {tc('text_555b6m')} i18n/no-chinese-hardcode - 263:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "绉嶇被"銆傚缓璁娇鐢? {tc('text_lefi')} i18n/no-chinese-hardcode - 272:39 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "璁惧彉"銆傚缓璁娇鐢? {tc('text_o89m')} i18n/no-chinese-hardcode - 272:44 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "璁惧彉"銆傚缓璁娇鐢? {tc('text_o89m')} i18n/no-chinese-hardcode - 273:39 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "娴嬭瘯"銆傚缓璁娇鐢? {tc('text_jcve')} i18n/no-chinese-hardcode - 273:44 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "娴嬭瘯"銆傚缓璁娇鐢? {tc('text_jcve')} i18n/no-chinese-hardcode - 274:39 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏂版"銆傚缓璁娇鐢? {tc('text_hvv2')} i18n/no-chinese-hardcode - 274:44 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏂版"銆傚缓璁娇鐢? {tc('text_hvv2')} i18n/no-chinese-hardcode - 302:48 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "浜у搧淇℃伅"銆傚缓璁娇鐢? {tc('text_a9xpns')} i18n/no-chinese-hardcode - 306:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍝佸悕"銆傚缓璁娇鐢? {tc('text_evl8')} i18n/no-chinese-hardcode - 315:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏂欏彿"銆傚缓璁娇鐢? {tc('text_hqpq')} i18n/no-chinese-hardcode - 347:48 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍗板埛淇℃伅"銆傚缓璁娇鐢? {tc('text_availx')} i18n/no-chinese-hardcode - 351:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍗板埛鏂瑰紡"銆傚缓璁娇鐢? {tc('text_ave83h')} i18n/no-chinese-hardcode - 360:39 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍗锋枡涓濆嵃"銆傚缓璁娇鐢? {tc('text_ay8tit')} i18n/no-chinese-hardcode - 360:46 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍗锋枡涓濆嵃"銆傚缓璁娇鐢? {tc('text_ay8tit')} i18n/no-chinese-hardcode - 361:39 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "杞浆鍗?銆傚缓璁娇鐢? {tc('text_lp5ki')} i18n/no-chinese-hardcode - 361:45 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "杞浆鍗?銆傚缓璁娇鐢? {tc('text_lp5ki')} i18n/no-chinese-hardcode - 362:39 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "绌虹櫧"銆傚缓璁娇鐢? {tc('text_lhdv')} i18n/no-chinese-hardcode - 362:44 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "绌虹櫧"銆傚缓璁娇鐢? {tc('text_lhdv')} i18n/no-chinese-hardcode - 387:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "闇€姹傛棩鏈?銆傚缓璁娇鐢? {tc('text_jhzjj0')} i18n/no-chinese-hardcode - 400:48 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鎵撴牱淇℃伅"銆傚缓璁娇鐢? {tc('text_cu3xte')} i18n/no-chinese-hardcode - 413:39 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "浜х嚎鎷?銆傚缓璁娇鐢? {tc('text_c4u13')} i18n/no-chinese-hardcode - 413:45 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "浜х嚎鎷?銆傚缓璁娇鐢? {tc('text_c4u13')} i18n/no-chinese-hardcode - 414:39 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "绛夋潗鏂?銆傚缓璁娇鐢? {tc('text_ik7ki')} i18n/no-chinese-hardcode - 414:45 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "绛夋潗鏂?銆傚缓璁娇鐢? {tc('text_ik7ki')} i18n/no-chinese-hardcode - 415:39 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍐插帇"銆傚缓璁娇鐢? {tc('text_ecrd')} i18n/no-chinese-hardcode - 415:44 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍐插帇"銆傚缓璁娇鐢? {tc('text_ecrd')} i18n/no-chinese-hardcode - 416:39 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍗板埛"銆傚缓璁娇鐢? {tc('text_en5z')} i18n/no-chinese-hardcode - 416:44 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍗板埛"銆傚缓璁娇鐢? {tc('text_en5z')} i18n/no-chinese-hardcode - 417:39 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍒囧壊"銆傚缓璁娇鐢? {tc('text_eekr')} i18n/no-chinese-hardcode - 417:44 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍒囧壊"銆傚缓璁娇鐢? {tc('text_eekr')} i18n/no-chinese-hardcode - 419:39 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍡増"銆傚缓璁娇鐢? {tc('text_f8re')} i18n/no-chinese-hardcode - 419:44 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍡増"銆傚缓璁娇鐢? {tc('text_f8re')} i18n/no-chinese-hardcode - 420:39 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍑虹墖"銆傚缓璁娇鐢? {tc('text_ekjx')} i18n/no-chinese-hardcode - 420:44 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍑虹墖"銆傚缓璁娇鐢? {tc('text_ekjx')} i18n/no-chinese-hardcode - 421:39 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "妫€鏍?銆傚缓璁娇鐢? {tc('text_ie0n')} i18n/no-chinese-hardcode - 421:44 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "妫€鏍?銆傚缓璁娇鐢? {tc('text_ie0n')} i18n/no-chinese-hardcode - 422:39 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍋氬崱"銆傚缓璁娇鐢? {tc('text_e4hz')} i18n/no-chinese-hardcode - 422:44 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍋氬崱"銆傚缓璁娇鐢? {tc('text_e4hz')} i18n/no-chinese-hardcode - 456:39 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鐢靛瓙妗?銆傚缓璁娇鐢? {tc('text_hm4ug')} i18n/no-chinese-hardcode - 456:45 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鐢靛瓙妗?銆傚缓璁娇鐢? {tc('text_hm4ug')} i18n/no-chinese-hardcode - 457:39 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鎵撴牱鍗?銆傚缓璁娇鐢? {tc('text_ewn81')} i18n/no-chinese-hardcode - 457:45 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鎵撴牱鍗?銆傚缓璁娇鐢? {tc('text_ewn81')} i18n/no-chinese-hardcode - 476:48 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍏朵粬淇℃伅"銆傚缓璁娇鐢? {tc('text_altlu6')} i18n/no-chinese-hardcode - 496:26 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "瀹㈡埛纭"銆傚缓璁娇鐢? {tc('text_bz1n63')} i18n/no-chinese-hardcode - 519:71 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏄惁鎬ヤ欢"銆傚缓璁娇鐢? {tc('text_d8rnuw')} i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\[locale]\sample\orders\[id]\page.tsx - 59:12 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "寰呭鐞?銆傚缓璁娇鐢? tc('text_efg7b') i18n/no-chinese-hardcode - 63:12 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "杩涜涓?銆傚缓璁娇鐢? tc('text_lq5q4') i18n/no-chinese-hardcode - 67:12 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "宸插畬鎴?銆傚缓璁娇鐢? tc('text_e7hbq') i18n/no-chinese-hardcode - 71:12 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "宸插彇娑?銆傚缓璁娇鐢? tc('text_e68dg') i18n/no-chinese-hardcode - 97:7 warning Error: Cannot access variable before it is declared - -`fetchOrder` is accessed before it is declared, which prevents the earlier access from updating when this value changes over time. - -D:\dcprint\erp-project\src\app\[locale]\sample\orders\[id]\page.tsx:97:7 - 95 | useEffect(() => { - 96 | if (id) { -> 97 | fetchOrder(); - | ^^^^^^^^^^ `fetchOrder` accessed before it is declared - 98 | } - 99 | }, [id]); - 100 | - -D:\dcprint\erp-project\src\app\[locale]\sample\orders\[id]\page.tsx:101:3 - 99 | }, [id]); - 100 | -> 101 | const fetchOrder = async () => { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 102 | try { - | ^^^^^^^^^ -> 103 | setLoading(true); - 鈥? | ^^^^^^^^^ -> 116 | } - | ^^^^^^^^^ -> 117 | }; - | ^^^^^ `fetchOrder` is declared here - 118 | - 119 | const handleDelete = async () => { - 120 | try { react-hooks/immutability - 99:6 warning React Hook useEffect has a missing dependency: 'fetchOrder'. Either include it or remove the dependency array react-hooks/exhaustive-deps - 113:19 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鑾峰彇鎵撴牱鍗曡鎯呭け璐?銆傚缓璁娇鐢? tc('text_db0o2j') i18n/no-chinese-hardcode - 134:23 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鎵撴牱鍗曞垹闄ゆ垚鍔?銆傚缓璁娇鐢? tc('text_9ox3bw') i18n/no-chinese-hardcode - 140:19 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍒犻櫎澶辫触"銆傚缓璁娇鐢? tc('text_azdahk') i18n/no-chinese-hardcode - 148:46 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍔犺浇涓?.."銆傚缓璁娇鐢? {tc('text_27k1ha')} i18n/no-chinese-hardcode - 175:67 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鎵撴牱鍗曡鎯?銆傚缓璁娇鐢? {tc('text_s8k2s0')} i18n/no-chinese-hardcode - 183:48 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "缂栬緫"銆傚缓璁娇鐢? {tc('text_mekb')} i18n/no-chinese-hardcode - 187:50 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍒犻櫎"銆傚缓璁娇鐢? {tc('text_eslg')} i18n/no-chinese-hardcode - 197:48 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍩烘湰淇℃伅"銆傚缓璁娇鐢? {tc('text_biyzkw')} i18n/no-chinese-hardcode - 212:66 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏈堜唤"銆傚缓璁娇鐢? {tc('text_hyid')} i18n/no-chinese-hardcode - 213:67 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏈?銆傚缓璁娇鐢? {tc('text_kco')} i18n/no-chinese-hardcode - 221:64 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "瀹㈡埛鍚嶇О"銆傚缓璁娇鐢? {tc('text_byvcwo')} i18n/no-chinese-hardcode - 226:66 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "绉嶇被"銆傚缓璁娇鐢? {tc('text_lefi')} i18n/no-chinese-hardcode - 250:48 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "浜у搧淇℃伅"銆傚缓璁娇鐢? {tc('text_a9xpns')} i18n/no-chinese-hardcode - 254:64 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍝佸悕"銆傚缓璁娇鐢? {tc('text_evl8')} i18n/no-chinese-hardcode - 258:64 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏂欏彿"銆傚缓璁娇鐢? {tc('text_hqpq')} i18n/no-chinese-hardcode - 275:48 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍗板埛淇℃伅"銆傚缓璁娇鐢? {tc('text_availx')} i18n/no-chinese-hardcode - 280:66 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍗板埛鏂瑰紡"銆傚缓璁娇鐢? {tc('text_ave83h')} i18n/no-chinese-hardcode - 294:66 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "闇€姹傛棩鏈?銆傚缓璁娇鐢? {tc('text_jhzjj0')} i18n/no-chinese-hardcode - 304:48 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鎵撴牱淇℃伅"銆傚缓璁娇鐢? {tc('text_cu3xte')} i18n/no-chinese-hardcode - 341:48 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鐘舵€佷俊鎭?銆傚缓璁娇鐢? {tc('text_evb94p')} i18n/no-chinese-hardcode - 358:66 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏄惁鎬ヤ欢"銆傚缓璁娇鐢? {tc('text_d8rnuw')} i18n/no-chinese-hardcode - 382:66 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "瀹㈡埛纭"銆傚缓璁娇鐢? {tc('text_bz1n63')} i18n/no-chinese-hardcode - 400:28 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "纭鍒犻櫎"銆傚缓璁娇鐢? {tc('text_frotru')} i18n/no-chinese-hardcode - 401:34 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鎮ㄧ‘瀹氳鍒犻櫎鎵撴牱鍗?銆傚缓璁娇鐢? {tc('text_hi5ym8')} i18n/no-chinese-hardcode - 408:84 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍙栨秷"銆傚缓璁娇鐢? {tc('text_ev02')} i18n/no-chinese-hardcode - 411:68 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍒犻櫎"銆傚缓璁娇鐢? {tc('text_eslg')} i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\[locale]\sample\orders\page.tsx - 184:6 warning React Hook useCallback has a missing dependency: 't'. Either include it or remove the dependency array react-hooks/exhaustive-deps - 194:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\sample\orders\page.tsx:194:5 - 192 | - 193 | useEffect(() => { -> 194 | fetchOrders(); - | ^^^^^^^^^^^ Avoid calling setState() directly within an effect - 195 | }, [fetchOrders]); - 196 | - 197 | const handleCreate = async () => { react-hooks/set-state-in-effect - 379:9 warning 'handlePrint' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 571:28 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏍峰搧璁㈠崟鍒楄〃"銆傚缓璁娇鐢? {tc('text_hi04ky')} i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\[locale]\sample\standard-card\InputCardForm.tsx - 9:15 warning 'CardData' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 9:30 warning 'PrintSequence' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 69:5 warning 'customers' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 305:50 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "m/m瀹絰"銆傚缓璁娇鐢? {tc('text_1p9fqe')} i18n/no-chinese-hardcode - 314:38 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "m/m闀?銆傚缓璁娇鐢? {tc('text_1zgk4')} i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\[locale]\sample\standard-card\InputV2Form.tsx - 36:15 warning 'PrintSequence' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 46:5 warning 'loading' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 48:5 warning 'error' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 50:5 warning 'customerSearch' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 59:5 warning 'handleToggleMultiValue' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 85:57 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "宸蹭繚瀛?ID:"銆傚缓璁娇鐢? {tc('text_37wuxg')} i18n/no-chinese-hardcode - 147:49 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍩烘湰淇℃伅"銆傚缓璁娇鐢? {tc('text_biyzkw')} i18n/no-chinese-hardcode - 154:26 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏍囧噯鍗$紪鍙?銆傚缓璁娇鐢? {tc('text_8rqs71')} i18n/no-chinese-hardcode - 162:26 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏃ユ湡"銆傚缓璁娇鐢? {tc('text_hwbe')} i18n/no-chinese-hardcode - 170:26 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鐗堟湰"銆傚缓璁娇鐢? {tc('text_k06c')} i18n/no-chinese-hardcode - 178:26 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏂囦欢缂栧彿"銆傚缓璁娇鐢? {tc('text_d56bsg')} i18n/no-chinese-hardcode - 193:56 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "瀹㈡埛淇℃伅"銆傚缓璁娇鐢? {tc('text_byuibn')} i18n/no-chinese-hardcode - 199:30 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "瀹㈡埛鍚嶇О"銆傚缓璁娇鐢? {tc('text_byvcwo')} i18n/no-chinese-hardcode - 223:78 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "缂栫爜:"銆傚缓璁娇鐢? {tc('text_j6j8v')} i18n/no-chinese-hardcode - 224:60 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "| 鑱旂郴浜?"銆傚缓璁娇鐢? {tc('text_4vfpsb')} i18n/no-chinese-hardcode - 233:30 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "瀹㈡埛浠g爜"銆傚缓璁娇鐢? {tc('text_byugwj')} i18n/no-chinese-hardcode - 248:54 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "浜у搧淇℃伅"銆傚缓璁娇鐢? {tc('text_a9xpns')} i18n/no-chinese-hardcode - 254:30 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "浜у搧鍚嶇О"銆傚缓璁娇鐢? {tc('text_a9yk8t')} i18n/no-chinese-hardcode - 262:30 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鎴愬搧灏哄"銆傚缓璁娇鐢? {tc('text_cq4m7j')} i18n/no-chinese-hardcode - 270:30 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍏樊"銆傚缓璁娇鐢? {tc('text_ed4y')} i18n/no-chinese-hardcode - 282:66 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "涓嬩竴姝ワ細鏉愭枡瑙勬牸"銆傚缓璁娇鐢? {tc('text_kcmlqt')} i18n/no-chinese-hardcode - 293:48 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏉愭枡瑙勬牸"銆傚缓璁娇鐢? {tc('text_dgnf9d')} i18n/no-chinese-hardcode - 300:26 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏉愭枡鍚嶇О"銆傚缓璁娇鐢? {tc('text_dgedu4')} i18n/no-chinese-hardcode - 308:26 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏉愭枡绫诲瀷"銆傚缓璁娇鐢? {tc('text_dgl2m1')} i18n/no-chinese-hardcode - 317:41 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "纭兌"銆傚缓璁娇鐢? {tc('text_l6ve')} i18n/no-chinese-hardcode - 317:46 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "纭兌"銆傚缓璁娇鐢? {tc('text_l6ve')} i18n/no-chinese-hardcode - 318:41 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "杞兌"銆傚缓璁娇鐢? {tc('text_p3s7')} i18n/no-chinese-hardcode - 318:46 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "杞兌"銆傚缓璁娇鐢? {tc('text_p3s7')} i18n/no-chinese-hardcode - 323:26 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鎺掔増鏂瑰紡"銆傚缓璁娇鐢? {tc('text_d1cezw')} i18n/no-chinese-hardcode - 334:26 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鐗囨潗瑙勬牸"銆傚缓璁娇鐢? {tc('text_euosyp')} i18n/no-chinese-hardcode - 360:26 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鑺瀷"銆傚缓璁娇鐢? {tc('text_mpj0')} i18n/no-chinese-hardcode - 390:26 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "绾稿悜"銆傚缓璁娇鐢? {tc('text_m0k9')} i18n/no-chinese-hardcode - 398:26 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏂欏"銆傚缓璁娇鐢? {tc('text_hs90')} i18n/no-chinese-hardcode - 406:26 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "绾歌竟"銆傚缓璁娇鐢? {tc('text_mcch')} i18n/no-chinese-hardcode - 416:81 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "涓婁竴姝?銆傚缓璁娇鐢? {tc('text_btcrz')} i18n/no-chinese-hardcode - 419:63 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "涓嬩竴姝ワ細鍗板埛淇℃伅"銆傚缓璁娇鐢? {tc('text_mxzie9')} i18n/no-chinese-hardcode - 430:48 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍗板埛淇℃伅"銆傚缓璁娇鐢? {tc('text_availx')} i18n/no-chinese-hardcode - 437:26 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍗板埛绫诲瀷"銆傚缓璁娇鐢? {tc('text_avi1yv')} i18n/no-chinese-hardcode - 446:41 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鑳跺嵃"銆傚缓璁娇鐢? {tc('text_me62')} i18n/no-chinese-hardcode - 446:46 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鑳跺嵃"銆傚缓璁娇鐢? {tc('text_me62')} i18n/no-chinese-hardcode - 447:41 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍗锋枡涓濆嵃"銆傚缓璁娇鐢? {tc('text_ay8tit')} i18n/no-chinese-hardcode - 447:48 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍗锋枡涓濆嵃"銆傚缓璁娇鐢? {tc('text_ay8tit')} i18n/no-chinese-hardcode - 448:41 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鐗囨枡涓濆嵃"銆傚缓璁娇鐢? {tc('text_eu5i1x')} i18n/no-chinese-hardcode - 448:48 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鐗囨枡涓濆嵃"銆傚缓璁娇鐢? {tc('text_eu5i1x')} i18n/no-chinese-hardcode - 449:41 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "杞浆鍗?銆傚缓璁娇鐢? {tc('text_lp5ki')} i18n/no-chinese-hardcode - 449:47 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "杞浆鍗?銆傚缓璁娇鐢? {tc('text_lp5ki')} i18n/no-chinese-hardcode - 454:26 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "棣栬烦璺?銆傚缓璁娇鐢? {tc('text_n6s0w')} i18n/no-chinese-hardcode - 462:26 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏍囧噯鐢ㄩ噺"銆傚缓璁娇鐢? {tc('text_dgwgp2')} i18n/no-chinese-hardcode - 474:47 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍗板簭淇℃伅"銆傚缓璁娇鐢? {tc('text_ax3lsd')} i18n/no-chinese-hardcode - 481:56 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "搴?銆傚缓璁娇鐢? {tc('text_iof')} i18n/no-chinese-hardcode - 539:84 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "涓婁竴姝?銆傚缓璁娇鐢? {tc('text_btcrz')} i18n/no-chinese-hardcode - 542:65 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "涓嬩竴姝ワ細宸ヨ壓娴佺▼"銆傚缓璁娇鐢? {tc('text_lfak6v')} i18n/no-chinese-hardcode - 553:49 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "宸ヨ壓娴佺▼"銆傚缓璁娇鐢? {tc('text_cdzgtb')} i18n/no-chinese-hardcode - 560:26 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "宸ヨ壓娴佺▼1"銆傚缓璁娇鐢? {tc('text_svz7ea')} i18n/no-chinese-hardcode - 568:26 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "宸ヨ壓娴佺▼2"銆傚缓璁娇鐢? {tc('text_svz7eb')} i18n/no-chinese-hardcode - 579:26 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍔犲伐鏂瑰紡"銆傚缓璁娇鐢? {tc('text_atezy3')} i18n/no-chinese-hardcode - 588:41 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "妯″垏"銆傚缓璁娇鐢? {tc('text_ii2u')} i18n/no-chinese-hardcode - 588:46 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "妯″垏"銆傚缓璁娇鐢? {tc('text_ii2u')} i18n/no-chinese-hardcode - 589:41 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍐插帇"銆傚缓璁娇鐢? {tc('text_ecrd')} i18n/no-chinese-hardcode - 589:46 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍐插帇"銆傚缓璁娇鐢? {tc('text_ecrd')} i18n/no-chinese-hardcode - 594:26 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "妯″叿缂栧彿"銆傚缓璁娇鐢? {tc('text_dqfpnb')} i18n/no-chinese-hardcode - 602:26 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "婊磋兌绫诲瀷"銆傚缓璁娇鐢? {tc('text_ejab8y')} i18n/no-chinese-hardcode - 611:41 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "纭兌"銆傚缓璁娇鐢? {tc('text_l6ve')} i18n/no-chinese-hardcode - 611:46 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "纭兌"銆傚缓璁娇鐢? {tc('text_l6ve')} i18n/no-chinese-hardcode - 612:41 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "杞兌"銆傚缓璁娇鐢? {tc('text_p3s7')} i18n/no-chinese-hardcode - 612:46 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "杞兌"銆傚缓璁娇鐢? {tc('text_p3s7')} i18n/no-chinese-hardcode - 613:41 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "PU鑳?銆傚缓璁娇鐢? {tc('text_2ett')} i18n/no-chinese-hardcode - 613:47 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "PU鑳?銆傚缓璁娇鐢? {tc('text_2ett')} i18n/no-chinese-hardcode - 614:41 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍏跺畠鑳?銆傚缓璁娇鐢? {tc('text_cdtc9')} i18n/no-chinese-hardcode - 614:47 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍏跺畠鑳?銆傚缓璁娇鐢? {tc('text_cdtc9')} i18n/no-chinese-hardcode - 622:26 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍖呰绫诲瀷"銆傚缓璁娇鐢? {tc('text_b1lec0')} i18n/no-chinese-hardcode - 631:41 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍖呰"銆傚缓璁娇鐢? {tc('text_evds')} i18n/no-chinese-hardcode - 631:46 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍖呰"銆傚缓璁娇鐢? {tc('text_evds')} i18n/no-chinese-hardcode - 632:41 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "PCS/鍗?銆傚缓璁娇鐢? {tc('text_198iqw')} i18n/no-chinese-hardcode - 632:49 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "PCS/鍗?銆傚缓璁娇鐢? {tc('text_198iqw')} i18n/no-chinese-hardcode - 633:41 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "PCS/鎵?銆傚缓璁娇鐢? {tc('text_198lof')} i18n/no-chinese-hardcode - 633:49 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "PCS/鎵?銆傚缓璁娇鐢? {tc('text_198lof')} i18n/no-chinese-hardcode - 634:41 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "PCS/琚?銆傚缓璁娇鐢? {tc('text_198t8c')} i18n/no-chinese-hardcode - 634:49 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "PCS/琚?銆傚缓璁娇鐢? {tc('text_198t8c')} i18n/no-chinese-hardcode - 635:41 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "PCS/绠?銆傚缓璁娇鐢? {tc('text_198qoy')} i18n/no-chinese-hardcode - 635:49 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "PCS/绠?銆傚缓璁娇鐢? {tc('text_198qoy')} i18n/no-chinese-hardcode - 640:26 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "姣忔帓鐗囨暟"銆傚缓璁娇鐢? {tc('text_e0nnxo')} i18n/no-chinese-hardcode - 648:26 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "姣忓嵎鐗囨暟"銆傚缓璁娇鐢? {tc('text_dyaqoh')} i18n/no-chinese-hardcode - 656:26 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "姣忚鐗囨暟"銆傚缓璁娇鐢? {tc('text_e62mcl')} i18n/no-chinese-hardcode - 666:26 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "姣忕鐗囨暟"銆傚缓璁娇鐢? {tc('text_e46urv')} i18n/no-chinese-hardcode - 674:26 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "姣忔墡鐗囨暟"銆傚缓璁娇鐢? {tc('text_e0gzoo')} i18n/no-chinese-hardcode - 682:26 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍖呰鏁伴噺"銆傚缓璁娇鐢? {tc('text_b1hsqn')} i18n/no-chinese-hardcode - 699:59 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏇村瀛楁锛堥潪楂橀锛屾彁浜ゆ椂鑷姩鍖呭惈锛?銆傚缓璁娇鐢? {tc('text_erpm78')} i18n/no-chinese-hardcode - 712:32 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "闂磋窛"銆傚缓璁娇鐢? {tc('text_qa95')} i18n/no-chinese-hardcode - 720:32 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "闂磋窛鍊?銆傚缓璁娇鐢? {tc('text_mn9pv')} i18n/no-chinese-hardcode - 728:32 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "璺宠窛"銆傚缓璁娇鐢? {tc('text_ox8q')} i18n/no-chinese-hardcode - 736:32 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "璺宠窛2"銆傚缓璁娇鐢? {tc('text_lgmjs')} i18n/no-chinese-hardcode - 747:32 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍐插帇鏂规硶"銆傚缓璁娇鐢? {tc('text_anois5')} i18n/no-chinese-hardcode - 755:32 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鎺掓ā鏂规硶"銆傚缓璁娇鐢? {tc('text_d05i6j')} i18n/no-chinese-hardcode - 763:32 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鑿叉灄鍘傚晢"銆傚缓璁娇鐢? {tc('text_h2ozah')} i18n/no-chinese-hardcode - 771:32 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鑿叉灄缂栧彿"銆傚缓璁娇鐢? {tc('text_h2wdvq')} i18n/no-chinese-hardcode - 782:32 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鑳岃兌绫诲瀷"銆傚缓璁娇鐢? {tc('text_gsb3bu')} i18n/no-chinese-hardcode - 790:32 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鑳岃兌鍘傚晢"銆傚缓璁娇鐢? {tc('text_gs43zi')} i18n/no-chinese-hardcode - 798:32 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鑳岃兌缂栧彿"銆傚缓璁娇鐢? {tc('text_gsbikr')} i18n/no-chinese-hardcode - 806:32 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "铏氱嚎鍒€"銆傚缓璁娇鐢? {tc('text_kafwb')} i18n/no-chinese-hardcode - 815:55 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍚?銆傚缓璁娇鐢? {tc('text_gme')} i18n/no-chinese-hardcode - 816:54 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏄?銆傚缓璁娇鐢? {tc('text_k6n')} i18n/no-chinese-hardcode - 824:32 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "MYLAR鏉愭枡"銆傚缓璁娇鐢? {tc('text_xzv0km')} i18n/no-chinese-hardcode - 832:32 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "MYLAR瑙勬牸"銆傚缓璁娇鐢? {tc('text_xzp52v')} i18n/no-chinese-hardcode - 840:32 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "绂诲瀷绾哥绫?銆傚缓璁娇鐢? {tc('text_9ogap6')} i18n/no-chinese-hardcode - 848:32 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "绂诲瀷绾歌鏍?銆傚缓璁娇鐢? {tc('text_9odoxs')} i18n/no-chinese-hardcode - 859:32 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鑳屽垁妯″叿"銆傚缓璁娇鐢? {tc('text_glcbai')} i18n/no-chinese-hardcode - 867:32 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鑵愯殌鍒€妯?銆傚缓璁娇鐢? {tc('text_gv9glt')} i18n/no-chinese-hardcode - 875:32 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍨焊鏉愭枡"銆傚缓璁娇鐢? {tc('text_bl4pyu')} i18n/no-chinese-hardcode - 883:32 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鎵撳寘鏉愭枡"銆傚缓璁娇鐢? {tc('text_cr46vv')} i18n/no-chinese-hardcode - 894:32 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "涓撹壊淇℃伅"銆傚缓璁娇鐢? {tc('text_ae052l')} i18n/no-chinese-hardcode - 902:32 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "涓撹壊閰嶆瘮"銆傚缓璁娇鐢? {tc('text_aebbxi')} i18n/no-chinese-hardcode - 910:32 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍥炬。璺緞"銆傚缓璁娇鐢? {tc('text_bez1ay')} i18n/no-chinese-hardcode - 921:32 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍒惰〃"銆傚缓璁娇鐢? {tc('text_eqcy')} i18n/no-chinese-hardcode - 929:32 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "瀹℃牳"銆傚缓璁娇鐢? {tc('text_g5o7')} i18n/no-chinese-hardcode - 937:32 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍘傚姟"銆傚缓璁娇鐢? {tc('text_enof')} i18n/no-chinese-hardcode - 945:32 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍝佺"銆傚缓璁娇鐢? {tc('text_f3eo')} i18n/no-chinese-hardcode - 955:32 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "涓氬姟"銆傚缓璁娇鐢? {tc('text_dqkn')} i18n/no-chinese-hardcode - 963:32 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏍稿噯"銆傚缓璁娇鐢? {tc('text_i6by')} i18n/no-chinese-hardcode - 971:32 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏍峰搧淇℃伅"銆傚缓璁娇鐢? {tc('text_di07tk')} i18n/no-chinese-hardcode - 984:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "澶囨敞"銆傚缓璁娇鐢? {tc('text_fqo1')} i18n/no-chinese-hardcode - 993:81 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "涓婁竴姝?銆傚缓璁娇鐢? {tc('text_btcrz')} i18n/no-chinese-hardcode - 998:54 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "淇濆瓨"銆傚缓璁娇鐢? {tc('text_e32z')} i18n/no-chinese-hardcode - 1002:57 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "淇濆瓨骞堕瑙?銆傚缓璁娇鐢? {tc('text_uyqkw1')} i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\[locale]\sample\standard-card\input-card\page.tsx - 86:10 warning 'saving' is assigned a value but never used. Allowed unused elements of array destructuring must match /^_/u @typescript-eslint/no-unused-vars - 101:23 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "[StandardCard:Load] API杩斿洖澶辫触:"銆傚缓璁娇鐢? tc('text_uxqjbx') i18n/no-chinese-hardcode - 101:23 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "[StandardCard:Load] API杩斿洖澶辫触:"銆傚缓璁娇鐢? tc('text_uxqjbx') i18n/no-chinese-hardcode - 102:36 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍔犺浇澶辫触"銆傚缓璁娇鐢? tc('text_b0mmk1') i18n/no-chinese-hardcode - 105:21 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "[StandardCard:Load] 寮傚父:"銆傚缓璁娇鐢? tc('text_3l80if') i18n/no-chinese-hardcode - 105:21 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "[StandardCard:Load] 寮傚父:"銆傚缓璁娇鐢? tc('text_3l80if') i18n/no-chinese-hardcode - 106:49 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍔犺浇鏁版嵁澶辫触"銆傚缓璁娇鐢? tc('text_5k5pm7') i18n/no-chinese-hardcode - 139:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\sample\standard-card\input-card\page.tsx:139:5 - 137 | - 138 | useEffect(() => { -> 139 | fetchCustomers(); - | ^^^^^^^^^^^^^^ Avoid calling setState() directly within an effect - 140 | if (editId) { - 141 | loadData(editId); - 142 | } react-hooks/set-state-in-effect - 189:20 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "[StandardCard:Save] 鏍¢獙澶辫触: 瀹㈡埛涓虹┖"銆傚缓璁娇鐢? tc('text_h8ui5o') i18n/no-chinese-hardcode - 190:22 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "璇烽€夋嫨瀹㈡埛"銆傚缓璁娇鐢? tc('text_2embas') i18n/no-chinese-hardcode - 190:22 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "璇烽€夋嫨瀹㈡埛"銆傚缓璁娇鐢? tc('text_2embas') i18n/no-chinese-hardcode - 194:20 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "[StandardCard:Save] 鏍¢獙澶辫触: 鍝佸悕涓虹┖"銆傚缓璁娇鐢? tc('text_gbjdj7') i18n/no-chinese-hardcode - 195:22 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "璇疯緭鍏ュ搧鍚?銆傚缓璁娇鐢? tc('text_2jb9m3') i18n/no-chinese-hardcode - 195:22 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "璇疯緭鍏ュ搧鍚?銆傚缓璁娇鐢? tc('text_2jb9m3') i18n/no-chinese-hardcode - 215:23 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "[StandardCard:Save] API杩斿洖澶辫触:"銆傚缓璁娇鐢? tc('text_fkq0uk') i18n/no-chinese-hardcode - 215:23 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "[StandardCard:Save] API杩斿洖澶辫触:"銆傚缓璁娇鐢? tc('text_fkq0uk') i18n/no-chinese-hardcode - 216:42 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "淇濆瓨澶辫触"銆傚缓璁娇鐢? tc('text_agg8dr') i18n/no-chinese-hardcode - 221:35 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏍囧噯鍗℃洿鏂版垚鍔?銆傚缓璁娇鐢? tc('text_l3xnjh') i18n/no-chinese-hardcode - 221:47 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏍囧噯鍗′繚瀛樻垚鍔?銆傚缓璁娇鐢? tc('text_i5ksjw') i18n/no-chinese-hardcode - 227:21 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "[StandardCard:Save] 寮傚父:"銆傚缓璁娇鐢? tc('text_niwzwu') i18n/no-chinese-hardcode - 227:21 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "[StandardCard:Save] 寮傚父:"銆傚缓璁娇鐢? tc('text_niwzwu') i18n/no-chinese-hardcode - 228:22 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "淇濆瓨澶辫触锛岃妫€鏌ョ綉缁滆繛鎺?銆傚缓璁娇鐢? tc('text_ivhynl') i18n/no-chinese-hardcode - 228:22 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "淇濆瓨澶辫触锛岃妫€鏌ョ綉缁滆繛鎺?銆傚缓璁娇鐢? tc('text_ivhynl') i18n/no-chinese-hardcode - 234:9 warning 'handleSaveAndPreview' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 260:43 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍔犺浇閿欒:"銆傚缓璁娇鐢? {tc('text_ddb661')} i18n/no-chinese-hardcode - 262:51 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "杩斿洖鍒楄〃"銆傚缓璁娇鐢? {tc('text_ii78e3')} i18n/no-chinese-hardcode - 328:60 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "瀹㈡埛锛?銆傚缓璁娇鐢? {tc('text_dxa79')} i18n/no-chinese-hardcode - 362:60 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鐗堟:"銆傚缓璁娇鐢? {tc('text_h8tq9')} i18n/no-chinese-hardcode - 366:87 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏍囧噯鍗★紙娴佺▼鍗★級"銆傚缓璁娇鐢? {tc('text_shqs6g')} i18n/no-chinese-hardcode - 374:83 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏃ユ湡锛?銆傚缓璁娇鐢? {tc('text_fg874')} i18n/no-chinese-hardcode - 392:69 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍝佸悕"銆傚缓璁娇鐢? {tc('text_evl8')} i18n/no-chinese-hardcode - 400:97 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "瀹㈡埛鏂欏彿"銆傚缓璁娇鐢? {tc('text_byy4ur')} i18n/no-chinese-hardcode - 409:97 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鎴愬搧灏哄"銆傚缓璁娇鐢? {tc('text_cq4m7j')} i18n/no-chinese-hardcode - 419:62 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍏樊+"銆傚缓璁娇鐢? {tc('text_cdbah')} i18n/no-chinese-hardcode - 433:90 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏉愭枡鍚嶇О"銆傚缓璁娇鐢? {tc('text_dgedu4')} i18n/no-chinese-hardcode - 442:90 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鎺掔増鏂瑰紡"銆傚缓璁娇鐢? {tc('text_d1cezw')} i18n/no-chinese-hardcode - 454:18 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "闂磋窛"銆傚缓璁娇鐢? {tc('text_qa95')} i18n/no-chinese-hardcode - 460:90 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鐗囨枡瑙勬牸"銆傚缓璁娇鐢? {tc('text_eufrfu')} i18n/no-chinese-hardcode - 469:52 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "m/m瀹絰"銆傚缓璁娇鐢? {tc('text_1p9fqe')} i18n/no-chinese-hardcode - 478:40 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "m/m闀?銆傚缓璁娇鐢? {tc('text_1zgk4')} i18n/no-chinese-hardcode - 479:62 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏍囧噯鐢ㄩ噺"銆傚缓璁娇鐢? {tc('text_dgwgp2')} i18n/no-chinese-hardcode - 493:90 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "绾歌姱绫诲瀷"銆傚缓璁娇鐢? {tc('text_gj4hbb')} i18n/no-chinese-hardcode - 511:90 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍑虹焊鏂瑰悜"銆傚缓璁娇鐢? {tc('text_ava0vq')} i18n/no-chinese-hardcode - 526:90 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍗锋枡瀹藉害"銆傚缓璁娇鐢? {tc('text_ayb763')} i18n/no-chinese-hardcode - 536:40 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "绾歌竟"銆傚缓璁娇鐢? {tc('text_mcch')} i18n/no-chinese-hardcode - 544:62 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "璺宠窛"銆傚缓璁娇鐢? {tc('text_ox8q')} i18n/no-chinese-hardcode - 586:90 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍗板埛鏂瑰紡"銆傚缓璁娇鐢? {tc('text_ave83h')} i18n/no-chinese-hardcode - 591:23 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鑳跺嵃"銆傚缓璁娇鐢? tc('text_me62') i18n/no-chinese-hardcode - 591:29 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍗锋枡涓濆嵃"銆傚缓璁娇鐢? tc('text_ay8tit') i18n/no-chinese-hardcode - 591:37 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鐗囨枡涓濆嵃"銆傚缓璁娇鐢? tc('text_eu5i1x') i18n/no-chinese-hardcode - 591:45 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "杞浆鍗?銆傚缓璁娇鐢? tc('text_lp5ki') i18n/no-chinese-hardcode - 604:90 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "绗竴璺宠窛"銆傚缓璁娇鐢? {tc('text_fve65a')} i18n/no-chinese-hardcode - 613:74 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "瑕?鑶?銆傚缓璁娇鐢? {tc('text_k5w1u')} i18n/no-chinese-hardcode - 616:74 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鎴?鍨?銆傚缓璁娇鐢? {tc('text_edkzf')} i18n/no-chinese-hardcode - 626:90 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍗板簭"銆傚缓璁娇鐢? {tc('text_eplr')} i18n/no-chinese-hardcode - 627:90 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍗拌壊"銆傚缓璁娇鐢? {tc('text_ewoy')} i18n/no-chinese-hardcode - 628:90 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "娌瑰ⅷ缂栧彿"銆傚缓璁娇鐢? {tc('text_e39m28')} i18n/no-chinese-hardcode - 631:90 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鑿叉灄缂栧彿"銆傚缓璁娇鐢? {tc('text_h2wdvq')} i18n/no-chinese-hardcode - 634:90 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "瀛樻斁浣嶇疆"銆傚缓璁娇鐢? {tc('text_bxzajr')} i18n/no-chinese-hardcode - 637:90 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍗扮増缂栧彿"銆傚缓璁娇鐢? {tc('text_b07kah')} i18n/no-chinese-hardcode - 640:90 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "缃戠洰"銆傚缓璁娇鐢? {tc('text_mb3x')} i18n/no-chinese-hardcode - 641:90 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "瀛樻斁浣嶇疆"銆傚缓璁娇鐢? {tc('text_bxzajr')} i18n/no-chinese-hardcode - 644:90 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍗伴潰"銆傚缓璁娇鐢? {tc('text_f0tu')} i18n/no-chinese-hardcode - 645:90 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "绉嶇被"銆傚缓璁娇鐢? {tc('text_lefi')} i18n/no-chinese-hardcode - 657:23 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "妯″垏"銆傚缓璁娇鐢? tc('text_ii2u') i18n/no-chinese-hardcode - 657:29 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍐插帇"銆傚缓璁娇鐢? tc('text_ecrd') i18n/no-chinese-hardcode - 670:90 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏉愭枡"銆傚缓璁娇鐢? {tc('text_i4p5')} i18n/no-chinese-hardcode - 733:46 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍘傚晢"銆傚缓璁娇鐢? {tc('text_eo78')} i18n/no-chinese-hardcode - 740:58 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍐插帇鏂规硶"銆傚缓璁娇鐢? {tc('text_anois5')} i18n/no-chinese-hardcode - 760:46 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "缂栧彿"銆傚缓璁娇鐢? {tc('text_m2sh')} i18n/no-chinese-hardcode - 767:58 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "妯″叿缂栧彿"銆傚缓璁娇鐢? {tc('text_dqfpnb')} i18n/no-chinese-hardcode - 776:46 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鎺掓ā"銆傚缓璁娇鐢? {tc('text_hiof')} i18n/no-chinese-hardcode - 793:58 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鎺掓ā鏂规硶"銆傚缓璁娇鐢? {tc('text_d05i6j')} i18n/no-chinese-hardcode - 802:46 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "璺宠窛"銆傚缓璁娇鐢? {tc('text_ox8q')} i18n/no-chinese-hardcode - 813:58 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鑳岃兌"銆傚缓璁娇鐢? {tc('text_mm5m')} i18n/no-chinese-hardcode - 816:58 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍔犺櫄绾垮垁"銆傚缓璁娇鐢? {tc('text_azgifv')} i18n/no-chinese-hardcode - 828:36 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏄?銆傚缓璁娇鐢? {tc('text_k6n')} i18n/no-chinese-hardcode - 838:36 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍚?銆傚缓璁娇鐢? {tc('text_gme')} i18n/no-chinese-hardcode - 843:58 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍖呰"銆傚缓璁娇鐢? {tc('text_evds')} i18n/no-chinese-hardcode - 849:46 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "绉嶇被"銆傚缓璁娇鐢? {tc('text_lefi')} i18n/no-chinese-hardcode - 856:80 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍒囩墖鏂瑰紡"銆傚缓璁娇鐢? {tc('text_atos5y')} i18n/no-chinese-hardcode - 865:46 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "PCS/鎺?銆傚缓璁娇鐢? {tc('text_198lxf')} i18n/no-chinese-hardcode - 872:58 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "PCS/鍗?銆傚缓璁娇鐢? {tc('text_198iqw')} i18n/no-chinese-hardcode - 878:46 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍘傚晢"銆傚缓璁娇鐢? {tc('text_eo78')} i18n/no-chinese-hardcode - 891:46 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "PCS/琚?銆傚缓璁娇鐢? {tc('text_198t8c')} i18n/no-chinese-hardcode - 898:58 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "PCS/鎵?銆傚缓璁娇鐢? {tc('text_198lof')} i18n/no-chinese-hardcode - 917:46 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "PCS/绠?銆傚缓璁娇鐢? {tc('text_198qoy')} i18n/no-chinese-hardcode - 924:58 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "PCS/琚?銆傚缓璁娇鐢? {tc('text_198t8c')} i18n/no-chinese-hardcode - 934:74 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "涓撹壊閰嶆瘮"銆傚缓璁娇鐢? {tc('text_aebbxi')} i18n/no-chinese-hardcode - 944:52 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "绂诲瀷绾?銆傚缓璁娇鐢? {tc('text_i9gug')} i18n/no-chinese-hardcode - 947:52 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鑳屽垁鍒€妯?銆傚缓璁娇鐢? {tc('text_gl8cet')} i18n/no-chinese-hardcode - 962:52 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "PCS/绠?銆傚缓璁娇鐢? {tc('text_198qoy')} i18n/no-chinese-hardcode - 967:40 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "绉嶇被"銆傚缓璁娇鐢? {tc('text_lefi')} i18n/no-chinese-hardcode - 974:52 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鑵愯殌鍒€妯?銆傚缓璁娇鐢? {tc('text_gv9glt')} i18n/no-chinese-hardcode - 983:52 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍨焊鏉愭枡"銆傚缓璁娇鐢? {tc('text_bl4pyu')} i18n/no-chinese-hardcode - 1001:52 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鑵愯殌鍒€妯?銆傚缓璁娇鐢? {tc('text_gv9glt')} i18n/no-chinese-hardcode - 1010:52 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鎵撳寘鏉愭枡"銆傚缓璁娇鐢? {tc('text_cr46vv')} i18n/no-chinese-hardcode - 1023:52 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鐢佃剳鍥炬。瀛樺偍璺緞"銆傚缓璁娇鐢? {tc('text_lpm09y')} i18n/no-chinese-hardcode - 1034:57 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "娉ㄦ剰浜嬮」锛?銆傚缓璁娇鐢? {tc('text_c7ujut')} i18n/no-chinese-hardcode - 1046:62 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏍峰搧"銆傚缓璁娇鐢? {tc('text_i6wa')} i18n/no-chinese-hardcode - 1057:90 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍒惰〃"銆傚缓璁娇鐢? {tc('text_eqcy')} i18n/no-chinese-hardcode - 1070:90 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍘傚姟"銆傚缓璁娇鐢? {tc('text_enof')} i18n/no-chinese-hardcode - 1077:90 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍝佺"銆傚缓璁娇鐢? {tc('text_f3eo')} i18n/no-chinese-hardcode - 1084:90 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "涓氬姟"銆傚缓璁娇鐢? {tc('text_dqkn')} i18n/no-chinese-hardcode - 1088:90 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏍稿噯"銆傚缓璁娇鐢? {tc('text_i6by')} i18n/no-chinese-hardcode - 1102:52 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "缂栧彿锛?銆傚缓璁娇鐢? {tc('text_j1swp')} i18n/no-chinese-hardcode - 1120:48 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍔犺浇涓?.."銆傚缓璁娇鐢? {tc('text_27k1ha')} i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\[locale]\sample\standard-card\input-v2\page.tsx - 159:18 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "瀵煎叆灏嗚鐩栧綋鍓嶈〃鍗曞唴瀹癸紝纭畾缁х画锛?銆傚缓璁娇鐢? tc('text_1cueqx') i18n/no-chinese-hardcode - 162:22 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "妯℃澘宸插鍏?銆傚缓璁娇鐢? tc('text_2833fh') i18n/no-chinese-hardcode - 162:44 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "琛ㄥ崟鍐呭宸叉洿鏂?銆傚缓璁娇鐢? tc('text_odb62b') i18n/no-chinese-hardcode - 165:22 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "瀵煎叆澶辫触"銆傚缓璁娇鐢? tc('text_by141p') i18n/no-chinese-hardcode - 300:14 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "宸插紩鐢ㄧ孩鑹查璀﹀伐瑁?銆傚缓璁娇鐢? tc('text_9yg3pz') i18n/no-chinese-hardcode - 340:53 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "浠庢ā鏉垮鍏?銆傚缓璁娇鐢? {tc('text_x7kz0r')} i18n/no-chinese-hardcode - 344:50 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "淇濆瓨鑽夌"銆傚缓璁娇鐢? {tc('text_agnaep')} i18n/no-chinese-hardcode - 348:50 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鎻愪氦"銆傚缓璁娇鐢? {tc('text_heqc')} i18n/no-chinese-hardcode - 375:26 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍩虹淇℃伅"銆傚缓璁娇鐢? {tc('text_blh1h0')} i18n/no-chinese-hardcode - 390:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "瀹㈡埛"銆傚缓璁娇鐢? {tc('text_g4id')} i18n/no-chinese-hardcode - 409:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "浜у搧鍚嶇О"銆傚缓璁娇鐢? {tc('text_a9yk8t')} i18n/no-chinese-hardcode - 417:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍩烘潗"銆傚缓璁娇鐢? {tc('text_fj4m')} i18n/no-chinese-hardcode - 440:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "瑙勬牸"銆傚缓璁娇鐢? {tc('text_o06w')} i18n/no-chinese-hardcode - 448:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍗板埛棰滆壊"銆傚缓璁娇鐢? {tc('text_avn2ot')} i18n/no-chinese-hardcode - 456:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "娌瑰ⅷ鑹插彿"銆傚缓璁娇鐢? {tc('text_e3a6ms')} i18n/no-chinese-hardcode - 475:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍒€妯?銆傚缓璁娇鐢? {tc('text_ej35')} i18n/no-chinese-hardcode - 494:64 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "锛堝墿浣?銆傚缓璁娇鐢? {tc('text_11r860')} i18n/no-chinese-hardcode - 504:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "缃戠増"銆傚缓璁娇鐢? {tc('text_ma6v')} i18n/no-chinese-hardcode - 523:64 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "锛堝墿浣?銆傚缓璁娇鐢? {tc('text_11r860')} i18n/no-chinese-hardcode - 533:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鎹熻€楃巼(%)"銆傚缓璁娇鐢? {tc('text_cdb8n7')} i18n/no-chinese-hardcode - 542:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "棰勪及宸ユ椂"銆傚缓璁娇鐢? {tc('text_jkkmvx')} i18n/no-chinese-hardcode - 556:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鐗堟湰鍙?銆傚缓璁娇鐢? {tc('text_h8m1f')} i18n/no-chinese-hardcode - 565:26 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "宸ヨ壓鍥剧ず"銆傚缓璁娇鐢? {tc('text_cdvoc1')} i18n/no-chinese-hardcode - 573:25 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "宸ヨ壓鍥剧ず"銆傚缓璁娇鐢? {tc('text_cdvoc1')} i18n/no-chinese-hardcode - 582:58 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "绉婚櫎鍥剧ず"銆傚缓璁娇鐢? {tc('text_g0bo91')} i18n/no-chinese-hardcode - 592:49 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鐐瑰嚮涓婁紶宸ヨ壓绠€鍥?銆傚缓璁娇鐢? {tc('text_e8oyd')} i18n/no-chinese-hardcode - 593:63 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏀寔 jpg/png/gif/webp/svg锛屾渶澶?10MB"銆傚缓璁娇鐢? {tc('text_k3fu5b')} i18n/no-chinese-hardcode - 616:28 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鐗╂枡鏄庣粏"銆傚缓璁娇鐢? {tc('text_euvirs')} i18n/no-chinese-hardcode - 629:49 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "绫诲瀷"銆傚缓璁娇鐢? {tc('text_lnjk')} i18n/no-chinese-hardcode - 630:32 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鐗╂枡缂栫爜"銆傚缓璁娇鐢? {tc('text_euzqpn')} i18n/no-chinese-hardcode - 631:32 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鐗╂枡鍚嶇О"銆傚缓璁娇鐢? {tc('text_eusfkj')} i18n/no-chinese-hardcode - 632:49 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍗曡€?銆傚缓璁娇鐢? {tc('text_evky')} i18n/no-chinese-hardcode - 633:49 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍗曚綅"銆傚缓璁娇鐢? {tc('text_ely0')} i18n/no-chinese-hardcode - 634:49 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍗曚环"銆傚缓璁娇鐢? {tc('text_elvm')} i18n/no-chinese-hardcode - 652:51 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "涓绘枡"銆傚缓璁娇鐢? {tc('text_dv3y')} i18n/no-chinese-hardcode - 653:51 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "娌瑰ⅷ"銆傚缓璁娇鐢? {tc('text_iz9r')} i18n/no-chinese-hardcode - 654:51 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "杈呮枡"銆傚缓璁娇鐢? {tc('text_oywk')} i18n/no-chinese-hardcode - 751:28 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "宸ュ簭璺嚎"銆傚缓璁娇鐢? {tc('text_c8vnne')} i18n/no-chinese-hardcode - 754:54 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "娣诲姞宸ュ簭"銆傚缓璁娇鐢? {tc('text_e7xtsf')} i18n/no-chinese-hardcode - 764:49 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "搴忓彿"銆傚缓璁娇鐢? {tc('text_gjm0')} i18n/no-chinese-hardcode - 765:32 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "宸ュ簭鍚嶇О"銆傚缓璁娇鐢? {tc('text_c8ls99')} i18n/no-chinese-hardcode - 766:49 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "宸ユ椂"銆傚缓璁娇鐢? {tc('text_gj3l')} i18n/no-chinese-hardcode - 861:26 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "澶囨敞"銆傚缓璁娇鐢? {tc('text_fqo1')} i18n/no-chinese-hardcode - 884:52 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鐗╂枡鎴愭湰"銆傚缓璁娇鐢? {tc('text_euupnw')} i18n/no-chinese-hardcode - 891:50 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "浜哄伐鎴愭湰"銆傚缓璁娇鐢? {tc('text_abp687')} i18n/no-chinese-hardcode - 898:51 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "宸ヨ鎴愭湰"銆傚缓璁娇鐢? {tc('text_ceun4s')} i18n/no-chinese-hardcode - 906:57 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鎬绘垚鏈?銆傚缓璁娇鐢? {tc('text_eko0n')} i18n/no-chinese-hardcode - 923:26 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "浠庢ā鏉垮鍏?銆傚缓璁娇鐢? {tc('text_x7kz0r')} i18n/no-chinese-hardcode - 927:61 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍔犺浇涓?.."銆傚缓璁娇鐢? {tc('text_27k1ha')} i18n/no-chinese-hardcode - 929:61 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏆傛棤妯℃澘锛岃鍏堝湪宸茬‘璁ゅ伐鑹哄崱涓€屽瓨涓烘ā鏉裤€?銆傚缓璁娇鐢? {tc('text_8r1w83')} i18n/no-chinese-hardcode - 936:32 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "妯℃澘缂栧彿"銆傚缓璁娇鐢? {tc('text_dtnvrz')} i18n/no-chinese-hardcode - 937:32 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "妯℃澘鍚嶇О"銆傚缓璁娇鐢? {tc('text_dtgrr5')} i18n/no-chinese-hardcode - 938:32 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍒嗙被"銆傚缓璁娇鐢? {tc('text_emut')} i18n/no-chinese-hardcode - 939:55 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鎬绘垚鏈?銆傚缓璁娇鐢? {tc('text_eko0n')} i18n/no-chinese-hardcode - 940:56 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鎿嶄綔"銆傚缓璁娇鐢? {tc('text_hkxb')} i18n/no-chinese-hardcode - 953:90 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "閫夌敤"銆傚缓璁娇鐢? {tc('text_p54v')} i18n/no-chinese-hardcode - 964:84 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍙栨秷"銆傚缓璁娇鐢? {tc('text_ev02')} i18n/no-chinese-hardcode - 979:31 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "绾㈣壊棰勮宸ヨ寮曠敤纭"銆傚缓璁娇鐢? {tc('text_f4xe3p')} i18n/no-chinese-hardcode - 982:19 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "宸ヨ"銆傚缓璁娇鐢? {tc('text_gpz4')} i18n/no-chinese-hardcode - 983:61 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "宸茶繘鍏ョ孩鑹查璀︾姸鎬侊紝 鍓╀綑瀵垮懡浠?銆傚缓璁娇鐢? {tc('text_f70h3e')} i18n/no-chinese-hardcode - 984:69 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "娆°€?銆傚缓璁娇鐢? {tc('text_ihhd')} i18n/no-chinese-hardcode - 985:25 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "缁х画寮曠敤鍙兘瀵艰嚧鐢熶骇涓柇鎴栬川閲忛闄╋紝璇风‘璁ゆ槸鍚︿粛瑕佸紩鐢ㄦ宸ヨ銆?銆傚缓璁娇鐢? {tc('text_a6ehn2')} i18n/no-chinese-hardcode - 992:69 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍙栨秷"銆傚缓璁娇鐢? {tc('text_ev02')} i18n/no-chinese-hardcode - 993:61 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "纭寮曠敤"銆傚缓璁娇鐢? {tc('text_frqujt')} i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\[locale]\sample\standard-card\input\page.tsx - 120:19 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍩虹淇℃伅"銆傚缓璁娇鐢? tc('text_blh1h0') i18n/no-chinese-hardcode - 121:19 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鐗╂枡鏄庣粏"銆傚缓璁娇鐢? tc('text_euvirs') i18n/no-chinese-hardcode - 122:19 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "宸ュ簭璺嚎"銆傚缓璁娇鐢? tc('text_c8vnne') i18n/no-chinese-hardcode - 123:19 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鎴愭湰纭涓庢彁浜?銆傚缓璁娇鐢? tc('text_qxg4r4') i18n/no-chinese-hardcode - 181:18 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "瀵煎叆灏嗚鐩栧綋鍓嶈〃鍗曞唴瀹癸紝纭畾缁х画锛?銆傚缓璁娇鐢? tc('text_1cueqx') i18n/no-chinese-hardcode - 184:22 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "妯℃澘宸插鍏?銆傚缓璁娇鐢? tc('text_2833fh') i18n/no-chinese-hardcode - 184:44 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "琛ㄥ崟鍐呭宸叉洿鏂?銆傚缓璁娇鐢? tc('text_odb62b') i18n/no-chinese-hardcode - 187:22 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "瀵煎叆澶辫触"銆傚缓璁娇鐢? tc('text_by141p') i18n/no-chinese-hardcode - 377:14 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "宸插紩鐢ㄧ孩鑹查璀﹀伐瑁?銆傚缓璁娇鐢? tc('text_9yg3pz') i18n/no-chinese-hardcode - 420:53 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "浠庢ā鏉垮鍏?銆傚缓璁娇鐢? {tc('text_x7kz0r')} i18n/no-chinese-hardcode - 424:50 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "淇濆瓨鑽夌"銆傚缓璁娇鐢? {tc('text_agnaep')} i18n/no-chinese-hardcode - 515:22 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "瀹㈡埛"銆傚缓璁娇鐢? {tc('text_g4id')} i18n/no-chinese-hardcode - 534:22 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "浜у搧鍚嶇О"銆傚缓璁娇鐢? {tc('text_a9yk8t')} i18n/no-chinese-hardcode - 542:22 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍩烘潗"銆傚缓璁娇鐢? {tc('text_fj4m')} i18n/no-chinese-hardcode - 565:22 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "瑙勬牸"銆傚缓璁娇鐢? {tc('text_o06w')} i18n/no-chinese-hardcode - 573:22 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍗板埛棰滆壊"銆傚缓璁娇鐢? {tc('text_avn2ot')} i18n/no-chinese-hardcode - 581:22 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "娌瑰ⅷ鑹插彿"銆傚缓璁娇鐢? {tc('text_e3a6ms')} i18n/no-chinese-hardcode - 600:22 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍒€妯?銆傚缓璁娇鐢? {tc('text_ej35')} i18n/no-chinese-hardcode - 617:62 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "锛堝墿浣?銆傚缓璁娇鐢? {tc('text_11r860')} i18n/no-chinese-hardcode - 627:22 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "缃戠増"銆傚缓璁娇鐢? {tc('text_ma6v')} i18n/no-chinese-hardcode - 644:62 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "锛堝墿浣?銆傚缓璁娇鐢? {tc('text_11r860')} i18n/no-chinese-hardcode - 654:22 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鎹熻€楃巼(%)"銆傚缓璁娇鐢? {tc('text_cdb8n7')} i18n/no-chinese-hardcode - 663:22 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "棰勪及宸ユ椂"銆傚缓璁娇鐢? {tc('text_jkkmvx')} i18n/no-chinese-hardcode - 677:22 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鐗堟湰鍙?銆傚缓璁娇鐢? {tc('text_h8m1f')} i18n/no-chinese-hardcode - 681:22 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "澶囨敞"銆傚缓璁娇鐢? {tc('text_fqo1')} i18n/no-chinese-hardcode - 697:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "宸ヨ壓鍥剧ず"銆傚缓璁娇鐢? {tc('text_cdvoc1')} i18n/no-chinese-hardcode - 705:23 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "宸ヨ壓鍥剧ず"銆傚缓璁娇鐢? {tc('text_cdvoc1')} i18n/no-chinese-hardcode - 714:56 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "绉婚櫎鍥剧ず"銆傚缓璁娇鐢? {tc('text_g0bo91')} i18n/no-chinese-hardcode - 724:47 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鐐瑰嚮涓婁紶宸ヨ壓绠€鍥?銆傚缓璁娇鐢? {tc('text_e8oyd')} i18n/no-chinese-hardcode - 725:61 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏀寔 jpg/png/gif/webp/svg锛屾渶澶?10MB"銆傚缓璁娇鐢? {tc('text_k3fu5b')} i18n/no-chinese-hardcode - 763:47 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "绫诲瀷"銆傚缓璁娇鐢? {tc('text_lnjk')} i18n/no-chinese-hardcode - 764:47 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "閫夋嫨鐗╂枡"銆傚缓璁娇鐢? {tc('text_il1vtc')} i18n/no-chinese-hardcode - 765:30 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鐗╂枡缂栫爜"銆傚缓璁娇鐢? {tc('text_euzqpn')} i18n/no-chinese-hardcode - 766:30 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鐗╂枡鍚嶇О"銆傚缓璁娇鐢? {tc('text_eusfkj')} i18n/no-chinese-hardcode - 767:47 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍗曡€?銆傚缓璁娇鐢? {tc('text_evky')} i18n/no-chinese-hardcode - 768:47 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍗曚綅"銆傚缓璁娇鐢? {tc('text_ely0')} i18n/no-chinese-hardcode - 769:47 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍗曚环"銆傚缓璁娇鐢? {tc('text_elvm')} i18n/no-chinese-hardcode - 792:51 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "涓绘枡"銆傚缓璁娇鐢? {tc('text_dv3y')} i18n/no-chinese-hardcode - 793:51 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "娌瑰ⅷ"銆傚缓璁娇鐢? {tc('text_iz9r')} i18n/no-chinese-hardcode - 794:51 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "杈呮枡"銆傚缓璁娇鐢? {tc('text_oywk')} i18n/no-chinese-hardcode - 899:52 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "娣诲姞宸ュ簭"銆傚缓璁娇鐢? {tc('text_e7xtsf')} i18n/no-chinese-hardcode - 909:47 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "搴忓彿"銆傚缓璁娇鐢? {tc('text_gjm0')} i18n/no-chinese-hardcode - 910:30 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "宸ュ簭鍚嶇О"銆傚缓璁娇鐢? {tc('text_c8ls99')} i18n/no-chinese-hardcode - 911:47 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "宸ユ椂"銆傚缓璁娇鐢? {tc('text_gj3l')} i18n/no-chinese-hardcode - 1016:26 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鎴愭湰姹囨€?銆傚缓璁娇鐢? {tc('text_cswhkg')} i18n/no-chinese-hardcode - 1021:50 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鐗╂枡鎴愭湰"銆傚缓璁娇鐢? {tc('text_euupnw')} i18n/no-chinese-hardcode - 1028:48 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "浜哄伐鎴愭湰"銆傚缓璁娇鐢? {tc('text_abp687')} i18n/no-chinese-hardcode - 1035:49 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "宸ヨ鎴愭湰"銆傚缓璁娇鐢? {tc('text_ceun4s')} i18n/no-chinese-hardcode - 1042:53 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鎬绘垚鏈?銆傚缓璁娇鐢? {tc('text_eko0n')} i18n/no-chinese-hardcode - 1060:64 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍩虹淇℃伅"銆傚缓璁娇鐢? {tc('text_blh1h0')} i18n/no-chinese-hardcode - 1063:53 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "宸ヨ壓鍗″悕绉?銆傚缓璁娇鐢? {tc('text_ss0z0v')} i18n/no-chinese-hardcode - 1067:53 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "瀹㈡埛锛?銆傚缓璁娇鐢? {tc('text_dxa79')} i18n/no-chinese-hardcode - 1079:53 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "瑙勬牸锛?銆傚缓璁娇鐢? {tc('text_kpkbm')} i18n/no-chinese-hardcode - 1091:53 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍒€妯?銆傚缓璁娇鐢? {tc('text_ej35')} i18n/no-chinese-hardcode - 1095:53 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "缃戠増"銆傚缓璁娇鐢? {tc('text_ma6v')} i18n/no-chinese-hardcode - 1113:53 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "澶囨敞锛?銆傚缓璁娇鐢? {tc('text_dld2x')} i18n/no-chinese-hardcode - 1129:51 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "搴忓彿"銆傚缓璁娇鐢? {tc('text_gjm0')} i18n/no-chinese-hardcode - 1130:51 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "绫诲瀷"銆傚缓璁娇鐢? {tc('text_lnjk')} i18n/no-chinese-hardcode - 1131:34 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鐗╂枡缂栫爜"銆傚缓璁娇鐢? {tc('text_euzqpn')} i18n/no-chinese-hardcode - 1132:34 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鐗╂枡鍚嶇О"銆傚缓璁娇鐢? {tc('text_eusfkj')} i18n/no-chinese-hardcode - 1133:51 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍗曡€?銆傚缓璁娇鐢? {tc('text_evky')} i18n/no-chinese-hardcode - 1134:51 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍗曚綅"銆傚缓璁娇鐢? {tc('text_ely0')} i18n/no-chinese-hardcode - 1135:51 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍗曚环"銆傚缓璁娇鐢? {tc('text_elvm')} i18n/no-chinese-hardcode - 1172:51 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "搴忓彿"銆傚缓璁娇鐢? {tc('text_gjm0')} i18n/no-chinese-hardcode - 1173:34 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "宸ュ簭鍚嶇О"銆傚缓璁娇鐢? {tc('text_c8ls99')} i18n/no-chinese-hardcode - 1174:51 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "宸ユ椂"銆傚缓璁娇鐢? {tc('text_gj3l')} i18n/no-chinese-hardcode - 1212:48 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "淇濆瓨鑽夌"銆傚缓璁娇鐢? {tc('text_agnaep')} i18n/no-chinese-hardcode - 1224:50 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鎻愪氦"銆傚缓璁娇鐢? {tc('text_heqc')} i18n/no-chinese-hardcode - 1235:26 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "浠庢ā鏉垮鍏?銆傚缓璁娇鐢? {tc('text_x7kz0r')} i18n/no-chinese-hardcode - 1239:61 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍔犺浇涓?.."銆傚缓璁娇鐢? {tc('text_27k1ha')} i18n/no-chinese-hardcode - 1241:61 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏆傛棤妯℃澘锛岃鍏堝湪宸茬‘璁ゅ伐鑹哄崱涓€屽瓨涓烘ā鏉裤€?銆傚缓璁娇鐢? {tc('text_8r1w83')} i18n/no-chinese-hardcode - 1248:32 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "妯℃澘缂栧彿"銆傚缓璁娇鐢? {tc('text_dtnvrz')} i18n/no-chinese-hardcode - 1249:32 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "妯℃澘鍚嶇О"銆傚缓璁娇鐢? {tc('text_dtgrr5')} i18n/no-chinese-hardcode - 1250:32 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍒嗙被"銆傚缓璁娇鐢? {tc('text_emut')} i18n/no-chinese-hardcode - 1251:55 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鎬绘垚鏈?銆傚缓璁娇鐢? {tc('text_eko0n')} i18n/no-chinese-hardcode - 1252:56 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鎿嶄綔"銆傚缓璁娇鐢? {tc('text_hkxb')} i18n/no-chinese-hardcode - 1265:90 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "閫夌敤"銆傚缓璁娇鐢? {tc('text_p54v')} i18n/no-chinese-hardcode - 1276:84 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍙栨秷"銆傚缓璁娇鐢? {tc('text_ev02')} i18n/no-chinese-hardcode - 1291:31 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "绾㈣壊棰勮宸ヨ寮曠敤纭"銆傚缓璁娇鐢? {tc('text_f4xe3p')} i18n/no-chinese-hardcode - 1294:19 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "宸ヨ"銆傚缓璁娇鐢? {tc('text_gpz4')} i18n/no-chinese-hardcode - 1295:61 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "宸茶繘鍏ョ孩鑹查璀︾姸鎬侊紝 鍓╀綑瀵垮懡浠?銆傚缓璁娇鐢? {tc('text_f70h3e')} i18n/no-chinese-hardcode - 1296:69 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "娆°€?銆傚缓璁娇鐢? {tc('text_ihhd')} i18n/no-chinese-hardcode - 1297:25 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "缁х画寮曠敤鍙兘瀵艰嚧鐢熶骇涓柇鎴栬川閲忛闄╋紝璇风‘璁ゆ槸鍚︿粛瑕佸紩鐢ㄦ宸ヨ銆?銆傚缓璁娇鐢? {tc('text_a6ehn2')} i18n/no-chinese-hardcode - 1304:69 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍙栨秷"銆傚缓璁娇鐢? {tc('text_ev02')} i18n/no-chinese-hardcode - 1305:61 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "纭寮曠敤"銆傚缓璁娇鐢? {tc('text_frqujt')} i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\[locale]\sample\standard-card\page.tsx - 28:3 warning 'Edit' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 61:9 warning 'editId' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 80:21 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\sample\standard-card\page.tsx:80:21 - 78 | useEffect(() => { - 79 | const m = searchParams.get('mode'); -> 80 | if (m === 'v2') setMode('v2'); - | ^^^^^^^ Avoid calling setState() directly within an effect - 81 | else if (m === 'card') setMode('card'); - 82 | else if (!m) setMode('list'); - 83 | }, [searchParams]); react-hooks/set-state-in-effect - 121:7 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\sample\standard-card\page.tsx:121:7 - 119 | useEffect(() => { - 120 | if (mode === 'list') { -> 121 | fetchList(); - | ^^^^^^^^^ Avoid calling setState() directly within an effect - 122 | } - 123 | }, [mode, page, statusFilter, fetchList]); - 124 | react-hooks/set-state-in-effect - 161:25 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "纭鍒犻櫎閫変腑鐨?"銆傚缓璁娇鐢? tc(`text_gtbp46`) i18n/no-chinese-hardcode - 161:25 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? " 鏉℃爣鍑嗗崱鍚楋紵"銆傚缓璁娇鐢? tc(`text_79e74n`) i18n/no-chinese-hardcode - 168:24 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "宸插垹闄?"銆傚缓璁娇鐢? tc(`text_c7b50q`) i18n/no-chinese-hardcode - 168:24 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? " 鏉¤褰?銆傚缓璁娇鐢? tc(`text_gdtwm`) i18n/no-chinese-hardcode - 172:42 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鎵归噺鍒犻櫎澶辫触"銆傚缓璁娇鐢? tc('text_fgakzy') i18n/no-chinese-hardcode - 176:22 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鎵归噺鍒犻櫎澶辫触"銆傚缓璁娇鐢? tc('text_fgakzy') i18n/no-chinese-hardcode - 176:22 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鎵归噺鍒犻櫎澶辫触"銆傚缓璁娇鐢? tc('text_fgakzy') i18n/no-chinese-hardcode - 372:50 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鎵归噺鍒犻櫎 ("銆傚缓璁娇鐢? {tc('text_ffunr6')} i18n/no-chinese-hardcode - 483:41 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "閫夋嫨 "銆傚缓璁娇鐢? tc(`text_lkbhc`) i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\[locale]\sample\standard-card\print\page.tsx - 140:22 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍔犺浇鏁版嵁澶辫触 (HTTP "銆傚缓璁娇鐢? tc(`text_9euzpb`) i18n/no-chinese-hardcode - 235:40 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏍囧噯鍗′笉瀛樺湪"銆傚缓璁娇鐢? tc('text_c1941n') i18n/no-chinese-hardcode - 257:9 warning Error: Cannot access variable before it is declared - -`handlePrint` is accessed before it is declared, which prevents the earlier access from updating when this value changes over time. - -D:\dcprint\erp-project\src\app\[locale]\sample\standard-card\print\page.tsx:257:9 - 255 | autoPrintTriggered.current = true; - 256 | setTimeout(() => { -> 257 | handlePrint(); - | ^^^^^^^^^^^ `handlePrint` accessed before it is declared - 258 | }, 800); - 259 | } - 260 | }, [searchParams, loading, data]); - -D:\dcprint\erp-project\src\app\[locale]\sample\standard-card\print\page.tsx:262:3 - 260 | }, [searchParams, loading, data]); - 261 | -> 262 | const handlePrint = () => { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 263 | if (!printRef.current) return; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 264 | - 鈥? | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 327 | }, 250); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 328 | }; - | ^^^^^ `handlePrint` is declared here - 329 | - 330 | if (loading) { - 331 | return ( react-hooks/immutability - 269:32 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? " - - - 鏍囧噯鍗℃墦鍗?/title> - <style> - @page { size: A4 landscape; margin: 0; } - body { margin: 0; padding: 0; font-family: Arial, sans-serif; background: white; } - table { width: 100%; border-collapse: collapse; font-size: 12px; } - table td { padding: 2px 4px; vertical-align: middle; text-align: center; border: 1px solid #333; font-size: 12px; font-weight: normal; line-height: 1.4; color: #000; } - .border-none { border: none !important; } - .text-center { text-align: center; } - .font-bold { font-weight: bold; } - .flex { display: flex; } - .items-center { align-items: center; } - .justify-center { justify-content: center; } - .flex-1 { flex: 1; } - .mt-2 { margin-top: 8px; } - .mr-1 { margin-right: 4px; } - .text-2xl { font-size: 24px; } - .text-base { font-size: 16px; } - .text-sm { font-size: 14px; } - .text-xs { font-size: 12px; } - .text-black { color: #000; } - .text-gray-400 { color: #9ca3af; } - .bg-white { background-color: #fff; } - .bg-\\[\\#1a3c7a\\] { background-color: #1a3c7a; } - .text-\\[\\#1a3c7a\\] { color: #1a3c7a; } - .text-white { color: #fff; } - .px-3 { padding-left: 12px; padding-right: 12px; } - .py-1 { padding-top: 4px; padding-bottom: 4px; } - .rounded { border-radius: 4px; } - .gap-3 { gap: 12px; } - .gap-4 { gap: 16px; } - .w-4 { width: 16px; } - .h-4 { height: 16px; } - .w-3 { width: 12px; } - .h-3 { height: 12px; } - .align-top { vertical-align: top; } - .border-b { border-bottom: 1px solid #000; } - .border-black { border-color: #000; } - * { box-sizing: border-box; } - input[type="checkbox"], input[type="radio"] { margin-right: 4px; } - </style> - </head> - <body> - <div style="width: 297mm; height: 210mm; padding: 3mm; box-sizing: border-box; background: white;"> - "銆傚缓璁娇鐢? tc(`text_bxeysh`) i18n/no-chinese-hardcode - 341:41 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍔犺浇閿欒:"銆傚缓璁娇鐢? {tc('text_ddb661')} i18n/no-chinese-hardcode - 342:76 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "閲嶆柊褰曞叆"銆傚缓璁娇鐢? {tc('text_itdsab')} i18n/no-chinese-hardcode - 350:46 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏆傛棤鏁版嵁锛岃鍏堝綍鍏?銆傚缓璁娇鐢? {tc('text_gfmccx')} i18n/no-chinese-hardcode - 351:76 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍓嶅線褰曞叆"銆傚缓璁娇鐢? {tc('text_as5ayr')} i18n/no-chinese-hardcode - 460:49 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "杩斿洖鍒楄〃"銆傚缓璁娇鐢? {tc('text_ii78e3')} i18n/no-chinese-hardcode - 464:47 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鎵撳嵃 / 淇濆瓨PDF"銆傚缓璁娇鐢? {tc('text_bdf18d')} i18n/no-chinese-hardcode - 514:58 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "瀹㈡埛锛?銆傚缓璁娇鐢? {tc('text_dxa79')} i18n/no-chinese-hardcode - 518:58 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鐗堟:"銆傚缓璁娇鐢? {tc('text_h8tq9')} i18n/no-chinese-hardcode - 520:85 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏍囧噯鍗★紙娴佺▼鍗★級"銆傚缓璁娇鐢? {tc('text_shqs6g')} i18n/no-chinese-hardcode - 528:81 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏃ユ湡锛?銆傚缓璁娇鐢? {tc('text_fg874')} i18n/no-chinese-hardcode - 541:67 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍝佸悕"銆傚缓璁娇鐢? {tc('text_evl8')} i18n/no-chinese-hardcode - 545:123 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "瀹㈡埛鏂欏彿"銆傚缓璁娇鐢? {tc('text_byy4ur')} i18n/no-chinese-hardcode - 551:123 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鎴愬搧灏哄"銆傚缓璁娇鐢? {tc('text_cq4m7j')} i18n/no-chinese-hardcode - 558:60 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍏樊+"銆傚缓璁娇鐢? {tc('text_cdbah')} i18n/no-chinese-hardcode - 568:116 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏉愭枡鍚嶇О"銆傚缓璁娇鐢? {tc('text_dgedu4')} i18n/no-chinese-hardcode - 574:116 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鎺掔増鏂瑰紡"銆傚缓璁娇鐢? {tc('text_d1cezw')} i18n/no-chinese-hardcode - 583:16 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "闂磋窛"銆傚缓璁娇鐢? {tc('text_qa95')} i18n/no-chinese-hardcode - 589:116 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鐗囨枡瑙勬牸"銆傚缓璁娇鐢? {tc('text_eufrfu')} i18n/no-chinese-hardcode - 595:50 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "m/m瀹絰"銆傚缓璁娇鐢? {tc('text_1p9fqe')} i18n/no-chinese-hardcode - 601:38 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "m/m闀?銆傚缓璁娇鐢? {tc('text_1zgk4')} i18n/no-chinese-hardcode - 602:60 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏍囧噯鐢ㄩ噺"銆傚缓璁娇鐢? {tc('text_dgwgp2')} i18n/no-chinese-hardcode - 612:116 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "绾歌姱绫诲瀷"銆傚缓璁娇鐢? {tc('text_gj4hbb')} i18n/no-chinese-hardcode - 630:116 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍑虹焊鏂瑰悜"銆傚缓璁娇鐢? {tc('text_ava0vq')} i18n/no-chinese-hardcode - 639:116 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍗锋枡瀹藉害"銆傚缓璁娇鐢? {tc('text_ayb763')} i18n/no-chinese-hardcode - 646:38 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "绾歌竟"銆傚缓璁娇鐢? {tc('text_mcch')} i18n/no-chinese-hardcode - 651:60 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "璺宠窛"銆傚缓璁娇鐢? {tc('text_ox8q')} i18n/no-chinese-hardcode - 680:116 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍗板埛鏂瑰紡"銆傚缓璁娇鐢? {tc('text_ave83h')} i18n/no-chinese-hardcode - 685:21 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鑳跺嵃"銆傚缓璁娇鐢? tc('text_me62') i18n/no-chinese-hardcode - 685:27 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍗锋枡涓濆嵃"銆傚缓璁娇鐢? tc('text_ay8tit') i18n/no-chinese-hardcode - 685:35 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鐗囨枡涓濆嵃"銆傚缓璁娇鐢? tc('text_eu5i1x') i18n/no-chinese-hardcode - 685:43 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "杞浆鍗?銆傚缓璁娇鐢? tc('text_lp5ki') i18n/no-chinese-hardcode - 698:116 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "绗竴璺宠窛"銆傚缓璁娇鐢? {tc('text_fve65a')} i18n/no-chinese-hardcode - 704:72 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "瑕?鑶?銆傚缓璁娇鐢? {tc('text_k5w1u')} i18n/no-chinese-hardcode - 707:72 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鎴?鍨?銆傚缓璁娇鐢? {tc('text_edkzf')} i18n/no-chinese-hardcode - 716:116 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍗板簭"銆傚缓璁娇鐢? {tc('text_eplr')} i18n/no-chinese-hardcode - 719:116 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍗拌壊"銆傚缓璁娇鐢? {tc('text_ewoy')} i18n/no-chinese-hardcode - 722:116 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "娌瑰ⅷ缂栧彿"銆傚缓璁娇鐢? {tc('text_e39m28')} i18n/no-chinese-hardcode - 725:116 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鑿叉灄缂栧彿"銆傚缓璁娇鐢? {tc('text_h2wdvq')} i18n/no-chinese-hardcode - 728:116 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "瀛樻斁浣嶇疆"銆傚缓璁娇鐢? {tc('text_bxzajr')} i18n/no-chinese-hardcode - 731:116 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍗扮増缂栧彿"銆傚缓璁娇鐢? {tc('text_b07kah')} i18n/no-chinese-hardcode - 734:116 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "缃戠洰"銆傚缓璁娇鐢? {tc('text_mb3x')} i18n/no-chinese-hardcode - 737:116 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "瀛樻斁浣嶇疆"銆傚缓璁娇鐢? {tc('text_bxzajr')} i18n/no-chinese-hardcode - 740:116 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍗伴潰"銆傚缓璁娇鐢? {tc('text_f0tu')} i18n/no-chinese-hardcode - 743:116 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "绉嶇被"銆傚缓璁娇鐢? {tc('text_lefi')} i18n/no-chinese-hardcode - 754:21 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "妯″垏"銆傚缓璁娇鐢? tc('text_ii2u') i18n/no-chinese-hardcode - 754:27 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍐插帇"銆傚缓璁娇鐢? tc('text_ecrd') i18n/no-chinese-hardcode - 767:116 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏉愭枡"銆傚缓璁娇鐢? {tc('text_i4p5')} i18n/no-chinese-hardcode - 804:44 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍘傚晢"銆傚缓璁娇鐢? {tc('text_eo78')} i18n/no-chinese-hardcode - 808:56 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍐插帇鏂规硶"銆傚缓璁娇鐢? {tc('text_anois5')} i18n/no-chinese-hardcode - 822:44 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "缂栧彿"銆傚缓璁娇鐢? {tc('text_m2sh')} i18n/no-chinese-hardcode - 826:56 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "妯″叿缂栧彿"銆傚缓璁娇鐢? {tc('text_dqfpnb')} i18n/no-chinese-hardcode - 832:44 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鎺掓ā"銆傚缓璁娇鐢? {tc('text_hiof')} i18n/no-chinese-hardcode - 843:56 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鎺掓ā鏂规硶"銆傚缓璁娇鐢? {tc('text_d05i6j')} i18n/no-chinese-hardcode - 849:44 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "璺宠窛"銆傚缓璁娇鐢? {tc('text_ox8q')} i18n/no-chinese-hardcode - 857:56 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鑳岃兌"銆傚缓璁娇鐢? {tc('text_mm5m')} i18n/no-chinese-hardcode - 860:56 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍔犺櫄绾垮垁"銆傚缓璁娇鐢? {tc('text_azgifv')} i18n/no-chinese-hardcode - 871:34 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏄?銆傚缓璁娇鐢? {tc('text_k6n')} i18n/no-chinese-hardcode - 880:34 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍚?銆傚缓璁娇鐢? {tc('text_gme')} i18n/no-chinese-hardcode - 885:56 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍖呰"銆傚缓璁娇鐢? {tc('text_evds')} i18n/no-chinese-hardcode - 891:44 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "绉嶇被"銆傚缓璁娇鐢? {tc('text_lefi')} i18n/no-chinese-hardcode - 895:78 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍒囩墖鏂瑰紡"銆傚缓璁娇鐢? {tc('text_atos5y')} i18n/no-chinese-hardcode - 901:44 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "PCS/鎺?銆傚缓璁娇鐢? {tc('text_198lxf')} i18n/no-chinese-hardcode - 905:56 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "PCS/鍗?銆傚缓璁娇鐢? {tc('text_198iqw')} i18n/no-chinese-hardcode - 911:44 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍘傚晢"銆傚缓璁娇鐢? {tc('text_eo78')} i18n/no-chinese-hardcode - 918:44 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "PCS/琚?銆傚缓璁娇鐢? {tc('text_198t8c')} i18n/no-chinese-hardcode - 922:56 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "PCS/鎵?銆傚缓璁娇鐢? {tc('text_198lof')} i18n/no-chinese-hardcode - 935:44 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "PCS/绠?銆傚缓璁娇鐢? {tc('text_198qoy')} i18n/no-chinese-hardcode - 939:56 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "PCS/琚?銆傚缓璁娇鐢? {tc('text_198t8c')} i18n/no-chinese-hardcode - 950:76 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "涓撹壊閰嶆瘮"銆傚缓璁娇鐢? {tc('text_aebbxi')} i18n/no-chinese-hardcode - 955:38 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鐢佃剳鍥炬。瀛樺偍璺緞"銆傚缓璁娇鐢? {tc('text_lpm09y')} i18n/no-chinese-hardcode - 958:64 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏍峰搧"銆傚缓璁娇鐢? {tc('text_i6wa')} i18n/no-chinese-hardcode - 976:58 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "绂诲瀷绾?銆傚缓璁娇鐢? {tc('text_i9gug')} i18n/no-chinese-hardcode - 980:46 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "绉嶇被"銆傚缓璁娇鐢? {tc('text_lefi')} i18n/no-chinese-hardcode - 992:39 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鑳屽垁鍒€妯?銆傚缓璁娇鐢? tc('text_gl8cet') i18n/no-chinese-hardcode - 992:63 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鑵愯殌鍒€妯?銆傚缓璁娇鐢? tc('text_gv9glt') i18n/no-chinese-hardcode - 992:72 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鑵愯殌鍒€妯?銆傚缓璁娇鐢? tc('text_gv9glt') i18n/no-chinese-hardcode - 1007:41 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍨焊鏉愭枡"銆傚缓璁娇鐢? tc('text_bl4pyu') i18n/no-chinese-hardcode - 1007:50 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鎵撳寘鏉愭枡"銆傚缓璁娇鐢? tc('text_cr46vv') i18n/no-chinese-hardcode - 1011:58 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "PCS/绠?銆傚缓璁娇鐢? {tc('text_198qoy')} i18n/no-chinese-hardcode - 1024:43 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "娉ㄦ剰浜嬮」锛?銆傚缓璁娇鐢? tc(`text_c7ujut`) i18n/no-chinese-hardcode - 1033:116 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍒惰〃"銆傚缓璁娇鐢? {tc('text_eqcy')} i18n/no-chinese-hardcode - 1045:116 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍘傚姟"銆傚缓璁娇鐢? {tc('text_enof')} i18n/no-chinese-hardcode - 1051:116 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍝佺"銆傚缓璁娇鐢? {tc('text_f3eo')} i18n/no-chinese-hardcode - 1057:116 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "涓氬姟"銆傚缓璁娇鐢? {tc('text_dqkn')} i18n/no-chinese-hardcode - 1063:116 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏍稿噯"銆傚缓璁娇鐢? {tc('text_i6by')} i18n/no-chinese-hardcode - 1074:52 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "缂栧彿锛?銆傚缓璁娇鐢? {tc('text_j1swp')} i18n/no-chinese-hardcode - 1090:46 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍔犺浇涓?.."銆傚缓璁娇鐢? {tc('text_27k1ha')} i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\[locale]\sample\standard-card\template\page.tsx - 95:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\sample\standard-card\template\page.tsx:95:5 - 93 | - 94 | useEffect(() => { -> 95 | fetchList(); - | ^^^^^^^^^ Avoid calling setState() directly within an effect - 96 | }, [fetchList]); - 97 | - 98 | const handleOpenCreate = () => { react-hooks/set-state-in-effect - 120:22 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍔犺浇妯℃澘澶辫触"銆傚缓璁娇鐢? tc('text_6620rz') i18n/no-chinese-hardcode - 126:22 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "妯℃澘鍚嶇О涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_f2tvjj') i18n/no-chinese-hardcode - 152:24 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "淇濆瓨澶辫触"銆傚缓璁娇鐢? tc('text_agg8dr') i18n/no-chinese-hardcode - 155:22 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "淇濆瓨澶辫触"銆傚缓璁娇鐢? tc('text_agg8dr') i18n/no-chinese-hardcode - 162:18 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "纭鍒犻櫎姝ゆā鏉匡紵鍒犻櫎鍚庝笉鍙仮澶嶃€?銆傚缓璁娇鐢? tc('text_fmm526') i18n/no-chinese-hardcode - 169:24 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "宸插垹闄?銆傚缓璁娇鐢? tc('text_e65yu') i18n/no-chinese-hardcode - 172:24 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍒犻櫎澶辫触"銆傚缓璁娇鐢? tc('text_azdahk') i18n/no-chinese-hardcode - 175:22 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍒犻櫎澶辫触"銆傚缓璁娇鐢? tc('text_azdahk') i18n/no-chinese-hardcode - 189:44 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏍囧噯宸ヨ壓妯℃澘搴?銆傚缓璁娇鐢? {tc('text_5bcssf')} i18n/no-chinese-hardcode - 194:44 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏂板缓妯℃澘"銆傚缓璁娇鐢? {tc('text_d85hzc')} i18n/no-chinese-hardcode - 222:76 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍒锋柊"銆傚缓璁娇鐢? {tc('text_ejix')} i18n/no-chinese-hardcode - 232:26 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "妯℃澘缂栧彿"銆傚缓璁娇鐢? {tc('text_dtnvrz')} i18n/no-chinese-hardcode - 233:26 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "妯℃澘鍚嶇О"銆傚缓璁娇鐢? {tc('text_dtgrr5')} i18n/no-chinese-hardcode - 234:26 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍒嗙被"銆傚缓璁娇鐢? {tc('text_emut')} i18n/no-chinese-hardcode - 235:49 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鎬绘垚鏈?銆傚缓璁娇鐢? {tc('text_eko0n')} i18n/no-chinese-hardcode - 236:50 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "浣跨敤娆℃暟"銆傚缓璁娇鐢? {tc('text_ain5iw')} i18n/no-chinese-hardcode - 237:26 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍒涘缓鏃堕棿"銆傚缓璁娇鐢? {tc('text_ar84e5')} i18n/no-chinese-hardcode - 238:50 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鎿嶄綔"銆傚缓璁娇鐢? {tc('text_hkxb')} i18n/no-chinese-hardcode - 244:83 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏆傛棤妯℃澘锛岀偣鍑汇€屾柊寤烘ā鏉裤€嶆垨浠庡凡纭宸ヨ壓鍗°€屽瓨涓烘ā鏉裤€?銆傚缓璁娇鐢? {tc('text_7ek8mw')} i18n/no-chinese-hardcode - 298:12 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "涓婁竴椤?銆傚缓璁娇鐢? {tc('text_btlof')} i18n/no-chinese-hardcode - 301:51 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "绗?銆傚缓璁娇鐢? {tc('text_obw')} i18n/no-chinese-hardcode - 302:36 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "椤碉紝鍏?銆傚缓璁娇鐢? {tc('text_njqca')} i18n/no-chinese-hardcode - 302:48 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "椤?銆傚缓璁娇鐢? {tc('text_u49')} i18n/no-chinese-hardcode - 309:12 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "涓嬩竴椤?銆傚缓璁娇鐢? {tc('text_btmf4')} i18n/no-chinese-hardcode - 322:46 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "妯℃澘鍚嶇О *"銆傚缓璁娇鐢? {tc('text_37dd4l')} i18n/no-chinese-hardcode - 331:41 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍒嗙被"銆傚缓璁娇鐢? {tc('text_emut')} i18n/no-chinese-hardcode - 340:37 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏍囩锛堥€楀彿鍒嗛殧锛?銆傚缓璁娇鐢? {tc('text_hwleq2')} i18n/no-chinese-hardcode - 349:39 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "澶囨敞"銆傚缓璁娇鐢? {tc('text_fqo1')} i18n/no-chinese-hardcode - 360:94 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍙栨秷"銆傚缓璁娇鐢? {tc('text_ev02')} i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\[locale]\settings\announcement\page.tsx - 93:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\settings\announcement\page.tsx:93:5 - 91 | - 92 | useEffect(() => { -> 93 | fetchData(); - | ^^^^^^^^^ Avoid calling setState() directly within an effect - 94 | }, [fetchData]); - 95 | - 96 | const handleCreate = async () => { react-hooks/set-state-in-effect - 213:30 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍙戝竷鏃堕棿"銆傚缓璁娇鐢? {tc('text_ayuphs')} i18n/no-chinese-hardcode - 246:112 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "宸插彂甯?銆傚缓璁娇鐢? {tc('text_e656s')} i18n/no-chinese-hardcode - 264:61 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏌ョ湅"銆傚缓璁娇鐢? {tc('text_ibpi')} i18n/no-chinese-hardcode - 274:64 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍙戝竷"銆傚缓璁娇鐢? {tc('text_erte')} i18n/no-chinese-hardcode - 356:49 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "璀﹀憡"銆傚缓璁娇鐢? {tc('text_o690')} i18n/no-chinese-hardcode - 357:51 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "閲嶈"銆傚缓璁娇鐢? {tc('text_pjys')} i18n/no-chinese-hardcode - 372:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "杩囨湡鏃堕棿"銆傚缓璁娇鐢? {tc('text_ikg3cm')} i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\[locale]\settings\basics\page.tsx - 120:6 warning React Hook useCallback has a missing dependency: 'tc'. Either include it or remove the dependency array react-hooks/exhaustive-deps - 124:7 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\settings\basics\page.tsx:124:7 - 122 | useEffect(() => { - 123 | if (isAdmin) { -> 124 | loadConfigs(); - | ^^^^^^^^^^^ Avoid calling setState() directly within an effect - 125 | } else { - 126 | setLoading(false); - 127 | } react-hooks/set-state-in-effect - 240:28 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "閰嶇疆"銆傚缓璁娇鐢? {tc('text_pewx')} i18n/no-chinese-hardcode - 284:46 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "涓婚璁剧疆"銆傚缓璁娇鐢? {tc('text_ai8tm5')} i18n/no-chinese-hardcode - 300:50 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "涓婚璁剧疆"銆傚缓璁娇鐢? {tc('text_ai8tm5')} i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\[locale]\settings\config\page.tsx - 52:30 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "搴撳瓨绠$悊"銆傚缓璁娇鐢? tc('text_cbemwa') i18n/no-chinese-hardcode - 53:26 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "璁㈠崟绠$悊"銆傚缓璁娇鐢? tc('text_hytrqw') i18n/no-chinese-hardcode - 54:29 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "閲囪喘绠$悊"銆傚缓璁娇鐢? tc('text_iz76ff') i18n/no-chinese-hardcode - 55:28 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "璐㈠姟绠$悊"銆傚缓璁娇鐢? tc('text_i5j98k') i18n/no-chinese-hardcode - 56:27 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "绯荤粺璁剧疆"銆傚缓璁娇鐢? tc('text_gar1ro') i18n/no-chinese-hardcode - 57:26 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍏朵粬"銆傚缓璁娇鐢? tc('text_eae8') i18n/no-chinese-hardcode - 64:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "搴撳瓨鍏佽璐熸暟"銆傚缓璁娇鐢? tc('text_uwd5qr') i18n/no-chinese-hardcode - 68:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏄惁鍏佽搴撳瓨鏁伴噺涓鸿礋鏁?銆傚缓璁娇鐢? tc('text_n12udj') i18n/no-chinese-hardcode - 71:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "搴撳瓨棰勮闃堝€?銆傚缓璁娇鐢? tc('text_lwsuh1') i18n/no-chinese-hardcode - 75:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "浣庝簬姝ゆ暟閲忚Е鍙戝簱瀛橀璀?銆傚缓璁娇鐢? tc('text_6n71u3') i18n/no-chinese-hardcode - 78:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "搴撳瓨鐩樼偣宸紓瀹℃壒"銆傚缓璁娇鐢? tc('text_xafmum') i18n/no-chinese-hardcode - 82:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鐩樼偣宸紓鏄惁闇€瑕佸鎵?銆傚缓璁娇鐢? tc('text_v6z5x7') i18n/no-chinese-hardcode - 85:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "榛樿浠撳簱缂栫爜"銆傚缓璁娇鐢? tc('text_7y4r55') i18n/no-chinese-hardcode - 89:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏂板缓鍗曟嵁榛樿甯﹀嚭鐨勪粨搴撶紪鐮?銆傚缓璁娇鐢? tc('text_wudtjg') i18n/no-chinese-hardcode - 92:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "搴撳瓨鏁伴噺绮惧害"銆傚缓璁娇鐢? tc('text_sbwm50') i18n/no-chinese-hardcode - 96:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "搴撳瓨鏁伴噺淇濈暀鐨勫皬鏁颁綅鏁?銆傚缓璁娇鐢? tc('text_rqkw0o') i18n/no-chinese-hardcode - 99:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "瀹夊叏搴撳瓨姣斾緥"銆傚缓璁娇鐢? tc('text_agavz9') i18n/no-chinese-hardcode - 103:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "瀹夊叏搴撳瓨鍗犳棩鍧囬攢閲忕殑鐧惧垎姣?銆傚缓璁娇鐢? tc('text_4vbdg5') i18n/no-chinese-hardcode - 106:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鑷姩鐢熸垚鎵规鍙?銆傚缓璁娇鐢? tc('text_30srr4') i18n/no-chinese-hardcode - 110:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍏ュ簱鏃舵槸鍚﹁嚜鍔ㄧ敓鎴愭壒娆″彿"銆傚缓璁娇鐢? tc('text_j99qe9') i18n/no-chinese-hardcode - 113:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鎵规杩囨湡棰勮澶╂暟"銆傚缓璁娇鐢? tc('text_owtvp3') i18n/no-chinese-hardcode - 117:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "璺濇壒娆″け鏁堣繕鏈夊灏戝ぉ鏃惰繘琛岄璀?銆傚缓璁娇鐢? tc('text_5chli4') i18n/no-chinese-hardcode - 120:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "搴撳瓨鐩樼偣鍛ㄦ湡"銆傚缓璁娇鐢? tc('text_q9wmir') i18n/no-chinese-hardcode - 124:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "榛樿搴撳瓨鐩樼偣鍛ㄦ湡锛堝ぉ锛?銆傚缓璁娇鐢? tc('text_8omsv3') i18n/no-chinese-hardcode - 127:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "璐熷簱瀛橀璀︽彁閱?銆傚缓璁娇鐢? tc('text_6shkns') i18n/no-chinese-hardcode - 131:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍑虹幇璐熷簱瀛樻椂鏄惁鍙戦€侀璀︽彁閱?銆傚缓璁娇鐢? tc('text_drsyh9') i18n/no-chinese-hardcode - 134:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍏ュ簱鍗曞彿鍓嶇紑"銆傚缓璁娇鐢? tc('text_hywycj') i18n/no-chinese-hardcode - 138:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍏ュ簱鍗曞彿鍓嶇紑"銆傚缓璁娇鐢? tc('text_hywycj') i18n/no-chinese-hardcode - 141:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍑哄簱鍗曞彿鍓嶇紑"銆傚缓璁娇鐢? tc('text_hhil4e') i18n/no-chinese-hardcode - 145:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍑哄簱鍗曞彿鍓嶇紑"銆傚缓璁娇鐢? tc('text_hhil4e') i18n/no-chinese-hardcode - 149:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "璁㈠崟鍙峰墠缂€"銆傚缓璁娇鐢? tc('text_bf7qdl') i18n/no-chinese-hardcode - 153:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "閿€鍞鍗曞彿鍓嶇紑"銆傚缓璁娇鐢? tc('text_dsudl5') i18n/no-chinese-hardcode - 156:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "璁㈠崟鑷姩瀹℃牳"銆傚缓璁娇鐢? tc('text_6ufyco') i18n/no-chinese-hardcode - 160:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "璁㈠崟鎻愪氦鍚庢槸鍚﹁嚜鍔ㄥ鏍搁€氳繃"銆傚缓璁娇鐢? tc('text_n138gw') i18n/no-chinese-hardcode - 163:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "缂鸿揣澶勭悊鏂瑰紡"銆傚缓璁娇鐢? tc('text_2pc5yd') i18n/no-chinese-hardcode - 167:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "warn=鎻愰啋锛宐lock=绂佹涓嬪崟锛宎llow=鍏佽瓒呭崠"銆傚缓璁娇鐢? tc('text_byahhi') i18n/no-chinese-hardcode - 170:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "璁㈠崟瓒呮湡鎻愰啋"銆傚缓璁娇鐢? tc('text_8e6jfz') i18n/no-chinese-hardcode - 174:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "璁㈠崟瓒呰繃鎵胯浜よ揣鏈熷灏戝ぉ瑙﹀彂鎻愰啋"銆傚缓璁娇鐢? tc('text_u1sblf') i18n/no-chinese-hardcode - 177:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍏佽淇敼宸插鏍歌鍗?銆傚缓璁娇鐢? tc('text_xvw72e') i18n/no-chinese-hardcode - 181:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "宸插鏍歌鍗曟槸鍚﹀厑璁哥紪杈?銆傚缓璁娇鐢? tc('text_c27bd1') i18n/no-chinese-hardcode - 184:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "榛樿浜よ揣鍛ㄦ湡"銆傚缓璁娇鐢? tc('text_7sciey') i18n/no-chinese-hardcode - 188:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏂板缓閿€鍞鍗曢粯璁ょ殑鎵胯浜よ揣澶╂暟"銆傚缓璁娇鐢? tc('text_fvonny') i18n/no-chinese-hardcode - 191:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "璁㈠崟浠锋牸绮惧害"銆傚缓璁娇鐢? tc('text_if1fk') i18n/no-chinese-hardcode - 195:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "璁㈠崟鍗曚环涓庨噾棰濅繚鐣欑殑灏忔暟浣嶆暟"銆傚缓璁娇鐢? tc('text_8qrmsn') i18n/no-chinese-hardcode - 198:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏈€浣庤鍗曢噾棰?銆傚缓璁娇鐢? tc('text_r1liel') i18n/no-chinese-hardcode - 202:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "浣庝簬姝ら噾棰濈殑璁㈠崟灏嗚鎷掔粷锛?琛ㄧず涓嶉檺鍒?銆傚缓璁娇鐢? tc('text_qly6bq') i18n/no-chinese-hardcode - 205:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "瀹㈡埛淇$敤棰濆害鎺у埗"銆傚缓璁娇鐢? tc('text_m8huc4') i18n/no-chinese-hardcode - 209:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "涓嬪崟鏃舵槸鍚︽牎楠屽鎴峰彲鐢ㄤ俊鐢ㄩ搴?銆傚缓璁娇鐢? tc('text_xxew9g') i18n/no-chinese-hardcode - 212:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "閫€璐ф湡闄?銆傚缓璁娇鐢? tc('text_ir1854') i18n/no-chinese-hardcode - 216:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "閿€鍞鍗曞厑璁搁€€璐х殑澶╂暟"銆傚缓璁娇鐢? tc('text_hko87o') i18n/no-chinese-hardcode - 219:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏈粯娆捐嚜鍔ㄥ彇娑?銆傚缓璁娇鐢? tc('text_mhcsg') i18n/no-chinese-hardcode - 223:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏈粯娆捐鍗曡秴杩囧灏戝ぉ鑷姩鍙栨秷锛?琛ㄧず涓嶅彇娑?銆傚缓璁娇鐢? tc('text_1ikxhw') i18n/no-chinese-hardcode - 227:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "閲囪喘鍗曞彿鍓嶇紑"銆傚缓璁娇鐢? tc('text_mikvc5') i18n/no-chinese-hardcode - 231:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "閲囪喘璁㈠崟鍙峰墠缂€"銆傚缓璁娇鐢? tc('text_j9uhwx') i18n/no-chinese-hardcode - 234:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "閲囪喘浠锋牸涓婇檺鎺у埗"銆傚缓璁娇鐢? tc('text_2476jk') i18n/no-chinese-hardcode - 238:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "閲囪喘浠疯秴杩囧巻鍙叉渶楂樹环鏃舵槸鍚︽嫤鎴?銆傚缓璁娇鐢? tc('text_nbaomn') i18n/no-chinese-hardcode - 241:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "閲囪喘鍒拌揣鎻愰啋"銆傚缓璁娇鐢? tc('text_mfbbup') i18n/no-chinese-hardcode - 245:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "璺濋璁″埌璐ф棩鏈熷灏戝ぉ鍙戦€佹彁閱?銆傚缓璁娇鐢? tc('text_vm59o9') i18n/no-chinese-hardcode - 248:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "閲囪喘鍏佸樊姣斾緥"銆傚缓璁娇鐢? tc('text_mqfxti') i18n/no-chinese-hardcode - 252:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍒拌揣鏁伴噺涓庤鍗曟暟閲忕殑鍏佽鍋忓樊鐧惧垎姣?銆傚缓璁娇鐢? tc('text_s902f0') i18n/no-chinese-hardcode - 255:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "渚涘簲鍟嗚瘎浼板懆鏈?銆傚缓璁娇鐢? tc('text_tghvzk') i18n/no-chinese-hardcode - 259:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "渚涘簲鍟嗙哗鏁堣瘎浼板懆鏈燂紙澶╋級"銆傚缓璁娇鐢? tc('text_8i9n47') i18n/no-chinese-hardcode - 262:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "閲囪喘榛樿绋庣巼"銆傚缓璁娇鐢? tc('text_crd9yt') i18n/no-chinese-hardcode - 266:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "閲囪喘鍗曟嵁榛樿绋庣巼锛?锛?銆傚缓璁娇鐢? tc('text_hbdurc') i18n/no-chinese-hardcode - 269:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "閲囪喘鍒拌揣鑷姩鍏ュ簱"銆傚缓璁娇鐢? tc('text_owfomv') i18n/no-chinese-hardcode - 273:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "閲囪喘鍒拌揣纭鍚庢槸鍚﹁嚜鍔ㄧ敓鎴愬叆搴撳崟"銆傚缓璁娇鐢? tc('text_4pxry2') i18n/no-chinese-hardcode - 276:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "閲囪喘瀹℃壒閲戦闃堝€?銆傚缓璁娇鐢? tc('text_vi7ye6') i18n/no-chinese-hardcode - 280:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "瓒呰繃姝ら噾棰濈殑閲囪喘鍗曢渶瑕佸鎵?銆傚缓璁娇鐢? tc('text_ko5lte') i18n/no-chinese-hardcode - 283:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏈€灏忛噰璐壒閲?銆傚缓璁娇鐢? tc('text_6vv16z') i18n/no-chinese-hardcode - 287:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "閲囪喘鏄庣粏榛樿鏈€灏忚捣璁㈡暟閲?銆傚缓璁娇鐢? tc('text_n3k01v') i18n/no-chinese-hardcode - 290:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "閲囪喘閫€璐ф湡闄?銆傚缓璁娇鐢? tc('text_emmq82') i18n/no-chinese-hardcode - 294:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "閲囪喘璁㈠崟鍏佽閫€璐х殑澶╂暟"銆傚缓璁娇鐢? tc('text_28y250') i18n/no-chinese-hardcode - 298:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "榛樿浠樻鏈熼檺"銆傚缓璁娇鐢? tc('text_7wao8t') i18n/no-chinese-hardcode - 302:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "榛樿浠樻鏈熼檺澶╂暟"銆傚缓璁娇鐢? tc('text_b6keay') i18n/no-chinese-hardcode - 305:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "绋庣巼"銆傚缓璁娇鐢? tc('text_le7t') i18n/no-chinese-hardcode - 309:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "榛樿澧炲€肩◣绋庣巼锛?锛?銆傚缓璁娇鐢? tc('text_ntrb3l') i18n/no-chinese-hardcode - 312:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "榛樿甯佺"銆傚缓璁娇鐢? tc('text_km5xvc') i18n/no-chinese-hardcode - 316:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "绯荤粺榛樿缁撶畻甯佺"銆傚缓璁娇鐢? tc('text_kra9ls') i18n/no-chinese-hardcode - 319:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "璐㈠姟缁撹处鏃?銆傚缓璁娇鐢? tc('text_5gpswd') i18n/no-chinese-hardcode - 323:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "姣忔湀璐㈠姟缁撹处鏃ワ紙鏃ユ湡锛?銆傚缓璁娇鐢? tc('text_aillzz') i18n/no-chinese-hardcode - 326:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "搴旀敹璐︽棰勮"銆傚缓璁娇鐢? tc('text_fku5p8') i18n/no-chinese-hardcode - 330:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "搴旀敹璐︽瓒呰繃澶氬皯澶╂湭鏀跺洖瑙﹀彂棰勮"銆傚缓璁娇鐢? tc('text_f2zc2f') i18n/no-chinese-hardcode - 333:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍚敤澶氬竵绉?銆傚缓璁娇鐢? tc('text_auh243') i18n/no-chinese-hardcode - 337:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏄惁鍚敤澶氬竵绉嶆牳绠?銆傚缓璁娇鐢? tc('text_3coygl') i18n/no-chinese-hardcode - 340:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "榛樿鍙戠エ绫诲瀷"銆傚缓璁娇鐢? tc('text_77jutp') i18n/no-chinese-hardcode - 344:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "vat=澧炲€肩◣涓撶敤鍙戠エ锛宯ormal=鏅€氬彂绁?銆傚缓璁娇鐢? tc('text_nimpaj') i18n/no-chinese-hardcode - 347:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鎴愭湰璁′环鏂规硶"銆傚缓璁娇鐢? tc('text_j334ni') i18n/no-chinese-hardcode - 351:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "moving_avg=绉诲姩骞冲潎锛宖ifo=鍏堣繘鍏堝嚭锛寃eighted=鍔犳潈骞冲潎"銆傚缓璁娇鐢? tc('text_dtugo') i18n/no-chinese-hardcode - 354:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鐜伴噾鎶樻墸鐜?銆傚缓璁娇鐢? tc('text_q2oucl') i18n/no-chinese-hardcode - 358:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鎻愬墠浠樻鍙韩鍙楃殑鐜伴噾鎶樻墸鐜囷紙%锛?銆傚缓璁娇鐢? tc('text_sh7f4z') i18n/no-chinese-hardcode - 361:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "閲戦鑸嶅叆鏂瑰紡"銆傚缓璁娇鐢? tc('text_wtn9sq') i18n/no-chinese-hardcode - 365:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "round_half_up=鍥涜垗浜斿叆锛宺ound_up=鍚戜笂锛宺ound_down=鍚戜笅"銆傚缓璁娇鐢? tc('text_zee3y8') i18n/no-chinese-hardcode - 369:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "瀵嗙爜鏈€灏忛暱搴?銆傚缓璁娇鐢? tc('text_scr9ox') i18n/no-chinese-hardcode - 373:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鐢ㄦ埛瀵嗙爜鏈€灏忛暱搴﹁姹?銆傚缓璁娇鐢? tc('text_skskn5') i18n/no-chinese-hardcode - 376:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鐧诲綍澶辫触閿佸畾娆℃暟"銆傚缓璁娇鐢? tc('text_42f9l6') i18n/no-chinese-hardcode - 380:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "杩炵画鐧诲綍澶辫触澶氬皯娆″悗閿佸畾璐﹀彿"銆傚缓璁娇鐢? tc('text_ac40x7') i18n/no-chinese-hardcode - 383:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鐧诲綍閿佸畾鏃堕棿"銆傚缓璁娇鐢? tc('text_lkfzin') i18n/no-chinese-hardcode - 387:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "璐﹀彿閿佸畾鎸佺画鏃堕棿锛堝垎閽燂級"銆傚缓璁娇鐢? tc('text_yntme4') i18n/no-chinese-hardcode - 390:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "瀵嗙爜杩囨湡澶╂暟"銆傚缓璁娇鐢? tc('text_xj8xlm') i18n/no-chinese-hardcode - 394:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "0琛ㄧず姘镐笉杩囨湡"銆傚缓璁娇鐢? tc('text_vigai9') i18n/no-chinese-hardcode - 397:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍒濆瀵嗙爜寮哄埗淇敼"銆傚缓璁娇鐢? tc('text_j0i1zk') i18n/no-chinese-hardcode - 401:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "棣栨鐧诲綍鏄惁寮哄埗淇敼瀵嗙爜"銆傚缓璁娇鐢? tc('text_nkt7he') i18n/no-chinese-hardcode - 404:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鎿嶄綔鏃ュ織淇濈暀澶╂暟"銆傚缓璁娇鐢? tc('text_zb9d6k') i18n/no-chinese-hardcode - 408:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鎿嶄綔鏃ュ織淇濈暀澶╂暟锛?琛ㄧず姘镐箙淇濈暀"銆傚缓璁娇鐢? tc('text_69kt43') i18n/no-chinese-hardcode - 411:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "浼氳瘽瓒呮椂"銆傚缓璁娇鐢? tc('text_akc8yc') i18n/no-chinese-hardcode - 415:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鐢ㄦ埛浼氳瘽瓒呮椂鏃堕棿锛堝垎閽燂級"銆傚缓璁娇鐢? tc('text_5ysclz') i18n/no-chinese-hardcode - 418:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "绯荤粺鍚嶇О"銆傚缓璁娇鐢? tc('text_gahjnr') i18n/no-chinese-hardcode - 422:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "绯荤粺鐧诲綍椤典笌鏍囬鏄剧ず鐨勫悕绉?銆傚缓璁娇鐢? tc('text_mv4eu5') i18n/no-chinese-hardcode - 425:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "榛樿璇█"銆傚缓璁娇鐢? tc('text_kmdu9r') i18n/no-chinese-hardcode - 429:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏂扮敤鎴烽粯璁よ瑷€"銆傚缓璁娇鐢? tc('text_cys8ci') i18n/no-chinese-hardcode - 432:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "绯荤粺榛樿鏃跺尯"銆傚缓璁娇鐢? tc('text_wctcdo') i18n/no-chinese-hardcode - 436:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "绯荤粺鏃堕棿鏄剧ず浣跨敤鐨勬椂鍖?銆傚缓璁娇鐢? tc('text_hp0xy9') i18n/no-chinese-hardcode - 439:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏁版嵁澶囦唤鍛ㄦ湡"銆傚缓璁娇鐢? tc('text_j18xrp') i18n/no-chinese-hardcode - 443:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鑷姩鏁版嵁澶囦唤鍛ㄦ湡锛堝ぉ锛?銆傚缓璁娇鐢? tc('text_8ki529') i18n/no-chinese-hardcode - 446:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍚敤鍙屽洜绱犺璇?銆傚缓璁娇鐢? tc('text_3jp7rk') i18n/no-chinese-hardcode - 450:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鐧诲綍鏃舵槸鍚﹀惎鐢ㄥ弻鍥犵礌璁よ瘉锛?FA锛?銆傚缓璁娇鐢? tc('text_izfns7') i18n/no-chinese-hardcode - 453:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "绯荤粺缁存姢妯″紡"銆傚缓璁娇鐢? tc('text_yhidmq') i18n/no-chinese-hardcode - 457:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "寮€鍚悗浠呯鐞嗗憳鍙櫥褰曠郴缁?銆傚缓璁娇鐢? tc('text_vsv8lc') i18n/no-chinese-hardcode - 461:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍏徃鍚嶇О"銆傚缓璁娇鐢? tc('text_amf4wf') i18n/no-chinese-hardcode - 463:19 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏌愭煇绉戞妧鏈夐檺鍏徃"銆傚缓璁娇鐢? tc('text_6ye96q') i18n/no-chinese-hardcode - 465:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鎵撳嵃鍗曟嵁涓庢姤琛ㄦ姮澶存樉绀虹殑鍏徃鍚嶇О"銆傚缓璁娇鐢? tc('text_5wmc50') i18n/no-chinese-hardcode - 468:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍏徃鍦板潃"銆傚缓璁娇鐢? tc('text_amfh98') i18n/no-chinese-hardcode - 472:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍏徃娉ㄥ唽鍦板潃"銆傚缓璁娇鐢? tc('text_gystog') i18n/no-chinese-hardcode - 475:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "瀹㈡湇鐢佃瘽"銆傚缓璁娇鐢? tc('text_bzqlgz') i18n/no-chinese-hardcode - 479:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "瀵瑰鍏竷鐨勫鏈嶈仈绯荤數璇?銆傚缓璁娇鐢? tc('text_tksbt6') i18n/no-chinese-hardcode - 482:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "绾崇◣浜鸿瘑鍒彿"銆傚缓璁娇鐢? tc('text_4yz4qb') i18n/no-chinese-hardcode - 486:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍏徃绾崇◣浜鸿瘑鍒彿锛堝紑绁ㄤ娇鐢級"銆傚缓璁娇鐢? tc('text_vycr4h') i18n/no-chinese-hardcode - 489:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍚敤閭欢閫氱煡"銆傚缓璁娇鐢? tc('text_pv5iek') i18n/no-chinese-hardcode - 493:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "涓氬姟浜嬩欢鏄惁鍙戦€侀偖浠堕€氱煡"銆傚缓璁娇鐢? tc('text_w2fuwk') i18n/no-chinese-hardcode - 496:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍚敤鐭俊閫氱煡"銆傚缓璁娇鐢? tc('text_mqw0s8') i18n/no-chinese-hardcode - 500:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "涓氬姟浜嬩欢鏄惁鍙戦€佺煭淇¢€氱煡"銆傚缓璁娇鐢? tc('text_z6pciw') i18n/no-chinese-hardcode - 503:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "瀵煎嚭鏂囦欢缂栫爜"銆傚缓璁娇鐢? tc('text_nz3o08') i18n/no-chinese-hardcode - 507:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏁版嵁瀵煎嚭鏂囦欢鐨勫瓧绗︾紪鐮?銆傚缓璁娇鐢? tc('text_970hfx') i18n/no-chinese-hardcode - 510:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "榛樿鍒嗛〉澶у皬"銆傚缓璁娇鐢? tc('text_7bb6yl') i18n/no-chinese-hardcode - 514:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍒楄〃椤甸粯璁ゆ瘡椤垫樉绀烘潯鏁?銆傚缓璁娇鐢? tc('text_p6eu1t') i18n/no-chinese-hardcode - 517:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏄剧ず婕旂ず鏁版嵁"銆傚缓璁娇鐢? tc('text_omb4yo') i18n/no-chinese-hardcode - 521:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏄惁鍦ㄦ紨绀虹幆澧冧腑鏄剧ず绀轰緥鏁版嵁"銆傚缓璁娇鐢? tc('text_91wi31') i18n/no-chinese-hardcode - 581:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\settings\config\page.tsx:581:5 - 579 | - 580 | useEffect(() => { -> 581 | fetchData(); - | ^^^^^^^^^ Avoid calling setState() directly within an effect - 582 | }, [fetchData]); - 583 | - 584 | const handleSave = async () => { react-hooks/set-state-in-effect - 743:130 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍔犺浇涓?.."銆傚缓璁娇鐢? {tc('text_27k1ha')} i18n/no-chinese-hardcode - 752:42 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍔犺浇澶辫触"銆傚缓璁娇鐢? {tc('text_b0mmk1')} i18n/no-chinese-hardcode - 754:100 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "閲嶈瘯"銆傚缓璁娇鐢? {tc('text_pkfc')} i18n/no-chinese-hardcode - 835:32 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏆傛棤绯荤粺閰嶇疆椤?銆傚缓璁娇鐢? {tc('text_vtremi')} i18n/no-chinese-hardcode - 836:52 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "璇风偣鍑讳笂鏂广€屽垵濮嬪寲棰勮閰嶇疆銆嶆寜閽垨鎵嬪姩娣诲姞閰嶇疆"銆傚缓璁娇鐢? {tc('text_fzkirq')} i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\[locale]\settings\currency\page.tsx - 62:71 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鑾峰彇甯佺鍒楄〃澶辫触"銆傚缓璁娇鐢? tc('text_r0x4y8') i18n/no-chinese-hardcode - 72:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\settings\currency\page.tsx:72:5 - 70 | - 71 | useEffect(() => { -> 72 | fetchCurrencies(); - | ^^^^^^^^^^^^^^^ Avoid calling setState() directly within an effect - 73 | }, [fetchCurrencies]); - 74 | - 75 | const handleSave = async () => { react-hooks/set-state-in-effect - -D:\dcprint\erp-project\src\app\[locale]\settings\dict\page.tsx - 58:10 warning 'loading' is assigned a value but never used. Allowed unused elements of array destructuring must match /^_/u @typescript-eslint/no-unused-vars - 86:6 warning React Hook useCallback has a missing dependency: 'selectedType'. Either include it or remove the dependency array react-hooks/exhaustive-deps - 89:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\settings\dict\page.tsx:89:5 - 87 | - 88 | useEffect(() => { -> 89 | fetchData(); - | ^^^^^^^^^ Avoid calling setState() directly within an effect - 90 | }, [fetchData]); - 91 | - 92 | const handleCreateType = async () => { react-hooks/set-state-in-effect - 329:74 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "璇蜂粠宸︿晶閫夋嫨涓€涓瓧鍏哥被鍨?銆傚缓璁娇鐢? {tc('text_wxodgt')} i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\[locale]\settings\exchange-rate\page.tsx - 68:66 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鑾峰彇姹囩巼鍒楄〃澶辫触"銆傚缓璁娇鐢? tc('text_q810kc') i18n/no-chinese-hardcode - 78:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\settings\exchange-rate\page.tsx:78:5 - 76 | - 77 | useEffect(() => { -> 78 | fetchRates(); - | ^^^^^^^^^^ Avoid calling setState() directly within an effect - 79 | }, [fetchRates]); - 80 | - 81 | const handleSave = async () => { react-hooks/set-state-in-effect - -D:\dcprint\erp-project\src\app\[locale]\settings\login-log\page.tsx - 60:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\settings\login-log\page.tsx:60:5 - 58 | }; - 59 | useEffect(() => { -> 60 | fetchData(); - | ^^^^^^^^^ Avoid calling setState() directly within an effect - 61 | }, [page]); - 62 | - 63 | const handleClear = async () => { react-hooks/set-state-in-effect - 61:6 warning React Hook useEffect has a missing dependency: 'fetchData'. Either include it or remove the dependency array react-hooks/exhaustive-deps - -D:\dcprint\erp-project\src\app\[locale]\settings\menus\page.tsx - 94:6 warning React Hook useCallback has a missing dependency: 'tc'. Either include it or remove the dependency array react-hooks/exhaustive-deps - 97:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\settings\menus\page.tsx:97:5 - 95 | - 96 | useEffect(() => { -> 97 | fetchData(); - | ^^^^^^^^^ Avoid calling setState() directly within an effect - 98 | }, [fetchData]); - 99 | - 100 | const toggleExpand = (id: number) => { react-hooks/set-state-in-effect - -D:\dcprint\erp-project\src\app\[locale]\settings\notice\page.tsx - 78:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\settings\notice\page.tsx:78:5 - 76 | }; - 77 | useEffect(() => { -> 78 | fetchData(); - | ^^^^^^^^^ Avoid calling setState() directly within an effect - 79 | }, [page]); - 80 | - 81 | const handleSave = async () => { react-hooks/set-state-in-effect - 79:6 warning React Hook useEffect has a missing dependency: 'fetchData'. Either include it or remove the dependency array react-hooks/exhaustive-deps - -D:\dcprint\erp-project\src\app\[locale]\settings\oper-log\page.tsx - 60:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\settings\oper-log\page.tsx:60:5 - 58 | }; - 59 | useEffect(() => { -> 60 | fetchData(); - | ^^^^^^^^^ Avoid calling setState() directly within an effect - 61 | }, [page]); - 62 | - 63 | const handleClear = async () => { react-hooks/set-state-in-effect - 61:6 warning React Hook useEffect has a missing dependency: 'fetchData'. Either include it or remove the dependency array react-hooks/exhaustive-deps - -D:\dcprint\erp-project\src\app\[locale]\settings\organization\department-table.tsx - 77:71 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍚敤"銆傚缓璁娇鐢? {tc('text_eymx')} i18n/no-chinese-hardcode - 79:70 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍋滅敤"銆傚缓璁娇鐢? {tc('text_eb7w')} i18n/no-chinese-hardcode - 131:74 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "(涓€绾ч儴闂?"銆傚缓璁娇鐢? {tc('text_enj4zq')} i18n/no-chinese-hardcode - 207:65 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "灞曞紑鍏ㄩ儴"銆傚缓璁娇鐢? {tc('text_c1kdmz')} i18n/no-chinese-hardcode - 210:67 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏀惰捣鍏ㄩ儴"銆傚缓璁娇鐢? {tc('text_dcor41')} i18n/no-chinese-hardcode - 218:48 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "閮ㄩ棬缂栫爜"銆傚缓璁娇鐢? {tc('text_iwjfl7')} i18n/no-chinese-hardcode - 219:26 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "閮ㄩ棬鍚嶇О"銆傚缓璁娇鐢? {tc('text_iwc4g3')} i18n/no-chinese-hardcode - 220:48 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "璐熻矗浜?銆傚缓璁娇鐢? {tc('text_lckeu')} i18n/no-chinese-hardcode - 221:47 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鎺掑簭"銆傚缓璁娇鐢? {tc('text_hge5')} i18n/no-chinese-hardcode - 222:47 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鐘舵€?銆傚缓璁娇鐢? {tc('text_k1e3')} i18n/no-chinese-hardcode - 223:59 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鎿嶄綔"銆傚缓璁娇鐢? {tc('text_hkxb')} i18n/no-chinese-hardcode - 229:83 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏆傛棤閮ㄩ棬鏁版嵁"銆傚缓璁娇鐢? {tc('text_egqh5w')} i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\[locale]\settings\organization\page.tsx - 7:29 warning 'TabsList' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 7:39 warning 'TabsTrigger' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 179:11 warning Error: Cannot access variable before it is declared - -`loadMockCompany` is accessed before it is declared, which prevents the earlier access from updating when this value changes over time. - -D:\dcprint\erp-project\src\app\[locale]\settings\organization\page.tsx:179:11 - 177 | } else { - 178 | // 浣跨敤妯℃嫙鏁版嵁 -> 179 | loadMockCompany(); - | ^^^^^^^^^^^^^^^ `loadMockCompany` accessed before it is declared - 180 | } - 181 | } else { - 182 | // 浣跨敤妯℃嫙鏁版嵁 - -D:\dcprint\erp-project\src\app\[locale]\settings\organization\page.tsx:193:3 - 191 | - 192 | // 妯℃嫙浼佷笟鏁版嵁 -> 193 | const loadMockCompany = () => { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 194 | setCompany({ - | ^^^^^^^^^^^^^^^^ -> 195 | id: 1, - 鈥? | ^^^^^^^^^^^^^^^^ -> 211 | }); - | ^^^^^^^^^^^^^^^^ -> 212 | }; - | ^^^^^ `loadMockCompany` is declared here - 213 | - 214 | // 淇濆瓨浼佷笟淇℃伅 - 215 | const saveCompany = async () => { react-hooks/immutability - 242:9 warning Error: Cannot access variable before it is declared - -`loadMockDepartments` is accessed before it is declared, which prevents the earlier access from updating when this value changes over time. - -D:\dcprint\erp-project\src\app\[locale]\settings\organization\page.tsx:242:9 - 240 | const response = await authFetch('/api/organization/department'); - 241 | if (!response.ok) { -> 242 | loadMockDepartments(); - | ^^^^^^^^^^^^^^^^^^^ `loadMockDepartments` accessed before it is declared - 243 | return; - 244 | } - 245 | const result = await response.json(); - -D:\dcprint\erp-project\src\app\[locale]\settings\organization\page.tsx:270:3 - 268 | - 269 | // 妯℃嫙閮ㄩ棬鏁版嵁 -> 270 | const loadMockDepartments = () => { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 271 | setDepartments([ - | ^^^^^^^^^^^^^^^^^^^^ -> 272 | { - 鈥? | ^^^^^^^^^^^^^^^^^^^^ -> 352 | ]); - | ^^^^^^^^^^^^^^^^^^^^ -> 353 | }; - | ^^^^^ `loadMockDepartments` is declared here - 354 | - 355 | // 淇濆瓨閮ㄩ棬 - 356 | const saveDepartment = async () => { react-hooks/immutability - 401:9 warning Error: Cannot access variable before it is declared - -`loadMockRoles` is accessed before it is declared, which prevents the earlier access from updating when this value changes over time. - -D:\dcprint\erp-project\src\app\[locale]\settings\organization\page.tsx:401:9 - 399 | const response = await authFetch('/api/organization/role'); - 400 | if (!response.ok) { -> 401 | loadMockRoles(); - | ^^^^^^^^^^^^^ `loadMockRoles` accessed before it is declared - 402 | return; - 403 | } - 404 | const result = await response.json(); - -D:\dcprint\erp-project\src\app\[locale]\settings\organization\page.tsx:429:3 - 427 | - 428 | // 妯℃嫙瑙掕壊鏁版嵁 -> 429 | const loadMockRoles = () => { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 430 | setRoles([ - | ^^^^^^^^^^^^^^ -> 431 | { - 鈥? | ^^^^^^^^^^^^^^ -> 541 | ]); - | ^^^^^^^^^^^^^^ -> 542 | }; - | ^^^^^ `loadMockRoles` is declared here - 543 | - 544 | // 淇濆瓨瑙掕壊 - 545 | const saveRole = async () => { react-hooks/immutability - 611:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\settings\organization\page.tsx:611:5 - 609 | // 鍒濆鍖栧姞杞? 610 | useEffect(() => { -> 611 | fetchCompany(); - | ^^^^^^^^^^^^ Avoid calling setState() directly within an effect - 612 | fetchDepartments(); - 613 | fetchRoles(); - 614 | }, [fetchCompany, fetchDepartments, fetchRoles]); react-hooks/set-state-in-effect - 640:52 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "绯荤粺瑙掕壊"銆傚缓璁娇鐢? {tc('text_gaqqlg')} i18n/no-chinese-hardcode - 642:56 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鑷畾涔?銆傚缓璁娇鐢? {tc('text_jh1ll')} i18n/no-chinese-hardcode - 768:32 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "浼犵湡"銆傚缓璁娇鐢? {tc('text_e41r')} i18n/no-chinese-hardcode - 797:34 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "寮€鎴烽摱琛?銆傚缓璁娇鐢? {tc('text_cegvwt')} i18n/no-chinese-hardcode - 805:34 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "閾惰璐﹀彿"銆傚缓璁娇鐢? {tc('text_jd0njb')} i18n/no-chinese-hardcode - 853:50 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "閮ㄩ棬绠$悊"銆傚缓璁娇鐢? {tc('text_iwitmt')} i18n/no-chinese-hardcode - 866:52 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏂板涓€绾ч儴闂?銆傚缓璁娇鐢? {tc('text_w74baj')} i18n/no-chinese-hardcode - 901:51 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "瑙掕壊鏉冮檺"銆傚缓璁娇鐢? {tc('text_hxen9p')} i18n/no-chinese-hardcode - 922:52 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏂板瑙掕壊"銆傚缓璁娇鐢? {tc('text_d7di6m')} i18n/no-chinese-hardcode - 936:36 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "瑙掕壊缂栫爜"銆傚缓璁娇鐢? {tc('text_hxij63')} i18n/no-chinese-hardcode - 937:36 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "瑙掕壊鍚嶇О"銆傚缓璁娇鐢? {tc('text_hxb80z')} i18n/no-chinese-hardcode - 997:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "閮ㄩ棬缂栫爜"銆傚缓璁娇鐢? {tc('text_iwjfl7')} i18n/no-chinese-hardcode - 1008:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "閮ㄩ棬鍚嶇О"銆傚缓璁娇鐢? {tc('text_iwc4g3')} i18n/no-chinese-hardcode - 1050:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鎺掑簭鍙?銆傚缓璁娇鐢? {tc('text_f1kre')} i18n/no-chinese-hardcode - 1072:41 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍋滅敤"銆傚缓璁娇鐢? {tc('text_eb7w')} i18n/no-chinese-hardcode - 1077:22 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "閮ㄩ棬鎻忚堪"銆傚缓璁娇鐢? {tc('text_iwexa9')} i18n/no-chinese-hardcode - 1087:80 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍙栨秷"銆傚缓璁娇鐢? {tc('text_ev02')} i18n/no-chinese-hardcode - 1090:88 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "淇濆瓨"銆傚缓璁娇鐢? {tc('text_e32z')} i18n/no-chinese-hardcode - 1108:22 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "瑙掕壊缂栫爜"銆傚缓璁娇鐢? {tc('text_hxij63')} i18n/no-chinese-hardcode - 1138:20 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鑷姩鐢熸垚"銆傚缓璁娇鐢? {tc('text_gqkcpb')} i18n/no-chinese-hardcode - 1146:22 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "瑙掕壊鍚嶇О"銆傚缓璁娇鐢? {tc('text_hxb80z')} i18n/no-chinese-hardcode - 1166:41 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "绯荤粺瑙掕壊"銆傚缓璁娇鐢? {tc('text_gaqqlg')} i18n/no-chinese-hardcode - 1181:41 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍏ㄩ儴鏁版嵁"銆傚缓璁娇鐢? {tc('text_avcqke')} i18n/no-chinese-hardcode - 1182:41 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏈儴闂ㄦ暟鎹?銆傚缓璁娇鐢? {tc('text_3vvdg6')} i18n/no-chinese-hardcode - 1188:22 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鎺掑簭鍙?銆傚缓璁娇鐢? {tc('text_f1kre')} i18n/no-chinese-hardcode - 1209:41 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍋滅敤"銆傚缓璁娇鐢? {tc('text_eb7w')} i18n/no-chinese-hardcode - 1224:94 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍙栨秷"銆傚缓璁娇鐢? {tc('text_ev02')} i18n/no-chinese-hardcode - 1235:14 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "淇濆瓨"銆傚缓璁娇鐢? {tc('text_e32z')} i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\[locale]\settings\profile\page.tsx - 39:9 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\settings\profile\page.tsx:39:9 - 37 | try { - 38 | const user = JSON.parse(stored); -> 39 | setUserInfo(user); - | ^^^^^^^^^^^ Avoid calling setState() directly within an effect - 40 | setProfileForm({ - 41 | realName: user.realName || '', - 42 | phone: user.phone || '', react-hooks/set-state-in-effect - 175:50 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍩烘湰淇℃伅"銆傚缓璁娇鐢? {tc('text_biyzkw')} i18n/no-chinese-hardcode - 179:50 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "淇敼瀵嗙爜"銆傚缓璁娇鐢? {tc('text_ai7i2u')} i18n/no-chinese-hardcode - 187:30 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍩烘湰淇℃伅"銆傚缓璁娇鐢? {tc('text_biyzkw')} i18n/no-chinese-hardcode - 238:28 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏂板瘑鐮?銆傚缓璁娇鐢? {tc('text_fcgq3')} i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\[locale]\settings\roles\page.tsx - 74:22 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍏ㄩ儴鏁版嵁"銆傚缓璁娇鐢? tc('text_avcqke') i18n/no-chinese-hardcode - 75:22 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏈儴闂ㄦ暟鎹?銆傚缓璁娇鐢? tc('text_3vvdg6') i18n/no-chinese-hardcode - 76:22 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏈儴闂ㄥ強浠ヤ笅鏁版嵁"銆傚缓璁娇鐢? tc('text_wlp4yq') i18n/no-chinese-hardcode - 77:22 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "浠呮湰浜烘暟鎹?銆傚缓璁娇鐢? tc('text_xtjdwv') i18n/no-chinese-hardcode - 78:22 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鑷畾涔?銆傚缓璁娇鐢? tc('text_jh1ll') i18n/no-chinese-hardcode - 126:6 warning React Hook useCallback has missing dependencies: 'tc' and 'toast'. Either include them or remove the dependency array react-hooks/exhaustive-deps - 412:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\settings\roles\page.tsx:412:5 - 410 | - 411 | useEffect(() => { -> 412 | fetchRoles(); - | ^^^^^^^^^^ Avoid calling setState() directly within an effect - 413 | fetchMenus(); - 414 | }, [fetchRoles, fetchMenus]); - 415 | react-hooks/set-state-in-effect - -D:\dcprint\erp-project\src\app\[locale]\settings\scheduler\page.tsx - 91:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\settings\scheduler\page.tsx:91:5 - 89 | - 90 | useEffect(() => { -> 91 | fetchData(); - | ^^^^^^^^^ Avoid calling setState() directly within an effect - 92 | }, [fetchData]); - 93 | - 94 | const handleCreate = async () => { react-hooks/set-state-in-effect - 401:118 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鎴愬姛"銆傚缓璁娇鐢? {tc('text_h4sv')} i18n/no-chinese-hardcode - 405:74 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "澶辫触"銆傚缓璁娇鐢? {tc('text_fy1g')} i18n/no-chinese-hardcode - 409:72 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "杩愯涓?銆傚缓璁娇鐢? {tc('text_lpxkh')} i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\[locale]\settings\seed-data\page.tsx - 104:58 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍒濆鍖栧け璐?銆傚缓璁娇鐢? {tc('text_mf1bac')} i18n/no-chinese-hardcode - 115:47 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "绯荤粺鏁版嵁绉嶅瓙鍒濆鍖?銆傚缓璁娇鐢? {tc('text_p7a6wz')} i18n/no-chinese-hardcode - 118:30 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍒濆鍖栫郴缁熷熀纭€鏁版嵁锛屽寘鎷儴闂ㄣ€佽鑹层€佺敤鎴枫€佷粨搴撳垎绫汇€佺墿鏂欏垎绫汇€佸瓧鍏搁厤缃瓑銆?銆傚缓璁娇鐢? {tc('text_2k4uu2')} i18n/no-chinese-hardcode - 127:62 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "娉ㄦ剰浜嬮」"銆傚缓璁娇鐢? {tc('text_e541px')} i18n/no-chinese-hardcode - 129:25 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鈥?姝ゆ搷浣滀細娓呯┖绯荤粺璁剧疆鐩稿叧琛ㄧ殑鏁版嵁"銆傚缓璁娇鐢? {tc('text_ark69p')} i18n/no-chinese-hardcode - 130:25 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鈥?寤鸿鍦ㄩ娆¢儴缃叉垨闇€瑕侀噸缃郴缁熼厤缃椂浣跨敤"銆傚缓璁娇鐢? {tc('text_yoby43')} i18n/no-chinese-hardcode - 131:25 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鈥?鍒濆鍖栧悗榛樿绠$悊鍛樿处鍙凤細admin锛屽瘑鐮侊細admin123"銆傚缓璁娇鐢? {tc('text_vn04ip')} i18n/no-chinese-hardcode - 145:70 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍒濆鍖栦腑..."銆傚缓璁娇鐢? {tc('text_mzll5j')} i18n/no-chinese-hardcode - 150:56 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍒濆鍖栫郴缁熸暟鎹?銆傚缓璁娇鐢? {tc('text_gk0z7a')} i18n/no-chinese-hardcode - 161:64 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "绯荤粺鏁版嵁鍒濆鍖栨垚鍔?銆傚缓璁娇鐢? {tc('text_bwerkl')} i18n/no-chinese-hardcode - 169:61 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏉?銆傚缓璁娇鐢? {tc('text_kf5')} i18n/no-chinese-hardcode - 184:46 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "涓氬姟鏁版嵁绉嶅瓙鍒濆鍖?銆傚缓璁娇鐢? {tc('text_86r78g')} i18n/no-chinese-hardcode - 187:30 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍒濆鍖栦笟鍔℃暟鎹紝鍖呮嫭瀹㈡埛銆侀攢鍞鍗曘€佺敓浜у伐鍗曘€佸叆搴撳崟銆佺墿鏂欐爣绛俱€佸簱瀛樸€佽川閲忔楠岃褰曠瓑銆?銆傚缓璁娇鐢? {tc('text_kldi64')} i18n/no-chinese-hardcode - 196:62 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "娉ㄦ剰浜嬮」"銆傚缓璁娇鐢? {tc('text_e541px')} i18n/no-chinese-hardcode - 198:25 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鈥?姝ゆ搷浣滀細娓呯┖涓氬姟鐩稿叧琛ㄧ殑鏁版嵁"銆傚缓璁娇鐢? {tc('text_nhgkgm')} i18n/no-chinese-hardcode - 199:25 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鈥?寤鸿鍦ㄩ渶瑕侀噸缃笟鍔℃暟鎹椂浣跨敤"銆傚缓璁娇鐢? {tc('text_mg2jq6')} i18n/no-chinese-hardcode - 200:25 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鈥?鍖呭惈婕旂ず鐢ㄧ殑鐪熷疄涓氬姟鍦烘櫙鏁版嵁"銆傚缓璁娇鐢? {tc('text_krsa5d')} i18n/no-chinese-hardcode - 214:70 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍒濆鍖栦腑..."銆傚缓璁娇鐢? {tc('text_mzll5j')} i18n/no-chinese-hardcode - 219:55 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍒濆鍖栦笟鍔℃暟鎹?銆傚缓璁娇鐢? {tc('text_mnd75f')} i18n/no-chinese-hardcode - 230:64 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "涓氬姟鏁版嵁鍒濆鍖栨垚鍔?銆傚缓璁娇鐢? {tc('text_lhmmku')} i18n/no-chinese-hardcode - 238:61 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏉?銆傚缓璁娇鐢? {tc('text_kf5')} i18n/no-chinese-hardcode - 252:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏁版嵁璇存槑"銆傚缓璁娇鐢? {tc('text_d7tuzc')} i18n/no-chinese-hardcode - 253:30 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍒濆鍖栧悗灏嗗寘鍚互涓嬫暟鎹?銆傚缓璁娇鐢? {tc('text_kbgkei')} i18n/no-chinese-hardcode - 259:51 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "绯荤粺鏁版嵁"銆傚缓璁娇鐢? {tc('text_gakdoi')} i18n/no-chinese-hardcode - 263:22 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鈥?10涓儴闂紙绠$悊閮ㄣ€佷笟鍔¢儴銆佸伐绋嬫妧鏈儴绛夛級"銆傚缓璁娇鐢? {tc('text_5kydy2')} i18n/no-chinese-hardcode - 264:22 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鈥?10涓鑹诧紙瓒呯骇绠$悊鍛樸€佷笟鍔$粡鐞嗐€佷笟鍔″憳绛夛級"銆傚缓璁娇鐢? {tc('text_dbwyxz')} i18n/no-chinese-hardcode - 265:22 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鈥?10涓敤鎴凤紙admin銆亃hangwei銆乴ina绛夛級"銆傚缓璁娇鐢? {tc('text_b1qg4')} i18n/no-chinese-hardcode - 266:22 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鈥?10涓粨搴撳垎绫伙紙鍘熸潗鏂欎粨銆佸崐鎴愬搧浠撱€佹垚鍝佷粨绛夛級"銆傚缓璁娇鐢? {tc('text_2qixgp')} i18n/no-chinese-hardcode - 267:22 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鈥?10涓墿鏂欏垎绫伙紙钖勮啘鏉愭枡銆佹补澧ㄦ潗鏂欑瓑锛?銆傚缓璁娇鐢? {tc('text_yb15y6')} i18n/no-chinese-hardcode - 268:22 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鈥?100+鏉″瓧鍏告暟鎹紙浠撳簱绫诲瀷銆佺墿鏂欑被鍨嬬瓑锛?銆傚缓璁娇鐢? {tc('text_lm5iz5')} i18n/no-chinese-hardcode - 273:50 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "涓氬姟鏁版嵁"銆傚缓璁娇鐢? {tc('text_a785qd')} i18n/no-chinese-hardcode - 277:22 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鈥?5涓鎴凤紙缇庣殑闆嗗洟銆佹牸鍔涚數鍣ㄣ€佹捣灏旈泦鍥㈢瓑锛?銆傚缓璁娇鐢? {tc('text_rk9n89')} i18n/no-chinese-hardcode - 278:22 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鈥?10涓攢鍞鍗?銆傚缓璁娇鐢? {tc('text_hnddla')} i18n/no-chinese-hardcode - 279:22 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鈥?8涓敓浜у伐鍗?銆傚缓璁娇鐢? {tc('text_vkds14')} i18n/no-chinese-hardcode - 280:22 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鈥?8涓叆搴撳崟"銆傚缓璁娇鐢? {tc('text_cbxo5z')} i18n/no-chinese-hardcode - 281:22 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鈥?10涓墿鏂欐爣绛?銆傚缓璁娇鐢? {tc('text_dcjz84')} i18n/no-chinese-hardcode - 282:22 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鈥?6涓簱瀛樿褰?銆傚缓璁娇鐢? {tc('text_rtzqws')} i18n/no-chinese-hardcode - 283:22 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鈥?10涓川閲忔楠岃褰?銆傚缓璁娇鐢? {tc('text_myddmj')} i18n/no-chinese-hardcode - 294:44 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "榛樿璐﹀彿瀵嗙爜"銆傚缓璁娇鐢? {tc('text_4v1m0')} i18n/no-chinese-hardcode - 320:57 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鎻愮ず锛氶娆$櫥褰曞悗寤鸿淇敼瀵嗙爜"銆傚缓璁娇鐢? {tc('text_jqiovn')} i18n/no-chinese-hardcode - 329:52 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "蹇€熷鑸?銆傚缓璁娇鐢? {tc('text_cp39z6')} i18n/no-chinese-hardcode - 335:97 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鐢ㄦ埛绠$悊"銆傚缓璁娇鐢? {tc('text_f6y6dw')} i18n/no-chinese-hardcode - 341:16 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "缁勭粐璁剧疆"銆傚缓璁娇鐢? {tc('text_giucz7')} i18n/no-chinese-hardcode - 344:101 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍏ュ簱绠$悊"銆傚缓璁娇鐢? {tc('text_ao1adv')} i18n/no-chinese-hardcode - 347:98 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏍囩绠$悊"銆傚缓璁娇鐢? {tc('text_dn1dss')} i18n/no-chinese-hardcode - 350:101 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "璐ㄩ噺浠〃鏉?銆傚缓璁娇鐢? {tc('text_2ef14q')} i18n/no-chinese-hardcode - 353:96 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "閿€鍞鍗?銆傚缓璁娇鐢? {tc('text_j5p8kh')} i18n/no-chinese-hardcode - 359:16 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鐢熶骇宸ュ崟"銆傚缓璁娇鐢? {tc('text_f3s1h4')} i18n/no-chinese-hardcode - 365:16 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "搴撳瓨绠$悊"銆傚缓璁娇鐢? {tc('text_cbemwa')} i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\[locale]\settings\system\page.tsx - 89:5 warning Error: Cannot access variable before it is declared - -`fetchConfig` is accessed before it is declared, which prevents the earlier access from updating when this value changes over time. - -D:\dcprint\erp-project\src\app\[locale]\settings\system\page.tsx:89:5 - 87 | - 88 | useEffect(() => { -> 89 | fetchConfig(); - | ^^^^^^^^^^^ `fetchConfig` accessed before it is declared - 90 | }, []); - 91 | - 92 | const fetchConfig = async () => { - -D:\dcprint\erp-project\src\app\[locale]\settings\system\page.tsx:92:3 - 90 | }, []); - 91 | -> 92 | const fetchConfig = async () => { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 93 | setLoading(true); - | ^^^^^^^^^^^^^^^^^^^^^ -> 94 | try { - 鈥? | ^^^^^^^^^^^^^^^^^^^^^ -> 105 | } - | ^^^^^^^^^^^^^^^^^^^^^ -> 106 | }; - | ^^^^^ `fetchConfig` is declared here - 107 | - 108 | const updateConfigValue = (key: string, value: string) => { - 109 | setModifiedConfigs((prev) => ({ react-hooks/immutability - 90:6 warning React Hook useEffect has a missing dependency: 'fetchConfig'. Either include it or remove the dependency array react-hooks/exhaustive-deps - 225:63 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "绯荤粺鏍稿績鍙傛暟閰嶇疆涓績锛屼慨鏀归渶瀹℃壒鍚庣敓鏁?銆傚缓璁娇鐢? {tc('text_qh7dj2')} i18n/no-chinese-hardcode - 231:53 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍒锋柊"銆傚缓璁娇鐢? {tc('text_ejix')} i18n/no-chinese-hardcode - 285:60 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "椤?銆傚缓璁娇鐢? {tc('text_u49')} i18n/no-chinese-hardcode - 307:32 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "蹇呭~"銆傚缓璁娇鐢? {tc('text_grwm')} i18n/no-chinese-hardcode - 315:32 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "闇€瀹℃壒"銆傚缓璁娇鐢? {tc('text_mkcfs')} i18n/no-chinese-hardcode - 327:78 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "宸蹭慨鏀?銆傚缓璁娇鐢? {tc('text_e5it9')} i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\[locale]\settings\user\page.tsx - 422:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\settings\user\page.tsx:422:5 - 420 | - 421 | useEffect(() => { -> 422 | fetchData(); - | ^^^^^^^^^ Avoid calling setState() directly within an effect - 423 | }, [page]); - 424 | useEffect(() => { - 425 | fetchRoles(); react-hooks/set-state-in-effect - 423:6 warning React Hook useEffect has a missing dependency: 'fetchData'. Either include it or remove the dependency array react-hooks/exhaustive-deps - 425:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\settings\user\page.tsx:425:5 - 423 | }, [page]); - 424 | useEffect(() => { -> 425 | fetchRoles(); - | ^^^^^^^^^^ Avoid calling setState() directly within an effect - 426 | fetchDepartments(); - 427 | fetchEmployees(); - 428 | }, []); react-hooks/set-state-in-effect - 428:6 warning React Hook useEffect has missing dependencies: 'fetchDepartments', 'fetchEmployees', and 'fetchRoles'. Either include them or remove the dependency array react-hooks/exhaustive-deps - 616:87 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏆傛棤璁板綍"銆傚缓璁娇鐢? {tc('text_dd1mmb')} i18n/no-chinese-hardcode - 626:51 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍏?銆傚缓璁娇鐢? {tc('text_g35')} i18n/no-chinese-hardcode - 626:59 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏉?銆傚缓璁娇鐢? {tc('text_kf5')} i18n/no-chinese-hardcode - 633:14 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "涓婁竴椤?銆傚缓璁娇鐢? {tc('text_btlof')} i18n/no-chinese-hardcode - 641:14 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "涓嬩竴椤?銆傚缓璁娇鐢? {tc('text_btmf4')} i18n/no-chinese-hardcode - 660:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鐢ㄦ埛鍚?銆傚缓璁娇鐢? {tc('text_hmxge')} i18n/no-chinese-hardcode - 672:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "瀵嗙爜"銆傚缓璁娇鐢? {tc('text_g9ob')} i18n/no-chinese-hardcode - 684:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "濮撳悕"銆傚缓璁娇鐢? {tc('text_fqmy')} i18n/no-chinese-hardcode - 711:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鎵嬫満"銆傚缓璁娇鐢? {tc('text_haa7')} i18n/no-chinese-hardcode - 803:65 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍙悓鏃朵负鐢ㄦ埛鍒嗛厤澶氫釜瑙掕壊锛屾潈闄愪細鑷姩鍚堝苟"銆傚缓璁娇鐢? {tc('text_6xlyt0')} i18n/no-chinese-hardcode - 814:16 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍙栨秷"銆傚缓璁娇鐢? {tc('text_ev02')} i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\[locale]\settings\warehouse-category\warehouse-category-manager.tsx - 126:6 warning React Hook useCallback has a missing dependency: 'tc'. Either include it or remove the dependency array react-hooks/exhaustive-deps - 202:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\settings\warehouse-category\warehouse-category-manager.tsx:202:5 - 200 | // 鍒濆鍖栧姞杞? 201 | useEffect(() => { -> 202 | fetchCategories(); - | ^^^^^^^^^^^^^^^ Avoid calling setState() directly within an effect - 203 | // eslint-disable-next-line react-hooks/exhaustive-deps - 204 | }, []); - 205 | react-hooks/set-state-in-effect - 209:54 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍚敤"銆傚缓璁娇鐢? {tc('text_eymx')} i18n/no-chinese-hardcode - 211:65 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍋滅敤"銆傚缓璁娇鐢? {tc('text_eb7w')} i18n/no-chinese-hardcode - 229:62 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鎬诲垎绫绘暟"銆傚缓璁娇鐢? {tc('text_chkaf4')} i18n/no-chinese-hardcode - 242:62 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍚敤鍒嗙被"銆傚缓璁娇鐢? {tc('text_b3t8lq')} i18n/no-chinese-hardcode - 257:62 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "浠撳簱鎬绘暟"銆傚缓璁娇鐢? {tc('text_ac8ed1')} i18n/no-chinese-hardcode - 272:62 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍙敤浠撳簱"銆傚缓璁娇鐢? {tc('text_b2nny1')} i18n/no-chinese-hardcode - 290:48 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "浠撳簱鍒嗙被绠$悊"銆傚缓璁娇鐢? {tc('text_9g3fxi')} i18n/no-chinese-hardcode - 293:30 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "绠$悊浠撳簱鍒嗙被锛岀敤浜庤祫婧愰厤缃拰瀹归噺瑙勫垝"銆傚缓璁娇鐢? {tc('text_nucjbf')} i18n/no-chinese-hardcode - 310:46 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏂板鍒嗙被"銆傚缓璁娇鐢? {tc('text_d73zc3')} i18n/no-chinese-hardcode - 323:30 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍒嗙被缂栫爜"銆傚缓璁娇鐢? {tc('text_avae8w')} i18n/no-chinese-hardcode - 324:30 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍒嗙被鍚嶇О"銆傚缓璁娇鐢? {tc('text_av333s')} i18n/no-chinese-hardcode - 325:30 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "浠撳簱鏁伴噺"銆傚缓璁娇鐢? {tc('text_ac9j0f')} i18n/no-chinese-hardcode - 326:30 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鎬诲閲?宸蹭娇鐢?銆傚缓璁娇鐢? {tc('text_5lkvq5')} i18n/no-chinese-hardcode - 327:30 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "浣跨敤鐜?銆傚缓璁娇鐢? {tc('text_c7qqm')} i18n/no-chinese-hardcode - 328:30 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鎺掑簭"銆傚缓璁娇鐢? {tc('text_hge5')} i18n/no-chinese-hardcode - 329:30 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鐘舵€?銆傚缓璁娇鐢? {tc('text_k1e3')} i18n/no-chinese-hardcode - 330:53 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鎿嶄綔"銆傚缓璁娇鐢? {tc('text_hkxb')} i18n/no-chinese-hardcode - 336:95 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏆傛棤浠撳簱鍒嗙被鏁版嵁"銆傚缓璁娇鐢? {tc('text_wgn86p')} i18n/no-chinese-hardcode - 448:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍒嗙被缂栫爜"銆傚缓璁娇鐢? {tc('text_avae8w')} i18n/no-chinese-hardcode - 478:22 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鑷姩鐢熸垚"銆傚缓璁娇鐢? {tc('text_gqkcpb')} i18n/no-chinese-hardcode - 486:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍒嗙被鍚嶇О"銆傚缓璁娇鐢? {tc('text_av333s')} i18n/no-chinese-hardcode - 498:22 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鎺掑簭鍙?銆傚缓璁娇鐢? {tc('text_f1kre')} i18n/no-chinese-hardcode - 507:22 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鐘舵€?銆傚缓璁娇鐢? {tc('text_k1e3')} i18n/no-chinese-hardcode - 513:35 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍚敤"銆傚缓璁娇鐢? {tc('text_eymx')} i18n/no-chinese-hardcode - 514:35 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍋滅敤"銆傚缓璁娇鐢? {tc('text_eb7w')} i18n/no-chinese-hardcode - 518:22 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍒嗙被鎻忚堪"銆傚缓璁娇鐢? {tc('text_av5vxy')} i18n/no-chinese-hardcode - 529:90 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍙栨秷"銆傚缓璁娇鐢? {tc('text_ev02')} i18n/no-chinese-hardcode - 532:100 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "淇濆瓨"銆傚缓璁娇鐢? {tc('text_e32z')} i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\[locale]\srm\evaluation\page.tsx - 245:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\srm\evaluation\page.tsx:245:5 - 243 | - 244 | useEffect(() => { -> 245 | fetchData(); - | ^^^^^^^^^ Avoid calling setState() directly within an effect - 246 | }, [page]); - 247 | - 248 | const fetchDetail = async (id: number) => { react-hooks/set-state-in-effect - 246:6 warning React Hook useEffect has a missing dependency: 'fetchData'. Either include it or remove the dependency array react-hooks/exhaustive-deps - 419:9 warning 'handleExportXLS' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 462:9 warning 'handleExportPDF' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 535:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "渚涘簲鍟嗚瘎浠?銆傚缓璁娇鐢? {tc('text_vlr8n4')} i18n/no-chinese-hardcode - 1017:78 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍙栨秷"銆傚缓璁娇鐢? {tc('text_ev02')} i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\[locale]\warehouse\batch\page.tsx - 122:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\warehouse\batch\page.tsx:122:5 - 120 | - 121 | useEffect(() => { -> 122 | fetchData(); - | ^^^^^^^^^ Avoid calling setState() directly within an effect - 123 | }, [fetchData]); - 124 | - 125 | useEffect(() => { react-hooks/set-state-in-effect - -D:\dcprint\erp-project\src\app\[locale]\warehouse\cost\page.tsx - 70:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\warehouse\cost\page.tsx:70:5 - 68 | - 69 | useEffect(() => { -> 70 | fetchData(); - | ^^^^^^^^^ Avoid calling setState() directly within an effect - 71 | }, [fetchData]); - 72 | - 73 | const viewDetail = async (materialId: number) => { react-hooks/set-state-in-effect - 85:18 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "纭畾閲嶆柊璁$畻璇ョ墿鏂欑殑鎴愭湰锛?銆傚缓璁娇鐢? tc('text_s0qg6p') i18n/no-chinese-hardcode - 111:49 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鎴愭湰鏍哥畻绠$悊"銆傚缓璁娇鐢? {tc('text_esxz5s')} i18n/no-chinese-hardcode - 114:63 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "绉诲姩鍔犳潈骞冲潎娉曟垚鏈牳绠楋紝鏀寔鎴愭湰閲嶇畻鍜屾垚鏈垎鏋?銆傚缓璁娇鐢? {tc('text_da7v8a')} i18n/no-chinese-hardcode - 119:51 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍒锋柊"銆傚缓璁娇鐢? {tc('text_ejix')} i18n/no-chinese-hardcode - 160:55 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "绉诲姩鍔犳潈骞冲潎"銆傚缓璁娇鐢? {tc('text_adlun0')} i18n/no-chinese-hardcode - 172:30 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鐗╂枡缂栫爜"銆傚缓璁娇鐢? {tc('text_euzqpn')} i18n/no-chinese-hardcode - 173:30 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鐗╂枡鍚嶇О"銆傚缓璁娇鐢? {tc('text_eusfkj')} i18n/no-chinese-hardcode - 192:95 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏆傛棤鎴愭湰鏁版嵁"銆傚缓璁娇鐢? {tc('text_kka3bc')} i18n/no-chinese-hardcode - 231:61 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "璇︽儏"銆傚缓璁娇鐢? {tc('text_obrz')} i18n/no-chinese-hardcode - 240:68 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "閲嶇畻"銆傚缓璁娇鐢? {tc('text_ph7u')} i18n/no-chinese-hardcode - 254:59 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍏?銆傚缓璁娇鐢? {tc('text_g35')} i18n/no-chinese-hardcode - 264:14 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "涓婁竴椤?銆傚缓璁娇鐢? {tc('text_btlof')} i18n/no-chinese-hardcode - 272:14 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "涓嬩竴椤?銆傚缓璁娇鐢? {tc('text_btmf4')} i18n/no-chinese-hardcode - 290:50 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍚勪粨搴撴垚鏈?銆傚缓璁娇鐢? {tc('text_gbclvk')} i18n/no-chinese-hardcode - 296:54 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鎴愭湰浠?銆傚缓璁娇鐢? {tc('text_ev2aj')} i18n/no-chinese-hardcode - 330:54 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍗曚环"銆傚缓璁娇鐢? {tc('text_elvm')} i18n/no-chinese-hardcode - 355:26 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏆傛棤鍏ュ簱璁板綍"銆傚缓璁娇鐢? {tc('text_mp1izz')} i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\[locale]\warehouse\inbound-simple\page.tsx - 97:77 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "浠?銆傚缓璁娇鐢? {tc('text_fli')} i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\[locale]\warehouse\inbound\components\dialogs\AddDialog.tsx - 172:37 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍗?銆傚缓璁娇鐢? {tc('text_ghj')} i18n/no-chinese-hardcode - 173:37 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "寮?銆傚缓璁娇鐢? {tc('text_isg')} i18n/no-chinese-hardcode - 174:37 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "涓?銆傚缓璁娇鐢? {tc('text_ffu')} i18n/no-chinese-hardcode - 175:37 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "绠?銆傚缓璁娇鐢? {tc('text_ofl')} i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\[locale]\warehouse\inbound\components\dialogs\CuttingResultDialog.tsx - 35:34 warning 'forPrint' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars - -D:\dcprint\erp-project\src\app\[locale]\warehouse\inbound\cutting\page.tsx - 155:6 warning React Hook useEffect has a missing dependency: 'pageSize'. Either include it or remove the dependency array react-hooks/exhaustive-deps - 225:28 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍒嗗垏璁板綍"銆傚缓璁娇鐢? {tc('text_ap4js6')} i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\[locale]\warehouse\inbound\hooks\useCutting.ts - 85:24 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍒嗗垏瀹藉害瑙f瀽"銆傚缓璁娇鐢? tc('text_os1ch5') i18n/no-chinese-hardcode - 93:24 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "瑙勬牸瀹藉害瑙f瀽"銆傚缓璁娇鐢? tc('text_g1eb6') i18n/no-chinese-hardcode - 139:24 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍙戦€佸垎鍒囪姹?銆傚缓璁娇鐢? tc('text_7esef0') i18n/no-chinese-hardcode - 152:24 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍒嗗垏API鍝嶅簲"銆傚缓璁娇鐢? tc('text_1t71og') i18n/no-chinese-hardcode - 167:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "寮€濮嬪埛鏂板叆搴撳崟鍒楄〃"銆傚缓璁娇鐢? tc('text_o3erpw') i18n/no-chinese-hardcode - 169:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍏ュ簱鍗曞垪琛ㄥ埛鏂板畬鎴?銆傚缓璁娇鐢? tc('text_ultqz9') i18n/no-chinese-hardcode - 173:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "寮€濮嬫槧灏勫垎鍒囩粨鏋滃埌鎵撳嵃鏍囩"銆傚缓璁娇鐢? tc('text_f4dfm3') i18n/no-chinese-hardcode - 201:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍒嗗垏缁撴灉鏄犲皠瀹屾垚"銆傚缓璁娇鐢? tc('text_4afziq') i18n/no-chinese-hardcode - 213:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "宸叉墦寮€鍒嗗垏缁撴灉瀵硅瘽妗?銆傚缓璁娇鐢? tc('text_uq68p3') i18n/no-chinese-hardcode - 218:27 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍒嗗垏API杩斿洖澶辫触"銆傚缓璁娇鐢? tc('text_s72h1l') i18n/no-chinese-hardcode - 222:25 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍒嗗垏杩囩▼寮傚父"銆傚缓璁娇鐢? tc('text_i3xtet') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\[locale]\warehouse\inbound\hooks\useInboundData.ts - 41:24 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "API鍝嶅簲"銆傚缓璁娇鐢? tc('text_11m5lt') i18n/no-chinese-hardcode - 54:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍏ュ簱鍗曞垪琛ㄨ幏鍙栨垚鍔?銆傚缓璁娇鐢? tc('text_y9412y') i18n/no-chinese-hardcode - 60:25 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鑾峰彇鍏ュ簱鍗曞垪琛ㄥけ璐?銆傚缓璁娇鐢? tc('text_lvt4wj') i18n/no-chinese-hardcode - 81:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "浠撳簱鍒楄〃鑾峰彇鎴愬姛"銆傚缓璁娇鐢? tc('text_25ookh') i18n/no-chinese-hardcode - 87:25 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鑾峰彇浠撳簱鍒楄〃澶辫触"銆傚缓璁娇鐢? tc('text_fgr2ec') i18n/no-chinese-hardcode - 98:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "浠撳簱鍒嗙被鑾峰彇鎴愬姛"銆傚缓璁娇鐢? tc('text_e6zx5v') i18n/no-chinese-hardcode - 104:25 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鑾峰彇浠撳簱鍒嗙被澶辫触"銆傚缓璁娇鐢? tc('text_fepbs8') i18n/no-chinese-hardcode - 115:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "渚涘簲鍟嗗垪琛ㄨ幏鍙栨垚鍔?銆傚缓璁娇鐢? tc('text_psnfm4') i18n/no-chinese-hardcode - 121:25 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鑾峰彇渚涘簲鍟嗗垪琛ㄥけ璐?銆傚缓璁娇鐢? tc('text_bqhdwz') i18n/no-chinese-hardcode - 132:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鏍囩鍒楄〃鑾峰彇鎴愬姛"銆傚缓璁娇鐢? tc('text_e3qc16') i18n/no-chinese-hardcode - 138:25 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鑾峰彇鏍囩鍒楄〃澶辫触"銆傚缓璁娇鐢? tc('text_d04xut') i18n/no-chinese-hardcode - 144:22 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "缁勪欢鎸傝浇锛屽紑濮嬪苟琛屾媺鍙栧垵濮嬫暟鎹?銆傚缓璁娇鐢? tc('text_hjg121') i18n/no-chinese-hardcode - 145:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\warehouse\inbound\hooks\useInboundData.ts:145:5 - 143 | const ctx = { module: 'Warehouse', action: 'mountInit' }; - 144 | logger.info(ctx, '缁勪欢鎸傝浇锛屽紑濮嬪苟琛屾媺鍙栧垵濮嬫暟鎹?); -> 145 | fetchInboundRecords(); - | ^^^^^^^^^^^^^^^^^^^ Avoid calling setState() directly within an effect - 146 | fetchWarehouses(); - 147 | fetchWarehouseCategories(); - 148 | fetchSuppliers(); react-hooks/set-state-in-effect - -D:\dcprint\erp-project\src\app\[locale]\warehouse\inbound\page.tsx - 109:10 warning 'qrCodeLabelId' is assigned a value but never used. Allowed unused elements of array destructuring must match /^_/u @typescript-eslint/no-unused-vars - 110:22 warning 'setScanResult' is assigned a value but never used. Allowed unused elements of array destructuring must match /^_/u @typescript-eslint/no-unused-vars - 153:5 warning React Hook useCallback has a missing dependency: 'setIsQRCodeDialogOpen'. Either include it or remove the dependency array react-hooks/exhaustive-deps - 176:5 warning React Hook useCallback has a missing dependency: 'setIsQRCodeDialogOpen'. Either include it or remove the dependency array react-hooks/exhaustive-deps - -D:\dcprint\erp-project\src\app\[locale]\warehouse\inventory\page.tsx - 110:13 warning Error: Cannot access variable before it is declared - -`fetchInventory` is accessed before it is declared, which prevents the earlier access from updating when this value changes over time. - -D:\dcprint\erp-project\src\app\[locale]\warehouse\inventory\page.tsx:110:13 - 108 | toast({ title: tc('frozenCount', { count: ids.length }) }); - 109 | setSelectedIds([]); -> 110 | fetchInventory(); - | ^^^^^^^^^^^^^^ `fetchInventory` accessed before it is declared - 111 | } else { - 112 | toast({ title: result.message || tc('freezeFailed'), variant: 'destructive' }); - 113 | } - -D:\dcprint\erp-project\src\app\[locale]\warehouse\inventory\page.tsx:222:3 - 220 | }, []); - 221 | -> 222 | const fetchInventory = async () => { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 223 | setLoading(true); - | ^^^^^^^^^^^^^^^^^^^^^ -> 224 | try { - 鈥? | ^^^^^^^^^^^^^^^^^^^^^ -> 273 | } - | ^^^^^^^^^^^^^^^^^^^^^ -> 274 | }; - | ^^^^^ `fetchInventory` is declared here - 275 | - 276 | const handleSearch = () => { - 277 | fetchInventory(); react-hooks/immutability - 220:6 warning React Hook useEffect has a missing dependency: 'fetchInventory'. Either include it or remove the dependency array react-hooks/exhaustive-deps - -D:\dcprint\erp-project\src\app\[locale]\warehouse\outbound\page.tsx - 8:3 warning 'Package' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 13:3 warning 'Building2' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 14:3 warning 'User' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 19:3 warning 'Barcode' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 21:3 warning 'Download' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 23:3 warning 'TrendingUp' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 26:3 warning 'Warehouse' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 27:3 warning 'ChevronRight' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 30:3 warning 'ArrowUpDown' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 31:3 warning 'History' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 33:3 warning 'Palette' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 34:3 warning 'Ruler' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 35:3 warning 'Weight' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 42:3 warning 'Settings' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 43:3 warning 'Beaker' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 44:3 warning 'QrCode' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 45:3 warning 'ScanLine' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 163:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍘熸潗鏂?銆傚缓璁娇鐢? tc('text_cr294') i18n/no-chinese-hardcode - 164:11 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍘?.3鐑缉濂楃"銆傚缓璁娇鐢? tc('text_48n3vj') i18n/no-chinese-hardcode - 168:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鎭掔繉杈?銆傚缓璁娇鐢? tc('text_eqf5w') i18n/no-chinese-hardcode - 173:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍘熸潗鏂?銆傚缓璁娇鐢? tc('text_cr294') i18n/no-chinese-hardcode - 174:11 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "PE绠?銆傚缓璁娇鐢? tc('text_2de4') i18n/no-chinese-hardcode - 178:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鎭掔繉杈?銆傚缓璁娇鐢? tc('text_eqf5w') i18n/no-chinese-hardcode - 183:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍘熸潗鏂?銆傚缓璁娇鐢? tc('text_cr294') i18n/no-chinese-hardcode - 184:11 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍘?.2鐑缉濂楃"銆傚缓璁娇鐢? tc('text_496wgw') i18n/no-chinese-hardcode - 188:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鎭掔繉杈?銆傚缓璁娇鐢? tc('text_eqf5w') i18n/no-chinese-hardcode - 193:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍘熸潗鏂?銆傚缓璁娇鐢? tc('text_cr294') i18n/no-chinese-hardcode - 194:11 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "PVC缁濈紭鑳跺甫"銆傚缓璁娇鐢? tc('text_oyth1k') i18n/no-chinese-hardcode - 197:11 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍗?銆傚缓璁娇鐢? tc('text_ghj') i18n/no-chinese-hardcode - 198:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍗庨€氭潗鏂?銆傚缓璁娇鐢? tc('text_b3v5cl') i18n/no-chinese-hardcode - 203:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍘熸潗鏂?銆傚缓璁娇鐢? tc('text_cr294') i18n/no-chinese-hardcode - 204:11 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "閾滆姱绾?銆傚缓璁娇鐢? tc('text_mfuto') i18n/no-chinese-hardcode - 208:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "姹熷崡鐢电紗"銆傚缓璁娇鐢? tc('text_e0uo21') i18n/no-chinese-hardcode - 213:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍘熸潗鏂?銆傚缓璁娇鐢? tc('text_cr294') i18n/no-chinese-hardcode - 214:11 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "閾濈當灞忚斀甯?銆傚缓璁娇鐢? tc('text_ts9aw1') i18n/no-chinese-hardcode - 217:11 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍗?銆傚缓璁娇鐢? tc('text_ghj') i18n/no-chinese-hardcode - 218:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍗庨€氭潗鏂?銆傚缓璁娇鐢? tc('text_b3v5cl') i18n/no-chinese-hardcode - 223:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍘熸潗鏂?銆傚缓璁娇鐢? tc('text_cr294') i18n/no-chinese-hardcode - 224:11 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "灏奸緳鎵庡甫"銆傚缓璁娇鐢? tc('text_canmt1') i18n/no-chinese-hardcode - 227:11 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍖?銆傚缓璁娇鐢? tc('text_ged') i18n/no-chinese-hardcode - 228:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鎭掔繉杈?銆傚缓璁娇鐢? tc('text_eqf5w') i18n/no-chinese-hardcode - 233:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍘熸潗鏂?銆傚缓璁娇鐢? tc('text_cr294') i18n/no-chinese-hardcode - 234:11 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鐑啍鑳舵"銆傚缓璁娇鐢? tc('text_eq0ieb') i18n/no-chinese-hardcode - 238:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍗庨€氭潗鏂?銆傚缓璁娇鐢? tc('text_b3v5cl') i18n/no-chinese-hardcode - 248:19 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍘?.3鐑缉濂楃"銆傚缓璁娇鐢? tc('text_48n3vj') i18n/no-chinese-hardcode - 252:16 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍘熸潗鏂欎粨"銆傚缓璁娇鐢? tc('text_azbdez') i18n/no-chinese-hardcode - 254:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "寮犱笁"銆傚缓璁娇鐢? tc('text_glwp') i18n/no-chinese-hardcode - 257:11 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鐢熶骇鍑哄簱"銆傚缓璁娇鐢? tc('text_f3q2pt') i18n/no-chinese-hardcode - 258:13 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鐢熶骇杞﹂棿棰嗙敤"銆傚缓璁娇鐢? tc('text_w3u36g') i18n/no-chinese-hardcode - 267:19 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "PE绠?銆傚缓璁娇鐢? tc('text_2de4') i18n/no-chinese-hardcode - 271:16 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍘熸潗鏂欎粨"銆傚缓璁娇鐢? tc('text_azbdez') i18n/no-chinese-hardcode - 273:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "寮犱笁"銆傚缓璁娇鐢? tc('text_glwp') i18n/no-chinese-hardcode - 276:11 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鐢熶骇鍑哄簱"銆傚缓璁娇鐢? tc('text_f3q2pt') i18n/no-chinese-hardcode - 277:13 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鐢熶骇杞﹂棿棰嗙敤"銆傚缓璁娇鐢? tc('text_w3u36g') i18n/no-chinese-hardcode - 286:19 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍘?.2鐑缉濂楃"銆傚缓璁娇鐢? tc('text_496wgw') i18n/no-chinese-hardcode - 290:16 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍘熸潗鏂欎粨"銆傚缓璁娇鐢? tc('text_azbdez') i18n/no-chinese-hardcode - 292:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏉庡洓"銆傚缓璁娇鐢? tc('text_i1ql') i18n/no-chinese-hardcode - 295:11 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "閿€鍞嚭搴?銆傚缓璁娇鐢? tc('text_j5fhqf') i18n/no-chinese-hardcode - 296:13 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "瀹㈡埛璁㈠崟鍙戣揣"銆傚缓璁娇鐢? tc('text_1pzp4u') i18n/no-chinese-hardcode - 305:19 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍘?.3鐑缉濂楃"銆傚缓璁娇鐢? tc('text_48n3vj') i18n/no-chinese-hardcode - 309:16 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍘熸潗鏂欎粨"銆傚缓璁娇鐢? tc('text_azbdez') i18n/no-chinese-hardcode - 311:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "寮犱笁"銆傚缓璁娇鐢? tc('text_glwp') i18n/no-chinese-hardcode - 314:11 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鐢熶骇鍑哄簱"銆傚缓璁娇鐢? tc('text_f3q2pt') i18n/no-chinese-hardcode - 315:13 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏈堝害鐢熶骇璁″垝棰嗙敤"銆傚缓璁娇鐢? tc('text_8x2lo9') i18n/no-chinese-hardcode - 393:10 warning 'activeTab' is assigned a value but never used. Allowed unused elements of array destructuring must match /^_/u @typescript-eslint/no-unused-vars - 393:21 warning 'setActiveTab' is assigned a value but never used. Allowed unused elements of array destructuring must match /^_/u @typescript-eslint/no-unused-vars - 435:11 warning Error: Cannot access variable before it is declared - -`fetchOutboundRecords` is accessed before it is declared, which prevents the earlier access from updating when this value changes over time. - -D:\dcprint\erp-project\src\app\[locale]\warehouse\outbound\page.tsx:435:11 - 433 | const handleRefresh = useCallback(async () => { - 434 | setIsLoading(true); -> 435 | await fetchOutboundRecords(); - | ^^^^^^^^^^^^^^^^^^^^ `fetchOutboundRecords` accessed before it is declared - 436 | setIsLoading(false); - 437 | toast.success(t('dataRefreshed')); - 438 | }, []); - -D:\dcprint\erp-project\src\app\[locale]\warehouse\outbound\page.tsx:441:3 - 439 | - 440 | // 鑾峰彇鍑哄簱鍗曞垪琛?> 441 | const fetchOutboundRecords = useCallback(async () => { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 442 | logger.info({ module: 'Warehouse', action: 'fetchOutboundRecords' }, '寮€濮嬭幏鍙栧嚭搴撳崟鍒楄〃'); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 443 | try { - 鈥? | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 462 | } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 463 | }, [searchQuery, statusFilter]); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `fetchOutboundRecords` is declared here - 464 | - 465 | // 鑾峰彇浠撳簱鍒楄〃 - 466 | const fetchWarehouses = useCallback(async () => { react-hooks/immutability - 438:6 warning React Hook useCallback has missing dependencies: 'fetchOutboundRecords' and 't'. Either include them or remove the dependency array react-hooks/exhaustive-deps - 441:44 warning Compilation Skipped: Existing memoization could not be preserved - -React Compiler has skipped optimizing this component because the existing manual memoization could not be preserved. This value was memoized in source but not in compilation output. - -D:\dcprint\erp-project\src\app\[locale]\warehouse\outbound\page.tsx:441:44 - 439 | - 440 | // 鑾峰彇鍑哄簱鍗曞垪琛?> 441 | const fetchOutboundRecords = useCallback(async () => { - | ^^^^^^^^^^^^^ -> 442 | logger.info({ module: 'Warehouse', action: 'fetchOutboundRecords' }, '寮€濮嬭幏鍙栧嚭搴撳崟鍒楄〃'); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 443 | try { - 鈥? | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 462 | } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 463 | }, [searchQuery, statusFilter]); - | ^^^^ Could not preserve existing memoization - 464 | - 465 | // 鑾峰彇浠撳簱鍒楄〃 - 466 | const fetchWarehouses = useCallback(async () => { react-hooks/preserve-manual-memoization - 442:74 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "寮€濮嬭幏鍙栧嚭搴撳崟鍒楄〃"銆傚缓璁娇鐢? tc('text_u9vrwj') i18n/no-chinese-hardcode - 454:78 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍑哄簱鍗曞垪琛ㄨ幏鍙栨垚鍔?銆傚缓璁娇鐢? tc('text_d7nu91') i18n/no-chinese-hardcode - 459:77 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鑾峰彇鍑哄簱鍗曞垪琛ㄥけ璐?銆傚缓璁娇鐢? tc('text_y5xj2a') i18n/no-chinese-hardcode - 478:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\warehouse\outbound\page.tsx:478:5 - 476 | // 鍒濆鍔犺浇浠撳簱鏁版嵁 - 477 | useEffect(() => { -> 478 | fetchWarehouses(); - | ^^^^^^^^^^^^^^^ Avoid calling setState() directly within an effect - 479 | }, [fetchWarehouses]); - 480 | - 481 | // 閲嶇疆绛涢€? react-hooks/set-state-in-effect - 488:6 warning React Hook useCallback has a missing dependency: 't'. Either include it or remove the dependency array react-hooks/exhaustive-deps - -D:\dcprint\erp-project\src\app\[locale]\warehouse\page.tsx - 62:10 warning 'loading' is assigned a value but never used. Allowed unused elements of array destructuring must match /^_/u @typescript-eslint/no-unused-vars - 126:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\warehouse\page.tsx:126:5 - 124 | - 125 | useEffect(() => { -> 126 | fetchBatches(); - | ^^^^^^^^^^^^ Avoid calling setState() directly within an effect - 127 | }, [page]); - 128 | - 129 | const handleInbound = async () => { react-hooks/set-state-in-effect - 127:6 warning React Hook useEffect has a missing dependency: 'fetchBatches'. Either include it or remove the dependency array react-hooks/exhaustive-deps - 204:81 warning 'index' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars - -D:\dcprint\erp-project\src\app\[locale]\warehouse\production-inbound\page.tsx - 78:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\warehouse\production-inbound\page.tsx:78:5 - 76 | }; - 77 | useEffect(() => { -> 78 | fetchWarehouses(); - | ^^^^^^^^^^^^^^^ Avoid calling setState() directly within an effect - 79 | }, []); - 80 | - 81 | const handleSave = async () => { react-hooks/set-state-in-effect - 155:50 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏂板鍏ュ簱"銆傚缓璁娇鐢? {tc('text_d73pks')} i18n/no-chinese-hardcode - 166:50 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍏ュ簱鍗曞彿"銆傚缓璁娇鐢? {tc('text_anu9ao')} i18n/no-chinese-hardcode - 167:50 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "宸ュ崟鍙?銆傚缓璁娇鐢? {tc('text_e5qlj')} i18n/no-chinese-hardcode - 169:50 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍏ュ簱鏃ユ湡"銆傚缓璁娇鐢? {tc('text_anxiqw')} i18n/no-chinese-hardcode - 170:50 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "璐ㄦ鐘舵€?銆傚缓璁娇鐢? {tc('text_i8u3s3')} i18n/no-chinese-hardcode - 171:50 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鎿嶄綔浜?銆傚缓璁娇鐢? {tc('text_f5g8b')} i18n/no-chinese-hardcode - 198:28 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "纭鍏ュ簱"銆傚缓璁娇鐢? {tc('text_froe9w')} i18n/no-chinese-hardcode - 227:95 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏆傛棤璁板綍"銆傚缓璁娇鐢? {tc('text_dd1mmb')} i18n/no-chinese-hardcode - 279:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍏ュ簱鏃ユ湡"銆傚缓璁娇鐢? {tc('text_anxiqw')} i18n/no-chinese-hardcode - 287:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "宸ュ崟鍙?銆傚缓璁娇鐢? {tc('text_e5qlj')} i18n/no-chinese-hardcode - 294:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鎿嶄綔浜?銆傚缓璁娇鐢? {tc('text_f5g8b')} i18n/no-chinese-hardcode - 302:78 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍙栨秷"銆傚缓璁娇鐢? {tc('text_ev02')} i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\[locale]\warehouse\sales-outbound\page.tsx - 108:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\warehouse\sales-outbound\page.tsx:108:5 - 106 | }; - 107 | useEffect(() => { -> 108 | fetchData(); - | ^^^^^^^^^ Avoid calling setState() directly within an effect - 109 | }, [page]); - 110 | useEffect(() => { - 111 | fetchWarehouses(); react-hooks/set-state-in-effect - 109:6 warning React Hook useEffect has a missing dependency: 'fetchData'. Either include it or remove the dependency array react-hooks/exhaustive-deps - 111:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\warehouse\sales-outbound\page.tsx:111:5 - 109 | }, [page]); - 110 | useEffect(() => { -> 111 | fetchWarehouses(); - | ^^^^^^^^^^^^^^^ Avoid calling setState() directly within an effect - 112 | fetchCustomers(); - 113 | }, []); - 114 | react-hooks/set-state-in-effect - 166:46 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "閿€鍞嚭搴?銆傚缓璁娇鐢? {tc('text_j5fhqf')} i18n/no-chinese-hardcode - 186:48 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏂板鍑哄簱"銆傚缓璁娇鐢? {tc('text_d73t53')} i18n/no-chinese-hardcode - 196:50 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍑哄簱鍗曞彿"銆傚缓璁娇鐢? {tc('text_aqhecb')} i18n/no-chinese-hardcode - 197:50 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "閿€鍞鍗?銆傚缓璁娇鐢? {tc('text_j5p8kh')} i18n/no-chinese-hardcode - 200:50 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍑哄簱鏃ユ湡"銆傚缓璁娇鐢? {tc('text_aqknsj')} i18n/no-chinese-hardcode - 201:50 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍙戣揣浜?銆傚缓璁娇鐢? {tc('text_cyeis')} i18n/no-chinese-hardcode - 230:30 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "纭鍑哄簱"銆傚缓璁娇鐢? {tc('text_frohu7')} i18n/no-chinese-hardcode - 312:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍑哄簱鏃ユ湡"銆傚缓璁娇鐢? {tc('text_aqknsj')} i18n/no-chinese-hardcode - 327:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "瀹㈡埛鍚嶇О"銆傚缓璁娇鐢? {tc('text_byvcwo')} i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\[locale]\warehouse\setup\page.tsx - 141:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\warehouse\setup\page.tsx:141:5 - 139 | // 鍒濆鍔犺浇 - 140 | useEffect(() => { -> 141 | fetchWarehouses(); - | ^^^^^^^^^^^^^^^ Avoid calling setState() directly within an effect - 142 | }, []); - 143 | - 144 | // 鎼滅储鏃堕噸鏂板姞杞? react-hooks/set-state-in-effect - 142:6 warning React Hook useEffect has a missing dependency: 'fetchWarehouses'. Either include it or remove the dependency array react-hooks/exhaustive-deps - 150:6 warning React Hook useEffect has a missing dependency: 'fetchWarehouses'. Either include it or remove the dependency array react-hooks/exhaustive-deps - 365:95 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍔犺浇涓?.."銆傚缓璁娇鐢? {tc('text_27k1ha')} i18n/no-chinese-hardcode - 371:95 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏆傛棤浠撳簱鏁版嵁"銆傚缓璁娇鐢? {tc('text_n0w5s4')} i18n/no-chinese-hardcode - 388:103 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏄?銆傚缓璁娇鐢? {tc('text_k6n')} i18n/no-chinese-hardcode - 392:94 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍚?銆傚缓璁娇鐢? {tc('text_gme')} i18n/no-chinese-hardcode - 444:64 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "缂栬緫"銆傚缓璁娇鐢? {tc('text_mekb')} i18n/no-chinese-hardcode - 451:66 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍒犻櫎"銆傚缓璁娇鐢? {tc('text_eslg')} i18n/no-chinese-hardcode - 473:39 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "浠撳簱缂栫爜"銆傚缓璁娇鐢? {tc('text_acdqyz')} i18n/no-chinese-hardcode - 485:39 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "浠撳簱鍚嶇О"銆傚缓璁娇鐢? {tc('text_ac6ftv')} i18n/no-chinese-hardcode - 586:62 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "寮€鍚悗锛岃浠撳簱鐨勫簱瀛樺皢鍙備笌MRP闇€姹傝绠?銆傚缓璁娇鐢? {tc('text_va0t6m')} i18n/no-chinese-hardcode - 601:80 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍙栨秷"銆傚缓璁娇鐢? {tc('text_ev02')} i18n/no-chinese-hardcode - 618:52 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "纭畾瑕佸垹闄や粨搴?銆傚缓璁娇鐢? {tc('text_m9latj')} i18n/no-chinese-hardcode - 626:86 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍙栨秷"銆傚缓璁娇鐢? {tc('text_ev02')} i18n/no-chinese-hardcode - 629:75 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "纭鍒犻櫎"銆傚缓璁娇鐢? {tc('text_frotru')} i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\[locale]\warehouse\stock-adjust\page.tsx - 112:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\warehouse\stock-adjust\page.tsx:112:5 - 110 | }; - 111 | useEffect(() => { -> 112 | fetchData(); - | ^^^^^^^^^ Avoid calling setState() directly within an effect - 113 | }, [page]); - 114 | - 115 | const handleSave = async () => { react-hooks/set-state-in-effect - 113:6 warning React Hook useEffect has a missing dependency: 'fetchData'. Either include it or remove the dependency array react-hooks/exhaustive-deps - 190:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "搴撳瓨璋冩暣"銆傚缓璁娇鐢? {tc('text_cbhcc6')} i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\[locale]\warehouse\stocktaking\page.tsx - 156:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\warehouse\stocktaking\page.tsx:156:5 - 154 | - 155 | useEffect(() => { -> 156 | fetchData(); - | ^^^^^^^^^ Avoid calling setState() directly within an effect - 157 | }, [page]); - 158 | - 159 | const handleSave = async () => { react-hooks/set-state-in-effect - 157:6 warning React Hook useEffect has a missing dependency: 'fetchData'. Either include it or remove the dependency array react-hooks/exhaustive-deps - 297:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鐩樼偣鍗?銆傚缓璁娇鐢? {tc('text_hyahg')} i18n/no-chinese-hardcode - 693:49 warning 'v' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars - -D:\dcprint\erp-project\src\app\[locale]\warehouse\transfer\page.tsx - 126:10 warning 'transferItems' is assigned a value but never used. Allowed unused elements of array destructuring must match /^_/u @typescript-eslint/no-unused-vars - 163:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\app\[locale]\warehouse\transfer\page.tsx:163:5 - 161 | - 162 | useEffect(() => { -> 163 | fetchData(); - | ^^^^^^^^^ Avoid calling setState() directly within an effect - 164 | }, [page]); - 165 | - 166 | const handleSave = async () => { react-hooks/set-state-in-effect - 164:6 warning React Hook useEffect has a missing dependency: 'fetchData'. Either include it or remove the dependency array react-hooks/exhaustive-deps - 455:20 warning Error: Cannot create components during render - -Components created during render will reset their state each time they are created. Declare components outside of render. - -D:\dcprint\erp-project\src\app\[locale]\warehouse\transfer\page.tsx:455:20 - 453 | /> - 454 | </TableHead> -> 455 | <SortableHeader field="transfer_no">{t('transferNo')}</SortableHeader> - | ^^^^^^^^^^^^^^ This component is created during render - 456 | <SortableHeader field="type">{tc('type')}</SortableHeader> - 457 | <SortableHeader field="from_warehouse_name"> - 458 | {t('sourceWarehouse')} - -D:\dcprint\erp-project\src\app\[locale]\warehouse\transfer\page.tsx:394:26 - 392 | }; - 393 | -> 394 | const SortableHeader = ({ field, children }: { field: string; children: React.ReactNode }) => ( - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 395 | <TableHead - | ^^^^^^^^^^^^^^ -> 396 | className="cursor-pointer select-none border border-border bg-muted/50 text-muted-foreground text-center whitespace-nowrap hover:bg-muted/70 transition-colors" - 鈥? | ^^^^^^^^^^^^^^ -> 411 | </TableHead> - | ^^^^^^^^^^^^^^ -> 412 | ); - | ^^^^ The component is created during render here - 413 | - 414 | return ( - 415 | <MainLayout> react-hooks/static-components - 456:20 warning Error: Cannot create components during render - -Components created during render will reset their state each time they are created. Declare components outside of render. - -D:\dcprint\erp-project\src\app\[locale]\warehouse\transfer\page.tsx:456:20 - 454 | </TableHead> - 455 | <SortableHeader field="transfer_no">{t('transferNo')}</SortableHeader> -> 456 | <SortableHeader field="type">{tc('type')}</SortableHeader> - | ^^^^^^^^^^^^^^ This component is created during render - 457 | <SortableHeader field="from_warehouse_name"> - 458 | {t('sourceWarehouse')} - 459 | </SortableHeader> - -D:\dcprint\erp-project\src\app\[locale]\warehouse\transfer\page.tsx:394:26 - 392 | }; - 393 | -> 394 | const SortableHeader = ({ field, children }: { field: string; children: React.ReactNode }) => ( - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 395 | <TableHead - | ^^^^^^^^^^^^^^ -> 396 | className="cursor-pointer select-none border border-border bg-muted/50 text-muted-foreground text-center whitespace-nowrap hover:bg-muted/70 transition-colors" - 鈥? | ^^^^^^^^^^^^^^ -> 411 | </TableHead> - | ^^^^^^^^^^^^^^ -> 412 | ); - | ^^^^ The component is created during render here - 413 | - 414 | return ( - 415 | <MainLayout> react-hooks/static-components - 457:20 warning Error: Cannot create components during render - -Components created during render will reset their state each time they are created. Declare components outside of render. - -D:\dcprint\erp-project\src\app\[locale]\warehouse\transfer\page.tsx:457:20 - 455 | <SortableHeader field="transfer_no">{t('transferNo')}</SortableHeader> - 456 | <SortableHeader field="type">{tc('type')}</SortableHeader> -> 457 | <SortableHeader field="from_warehouse_name"> - | ^^^^^^^^^^^^^^ This component is created during render - 458 | {t('sourceWarehouse')} - 459 | </SortableHeader> - 460 | <SortableHeader field="to_warehouse_name">{t('targetWarehouse')}</SortableHeader> - -D:\dcprint\erp-project\src\app\[locale]\warehouse\transfer\page.tsx:394:26 - 392 | }; - 393 | -> 394 | const SortableHeader = ({ field, children }: { field: string; children: React.ReactNode }) => ( - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 395 | <TableHead - | ^^^^^^^^^^^^^^ -> 396 | className="cursor-pointer select-none border border-border bg-muted/50 text-muted-foreground text-center whitespace-nowrap hover:bg-muted/70 transition-colors" - 鈥? | ^^^^^^^^^^^^^^ -> 411 | </TableHead> - | ^^^^^^^^^^^^^^ -> 412 | ); - | ^^^^ The component is created during render here - 413 | - 414 | return ( - 415 | <MainLayout> react-hooks/static-components - 460:20 warning Error: Cannot create components during render - -Components created during render will reset their state each time they are created. Declare components outside of render. - -D:\dcprint\erp-project\src\app\[locale]\warehouse\transfer\page.tsx:460:20 - 458 | {t('sourceWarehouse')} - 459 | </SortableHeader> -> 460 | <SortableHeader field="to_warehouse_name">{t('targetWarehouse')}</SortableHeader> - | ^^^^^^^^^^^^^^ This component is created during render - 461 | <TableHead className="border border-border bg-muted/50 text-muted-foreground text-center"> - 462 | {tc('status')} - 463 | </TableHead> - -D:\dcprint\erp-project\src\app\[locale]\warehouse\transfer\page.tsx:394:26 - 392 | }; - 393 | -> 394 | const SortableHeader = ({ field, children }: { field: string; children: React.ReactNode }) => ( - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 395 | <TableHead - | ^^^^^^^^^^^^^^ -> 396 | className="cursor-pointer select-none border border-border bg-muted/50 text-muted-foreground text-center whitespace-nowrap hover:bg-muted/70 transition-colors" - 鈥? | ^^^^^^^^^^^^^^ -> 411 | </TableHead> - | ^^^^^^^^^^^^^^ -> 412 | ); - | ^^^^ The component is created during render here - 413 | - 414 | return ( - 415 | <MainLayout> react-hooks/static-components - -D:\dcprint\erp-project\src\app\api\advanced\cost-analysis\route.ts - 62:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鏈煡鎿嶄綔"銆傚缓璁娇鐢? tc('text_dih7qi') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\advanced\forecasting\route.ts - 8:41 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "缂哄皯 materialId"銆傚缓璁娇鐢? tc('text_5qxdbp') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\advanced\scheduling\optimize\route.ts - 3:27 warning 'errorResponse' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - -D:\dcprint\erp-project\src\app\api\audit\logs\route.ts - 14:3 warning 'generateAuditReport' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 102:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "涓嶆敮鎸佺殑鏃ュ織绫诲瀷"銆傚缓璁娇鐢? tc('text_5uwt7r') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\audit\report\route.ts - 19:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "璇锋彁渚涘紑濮嬫椂闂村拰缁撴潫鏃堕棿"銆傚缓璁娇鐢? tc('text_pelah7') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\auth\change-password\route.ts - 34:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "缂哄皯蹇呭~瀛楁: oldPassword, newPassword"銆傚缓璁娇鐢? tc('text_hcta6e') i18n/no-chinese-hardcode - 42:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "targetUserId 蹇呴』涓烘暟瀛?銆傚缓璁娇鐢? tc('text_mcta3m') i18n/no-chinese-hardcode - 68:11 warning 'passwordExpireDays' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 79:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鏂板瘑鐮佸繀椤诲寘鍚瓧姣嶅拰鏁板瓧"銆傚缓璁娇鐢? tc('text_b9jmyy') i18n/no-chinese-hardcode - 83:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鏂板瘑鐮佸繀椤诲寘鍚ぇ鍐欏瓧姣?銆傚缓璁娇鐢? tc('text_qr12sv') i18n/no-chinese-hardcode - 87:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鏂板瘑鐮佸繀椤诲寘鍚壒娈婂瓧绗?銆傚缓璁娇鐢? tc('text_u1sv6v') i18n/no-chinese-hardcode - 102:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍘熷瘑鐮侀敊璇?銆傚缓璁娇鐢? tc('text_g4459s') i18n/no-chinese-hardcode - 107:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鏂板瘑鐮佷笉鑳戒笌鏃у瘑鐮佺浉鍚?銆傚缓璁娇鐢? tc('text_vz713x') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\auth\login\route.ts - 260:11 warning 'isAbnormalLogin' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 450:10 warning 'parseBrowser' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 458:10 warning 'parseOS' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - -D:\dcprint\erp-project\src\app\api\auth\refresh\route.ts - 57:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "缂哄皯 refreshToken 鎴?userId"銆傚缓璁娇鐢? tc('text_7cc8jt') i18n/no-chinese-hardcode - 62:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "姝e湪鍒锋柊锛岃绋嶅悗閲嶈瘯"銆傚缓璁娇鐢? tc('text_r5yieq') i18n/no-chinese-hardcode - 68:30 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "refresh token 鏃犳晥鎴栧凡杩囨湡"銆傚缓璁娇鐢? tc('text_36pztk') i18n/no-chinese-hardcode - 78:30 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鐢ㄦ埛涓嶅瓨鍦ㄦ垨宸茬鐢?銆傚缓璁娇鐢? tc('text_p7wodr') i18n/no-chinese-hardcode - 142:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "Token 鍒锋柊澶辫触"銆傚缓璁娇鐢? tc('text_qly19o') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\auth\register\route.ts - 80:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鐢ㄦ埛鍚嶅彧鑳藉寘鍚瓧姣嶃€佹暟瀛楀拰涓嬪垝绾匡紝闀垮害4-20浣?銆傚缓璁娇鐢? tc('text_bnvqi2') i18n/no-chinese-hardcode - 85:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "瀵嗙爜闀垮害涓嶈兘灏戜簬6浣?銆傚缓璁娇鐢? tc('text_sua2re') i18n/no-chinese-hardcode - 90:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "閭鏍煎紡涓嶆纭?銆傚缓璁娇鐢? tc('text_venleq') i18n/no-chinese-hardcode - 100:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鐢ㄦ埛鍚嶅凡瀛樺湪"銆傚缓璁娇鐢? tc('text_y3ra90') i18n/no-chinese-hardcode - 110:30 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "閭宸茶娉ㄥ唽"銆傚缓璁娇鐢? tc('text_99ykbk') i18n/no-chinese-hardcode - 117:30 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鎵嬫満鍙锋牸寮忎笉姝g‘"銆傚缓璁娇鐢? tc('text_ve0m7x') i18n/no-chinese-hardcode - 124:30 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鎵嬫満鍙峰凡琚敞鍐?銆傚缓璁娇鐢? tc('text_twd997') i18n/no-chinese-hardcode - 134:30 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鎸囧畾鐨勯儴闂ㄤ笉瀛樺湪"銆傚缓璁娇鐢? tc('text_4uqdvw') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\biz\contract-review\route.ts - 44:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "缂哄皯蹇呭~瀛楁: order_id, order_no"銆傚缓璁娇鐢? tc('text_ac9fsj') i18n/no-chinese-hardcode - 96:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "缂哄皯蹇呭~瀛楁: id, department"銆傚缓璁娇鐢? tc('text_ulyy76') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\business\contract-review\route.ts - 74:46 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "瀹㈡埛鍚嶇О涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_uwwkbc') i18n/no-chinese-hardcode - 75:45 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "浜у搧鍚嶇О涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_5zh4st') i18n/no-chinese-hardcode - 150:35 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "ID涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_p1r591') i18n/no-chinese-hardcode - 248:51 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "娌℃湁闇€瑕佹洿鏂扮殑瀛楁"銆傚缓璁娇鐢? tc('text_nyjsyr') i18n/no-chinese-hardcode - 261:35 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "ID涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_p1r591') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\crm\analysis\route.ts - 75:44 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "瀹㈡埛ID涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_56nha8') i18n/no-chinese-hardcode - 108:35 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "ID涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_p1r591') i18n/no-chinese-hardcode - 145:35 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "ID涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_p1r591') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\crm\follow\route.ts - 57:44 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "瀹㈡埛ID涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_56nha8') i18n/no-chinese-hardcode - 85:35 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "ID涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_p1r591') i18n/no-chinese-hardcode - 120:35 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "ID涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_p1r591') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\customers\route.ts - 171:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "瀹㈡埛缂栫爜宸插瓨鍦?銆傚缓璁娇鐢? tc('text_8zfd36') i18n/no-chinese-hardcode - 247:30 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "瀹㈡埛缂栫爜宸插瓨鍦?銆傚缓璁娇鐢? tc('text_8zfd36') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\dcprint\formula\color\route.ts - 27:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鑹插彿缂栫爜鍜屽悕绉颁笉鑳戒负绌?銆傚缓璁娇鐢? tc('text_pcjjhr') i18n/no-chinese-hardcode - 35:30 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鑹插彿缂栫爜宸插瓨鍦?銆傚缓璁娇鐢? tc('text_2g3pim') i18n/no-chinese-hardcode - 49:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "缂哄皯鑹插彿ID"銆傚缓璁娇鐢? tc('text_syb12v') i18n/no-chinese-hardcode - 66:35 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "缂哄皯id"銆傚缓璁娇鐢? tc('text_gf6a2a') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\dcprint\formula\version\[id]\items\route.ts - 13:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "缂哄皯 items 鍙傛暟"銆傚缓璁娇鐢? tc('text_ric2x5') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\dcprint\formula\version\[id]\preview-cost\route.ts - 11:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "缂哄皯 items 鍙傛暟"銆傚缓璁娇鐢? tc('text_ric2x5') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\dcprint\formula\version\[id]\route.ts - 17:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鐗堟湰涓嶅瓨鍦?銆傚缓璁娇鐢? tc('text_xzstvd') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\dcprint\formula\version\compare\route.ts - 13:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "缂哄皯 leftId 鎴?rightId 鍙傛暟"銆傚缓璁娇鐢? tc('text_9k924q') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\dcprint\formula\version\route.ts - 35:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鐗堟湰涓嶅瓨鍦?銆傚缓璁娇鐢? tc('text_xzstvd') i18n/no-chinese-hardcode - 45:24 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "璇锋彁渚?colorId 鎴?id 鍙傛暟"銆傚缓璁娇鐢? tc('text_30xi3b') i18n/no-chinese-hardcode - 59:30 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "缂哄皯 items 鍙傛暟"銆傚缓璁娇鐢? tc('text_ric2x5') i18n/no-chinese-hardcode - 66:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "缂哄皯蹇呭~瀛楁: color_id, items"銆傚缓璁娇鐢? tc('text_yen1rg') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\dcprint\ink-dispatch\route.ts - 79:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "缂哄皯蹇呭~瀛楁: workorder_no, items"銆傚缓璁娇鐢? tc('text_tmsoko') i18n/no-chinese-hardcode - 233:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "閰嶆枡璁板綍ID涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_15si2k') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\dcprint\ink-formula\route.ts - 89:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "缂哄皯蹇呭~瀛楁: formula_name, items"銆傚缓璁娇鐢? tc('text_ll77jb') i18n/no-chinese-hardcode - 163:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "閰嶆柟ID涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_z4slo7') i18n/no-chinese-hardcode - 168:30 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "缂哄皯宸ュ崟淇℃伅"銆傚缓璁娇鐢? tc('text_ocn32t') i18n/no-chinese-hardcode - 181:30 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "缂哄皯宸ュ崟ID"銆傚缓璁娇鐢? tc('text_oc90oy') i18n/no-chinese-hardcode - 237:35 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "缂哄皯id"銆傚缓璁娇鐢? tc('text_gf6a2a') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\dcprint\ink-opening\route.ts - 274:30 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鏃犳晥鐨勭姸鎬佸€?銆傚缓璁娇鐢? tc('text_pxieq3') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\dcprint\ink-query\route.ts - 13:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "璇锋彁渚泀rCode鎴朾atchNo"銆傚缓璁娇鐢? tc('text_megb1x') i18n/no-chinese-hardcode - 24:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "浜岀淮鐮佷笉瀛樺湪"銆傚缓璁娇鐢? tc('text_h3puxw') i18n/no-chinese-hardcode - 30:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鏃犳硶纭畾鎵规鍙?銆傚缓璁娇鐢? tc('text_q3zlzm') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\dcprint\ink-surplus\route.ts - 21:40 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "缂哄皯batchNo"銆傚缓璁娇鐢? tc('text_k7ygf8') i18n/no-chinese-hardcode - 84:42 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? " - SELECT - ib.batch_no, - ib.material_name, - ib.available_qty, - ib.unit_price, - ib.expire_date, - ib.inbound_date, - ib.warehouse_id, - w.warehouse_name, - DATEDIFF(ib.expire_date, CURDATE()) as days_until_expiry, - d.pantone_code, - d.color_name as dispatch_color_name, - d.formula_no, - d.workorder_no as original_workorder - FROM inv_inventory_batch ib - LEFT JOIN inv_warehouse w ON ib.warehouse_id = w.id - LEFT JOIN ink_dispatch d ON d.batch_no = ib.batch_no AND d.deleted = 0 - WHERE ib.deleted = 0 AND ib.available_qty > 0 AND ib.status = 'normal' - AND (ib.expire_date IS NULL OR ib.expire_date >= CURDATE()) - AND (ib.material_name LIKE '%涓撹壊%' OR ib.material_name LIKE '%璋冭壊%' OR ib.material_name LIKE '%浣欏ⅷ%' - OR ib.batch_no LIKE 'INK%' OR ib.batch_no LIKE 'MIX%') - ORDER BY ib.inbound_date ASC - "銆傚缓璁娇鐢? tc(`text_rsen5j`) i18n/no-chinese-hardcode - 116:27 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "Pantone鑹插彿瀹屽叏鍖归厤"銆傚缓璁娇鐢? tc('text_l9t79g') i18n/no-chinese-hardcode - 119:27 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "Pantone鑹插彿鐩歌繎"銆傚缓璁娇鐢? tc('text_eb9ygj') i18n/no-chinese-hardcode - 129:27 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "棰滆壊鍚嶇О鍖归厤"銆傚缓璁娇鐢? tc('text_hglcc3') i18n/no-chinese-hardcode - 135:25 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍙敤閲忓厖瓒?銆傚缓璁娇鐢? tc('text_blexlo') i18n/no-chinese-hardcode - 140:25 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏈夋晥鏈熷厖瑁?銆傚缓璁娇鐢? tc('text_a454xc') i18n/no-chinese-hardcode - 143:25 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏈夋晥鏈熷嵆灏嗗埌鏈燂紝浼樺厛浣跨敤"銆傚缓璁娇鐢? tc('text_3pjx6l') i18n/no-chinese-hardcode - 185:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鎵规涓嶅瓨鍦?銆傚缓璁娇鐢? tc('text_rbfprf') i18n/no-chinese-hardcode - 185:26 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鎵规涓嶅瓨鍦?銆傚缓璁娇鐢? tc('text_rbfprf') i18n/no-chinese-hardcode - 250:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "缂哄皯蹇呭~瀛楁: batch_no, return_weight"銆傚缓璁娇鐢? tc('text_5see6s') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\dcprint\ink-usage\route.ts - 79:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "缂哄皯蹇呭~瀛楁: usage_type, weight"銆傚缓璁娇鐢? tc('text_3djg79') i18n/no-chinese-hardcode - 239:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "璁板綍ID涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_nebjio') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\dcprint\labels\route.ts - 250:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "缂哄皯鏍囩ID"銆傚缓璁娇鐢? tc('text_ps545l') i18n/no-chinese-hardcode - 289:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "娌℃湁瑕佹洿鏂扮殑瀛楁"銆傚缓璁娇鐢? tc('text_magxwt') i18n/no-chinese-hardcode - 311:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "缂哄皯鏍囩ID"銆傚缓璁娇鐢? tc('text_ps545l') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\dcprint\process-cards\route.ts - 143:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "涓绘潗鏍囩涓嶅瓨鍦?銆傚缓璁娇鐢? tc('text_tectbj') i18n/no-chinese-hardcode - 147:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "璇ユ爣绛句笉鏄瘝鏉愭爣绛撅紝涓嶈兘浣滀负涓绘潗浣跨敤"銆傚缓璁娇鐢? tc('text_22sv9c') i18n/no-chinese-hardcode - 223:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "缂哄皯娴佺▼鍗D鎴栧崱鍙?銆傚缓璁娇鐢? tc('text_gnje9b') i18n/no-chinese-hardcode - 245:29 warning 'createUserId' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 245:43 warning 'createUserName' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 254:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "娴佺▼鍗′笉瀛樺湪"銆傚缓璁娇鐢? tc('text_lnluva') i18n/no-chinese-hardcode - 254:26 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "娴佺▼鍗′笉瀛樺湪"銆傚缓璁娇鐢? tc('text_lnluva') i18n/no-chinese-hardcode - 258:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "娴佺▼鍗″凡閿佷綇锛屼笉鑳芥坊鍔犺緟鏂?銆傚缓璁娇鐢? tc('text_gpknh0') i18n/no-chinese-hardcode - 258:26 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "娴佺▼鍗″凡閿佷綇锛屼笉鑳芥坊鍔犺緟鏂?銆傚缓璁娇鐢? tc('text_gpknh0') i18n/no-chinese-hardcode - 269:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鐗╂枡鏍囩涓嶅瓨鍦?銆傚缓璁娇鐢? tc('text_wkfluy') i18n/no-chinese-hardcode - 269:26 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鐗╂枡鏍囩涓嶅瓨鍦?銆傚缓璁娇鐢? tc('text_wkfluy') i18n/no-chinese-hardcode - 295:32 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鎿嶄綔鎴愬姛"銆傚缓璁娇鐢? tc('text_d1spvi') i18n/no-chinese-hardcode - 305:32 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "閰嶆枡鐘舵€佹洿鏂版垚鍔?銆傚缓璁娇鐢? tc('text_wabaki') i18n/no-chinese-hardcode - 315:54 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "娴佺▼鍗″凡閿佷綇"銆傚缓璁娇鐢? tc('text_lq7fm1') i18n/no-chinese-hardcode - 315:65 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "娴佺▼鍗″凡瑙i攣"銆傚缓璁娇鐢? tc('text_lq5xax') i18n/no-chinese-hardcode - 340:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "娌℃湁瑕佹洿鏂扮殑瀛楁"銆傚缓璁娇鐢? tc('text_magxwt') i18n/no-chinese-hardcode - 340:26 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "娌℃湁瑕佹洿鏂扮殑瀛楁"銆傚缓璁娇鐢? tc('text_magxwt') i18n/no-chinese-hardcode - 350:32 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "娴佺▼鍗℃洿鏂版垚鍔?銆傚缓璁娇鐢? tc('text_z8gj5u') i18n/no-chinese-hardcode - 360:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "缂哄皯娴佺▼鍗D"銆傚缓璁娇鐢? tc('text_wjo3hh') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\dcprint\sample-card\[id]\route.ts - 17:37 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "宸ヨ壓鍗′笉瀛樺湪"銆傚缓璁娇鐢? tc('text_vamx0v') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\dcprint\sample-card\[id]\save-as-template\route.ts - 18:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "妯℃澘鍚嶇О涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_f2tvjj') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\dcprint\sample-card\template\[id]\route.ts - 2:27 warning 'errorResponse' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - -D:\dcprint\erp-project\src\app\api\dcprint\sample-card\template\route.ts - 25:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "妯℃澘鍚嶇О涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_f2tvjj') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\dcprint\scan\route.ts - 13:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "浜岀淮鐮佸唴瀹逛笉鑳戒负绌?銆傚缓璁娇鐢? tc('text_eg437h') i18n/no-chinese-hardcode - 28:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "浜岀淮鐮佹牸寮忎笉姝g‘"銆傚缓璁娇鐢? tc('text_b4aqus') i18n/no-chinese-hardcode - 64:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鏈壘鍒板搴旇褰?銆傚缓璁娇鐢? tc('text_59ls58') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\dcprint\trace\route.ts - 135:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "娴佺▼鍗″彿涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_kr348g') i18n/no-chinese-hardcode - 156:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "娴佺▼鍗′笉瀛樺湪"銆傚缓璁娇鐢? tc('text_lnluva') i18n/no-chinese-hardcode - 266:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "杩芥函鍗曞彿涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_zf14nw') i18n/no-chinese-hardcode - 290:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "杩芥函璁板綍涓嶅瓨鍦?銆傚缓璁娇鐢? tc('text_qtsksa') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\delivery\vehicles\route.ts - 124:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "杞︾墝鍙峰凡瀛樺湪"銆傚缓璁娇鐢? tc('text_ackpe9') i18n/no-chinese-hardcode - 198:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "杞︾墝鍙峰凡瀛樺湪"銆傚缓璁娇鐢? tc('text_ackpe9') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\document-number\route.ts - 15:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "缂哄皯docType鍙傛暟"銆傚缓璁娇鐢? tc('text_doqg53') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\engineering\sample-to-mass\route.ts - 56:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "缂哄皯蹇呭~瀛楁: sample_order_id, sample_order_no"銆傚缓璁娇鐢? tc('text_lph0dp') i18n/no-chinese-hardcode - 122:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "杞噺浜ц褰旾D涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_sz0gng') i18n/no-chinese-hardcode - 185:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鏃犳晥鐨勬搷浣?銆傚缓璁娇鐢? tc('text_el3ulx') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\engineering\sop\route.ts - 52:45 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "浜у搧鍚嶇О涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_5zh4st') i18n/no-chinese-hardcode - 93:35 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "ID涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_p1r591') i18n/no-chinese-hardcode - 133:35 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "ID涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_p1r591') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\engineering\standard-card\route.ts - 19:42 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "璇锋彁渚涘伐鑹哄崱鏁版嵁"銆傚缓璁娇鐢? tc('text_hkk7w8') i18n/no-chinese-hardcode - 37:54 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "璇锋彁渚涘伐鑹哄崱ID鍜岀増鏈彿"銆傚缓璁娇鐢? tc('text_vc1hp0') i18n/no-chinese-hardcode - 45:52 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "璇锋彁渚涘伐鑹哄崱ID鍜屾洿鏂版暟鎹?銆傚缓璁娇鐢? tc('text_lmqi0x') i18n/no-chinese-hardcode - 76:39 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "璇锋彁渚涘伐鑹哄崱ID"銆傚缓璁娇鐢? tc('text_hl1yvv') i18n/no-chinese-hardcode - 86:42 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "璇锋彁渚涘姣斿弬鏁?銆傚缓璁娇鐢? tc('text_5hf7x1') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\equipment\maintenance\route.ts - 30:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "缁翠繚璁板綍涓嶅瓨鍦?銆傚缓璁娇鐢? tc('text_8g6prl') i18n/no-chinese-hardcode - 112:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "璁惧ID鍜岀淮淇濇棩鏈熶笉鑳戒负绌?銆傚缓璁娇鐢? tc('text_j7qjzv') i18n/no-chinese-hardcode - 121:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "璁惧涓嶅瓨鍦?銆傚缓璁娇鐢? tc('text_aase18') i18n/no-chinese-hardcode - 208:35 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "ID涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_p1r591') i18n/no-chinese-hardcode - 242:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "娌℃湁鍙洿鏂扮殑瀛楁"銆傚缓璁娇鐢? tc('text_7471mp') i18n/no-chinese-hardcode - 263:35 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "ID涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_p1r591') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\equipment\plan\route.ts - 70:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "缁翠繚璁″垝涓嶅瓨鍦?銆傚缓璁娇鐢? tc('text_acnf4t') i18n/no-chinese-hardcode - 143:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "璁惧ID鍜岃鍒掑悕绉颁笉鑳戒负绌?銆傚缓璁娇鐢? tc('text_prb6ks') i18n/no-chinese-hardcode - 152:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "璁惧涓嶅瓨鍦?銆傚缓璁娇鐢? tc('text_aase18') i18n/no-chinese-hardcode - 205:35 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "ID涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_p1r591') i18n/no-chinese-hardcode - 235:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "娌℃湁鍙洿鏂扮殑瀛楁"銆傚缓璁娇鐢? tc('text_7471mp') i18n/no-chinese-hardcode - 276:35 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "ID涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_p1r591') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\equipment\route.ts - 31:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "璁惧涓嶅瓨鍦?銆傚缓璁娇鐢? tc('text_aase18') i18n/no-chinese-hardcode - 104:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "璁惧缂栧彿鍜屽悕绉颁笉鑳戒负绌?銆傚缓璁娇鐢? tc('text_twhfd7') i18n/no-chinese-hardcode - 113:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "璁惧缂栧彿宸插瓨鍦?銆傚缓璁娇鐢? tc('text_o2nvko') i18n/no-chinese-hardcode - 149:35 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "ID涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_p1r591') i18n/no-chinese-hardcode - 181:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "娌℃湁鍙洿鏂扮殑瀛楁"銆傚缓璁娇鐢? tc('text_7471mp') i18n/no-chinese-hardcode - 202:35 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "ID涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_p1r591') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\finance\cost-variance\route.ts - 231:45 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏉愭枡瓒呰€?銆傚缓璁娇鐢? tc('text_dgo6yj') i18n/no-chinese-hardcode - 232:44 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏉愭枡鑺傜害"銆傚缓璁娇鐢? tc('text_dgmb8t') i18n/no-chinese-hardcode - 233:41 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "宸ユ椂瓒呮敮"銆傚缓璁娇鐢? tc('text_c9yh8b') i18n/no-chinese-hardcode - 234:40 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏁堢巼鎻愬崌"銆傚缓璁娇鐢? tc('text_d9a9rq') i18n/no-chinese-hardcode - 235:35 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "搴熷搧鐜囧亸楂?銆傚缓璁娇鐢? tc('text_pzdu5q') i18n/no-chinese-hardcode - 236:36 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "璐ㄩ噺寮傚父"銆傚缓璁娇鐢? tc('text_ier8t9') i18n/no-chinese-hardcode - 237:42 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "姝e父鍋忓樊"銆傚缓璁娇鐢? tc('text_dxsnzo') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\finance\costs\route.ts - 47:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "缂哄皯workOrderId"銆傚缓璁娇鐢? tc('text_5bbq69') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\finance\expense\route.ts - 120:35 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鎶ラ攢鍗旾D涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_n03vvf') i18n/no-chinese-hardcode - 139:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鏃犳晥鐨勬搷浣滅被鍨?銆傚缓璁娇鐢? tc('text_obzmdh') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\finance\invoice\route.ts - 87:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍙戠エ鏄庣粏涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_sau1hb') i18n/no-chinese-hardcode - 189:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍙傛暟涓嶅畬鏁?銆傚缓璁娇鐢? tc('text_ejeut5') i18n/no-chinese-hardcode - 194:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍙戠エ涓嶅瓨鍦?銆傚缓璁娇鐢? tc('text_bshx16') i18n/no-chinese-hardcode - 201:30 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍙湁寰呭鏍哥姸鎬佹墠鑳藉鏍?銆傚缓璁娇鐢? tc('text_zc80en') i18n/no-chinese-hardcode - 212:30 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍙戠エ宸蹭綔搴?銆傚缓璁娇鐢? tc('text_bq83qa') i18n/no-chinese-hardcode - 221:30 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍙湁宸插鏍哥姸鎬佹墠鑳芥牳閿€"銆傚缓璁娇鐢? tc('text_6k5wnh') i18n/no-chinese-hardcode - 225:30 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鏍搁攢閲戦蹇呴』澶т簬0"銆傚缓璁娇鐢? tc('text_a50329') i18n/no-chinese-hardcode - 248:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "涓嶆敮鎸佺殑鎿嶄綔"銆傚缓璁娇鐢? tc('text_47gje4') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\finance\payables\[id]\payment\route.ts - 13:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "缂哄皯蹇呰鍙傛暟"銆傚缓璁娇鐢? tc('text_ot3wtd') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\finance\payment\route.ts - 15:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "缂哄皯蹇呭~瀛楁: payable_id, amount, payment_date"銆傚缓璁娇鐢? tc('text_98ca2o') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\finance\receipt\route.ts - 15:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "缂哄皯蹇呭~瀛楁: receivable_id, amount, receipt_date"銆傚缓璁娇鐢? tc('text_2326xe') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\finance\receivables\[id]\receipt\route.ts - 13:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "缂哄皯蹇呰鍙傛暟"銆傚缓璁娇鐢? tc('text_ot3wtd') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\hr\attendance\route.ts - 137:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鑰冨嫟鏃ユ湡鏍煎紡涓嶆纭紝搴斾负 YYYY-MM-DD"銆傚缓璁娇鐢? tc('text_2bldqg') i18n/no-chinese-hardcode - 141:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "涓婄彮鏃堕棿鏍煎紡涓嶆纭紝搴斾负 HH:mm"銆傚缓璁娇鐢? tc('text_l5rymw') i18n/no-chinese-hardcode - 145:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "涓嬬彮鏃堕棿鏍煎紡涓嶆纭紝搴斾负 HH:mm"銆傚缓璁娇鐢? tc('text_xdpddz') i18n/no-chinese-hardcode - 149:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鑰冨嫟鐘舵€佸€间笉姝g‘锛屽簲涓?normal/late/absent/leave"銆傚缓璁娇鐢? tc('text_jufxag') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\hr\certificates\route.ts - 43:38 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "缂哄皯璇佷功ID"銆傚缓璁娇鐢? tc('text_u3ra5z') i18n/no-chinese-hardcode - 52:33 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "缂哄皯璇佷功ID"銆傚缓璁娇鐢? tc('text_u3ra5z') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\hr\finance-sync\insurance\route.ts - 15:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鏈堜唤鏍煎紡閿欒 (YYYY-MM)"銆傚缓璁娇鐢? tc('text_ctyhhe') i18n/no-chinese-hardcode - 23:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "璇ユ湀浠芥棤钖祫璁$畻鏁版嵁"銆傚缓璁娇鐢? tc('text_5fwjuc') i18n/no-chinese-hardcode - 26:9 warning 'employeeIds' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - -D:\dcprint\erp-project\src\app\api\hr\finance-sync\salary-transfer\route.ts - 15:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "缂哄皯鍛樺伐ID鍒楄〃"銆傚缓璁娇鐢? tc('text_t0lac0') i18n/no-chinese-hardcode - 18:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鏈堜唤鏍煎紡閿欒 (YYYY-MM)"銆傚缓璁娇鐢? tc('text_ctyhhe') i18n/no-chinese-hardcode - 29:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鏈壘鍒扮鍚堟潯浠剁殑钖祫璁$畻缁撴灉"銆傚缓璁娇鐢? tc('text_uz92ra') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\hr\mes-sync\piece-work\route.ts - 12:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "缂哄皯璁′欢璁板綍鏁版嵁"銆傚缓璁娇鐢? tc('text_prkm5r') i18n/no-chinese-hardcode - 16:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍗曟鍚屾璁板綍鏁颁笉鑳借秴杩?000"銆傚缓璁娇鐢? tc('text_kvrwo9') i18n/no-chinese-hardcode - 23:42 warning 'request' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars - -D:\dcprint\erp-project\src\app\api\hr\quality-sync\defect-rate\route.ts - 4:10 warning 'query' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 18:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "缂哄皯鍛樺伐ID鎴栨湀浠?銆傚缓璁娇鐢? tc('text_nybx2k') i18n/no-chinese-hardcode - 23:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍛樺伐ID鏍煎紡涓嶆纭?銆傚缓璁娇鐢? tc('text_solopv') i18n/no-chinese-hardcode - 26:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鏈堜唤鏍煎紡閿欒 (YYYY-MM)"銆傚缓璁娇鐢? tc('text_ctyhhe') i18n/no-chinese-hardcode - 30:9 warning 'startDate' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 31:9 warning 'endDate' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - -D:\dcprint\erp-project\src\app\api\hr\reports\turnover\route.ts - 9:42 warning 'request' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars - -D:\dcprint\erp-project\src\app\api\hr\salary\calculate\route.ts - 23:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "缂哄皯鍛樺伐ID鎴栬绠楁湀浠?銆傚缓璁娇鐢? tc('text_a8vif6') i18n/no-chinese-hardcode - 26:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鏈堜唤鏍煎紡閿欒 (YYYY-MM)"銆傚缓璁娇鐢? tc('text_ctyhhe') i18n/no-chinese-hardcode - 43:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "缂哄皯璁$畻鏈堜唤"銆傚缓璁娇鐢? tc('text_uab1uq') i18n/no-chinese-hardcode - 51:5 warning Unexpected console statement. Only these console methods are allowed: warn, error no-console - 55:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鏈壘鍒颁换浣曞憳宸?銆傚缓璁娇鐢? tc('text_3k48gz') i18n/no-chinese-hardcode - 76:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "缂哄皯鍛樺伐ID鎴栨湀浠?銆傚缓璁娇鐢? tc('text_nybx2k') i18n/no-chinese-hardcode - 81:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鏈壘鍒拌绠楃粨鏋?銆傚缓璁娇鐢? tc('text_be6m6z') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\hr\salary\payslip\route.ts - 16:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "缂哄皯鍛樺伐ID鎴栨湀浠?銆傚缓璁娇鐢? tc('text_nybx2k') i18n/no-chinese-hardcode - 48:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鏈壘鍒拌鏈堣柂璧勬暟鎹?銆傚缓璁娇鐢? tc('text_yqpktz') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\hr\salary\route.ts - 1:23 warning 'NextResponse' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 17:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鏈堜唤鏍煎紡涓嶆纭紝搴斾负 yyyy-MM"銆傚缓璁娇鐢? tc('text_9t6fl') i18n/no-chinese-hardcode - 56:30 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "閮ㄩ棬ID鏍煎紡涓嶆纭?銆傚缓璁娇鐢? tc('text_yspf7a') i18n/no-chinese-hardcode - 66:30 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鎼滅储鍏抽敭璇嶈繃闀?銆傚缓璁娇鐢? tc('text_i62xw') i18n/no-chinese-hardcode - 120:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍛樺伐涓嶅瓨鍦ㄦ垨宸插仠鐢?銆傚缓璁娇鐢? tc('text_bzoyzs') i18n/no-chinese-hardcode - 146:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "澶囨敞闀垮害涓嶈兘瓒呰繃500涓瓧绗?銆傚缓璁娇鐢? tc('text_49z0la') i18n/no-chinese-hardcode - 234:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "缂哄皯璁板綍ID"銆傚缓璁娇鐢? tc('text_u5xpuf') i18n/no-chinese-hardcode - 239:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "璁板綍ID鏍煎紡涓嶆纭?銆傚缓璁娇鐢? tc('text_sxyzcr') i18n/no-chinese-hardcode - 245:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "钖祫璁板綍涓嶅瓨鍦?銆傚缓璁娇鐢? tc('text_nv2vgi') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\hr\shifts\route.ts - 35:38 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "缂哄皯鐝ID"銆傚缓璁娇鐢? tc('text_r7rs06') i18n/no-chinese-hardcode - 42:33 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "缂哄皯鐝ID"銆傚缓璁娇鐢? tc('text_r7rs06') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\hr\skills\route.ts - 3:19 warning 'like' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 41:38 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "缂哄皯鎶€鑳絀D"銆傚缓璁娇鐢? tc('text_p3ul0v') i18n/no-chinese-hardcode - 50:33 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "缂哄皯鎶€鑳絀D"銆傚缓璁娇鐢? tc('text_p3ul0v') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\init\business-seed\route.ts - 17:52 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 970:79 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - -D:\dcprint\erp-project\src\app\api\init\core-flow-seed\route.ts - 342:11 warning 'fieldWh' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 839:13 warning 'card' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - -D:\dcprint\erp-project\src\app\api\init\department\route.ts - 20:38 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "绠$悊閮?銆傚缓璁娇鐢? tc('text_iof7n') i18n/no-chinese-hardcode - 21:38 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "涓氬姟閮?銆傚缓璁娇鐢? tc('text_buoe9') i18n/no-chinese-hardcode - 22:38 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鐢熶骇閮?銆傚缓璁娇鐢? tc('text_hjr0g') i18n/no-chinese-hardcode - 23:38 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鎵撴牱涓績"銆傚缓璁娇鐢? tc('text_cu3n96') i18n/no-chinese-hardcode - 24:38 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "閲囪喘閮?銆傚缓璁娇鐢? tc('text_m1hlu') i18n/no-chinese-hardcode - 25:38 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍝佽川閮?銆傚缓璁娇鐢? tc('text_d3pkx') i18n/no-chinese-hardcode - 33:16 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "妯″垏"銆傚缓璁娇鐢? tc('text_ii2u') i18n/no-chinese-hardcode - 40:16 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍟嗘爣"銆傚缓璁娇鐢? tc('text_f2pt') i18n/no-chinese-hardcode - 47:16 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍏朵粬"銆傚缓璁娇鐢? tc('text_eae8') i18n/no-chinese-hardcode - 55:16 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "閲囪喘"銆傚缓璁娇鐢? tc('text_pkjq') i18n/no-chinese-hardcode - 62:16 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "浠撳簱"銆傚缓璁娇鐢? tc('text_dxcw') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\init\die-template-seed\route.ts - 1070:17 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍒€妯?缃戠増鏁伴噺涓嶆纭? 鏈熸湜20, 瀹為檯"銆傚缓璁娇鐢? tc(`text_pvtnn3`) i18n/no-chinese-hardcode - 1077:17 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "淇濆吇璁板綍鏁伴噺涓嶆纭? 鏈熸湜20, 瀹為檯"銆傚缓璁娇鐢? tc(`text_q66tm7`) i18n/no-chinese-hardcode - 1082:17 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "浣跨敤璁板綍鏁伴噺涓嶆纭? 鏈熸湜20, 瀹為檯"銆傚缓璁娇鐢? tc(`text_7bkoyi`) i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\init\inbound-tables\route.ts - 7:28 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? " --- 1. 鍏ュ簱璁㈠崟涓昏〃 -CREATE TABLE IF NOT EXISTS inv_inbound_order ( - id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY COMMENT '涓婚敭ID', - order_no VARCHAR(50) NOT NULL COMMENT '鍏ュ簱鍗曞彿', - order_type ENUM('purchase', 'return', 'transfer', 'other') DEFAULT 'purchase' COMMENT '鍏ュ簱绫诲瀷', - warehouse_id INT UNSIGNED NOT NULL COMMENT '浠撳簱ID', - supplier_id INT UNSIGNED DEFAULT NULL COMMENT '渚涘簲鍟咺D', - supplier_name VARCHAR(100) DEFAULT NULL COMMENT '渚涘簲鍟嗗悕绉?, - total_amount DECIMAL(12,2) DEFAULT 0 COMMENT '鎬婚噾棰?, - total_quantity DECIMAL(12,3) DEFAULT 0 COMMENT '鎬绘暟閲?, - status ENUM('draft', 'pending', 'approved', 'completed', 'cancelled') DEFAULT 'draft' COMMENT '鐘舵€?, - inbound_date DATE DEFAULT NULL COMMENT '鍏ュ簱鏃ユ湡', - remark TEXT COMMENT '澶囨敞', - create_by INT UNSIGNED DEFAULT NULL COMMENT '鍒涘缓浜篒D', - create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '鍒涘缓鏃堕棿', - update_by INT UNSIGNED DEFAULT NULL COMMENT '鏇存柊浜篒D', - update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '鏇存柊鏃堕棿', - deleted TINYINT(1) DEFAULT 0 COMMENT '鏄惁鍒犻櫎', - INDEX idx_order_no (order_no), - INDEX idx_warehouse (warehouse_id), - INDEX idx_status (status), - INDEX idx_create_time (create_time) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='鍏ュ簱璁㈠崟涓昏〃'; - --- 2. 鍏ュ簱璁㈠崟鏄庣粏琛?CREATE TABLE IF NOT EXISTS inv_inbound_item ( - id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY COMMENT '涓婚敭ID', - order_id INT UNSIGNED NOT NULL COMMENT '鍏ュ簱璁㈠崟ID', - material_id INT UNSIGNED NOT NULL COMMENT '鐗╂枡ID', - material_name VARCHAR(100) NOT NULL COMMENT '鐗╂枡鍚嶇О', - material_spec VARCHAR(200) DEFAULT NULL COMMENT '鐗╂枡瑙勬牸', - batch_no VARCHAR(50) DEFAULT NULL COMMENT '鎵规鍙?, - quantity DECIMAL(12,3) NOT NULL COMMENT '鏁伴噺', - unit VARCHAR(20) DEFAULT '浠? COMMENT '鍗曚綅', - unit_price DECIMAL(12,2) DEFAULT 0 COMMENT '鍗曚环', - total_price DECIMAL(12,2) DEFAULT 0 COMMENT '鎬讳环', - warehouse_location VARCHAR(50) DEFAULT NULL COMMENT '搴撲綅', - produce_date DATE DEFAULT NULL COMMENT '鐢熶骇鏃ユ湡', - expire_date DATE DEFAULT NULL COMMENT '鏈夋晥鏈熻嚦', - remark TEXT COMMENT '澶囨敞', - create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '鍒涘缓鏃堕棿', - INDEX idx_order_id (order_id), - INDEX idx_material (material_id), - INDEX idx_batch_no (batch_no) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='鍏ュ簱璁㈠崟鏄庣粏琛?; - --- 3. 搴撳瓨鎵规琛?CREATE TABLE IF NOT EXISTS inv_inventory_batch ( - id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY COMMENT '涓婚敭ID', - batch_no VARCHAR(50) NOT NULL COMMENT '鎵规鍙?, - material_id INT UNSIGNED NOT NULL COMMENT '鐗╂枡ID', - material_name VARCHAR(100) NOT NULL COMMENT '鐗╂枡鍚嶇О', - warehouse_id INT UNSIGNED NOT NULL COMMENT '浠撳簱ID', - warehouse_name VARCHAR(100) DEFAULT NULL COMMENT '浠撳簱鍚嶇О', - quantity DECIMAL(12,3) DEFAULT 0 COMMENT '鎬绘暟閲?, - available_qty DECIMAL(12,3) DEFAULT 0 COMMENT '鍙敤鏁伴噺', - locked_qty DECIMAL(12,3) DEFAULT 0 COMMENT '閿佸畾鏁伴噺', - unit VARCHAR(20) DEFAULT '浠? COMMENT '鍗曚綅', - unit_price DECIMAL(12,2) DEFAULT 0 COMMENT '鍗曚环', - produce_date DATE DEFAULT NULL COMMENT '鐢熶骇鏃ユ湡', - expire_date DATE DEFAULT NULL COMMENT '鏈夋晥鏈熻嚦', - inbound_date DATE DEFAULT NULL COMMENT '鍏ュ簱鏃ユ湡', - status ENUM('normal', 'frozen', 'expired') DEFAULT 'normal' COMMENT '鐘舵€?, - version INT UNSIGNED DEFAULT 1 COMMENT '涔愯閿佺増鏈彿', - create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '鍒涘缓鏃堕棿', - update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '鏇存柊鏃堕棿', - deleted TINYINT(1) DEFAULT 0 COMMENT '鏄惁鍒犻櫎', - UNIQUE KEY uk_batch_no (batch_no), - INDEX idx_material (material_id), - INDEX idx_warehouse (warehouse_id), - INDEX idx_status (status) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='搴撳瓨鎵规琛?; -"銆傚缓璁娇鐢? tc(`text_jt9xr5`) i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\init\ink-seed\route.ts - 362:45 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "娌瑰ⅷ鏁伴噺涓嶆纭? 鏈熸湜20, 瀹為檯"銆傚缓璁娇鐢? tc(`text_m82eed`) i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\init\inventory-tables\route.ts - 30:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "SQL鏂囦欢涓嶅瓨鍦?銆傚缓璁娇鐢? tc('text_obq6tc') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\init\menus\route.ts - 8:3 warning Unexpected console statement. Only these console methods are allowed: warn, error no-console - 9:3 warning Unexpected console statement. Only these console methods are allowed: warn, error no-console - 14:7 warning Unexpected console statement. Only these console methods are allowed: warn, error no-console - 87:7 warning Unexpected console statement. Only these console methods are allowed: warn, error no-console - 144:7 warning Unexpected console statement. Only these console methods are allowed: warn, error no-console - 168:7 warning Unexpected console statement. Only these console methods are allowed: warn, error no-console - 181:7 warning Unexpected console statement. Only these console methods are allowed: warn, error no-console - 204:7 warning Unexpected console statement. Only these console methods are allowed: warn, error no-console - 206:7 warning Unexpected console statement. Only these console methods are allowed: warn, error no-console - 1422:20 warning 'insertErr' is defined but never used. Allowed unused caught errors must match /^_/u @typescript-eslint/no-unused-vars - 1446:7 warning Unexpected console statement. Only these console methods are allowed: warn, error no-console - 1448:7 warning Unexpected console statement. Only these console methods are allowed: warn, error no-console - 1470:7 warning Unexpected console statement. Only these console methods are allowed: warn, error no-console - 1476:5 warning Unexpected console statement. Only these console methods are allowed: warn, error no-console - 1477:5 warning Unexpected console statement. Only these console methods are allowed: warn, error no-console - 1478:5 warning Unexpected console statement. Only these console methods are allowed: warn, error no-console - 1483:19 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "[MENU_INIT] ==================== 鑿滃崟鍒濆鍖栧け璐?===================="銆傚缓璁娇鐢? tc('text_1bntbv') i18n/no-chinese-hardcode - 1484:19 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "[MENU_INIT] 鑰楁椂:"銆傚缓璁娇鐢? tc('text_eaxzk3') i18n/no-chinese-hardcode - 1484:68 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "绉?銆傚缓璁娇鐢? tc('text_o2a') i18n/no-chinese-hardcode - 1485:19 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "[MENU_INIT] 閿欒:"銆傚缓璁娇鐢? tc('text_ee77xo') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\init\quality-final-seed\route.ts - 812:17 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏍囧噯鍗℃暟閲忎笉姝g‘: 鏈熸湜20, 瀹為檯"銆傚缓璁娇鐢? tc(`text_jlhvv2`) i18n/no-chinese-hardcode - 819:17 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "娴佺▼鍗℃暟閲忎笉姝g‘: 鏈熸湜20, 瀹為檯"銆傚缓璁娇鐢? tc(`text_rtzdfh`) i18n/no-chinese-hardcode - 826:17 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "缁堟璁板綍鏁伴噺涓嶆纭? 鏈熸湜20, 瀹為檯"銆傚缓璁娇鐢? tc(`text_98fjfb`) i18n/no-chinese-hardcode - 859:38 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "瀛樺湪"銆傚缓璁娇鐢? tc(`text_g0k0`) i18n/no-chinese-hardcode - 859:38 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏉$粓妫€璁板綍鏈叧鑱斿伐鍗?銆傚缓璁娇鐢? tc(`text_763gqr`) i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\init\sample-seed\route.ts - 344:17 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏍峰搧璁㈠崟鏁伴噺涓嶆纭? 鏈熸湜20, 瀹為檯"銆傚缓璁娇鐢? tc(`text_8hjlqv`) i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\init\seed-data\route.ts - 33:12 warning 'existingDieTemplates' is assigned a value but never used. Allowed unused elements of array destructuring must match /^_/u @typescript-eslint/no-unused-vars - 36:12 warning 'existingProcessRoutes' is assigned a value but never used. Allowed unused elements of array destructuring must match /^_/u @typescript-eslint/no-unused-vars - 39:12 warning 'existingBoms' is assigned a value but never used. Allowed unused elements of array destructuring must match /^_/u @typescript-eslint/no-unused-vars - 42:12 warning 'existingBomDetails' is assigned a value but never used. Allowed unused elements of array destructuring must match /^_/u @typescript-eslint/no-unused-vars - 54:12 warning 'existingUsers' is assigned a value but never used. Allowed unused elements of array destructuring must match /^_/u @typescript-eslint/no-unused-vars - -D:\dcprint\erp-project\src\app\api\init\warehouse-category-seed\route.ts - 973:17 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "浠撳簱鍒嗙被鏁伴噺涓嶆纭? 鏈熸湜20, 瀹為檯"銆傚缓璁娇鐢? tc(`text_rkdgtb`) i18n/no-chinese-hardcode - 980:17 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "浠撳簱鏁伴噺涓嶆纭? 鏈熸湜20, 瀹為檯"銆傚缓璁娇鐢? tc(`text_yix5u4`) i18n/no-chinese-hardcode - 987:17 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鐗╂枡鍒嗙被鏁伴噺涓嶆纭? 鏈熸湜20, 瀹為檯"銆傚缓璁娇鐢? tc(`text_f7vwlt`) i18n/no-chinese-hardcode - 994:17 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鐗╂枡鏁伴噺涓嶆纭? 鏈熸湜20, 瀹為檯"銆傚缓璁娇鐢? tc(`text_jokf7o`) i18n/no-chinese-hardcode - 1001:17 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "搴撳瓨鏁伴噺涓嶆纭? 鏈熸湜20, 瀹為檯"銆傚缓璁娇鐢? tc(`text_j2t1fz`) i18n/no-chinese-hardcode - 1007:41 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "瀛樺湪"銆傚缓璁娇鐢? tc(`text_g0k0`) i18n/no-chinese-hardcode - 1007:41 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "涓粨搴撴湭鍏宠仈鍒嗙被"銆傚缓璁娇鐢? tc(`text_rbktgq`) i18n/no-chinese-hardcode - 1013:42 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "瀛樺湪"銆傚缓璁娇鐢? tc(`text_g0k0`) i18n/no-chinese-hardcode - 1013:42 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "涓墿鏂欐湭鍏宠仈鍒嗙被"銆傚缓璁娇鐢? tc(`text_56qppi`) i18n/no-chinese-hardcode - 1019:42 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "瀛樺湪"銆傚缓璁娇鐢? tc(`text_g0k0`) i18n/no-chinese-hardcode - 1019:42 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏉″簱瀛樻湭鍏宠仈鐗╂枡鎴栦粨搴?銆傚缓璁娇鐢? tc(`text_mh5h0f`) i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\init\warehouse\route.ts - 10:21 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍘熸枡涓讳粨搴?銆傚缓璁娇鐢? tc('text_f1yq9r') i18n/no-chinese-hardcode - 12:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "骞夸笢"銆傚缓璁娇鐢? tc('text_gi2l') i18n/no-chinese-hardcode - 13:11 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "娣卞湷"銆傚缓璁娇鐢? tc('text_j6g2') i18n/no-chinese-hardcode - 14:14 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "A鏍?灞?銆傚缓璁娇鐢? tc('text_genq3') i18n/no-chinese-hardcode - 18:13 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "瀛樻斁涓昏鍘熸潗鏂?銆傚缓璁娇鐢? tc('text_oblm70') i18n/no-chinese-hardcode - 23:21 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鎴愬搧浠撳簱"銆傚缓璁娇鐢? tc('text_cq2cnl') i18n/no-chinese-hardcode - 25:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "骞夸笢"銆傚缓璁娇鐢? tc('text_gi2l') i18n/no-chinese-hardcode - 26:11 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "娣卞湷"銆傚缓璁娇鐢? tc('text_j6g2') i18n/no-chinese-hardcode - 27:14 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "B鏍?灞?銆傚缓璁娇鐢? tc('text_gfaqh') i18n/no-chinese-hardcode - 31:13 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "瀛樻斁鎴愬搧鏍囩"銆傚缓璁娇鐢? tc('text_qpo4eq') i18n/no-chinese-hardcode - 36:21 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍗婃垚鍝佷粨搴?銆傚缓璁娇鐢? tc('text_grspid') i18n/no-chinese-hardcode - 38:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "骞夸笢"銆傚缓璁娇鐢? tc('text_gi2l') i18n/no-chinese-hardcode - 39:11 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "娣卞湷"銆傚缓璁娇鐢? tc('text_j6g2') i18n/no-chinese-hardcode - 40:14 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "A鏍?灞?銆傚缓璁娇鐢? tc('text_genqy') i18n/no-chinese-hardcode - 44:13 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "瀛樻斁鍗板埛鍚庡崐鎴愬搧"銆傚缓璁娇鐢? tc('text_wfgn6i') i18n/no-chinese-hardcode - 49:21 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "搴熷搧鏆傚瓨鍖?銆傚缓璁娇鐢? tc('text_pxio4i') i18n/no-chinese-hardcode - 51:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "骞夸笢"銆傚缓璁娇鐢? tc('text_gi2l') i18n/no-chinese-hardcode - 52:11 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "娣卞湷"銆傚缓璁娇鐢? tc('text_j6g2') i18n/no-chinese-hardcode - 53:14 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "C鏍?灞?銆傚缓璁娇鐢? tc('text_gfxp5') i18n/no-chinese-hardcode - 57:13 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "涓嶈壇鍝佹殏瀛?銆傚缓璁娇鐢? tc('text_x61ciz') i18n/no-chinese-hardcode - 62:21 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "澶栫浠撳簱"銆傚缓璁娇鐢? tc('text_bqqqnd') i18n/no-chinese-hardcode - 64:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "骞夸笢"銆傚缓璁娇鐢? tc('text_gi2l') i18n/no-chinese-hardcode - 65:11 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "娣卞湷"銆傚缓璁娇鐢? tc('text_j6g2') i18n/no-chinese-hardcode - 66:14 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "宸ヤ笟鍖?鍙?銆傚缓璁娇鐢? tc('text_m5sd0p') i18n/no-chinese-hardcode - 70:13 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "绉熻祦浠撳簱锛屽瓨鏀惧鑺傛€т骇鍝?銆傚缓璁娇鐢? tc('text_pb54jy') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\ink-usages\route.ts - 1:26 warning 'transaction' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 96:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "缂哄皯蹇呰鍙傛暟"銆傚缓璁娇鐢? tc('text_ot3wtd') i18n/no-chinese-hardcode - 105:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "娌瑰ⅷ涓嶅瓨鍦?銆傚缓璁娇鐢? tc('text_afgu9a') i18n/no-chinese-hardcode - 141:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "缂哄皯璁板綍ID"銆傚缓璁娇鐢? tc('text_u5xpuf') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\inventory\materials\route.ts - 7:9 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "娌瑰ⅷ"銆傚缓璁娇鐢? tc('text_iz9r') i18n/no-chinese-hardcode - 7:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "澧ㄦ按"銆傚缓璁娇鐢? tc('text_fo98') i18n/no-chinese-hardcode - 8:49 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "钖勮啘"銆傚缓璁娇鐢? tc('text_nf6g') i18n/no-chinese-hardcode - 8:55 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍗锋潗"銆傚缓璁娇鐢? tc('text_eri1') i18n/no-chinese-hardcode - 8:61 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鐗囨潗"銆傚缓璁娇鐢? tc('text_k06h') i18n/no-chinese-hardcode - 9:13 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "婧跺墏"銆傚缓璁娇鐢? tc('text_ja6k') i18n/no-chinese-hardcode - 9:19 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "绋€閲婂墏"銆傚缓璁娇鐢? tc('text_ikjw8') i18n/no-chinese-hardcode - 9:26 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "娓呮礂鍓?銆傚缓璁娇鐢? tc('text_gn4y8') i18n/no-chinese-hardcode - 10:11 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "缃戠増"銆傚缓璁娇鐢? tc('text_ma6v') i18n/no-chinese-hardcode - 10:17 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍒€妯?銆傚缓璁娇鐢? tc('text_ej35') i18n/no-chinese-hardcode - 10:23 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "妯″垏鐗?銆傚缓璁娇鐢? tc('text_fy70i') i18n/no-chinese-hardcode - 10:30 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "涓濈綉"銆傚缓璁娇鐢? tc('text_dzh0') i18n/no-chinese-hardcode - 11:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鑳跺甫"銆傚缓璁娇鐢? tc('text_mga8') i18n/no-chinese-hardcode - 11:21 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "淇濇姢鑶?銆傚缓璁娇鐢? tc('text_c6lud') i18n/no-chinese-hardcode - 11:28 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "绂诲瀷绾?銆傚缓璁娇鐢? tc('text_i9gug') i18n/no-chinese-hardcode - 11:35 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍖呮潗"銆傚缓璁娇鐢? tc('text_eorv') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\inventory\route.ts - 266:30 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍑哄簱鏁伴噺蹇呴』澶т簬0"銆傚缓璁娇鐢? tc('text_81i27v') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\linkage\validate\route.ts - 17:7 warning 'PR_STATUS' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 26:7 warning 'PO_STATUS' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 159:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "缂哄皯蹇呰鍙傛暟"銆傚缓璁娇鐢? tc('text_ot3wtd') i18n/no-chinese-hardcode - 249:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "缂哄皯蹇呰鍙傛暟"銆傚缓璁娇鐢? tc('text_ot3wtd') i18n/no-chinese-hardcode - 413:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鏃犳晥鐨勬搷浣滅被鍨?銆傚缓璁娇鐢? tc('text_obzmdh') i18n/no-chinese-hardcode - 429:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "涓氬姟璁㈠崟ID涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_ewy145') i18n/no-chinese-hardcode - 443:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "涓氬姟璁㈠崟涓嶅瓨鍦?銆傚缓璁娇鐢? tc('text_pm18wz') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\material-labels\route.ts - 134:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "缂哄皯蹇呰鍙傛暟锛歮aterialCode, materialName, quantity"銆傚缓璁娇鐢? tc('text_w57suq') i18n/no-chinese-hardcode - 179:37 warning 'operatorName' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 182:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "缂哄皯蹇呰鍙傛暟"銆傚缓璁娇鐢? tc('text_ot3wtd') i18n/no-chinese-hardcode - 182:26 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "缂哄皯蹇呰鍙傛暟"銆傚缓璁娇鐢? tc('text_ot3wtd') i18n/no-chinese-hardcode - 195:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鐖舵爣绛句笉瀛樺湪"銆傚缓璁娇鐢? tc('text_b4uavk') i18n/no-chinese-hardcode - 195:26 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鐖舵爣绛句笉瀛樺湪"銆傚缓璁娇鐢? tc('text_b4uavk') i18n/no-chinese-hardcode - 199:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "璇ユ爣绛惧凡琚垎鍒?銆傚缓璁娇鐢? tc('text_d0zte2') i18n/no-chinese-hardcode - 199:26 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "璇ユ爣绛惧凡琚垎鍒?銆傚缓璁娇鐢? tc('text_d0zte2') i18n/no-chinese-hardcode - 205:26 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍒嗗垏鎬诲搴?"銆傚缓璁娇鐢? tc(`text_o8ab6r`) i18n/no-chinese-hardcode - 205:26 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? " 瓒呰繃鍘熷搴?"銆傚缓璁娇鐢? tc(`text_ccsud6`) i18n/no-chinese-hardcode - 245:9 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍒嗗垏鑷?"銆傚缓璁娇鐢? tc(`text_ap2d4n`) i18n/no-chinese-hardcode - 275:5 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍒嗗垏鎴愬姛"銆傚缓璁娇鐢? tc('text_aoxeds') i18n/no-chinese-hardcode - 284:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "缂哄皯鏍囩ID"銆傚缓璁娇鐢? tc('text_ps545l') i18n/no-chinese-hardcode - 306:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "娌℃湁闇€瑕佹洿鏂扮殑瀛楁"銆傚缓璁娇鐢? tc('text_nyjsyr') i18n/no-chinese-hardcode - 322:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "缂哄皯鏍囩ID"銆傚缓璁娇鐢? tc('text_ps545l') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\material-lifecycle\route.ts - 8:49 warning 'user' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars - 30:58 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "缂哄皯鐗╂枡ID"銆傚缓璁娇鐢? tc('text_r02v42') i18n/no-chinese-hardcode - 86:56 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏃犳晥鐨勬搷浣?銆傚缓璁娇鐢? tc('text_el3ulx') i18n/no-chinese-hardcode - 92:46 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏌ヨ澶辫触"銆傚缓璁娇鐢? tc('text_doodpt') i18n/no-chinese-hardcode - 107:58 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "缂哄皯蹇呰鍙傛暟"銆傚缓璁娇鐢? tc('text_ot3wtd') i18n/no-chinese-hardcode - 122:56 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "娑堣€楄褰曟垚鍔?銆傚缓璁娇鐢? tc('text_f2320z') i18n/no-chinese-hardcode - 126:58 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "缂哄皯蹇呰鍙傛暟"銆傚缓璁娇鐢? tc('text_ot3wtd') i18n/no-chinese-hardcode - 138:20 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "璋冩暣鍗曞垱寤烘垚鍔?銆傚缓璁娇鐢? tc('text_rmmjzm') i18n/no-chinese-hardcode - 144:58 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "缂哄皯璋冩暣鍗旾D"銆傚缓璁娇鐢? tc('text_e35xxk') i18n/no-chinese-hardcode - 147:56 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "瀹℃牳閫氳繃"銆傚缓璁娇鐢? tc('text_c00pno') i18n/no-chinese-hardcode - 150:56 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏃犳晥鐨勬搷浣?銆傚缓璁娇鐢? tc('text_el3ulx') i18n/no-chinese-hardcode - 156:46 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鎿嶄綔澶辫触"銆傚缓璁娇鐢? tc('text_d1rj43') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\material-requisitions\[id]\issue\route.ts - 13:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "璇锋彁渚涘嚭搴撶墿鏂欏垪琛?銆傚缓璁娇鐢? tc('text_56ryvw') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\material-requisitions\route.ts - 87:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "缂哄皯宸ュ崟ID"銆傚缓璁娇鐢? tc('text_oc90oy') i18n/no-chinese-hardcode - 91:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "缂哄皯浠撳簱ID"銆傚缓璁娇鐢? tc('text_mhgvde') i18n/no-chinese-hardcode - 100:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "宸ュ崟涓嶅瓨鍦?銆傚缓璁娇鐢? tc('text_mt7kp9') i18n/no-chinese-hardcode - 165:30 warning 'approverId' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 165:42 warning 'approverName' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 168:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "缂哄皯棰嗘枡鍗旾D"銆傚缓璁娇鐢? tc('text_805gze') i18n/no-chinese-hardcode - 177:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "棰嗘枡鍗曚笉瀛樺湪"銆傚缓璁娇鐢? tc('text_14empn') i18n/no-chinese-hardcode - 182:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍙湁寰呭嚭搴撶殑棰嗘枡鍗曟墠鑳藉嚭搴?銆傚缓璁娇鐢? tc('text_t80fhi') i18n/no-chinese-hardcode - 205:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍙湁寰呭嚭搴撶殑棰嗘枡鍗曟墠鑳藉彇娑?銆傚缓璁娇鐢? tc('text_t80ttr') i18n/no-chinese-hardcode - 215:24 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鏈煡鐨勬搷浣滅被鍨?銆傚缓璁娇鐢? tc('text_rom9iw') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\material-returns\route.ts - 71:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "缂哄皯宸ュ崟ID"銆傚缓璁娇鐢? tc('text_oc90oy') i18n/no-chinese-hardcode - 75:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "缂哄皯浠撳簱ID"銆傚缓璁娇鐢? tc('text_mhgvde') i18n/no-chinese-hardcode - 79:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "缂哄皯閫€鏂欐槑缁?銆傚缓璁娇鐢? tc('text_uqxox4') i18n/no-chinese-hardcode - 88:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "宸ュ崟涓嶅瓨鍦?銆傚缓璁娇鐢? tc('text_mt7kp9') i18n/no-chinese-hardcode - 150:23 warning 'operatorId' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 150:35 warning 'operatorName' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 153:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "缂哄皯閫€鏂欏崟ID"銆傚缓璁娇鐢? tc('text_tp7i9s') i18n/no-chinese-hardcode - 162:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "閫€鏂欏崟涓嶅瓨鍦?銆傚缓璁娇鐢? tc('text_xgs401') i18n/no-chinese-hardcode - 167:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍙湁寰呯‘璁ょ殑閫€鏂欏崟鎵嶈兘纭鍏ュ簱"銆傚缓璁娇鐢? tc('text_qcgsns') i18n/no-chinese-hardcode - 193:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍙湁寰呯‘璁ょ殑閫€鏂欏崟鎵嶈兘鍙栨秷"銆傚缓璁娇鐢? tc('text_95b6y') i18n/no-chinese-hardcode - 203:24 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鏈煡鐨勬搷浣滅被鍨?銆傚缓璁娇鐢? tc('text_rom9iw') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\menu\route.ts - 59:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鑾峰彇鑿滃崟鍒楄〃澶辫触"銆傚缓璁娇鐢? tc('text_72x071') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\menu\sort-order\route.ts - 15:11 warning 'MenuSortItem' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 58:30 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鎺掑簭椤规牸寮忎笉姝g‘锛岄渶瑕乮d鍜宻ort_order瀛楁"銆傚缓璁娇鐢? tc('text_n46z9k') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\migrations\add-workshop-column\route.ts - 18:16 warning 'createTableIfNotExists' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - -D:\dcprint\erp-project\src\app\api\orders\bom\[id]\route.ts - 14:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鏃犳晥鐨凚OM ID"銆傚缓璁娇鐢? tc('text_paiw5l') i18n/no-chinese-hardcode - 38:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "BOM涓嶅瓨鍦?銆傚缓璁娇鐢? tc('text_wtnjlp') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\orders\bom\expand\route.ts - 38:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "productId 蹇呴』鏄鏁存暟"銆傚缓璁娇鐢? tc('text_4tova4') i18n/no-chinese-hardcode - 42:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "quantity 蹇呴』鏄鏁?銆傚缓璁娇鐢? tc('text_49upzj') i18n/no-chinese-hardcode - 46:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "maxDepth 蹇呴』鏄?-20涔嬮棿鐨勬暣鏁?銆傚缓璁娇鐢? tc('text_kqpn5n') i18n/no-chinese-hardcode - 91:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "缂哄皯蹇呭~鍙傛暟: productId, quantity"銆傚缓璁娇鐢? tc('text_nu79g8') i18n/no-chinese-hardcode - 98:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "productId 蹇呴』鏄鏁存暟"銆傚缓璁娇鐢? tc('text_4tova4') i18n/no-chinese-hardcode - 102:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "quantity 蹇呴』鏄鏁?銆傚缓璁娇鐢? tc('text_49upzj') i18n/no-chinese-hardcode - 157:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "products 蹇呴』鏄潪绌烘暟缁?銆傚缓璁娇鐢? tc('text_u883gt') i18n/no-chinese-hardcode - 163:30 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "products 涓殑 productId 蹇呴』鏄鏁存暟"銆傚缓璁娇鐢? tc('text_ujxa3b') i18n/no-chinese-hardcode - 166:30 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "products 涓殑 quantity 蹇呴』鏄鏁?銆傚缓璁娇鐢? tc('text_atc4g2') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\orders\bom\materials\route.ts - 95:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鐗╂枡缂栫爜宸插瓨鍦?銆傚缓璁娇鐢? tc('text_dtcja1') i18n/no-chinese-hardcode - 135:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鐗╂枡ID涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_1njsw5') i18n/no-chinese-hardcode - 141:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鐗╂枡涓嶅瓨鍦?銆傚缓璁娇鐢? tc('text_yaspql') i18n/no-chinese-hardcode - 177:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "娌℃湁瑕佹洿鏂扮殑瀛楁"銆傚缓璁娇鐢? tc('text_magxwt') i18n/no-chinese-hardcode - 201:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鐗╂枡ID涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_1njsw5') i18n/no-chinese-hardcode - 208:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "璇ョ墿鏂欏凡琚獴OM寮曠敤锛屼笉鑳藉垹闄?銆傚缓璁娇鐢? tc('text_fc3zol') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\orders\bom\route.ts - 116:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "BOM鏄庣粏涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_x589pk') i18n/no-chinese-hardcode - 120:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "浜у搧缂栫爜鍜屼骇鍝佸悕绉颁笉鑳戒负绌?銆傚缓璁娇鐢? tc('text_a2hfu4') i18n/no-chinese-hardcode - 125:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "BOM鏄庣粏涓墿鏂欑紪鐮佸拰鍚嶇О涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_7w5ofl') i18n/no-chinese-hardcode - 237:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "BOM ID涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_h04v1n') i18n/no-chinese-hardcode - 401:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "BOM ID涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_h04v1n') i18n/no-chinese-hardcode - 409:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "BOM涓嶅瓨鍦?銆傚缓璁娇鐢? tc('text_wtnjlp') i18n/no-chinese-hardcode - 415:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "宸插彂甯冪殑BOM涓嶈兘鍒犻櫎锛岃鍋滅敤瀹?銆傚缓璁娇鐢? tc('text_z60jt4') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\orders\route.ts - 17:11 warning 'Order' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 175:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "瀹㈡埛淇℃伅涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_2z2uab') i18n/no-chinese-hardcode - 179:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "璁㈠崟椤逛笉鑳戒负绌?銆傚缓璁娇鐢? tc('text_fv2n06') i18n/no-chinese-hardcode - 184:30 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "璁㈠崟椤圭己灏戝繀濉瓧娈?鐗╂枡鍚嶇О銆佹暟閲忋€佸崟浠?"銆傚缓璁娇鐢? tc('text_i85tjg') i18n/no-chinese-hardcode - 187:30 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "璁㈠崟鏁伴噺蹇呴』澶т簬0"銆傚缓璁娇鐢? tc('text_d00m8v') i18n/no-chinese-hardcode - 190:30 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍗曚环涓嶈兘涓鸿礋鏁?銆傚缓璁娇鐢? tc('text_ynbdjt') i18n/no-chinese-hardcode - 236:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "璁㈠崟ID涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_a78c6a') i18n/no-chinese-hardcode - 248:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "宸插畬鎴愮殑璁㈠崟涓嶈兘淇敼"銆傚缓璁娇鐢? tc('text_865xlo') i18n/no-chinese-hardcode - 289:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "璁㈠崟ID涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_a78c6a') i18n/no-chinese-hardcode - 302:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "宸插畬鎴愮殑璁㈠崟涓嶈兘鍒犻櫎"銆傚缓璁娇鐢? tc('text_866kr9') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\orders\sales\route.ts - 13:64 warning 'user' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars - 79:7 warning 'customer_name' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 90:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "瀹㈡埛鍜岃鍗曟槑缁嗕笉鑳戒负绌?銆傚缓璁娇鐢? tc('text_9ngl1u') i18n/no-chinese-hardcode - 158:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "璁㈠崟ID涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_a78c6a') i18n/no-chinese-hardcode - 162:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "甯佺鍒涘缓鍚庝笉鍙慨鏀?銆傚缓璁娇鐢? tc('text_yqj4r4') i18n/no-chinese-hardcode - 165:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "姹囩巼鍒涘缓鍚庝笉鍙慨鏀?銆傚缓璁娇鐢? tc('text_6ha0ik') i18n/no-chinese-hardcode - 173:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "璁㈠崟涓嶅瓨鍦?銆傚缓璁娇鐢? tc('text_bg1liu') i18n/no-chinese-hardcode - 179:32 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍙湁鑽夌鐘舵€佺殑璁㈠崟鍙互鎻愪氦"銆傚缓璁娇鐢? tc('text_vdj6n') i18n/no-chinese-hardcode - 200:32 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍙湁宸叉彁浜ょ殑璁㈠崟鍙互瀹℃牳"銆傚缓璁娇鐢? tc('text_x8qjn') i18n/no-chinese-hardcode - 242:32 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍙湁宸叉彁浜ょ殑璁㈠崟鍙互椹冲洖"銆傚缓璁娇鐢? tc('text_wy5an') i18n/no-chinese-hardcode - 250:30 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鏈煡鎿嶄綔"銆傚缓璁娇鐢? tc('text_dih7qi') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\organization\crud\route.ts - 12:32 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 32:53 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "缂哄皯蹇呭~瀛楁"銆傚缓璁娇鐢? tc('text_olyemz') i18n/no-chinese-hardcode - 33:45 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鏃犳晥鐨勮妭鐐圭被鍨?銆傚缓璁娇鐢? tc('text_kg7pvh') i18n/no-chinese-hardcode - 36:36 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 49:42 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "缂哄皯ID鎴栫被鍨?銆傚缓璁娇鐢? tc('text_nbkrbo') i18n/no-chinese-hardcode - 50:45 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鏃犳晥鐨勮妭鐐圭被鍨?銆傚缓璁娇鐢? tc('text_kg7pvh') i18n/no-chinese-hardcode - 53:33 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 71:63 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "娌℃湁闇€瑕佹洿鏂扮殑瀛楁"銆傚缓璁娇鐢? tc('text_nyjsyr') i18n/no-chinese-hardcode - 81:42 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "缂哄皯ID鎴栫被鍨?銆傚缓璁娇鐢? tc('text_nbkrbo') i18n/no-chinese-hardcode - 82:45 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鏃犳晥鐨勮妭鐐圭被鍨?銆傚缓璁娇鐢? tc('text_kg7pvh') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\organization\department\route.ts - 74:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "閮ㄩ棬缂栫爜宸插瓨鍦?銆傚缓璁娇鐢? tc('text_wylho7') i18n/no-chinese-hardcode - 123:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "閮ㄩ棬缂栫爜宸插瓨鍦?銆傚缓璁娇鐢? tc('text_wylho7') i18n/no-chinese-hardcode - 174:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "璇ラ儴闂ㄤ笅鏈夊瓙閮ㄩ棬锛屾棤娉曞垹闄?銆傚缓璁娇鐢? tc('text_324nlk') i18n/no-chinese-hardcode - 183:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "璇ラ儴闂ㄤ笅鏈夊憳宸ワ紝鏃犳硶鍒犻櫎"銆傚缓璁娇鐢? tc('text_lg1s3v') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\organization\employee\route.ts - 148:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍛樺伐缂栧彿宸插瓨鍦?銆傚缓璁娇鐢? tc('text_7cu07o') i18n/no-chinese-hardcode - 224:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍛樺伐缂栧彿宸插瓨鍦?銆傚缓璁娇鐢? tc('text_7cu07o') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\organization\menu\route.ts - 62:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鑿滃崟缂栫爜宸插瓨鍦?銆傚缓璁娇鐢? tc('text_m43qua') i18n/no-chinese-hardcode - 122:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鑿滃崟缂栫爜宸插瓨鍦?銆傚缓璁娇鐢? tc('text_m43qua') i18n/no-chinese-hardcode - 184:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "璇峰厛鍒犻櫎瀛愯彍鍗?銆傚缓璁娇鐢? tc('text_wiz5sc') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\organization\role\route.ts - 143:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "瑙掕壊缂栫爜宸插瓨鍦?銆傚缓璁娇鐢? tc('text_tebmqf') i18n/no-chinese-hardcode - 253:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "璇ヨ鑹插凡鍒嗛厤缁欑敤鎴凤紝鏃犳硶鍒犻櫎"銆傚缓璁娇鐢? tc('text_ylwq1b') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\organization\warehouse-category\route.ts - 66:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍒嗙被缂栫爜宸插瓨鍦?銆傚缓璁娇鐢? tc('text_r6j1pa') i18n/no-chinese-hardcode - 108:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍒嗙被缂栫爜宸插瓨鍦?銆傚缓璁娇鐢? tc('text_r6j1pa') i18n/no-chinese-hardcode - 151:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "璇ュ垎绫讳笅鏈変粨搴擄紝鏃犳硶鍒犻櫎"銆傚缓璁娇鐢? tc('text_9w3g0j') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\outsource\issue\route.ts - 64:51 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "濮斿璁㈠崟涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_6vfrqz') i18n/no-chinese-hardcode - 65:45 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "浠撳簱涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_9xoqmo') i18n/no-chinese-hardcode - 116:35 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍙戞枡鍗旾D涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_z4xnc') i18n/no-chinese-hardcode - 221:35 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "缂哄皯id"銆傚缓璁娇鐢? tc('text_gf6a2a') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\outsource\order\route.ts - 62:44 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "渚涘簲鍟嗕笉鑳戒负绌?銆傚缓璁娇鐢? tc('text_lvjhr1') i18n/no-chinese-hardcode - 107:35 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "璁㈠崟ID涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_a78c6a') i18n/no-chinese-hardcode - 161:35 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "缂哄皯id"銆傚缓璁娇鐢? tc('text_gf6a2a') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\outsource\receive\route.ts - 59:51 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "濮斿璁㈠崟涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_6vfrqz') i18n/no-chinese-hardcode - 60:45 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍏ュ簱浠撳簱涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_bv1q6') i18n/no-chinese-hardcode - 97:35 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鏀惰揣鍗旾D涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_1hogwh') i18n/no-chinese-hardcode - 224:35 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "缂哄皯id"銆傚缓璁娇鐢? tc('text_gf6a2a') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\outsource\settlement\route.ts - 59:51 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "濮斿璁㈠崟涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_6vfrqz') i18n/no-chinese-hardcode - 60:44 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "渚涘簲鍟嗕笉鑳戒负绌?銆傚缓璁娇鐢? tc('text_lvjhr1') i18n/no-chinese-hardcode - 106:35 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "缁撶畻鍗旾D涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_enck2k') i18n/no-chinese-hardcode - 188:35 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "缂哄皯id"銆傚缓璁娇鐢? tc('text_gf6a2a') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\plm\eco\route.ts - 55:41 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍙樻洿绫诲瀷涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_dfzst8') i18n/no-chinese-hardcode - 93:35 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "ID涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_p1r591') i18n/no-chinese-hardcode - 130:35 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "ID涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_p1r591') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\plm\lifecycle\route.ts - 54:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "浜у搧ID鍜岀敓鍛藉懆鏈熼樁娈典笉鑳戒负绌?銆傚缓璁娇鐢? tc('text_ub31r9') i18n/no-chinese-hardcode - 84:35 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "ID涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_p1r591') i18n/no-chinese-hardcode - 121:35 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "ID涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_p1r591') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\prepress\die-maintenance\route.ts - 92:38 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍒€妯?缃戠増涓嶅瓨鍦?銆傚缓璁娇鐢? tc('text_duzdqg') i18n/no-chinese-hardcode - 192:40 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍒€妯?缃戠増涓嶅瓨鍦?銆傚缓璁娇鐢? tc('text_duzdqg') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\prepress\die-template\route.ts - 129:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍒€妯℃澘/缃戠増缂栧彿宸插瓨鍦?銆傚缓璁娇鐢? tc('text_uo7r21') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\prepress\die-usage\route.ts - 104:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "浣跨敤娆℃暟蹇呴』澶т簬0"銆傚缓璁娇鐢? tc('text_yx3cw5') i18n/no-chinese-hardcode - 113:38 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍒€妯?缃戠増涓嶅瓨鍦?銆傚缓璁娇鐢? tc('text_duzdqg') i18n/no-chinese-hardcode - 115:30 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "宸叉姤搴熺殑鍒€妯?缃戠増涓嶈兘缁х画浣跨敤"銆傚缓璁娇鐢? tc('text_7rbtsc') i18n/no-chinese-hardcode - 117:30 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "闇€閲嶅仛鐨勫垁妯?缃戠増璇峰厛淇濆吇鍚庡啀浣跨敤"銆傚缓璁娇鐢? tc('text_deakf9') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\production\material-issue\route.ts - 68:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "浠撳簱ID涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_ezwjlh') i18n/no-chinese-hardcode - 71:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍙戞枡鏄庣粏涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_wwaj68') i18n/no-chinese-hardcode - 223:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍙戞枡鍗旾D涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_z4xnc') i18n/no-chinese-hardcode - 317:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍙戞枡鍗曚笉瀛樺湪"銆傚缓璁娇鐢? tc('text_g6xfcg') i18n/no-chinese-hardcode - 320:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "宸插畬鎴愮殑鍙戞枡鍗曚笉鑳藉垹闄?銆傚缓璁娇鐢? tc('text_ke4vn7') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\production\material-return\route.ts - 101:30 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "閫€鏂欏崟涓嶅瓨鍦?銆傚缓璁娇鐢? tc('text_xgs401') i18n/no-chinese-hardcode - 105:30 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "閫€鏂欏崟鐘舵€佷笉鍏佽纭"銆傚缓璁娇鐢? tc('text_cbuk31') i18n/no-chinese-hardcode - 113:30 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "閫€鏂欏崟鏃犳槑缁?銆傚缓璁娇鐢? tc('text_xkbrwc') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\production\mrp\route.ts - 18:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "璇锋彁渚涘伐鍗旾D鍒楄〃"銆傚缓璁娇鐢? tc('text_tmhjoi') i18n/no-chinese-hardcode - 44:42 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "璇锋彁渚涗骇鍝両D"銆傚缓璁娇鐢? tc('text_79p0fd') i18n/no-chinese-hardcode - 59:43 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "璇锋彁渚涚墿鏂橧D"銆傚缓璁娇鐢? tc('text_2ov53n') i18n/no-chinese-hardcode - 80:57 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "璇锋彁渚涘伐鍗旾D鍒楄〃"銆傚缓璁娇鐢? tc('text_tmhjoi') i18n/no-chinese-hardcode - 87:24 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鏈煡鎿嶄綔"銆傚缓璁娇鐢? tc('text_dih7qi') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\production\schedule\auto\route.ts - 12:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "璇锋彁渚涘伐鍗旾D鍒楄〃"銆傚缓璁娇鐢? tc('text_tmhjoi') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\production\schedule\route.ts - 108:30 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鎺掍骇缁撴灉涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_hiqury') i18n/no-chinese-hardcode - 146:45 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "浜у搧鍚嶇О涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_5zh4st') i18n/no-chinese-hardcode - 220:35 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "ID涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_p1r591') i18n/no-chinese-hardcode - 258:35 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "ID涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_p1r591') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\production\trace\route.ts - 13:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "璇锋彁渚涜嚦灏戜竴涓煡璇㈠弬鏁? sn, batchNo, workorderNo"銆傚缓璁娇鐢? tc('text_b72nqx') i18n/no-chinese-hardcode - 67:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "缂哄皯蹇呭~瀛楁: sn"銆傚缓璁娇鐢? tc('text_gmjyzo') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\production\work-order\create-multi-color\route.ts - 13:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "缂哄皯蹇呰鍙傛暟: standardCardId, salesOrderId, planQty"銆傚缓璁娇鐢? tc('text_5sg6l5') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\products\categories\route.ts - 68:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍒嗙被缂栫爜鍜屽悕绉颁笉鑳戒负绌?銆傚缓璁娇鐢? tc('text_6wqsqn') i18n/no-chinese-hardcode - 78:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍒嗙被缂栫爜宸插瓨鍦?銆傚缓璁娇鐢? tc('text_r6j1pa') i18n/no-chinese-hardcode - 102:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍒嗙被ID涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_a6ints') i18n/no-chinese-hardcode - 129:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "娌℃湁瑕佹洿鏂扮殑瀛楁"銆傚缓璁娇鐢? tc('text_magxwt') i18n/no-chinese-hardcode - 150:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍒嗙被ID涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_a6ints') i18n/no-chinese-hardcode - 160:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "璇ュ垎绫讳笅鏈夊瓙鍒嗙被锛屼笉鑳藉垹闄?銆傚缓璁娇鐢? tc('text_rqn1zh') i18n/no-chinese-hardcode - 170:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "璇ュ垎绫讳笅鏈夊叧鑱斾骇鍝侊紝涓嶈兘鍒犻櫎"銆傚缓璁娇鐢? tc('text_rivle5') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\products\route.ts - 122:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "浜у搧缂栫爜宸插瓨鍦?銆傚缓璁娇鐢? tc('text_zdn1tp') i18n/no-chinese-hardcode - 165:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "浜у搧ID涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_vpq7tx') i18n/no-chinese-hardcode - 205:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "娌℃湁瑕佹洿鏂扮殑瀛楁"銆傚缓璁娇鐢? tc('text_magxwt') i18n/no-chinese-hardcode - 226:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "浜у搧ID涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_vpq7tx') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\purchase\convert-po\route.ts - 20:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "璇锋彁渚況equestId鎴杛equestIds"銆傚缓璁娇鐢? tc('text_ple4tz') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\purchase\orders\route.ts - 113:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "閲囪喘鏄庣粏涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_neo4aa') i18n/no-chinese-hardcode - 170:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "閲囪喘鍗旾D涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_b0zj96') i18n/no-chinese-hardcode - 174:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "甯佺鍒涘缓鍚庝笉鍙慨鏀?銆傚缓璁娇鐢? tc('text_yqj4r4') i18n/no-chinese-hardcode - 177:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "姹囩巼鍒涘缓鍚庝笉鍙慨鏀?銆傚缓璁娇鐢? tc('text_6ha0ik') i18n/no-chinese-hardcode - 200:11 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鏀惰揣鍔熻兘宸茶縼绉昏嚦鍏ュ簱妯″潡锛岃浣跨敤 POST /api/warehouse/inbound/from-po"銆傚缓璁娇鐢? tc('text_wvxmqe') i18n/no-chinese-hardcode - 206:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鏈煡鎿嶄綔"銆傚缓璁娇鐢? tc('text_dih7qi') i18n/no-chinese-hardcode - 229:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "閲囪喘鍗旾D涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_b0zj96') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\purchase\reconciliation\route.ts - 220:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍙傛暟涓嶅畬鏁达細闇€瑕?id 鍜?action"銆傚缓璁娇鐢? tc('text_nookzh') i18n/no-chinese-hardcode - 231:32 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鏍搁攢鎿嶄綔闇€瑕?payable_id 鍜?amount"銆傚缓璁娇鐢? tc('text_59dv84') i18n/no-chinese-hardcode - 252:32 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "浠呰崏绋跨姸鎬佸彲淇敼鎶樻墸"銆傚缓璁娇鐢? tc('text_iipmjj') i18n/no-chinese-hardcode - 267:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "涓嶆敮鎸佺殑鎿嶄綔绫诲瀷"銆傚缓璁娇鐢? tc('text_5nj4g4') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\purchase\request\route.ts - 62:10 warning 'generateRequestNoSync' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 167:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "閲囪喘鐢宠蹇呴』鍖呭惈鑷冲皯涓€鏉℃槑缁?銆傚缓璁娇鐢? tc('text_rbmae5') i18n/no-chinese-hardcode - 264:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "宸叉壒鍑嗙殑閲囪喘鐢宠涓嶈兘淇敼"銆傚缓璁娇鐢? tc('text_a8yx3e') i18n/no-chinese-hardcode - 361:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "宸叉壒鍑嗙殑閲囪喘鐢宠涓嶈兘鍒犻櫎"銆傚缓璁娇鐢? tc('text_a8zk8z') i18n/no-chinese-hardcode - 369:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "璇ヨ璐崟宸茶閲囪喘璁㈠崟寮曠敤锛屾棤娉曞垹闄?銆傚缓璁娇鐢? tc('text_ugzz7q') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\purchase\return\route.ts - 100:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "閫€璐ф槑缁嗕笉鑳戒负绌?銆傚缓璁娇鐢? tc('text_e558y9') i18n/no-chinese-hardcode - 153:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍙傛暟涓嶅畬鏁达細闇€瑕?id 鍜?action"銆傚缓璁娇鐢? tc('text_nookzh') i18n/no-chinese-hardcode - 172:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "涓嶆敮鎸佺殑鎿嶄綔绫诲瀷"銆傚缓璁娇鐢? tc('text_5nj4g4') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\purchase\suppliers\route.ts - 104:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "渚涘簲鍟嗙紪鐮佸凡瀛樺湪"銆傚缓璁娇鐢? tc('text_gfyfnu') i18n/no-chinese-hardcode - 140:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "渚涘簲鍟咺D涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_cyfkl4') i18n/no-chinese-hardcode - 178:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "娌℃湁瑕佹洿鏂扮殑瀛楁"銆傚缓璁娇鐢? tc('text_magxwt') i18n/no-chinese-hardcode - 198:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "渚涘簲鍟咺D涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_cyfkl4') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\qrcode\payload\route.ts - 92:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "璇锋彁渚涗簩缁寸爜鏁版嵁"銆傚缓璁娇鐢? tc('text_6uemzv') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\qrcode\print\route.ts - 12:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "缂哄皯浜岀淮鐮佺紪鐮?銆傚缓璁娇鐢? tc('text_9rgoxf') i18n/no-chinese-hardcode - 22:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "浜岀淮鐮佷笉瀛樺湪"銆傚缓璁娇鐢? tc('text_h3puxw') i18n/no-chinese-hardcode - 71:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "缂哄皯浜岀淮鐮佺紪鐮?銆傚缓璁娇鐢? tc('text_9rgoxf') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\qrcode\route.ts - 78:40 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "浜岀淮鐮佺被鍨嬩笉鑳戒负绌?銆傚缓璁娇鐢? tc('text_k43oih') i18n/no-chinese-hardcode - 139:66 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "ID涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_p1r591') i18n/no-chinese-hardcode - 143:43 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鎵规鍙蜂笉鑳戒负绌?銆傚缓璁娇鐢? tc('text_k9c9xd') i18n/no-chinese-hardcode - 185:41 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "浜岀淮鐮佽褰曚笉瀛樺湪"銆傚缓璁娇鐢? tc('text_ej0d5t') i18n/no-chinese-hardcode - 243:51 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "娌℃湁闇€瑕佹洿鏂扮殑瀛楁"銆傚缓璁娇鐢? tc('text_nyjsyr') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\qrcode\trace\route.ts - 14:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "璇锋彁渚涗簩缁寸爜缂栫爜銆佸崟鍙枫€佹壒娆″彿鎴栫墿鏂欑紪鐮?銆傚缓璁娇鐢? tc('text_8itaoy') i18n/no-chinese-hardcode - 43:37 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鏈壘鍒板搴旂殑浜岀淮鐮佽褰?銆傚缓璁娇鐢? tc('text_yshbe7') i18n/no-chinese-hardcode - 205:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍘熸枡"銆傚缓璁娇鐢? tc('text_es4a') i18n/no-chinese-hardcode - 206:14 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鎴愬搧"銆傚缓璁娇鐢? tc('text_h581') i18n/no-chinese-hardcode - 207:16 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "宸ュ崟"銆傚缓璁娇鐢? tc('text_gff4') i18n/no-chinese-hardcode - 208:10 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "娌瑰ⅷ"銆傚缓璁娇鐢? tc('text_iz9r') i18n/no-chinese-hardcode - 209:19 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "缃戠増"銆傚缓璁娇鐢? tc('text_ma6v') i18n/no-chinese-hardcode - 210:10 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍒€鍏?銆傚缓璁娇鐢? tc('text_ee7r') i18n/no-chinese-hardcode - 211:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍑鸿揣"銆傚缓璁娇鐢? tc('text_epv1') i18n/no-chinese-hardcode - 212:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "寮€缃?銆傚缓璁娇鐢? tc('text_guvk') i18n/no-chinese-hardcode - 213:16 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "璋冭壊"銆傚缓璁娇鐢? tc('text_oj4f') i18n/no-chinese-hardcode - 220:14 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍏ュ簱鎵弿"銆傚缓璁娇鐢? tc('text_anwwsy') i18n/no-chinese-hardcode - 221:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍑哄簱鎵弿"銆傚缓璁娇鐢? tc('text_aqk1ul') i18n/no-chinese-hardcode - 222:12 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "棰嗘枡鎵弿"銆傚缓璁娇鐢? tc('text_jnwu6v') i18n/no-chinese-hardcode - 223:13 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鎶ュ伐鎵弿"銆傚缓璁娇鐢? tc('text_cu51us') i18n/no-chinese-hardcode - 224:12 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "妫€楠屾壂鎻?銆傚缓璁娇鐢? tc('text_duqvtc') i18n/no-chinese-hardcode - 225:16 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鐩樼偣鎵弿"銆傚缓璁娇鐢? tc('text_fgm545') i18n/no-chinese-hardcode - 226:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "寮€缃愭壂鎻?銆傚缓璁娇鐢? tc('text_cihiqc') i18n/no-chinese-hardcode - 227:14 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "娌瑰ⅷ浣跨敤"銆傚缓璁娇鐢? tc('text_e31p7c') i18n/no-chinese-hardcode - 228:16 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "缃戠増棰嗙敤"銆傚缓璁娇鐢? tc('text_gjkm9l') i18n/no-chinese-hardcode - 229:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "缃戠増娓呮礂"銆傚缓璁娇鐢? tc('text_gjdceh') i18n/no-chinese-hardcode - 230:14 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍒€鍏蜂娇鐢?銆傚缓璁娇鐢? tc('text_aonmtc') i18n/no-chinese-hardcode - 231:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍒€鍏峰垉纾?銆傚缓璁娇鐢? tc('text_aoo2yk') i18n/no-chinese-hardcode - 232:12 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "杩芥函鏌ヨ"銆傚缓璁娇鐢? tc('text_imiq27') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\quality\complaint\route.ts - 67:46 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "瀹㈡埛鍚嶇О涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_uwwkbc') i18n/no-chinese-hardcode - 68:45 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "浜у搧鍚嶇О涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_5zh4st') i18n/no-chinese-hardcode - 145:35 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "ID涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_p1r591') i18n/no-chinese-hardcode - 279:51 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "娌℃湁闇€瑕佹洿鏂扮殑瀛楁"銆傚缓璁娇鐢? tc('text_nyjsyr') i18n/no-chinese-hardcode - 292:35 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "ID涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_p1r591') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\quality\lab-test\auto-request\route.ts - 12:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "缂哄皯蹇呭~鍙傛暟: materialId, batchNo, lotSize"銆傚缓璁娇鐢? tc('text_wezmuu') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\quality\lab-test\route.ts - 66:45 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "浜у搧鍚嶇О涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_5zh4st') i18n/no-chinese-hardcode - 125:35 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "ID涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_p1r591') i18n/no-chinese-hardcode - 191:51 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "娌℃湁闇€瑕佹洿鏂扮殑瀛楁"銆傚缓璁娇鐢? tc('text_nyjsyr') i18n/no-chinese-hardcode - 204:35 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "ID涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_p1r591') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\quality\process\route.ts - 132:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "缂哄皯蹇呭~鍙傛暟: cardId, inspectResult"銆傚缓璁娇鐢? tc('text_7xfoh') i18n/no-chinese-hardcode - 213:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "缂哄皯蹇呭~鍙傛暟: id, inspectResult"銆傚缓璁娇鐢? tc('text_qi8spd') i18n/no-chinese-hardcode - 229:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "妫€楠岃褰曚笉瀛樺湪"銆傚缓璁娇鐢? tc('text_eq34ok') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\quality\sgs\[id]\route.ts - 11:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "ID涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_p1r591') i18n/no-chinese-hardcode - 19:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "SGS璁よ瘉璁板綍涓嶅瓨鍦?銆傚缓璁娇鐢? tc('text_hb998c') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\quality\sgs\route.ts - 75:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "SGS璇佷功缂栧彿涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_x85v0r') i18n/no-chinese-hardcode - 83:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "璇佷功缂栧彿宸插瓨鍦?銆傚缓璁娇鐢? tc('text_cotz1g') i18n/no-chinese-hardcode - 142:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "ID涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_p1r591') i18n/no-chinese-hardcode - 212:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "ID涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_p1r591') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\quality\spc\route.ts - 25:43 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "璇锋彁渚涚墿鏂橧D"銆傚缓璁娇鐢? tc('text_2ov53n') i18n/no-chinese-hardcode - 103:24 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鏈煡鎿嶄綔锛屾敮鎸? xbar-r, pareto, p-chart"銆傚缓璁娇鐢? tc('text_5a938g') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\quality\supplier-audit\route.ts - 67:46 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "渚涘簲鍟嗗悕绉颁笉鑳戒负绌?銆傚缓璁娇鐢? tc('text_crtig0') i18n/no-chinese-hardcode - 128:35 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "ID涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_p1r591') i18n/no-chinese-hardcode - 198:51 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "娌℃湁闇€瑕佹洿鏂扮殑瀛楁"銆傚缓璁娇鐢? tc('text_nyjsyr') i18n/no-chinese-hardcode - 211:35 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "ID涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_p1r591') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\quality\unqualified\route.ts - 108:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "妫€楠屽崟ID涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_8uzawk') i18n/no-chinese-hardcode - 111:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "涓嶅悎鏍兼暟閲忓繀椤诲ぇ浜?"銆傚缓璁娇鐢? tc('text_282umr') i18n/no-chinese-hardcode - 114:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "澶勭悊鏂瑰紡鏃犳晥锛堝簲涓?rework/scrap/concession/return锛?銆傚缓璁娇鐢? tc('text_h0zcrf') i18n/no-chinese-hardcode - 154:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "缂哄皯 id"銆傚缓璁娇鐢? tc('text_buks78') i18n/no-chinese-hardcode - 159:30 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "澶勭悊鏂瑰紡鏃犳晥锛堝簲涓?rework/scrap/concession/return锛?銆傚缓璁娇鐢? tc('text_h0zcrf') i18n/no-chinese-hardcode - 162:30 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "璐d换閮ㄩ棬涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_6etz94') i18n/no-chinese-hardcode - 165:30 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "璐d换浜轰笉鑳戒负绌?銆傚缓璁娇鐢? tc('text_kvjf6a') i18n/no-chinese-hardcode - 182:30 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "澶勭悊浜轰笉鑳戒负绌?銆傚缓璁娇鐢? tc('text_np28aw') i18n/no-chinese-hardcode - 185:30 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "澶勭悊缁撴灉蹇呴』涓? 1-鍚堟牸 鎴?2-涓嶅悎鏍?銆傚缓璁娇鐢? tc('text_7ov0qz') i18n/no-chinese-hardcode - 188:30 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鎹熷け閲戦涓嶈兘涓鸿礋鏁?銆傚缓璁娇鐢? tc('text_e3g265') i18n/no-chinese-hardcode - 202:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "action 蹇呴』涓?start 鎴?complete"銆傚缓璁娇鐢? tc('text_2by7tz') i18n/no-chinese-hardcode - 213:35 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "缂哄皯 id"銆傚缓璁娇鐢? tc('text_buks78') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\role-permissions\buttons\route.ts - 72:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鏉冮檺鏍煎紡涓嶆纭紝搴斾负鏁扮粍"銆傚缓璁娇鐢? tc('text_y4v8su') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\sales\convert-wo\route.ts - 12:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "璇锋彁渚泂alesOrderId"銆傚缓璁娇鐢? tc('text_bl87z3') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\sales\delivery\[id]\ship\route.ts - 15:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "缂哄皯鍙戣揣鏄庣粏鏁版嵁"銆傚缓璁娇鐢? tc('text_y5wa0z') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\sales\delivery\partial\route.ts - 14:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "缂哄皯閿€鍞鍗旾D"銆傚缓璁娇鐢? tc('text_lnehm5') i18n/no-chinese-hardcode - 18:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍙戣揣鏁伴噺蹇呴』澶т簬0"銆傚缓璁娇鐢? tc('text_tq43um') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\sales\delivery\re-ship\route.ts - 14:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "缂哄皯鍘熷彂璐у崟ID"銆傚缓璁娇鐢? tc('text_453d76') i18n/no-chinese-hardcode - 18:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "琛ュ彂鏁伴噺蹇呴』澶т簬0"銆傚缓璁娇鐢? tc('text_62fjc') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\sales\reconciliation\route.ts - 175:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍙傛暟涓嶅畬鏁达細闇€瑕?id 鍜?action"銆傚缓璁娇鐢? tc('text_nookzh') i18n/no-chinese-hardcode - 186:32 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鏍搁攢鎿嶄綔闇€瑕?receivable_id 鍜?amount"銆傚缓璁娇鐢? tc('text_w2xylm') i18n/no-chinese-hardcode - 207:32 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "浠呰崏绋跨姸鎬佸彲淇敼鎶樻墸"銆傚缓璁娇鐢? tc('text_iipmjj') i18n/no-chinese-hardcode - 209:24 warning 'q' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 221:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "涓嶆敮鎸佺殑鎿嶄綔绫诲瀷"銆傚缓璁娇鐢? tc('text_5nj4g4') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\sales\return\route.ts - 99:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "閫€璐ф槑缁嗕笉鑳戒负绌?銆傚缓璁娇鐢? tc('text_e558y9') i18n/no-chinese-hardcode - 196:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍙傛暟涓嶅畬鏁?銆傚缓璁娇鐢? tc('text_ejeut5') i18n/no-chinese-hardcode - 215:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "涓嶆敮鎸佺殑鎿嶄綔绫诲瀷"銆傚缓璁娇鐢? tc('text_5nj4g4') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\sample\feedback\route.ts - 5:3 warning 'commonErrors' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 23:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鎵撴牱鍗旾D涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_5n451g') i18n/no-chinese-hardcode - 67:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "缂哄皯蹇呭~鍙傛暟"銆傚缓璁娇鐢? tc('text_olx2yz') i18n/no-chinese-hardcode - 92:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍙嶉ID涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_wnkaii') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\sample\inventory\route.ts - 2:10 warning 'query' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 7:3 warning 'commonErrors' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 81:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鏍峰搧搴撳瓨ID涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_vi6ohy') i18n/no-chinese-hardcode - 129:30 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "娌℃湁瑕佹洿鏂扮殑瀛楁"銆傚缓璁娇鐢? tc('text_magxwt') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\sample\orders\linkage\route.ts - 45:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鎵撴牱璁㈠崟ID涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_c60oke') i18n/no-chinese-hardcode - 48:22 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鎵撴牱鍗曡浆宸ュ崟璇锋眰"銆傚缓璁娇鐢? tc('text_32bet2') i18n/no-chinese-hardcode - 82:24 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鐢熸垚宸ュ崟鍙?銆傚缓璁娇鐢? tc('text_qihtoq') i18n/no-chinese-hardcode - 107:24 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "宸ュ崟宸插垱寤?銆傚缓璁娇鐢? tc('text_mvhuw1') i18n/no-chinese-hardcode - 184:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鎵撴牱璁㈠崟ID涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_c60oke') i18n/no-chinese-hardcode - 187:22 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "浜や粯鐘舵€佸彉鏇磋姹?銆傚缓璁娇鐢? tc('text_8ty1s6') i18n/no-chinese-hardcode - 258:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鏃犳晥鐨勬搷浣滅被鍨?銆傚缓璁娇鐢? tc('text_obzmdh') i18n/no-chinese-hardcode - 271:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鎵撴牱璁㈠崟ID涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_c60oke') i18n/no-chinese-hardcode - 274:20 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鏌ヨ鑱斿姩淇℃伅"銆傚缓璁娇鐢? tc('text_fqj3j3') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\sample\orders\route.ts - 89:20 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鏌ヨ鎵撴牱鍗曞垪琛?銆傚缓璁娇鐢? tc('text_i3wl51') i18n/no-chinese-hardcode - 109:20 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鏌ヨ缁撴灉"銆傚缓璁娇鐢? tc('text_doukqu') i18n/no-chinese-hardcode - 124:22 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍒涘缓鎵撴牱鍗曡姹?銆傚缓璁娇鐢? tc('text_3lizn') i18n/no-chinese-hardcode - 146:22 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "snake_case鈫抍amelCase杞崲瀹屾垚"銆傚缓璁娇鐢? tc('text_vnlx37') i18n/no-chinese-hardcode - 148:22 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍒涘缓鎵撴牱鍗曟垚鍔?銆傚缓璁娇鐢? tc('text_3ssn3') i18n/no-chinese-hardcode - 162:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鎵撴牱璁㈠崟ID涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_c60oke') i18n/no-chinese-hardcode - 165:22 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鏇存柊鎵撴牱鍗曡姹?銆傚缓璁娇鐢? tc('text_jpxm0w') i18n/no-chinese-hardcode - 171:22 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "snake_case鈫抍amelCase杞崲瀹屾垚"銆傚缓璁娇鐢? tc('text_vnlx37') i18n/no-chinese-hardcode - 173:22 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鏇存柊鎵撴牱鍗曟垚鍔?銆傚缓璁娇鐢? tc('text_jpqcdg') i18n/no-chinese-hardcode - 185:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鎵撴牱璁㈠崟ID涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_c60oke') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\sample\orders\status\route.ts - 18:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "缂哄皯蹇呭~鍙傛暟: id, action"銆傚缓璁娇鐢? tc('text_jr7wlm') i18n/no-chinese-hardcode - 22:22 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鐘舵€佸彉鏇磋姹?銆傚缓璁娇鐢? tc('text_d7el02') i18n/no-chinese-hardcode - 54:24 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鐘舵€佸彉鏇存垚鍔?銆傚缓璁娇鐢? tc('text_d77bcm') i18n/no-chinese-hardcode - 57:25 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鐘舵€佸彉鏇村け璐?銆傚缓璁娇鐢? tc('text_d764l7') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\screen-plates\history\route.ts - 11:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "缂哄皯缃戠増ID"銆傚缓璁娇鐢? tc('text_sojfgp') i18n/no-chinese-hardcode - 32:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "缂哄皯蹇呰鍙傛暟"銆傚缓璁娇鐢? tc('text_ot3wtd') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\screen-plates\route.ts - 1:26 warning 'transaction' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 110:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "缂哄皯蹇呰鍙傛暟锛歱lateCode, plateName"銆傚缓璁娇鐢? tc('text_76n87x') i18n/no-chinese-hardcode - 160:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "缂哄皯缃戠増ID"銆傚缓璁娇鐢? tc('text_sojfgp') i18n/no-chinese-hardcode - 193:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "娌℃湁闇€瑕佹洿鏂扮殑瀛楁"銆傚缓璁娇鐢? tc('text_nyjsyr') i18n/no-chinese-hardcode - 212:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "缂哄皯缃戠増ID"銆傚缓璁娇鐢? tc('text_sojfgp') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\settings\category-linkage\route.ts - 20:6 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍘熸潗鏂?銆傚缓璁娇鐢? tc('text_cr294') i18n/no-chinese-hardcode - 21:6 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍗婃垚鍝?銆傚缓璁娇鐢? tc('text_cobqz') i18n/no-chinese-hardcode - 22:6 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鎴愬搧"銆傚缓璁娇鐢? tc('text_h581') i18n/no-chinese-hardcode - 23:6 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "杈呮枡"銆傚缓璁娇鐢? tc('text_oywk') i18n/no-chinese-hardcode - 24:6 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍖呮潗"銆傚缓璁娇鐢? tc('text_eorv') i18n/no-chinese-hardcode - 25:6 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "娌瑰ⅷ"銆傚缓璁娇鐢? tc('text_iz9r') i18n/no-chinese-hardcode - 30:58 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍘熸潗鏂欎粨"銆傚缓璁娇鐢? tc('text_azbdez') i18n/no-chinese-hardcode - 31:58 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍗婃垚鍝佷粨"銆傚缓璁娇鐢? tc('text_awyjso') i18n/no-chinese-hardcode - 32:57 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鎴愬搧浠?銆傚缓璁娇鐢? tc('text_erxhe') i18n/no-chinese-hardcode - 33:58 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "杈呮枡浠?銆傚缓璁娇鐢? tc('text_lihlr') i18n/no-chinese-hardcode - 34:58 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍖呮潗浠?銆傚缓璁娇鐢? tc('text_cnrk8') i18n/no-chinese-hardcode - 35:58 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "娌瑰ⅷ浠?銆傚缓璁娇鐢? tc('text_gcsys') i18n/no-chinese-hardcode - 122:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鐗╂枡鍒嗙被ID鍜屼粨搴撳垎绫籌D涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_60eu0c') i18n/no-chinese-hardcode - 131:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鐗╂枡鍒嗙被涓嶅瓨鍦?銆傚缓璁娇鐢? tc('text_nl0zco') i18n/no-chinese-hardcode - 140:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "浠撳簱鍒嗙被涓嶅瓨鍦?銆傚缓璁娇鐢? tc('text_8xajtk') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\settings\category-rules\route.ts - 29:24 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "WH-CAT-XXX锛?浣嶄互涓婃暟瀛楋級"銆傚缓璁娇鐢? tc('text_q3eelc') i18n/no-chinese-hardcode - 32:25 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "绂佺敤"銆傚缓璁娇鐢? tc('text_lb5z') i18n/no-chinese-hardcode - 32:34 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍚敤"銆傚缓璁娇鐢? tc('text_eymx') i18n/no-chinese-hardcode - 36:24 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "MAT-CAT-XXX锛?浣嶄互涓婃暟瀛楋級"銆傚缓璁娇鐢? tc('text_r3668f') i18n/no-chinese-hardcode - 39:25 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "绂佺敤"銆傚缓璁娇鐢? tc('text_lb5z') i18n/no-chinese-hardcode - 39:34 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍚敤"銆傚缓璁娇鐢? tc('text_eymx') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\settings\change-approval\route.ts - 24:36 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍗曟嵁缂栫爜瑙勫垯"銆傚缓璁娇鐢? tc('text_6nj4ef') i18n/no-chinese-hardcode - 24:46 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "瀹℃壒瑙勫垯"銆傚缓璁娇鐢? tc('text_bz4zwt') i18n/no-chinese-hardcode - 24:54 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "浠撳簱绠$悊瑙勫垯"銆傚缓璁娇鐢? tc('text_48cw5i') i18n/no-chinese-hardcode - 24:64 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鐢熶骇涓庡搧璐ㄨ鍒?銆傚缓璁娇鐢? tc('text_l8g7k2') i18n/no-chinese-hardcode - 59:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "妯″潡銆侀厤缃敭鍜屾柊鍊间笉鑳戒负绌?銆傚缓璁娇鐢? tc('text_x0hc4m') i18n/no-chinese-hardcode - 134:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍙樻洿璇锋眰ID鍜屾搷浣滅被鍨嬩笉鑳戒负绌?銆傚缓璁娇鐢? tc('text_j6hjtl') i18n/no-chinese-hardcode - 138:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鎿嶄綔绫诲瀷鏃犳晥锛屼粎鏀寔approve/reject"銆傚缓璁娇鐢? tc('text_4tm1n7') i18n/no-chinese-hardcode - 147:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍙樻洿璇锋眰涓嶅瓨鍦ㄦ垨宸插鐞?銆傚缓璁娇鐢? tc('text_w2umzw') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\settings\system\route.ts - 29:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍗曟嵁缂栫爜瑙勫垯"銆傚缓璁娇鐢? tc('text_6nj4ef') i18n/no-chinese-hardcode - 30:19 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "娴佹按鍙烽暱搴?銆傚缓璁娇鐢? tc('text_f055jf') i18n/no-chinese-hardcode - 31:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鎵€鏈夊崟鎹湯灏炬祦姘村彿浣嶆暟(2-6)"銆傚缓璁娇鐢? tc('text_n5ly90') i18n/no-chinese-hardcode - 41:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍗曟嵁缂栫爜瑙勫垯"銆傚缓璁娇鐢? tc('text_6nj4ef') i18n/no-chinese-hardcode - 42:19 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍗曟嵁鏃ユ湡鏍煎紡"銆傚缓璁娇鐢? tc('text_9wjme2') i18n/no-chinese-hardcode - 43:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鎵€鏈夊崟鎹紪鍙蜂腑鐨勬棩鏈熸牸寮?銆傚缓璁娇鐢? tc('text_kv3zjt') i18n/no-chinese-hardcode - 53:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍗曟嵁缂栫爜瑙勫垯"銆傚缓璁娇鐢? tc('text_6nj4ef') i18n/no-chinese-hardcode - 54:19 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鐢熶骇宸ュ崟鍓嶇紑"銆傚缓璁娇鐢? tc('text_pkyvuz') i18n/no-chinese-hardcode - 55:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鐢熶骇宸ュ崟缂栧彿鍓嶇紑"銆傚缓璁娇鐢? tc('text_djpyr0') i18n/no-chinese-hardcode - 65:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍗曟嵁缂栫爜瑙勫垯"銆傚缓璁娇鐢? tc('text_6nj4ef') i18n/no-chinese-hardcode - 66:19 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鎵撴牱宸ュ崟鍓嶇紑"銆傚缓璁娇鐢? tc('text_lwb789') i18n/no-chinese-hardcode - 67:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鎵撴牱宸ュ崟缂栧彿鍓嶇紑"銆傚缓璁娇鐢? tc('text_cw7a48') i18n/no-chinese-hardcode - 77:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍗曟嵁缂栫爜瑙勫垯"銆傚缓璁娇鐢? tc('text_6nj4ef') i18n/no-chinese-hardcode - 78:19 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "棰嗘枡鍗曞墠缂€"銆傚缓璁娇鐢? tc('text_tr0bpn') i18n/no-chinese-hardcode - 79:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鐗╂枡棰嗙敤鍗曠紪鍙峰墠缂€"銆傚缓璁娇鐢? tc('text_nvs7iv') i18n/no-chinese-hardcode - 89:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍗曟嵁缂栫爜瑙勫垯"銆傚缓璁娇鐢? tc('text_6nj4ef') i18n/no-chinese-hardcode - 90:19 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鎴愬搧鍏ュ簱鍓嶇紑"銆傚缓璁娇鐢? tc('text_ath87m') i18n/no-chinese-hardcode - 91:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鎴愬搧鍏ュ簱鍗曠紪鍙峰墠缂€"銆傚缓璁娇鐢? tc('text_tj5nbq') i18n/no-chinese-hardcode - 101:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍗曟嵁缂栫爜瑙勫垯"銆傚缓璁娇鐢? tc('text_6nj4ef') i18n/no-chinese-hardcode - 102:19 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍙戣揣鍗曞墠缂€"銆傚缓璁娇鐢? tc('text_9a3nbi') i18n/no-chinese-hardcode - 103:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍙戣揣鍗曠紪鍙峰墠缂€"銆傚缓璁娇鐢? tc('text_yau6nh') i18n/no-chinese-hardcode - 113:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍗曟嵁缂栫爜瑙勫垯"銆傚缓璁娇鐢? tc('text_6nj4ef') i18n/no-chinese-hardcode - 114:19 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "閲囪喘璁㈠崟鍓嶇紑"銆傚缓璁娇鐢? tc('text_ff15is') i18n/no-chinese-hardcode - 115:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "閲囪喘璁㈠崟缂栧彿鍓嶇紑"銆傚缓璁娇鐢? tc('text_yo7izx') i18n/no-chinese-hardcode - 125:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍗曟嵁缂栫爜瑙勫垯"銆傚缓璁娇鐢? tc('text_6nj4ef') i18n/no-chinese-hardcode - 126:19 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "妫€楠屽崟鍓嶇紑"銆傚缓璁娇鐢? tc('text_3878kc') i18n/no-chinese-hardcode - 127:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "璐ㄦ妫€楠屽崟缂栧彿鍓嶇紑"銆傚缓璁娇鐢? tc('text_hkrx7') i18n/no-chinese-hardcode - 137:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍗曟嵁缂栫爜瑙勫垯"銆傚缓璁娇鐢? tc('text_6nj4ef') i18n/no-chinese-hardcode - 138:19 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏍囧噯鍗$紪鐮佸墠缂€"銆傚缓璁娇鐢? tc('text_o84lpc') i18n/no-chinese-hardcode - 139:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏍囧噯鍗$紪鍙峰墠缂€(棰滆壊SCC/宸ヨ壓SCP/璐ㄩ噺SCQ/缁煎悎SCZ)"銆傚缓璁娇鐢? tc('text_csl5rs') i18n/no-chinese-hardcode - 151:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍒€妯¢厤缃?銆傚缓璁娇鐢? tc('text_askxpe') i18n/no-chinese-hardcode - 152:19 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍒€妯℃湁鏁堝ぉ鏁?銆傚缓璁娇鐢? tc('text_2yv9eh') i18n/no-chinese-hardcode - 153:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍒€妯′粠鍚敤鍒版姤搴熺殑鏈夋晥澶╂暟"銆傚缓璁娇鐢? tc('text_tejtd2') i18n/no-chinese-hardcode - 163:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍒€妯¢厤缃?銆傚缓璁娇鐢? tc('text_askxpe') i18n/no-chinese-hardcode - 164:19 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍒€妯℃渶澶т娇鐢ㄦ鏁?銆傚缓璁娇鐢? tc('text_6neg1s') i18n/no-chinese-hardcode - 165:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍒€妯℃渶澶у厑璁镐娇鐢ㄦ鏁?銆傚缓璁娇鐢? tc('text_bnjl5j') i18n/no-chinese-hardcode - 175:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍒€妯¢厤缃?銆傚缓璁娇鐢? tc('text_askxpe') i18n/no-chinese-hardcode - 176:19 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍒€妯¢璀﹀ぉ鏁?銆傚缓璁娇鐢? tc('text_3fdx4a') i18n/no-chinese-hardcode - 177:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍒€妯″埌鏈熷墠澶氬皯澶╁紑濮嬮璀?銆傚缓璁娇鐢? tc('text_tv4yqm') i18n/no-chinese-hardcode - 187:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍒€妯¢厤缃?銆傚缓璁娇鐢? tc('text_askxpe') i18n/no-chinese-hardcode - 188:19 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍒€妯℃姤搴熻鍒?銆傚缓璁娇鐢? tc('text_3jjx4g') i18n/no-chinese-hardcode - 189:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鎶ュ簾瑙勫垯锛歜oth=鍒版湡+瓒呮鏁拌嚜鍔ㄦ姤搴? date_only=浠呭埌鏈? times_only=浠呰秴娆℃暟"銆傚缓璁娇鐢? tc('text_bqddyk') i18n/no-chinese-hardcode - 201:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "缃戠増閰嶇疆"銆傚缓璁娇鐢? tc('text_gjjg2g') i18n/no-chinese-hardcode - 202:19 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "缃戠増鏈夋晥澶╂暟"銆傚缓璁娇鐢? tc('text_iqfcyr') i18n/no-chinese-hardcode - 203:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "缃戠増浠庡惎鐢ㄥ埌鎶ュ簾鐨勬湁鏁堝ぉ鏁?銆傚缓璁娇鐢? tc('text_puvxhs') i18n/no-chinese-hardcode - 213:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "缃戠増閰嶇疆"銆傚缓璁娇鐢? tc('text_gjjg2g') i18n/no-chinese-hardcode - 214:19 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "缃戠増鏈€澶т娇鐢ㄦ鏁?銆傚缓璁娇鐢? tc('text_ef4zii') i18n/no-chinese-hardcode - 215:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "缃戠増鏈€澶у厑璁镐娇鐢ㄦ鏁?銆傚缓璁娇鐢? tc('text_6nwrt9') i18n/no-chinese-hardcode - 225:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "缃戠増閰嶇疆"銆傚缓璁娇鐢? tc('text_gjjg2g') i18n/no-chinese-hardcode - 226:19 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "缃戠増棰勮澶╂暟"銆傚缓璁娇鐢? tc('text_cc66g0') i18n/no-chinese-hardcode - 227:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "缃戠増鍒版湡鍓嶅灏戝ぉ寮€濮嬮璀?銆傚缓璁娇鐢? tc('text_50hiu0') i18n/no-chinese-hardcode - 239:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍘熸潗鏂欎繚璐ㄦ湡"銆傚缓璁娇鐢? tc('text_uuczo4') i18n/no-chinese-hardcode - 240:19 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "PET/PVC钖勮啘淇濊川鏈?銆傚缓璁娇鐢? tc('text_cmezov') i18n/no-chinese-hardcode - 241:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "PET/PVC钖勮啘鐨勬湁鏁堜繚璐ㄦ湡澶╂暟"銆傚缓璁娇鐢? tc('text_n1mzp9') i18n/no-chinese-hardcode - 251:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍘熸潗鏂欎繚璐ㄦ湡"銆傚缓璁娇鐢? tc('text_uuczo4') i18n/no-chinese-hardcode - 252:19 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "婧跺墏淇濊川鏈?銆傚缓璁娇鐢? tc('text_hesc08') i18n/no-chinese-hardcode - 253:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍗板埛婧跺墏鐨勬湁鏁堜繚璐ㄦ湡澶╂暟"銆傚缓璁娇鐢? tc('text_6edg39') i18n/no-chinese-hardcode - 263:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍘熸潗鏂欎繚璐ㄦ湡"銆傚缓璁娇鐢? tc('text_uuczo4') i18n/no-chinese-hardcode - 264:19 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "娌瑰ⅷ鏈紑鐩栦繚璐ㄦ湡"銆傚缓璁娇鐢? tc('text_t40jj7') i18n/no-chinese-hardcode - 265:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "娌瑰ⅷ鏈紑鐩栫姸鎬佷笅鐨勪繚璐ㄦ湡澶╂暟"銆傚缓璁娇鐢? tc('text_4gmgyu') i18n/no-chinese-hardcode - 275:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍘熸潗鏂欎繚璐ㄦ湡"銆傚缓璁娇鐢? tc('text_uuczo4') i18n/no-chinese-hardcode - 276:19 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "娌瑰ⅷ寮€鐩栧悗淇濊川鏈?銆傚缓璁娇鐢? tc('text_j9lrbp') i18n/no-chinese-hardcode - 277:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "娌瑰ⅷ寮€鐩栧悗鐨勪繚璐ㄦ湡澶╂暟"銆傚缓璁娇鐢? tc('text_ijqyo') i18n/no-chinese-hardcode - 287:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍘熸潗鏂欎繚璐ㄦ湡"銆傚缓璁娇鐢? tc('text_uuczo4') i18n/no-chinese-hardcode - 288:19 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "娣峰悎娌瑰ⅷ杩囨湡鏃堕棿"銆傚缓璁娇鐢? tc('text_24nmtm') i18n/no-chinese-hardcode - 289:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "娣峰悎娌瑰ⅷ閰嶅埗鍚庣殑鏈夋晥浣跨敤鏃堕暱锛堝皬鏃讹級"銆傚缓璁娇鐢? tc('text_iyvbee') i18n/no-chinese-hardcode - 299:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍘熸潗鏂欎繚璐ㄦ湡"銆傚缓璁娇鐢? tc('text_uuczo4') i18n/no-chinese-hardcode - 300:19 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "杈呮枡/鑳舵按淇濊川鏈?銆傚缓璁娇鐢? tc('text_dlyn2d') i18n/no-chinese-hardcode - 301:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鑳舵按鍜岃緟鏂欑殑淇濊川鏈熷ぉ鏁?銆傚缓璁娇鐢? tc('text_o62c5j') i18n/no-chinese-hardcode - 311:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍘熸潗鏂欎繚璐ㄦ湡"銆傚缓璁娇鐢? tc('text_uuczo4') i18n/no-chinese-hardcode - 312:19 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍘熸潗鏂欓璀﹀ぉ鏁?銆傚缓璁娇鐢? tc('text_nl566n') i18n/no-chinese-hardcode - 313:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鎵€鏈夊師鏉愭枡缁熶竴棰勮鎻愬墠澶╂暟"銆傚缓璁娇鐢? tc('text_gqpjwm') i18n/no-chinese-hardcode - 325:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "灏忔枡鎷嗗垎鏍囧噯"銆傚缓璁娇鐢? tc('text_9moh6f') i18n/no-chinese-hardcode - 326:19 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "PET钖勮啘鎷嗗垎闀垮害"銆傚缓璁娇鐢? tc('text_bdcvqa') i18n/no-chinese-hardcode - 327:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏁村嵎PET钖勮啘鎷嗗垎鎴愬皬鏂欑殑闀垮害鍗曚綅锛堢背锛?銆傚缓璁娇鐢? tc('text_lca0ld') i18n/no-chinese-hardcode - 337:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "灏忔枡鎷嗗垎鏍囧噯"銆傚缓璁娇鐢? tc('text_9moh6f') i18n/no-chinese-hardcode - 338:19 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "PVC钖勮啘鎷嗗垎闀垮害"銆傚缓璁娇鐢? tc('text_g066l8') i18n/no-chinese-hardcode - 339:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏁村嵎PVC钖勮啘鎷嗗垎鎴愬皬鏂欑殑闀垮害鍗曚綅锛堢背锛?銆傚缓璁娇鐢? tc('text_mvzhvh') i18n/no-chinese-hardcode - 349:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "灏忔枡鎷嗗垎鏍囧噯"銆傚缓璁娇鐢? tc('text_9moh6f') i18n/no-chinese-hardcode - 350:19 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "娌瑰ⅷ鎷嗗垎閲嶉噺"銆傚缓璁娇鐢? tc('text_t917tb') i18n/no-chinese-hardcode - 351:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏁存《娌瑰ⅷ鎷嗗垎鎴愬皬鏂欑殑閲嶉噺鍗曚綅锛坘g锛?銆傚缓璁娇鐢? tc('text_e9f88q') i18n/no-chinese-hardcode - 361:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "灏忔枡鎷嗗垎鏍囧噯"銆傚缓璁娇鐢? tc('text_9moh6f') i18n/no-chinese-hardcode - 362:19 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "婧跺墏鎷嗗垎瀹圭Н"銆傚缓璁娇鐢? tc('text_qdc2u6') i18n/no-chinese-hardcode - 363:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏁存《婧跺墏鎷嗗垎鎴愬皬鏂欑殑瀹圭Н鍗曚綅锛圠锛?銆傚缓璁娇鐢? tc('text_pl5fr3') i18n/no-chinese-hardcode - 373:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "灏忔枡鎷嗗垎鏍囧噯"銆傚缓璁娇鐢? tc('text_9moh6f') i18n/no-chinese-hardcode - 374:19 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "缃戝竷鎷嗗垎闀垮害"銆傚缓璁娇鐢? tc('text_rkbo6f') i18n/no-chinese-hardcode - 375:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏁村嵎缃戝竷鎷嗗垎鎴愬皬鏂欑殑闀垮害鍗曚綅锛堢背锛?銆傚缓璁娇鐢? tc('text_ciy4ow') i18n/no-chinese-hardcode - 387:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "浠撳簱绠$悊瑙勫垯"銆傚缓璁娇鐢? tc('text_48cw5i') i18n/no-chinese-hardcode - 388:19 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "寮哄埗鍏堣繘鍏堝嚭"銆傚缓璁娇鐢? tc('text_ch2qpt') i18n/no-chinese-hardcode - 389:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏄惁寮哄埗鎸夊叆搴撴椂闂村厛杩涘厛鍑哄嚭搴?銆傚缓璁娇鐢? tc('text_d1axxc') i18n/no-chinese-hardcode - 399:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "浠撳簱绠$悊瑙勫垯"銆傚缓璁娇鐢? tc('text_48cw5i') i18n/no-chinese-hardcode - 400:19 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍏佽鏁存枡鐩存帴鍙戞枡"銆傚缓璁娇鐢? tc('text_thrr8r') i18n/no-chinese-hardcode - 401:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏄惁鍏佽鏈媶鍒嗙殑鏁存枡鐩存帴鍙戞枡锛堝缓璁叧闂級"銆傚缓璁娇鐢? tc('text_4mqud5') i18n/no-chinese-hardcode - 411:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "浠撳簱绠$悊瑙勫垯"銆傚缓璁娇鐢? tc('text_48cw5i') i18n/no-chinese-hardcode - 412:19 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "棰嗘枡浼樺厛绾?銆傚缓璁娇鐢? tc('text_trmukc') i18n/no-chinese-hardcode - 413:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "expiry_first=鍏堝埌鏈熷厛鍑猴紝fifo=鏍囧噯鍏堣繘鍏堝嚭"銆傚缓璁娇鐢? tc('text_bptuyh') i18n/no-chinese-hardcode - 423:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "浠撳簱绠$悊瑙勫垯"銆傚缓璁娇鐢? tc('text_48cw5i') i18n/no-chinese-hardcode - 424:19 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍏佽鏃犲崟鍙戞枡"銆傚缓璁娇鐢? tc('text_xbfhe4') i18n/no-chinese-hardcode - 425:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏄惁鍏佽娌℃湁璁㈠崟鐩存帴鍙戞枡锛堝缓璁叧闂級"銆傚缓璁娇鐢? tc('text_p4iuph') i18n/no-chinese-hardcode - 435:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "浠撳簱绠$悊瑙勫垯"銆傚缓璁娇鐢? tc('text_48cw5i') i18n/no-chinese-hardcode - 436:19 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "瓒呴闇€瑕佸鎵?銆傚缓璁娇鐢? tc('text_bqiouy') i18n/no-chinese-hardcode - 437:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "瓒呰繃鏍囧噯瀹氶棰嗘枡鏄惁闇€瑕佸鎵?銆傚缓璁娇鐢? tc('text_tox1k7') i18n/no-chinese-hardcode - 447:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "浠撳簱绠$悊瑙勫垯"銆傚缓璁娇鐢? tc('text_48cw5i') i18n/no-chinese-hardcode - 448:19 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "琛ユ枡闇€瑕佸弻瀹℃壒"銆傚缓璁娇鐢? tc('text_7wpftb') i18n/no-chinese-hardcode - 449:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "琛ユ枡鏄惁闇€瑕佷粨搴撲富绠?鐢熶骇缁忕悊鍙岄噸瀹℃壒"銆傚缓璁娇鐢? tc('text_yegpf3') i18n/no-chinese-hardcode - 459:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "浠撳簱绠$悊瑙勫垯"銆傚缓璁娇鐢? tc('text_48cw5i') i18n/no-chinese-hardcode - 460:19 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍛嗘粸鏂欏垽瀹氬ぉ鏁?銆傚缓璁娇鐢? tc('text_48ky1u') i18n/no-chinese-hardcode - 461:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "搴撳瓨瓒呰繃姝ゅぉ鏁版湭鍔ㄩ攢鍒ゅ畾涓哄憜婊炴枡"銆傚缓璁娇鐢? tc('text_2ucuu3') i18n/no-chinese-hardcode - 473:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鐩樼偣鍛ㄦ湡绠$悊"銆傚缓璁娇鐢? tc('text_bbztgd') i18n/no-chinese-hardcode - 474:19 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "A绫荤墿鏂欑洏鐐瑰懆鏈?銆傚缓璁娇鐢? tc('text_p4fog2') i18n/no-chinese-hardcode - 475:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "A绫婚珮浠峰€肩墿鏂欑洏鐐瑰懆鏈?澶?"銆傚缓璁娇鐢? tc('text_wx2ysd') i18n/no-chinese-hardcode - 485:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鐩樼偣鍛ㄦ湡绠$悊"銆傚缓璁娇鐢? tc('text_bbztgd') i18n/no-chinese-hardcode - 486:19 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "B绫荤墿鏂欑洏鐐瑰懆鏈?銆傚缓璁娇鐢? tc('text_h31wgv') i18n/no-chinese-hardcode - 487:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "B绫讳腑浠峰€肩墿鏂欑洏鐐瑰懆鏈?澶?"銆傚缓璁娇鐢? tc('text_x86287') i18n/no-chinese-hardcode - 497:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鐩樼偣鍛ㄦ湡绠$悊"銆傚缓璁娇鐢? tc('text_bbztgd') i18n/no-chinese-hardcode - 498:19 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "C绫荤墿鏂欑洏鐐瑰懆鏈?銆傚缓璁娇鐢? tc('text_bqkklc') i18n/no-chinese-hardcode - 499:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "C绫讳綆浠峰€肩墿鏂欑洏鐐瑰懆鏈?澶?"銆傚缓璁娇鐢? tc('text_2jjag5') i18n/no-chinese-hardcode - 511:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鐢熶骇涓庡搧璐ㄨ鍒?銆傚缓璁娇鐢? tc('text_l8g7k2') i18n/no-chinese-hardcode - 512:19 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍏佽璺冲伐搴忔姤宸?銆傚缓璁娇鐢? tc('text_memryy') i18n/no-chinese-hardcode - 513:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏄惁鍏佽璺宠繃宸ュ簭椤哄簭杩涜鎶ュ伐锛堝缓璁叧闂級"銆傚缓璁娇鐢? tc('text_43wf9') i18n/no-chinese-hardcode - 523:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鐢熶骇涓庡搧璐ㄨ鍒?銆傚缓璁娇鐢? tc('text_l8g7k2') i18n/no-chinese-hardcode - 524:19 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍏佽閲嶅鎶ュ伐"銆傚缓璁娇鐢? tc('text_rr2n1l') i18n/no-chinese-hardcode - 525:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏄惁鍏佽鍚屼竴宸ュ簭閲嶅鎶ュ伐锛堝缓璁叧闂級"銆傚缓璁娇鐢? tc('text_hf97a7') i18n/no-chinese-hardcode - 535:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鐢熶骇涓庡搧璐ㄨ鍒?銆傚缓璁娇鐢? tc('text_l8g7k2') i18n/no-chinese-hardcode - 536:19 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鎴愬搧鍏ュ簱鍓嶅繀椤绘楠?銆傚缓璁娇鐢? tc('text_xsqx74') i18n/no-chinese-hardcode - 537:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鎴愬搧鍏ュ簱鍓嶆槸鍚﹀繀椤荤粡杩嘑QC妫€楠?銆傚缓璁娇鐢? tc('text_r1ik8f') i18n/no-chinese-hardcode - 547:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鐢熶骇涓庡搧璐ㄨ鍒?銆傚缓璁娇鐢? tc('text_l8g7k2') i18n/no-chinese-hardcode - 548:19 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍙戣揣鍓嶅繀椤籓QC妫€楠?銆傚缓璁娇鐢? tc('text_igf3fk') i18n/no-chinese-hardcode - 549:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍙戣揣鍓嶆槸鍚﹀繀椤荤粡杩嘜QC鍑鸿揣妫€楠?銆傚缓璁娇鐢? tc('text_3eiy82') i18n/no-chinese-hardcode - 561:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "瀹℃壒瑙勫垯"銆傚缓璁娇鐢? tc('text_bz4zwt') i18n/no-chinese-hardcode - 562:19 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "閰嶇疆淇敼闇€瀹℃壒"銆傚缓璁娇鐢? tc('text_p52jfw') i18n/no-chinese-hardcode - 563:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "淇敼绯荤粺鍙傛暟鏄惁闇€瑕佸鎵规祦绋?銆傚缓璁娇鐢? tc('text_xvxq2f') i18n/no-chinese-hardcode - 573:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "瀹℃壒瑙勫垯"銆傚缓璁娇鐢? tc('text_bz4zwt') i18n/no-chinese-hardcode - 574:19 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "瀹℃壒浜鸿鑹?銆傚缓璁娇鐢? tc('text_fvdj4y') i18n/no-chinese-hardcode - 575:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "閰嶇疆鍙樻洿鐨勫鎵逛汉瑙掕壊"銆傚缓璁娇鐢? tc('text_589vf') i18n/no-chinese-hardcode - 585:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "瀹℃壒瑙勫垯"銆傚缓璁娇鐢? tc('text_bz4zwt') i18n/no-chinese-hardcode - 586:19 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鐢熸晥鏂瑰紡"銆傚缓璁娇鐢? tc('text_f74rb3') i18n/no-chinese-hardcode - 587:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "immediate=绔嬪嵆鐢熸晥, next_day=娆℃棩鐢熸晥"銆傚缓璁娇鐢? tc('text_x035e1') i18n/no-chinese-hardcode - 597:19 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "瓒婂崡杈炬槍涓濈綉鍗板埛鏈夐檺鍏徃"銆傚缓璁娇鐢? tc('text_k8ia1j') i18n/no-chinese-hardcode - 599:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "绯荤粺鍩虹閰嶇疆"銆傚缓璁娇鐢? tc('text_tnvytn') i18n/no-chinese-hardcode - 600:19 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍏徃鍚嶇О"銆傚缓璁娇鐢? tc('text_amf4wf') i18n/no-chinese-hardcode - 601:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍏徃鍏ㄧО锛岀敤浜庡叏灞€鏄剧ず"銆傚缓璁娇鐢? tc('text_w1483y') i18n/no-chinese-hardcode - 609:19 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "杈炬槍鍗板埛"銆傚缓璁娇鐢? tc('text_ik15qt') i18n/no-chinese-hardcode - 611:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "绯荤粺鍩虹閰嶇疆"銆傚缓璁娇鐢? tc('text_tnvytn') i18n/no-chinese-hardcode - 612:19 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍏徃绠€绉?銆傚缓璁娇鐢? tc('text_amlugs') i18n/no-chinese-hardcode - 613:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍏徃绠€绉?銆傚缓璁娇鐢? tc('text_amlugs') i18n/no-chinese-hardcode - 621:19 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "VNERP涓濈綉鍗板埛绠$悊绯荤粺"銆傚缓璁娇鐢? tc('text_p944b5') i18n/no-chinese-hardcode - 623:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "绯荤粺鍩虹閰嶇疆"銆傚缓璁娇鐢? tc('text_tnvytn') i18n/no-chinese-hardcode - 624:19 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "绯荤粺鍚嶇О"銆傚缓璁娇鐢? tc('text_gahjnr') i18n/no-chinese-hardcode - 625:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "绯荤粺鏄剧ず鍚嶇О"銆傚缓璁娇鐢? tc('text_vgik5f') i18n/no-chinese-hardcode - 635:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "绯荤粺鍩虹閰嶇疆"銆傚缓璁娇鐢? tc('text_tnvytn') i18n/no-chinese-hardcode - 636:19 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "绯荤粺鐗堟湰鍙?銆傚缓璁娇鐢? tc('text_7xo7v3') i18n/no-chinese-hardcode - 637:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "绯荤粺鐗堟湰鍙?銆傚缓璁娇鐢? tc('text_7xo7v3') i18n/no-chinese-hardcode - 647:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "绯荤粺鍩虹閰嶇疆"銆傚缓璁娇鐢? tc('text_tnvytn') i18n/no-chinese-hardcode - 648:19 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鐗堟潈淇℃伅"銆傚缓璁娇鐢? tc('text_eufaax') i18n/no-chinese-hardcode - 649:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鐗堟潈澹版槑淇℃伅"銆傚缓璁娇鐢? tc('text_9v8htl') i18n/no-chinese-hardcode - 659:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "绯荤粺鍩虹閰嶇疆"銆傚缓璁娇鐢? tc('text_tnvytn') i18n/no-chinese-hardcode - 660:19 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鐢ㄦ埛榛樿瀵嗙爜"銆傚缓璁娇鐢? tc('text_rb1qju') i18n/no-chinese-hardcode - 661:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏂扮敤鎴烽粯璁ゅ瘑鐮?銆傚缓璁娇鐢? tc('text_cz0ire') i18n/no-chinese-hardcode - 682:17 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "绯荤粺鍩虹閰嶇疆"銆傚缓璁娇鐢? tc('text_tnvytn') i18n/no-chinese-hardcode - 683:21 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍏徃鍚嶇О"銆傚缓璁娇鐢? tc('text_amf4wf') i18n/no-chinese-hardcode - 684:20 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍏徃鍏ㄧО"銆傚缓璁娇鐢? tc('text_ameopg') i18n/no-chinese-hardcode - 689:17 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "绯荤粺鍩虹閰嶇疆"銆傚缓璁娇鐢? tc('text_tnvytn') i18n/no-chinese-hardcode - 690:21 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍏徃缂栫爜"銆傚缓璁娇鐢? tc('text_ammg1j') i18n/no-chinese-hardcode - 691:20 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍏徃绠€绉扮紪鐮?銆傚缓璁娇鐢? tc('text_eyfcnt') i18n/no-chinese-hardcode - 696:17 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "浠撳簱绠$悊瑙勫垯"銆傚缓璁娇鐢? tc('text_48cw5i') i18n/no-chinese-hardcode - 697:21 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "榛樿浠撳簱"銆傚缓璁娇鐢? tc('text_km37jg') i18n/no-chinese-hardcode - 698:20 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "绯荤粺榛樿浠撳簱"銆傚缓璁娇鐢? tc('text_wcx7tc') i18n/no-chinese-hardcode - 703:17 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "浠撳簱绠$悊瑙勫垯"銆傚缓璁娇鐢? tc('text_48cw5i') i18n/no-chinese-hardcode - 704:21 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "FIFO妯″紡"銆傚缓璁娇鐢? tc('text_yb3f8a') i18n/no-chinese-hardcode - 705:20 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍏堣繘鍏堝嚭妯″紡"銆傚缓璁娇鐢? tc('text_gffp2l') i18n/no-chinese-hardcode - 710:17 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "浠撳簱绠$悊瑙勫垯"銆傚缓璁娇鐢? tc('text_48cw5i') i18n/no-chinese-hardcode - 711:21 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍏ュ簱鑷姩瀹℃壒"銆傚缓璁娇鐢? tc('text_nu9w04') i18n/no-chinese-hardcode - 712:20 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍏ュ簱鍗曟槸鍚﹁嚜鍔ㄥ鎵?銆傚缓璁娇鐢? tc('text_57z19o') i18n/no-chinese-hardcode - 717:17 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍗曟嵁缂栫爜瑙勫垯"銆傚缓璁娇鐢? tc('text_6nj4ef') i18n/no-chinese-hardcode - 718:21 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鎵规鍙峰墠缂€"銆傚缓璁娇鐢? tc('text_ralum6') i18n/no-chinese-hardcode - 719:20 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "搴撳瓨鎵规鍙峰墠缂€"銆傚缓璁娇鐢? tc('text_siepq5') i18n/no-chinese-hardcode - 724:17 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍗曟嵁缂栫爜瑙勫垯"銆傚缓璁娇鐢? tc('text_6nj4ef') i18n/no-chinese-hardcode - 725:21 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "璁㈠崟缂栧彿鍓嶇紑"銆傚缓璁娇鐢? tc('text_6hr3mv') i18n/no-chinese-hardcode - 726:20 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "閿€鍞鍗曠紪鍙峰墠缂€"銆傚缓璁娇鐢? tc('text_73bwt1') i18n/no-chinese-hardcode - 731:17 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "绯荤粺鍩虹閰嶇疆"銆傚缓璁娇鐢? tc('text_tnvytn') i18n/no-chinese-hardcode - 732:21 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "榛樿璐у竵"銆傚缓璁娇鐢? tc('text_kmdt3a') i18n/no-chinese-hardcode - 733:20 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "绯荤粺榛樿璐у竵鍗曚綅"銆傚缓璁娇鐢? tc('text_mfybj6') i18n/no-chinese-hardcode - 738:17 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "绯荤粺鍩虹閰嶇疆"銆傚缓璁娇鐢? tc('text_tnvytn') i18n/no-chinese-hardcode - 739:21 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "榛樿绋庣巼"銆傚缓璁娇鐢? tc('text_kmaoed') i18n/no-chinese-hardcode - 740:20 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "绯荤粺榛樿绋庣巼(%)"銆傚缓璁娇鐢? tc('text_fugh7h') i18n/no-chinese-hardcode - 745:17 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "浠撳簱绠$悊瑙勫垯"銆傚缓璁娇鐢? tc('text_48cw5i') i18n/no-chinese-hardcode - 746:21 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍏ュ簱鎵撳嵃鏍囩"銆傚缓璁娇鐢? tc('text_jv0i8y') i18n/no-chinese-hardcode - 747:20 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍏ュ簱鏃舵槸鍚﹁嚜鍔ㄦ墦鍗版爣绛?銆傚缓璁娇鐢? tc('text_u9q429') i18n/no-chinese-hardcode - 775:26 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "閰嶇疆绫诲瀷"銆傚缓璁娇鐢? tc('text_iv0s8h') i18n/no-chinese-hardcode - 776:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍒嗙被"銆傚缓璁娇鐢? tc('text_emut') i18n/no-chinese-hardcode - 777:22 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏄剧ず鍚嶇О"銆傚缓璁娇鐢? tc('text_deexj3') i18n/no-chinese-hardcode - 778:21 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鎻忚堪"銆傚缓璁娇鐢? tc('text_hrlt') i18n/no-chinese-hardcode - 779:20 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鎺掑簭"銆傚缓璁娇鐢? tc('text_hge5') i18n/no-chinese-hardcode - 780:21 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏄惁蹇呭~"銆傚缓璁娇鐢? tc('text_d8rnfh') i18n/no-chinese-hardcode - 781:27 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "闇€瑕佸鎵?銆傚缓璁娇鐢? tc('text_jm7zg9') i18n/no-chinese-hardcode - 782:16 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鐘舵€?銆傚缓璁娇鐢? tc('text_k1e3') i18n/no-chinese-hardcode - 913:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "缂哄皯閰嶇疆鏁版嵁"銆傚缓璁娇鐢? tc('text_v0m9zq') i18n/no-chinese-hardcode - 993:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "缂哄皯鍙樻洿璁板綍ID"銆傚缓璁娇鐢? tc('text_ysbofn') i18n/no-chinese-hardcode - 1003:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "璇ヨ褰曞凡澶勭悊"銆傚缓璁娇鐢? tc('text_titi8m') i18n/no-chinese-hardcode - 1031:30 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鏃犳晥鐨勬搷浣滅被鍨?銆傚缓璁娇鐢? tc('text_obzmdh') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\srm\evaluation\[id]\route.ts - 9:33 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "ID涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_p1r591') i18n/no-chinese-hardcode - 15:51 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "璇勪及璁板綍涓嶅瓨鍦?銆傚缓璁娇鐢? tc('text_zgt1uc') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\srm\evaluation\route.ts - 68:44 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "渚涘簲鍟咺D涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_cyfkl4') i18n/no-chinese-hardcode - 134:35 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "ID涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_p1r591') i18n/no-chinese-hardcode - 197:35 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "ID涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_p1r591') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\standard-card\action\route.ts - 16:54 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "缂哄皯鏍囧噯鍗D"銆傚缓璁娇鐢? tc('text_cywosm') i18n/no-chinese-hardcode - 25:20 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鎻愪氦瀹℃牳鎴愬姛"銆傚缓璁娇鐢? tc('text_cq81ie') i18n/no-chinese-hardcode - 33:20 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "瀹℃牳閫氳繃"銆傚缓璁娇鐢? tc('text_c00pno') i18n/no-chinese-hardcode - 41:20 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "纭瀹屾垚"銆傚缓璁娇鐢? tc('text_frq54q') i18n/no-chinese-hardcode - 47:58 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "浣滃簾鏃跺繀椤诲~鍐欏師鍥?銆傚缓璁娇鐢? tc('text_hmbw7s') i18n/no-chinese-hardcode - 52:20 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "浣滃簾鎴愬姛"銆傚缓璁娇鐢? tc('text_aeobyq') i18n/no-chinese-hardcode - 60:20 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍒涘缓鏂扮増鏈垚鍔?銆傚缓璁娇鐢? tc('text_ee85l0') i18n/no-chinese-hardcode - 65:56 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏃犳晥鐨勬搷浣?銆傚缓璁娇鐢? tc('text_el3ulx') i18n/no-chinese-hardcode - 71:46 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鎿嶄綔澶辫触"銆傚缓璁娇鐢? tc('text_d1rj43') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\standard-card\by-material\route.ts - 8:49 warning 'user' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars - 15:54 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "缂哄皯鐗╂枡ID"銆傚缓璁娇鐢? tc('text_r02v42') i18n/no-chinese-hardcode - 27:46 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏌ヨ澶辫触"銆傚缓璁娇鐢? tc('text_doodpt') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\standard-card\route.ts - 8:49 warning 'user' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars - 18:54 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏍囧噯鍗′笉瀛樺湪"銆傚缓璁娇鐢? tc('text_c1941n') i18n/no-chinese-hardcode - 30:54 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏍囧噯鍗′笉瀛樺湪"銆傚缓璁娇鐢? tc('text_c1941n') i18n/no-chinese-hardcode - 95:16 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍒涘缓鎴愬姛"銆傚缓璁娇鐢? tc('text_ar737y') i18n/no-chinese-hardcode - 102:46 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍒涘缓澶辫触"銆傚缓璁娇鐢? tc('text_ar5wgj') i18n/no-chinese-hardcode - 113:54 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "缂哄皯鏍囧噯鍗D"銆傚缓璁娇鐢? tc('text_cywosm') i18n/no-chinese-hardcode - 122:16 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏇存柊鎴愬姛"銆傚缓璁娇鐢? tc('text_deua4r') i18n/no-chinese-hardcode - 129:46 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏇存柊澶辫触"銆傚缓璁娇鐢? tc('text_det3dc') i18n/no-chinese-hardcode - 136:52 warning 'user' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars - 141:54 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "缂哄皯鏍囧噯鍗D"銆傚缓璁娇鐢? tc('text_cywosm') i18n/no-chinese-hardcode - 148:54 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏍囧噯鍗D鏃犳晥"銆傚缓璁娇鐢? tc('text_7qx9ph') i18n/no-chinese-hardcode - 155:16 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍒犻櫎鎴愬姛"銆傚缓璁娇鐢? tc('text_azeh8z') i18n/no-chinese-hardcode - 161:46 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍒犻櫎澶辫触"銆傚缓璁娇鐢? tc('text_azdahk') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\standard-cards\approve\route.ts - 33:41 warning 'remark' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 36:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鏍囧噯鍗D涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_fefwoj') i18n/no-chinese-hardcode - 40:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "瀹℃牳绫诲瀷鏃犳晥"銆傚缓璁娇鐢? tc('text_mty2of') i18n/no-chinese-hardcode - 44:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "瀹℃牳浜轰俊鎭笉鑳戒负绌?銆傚缓璁娇鐢? tc('text_8g37fl') i18n/no-chinese-hardcode - 61:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鏍囧噯鍗′笉瀛樺湪"銆傚缓璁娇鐢? tc('text_c1941n') i18n/no-chinese-hardcode - 125:23 warning 'userId' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 125:31 warning 'userName' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 125:41 warning 'remark' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 128:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鏍囧噯鍗D涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_fefwoj') i18n/no-chinese-hardcode - 132:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "瀹℃牳绫诲瀷鏃犳晥"銆傚缓璁娇鐢? tc('text_mty2of') i18n/no-chinese-hardcode - 146:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鏍囧噯鍗′笉瀛樺湪"銆傚缓璁娇鐢? tc('text_c1941n') i18n/no-chinese-hardcode - 151:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "璇ョ幆鑺傛湭瀹℃牳锛屾棤娉曟挙閿€"銆傚缓璁娇鐢? tc('text_f1lelw') i18n/no-chinese-hardcode - 185:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鏍囧噯鍗D涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_fefwoj') i18n/no-chinese-hardcode - 205:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鏍囧噯鍗′笉瀛樺湪"銆傚缓璁娇鐢? tc('text_c1941n') i18n/no-chinese-hardcode - 252:8 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鑽夌"銆傚缓璁娇鐢? tc('text_n02e') i18n/no-chinese-hardcode - 253:8 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "寰呭鏍?銆傚缓璁娇鐢? tc('text_eftvg') i18n/no-chinese-hardcode - 254:8 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "宸插惎鐢?銆傚缓璁娇鐢? tc('text_e6c0b') i18n/no-chinese-hardcode - 255:8 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "宸插綊妗?銆傚缓璁娇鐢? tc('text_e85oj') i18n/no-chinese-hardcode - 257:28 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏈煡"銆傚缓璁娇鐢? tc('text_i7ej') i18n/no-chinese-hardcode - 262:13 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "瀹℃牳"銆傚缓璁娇鐢? tc('text_g5o7') i18n/no-chinese-hardcode - 263:14 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍘傚姟瀹℃牳"銆傚缓璁娇鐢? tc('text_avq8gm') i18n/no-chinese-hardcode - 264:14 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍝佺瀹℃牳"銆傚缓璁娇鐢? tc('text_b7e46v') i18n/no-chinese-hardcode - 265:12 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "涓氬姟瀹℃牳"銆傚缓璁娇鐢? tc('text_a76im6') i18n/no-chinese-hardcode - 266:14 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏍稿噯"銆傚缓璁娇鐢? tc('text_i6by') i18n/no-chinese-hardcode - 267:13 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "椹冲洖"銆傚缓璁娇鐢? tc('text_qqx7') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\standard-cards\by-material\[material_id]\route.ts - 66:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鏃犳晥鐨勪骇鍝両D"銆傚缓璁娇鐢? tc('text_r4d7pr') i18n/no-chinese-hardcode - 77:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鏈壘鍒拌浜у搧鐨勬爣鍑嗗崱"銆傚缓璁娇鐢? tc('text_c5gctb') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\standard-cards\by-work-order\[work_order_id]\route.ts - 33:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鏃犳晥鐨勫伐鍗旾D"銆傚缓璁娇鐢? tc('text_p7d6t5') i18n/no-chinese-hardcode - 45:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鏈壘鍒拌宸ュ崟鍏宠仈鐨勬爣鍑嗗崱"銆傚缓璁娇鐢? tc('text_8090lk') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\standard-cards\check-deviation\route.ts - 14:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "缂哄皯鏍囧噯鍗D"銆傚缓璁娇鐢? tc('text_cywosm') i18n/no-chinese-hardcode - 18:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "缂哄皯瀹為檯鍙傛暟鏁版嵁"銆傚缓璁娇鐢? tc('text_ihu4oa') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\standard-cards\route.ts - 228:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鏍囧噯鍗$紪鍙峰凡瀛樺湪"銆傚缓璁娇鐢? tc('text_yud4kh') i18n/no-chinese-hardcode - 371:30 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鏍囧噯鍗$紪鍙峰凡瀛樺湪"銆傚缓璁娇鐢? tc('text_yud4kh') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\standard-cards\scan\route.ts - 20:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "缂哄皯浜岀淮鐮佺紪鐮?銆傚缓璁娇鐢? tc('text_9rgoxf') i18n/no-chinese-hardcode - 30:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鏈壘鍒板搴旂殑浜岀淮鐮佽褰?銆傚缓璁娇鐢? tc('text_yshbe7') i18n/no-chinese-hardcode - 49:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "璇ヤ簩缁寸爜鏈叧鑱旂敓浜у伐鍗?銆傚缓璁娇鐢? tc('text_9d5gvl') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\system\announcement\route.ts - 97:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍏憡ID涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_vnm1yv') i18n/no-chinese-hardcode - 152:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "娌℃湁闇€瑕佹洿鏂扮殑瀛楁"銆傚缓璁娇鐢? tc('text_nyjsyr') i18n/no-chinese-hardcode - 172:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍏憡ID涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_vnm1yv') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\system\config\route.ts - 15:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "搴撳瓨鍏佽璐熸暟"銆傚缓璁娇鐢? tc('text_uwd5qr') i18n/no-chinese-hardcode - 19:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏄惁鍏佽搴撳瓨鏁伴噺涓鸿礋鏁?銆傚缓璁娇鐢? tc('text_n12udj') i18n/no-chinese-hardcode - 22:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "搴撳瓨棰勮闃堝€?銆傚缓璁娇鐢? tc('text_lwsuh1') i18n/no-chinese-hardcode - 26:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "浣庝簬姝ゆ暟閲忚Е鍙戝簱瀛橀璀?銆傚缓璁娇鐢? tc('text_6n71u3') i18n/no-chinese-hardcode - 29:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "搴撳瓨鐩樼偣宸紓瀹℃壒"銆傚缓璁娇鐢? tc('text_xafmum') i18n/no-chinese-hardcode - 33:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鐩樼偣宸紓鏄惁闇€瑕佸鎵?銆傚缓璁娇鐢? tc('text_v6z5x7') i18n/no-chinese-hardcode - 36:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "榛樿浠撳簱缂栫爜"銆傚缓璁娇鐢? tc('text_7y4r55') i18n/no-chinese-hardcode - 40:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏂板缓鍗曟嵁榛樿甯﹀嚭鐨勪粨搴撶紪鐮?銆傚缓璁娇鐢? tc('text_wudtjg') i18n/no-chinese-hardcode - 43:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "搴撳瓨鏁伴噺绮惧害"銆傚缓璁娇鐢? tc('text_sbwm50') i18n/no-chinese-hardcode - 47:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "搴撳瓨鏁伴噺淇濈暀鐨勫皬鏁颁綅鏁?銆傚缓璁娇鐢? tc('text_rqkw0o') i18n/no-chinese-hardcode - 50:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "瀹夊叏搴撳瓨姣斾緥"銆傚缓璁娇鐢? tc('text_agavz9') i18n/no-chinese-hardcode - 54:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "瀹夊叏搴撳瓨鍗犳棩鍧囬攢閲忕殑鐧惧垎姣?銆傚缓璁娇鐢? tc('text_4vbdg5') i18n/no-chinese-hardcode - 57:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鑷姩鐢熸垚鎵规鍙?銆傚缓璁娇鐢? tc('text_30srr4') i18n/no-chinese-hardcode - 61:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍏ュ簱鏃舵槸鍚﹁嚜鍔ㄧ敓鎴愭壒娆″彿"銆傚缓璁娇鐢? tc('text_j99qe9') i18n/no-chinese-hardcode - 64:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鎵规杩囨湡棰勮澶╂暟"銆傚缓璁娇鐢? tc('text_owtvp3') i18n/no-chinese-hardcode - 68:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "璺濇壒娆″け鏁堣繕鏈夊灏戝ぉ鏃惰繘琛岄璀?銆傚缓璁娇鐢? tc('text_5chli4') i18n/no-chinese-hardcode - 71:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "搴撳瓨鐩樼偣鍛ㄦ湡"銆傚缓璁娇鐢? tc('text_q9wmir') i18n/no-chinese-hardcode - 75:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "榛樿搴撳瓨鐩樼偣鍛ㄦ湡锛堝ぉ锛?銆傚缓璁娇鐢? tc('text_8omsv3') i18n/no-chinese-hardcode - 78:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍏ュ簱鍗曞彿鍓嶇紑"銆傚缓璁娇鐢? tc('text_hywycj') i18n/no-chinese-hardcode - 82:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍏ュ簱鍗曞彿鍓嶇紑"銆傚缓璁娇鐢? tc('text_hywycj') i18n/no-chinese-hardcode - 85:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍑哄簱鍗曞彿鍓嶇紑"銆傚缓璁娇鐢? tc('text_hhil4e') i18n/no-chinese-hardcode - 89:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍑哄簱鍗曞彿鍓嶇紑"銆傚缓璁娇鐢? tc('text_hhil4e') i18n/no-chinese-hardcode - 92:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "璁㈠崟鍙峰墠缂€"銆傚缓璁娇鐢? tc('text_bf7qdl') i18n/no-chinese-hardcode - 96:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "閿€鍞鍗曞彿鍓嶇紑"銆傚缓璁娇鐢? tc('text_dsudl5') i18n/no-chinese-hardcode - 99:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "璁㈠崟鑷姩瀹℃牳"銆傚缓璁娇鐢? tc('text_6ufyco') i18n/no-chinese-hardcode - 103:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "璁㈠崟鎻愪氦鍚庢槸鍚﹁嚜鍔ㄥ鏍搁€氳繃"銆傚缓璁娇鐢? tc('text_n138gw') i18n/no-chinese-hardcode - 106:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "缂鸿揣澶勭悊鏂瑰紡"銆傚缓璁娇鐢? tc('text_2pc5yd') i18n/no-chinese-hardcode - 110:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "warn=鎻愰啋锛宐lock=绂佹涓嬪崟锛宎llow=鍏佽瓒呭崠"銆傚缓璁娇鐢? tc('text_byahhi') i18n/no-chinese-hardcode - 113:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "璁㈠崟瓒呮湡鎻愰啋"銆傚缓璁娇鐢? tc('text_8e6jfz') i18n/no-chinese-hardcode - 117:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "璁㈠崟瓒呰繃鎵胯浜よ揣鏈熷灏戝ぉ瑙﹀彂鎻愰啋"銆傚缓璁娇鐢? tc('text_u1sblf') i18n/no-chinese-hardcode - 120:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "榛樿浜よ揣鍛ㄦ湡"銆傚缓璁娇鐢? tc('text_7sciey') i18n/no-chinese-hardcode - 124:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏂板缓閿€鍞鍗曢粯璁ょ殑鎵胯浜よ揣澶╂暟"銆傚缓璁娇鐢? tc('text_fvonny') i18n/no-chinese-hardcode - 127:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "璁㈠崟浠锋牸绮惧害"銆傚缓璁娇鐢? tc('text_if1fk') i18n/no-chinese-hardcode - 131:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "璁㈠崟鍗曚环涓庨噾棰濅繚鐣欑殑灏忔暟浣嶆暟"銆傚缓璁娇鐢? tc('text_8qrmsn') i18n/no-chinese-hardcode - 134:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "瀹㈡埛淇$敤棰濆害鎺у埗"銆傚缓璁娇鐢? tc('text_m8huc4') i18n/no-chinese-hardcode - 138:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "涓嬪崟鏃舵槸鍚︽牎楠屽鎴峰彲鐢ㄤ俊鐢ㄩ搴?銆傚缓璁娇鐢? tc('text_xxew9g') i18n/no-chinese-hardcode - 141:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "閫€璐ф湡闄?銆傚缓璁娇鐢? tc('text_ir1854') i18n/no-chinese-hardcode - 145:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "閿€鍞鍗曞厑璁搁€€璐х殑澶╂暟"銆傚缓璁娇鐢? tc('text_hko87o') i18n/no-chinese-hardcode - 148:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "閲囪喘鍗曞彿鍓嶇紑"銆傚缓璁娇鐢? tc('text_mikvc5') i18n/no-chinese-hardcode - 152:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "閲囪喘璁㈠崟鍙峰墠缂€"銆傚缓璁娇鐢? tc('text_j9uhwx') i18n/no-chinese-hardcode - 155:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "閲囪喘浠锋牸涓婇檺鎺у埗"銆傚缓璁娇鐢? tc('text_2476jk') i18n/no-chinese-hardcode - 159:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "閲囪喘浠疯秴杩囧巻鍙叉渶楂樹环鏃舵槸鍚︽嫤鎴?銆傚缓璁娇鐢? tc('text_nbaomn') i18n/no-chinese-hardcode - 162:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "閲囪喘鍒拌揣鎻愰啋"銆傚缓璁娇鐢? tc('text_mfbbup') i18n/no-chinese-hardcode - 166:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "璺濋璁″埌璐ф棩鏈熷灏戝ぉ鍙戦€佹彁閱?銆傚缓璁娇鐢? tc('text_vm59o9') i18n/no-chinese-hardcode - 169:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "閲囪喘鍏佸樊姣斾緥"銆傚缓璁娇鐢? tc('text_mqfxti') i18n/no-chinese-hardcode - 173:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍒拌揣鏁伴噺涓庤鍗曟暟閲忕殑鍏佽鍋忓樊鐧惧垎姣?銆傚缓璁娇鐢? tc('text_s902f0') i18n/no-chinese-hardcode - 176:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "閲囪喘榛樿绋庣巼"銆傚缓璁娇鐢? tc('text_crd9yt') i18n/no-chinese-hardcode - 180:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "閲囪喘鍗曟嵁榛樿绋庣巼锛?锛?銆傚缓璁娇鐢? tc('text_hbdurc') i18n/no-chinese-hardcode - 183:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "閲囪喘瀹℃壒閲戦闃堝€?銆傚缓璁娇鐢? tc('text_vi7ye6') i18n/no-chinese-hardcode - 187:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "瓒呰繃姝ら噾棰濈殑閲囪喘鍗曢渶瑕佸鎵?銆傚缓璁娇鐢? tc('text_ko5lte') i18n/no-chinese-hardcode - 190:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "榛樿浠樻鏈熼檺"銆傚缓璁娇鐢? tc('text_7wao8t') i18n/no-chinese-hardcode - 194:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "榛樿浠樻鏈熼檺澶╂暟"銆傚缓璁娇鐢? tc('text_b6keay') i18n/no-chinese-hardcode - 197:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "榛樿甯佺"銆傚缓璁娇鐢? tc('text_km5xvc') i18n/no-chinese-hardcode - 201:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "绯荤粺榛樿缁撶畻甯佺"銆傚缓璁娇鐢? tc('text_kra9ls') i18n/no-chinese-hardcode - 204:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鎴愭湰璁′环鏂规硶"銆傚缓璁娇鐢? tc('text_j334ni') i18n/no-chinese-hardcode - 208:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "moving_avg=绉诲姩骞冲潎锛宖ifo=鍏堣繘鍏堝嚭锛寃eighted=鍔犳潈骞冲潎"銆傚缓璁娇鐢? tc('text_dtugo') i18n/no-chinese-hardcode - 211:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "绋庣巼"銆傚缓璁娇鐢? tc('text_le7t') i18n/no-chinese-hardcode - 215:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "榛樿澧炲€肩◣绋庣巼锛?锛?銆傚缓璁娇鐢? tc('text_ntrb3l') i18n/no-chinese-hardcode - 218:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍚敤澶氬竵绉?銆傚缓璁娇鐢? tc('text_auh243') i18n/no-chinese-hardcode - 222:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏄惁鍚敤澶氬竵绉嶆牳绠?銆傚缓璁娇鐢? tc('text_3coygl') i18n/no-chinese-hardcode - 225:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "绯荤粺鍚嶇О"銆傚缓璁娇鐢? tc('text_gahjnr') i18n/no-chinese-hardcode - 229:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "绯荤粺鐧诲綍椤典笌鏍囬鏄剧ず鐨勫悕绉?銆傚缓璁娇鐢? tc('text_mv4eu5') i18n/no-chinese-hardcode - 232:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "榛樿璇█"銆傚缓璁娇鐢? tc('text_kmdu9r') i18n/no-chinese-hardcode - 236:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏂扮敤鎴烽粯璁よ瑷€"銆傚缓璁娇鐢? tc('text_cys8ci') i18n/no-chinese-hardcode - 239:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "瀵嗙爜鏈€灏忛暱搴?銆傚缓璁娇鐢? tc('text_scr9ox') i18n/no-chinese-hardcode - 243:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鐢ㄦ埛瀵嗙爜鏈€灏忛暱搴﹁姹?銆傚缓璁娇鐢? tc('text_skskn5') i18n/no-chinese-hardcode - 246:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鐧诲綍澶辫触閿佸畾娆℃暟"銆傚缓璁娇鐢? tc('text_42f9l6') i18n/no-chinese-hardcode - 250:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "杩炵画鐧诲綍澶辫触澶氬皯娆″悗閿佸畾璐﹀彿"銆傚缓璁娇鐢? tc('text_ac40x7') i18n/no-chinese-hardcode - 253:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "浼氳瘽瓒呮椂"銆傚缓璁娇鐢? tc('text_akc8yc') i18n/no-chinese-hardcode - 257:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鐢ㄦ埛浼氳瘽瓒呮椂鏃堕棿锛堝垎閽燂級"銆傚缓璁娇鐢? tc('text_5ysclz') i18n/no-chinese-hardcode - 260:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鎿嶄綔鏃ュ織淇濈暀澶╂暟"銆傚缓璁娇鐢? tc('text_zb9d6k') i18n/no-chinese-hardcode - 264:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鎿嶄綔鏃ュ織淇濈暀澶╂暟锛?琛ㄧず姘镐箙淇濈暀"銆傚缓璁娇鐢? tc('text_69kt43') i18n/no-chinese-hardcode - 267:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍏徃鍚嶇О"銆傚缓璁娇鐢? tc('text_amf4wf') i18n/no-chinese-hardcode - 269:19 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏌愭煇绉戞妧鏈夐檺鍏徃"銆傚缓璁娇鐢? tc('text_6ye96q') i18n/no-chinese-hardcode - 271:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鎵撳嵃鍗曟嵁涓庢姤琛ㄦ姮澶存樉绀虹殑鍏徃鍚嶇О"銆傚缓璁娇鐢? tc('text_5wmc50') i18n/no-chinese-hardcode - 274:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "榛樿鍒嗛〉澶у皬"銆傚缓璁娇鐢? tc('text_7bb6yl') i18n/no-chinese-hardcode - 278:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍒楄〃椤甸粯璁ゆ瘡椤垫樉绀烘潯鏁?銆傚缓璁娇鐢? tc('text_p6eu1t') i18n/no-chinese-hardcode - 281:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍚敤閭欢閫氱煡"銆傚缓璁娇鐢? tc('text_pv5iek') i18n/no-chinese-hardcode - 285:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "涓氬姟浜嬩欢鏄惁鍙戦€侀偖浠堕€氱煡"銆傚缓璁娇鐢? tc('text_w2fuwk') i18n/no-chinese-hardcode - 288:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "瀵煎嚭鏂囦欢缂栫爜"銆傚缓璁娇鐢? tc('text_nz3o08') i18n/no-chinese-hardcode - 292:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏁版嵁瀵煎嚭鏂囦欢鐨勫瓧绗︾紪鐮?銆傚缓璁娇鐢? tc('text_970hfx') i18n/no-chinese-hardcode - 298:19 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "CREATE TABLE IF NOT EXISTS sys_config ( - id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, - config_name VARCHAR(100) NOT NULL COMMENT '鍙傛暟鍚嶇О', - config_key VARCHAR(100) NOT NULL COMMENT '鍙傛暟閿悕', - config_value VARCHAR(500) NOT NULL COMMENT '鍙傛暟閿€?, - config_type TINYINT DEFAULT 1 COMMENT '鍙傛暟绫诲瀷: 1-鏂囨湰, 2-寮€鍏?, - description VARCHAR(500) COMMENT '鎻忚堪', - remark VARCHAR(500) COMMENT '澶囨敞', - create_time DATETIME DEFAULT CURRENT_TIMESTAMP, - update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - deleted TINYINT DEFAULT 0, - PRIMARY KEY (id), - UNIQUE KEY uk_config_key (config_key) - ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='绯荤粺鍙傛暟閰嶇疆琛?"銆傚缓璁娇鐢? tc(`text_6j3g8p`) i18n/no-chinese-hardcode - 321:17 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 350:19 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 360:22 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 363:17 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - -D:\dcprint\erp-project\src\app\api\system\config\update\route.ts - 12:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "缂哄皯 config_key"銆傚缓璁娇鐢? tc('text_dyieff') i18n/no-chinese-hardcode - 34:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "閰嶇疆椤逛笉瀛樺湪"銆傚缓璁娇鐢? tc('text_gqdd51') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\system\currency\route.ts - 46:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "甯佺浠g爜宸插瓨鍦?銆傚缓璁娇鐢? tc('text_aohfo8') i18n/no-chinese-hardcode - 85:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "甯佺鍚嶇О涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_vpi8zl') i18n/no-chinese-hardcode - 130:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "璇ュ竵绉嶅凡琚眹鐜囪褰曟垨鍏徃鏈綅甯佸紩鐢紝鏃犳硶鍒犻櫎"銆傚缓璁娇鐢? tc('text_ijv6xn') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\system\data-fix\route.ts - 33:30 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鏈煡鐨勪慨澶嶇被鍨?銆傚缓璁娇鐢? tc('text_u9ysqw') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\system\data-scope\route.ts - 12:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "瑙掕壊ID涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_bmvo1h') i18n/no-chinese-hardcode - 42:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "瑙掕壊ID涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_bmvo1h') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\system\dict\route.ts - 93:30 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "瀛楀吀绫诲瀷缂栫爜宸插瓨鍦?銆傚缓璁娇鐢? tc('text_dfubdy') i18n/no-chinese-hardcode - 116:30 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "瀛楀吀绫诲瀷涓嶅瓨鍦?銆傚缓璁娇鐢? tc('text_am0110') i18n/no-chinese-hardcode - 135:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鏃犳晥鐨勬搷浣滅被鍨?銆傚缓璁娇鐢? tc('text_obzmdh') i18n/no-chinese-hardcode - 147:42 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "ID涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_p1r591') i18n/no-chinese-hardcode - 168:54 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "娌℃湁闇€瑕佹洿鏂扮殑瀛楁"銆傚缓璁娇鐢? tc('text_nyjsyr') i18n/no-chinese-hardcode - 179:42 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "ID涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_p1r591') i18n/no-chinese-hardcode - 202:54 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "娌℃湁闇€瑕佹洿鏂扮殑瀛楁"銆傚缓璁娇鐢? tc('text_nyjsyr') i18n/no-chinese-hardcode - 212:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鏃犳晥鐨勬搷浣滅被鍨?銆傚缓璁娇鐢? tc('text_obzmdh') i18n/no-chinese-hardcode - 224:35 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "ID涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_p1r591') i18n/no-chinese-hardcode - 245:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鏃犳晥鐨勬搷浣滅被鍨?銆傚缓璁娇鐢? tc('text_obzmdh') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\system\exchange-rate\route.ts - 2:29 warning 'query' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 85:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "婧愬竵绉嶅拰鐩爣甯佺涓嶈兘鐩稿悓"銆傚缓璁娇鐢? tc('text_lm3rgn') i18n/no-chinese-hardcode - 90:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "姹囩巼蹇呴』澶т簬 0"銆傚缓璁娇鐢? tc('text_7lddl9') i18n/no-chinese-hardcode - 97:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "婧愬竵绉嶄笉瀛樺湪"銆傚缓璁娇鐢? tc('text_4gaish') i18n/no-chinese-hardcode - 103:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鐩爣甯佺涓嶅瓨鍦?銆傚缓璁娇鐢? tc('text_f4u1h4') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\system\monitor\deadlock\route.ts - 17:30 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "缂哄皯浜嬪姟ID (trxId)"銆傚缓璁娇鐢? tc('text_tnzdhg') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\system\monitor\route.ts - 120:24 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "澶?銆傚缓璁娇鐢? tc(`text_hm1`) i18n/no-chinese-hardcode - 120:24 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "灏忔椂"銆傚缓璁娇鐢? tc(`text_g7uv`) i18n/no-chinese-hardcode - 120:24 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍒嗛挓"銆傚缓璁娇鐢? tc(`text_ermh`) i18n/no-chinese-hardcode - 121:25 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "灏忔椂"銆傚缓璁娇鐢? tc(`text_g7uv`) i18n/no-chinese-hardcode - 121:25 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍒嗛挓"銆傚缓璁娇鐢? tc(`text_ermh`) i18n/no-chinese-hardcode - 122:10 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍒嗛挓"銆傚缓璁娇鐢? tc(`text_ermh`) i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\system\oper-log\route.ts - 154:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "涓嶆敮鎸佺殑鎿嶄綔"銆傚缓璁娇鐢? tc('text_47gje4') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\system\profile\password\route.ts - 19:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "璇峰~鍐欏畬鏁村瘑鐮佷俊鎭?銆傚缓璁娇鐢? tc('text_xdam3a') i18n/no-chinese-hardcode - 23:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鏂板瘑鐮侀暱搴︿笉鑳藉皯浜?浣?銆傚缓璁娇鐢? tc('text_d9jrjq') i18n/no-chinese-hardcode - 30:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "瀵嗙爜蹇呴』鍖呭惈瀛楁瘝鍜屾暟瀛?銆傚缓璁娇鐢? tc('text_x0wazq') i18n/no-chinese-hardcode - 39:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鐢ㄦ埛涓嶅瓨鍦?銆傚缓璁娇鐢? tc('text_qf4xqq') i18n/no-chinese-hardcode - 44:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "褰撳墠瀵嗙爜涓嶆纭?銆傚缓璁娇鐢? tc('text_l7bb5v') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\system\profile\route.ts - 24:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鐢ㄦ埛涓嶅瓨鍦?銆傚缓璁娇鐢? tc('text_qf4xqq') i18n/no-chinese-hardcode - 54:30 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "璇峰~鍐欏畬鏁村瘑鐮佷俊鎭?銆傚缓璁娇鐢? tc('text_xdam3a') i18n/no-chinese-hardcode - 58:30 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鏂板瘑鐮侀暱搴︿笉鑳藉皯浜?浣?銆傚缓璁娇鐢? tc('text_d9jrjq') i18n/no-chinese-hardcode - 66:30 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鐢ㄦ埛涓嶅瓨鍦?銆傚缓璁娇鐢? tc('text_qf4xqq') i18n/no-chinese-hardcode - 71:30 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "褰撳墠瀵嗙爜涓嶆纭?銆傚缓璁娇鐢? tc('text_l7bb5v') i18n/no-chinese-hardcode - 102:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "娌℃湁闇€瑕佹洿鏂扮殑瀛楁"銆傚缓璁娇鐢? tc('text_nyjsyr') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\system\scheduler\route.ts - 89:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍙傛暟涓嶅畬鏁?銆傚缓璁娇鐢? tc('text_ejeut5') i18n/no-chinese-hardcode - 106:30 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "浠诲姟涓嶅瓨鍦?銆傚缓璁娇鐢? tc('text_zfu7kn') i18n/no-chinese-hardcode - 173:30 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "娌℃湁闇€瑕佹洿鏂扮殑瀛楁"銆傚缓璁娇鐢? tc('text_nyjsyr') i18n/no-chinese-hardcode - 181:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "涓嶆敮鎸佺殑鎿嶄綔"銆傚缓璁娇鐢? tc('text_47gje4') i18n/no-chinese-hardcode - 191:35 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "浠诲姟ID涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_8l3tpt') i18n/no-chinese-hardcode - 208:10 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "搴撳瓨棰勮妫€鏌ュ畬鎴愶紝鍙戠幇 "銆傚缓璁娇鐢? tc(`text_nymtyd`) i18n/no-chinese-hardcode - 208:10 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? " 鏉′綆搴撳瓨璁板綍"銆傚缓璁娇鐢? tc(`text_v8ozpl`) i18n/no-chinese-hardcode - 226:10 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏁版嵁娓呯悊瀹屾垚锛屽垹闄?"銆傚缓璁娇鐢? tc(`text_vmtlf7`) i18n/no-chinese-hardcode - 226:10 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? " 鏉¤繃鏈熸棩蹇?銆傚缓璁娇鐢? tc(`text_bqqgej`) i18n/no-chinese-hardcode - 229:40 warning 'config' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars - 230:10 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鎶ヨ〃鐢熸垚浠诲姟宸茶Е鍙戯紙寮傛鎵ц涓級"銆傚缓璁娇鐢? tc('text_6w0nzd') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\system\user\fix-names\route.ts - 10:11 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "寮犺揪鏄?銆傚缓璁娇鐢? tc('text_emlny') i18n/no-chinese-hardcode - 11:16 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鎬荤粡鍔?銆傚缓璁娇鐢? tc('text_epfze') i18n/no-chinese-hardcode - 12:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鎬荤粡鐞?銆傚缓璁娇鐢? tc('text_epmky') i18n/no-chinese-hardcode - 17:11 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏉庤鏀?銆傚缓璁娇鐢? tc('text_fsgn5') i18n/no-chinese-hardcode - 18:16 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "琛屾斂閮?銆傚缓璁娇鐢? tc('text_kgpg5') i18n/no-chinese-hardcode - 19:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "琛屾斂缁忕悊"銆傚缓璁娇鐢? tc('text_hmbgzu') i18n/no-chinese-hardcode - 24:11 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鐜嬭储鍔?銆傚缓璁娇鐢? tc('text_hlpqi') i18n/no-chinese-hardcode - 25:16 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "璐㈠姟閮?銆傚缓璁娇鐢? tc('text_l31ft') i18n/no-chinese-hardcode - 26:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "璐㈠姟缁忕悊"銆傚缓璁娇鐢? tc('text_i5jspi') i18n/no-chinese-hardcode - 31:11 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "璧典汉浜?銆傚缓璁娇鐢? tc('text_l3pza') i18n/no-chinese-hardcode - 32:16 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "浜哄姏璧勬簮閮?銆傚缓璁娇鐢? tc('text_ypco2j') i18n/no-chinese-hardcode - 33:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "浜轰簨缁忕悊"銆傚缓璁娇鐢? tc('text_a9l6nc') i18n/no-chinese-hardcode - 38:11 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "閽遍攢鍞?銆傚缓璁娇鐢? tc('text_mhuvz') i18n/no-chinese-hardcode - 39:16 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "閿€鍞儴"銆傚缓璁娇鐢? tc('text_m8ygq') i18n/no-chinese-hardcode - 40:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "閿€鍞粡鐞?銆傚缓璁娇鐢? tc('text_j5n8hx') i18n/no-chinese-hardcode - 45:11 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "瀛欑敓浜?銆傚缓璁娇鐢? tc('text_dy0zl') i18n/no-chinese-hardcode - 46:16 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鐢熶骇閮?銆傚缓璁娇鐢? tc('text_hjr0g') i18n/no-chinese-hardcode - 47:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鐢熶骇缁忕悊"銆傚缓璁娇鐢? tc('text_f3xthb') i18n/no-chinese-hardcode - 52:11 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍛ㄦ妧鏈?銆傚缓璁娇鐢? tc('text_cue53') i18n/no-chinese-hardcode - 53:16 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鎶€鏈儴"銆傚缓璁娇鐢? tc('text_exqft') i18n/no-chinese-hardcode - 54:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鎶€鏈粡鐞?銆傚缓璁娇鐢? tc('text_cuzbpi') i18n/no-chinese-hardcode - 59:11 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍚村搧璐?銆傚缓璁娇鐢? tc('text_cr6wr') i18n/no-chinese-hardcode - 60:16 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍝佽川閮?銆傚缓璁娇鐢? tc('text_d3pkx') i18n/no-chinese-hardcode - 61:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍝佽川缁忕悊"銆傚缓璁娇鐢? tc('text_ba4l3y') i18n/no-chinese-hardcode - 66:11 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "閮戦噰璐?銆傚缓璁娇鐢? tc('text_lx6mv') i18n/no-chinese-hardcode - 67:16 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "閲囪喘閮?銆傚缓璁娇鐢? tc('text_m1hlu') i18n/no-chinese-hardcode - 68:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "閲囪喘缁忕悊"銆傚缓璁娇鐢? tc('text_iz7pwd') i18n/no-chinese-hardcode - 73:11 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "闄堜粨鍌?銆傚缓璁娇鐢? tc('text_mea2l') i18n/no-chinese-hardcode - 74:16 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "浠撳偍閮?銆傚缓璁娇鐢? tc('text_by5hv') i18n/no-chinese-hardcode - 75:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "浠撳簱涓荤"銆傚缓璁娇鐢? tc('text_ac5giu') i18n/no-chinese-hardcode - 80:11 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏋楀嵃鍒?銆傚缓璁娇鐢? tc('text_fkvby') i18n/no-chinese-hardcode - 81:16 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍗板埛杞﹂棿"銆傚缓璁娇鐢? tc('text_avlm51') i18n/no-chinese-hardcode - 82:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍗板埛鏈洪暱"銆傚缓璁娇鐢? tc('text_aves24') i18n/no-chinese-hardcode - 87:11 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "榛勫悗閬?銆傚缓璁娇鐢? tc('text_no98p') i18n/no-chinese-hardcode - 88:16 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍚庨亾杞﹂棿"銆傚缓璁娇鐢? tc('text_b7i043') i18n/no-chinese-hardcode - 89:15 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍚庨亾缁勯暱"銆傚缓璁娇鐢? tc('text_b7f668') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\system\workflow\route.ts - 8:7 warning 'workflowEngine' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 53:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "娴佺▼鍚嶇О鍜屾ā鍧楃被鍨嬩笉鑳戒负绌?銆傚缓璁娇鐢? tc('text_vae26t') i18n/no-chinese-hardcode - 57:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鑷冲皯闇€瑕侀厤缃竴涓鎵硅妭鐐?銆傚缓璁娇鐢? tc('text_a8m4tj') i18n/no-chinese-hardcode - 107:64 warning 'user' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars - 112:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "娴佺▼ID涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_7qd0q3') i18n/no-chinese-hardcode - 174:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "娴佺▼ID涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_7qd0q3') i18n/no-chinese-hardcode - 184:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "璇ユ祦绋嬫湁姝e湪杩涜鐨勫鎵瑰疄渚嬶紝鏃犳硶鍒犻櫎"銆傚缓璁娇鐢? tc('text_ndsok') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\upload\sop\route.ts - 29:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鏈壘鍒颁笂浼犵殑鏂囦欢"銆傚缓璁娇鐢? tc('text_zaz5jz') i18n/no-chinese-hardcode - 36:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍙兘涓婁紶PDF鏂囦欢"銆傚缓璁娇鐢? tc('text_ffavdk') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\warehouse\alert-push\route.ts - 70:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鏃犳晥鐨勬煡璇㈢被鍨?銆傚缓璁娇鐢? tc('text_np2rrr') i18n/no-chinese-hardcode - 99:30 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "瑙勫垯鍚嶇О銆侀璀︾被鍨嬪拰闃堝€间笉鑳戒负绌?銆傚缓璁娇鐢? tc('text_5hq3zp') i18n/no-chinese-hardcode - 125:30 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "閫氱煡ID鍒楄〃涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_22nj2x') i18n/no-chinese-hardcode - 134:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鏃犳晥鐨勬搷浣滅被鍨?銆傚缓璁娇鐢? tc('text_obzmdh') i18n/no-chinese-hardcode - 146:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "瑙勫垯ID涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_soue68') i18n/no-chinese-hardcode - 178:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "娌℃湁闇€瑕佹洿鏂扮殑瀛楁"銆傚缓璁娇鐢? tc('text_nyjsyr') i18n/no-chinese-hardcode - 204:47 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "褰撳墠鏃犲簱瀛橀璀?銆傚缓璁娇鐢? tc('text_dh7pir') i18n/no-chinese-hardcode - 217:19 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "搴撳瓨棰勮锛?銆傚缓璁娇鐢? tc(`text_qsf4xf`) i18n/no-chinese-hardcode - 217:19 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? " 搴撳瓨涓嶈冻"銆傚缓璁娇鐢? tc(`text_cssfuz`) i18n/no-chinese-hardcode - 218:21 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鐗╂枡 "銆傚缓璁娇鐢? tc(`text_h8kb4`) i18n/no-chinese-hardcode - 218:21 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? ") 鍦ㄤ粨搴?"銆傚缓璁娇鐢? tc(`text_v86elr`) i18n/no-chinese-hardcode - 218:21 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? " 鐨勫綋鍓嶅簱瀛樹负 "銆傚缓璁娇鐢? tc(`text_ho09pz`) i18n/no-chinese-hardcode - 218:21 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "锛屼綆浜庡畨鍏ㄥ簱瀛?"銆傚缓璁娇鐢? tc(`text_upftcg`) i18n/no-chinese-hardcode - 218:21 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "锛岃鍙婃椂琛ヨ揣銆?銆傚缓璁娇鐢? tc(`text_w7gr55`) i18n/no-chinese-hardcode - 243:5 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "宸叉帹閫?"銆傚缓璁娇鐢? tc(`text_c9uhth`) i18n/no-chinese-hardcode - 243:5 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? " 鏉″簱瀛橀璀?銆傚缓璁娇鐢? tc(`text_5hucjs`) i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\warehouse\batch-inventory\route.ts - 69:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鐗╂枡ID涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_1njsw5') i18n/no-chinese-hardcode - 127:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍏ュ簱鏄庣粏涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_tpjaom') i18n/no-chinese-hardcode - 243:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍑哄簱鏄庣粏涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_8kbfq7') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\warehouse\batch\route.ts - 169:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鎵规ID涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_c8hhp') i18n/no-chinese-hardcode - 187:30 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鎵规涓嶅瓨鍦?銆傚缓璁娇鐢? tc('text_rbfprf') i18n/no-chinese-hardcode - 216:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "娌℃湁闇€瑕佹洿鏂扮殑瀛楁"銆傚缓璁娇鐢? tc('text_nyjsyr') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\warehouse\batch\trace\route.ts - 13:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "batchNo 涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_s4piff') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\warehouse\cost\route.ts - 93:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鐗╂枡ID涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_1njsw5') i18n/no-chinese-hardcode - 108:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "娌℃湁鎵惧埌鍏ュ簱璁板綍锛屾棤娉曡绠楁垚鏈?銆傚缓璁娇鐢? tc('text_wthvmi') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\warehouse\fifo-recommend\route.ts - 58:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "缂哄皯蹇呭~鍙傛暟: materialId, warehouseId"銆傚缓璁娇鐢? tc('text_twwr9z') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\warehouse\freeze\route.ts - 90:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鐗╂枡ID銆佷粨搴揑D鍜屽喕缁撴暟閲忎笉鑳戒负绌?銆傚缓璁娇鐢? tc('text_c9syla') i18n/no-chinese-hardcode - 95:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍐荤粨鏁伴噺蹇呴』澶т簬0"銆傚缓璁娇鐢? tc('text_iat7es') i18n/no-chinese-hardcode - 110:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "搴撳瓨璁板綍涓嶅瓨鍦?銆傚缓璁娇鐢? tc('text_oxyf59') i18n/no-chinese-hardcode - 170:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍐荤粨璁板綍ID涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_79nf94') i18n/no-chinese-hardcode - 183:30 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍐荤粨璁板綍涓嶅瓨鍦ㄦ垨宸茶В鍐?銆傚缓璁娇鐢? tc('text_vjo5dg') i18n/no-chinese-hardcode - 219:30 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "瑙e喕鏁伴噺蹇呴』澶т簬0"銆傚缓璁娇鐢? tc('text_vcibus') i18n/no-chinese-hardcode - 230:30 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍐荤粨璁板綍涓嶅瓨鍦ㄦ垨宸茶В鍐?銆傚缓璁娇鐢? tc('text_vjo5dg') i18n/no-chinese-hardcode - 239:30 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "瑙e喕鏁伴噺涓嶈兘瓒呰繃鍐荤粨鏁伴噺"銆傚缓璁娇鐢? tc('text_p1tujk') i18n/no-chinese-hardcode - 278:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鏃犳晥鐨勬搷浣滅被鍨?銆傚缓璁娇鐢? tc('text_obzmdh') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\warehouse\inbound\audit\route.ts - 5:24 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "姝ゆ帴鍙e凡搴熷純锛岃浣跨敤 PUT /api/warehouse/inbound?action=approve"銆傚缓璁娇鐢? tc('text_y8ehcd') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\warehouse\inbound\cutting\route.ts - 109:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "缂哄皯蹇呭~瀛楁: cutWidthStr"銆傚缓璁娇鐢? tc('text_bmdj64') i18n/no-chinese-hardcode - 184:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "璇ユ爣绛惧凡缁忚繃鍒嗗垏锛屼笉鑳介噸澶嶅垎鍒?銆傚缓璁娇鐢? tc('text_vzcqeq') i18n/no-chinese-hardcode - 192:11 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍒嗗垏瀹藉箙鏍煎紡涓嶆纭紝璇蜂娇鐢ㄦ暟瀛?鏁板瓧鐨勬牸寮忥紝濡傦細300+400+300"銆傚缓璁娇鐢? tc('text_r88uv0') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\warehouse\inbound\from-po\route.ts - 28:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "閲囪喘鍗旾D涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_b0zj96') i18n/no-chinese-hardcode - 31:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "浠撳簱ID涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_ezwjlh') i18n/no-chinese-hardcode - 34:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍏ュ簱鏄庣粏涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_tpjaom') i18n/no-chinese-hardcode - 40:11 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍏ュ簱鏄庣粏缂哄皯蹇呭~瀛楁(line_no/material_id/material_name/batch_no)"銆傚缓璁娇鐢? tc('text_47dgep') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\warehouse\inbound\labels\route.ts - 310:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "宸蹭娇鐢ㄧ殑鏍囩涓嶈兘鍒犻櫎"銆傚缓璁娇鐢? tc('text_jzhjv8') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\warehouse\inbound\route.ts - 180:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鏈煡鎿嶄綔"銆傚缓璁娇鐢? tc('text_dih7qi') i18n/no-chinese-hardcode - 203:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍏ュ簱鍗旾D涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_nxs5v6') i18n/no-chinese-hardcode - 230:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍏ュ簱鍗旾D涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_nxs5v6') i18n/no-chinese-hardcode - 242:30 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍏ュ簱鍗曚笉瀛樺湪鎴栨湭琚垹闄?銆傚缓璁娇鐢? tc('text_yj5nf') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\warehouse\inbound\scan\route.ts - 11:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "浜岀淮鐮佸唴瀹逛笉鑳戒负绌?銆傚缓璁娇鐢? tc('text_eg437h') i18n/no-chinese-hardcode - 26:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鏈壘鍒板搴旂殑鍏ュ簱璁板綍"銆傚缓璁娇鐢? tc('text_gxji80') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\warehouse\inbound\with-po\route.ts - 5:24 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "姝ゆ帴鍙e凡搴熷純锛岃浣跨敤 POST /api/warehouse/inbound/from-po"銆傚缓璁娇鐢? tc('text_atrejo') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\warehouse\ink-mixing\route.ts - 64:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "缂哄皯蹇呭~瀛楁: total_qty, mixed_date, details"銆傚缓璁娇鐢? tc('text_9kaoz6') i18n/no-chinese-hardcode - 169:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "娣峰悎鎵规ID涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_3pgq5g') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\warehouse\ink-opening\route.ts - 64:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "缂哄皯蹇呭~瀛楁: material_id, open_time, expire_hours"銆傚缓璁娇鐢? tc('text_yuowjl') i18n/no-chinese-hardcode - 114:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "寮€缃愯褰旾D涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_9i0nq8') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\warehouse\inventory\adjust\route.ts - 22:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "缂哄皯蹇呰鍙傛暟: materialId, warehouseId, quantity, businessNo"銆傚缓璁娇鐢? tc('text_lx51xx') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\warehouse\outbound\confirm\route.ts - 18:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍑哄簱鍗旾D涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_q1vpa1') i18n/no-chinese-hardcode - 237:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍑哄簱鍗旾D涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_q1vpa1') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\warehouse\outbound\fifo\route.ts - 107:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "materialId 鍜?warehouseId 涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_j2lp8k') i18n/no-chinese-hardcode - 185:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "warehouseId 鍜?items 涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_41fiue') i18n/no-chinese-hardcode - 203:65 warning 'unit' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 356:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "orderId 涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_jm8sah') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\warehouse\production-inbound\route.ts - 69:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "浠撳簱ID涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_ezwjlh') i18n/no-chinese-hardcode - 72:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍏ュ簱鏄庣粏涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_tpjaom') i18n/no-chinese-hardcode - 142:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍏ュ簱鍗旾D涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_nxs5v6') i18n/no-chinese-hardcode - 335:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "璐ㄦ缁撴灉涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_jhhfov') i18n/no-chinese-hardcode - 409:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍏ュ簱鍗曚笉瀛樺湪"銆傚缓璁娇鐢? tc('text_hy337a') i18n/no-chinese-hardcode - 412:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "宸插畬鎴愮殑鍏ュ簱鍗曚笉鑳藉垹闄?銆傚缓璁娇鐢? tc('text_d8w0zx') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\warehouse\route.ts - 199:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "浠撳簱缂栫爜宸插瓨鍦?銆傚缓璁娇鐢? tc('text_opfziv') i18n/no-chinese-hardcode - 270:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "浠撳簱缂栫爜宸插瓨鍦?銆傚缓璁娇鐢? tc('text_opfziv') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\warehouse\sales-outbound\route.ts - 68:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "浠撳簱ID涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_ezwjlh') i18n/no-chinese-hardcode - 71:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍑哄簱鏄庣粏涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_8kbfq7') i18n/no-chinese-hardcode - 162:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍑哄簱鍗旾D涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_q1vpa1') i18n/no-chinese-hardcode - 420:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍑哄簱鍗曚笉瀛樺湪"銆傚缓璁娇鐢? tc('text_hgopz5') i18n/no-chinese-hardcode - 423:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "宸插畬鎴愮殑鍑哄簱鍗曚笉鑳藉垹闄?銆傚缓璁娇鐢? tc('text_1qhd0e') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\warehouse\stock-adjust\route.ts - 84:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "缂哄皯 expectedStatus 鍙傛暟锛堝綋鍓嶇姸鎬侊級"銆傚缓璁娇鐢? tc('text_sp7txl') i18n/no-chinese-hardcode - 114:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "骞跺彂鍐茬獊: 璋冩暣鍗曠姸鎬佸凡琚叾浠栨搷浣滃彉鏇达紝璇峰埛鏂板悗閲嶈瘯"銆傚缓璁娇鐢? tc('text_z8harz') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\warehouse\stocktaking\[id]\scan\route.ts - 17:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "璇锋壂鎻忎簩缁寸爜"銆傚缓璁娇鐢? tc('text_v8iiwu') i18n/no-chinese-hardcode - 21:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "璇疯緭鍏ュ疄闄呮暟閲?銆傚缓璁娇鐢? tc('text_lcknbl') i18n/no-chinese-hardcode - 46:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鏈壘鍒板搴旂殑搴撳瓨璁板綍"銆傚缓璁娇鐢? tc('text_fa65pl') i18n/no-chinese-hardcode - 56:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "璇ョ墿鏂欎笉鍦ㄦ湰娆$洏鐐硅寖鍥村唴"銆傚缓璁娇鐢? tc('text_pdh1pu') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\warehouse\stocktaking\[id]\split-summary\route.ts - 14:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "缂哄皯鐖朵簩缁寸爜鍙傛暟"銆傚缓璁娇鐢? tc('text_3xch5k') i18n/no-chinese-hardcode - 38:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鏈壘鍒版暣鏂欑洏鐐硅褰?銆傚缓璁娇鐢? tc('text_fagnsp') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\warehouse\stocktaking\diff-process\route.ts - 89:30 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "璇烽€夋嫨闇€瑕佸鎵圭殑宸紓椤?銆傚缓璁娇鐢? tc('text_vgs6pb') i18n/no-chinese-hardcode - 116:30 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "璇烽€夋嫨闇€瑕佸鐞嗙殑宸紓椤?銆傚缓璁娇鐢? tc('text_4iyhej') i18n/no-chinese-hardcode - 181:30 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "璇烽€夋嫨闇€瑕侀┏鍥炵殑宸紓椤?銆傚缓璁娇鐢? tc('text_8aba04') i18n/no-chinese-hardcode - 196:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鏃犳晥鐨勬搷浣滅被鍨?銆傚缓璁娇鐢? tc('text_obzmdh') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\warehouse\stocktaking\route.ts - 8:11 warning 'InventoryCheck' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 26:11 warning 'InventoryCheckItem' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 215:13 warning 'diffStats' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - -D:\dcprint\erp-project\src\app\api\warehouse\transfer\[id]\inbound\route.ts - 15:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "缂哄皯鍏ュ簱鏄庣粏鏁版嵁"銆傚缓璁娇鐢? tc('text_fbntut') i18n/no-chinese-hardcode - 47:30 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "姣忛」蹇呴』鎻愪緵浜岀淮鐮佹垨鐗╂枡ID"銆傚缓璁娇鐢? tc('text_oynvhv') i18n/no-chinese-hardcode - 51:30 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍏ュ簱鏁伴噺蹇呴』澶т簬0"銆傚缓璁娇鐢? tc('text_czy4m2') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\warehouse\transfer\[id]\outbound\route.ts - 14:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "缂哄皯鍑哄簱鏄庣粏鏁版嵁"銆傚缓璁娇鐢? tc('text_ft272y') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\warehouse\transfer\route.ts - 8:11 warning 'Transfer' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 26:11 warning 'TransferItem' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 111:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "璇烽€夋嫨璋冨嚭浠撳簱"銆傚缓璁娇鐢? tc('text_x8dg2a') i18n/no-chinese-hardcode - 115:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "璇烽€夋嫨璋冨叆浠撳簱"銆傚缓璁娇鐢? tc('text_x8gijr') i18n/no-chinese-hardcode - 119:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "璋冩嫧绫诲瀷鏃犳晥锛?=搴撲綅璋冩嫧锛?=浠撳簱璋冩嫧锛?銆傚缓璁娇鐢? tc('text_pbxlwj') i18n/no-chinese-hardcode - 123:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "搴撲綅璋冩嫧蹇呴』鍦ㄥ悓涓€浠撳簱鍐呰繘琛?銆傚缓璁娇鐢? tc('text_x0p9kh') i18n/no-chinese-hardcode - 127:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "璇烽€夋嫨璋冨嚭搴撲綅"銆傚缓璁娇鐢? tc('text_x8aumw') i18n/no-chinese-hardcode - 131:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "璇烽€夋嫨璋冨叆搴撲綅"銆傚缓璁娇鐢? tc('text_x8dx4d') i18n/no-chinese-hardcode - 197:30 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍙湁鑽夌鐘舵€佺殑璋冩嫧鍗曟墠鑳芥彁浜ゅ鎵?銆傚缓璁娇鐢? tc('text_uc5qh4') i18n/no-chinese-hardcode - 206:30 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍙湁寰呭鎵圭殑璋冩嫧鍗曟墠鑳藉鎵?銆傚缓璁娇鐢? tc('text_6h068e') i18n/no-chinese-hardcode - 210:30 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "璇锋寚瀹氬鎵逛汉"銆傚缓璁娇鐢? tc('text_wmsst4') i18n/no-chinese-hardcode - 224:30 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍙湁寰呭鎵圭殑璋冩嫧鍗曟墠鑳介┏鍥?銆傚缓璁娇鐢? tc('text_6gpjuj') i18n/no-chinese-hardcode - 235:30 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍙兘鍙栨秷鑽夌鎴栧緟瀹℃壒鐨勮皟鎷ㄥ崟"銆傚缓璁娇鐢? tc('text_yaxiem') i18n/no-chinese-hardcode - 245:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鏃犳晥鐨勬搷浣滅被鍨?銆傚缓璁娇鐢? tc('text_obzmdh') i18n/no-chinese-hardcode - 254:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "缂哄皯ID鍙傛暟"銆傚缓璁娇鐢? tc('text_c7g5wg') i18n/no-chinese-hardcode - 267:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍙兘鍒犻櫎鑽夌鎴栧凡鍙栨秷鐨勮皟鎷ㄥ崟"銆傚缓璁娇鐢? tc('text_346sx3') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\warehouse\unit-conversion\route.ts - 64:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鎹㈢畻姣斾緥蹇呴』澶т簬0"銆傚缓璁娇鐢? tc('text_bxnev') i18n/no-chinese-hardcode - 102:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "缂哄皯ID鍙傛暟"銆傚缓璁娇鐢? tc('text_c7g5wg') i18n/no-chinese-hardcode - 118:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鐗╂枡ID銆佹簮鍗曚綅銆佺洰鏍囧崟浣嶅拰鏁伴噺涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc('text_t1axob') i18n/no-chinese-hardcode - 128:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鏈壘鍒板搴旂殑鍗曚綅鎹㈢畻鍏崇郴"銆傚缓璁娇鐢? tc('text_msv98i') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\api\workflow\process\route.ts - 17:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "缂哄皯蹇呰鐨勫弬鏁?銆傚缓璁娇鐢? tc('text_c4uiox') i18n/no-chinese-hardcode - 21:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鏃犳晥鐨勫鎵瑰姩浣?銆傚缓璁娇鐢? tc('text_pf192w') i18n/no-chinese-hardcode - 62:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "缂哄皯瀹炰緥ID鎴栨潵婧愪俊鎭?銆傚缓璁娇鐢? tc('text_ob571s') i18n/no-chinese-hardcode - 80:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "瀹℃壒瀹炰緥涓嶅瓨鍦?銆傚缓璁娇鐢? tc('text_nrmpqw') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\app\global-error.tsx - 51:16 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "绯荤粺鍙戠敓涓ラ噸閿欒"銆傚缓璁娇鐢? {tc('text_4cy8sw')} i18n/no-chinese-hardcode - 74:72 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "閲嶈瘯"銆傚缓璁娇鐢? {tc('text_pkfc')} i18n/no-chinese-hardcode - 94:67 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "杩斿洖棣栭〉"銆傚缓璁娇鐢? {tc('text_iijhd5')} i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\application\handlers\CrossModuleSagaHandler.ts - 1:29 warning 'SagaStatus' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 1:41 warning 'SagaStepStatus' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 3:10 warning 'transaction' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - -D:\dcprint\erp-project\src\application\handlers\DeliveryCancelledHandler.ts - 34:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍙戣揣鍗曟槑缁嗗姞杞藉畬鎴?銆傚缓璁娇鐢? tc('text_u9or60') i18n/no-chinese-hardcode - 46:30 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍥炴粴璁㈠崟鏄庣粏宸插彂璐ф暟閲?銆傚缓璁娇鐢? tc('text_kuzmf4') i18n/no-chinese-hardcode - 76:34 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "璁㈠崟鐘舵€佹仮澶嶄负宸插鏍?銆傚缓璁娇鐢? tc('text_4r2pqw') i18n/no-chinese-hardcode - 82:34 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "璁㈠崟鐘舵€佹仮澶嶄负閮ㄥ垎鍙戣揣"銆傚缓璁娇鐢? tc('text_bn0wn9') i18n/no-chinese-hardcode - 102:30 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "搴旀敹鍗曡仈鍔ㄨ蒋鍒犲畬鎴?(T403)"銆傚缓璁娇鐢? tc('text_yftjcy') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\application\handlers\DeliveryReceivableHandler.ts - 8:37 warning 'orderId' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 13:24 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "璺宠繃锛氬彂璐ч噾棰濅负 0"銆傚缓璁娇鐢? tc('text_o3fhq6') i18n/no-chinese-hardcode - 22:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "寮€濮嬪鐞嗗簲鏀惰处娆惧垱寤?銆傚缓璁娇鐢? tc('text_lq8kcm') i18n/no-chinese-hardcode - 35:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鏃犻噸澶嶈褰曪紝鍑嗗鍒涘缓搴旀敹璐︽"銆傚缓璁娇鐢? tc('text_7fs6o1') i18n/no-chinese-hardcode - 46:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "INSERT fin_receivable 鍙傛暟璇︽儏"銆傚缓璁娇鐢? tc('text_f6lpxm') i18n/no-chinese-hardcode - 67:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "搴旀敹璐︽娴佺▼鎴愬姛缁撴潫"銆傚缓璁娇鐢? tc('text_k659jz') i18n/no-chinese-hardcode - 69:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "搴旀敹璐︽娴佺▼璺宠繃锛堟湭鍒涘缓锛?銆傚缓璁娇鐢? tc('text_fbkbtc') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\application\handlers\DeliveryShippedHandler.ts - 95:30 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "璁㈠崟鐘舵€佹洿鏂颁负閮ㄥ垎鍙戣揣"銆傚缓璁娇鐢? tc('text_kslod0') i18n/no-chinese-hardcode - 120:30 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "璁㈠崟鐘舵€佹洿鏂颁负鍏ㄩ儴鍙戣揣"銆傚缓璁娇鐢? tc('text_d1hegm') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\application\handlers\OutboundReceivableHandler.ts - 13:24 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "璺宠繃锛氶噾棰濅负0鎴栨棤瀹㈡埛"銆傚缓璁娇鐢? tc('text_rtrxjt') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\application\handlers\PickOrderInventoryHandler.ts - 37:22 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - -D:\dcprint\erp-project\src\application\handlers\ProductionFinanceHandler.ts - 29:25 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - -D:\dcprint\erp-project\src\application\handlers\PurchaseInboundSyncHandler.ts - 22:24 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "璺宠繃锛氭棤閲囪喘璁㈠崟鍏宠仈"銆傚缓璁娇鐢? tc('text_v28y30') i18n/no-chinese-hardcode - 41:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "閲囪喘璁㈠崟琛屽姞杞藉畬鎴?銆傚缓璁娇鐢? tc('text_pk2ip8') i18n/no-chinese-hardcode - 65:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍑嗗鏇存柊閲囪喘琛屽凡鏀堕噺"銆傚缓璁娇鐢? tc('text_j282v2') i18n/no-chinese-hardcode - 113:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "PO 鐘舵€佸喅绛?銆傚缓璁娇鐢? tc('text_jgb1zj') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\application\handlers\PurchasePayableHandler.ts - 14:24 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "璺宠繃锛氭€绘敹璐ч噾棰濅负 0"銆傚缓璁娇鐢? tc('text_1bx3ag') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\application\handlers\ReconciliationWriteOffHandler.ts - 27:24 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "璺宠繃锛氭棤鏍搁攢璁板綍"銆傚缓璁娇鐢? tc('text_x9lrjb') i18n/no-chinese-hardcode - 112:22 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "瀵硅处鏍搁攢瀹屾垚锛屽簲鏀跺崟宸插悓姝ユ洿鏂?銆傚缓璁娇鐢? tc('text_p4ld53') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\application\handlers\ReturnOrderInventoryHandler.ts - 35:24 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - -D:\dcprint\erp-project\src\application\handlers\SalesReceivableHandler.ts - 13:24 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "璺宠繃锛氬嚭搴撻噾棰濅负 0"銆傚缓璁娇鐢? tc('text_ain0n5') i18n/no-chinese-hardcode - 22:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "寮€濮嬪鐞嗗簲鏀惰处娆惧垱寤?銆傚缓璁娇鐢? tc('text_lq8kcm') i18n/no-chinese-hardcode - 35:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鏃犻噸澶嶈褰曪紝鍑嗗鍒涘缓搴旀敹璐︽"銆傚缓璁娇鐢? tc('text_7fs6o1') i18n/no-chinese-hardcode - 46:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "INSERT fin_receivable 鍙傛暟璇︽儏"銆傚缓璁娇鐢? tc('text_f6lpxm') i18n/no-chinese-hardcode - 67:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "搴旀敹璐︽娴佺▼鎴愬姛缁撴潫"銆傚缓璁娇鐢? tc('text_k659jz') i18n/no-chinese-hardcode - 69:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "搴旀敹璐︽娴佺▼璺宠繃锛堟湭鍒涘缓锛?銆傚缓璁娇鐢? tc('text_fbkbtc') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\application\handlers\SalesToWorkOrderHandler.ts - 26:64 warning 'totalAmount' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - -D:\dcprint\erp-project\src\application\handlers\SampleOrderConversionHandler.ts - 1:10 warning 'query' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 4:53 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - -D:\dcprint\erp-project\src\application\handlers\SampleOrderInventoryHandler.ts - 4:53 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - -D:\dcprint\erp-project\src\application\handlers\ScreenPlateCostHandler.ts - 56:5 warning 'completedQty' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars - -D:\dcprint\erp-project\src\application\handlers\StandardCardHandlers.ts - 34:31 warning 'event' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars - 61:25 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "閫氱煡娴佺▼寮傚父"銆傚缓璁娇鐢? tc('text_lanmol') i18n/no-chinese-hardcode - 94:25 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "瀹℃壒閫氱煡娴佺▼寮傚父"銆傚缓璁娇鐢? tc('text_f2jl9f') i18n/no-chinese-hardcode - 119:29 warning 'code' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 119:35 warning 'version' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 119:44 warning 'reason' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 119:52 warning 'userId' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 149:35 warning 'parentVersion' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 149:50 warning 'newVersion' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 149:62 warning 'code' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - -D:\dcprint\erp-project\src\application\handlers\WorkOrderCompletedHandler.ts - 32:12 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 40:12 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - -D:\dcprint\erp-project\src\application\handlers\WorkOrderMaterialIssuedHandler.ts - 19:24 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "璺宠繃锛氭棤棰嗘枡鏄庣粏"銆傚缓璁娇鐢? tc('text_vvah4r') i18n/no-chinese-hardcode - 33:16 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 82:18 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - -D:\dcprint\erp-project\src\application\services\CurrencyApplicationService.ts - 48:74 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "姹囩巼鎹㈢畻"銆傚缓璁娇鐢? tc('text_e520lx') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\application\services\DeliveryApplicationService.ts - 160:37 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - -D:\dcprint\erp-project\src\application\services\DemandForecastingService.ts - 2:17 warning 'execute' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 2:26 warning 'queryOne' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - -D:\dcprint\erp-project\src\application\services\InboundApplicationService.ts - 186:31 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - -D:\dcprint\erp-project\src\application\services\InkFormulaVersionService.ts - 28:15 warning 'ResultSetHeader' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 372:28 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 585:47 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 602:21 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? " 缂哄皯鎴愭湰"銆傚缓璁娇鐢? tc(`text_gx8vbn`) i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\application\services\OutboundApplicationService.ts - 79:30 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - -D:\dcprint\erp-project\src\application\services\ProductionApplicationService.ts - 13:32 warning 'RowDataPacket' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 176:55 warning 'workOrderNo' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars - 312:73 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - -D:\dcprint\erp-project\src\application\services\PurchaseApplicationService.ts - 206:31 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 226:5 warning 'id' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars - 227:5 warning 'lineReceives' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars - -D:\dcprint\erp-project\src\application\services\PurchaseReconciliationApplicationService.ts - 153:31 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 208:37 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - -D:\dcprint\erp-project\src\application\services\PurchaseReturnApplicationService.ts - 15:10 warning 'getSystemConfig' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 19:32 warning 'RowDataPacket' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - -D:\dcprint\erp-project\src\application\services\ReconciliationApplicationService.ts - 255:37 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - -D:\dcprint\erp-project\src\application\services\ReturnOrderApplicationService.ts - 283:37 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - -D:\dcprint\erp-project\src\application\services\SalesApplicationService.ts - 164:31 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 198:31 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - -D:\dcprint\erp-project\src\application\services\SampleOrderApplicationService.ts - 48:22 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鐢熸垚鎵撴牱鍗曞彿"銆傚缓璁娇鐢? tc('text_v36ndj') i18n/no-chinese-hardcode - 55:24 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鎵撴牱鍗曞凡淇濆瓨"銆傚缓璁娇鐢? tc('text_n6qf1g') i18n/no-chinese-hardcode - 74:22 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鏇存柊鍓嶇姸鎬?銆傚缓璁娇鐢? tc('text_agqqas') i18n/no-chinese-hardcode - 82:22 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鏇存柊鍚庣姸鎬?銆傚缓璁娇鐢? tc('text_aghhcz') i18n/no-chinese-hardcode - 104:22 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鐘舵€佹祦杞墠"銆傚缓璁娇鐢? tc('text_ywjvcn') i18n/no-chinese-hardcode - 106:22 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鐘舵€佹祦杞悗"銆傚缓璁娇鐢? tc('text_ywjvp4') i18n/no-chinese-hardcode - 126:22 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鐘舵€佹祦杞墠"銆傚缓璁娇鐢? tc('text_ywjvcn') i18n/no-chinese-hardcode - 128:22 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鐘舵€佹祦杞悗"銆傚缓璁娇鐢? tc('text_ywjvp4') i18n/no-chinese-hardcode - 148:22 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鐘舵€佹祦杞墠"銆傚缓璁娇鐢? tc('text_ywjvcn') i18n/no-chinese-hardcode - 150:22 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鐘舵€佹祦杞悗"銆傚缓璁娇鐢? tc('text_ywjvp4') i18n/no-chinese-hardcode - 170:22 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鐘舵€佹祦杞墠"銆傚缓璁娇鐢? tc('text_ywjvcn') i18n/no-chinese-hardcode - 172:22 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鐘舵€佹祦杞悗"銆傚缓璁娇鐢? tc('text_ywjvp4') i18n/no-chinese-hardcode - 313:22 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鐘舵€佹祦杞墠"銆傚缓璁娇鐢? tc('text_ywjvcn') i18n/no-chinese-hardcode - 315:22 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鐘舵€佹祦杞悗"銆傚缓璁娇鐢? tc('text_ywjvp4') i18n/no-chinese-hardcode - 336:22 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鐘舵€佹祦杞墠"銆傚缓璁娇鐢? tc('text_ywjvcn') i18n/no-chinese-hardcode - 338:22 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鐘舵€佹祦杞悗"銆傚缓璁娇鐢? tc('text_ywjvp4') i18n/no-chinese-hardcode - 360:16 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 371:22 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鎸佷箙鍖栭鍩熶簨浠?銆傚缓璁娇鐢? tc('text_oohjje') i18n/no-chinese-hardcode - 374:34 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 379:24 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "棰嗗煙浜嬩欢宸插啓鍏?Outbox锛堝閮ㄤ簨鍔★級"銆傚缓璁娇鐢? tc('text_q0rf0c') i18n/no-chinese-hardcode - 386:22 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "棰嗗煙浜嬩欢宸叉寔涔呭寲"銆傚缓璁娇鐢? tc('text_42x84c') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\application\services\SampleProcessCardService.ts - 70:3 warning 'dieToolId' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars - 71:3 warning 'screenPlateId' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars - 276:33 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 512:33 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 641:34 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 809:33 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 949:33 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - -D:\dcprint\erp-project\src\application\services\SampleProcessTemplateService.ts - 159:31 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 227:32 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - -D:\dcprint\erp-project\src\application\services\StandardCardApplicationService.ts - 223:5 warning Unused eslint-disable directive (no problems were reported from '@typescript-eslint/no-explicit-any') - -D:\dcprint\erp-project\src\application\workflow\WorkflowEngine.ts - 150:11 warning 'startNode' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 485:5 warning 'node' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars - 486:5 warning 'amount' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars - -D:\dcprint\erp-project\src\components\ClientOnly.tsx - 7:21 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\components\ClientOnly.tsx:7:21 - 5 | export function ClientOnly({ children, fallback }: { children: React.ReactNode; fallback?: React.ReactNode }) { - 6 | const [mounted, setMounted] = useState(false); -> 7 | useEffect(() => { setMounted(true); }, []); - | ^^^^^^^^^^ Avoid calling setState() directly within an effect - 8 | if (!mounted) return fallback ?? null; - 9 | return <>{children}</>; - 10 | } react-hooks/set-state-in-effect - -D:\dcprint\erp-project\src\components\GlassGauge.tsx - 42:7 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\components\GlassGauge.tsx:42:7 - 40 | useEffect(() => { - 41 | if (!animate) { -> 42 | setDisplayVal(value); - | ^^^^^^^^^^^^^ Avoid calling setState() directly within an effect - 43 | currentRef.current = value; - 44 | return; - 45 | } react-hooks/set-state-in-effect - -D:\dcprint\erp-project\src\components\GlassKnob.tsx - 119:24 warning Error: Cannot access refs during render - -React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef). - -D:\dcprint\erp-project\src\components\GlassKnob.tsx:119:24 - 117 | }, []); - 118 | -> 119 | const displayAngle = displayAngleRef.current; - | ^^^^^^^^^^^^^^^^^^^^^^^ Passing a ref to a function may read its value during render - 120 | const pct = (value - min) / (max - min); - 121 | const cx = size / 2; - 122 | const cy = size / 2; react-hooks/refs - 119:24 warning Error: Cannot access refs during render - -React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef). - -D:\dcprint\erp-project\src\components\GlassKnob.tsx:119:24 - 117 | }, []); - 118 | -> 119 | const displayAngle = displayAngleRef.current; - | ^^^^^^^^^^^^^^^^^^^^^^^ Passing a ref to a function may read its value during render - 120 | const pct = (value - min) / (max - min); - 121 | const cx = size / 2; - 122 | const cy = size / 2; react-hooks/refs - -D:\dcprint\erp-project\src\components\WarehouseCharts.tsx - 17:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\components\WarehouseCharts.tsx:17:5 - 15 | - 16 | useEffect(() => { -> 17 | setImageLoaded(false); - | ^^^^^^^^^^^^^^ Avoid calling setState() directly within an effect - 18 | setHasError(false); - 19 | }, [url]); - 20 | react-hooks/set-state-in-effect - 35:48 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍔犺浇鍥捐〃涓?.."銆傚缓璁娇鐢? {tc('text_tszxco')} i18n/no-chinese-hardcode - 45:52 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍥捐〃鍔犺浇澶辫触"銆傚缓璁娇鐢? {tc('text_7swziz')} i18n/no-chinese-hardcode - 49:12 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鐐瑰嚮閲嶈瘯"銆傚缓璁娇鐢? {tc('text_ekjr0q')} i18n/no-chinese-hardcode - 73:3 warning 'title' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars - 85:50 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍔犺浇鍥捐〃涓?.."銆傚缓璁娇鐢? {tc('text_tszxco')} i18n/no-chinese-hardcode - 91:50 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏆傛棤鏁版嵁"銆傚缓璁娇鐢? {tc('text_dcv57g')} i18n/no-chinese-hardcode - 97:49 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍥捐〃鍔犺浇澶辫触"銆傚缓璁娇鐢? {tc('text_7swziz')} i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\components\common\status-badge.tsx - 20:48 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏈煡"銆傚缓璁娇鐢? {tc('text_i7ej')} i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\components\common\use-paginated-list.ts - 5:35 warning 'T' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 55:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\components\common\use-paginated-list.ts:55:5 - 53 | - 54 | useEffect(() => { -> 55 | fetchData(); - | ^^^^^^^^^ Avoid calling setState() directly within an effect - 56 | }, [fetchData]); - 57 | - 58 | return { react-hooks/set-state-in-effect - -D:\dcprint\erp-project\src\components\hr\OrganizationTree.tsx - 48:42 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 66:10 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "闆嗗洟"銆傚缓璁娇鐢? tc('text_q4f0') i18n/no-chinese-hardcode - 67:17 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "娉曚汉瀹炰綋"銆傚缓璁娇鐢? tc('text_e252nu') i18n/no-chinese-hardcode - 68:12 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "宸ュ巶"銆傚缓璁娇鐢? tc('text_gfgd') i18n/no-chinese-hardcode - 69:13 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "杞﹂棿"銆傚缓璁娇鐢? tc('text_p7pq') i18n/no-chinese-hardcode - 70:9 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鐝粍"銆傚缓璁娇鐢? tc('text_kewn') i18n/no-chinese-hardcode - 71:13 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "宀椾綅"銆傚缓璁娇鐢? tc('text_g6mu') i18n/no-chinese-hardcode - 110:76 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏂板"銆傚缓璁娇鐢? tc(`text_hs6m`) i18n/no-chinese-hardcode - 155:21 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\components\hr\OrganizationTree.tsx:155:21 - 153 | }; - 154 | -> 155 | useEffect(() => { fetchTree(); }, []); - | ^^^^^^^^^ Avoid calling setState() directly within an effect - 156 | - 157 | const openCreate = (parent?: OrgNode) => { - 158 | const childType = parent ? childTypeMap[parent.type] : 'group'; react-hooks/set-state-in-effect - 179:23 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍒犻櫎鎴愬姛"銆傚缓璁娇鐢? tc('text_azeh8z') i18n/no-chinese-hardcode - 185:19 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍒犻櫎璇锋眰澶辫触"銆傚缓璁娇鐢? tc('text_n5t4xp') i18n/no-chinese-hardcode - 190:49 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "缂栫爜鍜屽悕绉颁负蹇呭~"銆傚缓璁娇鐢? tc('text_88i4g4') i18n/no-chinese-hardcode - 209:19 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "淇濆瓨璇锋眰澶辫触"銆傚缓璁娇鐢? tc('text_uyhyp2') i18n/no-chinese-hardcode - 221:44 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏂板"銆傚缓璁娇鐢? {tc('text_hs6m')} i18n/no-chinese-hardcode - 241:22 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "缂栫爜"銆傚缓璁娇鐢? {tc('text_m9wr')} i18n/no-chinese-hardcode - 245:22 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍚嶇О"銆傚缓璁娇鐢? {tc('text_eyrn')} i18n/no-chinese-hardcode - 249:22 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鎺掑簭"銆傚缓璁娇鐢? {tc('text_hge5')} i18n/no-chinese-hardcode - 254:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "璐熻矗浜?銆傚缓璁娇鐢? {tc('text_lckeu')} i18n/no-chinese-hardcode - 260:24 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鐝粍闀?銆傚缓璁娇鐢? {tc('text_hlnmw')} i18n/no-chinese-hardcode - 267:26 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鎶€鑳界瓑绾?銆傚缓璁娇鐢? {tc('text_cyqunv')} i18n/no-chinese-hardcode - 271:26 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "钖祫鑼冨洿"銆傚缓璁娇鐢? {tc('text_hg8omj')} i18n/no-chinese-hardcode - 277:22 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "澶囨敞"銆傚缓璁娇鐢? {tc('text_fqo1')} i18n/no-chinese-hardcode - 282:76 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍙栨秷"銆傚缓璁娇鐢? {tc('text_ev02')} i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\components\hr\charts\LaborCostChart.tsx - 36:9 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍩烘湰宸ヨ祫"銆傚缓璁娇鐢? tc('text_bj1m9d') i18n/no-chinese-hardcode - 37:10 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "璁′欢宸ヨ祫"銆傚缓璁娇鐢? tc('text_hy15x0') i18n/no-chinese-hardcode - 38:13 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍔犵彮璐?銆傚缓璁娇鐢? tc('text_co68c') i18n/no-chinese-hardcode - 39:16 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "缁╂晥濂栭噾"銆傚缓璁娇鐢? tc('text_gfj2m2') i18n/no-chinese-hardcode - 40:14 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "绀句繚/鍏Н閲?銆傚缓璁娇鐢? tc('text_kskkfy') i18n/no-chinese-hardcode - 46:89 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏆傛棤鏁版嵁"銆傚缓璁娇鐢? {tc('text_dcv57g')} i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\components\hr\charts\SalaryStructureChart.tsx - 46:29 error React Hook "useCallback" is called conditionally. React Hooks must be called in the exact same order in every component render. Did you accidentally call a React Hook after an early return? react-hooks/rules-of-hooks - 98:65 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - -D:\dcprint\erp-project\src\components\layout\DynamicMenu.tsx - 109:7 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\components\layout\DynamicMenu.tsx:109:7 - 107 | const ancestorCodes = findAncestorsByPathPrefix(menus, pathname); - 108 | if (ancestorCodes.length > 0) { -> 109 | setExpandedMenus((prev) => { - | ^^^^^^^^^^^^^^^^ Avoid calling setState() directly within an effect - 110 | const newSet = new Set([...prev, ...ancestorCodes]); - 111 | return Array.from(newSet); - 112 | }); react-hooks/set-state-in-effect - 117:55 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍔犺浇涓?.."銆傚缓璁娇鐢? {tc('text_27k1ha')} i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\components\layout\header.tsx - 3:44 warning 'useRef' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 159:6 warning React Hook useCallback has a missing dependency: 'isActive'. Either include it or remove the dependency array react-hooks/exhaustive-deps - 162:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\components\layout\header.tsx:162:5 - 160 | - 161 | useEffect(() => { -> 162 | setActiveTopMenu(findActiveParent()); - | ^^^^^^^^^^^^^^^^ Avoid calling setState() directly within an effect - 163 | }, [findActiveParent]); - 164 | - 165 | const fetchNotifications = useCallback(async () => { react-hooks/set-state-in-effect - 199:6 warning React Hook useCallback has missing dependencies: 'tc' and 'ts'. Either include them or remove the dependency array react-hooks/exhaustive-deps - 217:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\components\layout\header.tsx:217:5 - 215 | - 216 | useEffect(() => { -> 217 | fetchNotifications(); - | ^^^^^^^^^^^^^^^^^^ Avoid calling setState() directly within an effect - 218 | fetchUserInfo(); - 219 | }, [fetchNotifications, fetchUserInfo]); - 220 | react-hooks/set-state-in-effect - 250:43 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "杈炬槍"銆傚缓璁娇鐢? {tc('text_p0cu')} i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\components\layout\sidebar.tsx - 4:52 warning 'useMemo' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 230:35 warning 'isAuthenticated' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 298:9 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\components\layout\sidebar.tsx:298:9 - 296 | return indexA - indexB; - 297 | }); -> 298 | setOrderedMenus(sortedMenus); - | ^^^^^^^^^^^^^^^ Avoid calling setState() directly within an effect - 299 | } catch { - 300 | setOrderedMenus(safeMenus); - 301 | } react-hooks/set-state-in-effect - 350:19 warning Error: Cannot access variable before it is declared - -`isActive` is accessed before it is declared, which prevents the earlier access from updating when this value changes over time. - -D:\dcprint\erp-project\src\components\layout\sidebar.tsx:350:19 - 348 | if (menu.children && menu.children.length > 0) { - 349 | for (const child of menu.children) { -> 350 | if (isActive(child.path)) { - | ^^^^^^^^ `isActive` accessed before it is declared - 351 | if (!codesToExpand.includes(menu.code)) { - 352 | codesToExpand.push(menu.code); - 353 | changed = true; - -D:\dcprint\erp-project\src\components\layout\sidebar.tsx:447:3 - 445 | - 446 | // 妫€鏌ヨ彍鍗曟槸鍚︽縺娲?> 447 | const isActive = (path?: string) => { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 448 | if (!path) return false; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 449 | return pathname === path || pathname.startsWith(path + '/'); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 450 | }; - | ^^^^^ `isActive` is declared here - 451 | - 452 | // 璁$畻褰撳墠婵€娲荤殑涓€绾ц彍鍗曪紙鐢ㄤ簬娣峰悎瀵艰埅妯″紡锛? 453 | useEffect(() => { react-hooks/immutability - 366:6 warning React Hook useEffect has a missing dependency: 'isActive'. Either include it or remove the dependency array react-hooks/exhaustive-deps - 396:5 warning React Hook useCallback has missing dependencies: 'tc' and 'toast'. Either include them or remove the dependency array react-hooks/exhaustive-deps - 459:13 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\components\layout\sidebar.tsx:459:13 - 457 | for (const child of menu.children) { - 458 | if (isActive(child.path)) { -> 459 | setActiveParentCode(menu.code); - | ^^^^^^^^^^^^^^^^^^^ Avoid calling setState() directly within an effect - 460 | return; - 461 | } - 462 | } react-hooks/set-state-in-effect - 470:6 warning React Hook useEffect has a missing dependency: 'isActive'. Either include it or remove the dependency array react-hooks/exhaustive-deps - 572:45 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "杈炬槍"銆傚缓璁娇鐢? {tc('text_p0cu')} i18n/no-chinese-hardcode - 577:23 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "杈炬槍"銆傚缓璁娇鐢? {tc('text_p0cu')} i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\components\printing\LabelPrintPreview.tsx - 183:25 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "瑙﹀彂鎵撳嵃寮傚父"銆傚缓璁娇鐢? tc('text_7mnavy') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\components\printing\PrinterManagement.tsx - 74:17 warning 'setQueue' is assigned a value but never used. Allowed unused elements of array destructuring must match /^_/u @typescript-eslint/no-unused-vars - 111:46 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鎵撳嵃鏈洪厤缃?銆傚缓璁娇鐢? {tc('text_uryeci')} i18n/no-chinese-hardcode - 114:30 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "绠$悊鍜岄厤缃墦鍗拌澶?銆傚缓璁娇鐢? {tc('text_eqng8e')} i18n/no-chinese-hardcode - 119:45 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "娣诲姞鎵撳嵃鏈?銆傚缓璁娇鐢? {tc('text_eo2cs8')} i18n/no-chinese-hardcode - 125:30 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "娣诲姞鎵撳嵃鏈?銆傚缓璁娇鐢? {tc('text_eo2cs8')} i18n/no-chinese-hardcode - 129:26 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鎵撳嵃鏈哄悕绉?銆傚缓璁娇鐢? {tc('text_us8uhs')} i18n/no-chinese-hardcode - 133:26 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鎵撳嵃鏈虹被鍨?銆傚缓璁娇鐢? {tc('text_us25pv')} i18n/no-chinese-hardcode - 139:51 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鐑晱鎵撳嵃鏈?銆傚缓璁娇鐢? {tc('text_sjhafv')} i18n/no-chinese-hardcode - 140:49 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "婵€鍏夋墦鍗版満"銆傚缓璁娇鐢? {tc('text_lf252s')} i18n/no-chinese-hardcode - 141:50 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍠峰ⅷ鎵撳嵃鏈?銆傚缓璁娇鐢? {tc('text_8f1nh0')} i18n/no-chinese-hardcode - 146:26 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍨嬪彿锛堝彲閫夛級"銆傚缓璁娇鐢? {tc('text_x39pg3')} i18n/no-chinese-hardcode - 150:26 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "IP鍦板潃锛堝彲閫夛級"銆傚缓璁娇鐢? {tc('text_dppv0e')} i18n/no-chinese-hardcode - 154:26 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "榛樿绾稿紶灏哄"銆傚缓璁娇鐢? {tc('text_1wqzf2')} i18n/no-chinese-hardcode - 167:26 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "璁句负榛樿鎵撳嵃鏈?銆傚缓璁娇鐢? {tc('text_hdi6y3')} i18n/no-chinese-hardcode - 184:70 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "榛樿"銆傚缓璁娇鐢? {tc('text_rs98')} i18n/no-chinese-hardcode - 200:34 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鎵撳嵃鏈?銆傚缓璁娇鐢? {tc('text_et7x9')} i18n/no-chinese-hardcode - 212:22 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "璁句负榛樿"銆傚缓璁娇鐢? {tc('text_hyquiw')} i18n/no-chinese-hardcode - 236:42 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鎵撳嵃闃熷垪"銆傚缓璁娇鐢? {tc('text_cre905')} i18n/no-chinese-hardcode - 239:28 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏌ョ湅鍜岀鐞嗘墦鍗颁换鍔?銆傚缓璁娇鐢? {tc('text_li9xzi')} i18n/no-chinese-hardcode - 243:69 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏆傛棤鎵撳嵃浠诲姟"銆傚缓璁娇鐢? {tc('text_km3a5b')} i18n/no-chinese-hardcode - 280:57 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "閲嶈瘯"銆傚缓璁娇鐢? {tc('text_pkfc')} i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\components\qr-code\QRCodeGenerator.tsx - 75:24 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "璋冪敤鐢熸垚鎺ュ彛"銆傚缓璁娇鐢? tc('text_tm91u4') i18n/no-chinese-hardcode - 93:25 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鐢熸垚寮傚父"銆傚缓璁娇鐢? tc('text_f6mn0n') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\components\qr-code\QRCodePrinter.tsx - 163:24 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "璋冪敤鎵撳嵃鎺ュ彛"銆傚缓璁娇鐢? tc('text_w1qqxs') i18n/no-chinese-hardcode - 181:25 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鎵撳嵃寮傚父"銆傚缓璁娇鐢? tc('text_cr4xwz') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\components\qr-code\QRCodeScanner.tsx - 96:20 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "楠岃瘉澶辫触"銆傚缓璁娇鐢? tc('text_k2ng5l') i18n/no-chinese-hardcode - 118:22 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鎵弿鎴愬姛"銆傚缓璁娇鐢? tc('text_ctz1oz') i18n/no-chinese-hardcode - 134:16 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鎵弿澶辫触"銆傚缓璁娇鐢? tc('text_ctxuxk') i18n/no-chinese-hardcode - 232:54 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "纭"銆傚缓璁娇鐢? {tc('text_l912')} i18n/no-chinese-hardcode - 268:78 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "娓呯┖"銆傚缓璁娇鐢? {tc('text_jdw5')} i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\components\qr-code\QRCodeSearch.tsx - 48:22 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "璇疯緭鍏ユ悳绱㈠叧閿瘝"銆傚缓璁娇鐢? tc('text_7ek64j') i18n/no-chinese-hardcode - 80:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鏈壘鍒扮浉鍏宠褰?銆傚缓璁娇鐢? tc('text_8m9e9o') i18n/no-chinese-hardcode - 83:24 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鎼滅储澶辫触"銆傚缓璁娇鐢? tc('text_d5bvh6') i18n/no-chinese-hardcode - 86:22 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鎼滅储澶辫触"銆傚缓璁娇鐢? tc('text_d5bvh6') i18n/no-chinese-hardcode - 109:41 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "浜岀淮鐮佹煡璇?銆傚缓璁娇鐢? {tc('text_viu0zu')} i18n/no-chinese-hardcode - 122:52 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "浜岀淮鐮?銆傚缓璁娇鐢? {tc('text_c4ffd')} i18n/no-chinese-hardcode - 129:14 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍗曞彿"銆傚缓璁娇鐢? {tc('text_emv6')} i18n/no-chinese-hardcode - 136:14 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鎵规"銆傚缓璁娇鐢? {tc('text_hc5k')} i18n/no-chinese-hardcode - 144:48 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鐗╂枡"銆傚缓璁娇鐢? {tc('text_k0nk')} i18n/no-chinese-hardcode - 177:47 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "閫夋嫨"銆傚缓璁娇鐢? {tc('text_p1j4')} i18n/no-chinese-hardcode - 178:30 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "浜岀淮鐮佺紪鐮?銆傚缓璁娇鐢? {tc('text_viq6ws')} i18n/no-chinese-hardcode - 179:30 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "绫诲瀷"銆傚缓璁娇鐢? {tc('text_lnjk')} i18n/no-chinese-hardcode - 180:30 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍏宠仈鍗曞彿"銆傚缓璁娇鐢? {tc('text_at16ir')} i18n/no-chinese-hardcode - 181:30 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鐗╂枡鍚嶇О"銆傚缓璁娇鐢? {tc('text_eusfkj')} i18n/no-chinese-hardcode - 182:30 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏁伴噺"銆傚缓璁娇鐢? {tc('text_i1y7')} i18n/no-chinese-hardcode - 183:30 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鐘舵€?銆傚缓璁娇鐢? {tc('text_k1e3')} i18n/no-chinese-hardcode - 184:30 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鎿嶄綔"銆傚缓璁娇鐢? {tc('text_hkxb')} i18n/no-chinese-hardcode - 190:95 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏈壘鍒扮浉鍏宠褰?銆傚缓璁娇鐢? {tc('text_8m9e9o')} i18n/no-chinese-hardcode - 231:30 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "杩芥函"銆傚缓璁娇鐢? {tc('text_p3ki')} i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\components\qr-code\QRCodeTrace.tsx - 155:7 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\components\qr-code\QRCodeTrace.tsx:155:7 - 153 | useEffect(() => { - 154 | if (qrCode && showDialog) { -> 155 | handleTrace(); - | ^^^^^^^^^^^ Avoid calling setState() directly within an effect - 156 | } - 157 | }, [qrCode, showDialog]); - 158 | react-hooks/set-state-in-effect - 157:6 warning React Hook useEffect has a missing dependency: 'handleTrace'. Either include it or remove the dependency array react-hooks/exhaustive-deps - -D:\dcprint\erp-project\src\components\qr-code\QRCodeViewer.tsx - 68:20 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "宸插鍒跺埌鍓创鏉?銆傚缓璁娇鐢? tc('text_jx65og') i18n/no-chinese-hardcode - 96:41 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏌ョ湅"銆傚缓璁娇鐢? {tc('text_ibpi')} i18n/no-chinese-hardcode - 103:26 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "浜岀淮鐮佽鎯?銆傚缓璁娇鐢? {tc('text_vio51k')} i18n/no-chinese-hardcode - 134:47 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍩烘湰淇℃伅"銆傚缓璁娇鐢? {tc('text_biyzkw')} i18n/no-chinese-hardcode - 135:49 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鎵╁睍淇℃伅"銆傚缓璁娇鐢? {tc('text_csrbui')} i18n/no-chinese-hardcode - 143:69 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "绫诲瀷锛?銆傚缓璁娇鐢? {tc('text_ioo8a')} i18n/no-chinese-hardcode - 149:69 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鐘舵€侊細"銆傚缓璁娇鐢? {tc('text_halin')} i18n/no-chinese-hardcode - 155:69 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍏宠仈鍗曞彿锛?銆傚缓璁娇鐢? {tc('text_k5i9c9')} i18n/no-chinese-hardcode - 159:69 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鎵规鍙凤細"銆傚缓璁娇鐢? {tc('text_cv94uj')} i18n/no-chinese-hardcode - 163:69 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鐗╂枡鍚嶇О锛?銆傚缓璁娇鐢? {tc('text_ybuh7r')} i18n/no-chinese-hardcode - 167:69 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "瑙勬牸锛?銆傚缓璁娇鐢? {tc('text_kpkbm')} i18n/no-chinese-hardcode - 171:69 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏁伴噺锛?銆傚缓璁娇鐢? {tc('text_fl2u3')} i18n/no-chinese-hardcode - 177:69 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "浠撳簱锛?銆傚缓璁娇鐢? {tc('text_c14hm')} i18n/no-chinese-hardcode - 181:69 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "渚涘簲鍟嗭細"銆傚缓璁娇鐢? {tc('text_afr3ql')} i18n/no-chinese-hardcode - 185:69 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "瀹㈡埛锛?銆傚缓璁娇鐢? {tc('text_dxa79')} i18n/no-chinese-hardcode - 191:79 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏆傛棤璇︾粏淇℃伅"銆傚缓璁娇鐢? {tc('text_f6yq6s')} i18n/no-chinese-hardcode - 199:67 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "宸ュ崟缂栧彿锛?銆傚缓璁娇鐢? {tc('text_n0dsw9')} i18n/no-chinese-hardcode - 203:67 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鐢熶骇鏃ユ湡锛?銆傚缓璁娇鐢? {tc('text_sxc5wo')} i18n/no-chinese-hardcode - 207:67 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏈夋晥鏈燂細"銆傚缓璁娇鐢? {tc('text_df7csa')} i18n/no-chinese-hardcode - 211:67 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鎵撳嵃娆℃暟锛?銆傚缓璁娇鐢? {tc('text_urkkya')} i18n/no-chinese-hardcode - 215:67 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鎵弿娆℃暟锛?銆傚缓璁娇鐢? {tc('text_sc1gop')} i18n/no-chinese-hardcode - 219:67 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍒涘缓鏃堕棿锛?銆傚缓璁娇鐢? {tc('text_lpj3b7')} i18n/no-chinese-hardcode - 224:69 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "澶囨敞锛?銆傚缓璁娇鐢? {tc('text_dld2x')} i18n/no-chinese-hardcode - 230:79 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鏆傛棤鎵╁睍淇℃伅"銆傚缓璁娇鐢? {tc('text_kke3s8')} i18n/no-chinese-hardcode - 247:58 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "杩芥函"銆傚缓璁娇鐢? {tc('text_p3ki')} i18n/no-chinese-hardcode - 251:76 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍏抽棴"銆傚缓璁娇鐢? {tc('text_eod6')} i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\components\qr-code\qr-code-types.ts - 73:25 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 86:25 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 89:29 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - -D:\dcprint\erp-project\src\components\reports\PrepressReportsContent.tsx - 5:29 warning 'CardDescription' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 74:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\components\reports\PrepressReportsContent.tsx:74:5 - 72 | - 73 | useEffect(() => { -> 74 | setLoading(true); - | ^^^^^^^^^^ Avoid calling setState() directly within an effect - 75 | Promise.all([fetchData('dashboard').then(setDashboardData)]).finally(() => setLoading(false)); - 76 | }, [period, fetchData]); - 77 | react-hooks/set-state-in-effect - 156:10 warning Error: Cannot create components during render - -Components created during render will reset their state each time they are created. Declare components outside of render. - -D:\dcprint\erp-project\src\components\reports\PrepressReportsContent.tsx:156:10 - 154 | ))} - 155 | </div> -> 156 | <S rows={6} /> - | ^ This component is created during render - 157 | </div> - 158 | ); - 159 | } - -D:\dcprint\erp-project\src\components\reports\PrepressReportsContent.tsx:115:13 - 113 | const formatNum = (v: number) => new Intl.NumberFormat('zh-CN').format(v); - 114 | -> 115 | const S = ({ rows = 4 }: { rows?: number }) => ( - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 116 | <div className="space-y-2 p-4"> - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 117 | {Array.from({ length: rows }).map((_, i) => ( - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 118 | <Skeleton key={i} className="h-8 w-full" /> - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 119 | ))} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 120 | </div> - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 121 | ); - | ^^^^ The component is created during render here - 122 | - 123 | function KpiCard({ - 124 | title, react-hooks/static-components - 265:14 warning Error: Cannot create components during render - -Components created during render will reset their state each time they are created. Declare components outside of render. - -D:\dcprint\erp-project\src\components\reports\PrepressReportsContent.tsx:265:14 - 263 | <TabsContent value="overview" className="space-y-6"> - 264 | {!dashboardData ? ( -> 265 | <S rows={8} /> - | ^ This component is created during render - 266 | ) : ( - 267 | <> - 268 | <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-5 gap-4"> - -D:\dcprint\erp-project\src\components\reports\PrepressReportsContent.tsx:115:13 - 113 | const formatNum = (v: number) => new Intl.NumberFormat('zh-CN').format(v); - 114 | -> 115 | const S = ({ rows = 4 }: { rows?: number }) => ( - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 116 | <div className="space-y-2 p-4"> - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 117 | {Array.from({ length: rows }).map((_, i) => ( - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 118 | <Skeleton key={i} className="h-8 w-full" /> - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 119 | ))} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 120 | </div> - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 121 | ); - | ^^^^ The component is created during render here - 122 | - 123 | function KpiCard({ - 124 | title, react-hooks/static-components - 269:18 warning Error: Cannot create components during render - -Components created during render will reset their state each time they are created. Declare components outside of render. - -D:\dcprint\erp-project\src\components\reports\PrepressReportsContent.tsx:269:18 - 267 | <> - 268 | <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-5 gap-4"> -> 269 | <KpiCard - | ^^^^^^^ This component is created during render - 270 | title={t('dieTotal')} - 271 | value={formatNum(dashboardData.dieMetrics?.totalTemplates)} - 272 | sub={`${t('dieAvailable')}: ${dashboardData.dieMetrics?.availableCount} | ${t('dieWarning')}: ${dashboardData.dieMetrics?.warningCount}`} - -D:\dcprint\erp-project\src\components\reports\PrepressReportsContent.tsx:123:3 - 121 | ); - 122 | -> 123 | function KpiCard({ - | ^^^^^^^^^^^^^^^^^^ -> 124 | title, - | ^^^^^^^^^^ -> 125 | value, - 鈥? | ^^^^^^^^^^ -> 145 | ); - | ^^^^^^^^^^ -> 146 | } - | ^^^^ The component is created during render here - 147 | - 148 | if (loading && !dashboardData) { - 149 | return ( react-hooks/static-components - 275:18 warning Error: Cannot create components during render - -Components created during render will reset their state each time they are created. Declare components outside of render. - -D:\dcprint\erp-project\src\components\reports\PrepressReportsContent.tsx:275:18 - 273 | icon={<Scissors className="h-4 w-4 text-muted-foreground" />} - 274 | /> -> 275 | <KpiCard - | ^^^^^^^ This component is created during render - 276 | title={t('inkConsumed')} - 277 | value={`${formatNum(dashboardData.inkMetrics?.totalConsumed)}kg`} - 278 | sub={`${t('inkAffectedOrders')}: ${dashboardData.inkMetrics?.affectedWorkOrders}`} - -D:\dcprint\erp-project\src\components\reports\PrepressReportsContent.tsx:123:3 - 121 | ); - 122 | -> 123 | function KpiCard({ - | ^^^^^^^^^^^^^^^^^^ -> 124 | title, - | ^^^^^^^^^^ -> 125 | value, - 鈥? | ^^^^^^^^^^ -> 145 | ); - | ^^^^^^^^^^ -> 146 | } - | ^^^^ The component is created during render here - 147 | - 148 | if (loading && !dashboardData) { - 149 | return ( react-hooks/static-components - 281:18 warning Error: Cannot create components during render - -Components created during render will reset their state each time they are created. Declare components outside of render. - -D:\dcprint\erp-project\src\components\reports\PrepressReportsContent.tsx:281:18 - 279 | icon={<Droplets className="h-4 w-4 text-muted-foreground" />} - 280 | /> -> 281 | <KpiCard - | ^^^^^^^ This component is created during render - 282 | title={t('openingTotal')} - 283 | value={formatNum(dashboardData.openingMetrics?.totalOpenings)} - 284 | sub={`${t('openingExpired')}: ${dashboardData.openingMetrics?.expiredCount} | ${t('openingExpiryRate')}: ${dashboardData.openingMetrics?.expiryRate}%`} - -D:\dcprint\erp-project\src\components\reports\PrepressReportsContent.tsx:123:3 - 121 | ); - 122 | -> 123 | function KpiCard({ - | ^^^^^^^^^^^^^^^^^^ -> 124 | title, - | ^^^^^^^^^^ -> 125 | value, - 鈥? | ^^^^^^^^^^ -> 145 | ); - | ^^^^^^^^^^ -> 146 | } - | ^^^^ The component is created during render here - 147 | - 148 | if (loading && !dashboardData) { - 149 | return ( react-hooks/static-components - 287:18 warning Error: Cannot create components during render - -Components created during render will reset their state each time they are created. Declare components outside of render. - -D:\dcprint\erp-project\src\components\reports\PrepressReportsContent.tsx:287:18 - 285 | icon={<Package className="h-4 w-4 text-muted-foreground" />} - 286 | /> -> 287 | <KpiCard - | ^^^^^^^ This component is created during render - 288 | title={t('toolTotal')} - 289 | value={formatNum(dashboardData.toolMetrics?.totalTools)} - 290 | sub={`${t('toolDieCount')}: ${dashboardData.toolMetrics?.dieCount} | ${t('toolPlateCount')}: ${dashboardData.toolMetrics?.screenPlateCount}`} - -D:\dcprint\erp-project\src\components\reports\PrepressReportsContent.tsx:123:3 - 121 | ); - 122 | -> 123 | function KpiCard({ - | ^^^^^^^^^^^^^^^^^^ -> 124 | title, - | ^^^^^^^^^^ -> 125 | value, - 鈥? | ^^^^^^^^^^ -> 145 | ); - | ^^^^^^^^^^ -> 146 | } - | ^^^^ The component is created during render here - 147 | - 148 | if (loading && !dashboardData) { - 149 | return ( react-hooks/static-components - 293:18 warning Error: Cannot create components during render - -Components created during render will reset their state each time they are created. Declare components outside of render. - -D:\dcprint\erp-project\src\components\reports\PrepressReportsContent.tsx:293:18 - 291 | icon={<Wrench className="h-4 w-4 text-muted-foreground" />} - 292 | /> -> 293 | <KpiCard - | ^^^^^^^ This component is created during render - 294 | title={t('sampleTotal')} - 295 | value={formatNum(dashboardData.sampleMetrics?.totalSamples)} - 296 | sub={`${t('sampleCost')}: ${formatCurrency(dashboardData.sampleMetrics?.totalCost)}`} - -D:\dcprint\erp-project\src\components\reports\PrepressReportsContent.tsx:123:3 - 121 | ); - 122 | -> 123 | function KpiCard({ - | ^^^^^^^^^^^^^^^^^^ -> 124 | title, - | ^^^^^^^^^^ -> 125 | value, - 鈥? | ^^^^^^^^^^ -> 145 | ); - | ^^^^^^^^^^ -> 146 | } - | ^^^^ The component is created during render here - 147 | - 148 | if (loading && !dashboardData) { - 149 | return ( react-hooks/static-components - 385:14 warning Error: Cannot create components during render - -Components created during render will reset their state each time they are created. Declare components outside of render. - -D:\dcprint\erp-project\src\components\reports\PrepressReportsContent.tsx:385:14 - 383 | <TabsContent value="die" className="space-y-6"> - 384 | {!dieData ? ( -> 385 | <S rows={8} /> - | ^ This component is created during render - 386 | ) : ( - 387 | <> - 388 | <div className="grid grid-cols-1 lg:grid-cols-2 gap-6"> - -D:\dcprint\erp-project\src\components\reports\PrepressReportsContent.tsx:115:13 - 113 | const formatNum = (v: number) => new Intl.NumberFormat('zh-CN').format(v); - 114 | -> 115 | const S = ({ rows = 4 }: { rows?: number }) => ( - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 116 | <div className="space-y-2 p-4"> - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 117 | {Array.from({ length: rows }).map((_, i) => ( - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 118 | <Skeleton key={i} className="h-8 w-full" /> - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 119 | ))} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 120 | </div> - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 121 | ); - | ^^^^ The component is created during render here - 122 | - 123 | function KpiCard({ - 124 | title, react-hooks/static-components - 551:14 warning Error: Cannot create components during render - -Components created during render will reset their state each time they are created. Declare components outside of render. - -D:\dcprint\erp-project\src\components\reports\PrepressReportsContent.tsx:551:14 - 549 | <TabsContent value="ink" className="space-y-6"> - 550 | {!inkData ? ( -> 551 | <S rows={8} /> - | ^ This component is created during render - 552 | ) : ( - 553 | <> - 554 | <div className="grid grid-cols-1 md:grid-cols-4 gap-4"> - -D:\dcprint\erp-project\src\components\reports\PrepressReportsContent.tsx:115:13 - 113 | const formatNum = (v: number) => new Intl.NumberFormat('zh-CN').format(v); - 114 | -> 115 | const S = ({ rows = 4 }: { rows?: number }) => ( - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 116 | <div className="space-y-2 p-4"> - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 117 | {Array.from({ length: rows }).map((_, i) => ( - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 118 | <Skeleton key={i} className="h-8 w-full" /> - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 119 | ))} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 120 | </div> - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 121 | ); - | ^^^^ The component is created during render here - 122 | - 123 | function KpiCard({ - 124 | title, react-hooks/static-components - 555:18 warning Error: Cannot create components during render - -Components created during render will reset their state each time they are created. Declare components outside of render. - -D:\dcprint\erp-project\src\components\reports\PrepressReportsContent.tsx:555:18 - 553 | <> - 554 | <div className="grid grid-cols-1 md:grid-cols-4 gap-4"> -> 555 | <KpiCard - | ^^^^^^^ This component is created during render - 556 | title={t('inkConsumed')} - 557 | value={`${formatNum(inkData.summary?.totalConsumed)}kg`} - 558 | icon={<Droplets className="h-4 w-4 text-muted-foreground" />} - -D:\dcprint\erp-project\src\components\reports\PrepressReportsContent.tsx:123:3 - 121 | ); - 122 | -> 123 | function KpiCard({ - | ^^^^^^^^^^^^^^^^^^ -> 124 | title, - | ^^^^^^^^^^ -> 125 | value, - 鈥? | ^^^^^^^^^^ -> 145 | ); - | ^^^^^^^^^^ -> 146 | } - | ^^^^ The component is created during render here - 147 | - 148 | if (loading && !dashboardData) { - 149 | return ( react-hooks/static-components - 560:18 warning Error: Cannot create components during render - -Components created during render will reset their state each time they are created. Declare components outside of render. - -D:\dcprint\erp-project\src\components\reports\PrepressReportsContent.tsx:560:18 - 558 | icon={<Droplets className="h-4 w-4 text-muted-foreground" />} - 559 | /> -> 560 | <KpiCard - | ^^^^^^^ This component is created during render - 561 | title={t('inkReturned')} - 562 | value={`${formatNum(inkData.summary?.totalReturned)}kg`} - 563 | icon={<Droplets className="h-4 w-4 text-muted-foreground" />} - -D:\dcprint\erp-project\src\components\reports\PrepressReportsContent.tsx:123:3 - 121 | ); - 122 | -> 123 | function KpiCard({ - | ^^^^^^^^^^^^^^^^^^ -> 124 | title, - | ^^^^^^^^^^ -> 125 | value, - 鈥? | ^^^^^^^^^^ -> 145 | ); - | ^^^^^^^^^^ -> 146 | } - | ^^^^ The component is created during render here - 147 | - 148 | if (loading && !dashboardData) { - 149 | return ( react-hooks/static-components - 565:18 warning Error: Cannot create components during render - -Components created during render will reset their state each time they are created. Declare components outside of render. - -D:\dcprint\erp-project\src\components\reports\PrepressReportsContent.tsx:565:18 - 563 | icon={<Droplets className="h-4 w-4 text-muted-foreground" />} - 564 | /> -> 565 | <KpiCard - | ^^^^^^^ This component is created during render - 566 | title={t('inkScraped')} - 567 | value={`${formatNum(inkData.summary?.totalScraped)}kg`} - 568 | icon={<Droplets className="h-4 w-4 text-muted-foreground" />} - -D:\dcprint\erp-project\src\components\reports\PrepressReportsContent.tsx:123:3 - 121 | ); - 122 | -> 123 | function KpiCard({ - | ^^^^^^^^^^^^^^^^^^ -> 124 | title, - | ^^^^^^^^^^ -> 125 | value, - 鈥? | ^^^^^^^^^^ -> 145 | ); - | ^^^^^^^^^^ -> 146 | } - | ^^^^ The component is created during render here - 147 | - 148 | if (loading && !dashboardData) { - 149 | return ( react-hooks/static-components - 570:18 warning Error: Cannot create components during render - -Components created during render will reset their state each time they are created. Declare components outside of render. - -D:\dcprint\erp-project\src\components\reports\PrepressReportsContent.tsx:570:18 - 568 | icon={<Droplets className="h-4 w-4 text-muted-foreground" />} - 569 | /> -> 570 | <KpiCard - | ^^^^^^^ This component is created during render - 571 | title={t('inkAffectedOrders')} - 572 | value={formatNum(inkData.summary?.workOrderCount)} - 573 | icon={<BarChart3 className="h-4 w-4 text-muted-foreground" />} - -D:\dcprint\erp-project\src\components\reports\PrepressReportsContent.tsx:123:3 - 121 | ); - 122 | -> 123 | function KpiCard({ - | ^^^^^^^^^^^^^^^^^^ -> 124 | title, - | ^^^^^^^^^^ -> 125 | value, - 鈥? | ^^^^^^^^^^ -> 145 | ); - | ^^^^^^^^^^ -> 146 | } - | ^^^^ The component is created during render here - 147 | - 148 | if (loading && !dashboardData) { - 149 | return ( react-hooks/static-components - 648:14 warning Error: Cannot create components during render - -Components created during render will reset their state each time they are created. Declare components outside of render. - -D:\dcprint\erp-project\src\components\reports\PrepressReportsContent.tsx:648:14 - 646 | <TabsContent value="surplus" className="space-y-6"> - 647 | {!surplusData ? ( -> 648 | <S rows={8} /> - | ^ This component is created during render - 649 | ) : ( - 650 | <> - 651 | <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4"> - -D:\dcprint\erp-project\src\components\reports\PrepressReportsContent.tsx:115:13 - 113 | const formatNum = (v: number) => new Intl.NumberFormat('zh-CN').format(v); - 114 | -> 115 | const S = ({ rows = 4 }: { rows?: number }) => ( - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 116 | <div className="space-y-2 p-4"> - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 117 | {Array.from({ length: rows }).map((_, i) => ( - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 118 | <Skeleton key={i} className="h-8 w-full" /> - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 119 | ))} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 120 | </div> - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 121 | ); - | ^^^^ The component is created during render here - 122 | - 123 | function KpiCard({ - 124 | title, react-hooks/static-components - 652:18 warning Error: Cannot create components during render - -Components created during render will reset their state each time they are created. Declare components outside of render. - -D:\dcprint\erp-project\src\components\reports\PrepressReportsContent.tsx:652:18 - 650 | <> - 651 | <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4"> -> 652 | <KpiCard - | ^^^^^^^ This component is created during render - 653 | title={t('surplusSummary')} - 654 | value={`${formatNum(surplusData.surplusSummary?.totalSurplus)}kg`} - 655 | sub={`${t('surplusAvailable')}: ${surplusData.surplusSummary?.availableCount}`} - -D:\dcprint\erp-project\src\components\reports\PrepressReportsContent.tsx:123:3 - 121 | ); - 122 | -> 123 | function KpiCard({ - | ^^^^^^^^^^^^^^^^^^ -> 124 | title, - | ^^^^^^^^^^ -> 125 | value, - 鈥? | ^^^^^^^^^^ -> 145 | ); - | ^^^^^^^^^^ -> 146 | } - | ^^^^ The component is created during render here - 147 | - 148 | if (loading && !dashboardData) { - 149 | return ( react-hooks/static-components - 658:18 warning Error: Cannot create components during render - -Components created during render will reset their state each time they are created. Declare components outside of render. - -D:\dcprint\erp-project\src\components\reports\PrepressReportsContent.tsx:658:18 - 656 | icon={<Package className="h-4 w-4 text-muted-foreground" />} - 657 | /> -> 658 | <KpiCard - | ^^^^^^^ This component is created during render - 659 | title={t('surplusReuseRate')} - 660 | value={`${surplusData.surplusSummary?.reuseRate}%`} - 661 | sub={`${t('surplusReused')}: ${surplusData.surplusSummary?.reusedCount}`} - -D:\dcprint\erp-project\src\components\reports\PrepressReportsContent.tsx:123:3 - 121 | ); - 122 | -> 123 | function KpiCard({ - | ^^^^^^^^^^^^^^^^^^ -> 124 | title, - | ^^^^^^^^^^ -> 125 | value, - 鈥? | ^^^^^^^^^^ -> 145 | ); - | ^^^^^^^^^^ -> 146 | } - | ^^^^ The component is created during render here - 147 | - 148 | if (loading && !dashboardData) { - 149 | return ( react-hooks/static-components - 664:18 warning Error: Cannot create components during render - -Components created during render will reset their state each time they are created. Declare components outside of render. - -D:\dcprint\erp-project\src\components\reports\PrepressReportsContent.tsx:664:18 - 662 | icon={<Package className="h-4 w-4 text-muted-foreground" />} - 663 | /> -> 664 | <KpiCard - | ^^^^^^^ This component is created during render - 665 | title={t('openingTotal')} - 666 | value={formatNum( - 667 | surplusData.openingStatus?.reduce( - -D:\dcprint\erp-project\src\components\reports\PrepressReportsContent.tsx:123:3 - 121 | ); - 122 | -> 123 | function KpiCard({ - | ^^^^^^^^^^^^^^^^^^ -> 124 | title, - | ^^^^^^^^^^ -> 125 | value, - 鈥? | ^^^^^^^^^^ -> 145 | ); - | ^^^^^^^^^^ -> 146 | } - | ^^^^ The component is created during render here - 147 | - 148 | if (loading && !dashboardData) { - 149 | return ( react-hooks/static-components - 674:18 warning Error: Cannot create components during render - -Components created during render will reset their state each time they are created. Declare components outside of render. - -D:\dcprint\erp-project\src\components\reports\PrepressReportsContent.tsx:674:18 - 672 | icon={<Package className="h-4 w-4 text-muted-foreground" />} - 673 | /> -> 674 | <KpiCard - | ^^^^^^^ This component is created during render - 675 | title={t('openingExpired')} - 676 | value={formatNum(surplusData.expiredWarning?.length || 0)} - 677 | sub={t('overdueUsing')} - -D:\dcprint\erp-project\src\components\reports\PrepressReportsContent.tsx:123:3 - 121 | ); - 122 | -> 123 | function KpiCard({ - | ^^^^^^^^^^^^^^^^^^ -> 124 | title, - | ^^^^^^^^^^ -> 125 | value, - 鈥? | ^^^^^^^^^^ -> 145 | ); - | ^^^^^^^^^^ -> 146 | } - | ^^^^ The component is created during render here - 147 | - 148 | if (loading && !dashboardData) { - 149 | return ( react-hooks/static-components - 783:14 warning Error: Cannot create components during render - -Components created during render will reset their state each time they are created. Declare components outside of render. - -D:\dcprint\erp-project\src\components\reports\PrepressReportsContent.tsx:783:14 - 781 | <TabsContent value="tool" className="space-y-6"> - 782 | {!toolData ? ( -> 783 | <S rows={8} /> - | ^ This component is created during render - 784 | ) : ( - 785 | <> - 786 | <div className="grid grid-cols-1 lg:grid-cols-2 gap-6"> - -D:\dcprint\erp-project\src\components\reports\PrepressReportsContent.tsx:115:13 - 113 | const formatNum = (v: number) => new Intl.NumberFormat('zh-CN').format(v); - 114 | -> 115 | const S = ({ rows = 4 }: { rows?: number }) => ( - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 116 | <div className="space-y-2 p-4"> - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 117 | {Array.from({ length: rows }).map((_, i) => ( - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 118 | <Skeleton key={i} className="h-8 w-full" /> - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 119 | ))} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 120 | </div> - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 121 | ); - | ^^^^ The component is created during render here - 122 | - 123 | function KpiCard({ - 124 | title, react-hooks/static-components - 828:38 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍘熷€?銆傚缓璁娇鐢? {tc('text_enwd')} i18n/no-chinese-hardcode - 829:38 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "绱鎽婇攢"銆傚缓璁娇鐢? {tc('text_gdcxjc')} i18n/no-chinese-hardcode - 830:38 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍑€鍊?銆傚缓璁娇鐢? {tc('text_ecfw')} i18n/no-chinese-hardcode - 907:14 warning Error: Cannot create components during render - -Components created during render will reset their state each time they are created. Declare components outside of render. - -D:\dcprint\erp-project\src\components\reports\PrepressReportsContent.tsx:907:14 - 905 | <TabsContent value="sample" className="space-y-6"> - 906 | {!sampleData ? ( -> 907 | <S rows={8} /> - | ^ This component is created during render - 908 | ) : ( - 909 | <> - 910 | <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4"> - -D:\dcprint\erp-project\src\components\reports\PrepressReportsContent.tsx:115:13 - 113 | const formatNum = (v: number) => new Intl.NumberFormat('zh-CN').format(v); - 114 | -> 115 | const S = ({ rows = 4 }: { rows?: number }) => ( - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 116 | <div className="space-y-2 p-4"> - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 117 | {Array.from({ length: rows }).map((_, i) => ( - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 118 | <Skeleton key={i} className="h-8 w-full" /> - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 119 | ))} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 120 | </div> - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 121 | ); - | ^^^^ The component is created during render here - 122 | - 123 | function KpiCard({ - 124 | title, react-hooks/static-components - 911:18 warning Error: Cannot create components during render - -Components created during render will reset their state each time they are created. Declare components outside of render. - -D:\dcprint\erp-project\src\components\reports\PrepressReportsContent.tsx:911:18 - 909 | <> - 910 | <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4"> -> 911 | <KpiCard - | ^^^^^^^ This component is created during render - 912 | title={t('sampleTotal')} - 913 | value={formatNum(sampleData.summary?.total)} - 914 | sub={`${t('sampleCompleted')}: ${sampleData.summary?.completed}`} - -D:\dcprint\erp-project\src\components\reports\PrepressReportsContent.tsx:123:3 - 121 | ); - 122 | -> 123 | function KpiCard({ - | ^^^^^^^^^^^^^^^^^^ -> 124 | title, - | ^^^^^^^^^^ -> 125 | value, - 鈥? | ^^^^^^^^^^ -> 145 | ); - | ^^^^^^^^^^ -> 146 | } - | ^^^^ The component is created during render here - 147 | - 148 | if (loading && !dashboardData) { - 149 | return ( react-hooks/static-components - 917:18 warning Error: Cannot create components during render - -Components created during render will reset their state each time they are created. Declare components outside of render. - -D:\dcprint\erp-project\src\components\reports\PrepressReportsContent.tsx:917:18 - 915 | icon={<FlaskConical className="h-4 w-4 text-muted-foreground" />} - 916 | /> -> 917 | <KpiCard - | ^^^^^^^ This component is created during render - 918 | title={t('sampleCost')} - 919 | value={formatCurrency(sampleData.summary?.totalCost)} - 920 | sub={`${t('materialCost')} ${sampleData.summary?.materialRatio}% / ${t('laborCost')} ${sampleData.summary?.laborRatio}%`} - -D:\dcprint\erp-project\src\components\reports\PrepressReportsContent.tsx:123:3 - 121 | ); - 122 | -> 123 | function KpiCard({ - | ^^^^^^^^^^^^^^^^^^ -> 124 | title, - | ^^^^^^^^^^ -> 125 | value, - 鈥? | ^^^^^^^^^^ -> 145 | ); - | ^^^^^^^^^^ -> 146 | } - | ^^^^ The component is created during render here - 147 | - 148 | if (loading && !dashboardData) { - 149 | return ( react-hooks/static-components - 923:18 warning Error: Cannot create components during render - -Components created during render will reset their state each time they are created. Declare components outside of render. - -D:\dcprint\erp-project\src\components\reports\PrepressReportsContent.tsx:923:18 - 921 | icon={<FlaskConical className="h-4 w-4 text-muted-foreground" />} - 922 | /> -> 923 | <KpiCard - | ^^^^^^^ This component is created during render - 924 | title={t('completedCount')} - 925 | value={formatNum(sampleData.summary?.completed)} - 926 | sub={`${t('cancelledCount')}: ${sampleData.summary?.cancelled}`} - -D:\dcprint\erp-project\src\components\reports\PrepressReportsContent.tsx:123:3 - 121 | ); - 122 | -> 123 | function KpiCard({ - | ^^^^^^^^^^^^^^^^^^ -> 124 | title, - | ^^^^^^^^^^ -> 125 | value, - 鈥? | ^^^^^^^^^^ -> 145 | ); - | ^^^^^^^^^^ -> 146 | } - | ^^^^ The component is created during render here - 147 | - 148 | if (loading && !dashboardData) { - 149 | return ( react-hooks/static-components - 929:18 warning Error: Cannot create components during render - -Components created during render will reset their state each time they are created. Declare components outside of render. - -D:\dcprint\erp-project\src\components\reports\PrepressReportsContent.tsx:929:18 - 927 | icon={<BarChart3 className="h-4 w-4 text-muted-foreground" />} - 928 | /> -> 929 | <KpiCard - | ^^^^^^^ This component is created during render - 930 | title={t('inProgressCount')} - 931 | value={formatNum(sampleData.summary?.inProgress)} - 932 | icon={<BarChart3 className="h-4 w-4 text-muted-foreground" />} - -D:\dcprint\erp-project\src\components\reports\PrepressReportsContent.tsx:123:3 - 121 | ); - 122 | -> 123 | function KpiCard({ - | ^^^^^^^^^^^^^^^^^^ -> 124 | title, - | ^^^^^^^^^^ -> 125 | value, - 鈥? | ^^^^^^^^^^ -> 145 | ); - | ^^^^^^^^^^ -> 146 | } - | ^^^^ The component is created during render here - 147 | - 148 | if (loading && !dashboardData) { - 149 | return ( react-hooks/static-components - -D:\dcprint\erp-project\src\components\theme-settings.tsx - 24:12 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "渚ц竟鏍?銆傚缓璁娇鐢? tc('text_cd0t9') i18n/no-chinese-hardcode - 26:11 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "缁忓吀宸︿晶鑿滃崟瀵艰埅"銆傚缓璁娇鐢? tc('text_mxmofl') i18n/no-chinese-hardcode - 28:26 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "椤堕儴"銆傚缓璁娇鐢? tc('text_qq7m') i18n/no-chinese-hardcode - 28:78 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "椤堕儴姘村钩鑿滃崟瀵艰埅"銆傚缓璁娇鐢? tc('text_7ephzs') i18n/no-chinese-hardcode - 31:12 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "娣峰悎"銆傚缓璁娇鐢? tc('text_j5yp') i18n/no-chinese-hardcode - 33:11 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "椤堕儴+渚ц竟鏍忔贩鍚堝鑸?銆傚缓璁娇鐢? tc('text_i2bf8j') i18n/no-chinese-hardcode - 55:44 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "涓婚璁剧疆"銆傚缓璁娇鐢? {tc('text_ai8tm5')} i18n/no-chinese-hardcode - 57:26 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鑷畾涔夊鑸ā寮忋€佷晶杈规爮鏍峰紡鍙婅瑙夎緟鍔╁姛鑳?銆傚缓璁娇鐢? {tc('text_qmxxuu')} i18n/no-chinese-hardcode - 62:52 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "瀵艰埅妯″紡"銆傚缓璁娇鐢? {tc('text_c58wfw')} i18n/no-chinese-hardcode - 103:50 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍔熻兘寮€鍏?銆傚缓璁娇鐢? {tc('text_ayi6up')} i18n/no-chinese-hardcode - 109:42 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "渚ц竟鏍忔繁鑹?銆傚缓璁娇鐢? {tc('text_p5e62q')} i18n/no-chinese-hardcode - 110:64 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "渚ц竟鏍忎娇鐢ㄦ繁鑹茶儗鏅?銆傚缓璁娇鐢? {tc('text_qfu66u')} i18n/no-chinese-hardcode - 120:42 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鑹插急妯″紡"銆傚缓璁娇鐢? {tc('text_gurej1')} i18n/no-chinese-hardcode - 121:64 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "涓鸿壊寮辩敤鎴蜂紭鍖栬瑙夊姣斿害"銆傚缓璁娇鐢? {tc('text_6tibmm')} i18n/no-chinese-hardcode - 131:42 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鐏拌壊妯″紡"銆傚缓璁娇鐢? {tc('text_eq5p5c')} i18n/no-chinese-hardcode - 132:64 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍏ㄧ珯鐏板害婊ら暅"銆傚缓璁娇鐢? {tc('text_8ekgv5')} i18n/no-chinese-hardcode - 143:51 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鎭㈠榛樿"銆傚缓璁娇鐢? {tc('text_cjgauv')} i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\components\theme-toggle.tsx - 22:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\components\theme-toggle.tsx:22:5 - 20 | - 21 | useEffect(() => { -> 22 | setMounted(true); - | ^^^^^^^^^^ Avoid calling setState() directly within an effect - 23 | // 浠?localStorage 璇诲彇涓婚璁剧疆 - 24 | const savedTheme = localStorage.getItem('theme') as Theme; - 25 | if (savedTheme) { react-hooks/set-state-in-effect - 27:7 warning Error: Cannot access variable before it is declared - -`applyTheme` is accessed before it is declared, which prevents the earlier access from updating when this value changes over time. - -D:\dcprint\erp-project\src\components\theme-toggle.tsx:27:7 - 25 | if (savedTheme) { - 26 | setTheme(savedTheme); -> 27 | applyTheme(savedTheme); - | ^^^^^^^^^^ `applyTheme` accessed before it is declared - 28 | } - 29 | }, []); - 30 | - -D:\dcprint\erp-project\src\components\theme-toggle.tsx:31:3 - 29 | }, []); - 30 | -> 31 | const applyTheme = (newTheme: Theme) => { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 32 | const root = document.documentElement; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 33 | - 鈥? | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 46 | } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 47 | }; - | ^^^^^ `applyTheme` is declared here - 48 | - 49 | const handleThemeChange = (newTheme: Theme) => { - 50 | setTheme(newTheme); react-hooks/immutability - -D:\dcprint\erp-project\src\components\ui\advanced-search.tsx - 74:58 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "楂樼骇鎼滅储"銆傚缓璁娇鐢? {tc('text_k24nth')} i18n/no-chinese-hardcode - 86:51 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "绛涢€夋潯浠?銆傚缓璁娇鐢? {tc('text_g5ph6r')} i18n/no-chinese-hardcode - 87:95 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "閲嶇疆"銆傚缓璁娇鐢? {tc('text_phz5')} i18n/no-chinese-hardcode - 112:53 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍏ㄩ儴"銆傚缓璁娇鐢? {tc('text_en40')} i18n/no-chinese-hardcode - 133:82 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍙栨秷"銆傚缓璁娇鐢? {tc('text_ev02')} i18n/no-chinese-hardcode - 137:52 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鎼滅储"銆傚缓璁娇鐢? {tc('text_hpqe')} i18n/no-chinese-hardcode - 164:12 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "娓呴櫎鍏ㄩ儴"銆傚缓璁娇鐢? {tc('text_ehzovz')} i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\components\ui\batch-toolbar.tsx - 67:55 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "宸查€?銆傚缓璁娇鐢? {tc('text_grpz')} i18n/no-chinese-hardcode - 69:89 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "椤?銆傚缓璁娇鐢? {tc('text_u49')} i18n/no-chinese-hardcode - 72:41 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍙栨秷閫夋嫨"銆傚缓璁娇鐢? {tc('text_b1em0i')} i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\components\ui\carousel.tsx - 96:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\components\ui\carousel.tsx:96:5 - 94 | React.useEffect(() => { - 95 | if (!api) return; -> 96 | onSelect(api); - | ^^^^^^^^ Avoid calling setState() directly within an effect - 97 | api.on('reInit', onSelect); - 98 | api.on('select', onSelect); - 99 | react-hooks/set-state-in-effect - -D:\dcprint\erp-project\src\components\ui\global-export-toolbar.tsx - 158:30 warning 'idx' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars - -D:\dcprint\erp-project\src\components\ui\global-import-dialog.tsx - 123:7 warning Error: Cannot access variable before it is declared - -`handleClose` is accessed before it is declared, which prevents the earlier access from updating when this value changes over time. - -D:\dcprint\erp-project\src\components\ui\global-import-dialog.tsx:123:7 - 121 | await onConfirm(result.validRows); - 122 | toast.success(`${result.validRows.length} ${t('rowsImported') || '琛屽鍏ユ垚鍔?}`); -> 123 | handleClose(); - | ^^^^^^^^^^^ `handleClose` accessed before it is declared - 124 | } catch (error) { - 125 | toast.error(`${t('importFailed') || '瀵煎叆澶辫触'}: ${(error as Error).message}`); - 126 | } finally { - -D:\dcprint\erp-project\src\components\ui\global-import-dialog.tsx:131:3 - 129 | }, [result, onConfirm, t]); - 130 | -> 131 | const handleClose = useCallback(() => { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> 132 | onOpenChange(false); - | ^^^^^^^^^^^^^^^^^^^^^^^^ -> 133 | setResult(null); - | ^^^^^^^^^^^^^^^^^^^^^^^^ -> 134 | setFileName(''); - | ^^^^^^^^^^^^^^^^^^^^^^^^ -> 135 | if (fileInputRef.current) { - | ^^^^^^^^^^^^^^^^^^^^^^^^ -> 136 | fileInputRef.current.value = ''; - | ^^^^^^^^^^^^^^^^^^^^^^^^ -> 137 | } - | ^^^^^^^^^^^^^^^^^^^^^^^^ -> 138 | }, [onOpenChange]); - | ^^^^^^^^^^^^^^^^^^^^^^ `handleClose` is declared here - 139 | - 140 | return ( - 141 | <Dialog open={open} onOpenChange={handleClose}> react-hooks/immutability - 129:6 warning React Hook useCallback has a missing dependency: 'handleClose'. Either include it or remove the dependency array react-hooks/exhaustive-deps - 131:35 warning Compilation Skipped: Existing memoization could not be preserved - -React Compiler has skipped optimizing this component because the existing manual memoization could not be preserved. This value was memoized in source but not in compilation output. - -D:\dcprint\erp-project\src\components\ui\global-import-dialog.tsx:131:35 - 129 | }, [result, onConfirm, t]); - 130 | -> 131 | const handleClose = useCallback(() => { - | ^^^^^^^ -> 132 | onOpenChange(false); - | ^^^^^^^^^^^^^^^^^^^^^^^^ -> 133 | setResult(null); - | ^^^^^^^^^^^^^^^^^^^^^^^^ -> 134 | setFileName(''); - | ^^^^^^^^^^^^^^^^^^^^^^^^ -> 135 | if (fileInputRef.current) { - | ^^^^^^^^^^^^^^^^^^^^^^^^ -> 136 | fileInputRef.current.value = ''; - | ^^^^^^^^^^^^^^^^^^^^^^^^ -> 137 | } - | ^^^^^^^^^^^^^^^^^^^^^^^^ -> 138 | }, [onOpenChange]); - | ^^^^ Could not preserve existing memoization - 139 | - 140 | return ( - 141 | <Dialog open={open} onOpenChange={handleClose}> react-hooks/preserve-manual-memoization - 237:57 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "琛?銆傚缓璁娇鐢? {tc('text_qx8')} i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\components\ui\material-picker.tsx - 27:10 warning 'page' is assigned a value but never used. Allowed unused elements of array destructuring must match /^_/u @typescript-eslint/no-unused-vars - 56:7 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\components\ui\material-picker.tsx:56:7 - 54 | useEffect(() => { - 55 | if (open) { -> 56 | setKeyword(''); - | ^^^^^^^^^^ Avoid calling setState() directly within an effect - 57 | setPage(1); - 58 | setHighlightIdx(-1); - 59 | fetchMaterials('', 1); react-hooks/set-state-in-effect - 180:12 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "ESC 鍏抽棴"銆傚缓璁娇鐢? {tc('text_xzcyo5')} i18n/no-chinese-hardcode - 197:19 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鐗╂枡缂栫爜"銆傚缓璁娇鐢? {tc('text_euzqpn')} i18n/no-chinese-hardcode - 198:19 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鐗╂枡鍚嶇О"銆傚缓璁娇鐢? {tc('text_eusfkj')} i18n/no-chinese-hardcode - 199:19 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "瑙勬牸"銆傚缓璁娇鐢? {tc('text_o06w')} i18n/no-chinese-hardcode - 200:19 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍗曚綅"銆傚缓璁娇鐢? {tc('text_ely0')} i18n/no-chinese-hardcode - 201:19 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍙傝€冨崟浠?銆傚缓璁娇鐢? {tc('text_b3gupv')} i18n/no-chinese-hardcode - 209:14 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍔犺浇涓?.."銆傚缓璁娇鐢? {tc('text_27k1ha')} i18n/no-chinese-hardcode - 276:17 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍏?銆傚缓璁娇鐢? {tc('text_g35')} i18n/no-chinese-hardcode - 280:17 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鈫戔啌 閫夋嫨 路 Enter 纭 路 Esc 鍏抽棴"銆傚缓璁娇鐢? {tc('text_6nti4b')} i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\components\ui\sortable-table.tsx - 28:5 warning The attribute aria-sort is not supported by the role button jsx-a11y/role-supports-aria-props - -D:\dcprint\erp-project\src\components\ui\table-export-toolbar.tsx - 27:3 warning 'onSelectAll' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars - 28:3 warning 'onDeselectAll' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars - -D:\dcprint\erp-project\src\components\ui\warehouse-select.tsx - 93:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\components\ui\warehouse-select.tsx:93:5 - 91 | - 92 | useEffect(() => { -> 93 | fetchWarehouses(); - | ^^^^^^^^^^^^^^^ Avoid calling setState() directly within an effect - 94 | }, [fetchWarehouses]); - 95 | - 96 | // 鏍规嵁閫変腑鐨勫垎绫昏繃婊や粨搴? react-hooks/set-state-in-effect - 119:37 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "鍏ㄩ儴鍒嗙被"銆傚缓璁娇鐢? {tc('text_av9kmt')} i18n/no-chinese-hardcode - 134:49 warning 绂佹鍦↗SX涓‖缂栫爜涓枃鏂囨湰: "璇ュ垎绫讳笅鏆傛棤浠撳簱"銆傚缓璁娇鐢? {tc('text_58kx7z')} i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\contexts\AuthContext.tsx - 84:10 warning 'loadCachedMenus' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 144:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\contexts\AuthContext.tsx:144:5 - 142 | - 143 | useEffect(() => { -> 144 | setIsHydrated(true); - | ^^^^^^^^^^^^^ Avoid calling setState() directly within an effect - 145 | }, []); - 146 | - 147 | const authChecked = useRef(false); react-hooks/set-state-in-effect - 332:61 warning 'e' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars - 404:5 warning React Hook useCallback has a missing dependency: 'state'. Either include it or remove the dependency array react-hooks/exhaustive-deps - -D:\dcprint\erp-project\src\domain\cost\aggregates\CostAnalysis.ts - 54:11 warning 'overheadRate' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - -D:\dcprint\erp-project\src\domain\dcprint\aggregates\InkFormulaVersion.ts - 464:23 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 528:28 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鑽夌"銆傚缓璁娇鐢? tc('text_n02e') i18n/no-chinese-hardcode - 529:29 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "宸茬敓鏁?銆傚缓璁娇鐢? tc('text_ebukb') i18n/no-chinese-hardcode - 530:32 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "宸蹭綔搴?銆傚缓璁娇鐢? tc('text_e5e0l') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\domain\dcprint\repositories\IFormulaVersionRepository.ts - 61:33 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 62:37 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 68:23 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 69:14 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 70:28 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - -D:\dcprint\erp-project\src\domain\finance\value-objects\PayableStatus.ts - 13:6 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏈粯娆?銆傚缓璁娇鐢? tc('text_fhzbk') i18n/no-chinese-hardcode - 14:6 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "閮ㄥ垎浠樻"銆傚缓璁娇鐢? tc('text_imd7no') i18n/no-chinese-hardcode - 15:6 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "宸茬粨娓?銆傚缓璁娇鐢? tc('text_edjpg') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\domain\finance\value-objects\ReceivableStatus.ts - 14:6 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏈敹娆?銆傚缓璁娇鐢? tc('text_flsaa') i18n/no-chinese-hardcode - 15:6 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "閮ㄥ垎鏀舵"銆傚缓璁娇鐢? tc('text_imh0me') i18n/no-chinese-hardcode - 16:6 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "宸茬粨娓?銆傚缓璁娇鐢? tc('text_edjpg') i18n/no-chinese-hardcode - 17:6 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "宸插潖璐?銆傚缓璁娇鐢? tc('text_e6zvt') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\domain\finance\value-objects\VoucherStatus.ts - 15:6 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鑽夌"銆傚缓璁娇鐢? tc('text_n02e') i18n/no-chinese-hardcode - 16:6 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "宸叉彁浜?銆傚缓璁娇鐢? tc('text_e8s3q') i18n/no-chinese-hardcode - 17:6 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "宸插鏍?銆傚缓璁娇鐢? tc('text_e7j1l') i18n/no-chinese-hardcode - 18:6 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "宸茶璐?銆傚缓璁娇鐢? tc('text_efwmg') i18n/no-chinese-hardcode - 19:6 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "宸蹭綔搴?銆傚缓璁娇鐢? tc('text_e5e0l') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\domain\hr\aggregates\Employee.ts - 67:11 warning 'confirmedBy' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars - -D:\dcprint\erp-project\src\domain\hr\infrastructure\EmployeeRepository.ts - 1:1 warning domain 灞傜姝㈠鍏?"drizzle-orm"銆侱DD 鍒嗗眰绾︽潫锛氶鍩熷眰涓嶅簲渚濊禆鍩虹璁炬柦/妗嗘灦锛屽簲鐢ㄥ眰涓嶅簲渚濊禆琛ㄧ幇灞?妗嗘灦锛屽熀纭€璁炬柦灞備笉搴斾緷璧栬〃鐜板眰銆? ddd/layer-dependencies - 2:1 warning domain 灞傜姝㈠鍏?"@/lib/db"銆侱DD 鍒嗗眰绾︽潫锛氶鍩熷眰涓嶅簲渚濊禆鍩虹璁炬柦/妗嗘灦锛屽簲鐢ㄥ眰涓嶅簲渚濊禆琛ㄧ幇灞?妗嗘灦锛屽熀纭€璁炬柦灞備笉搴斾緷璧栬〃鐜板眰銆? ddd/layer-dependencies - 3:1 warning domain 灞傜姝㈠鍏?"@/lib/db/schema"銆侱DD 鍒嗗眰绾︽潫锛氶鍩熷眰涓嶅簲渚濊禆鍩虹璁炬柦/妗嗘灦锛屽簲鐢ㄥ眰涓嶅簲渚濊禆琛ㄧ幇灞?妗嗘灦锛屽熀纭€璁炬柦灞備笉搴斾緷璧栬〃鐜板眰銆? ddd/layer-dependencies - -D:\dcprint\erp-project\src\domain\hr\infrastructure\SalaryCalculationRepository.ts - 1:1 warning domain 灞傜姝㈠鍏?"drizzle-orm"銆侱DD 鍒嗗眰绾︽潫锛氶鍩熷眰涓嶅簲渚濊禆鍩虹璁炬柦/妗嗘灦锛屽簲鐢ㄥ眰涓嶅簲渚濊禆琛ㄧ幇灞?妗嗘灦锛屽熀纭€璁炬柦灞備笉搴斾緷璧栬〃鐜板眰銆? ddd/layer-dependencies - 2:1 warning domain 灞傜姝㈠鍏?"@/lib/db"銆侱DD 鍒嗗眰绾︽潫锛氶鍩熷眰涓嶅簲渚濊禆鍩虹璁炬柦/妗嗘灦锛屽簲鐢ㄥ眰涓嶅簲渚濊禆琛ㄧ幇灞?妗嗘灦锛屽熀纭€璁炬柦灞備笉搴斾緷璧栬〃鐜板眰銆? ddd/layer-dependencies - 3:1 warning domain 灞傜姝㈠鍏?"@/lib/db/schema"銆侱DD 鍒嗗眰绾︽潫锛氶鍩熷眰涓嶅簲渚濊禆鍩虹璁炬柦/妗嗘灦锛屽簲鐢ㄥ眰涓嶅簲渚濊禆琛ㄧ幇灞?妗嗘灦锛屽熀纭€璁炬柦灞備笉搴斾緷璧栬〃鐜板眰銆? ddd/layer-dependencies - -D:\dcprint\erp-project\src\domain\prepress\aggregates\Die.ts - 5:3 warning 'DieStatusChangedEvent' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 7:3 warning 'DieMaintenanceCreatedEvent' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 164:11 warning 'prevCumulative' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - -D:\dcprint\erp-project\src\domain\prepress\value-objects\InkSpecification.ts - 1:10 warning 'DomainError' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - -D:\dcprint\erp-project\src\domain\production\value-objects\WorkOrderStateMachine.ts - 50:12 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "寰呯‘璁?銆傚缓璁娇鐢? tc('text_ekx8b') i18n/no-chinese-hardcode - 56:12 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "宸茬‘璁?銆傚缓璁娇鐢? tc('text_ecmeg') i18n/no-chinese-hardcode - 62:12 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "澶囨枡涓?銆傚缓璁娇鐢? tc('text_dj5dn') i18n/no-chinese-hardcode - 68:12 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "澶囨枡瀹屾垚"銆傚缓璁娇鐢? tc('text_bnkfzq') i18n/no-chinese-hardcode - 74:12 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鐢熶骇涓?銆傚缓璁娇鐢? tc('text_hjdtx') i18n/no-chinese-hardcode - 80:12 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "寰呮楠?銆傚缓璁娇鐢? tc('text_eic5t') i18n/no-chinese-hardcode - 86:12 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "妫€楠岄€氳繃"銆傚缓璁娇鐢? tc('text_duyw8p') i18n/no-chinese-hardcode - 92:12 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "妫€楠屽け璐?銆傚缓璁娇鐢? tc('text_dupjhc') i18n/no-chinese-hardcode - 98:12 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "杩斿伐涓?銆傚缓璁娇鐢? tc('text_lisvw') i18n/no-chinese-hardcode - 104:12 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "宸插畬鎴?銆傚缓璁娇鐢? tc('text_e7hbq') i18n/no-chinese-hardcode - 110:12 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "宸插彇娑?銆傚缓璁娇鐢? tc('text_e68dg') i18n/no-chinese-hardcode - 119:12 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "寰呭鐞?銆傚缓璁娇鐢? tc('text_efg7b') i18n/no-chinese-hardcode - 124:12 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "杩涜涓?銆傚缓璁娇鐢? tc('text_lq5q4') i18n/no-chinese-hardcode - 129:12 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "宸插畬鎴?銆傚缓璁娇鐢? tc('text_e7hbq') i18n/no-chinese-hardcode - 134:12 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "宸茶烦杩?銆傚缓璁娇鐢? tc('text_egazq') i18n/no-chinese-hardcode - 139:12 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "澶辫触"銆傚缓璁娇鐢? tc('text_fy1g') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\domain\purchase\value-objects\PurchaseReconciliationStatus.ts - 15:6 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鑽夌"銆傚缓璁娇鐢? tc('text_n02e') i18n/no-chinese-hardcode - 16:6 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "宸茬‘璁?銆傚缓璁娇鐢? tc('text_ecmeg') i18n/no-chinese-hardcode - 17:6 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "閮ㄥ垎鏍搁攢"銆傚缓璁娇鐢? tc('text_imhrae') i18n/no-chinese-hardcode - 18:6 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "宸叉牳閿€瀹屾垚"銆傚缓璁娇鐢? tc('text_pxlari') i18n/no-chinese-hardcode - 19:6 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "宸插叧闂?銆傚缓璁娇鐢? tc('text_e61qk') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\domain\purchase\value-objects\PurchaseReturnStatus.ts - 14:6 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "寰呭鏍?銆傚缓璁娇鐢? tc('text_eftvg') i18n/no-chinese-hardcode - 15:6 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "宸插鏍?銆傚缓璁娇鐢? tc('text_e7j1l') i18n/no-chinese-hardcode - 16:6 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "宸插畬鎴?銆傚缓璁娇鐢? tc('text_e7hbq') i18n/no-chinese-hardcode - 17:6 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "宸插彇娑?銆傚缓璁娇鐢? tc('text_e68dg') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\domain\quality\aggregates\SGSCertificate.ts - 1:10 warning 'DomainEvent' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - -D:\dcprint\erp-project\src\domain\sales\value-objects\DeliveryStatus.ts - 14:6 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "寰呭彂璐?銆傚缓璁娇鐢? tc('text_eepbv') i18n/no-chinese-hardcode - 15:6 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "宸插彂璐?銆傚缓璁娇鐢? tc('text_e6ei0') i18n/no-chinese-hardcode - 16:6 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "宸茬鏀?銆傚缓璁娇鐢? tc('text_ecxka') i18n/no-chinese-hardcode - 17:6 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "宸插彇娑?銆傚缓璁娇鐢? tc('text_e68dg') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\domain\sales\value-objects\ReconciliationStatus.ts - 15:6 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鑽夌"銆傚缓璁娇鐢? tc('text_n02e') i18n/no-chinese-hardcode - 16:6 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "宸茬‘璁?銆傚缓璁娇鐢? tc('text_ecmeg') i18n/no-chinese-hardcode - 17:6 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "閮ㄥ垎鏍搁攢"銆傚缓璁娇鐢? tc('text_imhrae') i18n/no-chinese-hardcode - 18:6 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "宸叉牳閿€瀹屾垚"銆傚缓璁娇鐢? tc('text_pxlari') i18n/no-chinese-hardcode - 19:6 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "宸插叧闂?銆傚缓璁娇鐢? tc('text_e61qk') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\domain\sales\value-objects\ReturnOrderStatus.ts - 14:6 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "寰呭鏍?銆傚缓璁娇鐢? tc('text_eftvg') i18n/no-chinese-hardcode - 15:6 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "宸插鏍?銆傚缓璁娇鐢? tc('text_e7j1l') i18n/no-chinese-hardcode - 16:6 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "宸插畬鎴?銆傚缓璁娇鐢? tc('text_e7hbq') i18n/no-chinese-hardcode - 17:6 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "宸插彇娑?銆傚缓璁娇鐢? tc('text_e68dg') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\domain\sample\entities\SampleInventory.ts - 84:14 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 85:14 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - -D:\dcprint\erp-project\src\domain\shipment\value-objects\ShipmentStateMachine.ts - 24:12 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鑽夌"銆傚缓璁娇鐢? tc('text_n02e') i18n/no-chinese-hardcode - 31:12 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "寰呭鎵?銆傚缓璁娇鐢? tc('text_efsql') i18n/no-chinese-hardcode - 38:12 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "寰呭彂璐?銆傚缓璁娇鐢? tc('text_eepbv') i18n/no-chinese-hardcode - 45:12 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鎷h揣涓?銆傚缓璁娇鐢? tc('text_f5v61') i18n/no-chinese-hardcode - 52:12 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "宸叉嫞璐?銆傚缓璁娇鐢? tc('text_e8ys6') i18n/no-chinese-hardcode - 59:12 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "閮ㄥ垎鍙戣揣"銆傚缓璁娇鐢? tc('text_ime8t0') i18n/no-chinese-hardcode - 66:12 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "宸插彂璐?銆傚缓璁娇鐢? tc('text_e6ei0') i18n/no-chinese-hardcode - 73:12 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "杩愯緭涓?銆傚缓璁娇鐢? tc('text_lr64q') i18n/no-chinese-hardcode - 80:12 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "宸茬鏀?銆傚缓璁娇鐢? tc('text_ecxka') i18n/no-chinese-hardcode - 87:12 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "宸查€€璐?銆傚缓璁娇鐢? tc('text_egn15') i18n/no-chinese-hardcode - 94:12 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "宸插彇娑?銆傚缓璁娇鐢? tc('text_e68dg') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\domain\standard-card\events\StandardCardEvents.ts - 5:12 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - -D:\dcprint\erp-project\src\domain\standard-card\repositories\IStandardCardRepository.ts - 36:57 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 37:44 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 42:57 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 43:44 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 48:57 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 49:44 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 54:57 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 55:48 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 60:57 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 61:43 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 66:57 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 67:47 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 72:57 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 73:44 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 79:57 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 80:13 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - -D:\dcprint\erp-project\src\domain\warehouse\aggregates\TransferOrder.ts - 225:11 warning 'operatorId' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars - 225:32 warning 'operatorName' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars - 246:13 warning 'operatorId' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars - 246:34 warning 'operatorName' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars - -D:\dcprint\erp-project\src\domain\warehouse\value-objects\StocktakingStatus.ts - 15:6 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鑽夌"銆傚缓璁娇鐢? tc('text_n02e') i18n/no-chinese-hardcode - 16:6 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "杩涜涓?銆傚缓璁娇鐢? tc('text_lq5q4') i18n/no-chinese-hardcode - 17:6 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "寰呭鎵?銆傚缓璁娇鐢? tc('text_efsql') i18n/no-chinese-hardcode - 18:6 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "宸插鎵?銆傚缓璁娇鐢? tc('text_e7hwq') i18n/no-chinese-hardcode - 19:6 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "宸插彇娑?銆傚缓璁娇鐢? tc('text_e68dg') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\domain\warehouse\value-objects\TransferStatus.ts - 15:6 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鑽夌"銆傚缓璁娇鐢? tc('text_n02e') i18n/no-chinese-hardcode - 16:6 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "寰呭鎵?銆傚缓璁娇鐢? tc('text_efsql') i18n/no-chinese-hardcode - 17:6 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "宸插嚭搴?銆傚缓璁娇鐢? tc('text_e5u17') i18n/no-chinese-hardcode - 18:6 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "宸插叆搴?銆傚缓璁娇鐢? tc('text_e5qgw') i18n/no-chinese-hardcode - 19:6 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "宸插彇娑?銆傚缓璁娇鐢? tc('text_e68dg') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\hooks\use-mobile.ts - 14:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\hooks\use-mobile.ts:14:5 - 12 | }; - 13 | mql.addEventListener('change', onChange); -> 14 | setIsMobile(window.innerWidth < MOBILE_BREAKPOINT); - | ^^^^^^^^^^^ Avoid calling setState() directly within an effect - 15 | return () => mql.removeEventListener('change', onChange); - 16 | }, []); - 17 | react-hooks/set-state-in-effect - -D:\dcprint\erp-project\src\hooks\usePermission.ts - 135:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\hooks\usePermission.ts:135:5 - 133 | - 134 | useEffect(() => { -> 135 | loadPermissions(); - | ^^^^^^^^^^^^^^^ Avoid calling setState() directly within an effect - 136 | }, [loadPermissions]); - 137 | - 138 | return { react-hooks/set-state-in-effect - -D:\dcprint\erp-project\src\hooks\useSampleProcessForm.ts - 122:24 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍔犺浇澶辫触"銆傚缓璁娇鐢? tc('text_b0mmk1') i18n/no-chinese-hardcode - 131:17 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\hooks\useSampleProcessForm.ts:131:17 - 129 | - 130 | useEffect(() => { -> 131 | if (cardId) loadCard(cardId); - | ^^^^^^^^ Avoid calling setState() directly within an effect - 132 | }, [cardId, loadCard]); - 133 | - 134 | // 瀛楁鏇存柊 react-hooks/set-state-in-effect - 259:24 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鑽夌宸叉仮澶?銆傚缓璁娇鐢? tc('text_vx3s7b') i18n/no-chinese-hardcode - 284:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鍥剧ず涓婁紶鎴愬姛"銆傚缓璁娇鐢? tc('text_in4py9') i18n/no-chinese-hardcode - 287:24 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "涓婁紶澶辫触"銆傚缓璁娇鐢? tc('text_a6dm22') i18n/no-chinese-hardcode - 290:24 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "涓婁紶澶辫触"銆傚缓璁娇鐢? tc('text_a6dm22') i18n/no-chinese-hardcode - 326:26 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "妯℃澘宸插鍏?銆傚缓璁娇鐢? tc('text_2833fh') i18n/no-chinese-hardcode - 329:24 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "瀵煎叆澶辫触"銆傚缓璁娇鐢? tc('text_by141p') i18n/no-chinese-hardcode - 332:24 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "瀵煎叆澶辫触"銆傚缓璁娇鐢? tc('text_by141p') i18n/no-chinese-hardcode - 379:24 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "淇濆瓨澶辫触"銆傚缓璁娇鐢? tc('text_agg8dr') i18n/no-chinese-hardcode - 383:22 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "淇濆瓨澶辫触"銆傚缓璁娇鐢? tc('text_agg8dr') i18n/no-chinese-hardcode - 393:22 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鏍¢獙澶辫触"銆傚缓璁娇鐢? tc('text_drw0kf') i18n/no-chinese-hardcode - 393:43 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "璇锋鏌ュ繀濉」"銆傚缓璁娇鐢? tc('text_egep61') i18n/no-chinese-hardcode - 408:24 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鎻愪氦鎴愬姛"銆傚缓璁娇鐢? tc('text_cx7dr7') i18n/no-chinese-hardcode - 412:24 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鎻愪氦澶辫触"銆傚缓璁娇鐢? tc('text_cx66zs') i18n/no-chinese-hardcode - 416:22 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鎻愪氦澶辫触"銆傚缓璁娇鐢? tc('text_cx66zs') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\hooks\useSnowAdminTheme.tsx - 57:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\hooks\useSnowAdminTheme.tsx:57:5 - 55 | - 56 | useEffect(() => { -> 57 | setState(loadState()); - | ^^^^^^^^ Avoid calling setState() directly within an effect - 58 | setMounted(true); - 59 | }, []); - 60 | react-hooks/set-state-in-effect - -D:\dcprint\erp-project\src\hooks\useStandardCardForm.ts - 3:44 warning 'useRef' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 63:5 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\hooks\useStandardCardForm.ts:63:5 - 61 | - 62 | useEffect(() => { -> 63 | fetchCustomers(); - | ^^^^^^^^^^^^^^ Avoid calling setState() directly within an effect - 64 | }, [fetchCustomers]); - 65 | - 66 | const loadData = useCallback(async (id: string) => { react-hooks/set-state-in-effect - 86:7 warning Error: Calling setState synchronously within an effect can trigger cascading renders - -Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following: -* Update external systems with the latest state from React. -* Subscribe for updates from some external system, calling setState in a callback function when external state changes. - -Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect). - -D:\dcprint\erp-project\src\hooks\useStandardCardForm.ts:86:7 - 84 | useEffect(() => { - 85 | if (editId) { -> 86 | loadData(editId); - | ^^^^^^^^ Avoid calling setState() directly within an effect - 87 | } - 88 | }, [editId, loadData]); - 89 | react-hooks/set-state-in-effect - 188:21 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "[StandardCard:Save] 寮傚父:"銆傚缓璁娇鐢? tc('text_niwzwu') i18n/no-chinese-hardcode - 189:22 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "淇濆瓨澶辫触锛岃妫€鏌ョ綉缁滆繛鎺?銆傚缓璁娇鐢? tc('text_ivhynl') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\infrastructure\event-bus\DomainEventOutbox.ts - 21:79 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 23:45 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - -D:\dcprint\erp-project\src\infrastructure\event-bus\MemoryDomainEventOutbox.ts - 1:1 warning Unused eslint-disable directive (no problems were reported from '@typescript-eslint/no-unused-vars') - -D:\dcprint\erp-project\src\infrastructure\monitoring\DeadlockMonitor.ts - 41:38 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 64:38 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 101:43 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 119:35 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - -D:\dcprint\erp-project\src\infrastructure\providers\MaterialCostProvider.ts - 25:36 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 40:42 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - -D:\dcprint\erp-project\src\infrastructure\repositories\DrizzleInboundOrderRepository.ts - 310:5 warning 'financePosted' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars - -D:\dcprint\erp-project\src\infrastructure\repositories\DrizzlePurchaseOrderRepository.ts - 16:54 warning 'sql' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 31:32 warning 'FieldPacket' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - -D:\dcprint\erp-project\src\infrastructure\repositories\DrizzleSalesOrderRepository.ts - 19:54 warning 'sql' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - -D:\dcprint\erp-project\src\infrastructure\repositories\MysqlDomainEventOutboxRepository.ts - 81:13 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 81:20 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 204:25 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - -D:\dcprint\erp-project\src\infrastructure\repositories\MysqlFinishOrderRepository.ts - 7:15 warning 'ResultSetHeader' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 29:25 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 66:31 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 102:28 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - -D:\dcprint\erp-project\src\infrastructure\repositories\MysqlFormulaVersionRepository.ts - 33:30 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 42:30 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 51:31 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 56:37 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 61:30 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 67:27 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 71:30 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 82:30 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 87:25 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 215:30 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 248:31 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 273:39 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 274:30 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 281:43 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 282:30 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 294:23 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 309:35 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 315:30 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 331:20 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 350:34 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - -D:\dcprint\erp-project\src\infrastructure\repositories\MysqlInboundOrderRepository.ts - 32:32 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 40:31 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 61:31 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 128:44 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 135:40 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 136:35 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 143:42 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 149:33 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 166:45 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 259:5 warning 'financePosted' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars - -D:\dcprint\erp-project\src\infrastructure\repositories\MysqlOutboundOrderRepository.ts - 190:31 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 257:70 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - -D:\dcprint\erp-project\src\infrastructure\repositories\MysqlPickOrderRepository.ts - 7:15 warning 'ResultSetHeader' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 29:37 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 66:49 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 123:34 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 138:36 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - -D:\dcprint\erp-project\src\infrastructure\repositories\MysqlReconciliationRepository.ts - 13:6 warning 'SqlValue' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - -D:\dcprint\erp-project\src\infrastructure\repositories\MysqlSampleProcessCardRepository.ts - 7:26 warning 'transaction' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - -D:\dcprint\erp-project\src\infrastructure\repositories\MysqlSampleProcessTemplateRepository.ts - 4:3 warning 'TemplateItemProps' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 5:3 warning 'TemplateStepProps' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - -D:\dcprint\erp-project\src\infrastructure\repositories\MysqlScheduleRepository.ts - 5:39 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 12:62 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 19:57 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 34:11 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - -D:\dcprint\erp-project\src\infrastructure\repositories\MysqlStandardCardRepository.ts - 12:15 warning 'ResultSetHeader' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 227:28 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 265:63 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 271:50 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 297:63 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 304:50 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 334:63 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 340:50 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 366:63 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 376:54 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 400:63 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 410:49 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 433:63 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 439:53 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 461:63 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 467:50 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 492:63 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 503:19 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - -D:\dcprint\erp-project\src\infrastructure\repositories\MysqlStocktakingOrderRepository.ts - 111:44 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 118:40 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 119:35 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 126:42 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 132:33 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 165:12 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 249:32 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 249:44 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 265:31 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - -D:\dcprint\erp-project\src\infrastructure\repositories\MysqlToolRepository.ts - 1:26 warning 'transaction' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - -D:\dcprint\erp-project\src\infrastructure\repositories\MysqlTransferOrderRepository.ts - 116:44 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 123:40 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 124:35 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 131:42 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 137:33 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 172:12 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 239:32 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 239:44 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 258:31 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - -D:\dcprint\erp-project\src\infrastructure\repositories\MysqlWorkOrderRepository.ts - 72:33 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 102:12 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 141:29 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 141:45 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 161:55 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - -D:\dcprint\erp-project\src\infrastructure\repositories\MysqlWorkReportRepository.ts - 28:25 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 73:31 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 97:23 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 122:28 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - -D:\dcprint\erp-project\src\infrastructure\schedulers\BatchExpiryScheduler.ts - 64:42 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 93:38 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - -D:\dcprint\erp-project\src\lib\FinanceVoucherHandler.ts - 40:7 warning 'VOUCHER_SOURCE_LABELS' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 41:19 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "閿€鍞嚭搴?銆傚缓璁娇鐢? tc('text_j5fhqf') i18n/no-chinese-hardcode - 42:21 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "閲囪喘鍏ュ簱"銆傚缓璁娇鐢? tc('text_iyzvkk') i18n/no-chinese-hardcode - 43:23 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鐢熶骇鍏ュ簱"銆傚缓璁娇鐢? tc('text_f3pz5i') i18n/no-chinese-hardcode - 44:19 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鐢熶骇棰嗘枡"銆傚缓璁娇鐢? tc('text_f4243f') i18n/no-chinese-hardcode - 45:20 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏉愭枡閫€鍥?銆傚缓璁娇鐢? tc('text_dgoe07') i18n/no-chinese-hardcode - 46:17 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "搴撳瓨璋冩暣"銆傚缓璁娇鐢? tc('text_cbhcc6') i18n/no-chinese-hardcode - 47:20 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鎴愭湰鍒嗘憡"銆傚缓璁娇鐢? tc('text_css1ls') i18n/no-chinese-hardcode - 64:5 warning 'warehouseId' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars - 390:5 warning 'operatorName' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars - -D:\dcprint\erp-project\src\lib\api-auth.ts - 70:20 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏃犳潈闄愯闂?銆傚缓璁娇鐢? tc('text_e79fxg') i18n/no-chinese-hardcode - 115:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鏈彁渚涜璇佷护鐗?銆傚缓璁娇鐢? tc('text_jbxt8a') i18n/no-chinese-hardcode - 121:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "璁よ瘉浠ょ墝鏃犳晥鎴栧凡杩囨湡"銆傚缓璁娇鐢? tc('text_invqv5') i18n/no-chinese-hardcode - 127:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "璁よ瘉浠ょ墝宸插け鏁堬紝璇烽噸鏂扮櫥褰?銆傚缓璁娇鐢? tc('text_e7fxzo') i18n/no-chinese-hardcode - 132:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鐧诲綍鐘舵€佸凡澶辨晥锛堣处鍙峰凡鍙樻洿锛夛紝璇烽噸鏂扮櫥褰?銆傚缓璁娇鐢? tc('text_qcl5vs') i18n/no-chinese-hardcode - 138:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "鐢ㄦ埛涓嶅瓨鍦ㄦ垨宸茶绂佺敤"銆傚缓璁娇鐢? tc('text_en1so') i18n/no-chinese-hardcode - 147:28 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "娌℃湁鏉冮檺鎵ц姝ゆ搷浣?銆傚缓璁娇鐢? tc('text_g7pw9') i18n/no-chinese-hardcode - 158:32 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "娌℃湁鏉冮檺璁块棶姝よ祫婧?銆傚缓璁娇鐢? tc('text_lrlka4') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\lib\audit-logger.ts - 308:11 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "30鍒嗛挓鍐呭瘑鐮侀敊璇?銆傚缓璁娇鐢? tc(`text_j65thc`) i18n/no-chinese-hardcode - 308:11 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "娆? "銆傚缓璁娇鐢? tc(`text_foxdz`) i18n/no-chinese-hardcode - 326:46 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "寮傚湴鐧诲綍: 涓婃IP "銆傚缓璁娇鐢? tc(`text_m6w590`) i18n/no-chinese-hardcode - 326:46 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? ", 鏈IP "銆傚缓璁娇鐢? tc(`text_cf2o28`) i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\lib\audit-middleware.ts - 16:20 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "閲囪喘绠$悊"銆傚缓璁娇鐢? tc('text_iz76ff') i18n/no-chinese-hardcode - 17:17 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "閿€鍞鐞?銆傚缓璁娇鐢? tc('text_j5mp0z') i18n/no-chinese-hardcode - 18:21 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "搴撳瓨绠$悊"銆傚缓璁娇鐢? tc('text_cbemwa') i18n/no-chinese-hardcode - 19:22 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鐢熶骇绠$悊"銆傚缓璁娇鐢? tc('text_f3xa0d') i18n/no-chinese-hardcode - 20:19 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "璐㈠姟绠$悊"銆傚缓璁娇鐢? tc('text_i5j98k') i18n/no-chinese-hardcode - 21:19 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "璐ㄩ噺绠$悊"銆傚缓璁娇鐢? tc('text_iew8do') i18n/no-chinese-hardcode - 22:19 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍗板墠绠$悊"銆傚缓璁娇鐢? tc('text_aviioy') i18n/no-chinese-hardcode - 23:14 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "浜轰簨绠$悊"銆傚缓璁娇鐢? tc('text_a9kn6e') i18n/no-chinese-hardcode - 24:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "绯荤粺绠$悊"銆傚缓璁娇鐢? tc('text_gao8uh') i18n/no-chinese-hardcode - 25:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鎶ヨ〃涓績"銆傚缓璁娇鐢? tc('text_d09qzd') i18n/no-chinese-hardcode - 26:21 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏁版嵁鐪嬫澘"銆傚缓璁娇鐢? tc('text_d7qb82') i18n/no-chinese-hardcode - 34:8 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏌ヨ"銆傚缓璁娇鐢? tc('text_iftp') i18n/no-chinese-hardcode - 35:9 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏂板"銆傚缓璁娇鐢? tc('text_hs6m') i18n/no-chinese-hardcode - 36:8 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "淇敼"銆傚缓璁娇鐢? tc('text_e5fv') i18n/no-chinese-hardcode - 37:10 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "淇敼"銆傚缓璁娇鐢? tc('text_e5fv') i18n/no-chinese-hardcode - 38:11 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍒犻櫎"銆傚缓璁娇鐢? tc('text_eslg') i18n/no-chinese-hardcode - 45:29 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "閲囪喘绠$悊"銆傚缓璁娇鐢? tc('text_iz76ff') i18n/no-chinese-hardcode - 45:37 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "閿€鍞鐞?銆傚缓璁娇鐢? tc('text_j5mp0z') i18n/no-chinese-hardcode - 45:45 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "搴撳瓨绠$悊"銆傚缓璁娇鐢? tc('text_cbemwa') i18n/no-chinese-hardcode - 45:53 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鐢熶骇绠$悊"銆傚缓璁娇鐢? tc('text_f3xa0d') i18n/no-chinese-hardcode - 45:61 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "璐㈠姟绠$悊"銆傚缓璁娇鐢? tc('text_i5j98k') i18n/no-chinese-hardcode - 55:10 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "绯荤粺绠$悊"銆傚缓璁娇鐢? tc('text_gao8uh') i18n/no-chinese-hardcode - 60:44 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "瀹℃牳"銆傚缓璁娇鐢? tc('text_g5o7') i18n/no-chinese-hardcode - 61:45 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "浣滃簾"銆傚缓璁娇鐢? tc('text_e0n7') i18n/no-chinese-hardcode - 62:46 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "瀹℃牳"銆傚缓璁娇鐢? tc('text_g5o7') i18n/no-chinese-hardcode - 63:45 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍙嶅"銆傚缓璁娇鐢? tc('text_er90') i18n/no-chinese-hardcode - 64:45 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "瀵煎叆"銆傚缓璁娇鐢? tc('text_g3c9') i18n/no-chinese-hardcode - 65:45 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "瀵煎嚭"銆傚缓璁娇鐢? tc('text_g3ge') i18n/no-chinese-hardcode - 66:44 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鎵撳嵃"銆傚缓璁娇鐢? tc('text_h6kd') i18n/no-chinese-hardcode - 67:45 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鎻愪氦"銆傚缓璁娇鐢? tc('text_heqc') i18n/no-chinese-hardcode - 82:10 warning 'truncateString' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - -D:\dcprint\erp-project\src\lib\bom-expansion.ts - 79:11 warning 'BomNode' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 191:19 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "杈惧埌鏈€澶ч€掑綊娣卞害 "銆傚缓璁娇鐢? tc(`text_i8d2ha`) i18n/no-chinese-hardcode - 191:19 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "锛屼骇鍝?"銆傚缓璁娇鐢? tc(`text_whzowq`) i18n/no-chinese-hardcode - 191:19 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? " 鐨勫睍寮€宸插仠姝?銆傚缓璁娇鐢? tc(`text_7nq86h`) i18n/no-chinese-hardcode - 198:19 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "妫€娴嬪埌寰幆寮曠敤锛?銆傚缓璁娇鐢? tc(`text_pqwadf`) i18n/no-chinese-hardcode - 225:21 warning 'bomId' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - -D:\dcprint\erp-project\src\lib\cost-engine.ts - 553:5 warning 'period' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars - 554:5 warning 'workOrderId' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars - 612:5 warning 'period' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars - 613:5 warning 'workOrderId' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars - 651:5 warning 'period' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars - 652:5 warning 'workOrderId' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars - -D:\dcprint\erp-project\src\lib\data-diff.ts - 308:53 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "绌?銆傚缓璁娇鐢? tc('text_o6y') i18n/no-chinese-hardcode - 309:50 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏄?銆傚缓璁娇鐢? tc('text_k6n') i18n/no-chinese-hardcode - 309:56 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍚?銆傚缓璁娇鐢? tc('text_gme') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\lib\db\index.ts - 111:15 warning 'sqlStr' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - -D:\dcprint\erp-project\src\lib\demo-data.ts - 105:5 warning Unexpected console statement. Only these console methods are allowed: warn, error no-console - 132:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "浠〃鐩?銆傚缓璁娇鐢? tc('text_c7ysa') i18n/no-chinese-hardcode - 145:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "浠撳偍绠$悊"銆傚缓璁娇鐢? tc('text_aabr0a') i18n/no-chinese-hardcode - 158:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍏ュ簱绠$悊"銆傚缓璁娇鐢? tc('text_ao1adv') i18n/no-chinese-hardcode - 171:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍑哄簱绠$悊"銆傚缓璁娇鐢? tc('text_aqoffi') i18n/no-chinese-hardcode - 184:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "搴撳瓨鏌ヨ"銆傚缓璁娇鐢? tc('text_cbberm') i18n/no-chinese-hardcode - 197:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鐢熶骇绠$悊"銆傚缓璁娇鐢? tc('text_f3xa0d') i18n/no-chinese-hardcode - 210:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鐢熶骇宸ュ崟"銆傚缓璁娇鐢? tc('text_f3s1h4') i18n/no-chinese-hardcode - 223:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鎺掍骇绠$悊"銆傚缓璁娇鐢? tc('text_cw8dy2') i18n/no-chinese-hardcode - 236:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "棰嗘枡绠$悊"銆傚缓璁娇鐢? tc('text_jo17rs') i18n/no-chinese-hardcode - 249:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "閿€鍞鐞?銆傚缓璁娇鐢? tc('text_j5mp0z') i18n/no-chinese-hardcode - 262:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "閿€鍞鍗?銆傚缓璁娇鐢? tc('text_j5p8kh') i18n/no-chinese-hardcode - 275:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍙戣揣绠$悊"銆傚缓璁娇鐢? tc('text_b5us1n') i18n/no-chinese-hardcode - 288:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "閲囪喘绠$悊"銆傚缓璁娇鐢? tc('text_iz76ff') i18n/no-chinese-hardcode - 301:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "閲囪喘璁㈠崟"銆傚缓璁娇鐢? tc('text_iz9pyx') i18n/no-chinese-hardcode - 314:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍗板墠绠$悊"銆傚缓璁娇鐢? tc('text_aviioy') i18n/no-chinese-hardcode - 327:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "娌瑰ⅷ绠$悊"銆傚缓璁娇鐢? tc('text_e39784') i18n/no-chinese-hardcode - 340:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍒€妯$鐞?銆傚缓璁娇鐢? tc('text_ash6qu') i18n/no-chinese-hardcode - 353:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鎵撴牱绠$悊"銆傚缓璁娇鐢? tc('text_cubhl5') i18n/no-chinese-hardcode - 366:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鎵撴牱璁㈠崟"銆傚缓璁娇鐢? tc('text_cue14n') i18n/no-chinese-hardcode - 379:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "璐ㄩ噺绠$悊"銆傚缓璁娇鐢? tc('text_iew8do') i18n/no-chinese-hardcode - 392:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "璐㈠姟绠$悊"銆傚缓璁娇鐢? tc('text_i5j98k') i18n/no-chinese-hardcode - 405:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "绯荤粺璁剧疆"銆傚缓璁娇鐢? tc('text_gar1ro') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\lib\die-matcher.ts - 48:4 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "閽㈡潗"銆傚缓璁娇鐢? tc('text_pujy') i18n/no-chinese-hardcode - 48:10 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "閾佹潗"銆傚缓璁娇鐢? tc('text_pvan') i18n/no-chinese-hardcode - 48:16 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍚堥噾閽?銆傚缓璁娇鐢? tc('text_d0po9') i18n/no-chinese-hardcode - 48:23 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "纰抽挗"銆傚缓璁娇鐢? tc('text_lcgf') i18n/no-chinese-hardcode - 48:29 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "涓嶉攬閽?銆傚缓璁娇鐢? tc('text_c5q3r') i18n/no-chinese-hardcode - 48:36 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍚堥噾"銆傚缓璁娇鐢? tc('text_f3d5') i18n/no-chinese-hardcode - 49:4 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "灏奸緳缃?銆傚缓璁娇鐢? tc('text_ea710') i18n/no-chinese-hardcode - 49:11 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鑱氶叝缃?銆傚缓璁娇鐢? tc('text_ji8bg') i18n/no-chinese-hardcode - 49:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "涓嶉攬閽㈢綉"銆傚缓璁娇鐢? tc('text_agy6dm') i18n/no-chinese-hardcode - 49:26 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "涓濈綉"銆傚缓璁娇鐢? tc('text_dzh0') i18n/no-chinese-hardcode - 49:32 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鑱氶叝"銆傚缓璁娇鐢? tc('text_mmol') i18n/no-chinese-hardcode - 49:38 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鑱氶叞鑳?銆傚缓璁娇鐢? tc('text_ji8o4') i18n/no-chinese-hardcode - 50:4 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "姗¤兌"銆傚缓璁娇鐢? tc('text_isvp') i18n/no-chinese-hardcode - 50:10 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "娴风坏"銆傚缓璁娇鐢? tc('text_jbdq') i18n/no-chinese-hardcode - 50:16 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏍戣剛"銆傚缓璁娇鐢? tc('text_ieq9') i18n/no-chinese-hardcode - 50:22 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鑳跺甫"銆傚缓璁娇鐢? tc('text_mga8') i18n/no-chinese-hardcode - 51:4 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "绾告澘"銆傚缓璁娇鐢? tc('text_m4ef') i18n/no-chinese-hardcode - 51:10 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍙岄潰鑳?銆傚缓璁娇鐢? tc('text_d0bds') i18n/no-chinese-hardcode - 51:17 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鐗涚毊绾?銆傚缓璁娇鐢? tc('text_hbvj9') i18n/no-chinese-hardcode - 88:57 warning 'assetType' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars - -D:\dcprint\erp-project\src\lib\enum-status.ts - 110:3 warning 'statusEnum' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars - -D:\dcprint\erp-project\src\lib\error-handler.ts - 9:17 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鐧诲綍宸茶繃鏈燂紝璇烽噸鏂扮櫥褰?銆傚缓璁娇鐢? tc('text_6wvw9k') i18n/no-chinese-hardcode - 10:14 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鎮ㄦ病鏈夋潈闄愭墽琛屾鎿嶄綔"銆傚缓璁娇鐢? tc('text_9x73yn') i18n/no-chinese-hardcode - 11:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鐧诲綍鍑瘉鏃犳晥锛岃閲嶆柊鐧诲綍"銆傚缓璁娇鐢? tc('text_almp36') i18n/no-chinese-hardcode - 12:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鐧诲綍宸茶繃鏈燂紝璇烽噸鏂扮櫥褰?銆傚缓璁娇鐢? tc('text_6wvw9k') i18n/no-chinese-hardcode - 13:17 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鐢ㄦ埛鍚嶆垨瀵嗙爜閿欒"銆傚缓璁娇鐢? tc('text_pkd8t3') i18n/no-chinese-hardcode - 14:19 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "璐﹀彿宸查攣瀹氾紝璇风◢鍚庡啀璇?銆傚缓璁娇鐢? tc('text_9guj4i') i18n/no-chinese-hardcode - 15:21 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "璐﹀彿宸茶绂佺敤锛岃鑱旂郴绠$悊鍛?銆傚缓璁娇鐢? tc('text_gxsswg') i18n/no-chinese-hardcode - 18:22 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏉冮檺涓嶈冻锛岃鑱旂郴绠$悊鍛?銆傚缓璁娇鐢? tc('text_nzguxa') i18n/no-chinese-hardcode - 19:19 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "瑙掕壊涓嶅瓨鍦?銆傚缓璁娇鐢? tc('text_cl8w1v') i18n/no-chinese-hardcode - 20:28 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鎮ㄧ殑鏉冮檺涓嶈冻浠ユ墽琛屾鎿嶄綔"銆傚缓璁娇鐢? tc('text_id22o8') i18n/no-chinese-hardcode - 23:14 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "璇锋眰鐨勬暟鎹笉瀛樺湪"銆傚缓璁娇鐢? tc('text_ik8ssm') i18n/no-chinese-hardcode - 24:20 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏁版嵁宸插瓨鍦紝璇峰嬁閲嶅娣诲姞"銆傚缓璁娇鐢? tc('text_y5i39') i18n/no-chinese-hardcode - 25:25 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏁版嵁瀹屾暣鎬ч敊璇紝璇锋鏌ュ叧鑱旀暟鎹?銆傚缓璁娇鐢? tc('text_i9pw8q') i18n/no-chinese-hardcode - 26:22 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "璇ユ暟鎹鍏朵粬璁板綍寮曠敤锛屾棤娉曞垹闄?銆傚缓璁娇鐢? tc('text_afkdph') i18n/no-chinese-hardcode - 27:21 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏁版嵁楠岃瘉澶辫触锛岃妫€鏌ヨ緭鍏?銆傚缓璁娇鐢? tc('text_mprf49') i18n/no-chinese-hardcode - 30:23 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "搴撳瓨涓嶈冻锛屾棤娉曞嚭搴?銆傚缓璁娇鐢? tc('text_8maqb5') i18n/no-chinese-hardcode - 31:23 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "璁㈠崟鐘舵€佷笉鍏佽姝ゆ搷浣?銆傚缓璁娇鐢? tc('text_x1yr8t') i18n/no-chinese-hardcode - 32:21 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "璇ュ崟鎹鍦ㄥ鎵逛腑锛岃鍕块噸澶嶆彁浜?銆傚缓璁娇鐢? tc('text_b0kqom') i18n/no-chinese-hardcode - 33:21 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "璇ュ崟鎹凡瀹℃壒锛岃鍕块噸澶嶆搷浣?銆傚缓璁娇鐢? tc('text_2colyv') i18n/no-chinese-hardcode - 34:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鎵规宸茶繃鏈?銆傚缓璁娇鐢? tc('text_r8uwbi') i18n/no-chinese-hardcode - 35:27 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鎴愭湰璁$畻閿欒"銆傚缓璁娇鐢? tc('text_j9unxk') i18n/no-chinese-hardcode - 38:19 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "绯荤粺鍐呴儴閿欒锛岃绋嶅悗閲嶈瘯"銆傚缓璁娇鐢? tc('text_rlw7yn') i18n/no-chinese-hardcode - 39:19 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏁版嵁搴撴搷浣滃け璐ワ紝璇风◢鍚庨噸璇?銆傚缓璁娇鐢? tc('text_a4dkk') i18n/no-chinese-hardcode - 40:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "缃戠粶杩炴帴澶辫触锛岃妫€鏌ョ綉缁?銆傚缓璁娇鐢? tc('text_ki5ipr') i18n/no-chinese-hardcode - 41:17 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鎿嶄綔杩囦簬棰戠箒锛岃绋嶅悗鍐嶈瘯"銆傚缓璁娇鐢? tc('text_5o3e3e') i18n/no-chinese-hardcode - 42:24 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏈嶅姟鏆傛椂涓嶅彲鐢紝璇风◢鍚庨噸璇?銆傚缓璁娇鐢? tc('text_1ocdbm') i18n/no-chinese-hardcode - 47:8 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "璇锋眰鍙傛暟閿欒锛岃妫€鏌ヨ緭鍏?銆傚缓璁娇鐢? tc('text_iryeoh') i18n/no-chinese-hardcode - 48:8 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鐧诲綍宸茶繃鏈燂紝璇烽噸鏂扮櫥褰?銆傚缓璁娇鐢? tc('text_6wvw9k') i18n/no-chinese-hardcode - 49:8 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鎮ㄦ病鏈夋潈闄愭墽琛屾鎿嶄綔"銆傚缓璁娇鐢? tc('text_9x73yn') i18n/no-chinese-hardcode - 50:8 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "璇锋眰鐨勮祫婧愪笉瀛樺湪"銆傚缓璁娇鐢? tc('text_xmqreg') i18n/no-chinese-hardcode - 51:8 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏁版嵁鍐茬獊锛岃鍒锋柊鍚庨噸璇?銆傚缓璁娇鐢? tc('text_ze47ej') i18n/no-chinese-hardcode - 52:8 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏁版嵁楠岃瘉澶辫触锛岃妫€鏌ヨ緭鍏?銆傚缓璁娇鐢? tc('text_mprf49') i18n/no-chinese-hardcode - 53:8 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鎿嶄綔杩囦簬棰戠箒锛岃绋嶅悗鍐嶈瘯"銆傚缓璁娇鐢? tc('text_5o3e3e') i18n/no-chinese-hardcode - 54:8 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏈嶅姟鍣ㄥ唴閮ㄩ敊璇紝璇风◢鍚庨噸璇?銆傚缓璁娇鐢? tc('text_p9iz1b') i18n/no-chinese-hardcode - 55:8 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏈嶅姟鏆傛椂涓嶅彲鐢紝璇风◢鍚庨噸璇?銆傚缓璁娇鐢? tc('text_1ocdbm') i18n/no-chinese-hardcode - 56:8 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鏈嶅姟缁存姢涓紝璇风◢鍚庨噸璇?銆傚缓璁娇鐢? tc('text_5bg4fx') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\lib\fifo-allocation.ts - 369:11 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鎵规"銆傚缓璁娇鐢? tc(`text_hc5k`) i18n/no-chinese-hardcode - 369:11 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "涔愯閿佸啿绐? 鏈熸湜鐗堟湰"銆傚缓璁娇鐢? tc(`text_y6kl1g`) i18n/no-chinese-hardcode - 370:13 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "瀹為檯鐗堟湰"銆傚缓璁娇鐢? tc(`text_c6kbrf`) i18n/no-chinese-hardcode - 370:13 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? ", 鍙敤閲?銆傚缓璁娇鐢? tc(`text_11morm`) i18n/no-chinese-hardcode - 373:23 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "FIFO搴撳瓨鏇存柊澶辫触: 鎵规"銆傚缓璁娇鐢? tc(`text_ibxwhb`) i18n/no-chinese-hardcode - 373:23 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "锛屽彲鑳藉凡琚叾浠栨搷浣滀慨鏀?銆傚缓璁娇鐢? tc(`text_tkd8oj`) i18n/no-chinese-hardcode - 413:9 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "FIFO鍑哄簱-鎵规"銆傚缓璁娇鐢? tc(`text_boa39s`) i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\lib\general-ledger.ts - 726:21 warning 'monthStr' is assigned a value but never used. Allowed unused elements of array destructuring must match /^_/u @typescript-eslint/no-unused-vars - 727:11 warning 'firstPeriod' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - -D:\dcprint\erp-project\src\lib\global-export-service.ts - 106:25 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "涓嶆敮鎸佺殑瀵煎嚭鏍煎紡: "銆傚缓璁娇鐢? tc(`text_vombok`) i18n/no-chinese-hardcode - 130:23 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "瀵煎嚭鏃堕棿: "銆傚缓璁娇鐢? tc(`text_nncopq`) i18n/no-chinese-hardcode - 275:17 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? " | 瀵煎嚭鏃堕棿: "銆傚缓璁娇鐢? tc(`text_g9fmn2`) i18n/no-chinese-hardcode - 276:17 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "瀵煎嚭鏃堕棿: "銆傚缓璁娇鐢? tc(`text_nncopq`) i18n/no-chinese-hardcode - 378:13 warning 绂佹鍦ㄥ嚱鏁拌皟鐢ㄤ腑纭紪鐮佷腑鏂囧弬鏁? "璇峰厑璁稿脊鍑虹獥鍙d互浣跨敤鎵撳嵃鍔熻兘"銆傚缓璁娇鐢? tc('text_7rjfzi') i18n/no-chinese-hardcode - 378:13 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "璇峰厑璁稿脊鍑虹獥鍙d互浣跨敤鎵撳嵃鍔熻兘"銆傚缓璁娇鐢? tc('text_7rjfzi') i18n/no-chinese-hardcode - 396:32 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "瀵煎嚭鏃堕棿: "銆傚缓璁娇鐢? tc(`text_nncopq`) i18n/no-chinese-hardcode - 401:27 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鎵撳嵃"銆傚缓璁娇鐢? tc('text_h6kd') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\lib\global-import-service.ts - 172:23 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍒?"銆傚缓璁娇鐢? tc(`text_dyp7`) i18n/no-chinese-hardcode - 172:23 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? ""涓嶈兘涓虹┖"銆傚缓璁娇鐢? tc(`text_awdhrm`) i18n/no-chinese-hardcode - 228:21 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "瀛楁璇存槑:"銆傚缓璁娇鐢? tc('text_g5e9ya') i18n/no-chinese-hardcode - 230:34 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? " (蹇呭~)"銆傚缓璁娇鐢? tc('text_wqzkr') i18n/no-chinese-hardcode - 230:44 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? " (閫夊~)"銆傚缓璁娇鐢? tc('text_13thpb') i18n/no-chinese-hardcode - 239:42 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "瀵煎叆妯℃澘"銆傚缓璁娇鐢? tc('text_by3sbr') i18n/no-chinese-hardcode - 247:18 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "_瀵煎叆妯℃澘.xlsx"銆傚缓璁娇鐢? tc(`text_50aksf`) i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\lib\hr\overtime-calculator.ts - 61:7 error 'holidayHours' is never reassigned. Use 'const' instead prefer-const - -D:\dcprint\erp-project\src\lib\hr\performance-calculator.ts - 16:11 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "浜ч噺杈炬垚鐜?銆傚缓璁娇鐢? tc('text_smxdn7') i18n/no-chinese-hardcode - 17:11 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "璐ㄩ噺鍚堟牸鐜?銆傚缓璁娇鐢? tc('text_2f0lvw') i18n/no-chinese-hardcode - 18:11 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "璁惧绋煎姩鐜?銆傚缓璁娇鐢? tc('text_a4cqdq') i18n/no-chinese-hardcode - 19:11 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "5S鐜板満绠$悊"銆傚缓璁娇鐢? tc('text_tpf0dv') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\lib\hr\salary-engine.ts - 3:27 warning 'hrSalaryCalculation' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 184:3 warning 'employeeId' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars - 185:3 warning 'month' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars - -D:\dcprint\erp-project\src\lib\logger.ts - 43:28 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "\n 鏁版嵁: "銆傚缓璁娇鐢? tc(`text_x8iegm`) i18n/no-chinese-hardcode - 70:24 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鈻?寮€濮? "銆傚缓璁娇鐢? tc(`text_x98bdx`) i18n/no-chinese-hardcode - 77:24 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鉁?瀹屾垚: "銆傚缓璁娇鐢? tc(`text_a05jyi`) i18n/no-chinese-hardcode - 92:7 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鈶?鍒嗘敮["銆傚缓璁娇鐢? tc(`text_2gd6c`) i18n/no-chinese-hardcode - 92:7 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "]: 鏉′欢=""銆傚缓璁娇鐢? tc(`text_ea68n7`) i18n/no-chinese-hardcode - 92:59 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鉁?鍛戒腑"銆傚缓璁娇鐢? tc('text_4xumbh') i18n/no-chinese-hardcode - 92:68 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鉁?鏈懡涓?銆傚缓璁娇鐢? tc('text_b849kh') i18n/no-chinese-hardcode - 109:27 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "馃攽 鏉冮檺["銆傚缓璁娇鐢? tc(`text_c4uqci`) i18n/no-chinese-hardcode - 109:27 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "]: 鉁?閫氳繃"銆傚缓璁娇鐢? tc(`text_mp02lf`) i18n/no-chinese-hardcode - 111:26 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "馃攽 鏉冮檺["銆傚缓璁娇鐢? tc(`text_c4uqci`) i18n/no-chinese-hardcode - 111:26 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "]: 鉁?鎷掔粷"銆傚缓璁娇鐢? tc(`text_mp5b9l`) i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\lib\mrp-engine-v2.ts - 245:3 warning 'periodSupplyDays' is assigned a value but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars - 858:5 warning 'warehouseId' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars - 966:11 warning 'buckets' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - -D:\dcprint\erp-project\src\lib\mrp-engine.ts - 67:11 warning 'MaterialInfoRow' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 297:9 warning 'matInfoRows' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 797:19 warning 'r' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars - 837:3 warning 'operatorName' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars - -D:\dcprint\erp-project\src\lib\multi-color-printing.ts - 312:3 warning 'substrateType' is assigned a value but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars - -D:\dcprint\erp-project\src\lib\performance\frontend-optimization.ts - 140:31 warning 'componentName' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars - -D:\dcprint\erp-project\src\lib\qrcode-service.ts - 17:30 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 85:35 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 150:47 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 200:47 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 244:5 warning 'productName' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars - 245:5 warning 'options' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars - 251:47 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 348:33 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - -D:\dcprint\erp-project\src\lib\sanitize.ts - 20:48 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 34:60 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 35:89 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 37:34 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 38:69 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 56:57 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 58:32 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - -D:\dcprint\erp-project\src\lib\soft-delete.ts - 154:5 warning 'module' is assigned a value but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars - 253:3 warning 'restoredById' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars - 389:42 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "閲囪喘鍗曞凡瀛樺湪鍏ュ簱璁板綍锛屼笉鍙綔搴?銆傚缓璁娇鐢? tc('text_eojxth') i18n/no-chinese-hardcode - 402:42 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "閿€鍞崟宸插瓨鍦ㄥ嚭搴撹褰曪紝涓嶅彲浣滃簾"銆傚缓璁娇鐢? tc('text_2fnyrc') i18n/no-chinese-hardcode - 413:42 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "宸ュ崟宸插紑濮嬬敓浜э紝涓嶅彲浣滃簾"銆傚缓璁娇鐢? tc('text_m221k4') i18n/no-chinese-hardcode - 429:42 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "璋冩嫧鍗曞凡瀹屾垚锛屼笉鍙綔搴?銆傚缓璁娇鐢? tc('text_1fuvuz') i18n/no-chinese-hardcode - 441:42 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "宸蹭粯娆捐褰曚笉鍙綔搴?銆傚缓璁娇鐢? tc('text_5cui82') i18n/no-chinese-hardcode - 450:42 warning 绂佹纭紪鐮佷腑鏂囨枃鏈? "鍑瘉宸茶繃璐︼紝涓嶅彲浣滃簾"銆傚缓璁娇鐢? tc('text_7696wk') i18n/no-chinese-hardcode - -D:\dcprint\erp-project\src\lib\system-config.ts - 66:26 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - -D:\dcprint\erp-project\src\lib\validation.ts - 40:10 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 44:20 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 61:31 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 151:38 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 201:40 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 227:48 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 357:59 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 403:32 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 418:37 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 430:29 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 439:35 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 456:33 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 478:44 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - -D:\dcprint\erp-project\src\services\CostAmortizationService.ts - 164:31 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 208:39 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 237:34 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - -D:\dcprint\erp-project\src\test\fixtures\index.ts - 4:67 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 9:43 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 15:49 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - -D:\dcprint\erp-project\src\test\setup.ts - 21:3 warning Unused eslint-disable directive (no problems were reported from '@typescript-eslint/no-extraneous-class') - 33:8 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - -D:\dcprint\erp-project\src\types\swagger-ui-react.d.ts - 3:34 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - -鉁?5119 problems (2 errors, 5117 warnings) - 1 error and 3 warnings potentially fixable with the `--fix` option. - diff --git a/eslint-baseline.json b/eslint-baseline.json deleted file mode 100644 index 24824032..00000000 --- a/eslint-baseline.json +++ /dev/null @@ -1 +0,0 @@ -[{"filePath":"D:\\dcprint\\erp-project\\src\\app\\[locale]\\advanced\\cost-analysis\\page.tsx","messages":[{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":31,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":31,"endColumn":36},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":32,"column":16,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":32,"endColumn":23},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction<ProfitRow[]>`.","line":32,"column":39,"nodeType":"MemberExpression","messageId":"unsafeArgument","endLine":32,"endColumn":48},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":32,"column":44,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":32,"endColumn":48},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":38,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":38,"endColumn":36},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":39,"column":16,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":39,"endColumn":23},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction<{ A: number[]; B: number[]; C: number[]; }>`.","line":39,"column":36,"nodeType":"MemberExpression","messageId":"unsafeArgument","endLine":39,"endColumn":45},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":39,"column":41,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":39,"endColumn":45},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"A 类物料\"。建议使用: {tc('text_1j3gks')}","line":49,"column":26,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":49,"endColumn":31},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"B 类物料\"。建议使用: {tc('text_1jn965')}","line":57,"column":26,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":57,"endColumn":31},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"C 类物料\"。建议使用: {tc('text_1k71ri')}","line":65,"column":26,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":65,"endColumn":31},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"产品利润分析\"。建议使用: {tc('text_2azgrl')}","line":75,"column":24,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":75,"endColumn":30},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"产品ID\"。建议使用: {tc('text_a9jn9x')}","line":81,"column":49,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":81,"endColumn":53},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"收入\"。建议使用: {tc('text_hnu7')}","line":82,"column":50,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":82,"endColumn":52},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"直接成本\"。建议使用: {tc('text_ff70nh')}","line":83,"column":50,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":83,"endColumn":54},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"间接费用\"。建议使用: {tc('text_jc5rls')}","line":84,"column":50,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":84,"endColumn":54},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"毛利率\"。建议使用: {tc('text_g7btl')}","line":85,"column":50,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":85,"endColumn":53},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"净利率\"。建议使用: {tc('text_cdoam')}","line":86,"column":50,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":86,"endColumn":53}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":18,"fixableErrorCount":0,"fixableWarningCount":0,"source":"'use client';\r\n\r\nimport { useState, useEffect } from 'react';\r\nimport { MainLayout } from '@/components/layout';\r\nimport { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';\r\nimport { authFetch } from '@/lib/auth-fetch';\r\n\r\ninterface ProfitRow {\r\n productId: number | string;\r\n revenue: number;\r\n directCost: number;\r\n overhead: number;\r\n grossMargin: number;\r\n netMargin: number;\r\n}\r\n\r\nexport default function CostAnalysisPage() {\r\n const [_activeTab, _setActiveTab] = useState('profit');\r\n const [profitData, setProfitData] = useState<ProfitRow[]>([]);\r\n const [abcData, setAbcData] = useState<{ A: number[]; B: number[]; C: number[] }>({\r\n A: [],\r\n B: [],\r\n C: [],\r\n });\r\n\r\n useEffect(() => {\r\n authFetch('/api/advanced/cost-analysis', {\r\n method: 'POST',\r\n body: JSON.stringify({ action: 'profit-analysis' }),\r\n }).then(async (res) => {\r\n const data = await res.json();\r\n if (data.success) setProfitData(data.data);\r\n });\r\n authFetch('/api/advanced/cost-analysis', {\r\n method: 'POST',\r\n body: JSON.stringify({ action: 'abc-classification' }),\r\n }).then(async (res) => {\r\n const data = await res.json();\r\n if (data.success) setAbcData(data.data);\r\n });\r\n }, []);\r\n\r\n return (\r\n <MainLayout title=\"成本分析中心\">\r\n <div className=\"space-y-6\">\r\n <div className=\"grid grid-cols-3 gap-4\">\r\n <Card>\r\n <CardHeader>\r\n <CardTitle>A 类物料</CardTitle>\r\n </CardHeader>\r\n <CardContent>\r\n <div className=\"text-2xl font-bold\">{abcData.A.length}</div>\r\n </CardContent>\r\n </Card>\r\n <Card>\r\n <CardHeader>\r\n <CardTitle>B 类物料</CardTitle>\r\n </CardHeader>\r\n <CardContent>\r\n <div className=\"text-2xl font-bold\">{abcData.B.length}</div>\r\n </CardContent>\r\n </Card>\r\n <Card>\r\n <CardHeader>\r\n <CardTitle>C 类物料</CardTitle>\r\n </CardHeader>\r\n <CardContent>\r\n <div className=\"text-2xl font-bold\">{abcData.C.length}</div>\r\n </CardContent>\r\n </Card>\r\n </div>\r\n\r\n <Card>\r\n <CardHeader>\r\n <CardTitle>产品利润分析</CardTitle>\r\n </CardHeader>\r\n <CardContent>\r\n <table className=\"w-full\">\r\n <thead>\r\n <tr className=\"border-b\">\r\n <th className=\"text-left p-2\">产品ID</th>\r\n <th className=\"text-right p-2\">收入</th>\r\n <th className=\"text-right p-2\">直接成本</th>\r\n <th className=\"text-right p-2\">间接费用</th>\r\n <th className=\"text-right p-2\">毛利率</th>\r\n <th className=\"text-right p-2\">净利率</th>\r\n </tr>\r\n </thead>\r\n <tbody>\r\n {profitData.map((p: ProfitRow) => (\r\n <tr key={p.productId} className=\"border-b\">\r\n <td className=\"p-2\">#{p.productId}</td>\r\n <td className=\"text-right p-2\">¥{Number(p.revenue).toLocaleString()}</td>\r\n <td className=\"text-right p-2\">¥{Number(p.directCost).toLocaleString()}</td>\r\n <td className=\"text-right p-2\">¥{Number(p.overhead).toLocaleString()}</td>\r\n <td\r\n className={`text-right p-2 ${p.grossMargin < 20 ? 'text-red-500' : 'text-green-500'}`}\r\n >\r\n {p.grossMargin.toFixed(1)}%\r\n </td>\r\n <td\r\n className={`text-right p-2 ${p.netMargin < 10 ? 'text-red-500' : 'text-green-500'}`}\r\n >\r\n {p.netMargin.toFixed(1)}%\r\n </td>\r\n </tr>\r\n ))}\r\n </tbody>\r\n </table>\r\n </CardContent>\r\n </Card>\r\n </div>\r\n </MainLayout>\r\n );\r\n}\r\n","usedDeprecatedRules":[]},{"filePath":"D:\\dcprint\\erp-project\\src\\app\\[locale]\\advanced\\dashboard\\page.tsx","messages":[{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":19,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":19,"endColumn":38},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":20,"column":18,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":20,"endColumn":25},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction<{ currentMonth: Record<string, number>; trend: { month: string; value: number; }[]; alerts: { level: \"warning\" | \"critical\" | \"info\"; message: string; }[]; }>`.","line":20,"column":35,"nodeType":"MemberExpression","messageId":"unsafeArgument","endLine":20,"endColumn":46},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":20,"column":42,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":20,"endColumn":46},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"月销售收入\"。建议使用: {tc('text_46k8d7')}","line":50,"column":46,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":50,"endColumn":51},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"个订单\"。建议使用: {tc('text_c4d5p')}","line":57,"column":48,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":58,"endColumn":15},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"订单完成率\"。建议使用: {tc('text_be14uo')}","line":63,"column":46,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":63,"endColumn":51},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"库存周转率\"。建议使用: {tc('text_qifwzi')}","line":75,"column":46,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":75,"endColumn":51},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"库存价值\"。建议使用: {tc('text_cb6ubu')}","line":87,"column":46,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":87,"endColumn":50},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"6 个月收入趋势\"。建议使用: {tc('text_gw5r05')}","line":99,"column":24,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":99,"endColumn":32},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"智能建议\"。建议使用: {tc('text_dgo4jb')}","line":125,"column":24,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":125,"endColumn":28},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"当前无预警,运行状态良好\"。建议使用: {tc('text_ardo13')}","line":128,"column":45,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":128,"endColumn":57}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":12,"fixableErrorCount":0,"fixableWarningCount":0,"source":"'use client';\n\nimport { useState, useEffect } from 'react';\nimport { MainLayout } from '@/components/layout';\nimport { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';\nimport { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert';\nimport { AlertCircle, AlertTriangle, Info } from 'lucide-react';\nimport { authFetch } from '@/lib/auth-fetch';\n\nexport default function AdvancedDashboardPage() {\n const [data, setData] = useState<{\n currentMonth: Record<string, number>;\n trend: Array<{ month: string; value: number }>;\n alerts: Array<{ level: 'critical' | 'warning' | 'info'; message: string }>;\n }>({ currentMonth: {}, trend: [], alerts: [] });\n\n useEffect(() => {\n authFetch('/api/advanced/dashboard').then(async (res) => {\n const result = await res.json();\n if (result.success) setData(result.data);\n });\n }, []);\n\n const alertIcons = { critical: AlertCircle, warning: AlertTriangle, info: Info };\n const alertVariants = {\n critical: 'destructive' as const,\n warning: 'default' as const,\n info: 'default' as const,\n };\n\n return (\n <MainLayout title=\"决策支持中心\">\n <div className=\"space-y-6\">\n {data.alerts.map((alert, idx) => {\n const Icon = alertIcons[alert.level];\n return (\n <Alert key={idx} variant={alertVariants[alert.level]}>\n <Icon className=\"h-4 w-4\" />\n <AlertTitle>\n {alert.level === 'critical' ? '紧急' : alert.level === 'warning' ? '预警' : '提示'}\n </AlertTitle>\n <AlertDescription>{alert.message}</AlertDescription>\n </Alert>\n );\n })}\n\n <div className=\"grid grid-cols-4 gap-4\">\n <Card>\n <CardHeader>\n <CardTitle className=\"text-sm\">月销售收入</CardTitle>\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold\">\n ¥{Number(data.currentMonth.revenue || 0).toLocaleString()}\n </div>\n <p className=\"text-xs text-muted-foreground\">\n {data.currentMonth.orders || 0} 个订单\n </p>\n </CardContent>\n </Card>\n <Card>\n <CardHeader>\n <CardTitle className=\"text-sm\">订单完成率</CardTitle>\n </CardHeader>\n <CardContent>\n <div\n className={`text-2xl font-bold ${(data.currentMonth.fulfillmentRate || 0) < 95 ? 'text-red-500' : 'text-green-500'}`}\n >\n {Number(data.currentMonth.fulfillmentRate || 0).toFixed(1)}%\n </div>\n </CardContent>\n </Card>\n <Card>\n <CardHeader>\n <CardTitle className=\"text-sm\">库存周转率</CardTitle>\n </CardHeader>\n <CardContent>\n <div\n className={`text-2xl font-bold ${(data.currentMonth.turnoverRate || 0) < 4 ? 'text-yellow-500' : 'text-green-500'}`}\n >\n {Number(data.currentMonth.turnoverRate || 0).toFixed(1)}x\n </div>\n </CardContent>\n </Card>\n <Card>\n <CardHeader>\n <CardTitle className=\"text-sm\">库存价值</CardTitle>\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold\">\n ¥{Number(data.currentMonth.inventoryValue || 0).toLocaleString()}\n </div>\n </CardContent>\n </Card>\n </div>\n\n <Card>\n <CardHeader>\n <CardTitle>6 个月收入趋势</CardTitle>\n </CardHeader>\n <CardContent>\n <div className=\"space-y-2\">\n {data.trend.map((item) => (\n <div key={item.month} className=\"flex items-center gap-4\">\n <span className=\"w-20 text-sm\">{item.month}</span>\n <div className=\"flex-1 h-6 bg-secondary rounded-full overflow-hidden\">\n <div\n className=\"h-full bg-primary rounded-full transition-all\"\n style={{\n width: `${Math.min(100, (Number(item.value) / Math.max(...data.trend.map((t) => Number(t.value)))) * 100)}%`,\n }}\n />\n </div>\n <span className=\"w-24 text-right text-sm\">\n ¥{Number(item.value).toLocaleString()}\n </span>\n </div>\n ))}\n </div>\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle>智能建议</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-2 text-sm\">\n {data.alerts.length === 0 && <p>当前无预警,运行状态良好</p>}\n {data.alerts.map((alert, idx) => (\n <div key={idx} className=\"flex items-start gap-2\">\n <span className=\"mt-1\">•</span>\n <span>{alert.message}</span>\n </div>\n ))}\n </CardContent>\n </Card>\n </div>\n </MainLayout>\n );\n}\n","usedDeprecatedRules":[]},{"filePath":"D:\\dcprint\\erp-project\\src\\app\\[locale]\\analysis\\db-relations\\page.tsx","messages":[{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction<DbRelations | null>`.","line":82,"column":17,"nodeType":"Identifier","messageId":"unsafeArgument","endLine":82,"endColumn":21},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"个\"。建议使用: {tc('text_ffu')}","line":361,"column":40,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":362,"endColumn":15}],"suppressedMessages":[{"ruleId":"@next/next/no-img-element","severity":1,"message":"Using `<img>` could result in slower LCP and higher bandwidth. Consider using `<Image />` from `next/image` or a custom image loader to automatically optimize images. This may incur additional usage or cost from your provider. See: https://nextjs.org/docs/messages/no-img-element","line":172,"column":13,"nodeType":"JSXOpeningElement","endLine":176,"endColumn":15,"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"@next/next/no-img-element","severity":1,"message":"Using `<img>` could result in slower LCP and higher bandwidth. Consider using `<Image />` from `next/image` or a custom image loader to automatically optimize images. This may incur additional usage or cost from your provider. See: https://nextjs.org/docs/messages/no-img-element","line":187,"column":13,"nodeType":"JSXOpeningElement","endLine":191,"endColumn":15,"suppressions":[{"kind":"directive","justification":""}]}],"errorCount":0,"fatalErrorCount":0,"warningCount":2,"fixableErrorCount":0,"fixableWarningCount":0,"source":"'use client';\r\n\r\nimport { useState, useEffect } from 'react';\r\nimport { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';\r\nimport { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';\r\nimport { Badge } from '@/components/ui/badge';\r\nimport {\r\n Table,\r\n TableBody,\r\n TableCell,\r\n TableHead,\r\n TableHeader,\r\n TableRow,\r\n} from '@/components/ui/table';\r\nimport { ScrollArea } from '@/components/ui/scroll-area';\r\nimport { useTranslations } from 'next-intl';\r\n\r\n/* eslint-disable @next/next/no-img-element */\r\n\r\ninterface TableInfo {\r\n name: string;\r\n comment: string;\r\n rows: number;\r\n}\r\n\r\ninterface Relation {\r\n fromTable: string;\r\n fromColumn: string;\r\n toTable: string;\r\n toColumn: string;\r\n type: string;\r\n constraint?: string;\r\n}\r\n\r\ninterface ModuleData {\r\n [key: string]: TableInfo[];\r\n}\r\n\r\ninterface DbRelations {\r\n tables: TableInfo[];\r\n foreignKeys: Relation[];\r\n logicalRelations: Relation[];\r\n modules: ModuleData;\r\n}\r\n\r\nexport default function DbRelationsPage() {\r\n // 翻译钩子\r\n const tc = useTranslations('Common');\r\n\r\n const moduleColors: Record<string, string> = {\r\n system: 'bg-blue-500',\r\n order: 'bg-green-500',\r\n product: 'bg-yellow-500',\r\n partner: 'bg-purple-500',\r\n production: 'bg-cyan-500',\r\n inventory: 'bg-orange-500',\r\n finance: 'bg-pink-500',\r\n sample: 'bg-lime-500',\r\n other: 'bg-gray-500',\r\n };\r\n\r\n const moduleLabels: Record<string, string> = {\r\n system: '系统管理',\r\n order: '订单管理',\r\n product: '产品管理',\r\n partner: '合作伙伴',\r\n production: '生产管理',\r\n inventory: '库存管理',\r\n finance: '财务管理',\r\n sample: '样品管理',\r\n other: '其他',\r\n };\r\n\r\n const [data, setData] = useState<DbRelations | null>(null);\r\n const [loading, setLoading] = useState(true);\r\n const [selectedModule, setSelectedModule] = useState<string | null>(null);\r\n\r\n useEffect(() => {\r\n fetch('/api/db-relations')\r\n .then((res) => res.json())\r\n .then((data) => {\r\n setData(data);\r\n setLoading(false);\r\n })\r\n .catch((_err) => {\r\n setLoading(false);\r\n });\r\n }, []);\r\n\r\n if (loading) {\r\n return (\r\n <div className=\"flex items-center justify-center min-h-screen\">\r\n <div className=\"text-center\">\r\n <div className=\"animate-spin rounded-full h-12 w-12 border-b-2 border-primary mx-auto mb-4\"></div>\r\n <p className=\"text-muted-foreground\">{tc('analysisLoading')}</p>\r\n </div>\r\n </div>\r\n );\r\n }\r\n\r\n if (!data) {\r\n return (\r\n <div className=\"flex items-center justify-center min-h-screen\">\r\n <p className=\"text-destructive\">{tc('analysisLoadFailed')}</p>\r\n </div>\r\n );\r\n }\r\n\r\n const totalTables = data.tables.length;\r\n const totalForeignKeys = data.foreignKeys.length;\r\n const totalLogicalRelations = data.logicalRelations.length;\r\n\r\n return (\r\n <div className=\"container mx-auto py-6 space-y-6\">\r\n {/* 页面标题 */}\r\n <div className=\"flex flex-col gap-2\">\r\n <h1 className=\"text-3xl font-bold tracking-tight\">{tc('analysisPageTitle')}</h1>\r\n <p className=\"text-muted-foreground\">\r\n {tc('analysisPageDescPrefix')}\r\n {totalTables}\r\n {tc('analysisPageDescSuffix')}\r\n </p>\r\n </div>\r\n\r\n {/* 统计卡片 */}\r\n <div className=\"grid grid-cols-1 md:grid-cols-4 gap-4\">\r\n <Card>\r\n <CardHeader className=\"pb-2\">\r\n <CardTitle className=\"text-sm font-medium\">{tc('analysisTotalTables')}</CardTitle>\r\n </CardHeader>\r\n <CardContent>\r\n <div className=\"text-2xl font-bold\">{totalTables}</div>\r\n </CardContent>\r\n </Card>\r\n <Card>\r\n <CardHeader className=\"pb-2\">\r\n <CardTitle className=\"text-sm font-medium\">{tc('analysisFkTitle')}</CardTitle>\r\n </CardHeader>\r\n <CardContent>\r\n <div className=\"text-2xl font-bold text-red-500\">{totalForeignKeys}</div>\r\n </CardContent>\r\n </Card>\r\n <Card>\r\n <CardHeader className=\"pb-2\">\r\n <CardTitle className=\"text-sm font-medium\">{tc('analysisLogicalCardTitle')}</CardTitle>\r\n </CardHeader>\r\n <CardContent>\r\n <div className=\"text-2xl font-bold text-blue-500\">{totalLogicalRelations}</div>\r\n </CardContent>\r\n </Card>\r\n <Card>\r\n <CardHeader className=\"pb-2\">\r\n <CardTitle className=\"text-sm font-medium\">{tc('analysisModuleCount')}</CardTitle>\r\n </CardHeader>\r\n <CardContent>\r\n <div className=\"text-2xl font-bold text-green-500\">\r\n {Object.keys(data.modules).length}\r\n </div>\r\n </CardContent>\r\n </Card>\r\n </div>\r\n\r\n {/* 图表展示 */}\r\n <div className=\"grid grid-cols-1 md:grid-cols-2 gap-6\">\r\n {/* 模块分布饼图 */}\r\n <Card>\r\n <CardHeader>\r\n <CardTitle>{tc('analysisModuleDistribution')}</CardTitle>\r\n <CardDescription>{tc('analysisModuleDistDesc')}</CardDescription>\r\n </CardHeader>\r\n <CardContent>\r\n <img\r\n src=\"https://mdn.alipayobjects.com/one_clip/afts/img/AW7BSbaam-gAAAAARSAAAAgAoEACAQFr/original\"\r\n alt={tc('analysisModulePieAlt')}\r\n className=\"w-full h-auto\"\r\n />\r\n </CardContent>\r\n </Card>\r\n\r\n {/* 关联数量柱状图 */}\r\n <Card>\r\n <CardHeader>\r\n <CardTitle>{tc('analysisTop20Relations')}</CardTitle>\r\n <CardDescription>{tc('analysisTopRelDesc')}</CardDescription>\r\n </CardHeader>\r\n <CardContent>\r\n <img\r\n src=\"https://mdn.alipayobjects.com/one_clip/afts/img/GnogTL6uYV8AAAAAQ0AAAAgAoEACAQFr/original\"\r\n alt={tc('analysisRelBarAlt')}\r\n className=\"w-full h-auto\"\r\n />\r\n </CardContent>\r\n </Card>\r\n </div>\r\n\r\n {/* 详细信息标签页 */}\r\n <Tabs defaultValue=\"modules\" className=\"w-full\">\r\n <TabsList className=\"grid w-full grid-cols-4\">\r\n <TabsTrigger value=\"modules\">{tc('analysisModulesTab')}</TabsTrigger>\r\n <TabsTrigger value=\"tables\">{tc('analysisAllTablesTab')}</TabsTrigger>\r\n <TabsTrigger value=\"foreignkeys\">{tc('analysisFkTab')}</TabsTrigger>\r\n <TabsTrigger value=\"logical\">{tc('analysisLogicalTab')}</TabsTrigger>\r\n </TabsList>\r\n\r\n {/* 模块分组 */}\r\n <TabsContent value=\"modules\">\r\n <Card>\r\n <CardHeader>\r\n <CardTitle>{tc('analysisModulesTab')}</CardTitle>\r\n <CardDescription>{tc('analysisModuleGroupDesc')}</CardDescription>\r\n </CardHeader>\r\n <CardContent>\r\n <div className=\"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4\">\r\n {Object.entries(data.modules).map(([module, tables]) => (\r\n <Card\r\n key={module}\r\n className={`cursor-pointer transition-all hover:shadow-lg ${selectedModule === module ? 'ring-2 ring-primary' : ''}`}\r\n onClick={() => setSelectedModule(selectedModule === module ? null : module)}\r\n >\r\n <CardHeader className=\"pb-2\">\r\n <div className=\"flex items-center justify-between\">\r\n <CardTitle className=\"text-lg flex items-center gap-2\">\r\n <Badge className={moduleColors[module]}>\r\n {moduleLabels[module] || module}\r\n </Badge>\r\n </CardTitle>\r\n <Badge variant=\"outline\">\r\n {tables.length}\r\n {tc('analysisTablesUnit')}\r\n </Badge>\r\n </div>\r\n </CardHeader>\r\n <CardContent>\r\n <ScrollArea className=\"h-[200px]\">\r\n <div className=\"space-y-2\">\r\n {tables.map((table) => (\r\n <div\r\n key={table.name}\r\n className=\"flex items-center justify-between p-2 rounded-lg hover:bg-muted\"\r\n >\r\n <div className=\"flex-1 min-w-0\">\r\n <p className=\"font-medium truncate\">{table.name}</p>\r\n <p className=\"text-xs text-muted-foreground truncate\">\r\n {table.comment || tc('analysisNoComment')}\r\n </p>\r\n </div>\r\n <Badge variant=\"secondary\" className=\"ml-2\">\r\n {table.rows}\r\n {tc('analysisRowsSuffix')}\r\n </Badge>\r\n </div>\r\n ))}\r\n </div>\r\n </ScrollArea>\r\n </CardContent>\r\n </Card>\r\n ))}\r\n </div>\r\n </CardContent>\r\n </Card>\r\n </TabsContent>\r\n\r\n {/* 所有表 */}\r\n <TabsContent value=\"tables\">\r\n <Card>\r\n <CardHeader>\r\n <CardTitle>{tc('analysisAllTables')}</CardTitle>\r\n <CardDescription>\r\n {tc('analysisTotalPrefix')}\r\n {totalTables}\r\n {tc('analysisTableCount')}\r\n </CardDescription>\r\n </CardHeader>\r\n <CardContent>\r\n <Table>\r\n <TableHeader>\r\n <TableRow>\r\n <TableHead>{tc('analysisTableName')}</TableHead>\r\n <TableHead>{tc('analysisTableComment')}</TableHead>\r\n <TableHead>{tc('analysisRowCount')}</TableHead>\r\n <TableHead>{tc('analysisTableModule')}</TableHead>\r\n </TableRow>\r\n </TableHeader>\r\n <TableBody>\r\n {data.tables.map((table) => {\r\n const moduleName =\r\n Object.entries(data.modules).find(([, tables]) =>\r\n tables.some((t) => t.name === table.name)\r\n )?.[0] || 'other';\r\n\r\n return (\r\n <TableRow key={table.name}>\r\n <TableCell className=\"font-medium\">{table.name}</TableCell>\r\n <TableCell>{table.comment || '-'}</TableCell>\r\n <TableCell>{table.rows}</TableCell>\r\n <TableCell>\r\n <Badge className={moduleColors[moduleName]}>\r\n {moduleLabels[moduleName] || moduleName}\r\n </Badge>\r\n </TableCell>\r\n </TableRow>\r\n );\r\n })}\r\n </TableBody>\r\n </Table>\r\n </CardContent>\r\n </Card>\r\n </TabsContent>\r\n\r\n {/* 外键关系 */}\r\n <TabsContent value=\"foreignkeys\">\r\n <Card>\r\n <CardHeader>\r\n <CardTitle>{tc('analysisFkTitle')}</CardTitle>\r\n <CardDescription>\r\n {tc('analysisTotalPrefix')}\r\n {totalForeignKeys}\r\n {tc('analysisFkCount')}\r\n </CardDescription>\r\n </CardHeader>\r\n <CardContent>\r\n {totalForeignKeys === 0 ? (\r\n <p className=\"text-muted-foreground text-center py-8\">{tc('analysisNoFk')}</p>\r\n ) : (\r\n <Table>\r\n <TableHeader>\r\n <TableRow>\r\n <TableHead>{tc('analysisFromTable')}</TableHead>\r\n <TableHead>{tc('analysisFromColumn')}</TableHead>\r\n <TableHead>{tc('analysisToTable')}</TableHead>\r\n <TableHead>{tc('analysisToColumn')}</TableHead>\r\n <TableHead>{tc('analysisConstraint')}</TableHead>\r\n </TableRow>\r\n </TableHeader>\r\n <TableBody>\r\n {data.foreignKeys.map((fk, index) => (\r\n <TableRow key={index}>\r\n <TableCell className=\"font-medium\">{fk.fromTable}</TableCell>\r\n <TableCell>{fk.fromColumn}</TableCell>\r\n <TableCell className=\"font-medium text-red-500\">{fk.toTable}</TableCell>\r\n <TableCell>{fk.toColumn}</TableCell>\r\n <TableCell>\r\n <Badge variant=\"outline\">{fk.constraint}</Badge>\r\n </TableCell>\r\n </TableRow>\r\n ))}\r\n </TableBody>\r\n </Table>\r\n )}\r\n </CardContent>\r\n </Card>\r\n </TabsContent>\r\n\r\n {/* 逻辑关联 */}\r\n <TabsContent value=\"logical\">\r\n <Card>\r\n <CardHeader>\r\n <CardTitle>{tc('analysisLogicalTitle')}</CardTitle>\r\n <CardDescription>\r\n {tc('analysisLogicalDesc')}\r\n {totalLogicalRelations}个\r\n </CardDescription>\r\n </CardHeader>\r\n <CardContent>\r\n <Table>\r\n <TableHeader>\r\n <TableRow>\r\n <TableHead>{tc('analysisFromTable')}</TableHead>\r\n <TableHead>{tc('analysisLogicalFromColumn')}</TableHead>\r\n <TableHead>{tc('analysisToTable')}</TableHead>\r\n <TableHead>{tc('analysisToColumn')}</TableHead>\r\n </TableRow>\r\n </TableHeader>\r\n <TableBody>\r\n {data.logicalRelations.map((rel, index) => (\r\n <TableRow key={index}>\r\n <TableCell className=\"font-medium\">{rel.fromTable}</TableCell>\r\n <TableCell>\r\n <Badge variant=\"secondary\">{rel.fromColumn}</Badge>\r\n </TableCell>\r\n <TableCell className=\"font-medium text-blue-500\">{rel.toTable}</TableCell>\r\n <TableCell>{rel.toColumn}</TableCell>\r\n </TableRow>\r\n ))}\r\n </TableBody>\r\n </Table>\r\n </CardContent>\r\n </Card>\r\n </TabsContent>\r\n </Tabs>\r\n </div>\r\n );\r\n}\r\n","usedDeprecatedRules":[]},{"filePath":"D:\\dcprint\\erp-project\\src\\app\\[locale]\\analysis\\error.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\dcprint\\erp-project\\src\\app\\[locale]\\api-docs\\page.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\dcprint\\erp-project\\src\\app\\[locale]\\base-data\\error.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\dcprint\\erp-project\\src\\app\\[locale]\\base-data\\material-category\\page.tsx","messages":[{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":81,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":81,"endColumn":38},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":82,"column":18,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":82,"endColumn":25},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction<Item[]>`.","line":83,"column":17,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":83,"endColumn":39},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":83,"column":24,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":83,"endColumn":28},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction<number>`.","line":84,"column":18,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":84,"endColumn":40},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":84,"column":25,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":84,"endColumn":29},{"ruleId":"react-hooks/set-state-in-effect","severity":1,"message":"Error: Calling setState synchronously within an effect can trigger cascading renders\n\nEffects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following:\n* Update external systems with the latest state from React.\n* Subscribe for updates from some external system, calling setState in a callback function when external state changes.\n\nCalling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect).\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\base-data\\material-category\\page.tsx:89:5\n 87 | };\n 88 | useEffect(() => {\n> 89 | fetchData();\n | ^^^^^^^^^ Avoid calling setState() directly within an effect\n 90 | }, [page]);\n 91 |\n 92 | const handleSave = async () => {","line":89,"column":5,"nodeType":null,"endLine":89,"endColumn":14},{"ruleId":"react-hooks/exhaustive-deps","severity":1,"message":"React Hook useEffect has a missing dependency: 'fetchData'. Either include it or remove the dependency array.","line":90,"column":6,"nodeType":"ArrayExpression","endLine":90,"endColumn":12,"suggestions":[{"desc":"Update the dependencies array to be: [fetchData, page]","fix":{"range":[2368,2374],"text":"[fetchData, page]"}}]},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":100,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":100,"endColumn":38},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":101,"column":18,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":101,"endColumn":25},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"失败\"。建议使用: tc('text_fy1g')","line":106,"column":24,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":106,"endColumn":28},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":106,"column":30,"nodeType":"Property","messageId":"anyAssignment","endLine":106,"endColumn":57},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .message on an `any` value.","line":106,"column":50,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":106,"endColumn":57},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"失败\"。建议使用: tc('text_fy1g')","line":109,"column":22,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":109,"endColumn":26},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"确定删除?\"。建议使用: tc('text_efhx5d')","line":113,"column":18,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":113,"endColumn":25},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":116,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":116,"endColumn":38},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":117,"column":18,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":117,"endColumn":25},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"删除成功\"。建议使用: tc('text_azeh8z')","line":118,"column":24,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":118,"endColumn":30},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"失败\"。建议使用: tc('text_fy1g')","line":122,"column":22,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":122,"endColumn":26}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":19,"fixableErrorCount":0,"fixableWarningCount":0,"source":"'use client';\nimport { useEffect, useState } from 'react';\nimport { MainLayout } from '@/components/layout';\nimport { Card, CardContent } from '@/components/ui/card';\nimport { Button } from '@/components/ui/button';\nimport { Input } from '@/components/ui/input';\nimport { Badge } from '@/components/ui/badge';\nimport {\n Table,\n TableBody,\n TableCell,\n TableHead,\n TableHeader,\n TableRow,\n} from '@/components/ui/table';\nimport {\n Dialog,\n DialogContent,\n DialogHeader,\n DialogTitle,\n DialogFooter,\n} from '@/components/ui/dialog';\nimport { Label } from '@/components/ui/label';\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@/components/ui/select';\nimport { Plus, Search, Edit, Trash2 } from 'lucide-react';\nimport { useToast } from '@/hooks/use-toast';\nimport { usePermission } from '@/hooks/usePermission';\nimport { useTranslations } from 'next-intl';\n\ninterface Item {\n id: number;\n category_code: string;\n category_name: string;\n parent_id: number;\n category_type: number;\n sort_order: number;\n status: number;\n remark: string;\n}\n\nexport default function MaterialCategoryPage() {\n const t = useTranslations('MaterialCategory');\n const tc = useTranslations('Common');\n\n const typeMap: Record<number, string> = {\n 1: t('rawMaterial'),\n 2: t('semiFinished'),\n 3: t('finished'),\n 4: t('auxiliary'),\n 5: t('packaging'),\n 6: t('ink'),\n 7: t('solvent'),\n 8: t('screen'),\n 9: t('blade'),\n 10: t('equipmentParts'),\n };\n\n const { toast } = useToast();\n const { hasPermission } = usePermission();\n const [list, setList] = useState<Item[]>([]);\n const [total, setTotal] = useState(0);\n const [page, setPage] = useState(1);\n const [searchName, setSearchName] = useState('');\n const [showDialog, setShowDialog] = useState(false);\n const [editItem, setEditItem] = useState<Partial<Item>>({});\n\n const fetchData = async () => {\n try {\n const params = new URLSearchParams({\n page: String(page),\n pageSize: '50',\n categoryName: searchName,\n });\n const res = await fetch('/api/base-data/material-category?' + params);\n const result = await res.json();\n if (result.success) {\n setList(result.data.list || []);\n setTotal(result.data.total || 0);\n }\n } catch {}\n };\n useEffect(() => {\n fetchData();\n }, [page]);\n\n const handleSave = async () => {\n try {\n const method = editItem.id ? 'PUT' : 'POST';\n const res = await fetch('/api/base-data/material-category', {\n method,\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(editItem),\n });\n const result = await res.json();\n if (result.success) {\n toast({ title: editItem.id ? '更新成功' : '创建成功' });\n setShowDialog(false);\n fetchData();\n } else {\n toast({ title: '失败', description: result.message, variant: 'destructive' });\n }\n } catch {\n toast({ title: '失败', variant: 'destructive' });\n }\n };\n const handleDelete = async (id: number) => {\n if (!confirm('确定删除?')) return;\n try {\n const res = await fetch('/api/base-data/material-category?id=' + id, { method: 'DELETE' });\n const result = await res.json();\n if (result.success) {\n toast({ title: '删除成功' });\n fetchData();\n }\n } catch {\n toast({ title: '失败', variant: 'destructive' });\n }\n };\n\n return (\n <MainLayout>\n <div className=\"p-6 space-y-6\">\n <div className=\"flex items-center justify-between\">\n <h1 className=\"text-2xl font-bold\">{t('materialCategory')}</h1>\n <div className=\"flex gap-2\">\n <div className=\"flex items-center gap-2\">\n <Input\n placeholder={tc('searchCategoryPlaceholder')}\n value={searchName}\n onChange={(e) => setSearchName(e.target.value)}\n className=\"w-36 h-8 text-sm\"\n />\n <Button size=\"sm\" variant=\"outline\" onClick={fetchData}>\n <Search className=\"h-3 w-3\" />\n </Button>\n </div>\n {hasPermission('base-data:material-category:create') && (\n <Button\n size=\"sm\"\n onClick={() => {\n setEditItem({});\n setShowDialog(true);\n }}\n >\n <Plus className=\"h-3 w-3 mr-1\" />\n {t('addCategory')}\n </Button>\n )}\n </div>\n </div>\n <Card>\n <CardContent className=\"p-0\">\n <Table>\n <TableHeader>\n <TableRow>\n <TableHead className=\"text-xs\">{tc('categoryCode')}</TableHead>\n <TableHead className=\"text-xs\">{tc('categoryName')}</TableHead>\n <TableHead className=\"text-xs\">{tc('categoryType')}</TableHead>\n <TableHead className=\"text-xs\">{tc('sortOrder')}</TableHead>\n <TableHead className=\"text-xs\">{tc('status')}</TableHead>\n <TableHead className=\"text-xs\">{tc('remark')}</TableHead>\n <TableHead className=\"text-xs\">{tc('actions')}</TableHead>\n </TableRow>\n </TableHeader>\n <TableBody>\n {list.map((item) => (\n <TableRow key={item.id}>\n <TableCell className=\"text-xs font-mono\">{item.category_code}</TableCell>\n <TableCell className=\"text-xs\">{item.category_name}</TableCell>\n <TableCell className=\"text-xs\">{typeMap[item.category_type] || '-'}</TableCell>\n <TableCell className=\"text-xs\">{item.sort_order}</TableCell>\n <TableCell>\n <Badge\n variant={item.status === 1 ? 'default' : 'destructive'}\n className=\"text-xs\"\n >\n {item.status === 1 ? tc('enabled') : tc('disabled')}\n </Badge>\n </TableCell>\n <TableCell className=\"text-xs max-w-32 truncate\">\n {item.remark || '-'}\n </TableCell>\n <TableCell>\n <div className=\"flex gap-1\">\n <Button\n size=\"sm\"\n variant=\"ghost\"\n className=\"h-6 w-6 p-0\"\n onClick={() => {\n setEditItem(item);\n setShowDialog(true);\n }}\n >\n <Edit className=\"h-3 w-3\" />\n </Button>\n <Button\n size=\"sm\"\n variant=\"ghost\"\n className=\"h-6 w-6 p-0 text-red-600\"\n onClick={() => handleDelete(item.id)}\n >\n <Trash2 className=\"h-3 w-3\" />\n </Button>\n </div>\n </TableCell>\n </TableRow>\n ))}\n {list.length === 0 && (\n <TableRow>\n <TableCell colSpan={7} className=\"text-center text-gray-400 py-8\">\n {tc('noRecords')}\n </TableCell>\n </TableRow>\n )}\n </TableBody>\n </Table>\n </CardContent>\n </Card>\n <div className=\"flex items-center justify-between\">\n <span className=\"text-sm text-gray-500\">{tc('totalRecords', { count: total })}</span>\n <div className=\"flex gap-2\">\n <Button\n size=\"sm\"\n variant=\"outline\"\n disabled={page <= 1}\n onClick={() => setPage((p) => p - 1)}\n >\n {tc('prevPage')}\n </Button>\n <Button\n size=\"sm\"\n variant=\"outline\"\n disabled={page * 50 >= total}\n onClick={() => setPage((p) => p + 1)}\n >\n {tc('nextPage')}\n </Button>\n </div>\n </div>\n <Dialog open={showDialog} onOpenChange={setShowDialog}>\n <DialogContent className=\"max-w-lg\" resizable>\n <DialogHeader>\n <DialogTitle>{editItem.id ? t('editCategory') : t('addCategory')}</DialogTitle>\n </DialogHeader>\n <div className=\"grid grid-cols-2 gap-4\">\n <div>\n <Label>{tc('categoryCode')}</Label>\n <Input\n value={editItem.category_code || ''}\n onChange={(e) => setEditItem({ ...editItem, category_code: e.target.value })}\n />\n </div>\n <div>\n <Label>{tc('categoryName')}</Label>\n <Input\n value={editItem.category_name || ''}\n onChange={(e) => setEditItem({ ...editItem, category_name: e.target.value })}\n />\n </div>\n <div>\n <Label>{tc('categoryType')}</Label>\n <Select\n value={String(editItem.category_type || 1)}\n onValueChange={(v) => setEditItem({ ...editItem, category_type: Number(v) })}\n >\n <SelectTrigger>\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"1\">{tc('rawMaterial')}</SelectItem>\n <SelectItem value=\"2\">{tc('semiFinished')}</SelectItem>\n <SelectItem value=\"3\">{tc('finishedProduct')}</SelectItem>\n <SelectItem value=\"4\">{tc('auxiliaryMaterial')}</SelectItem>\n <SelectItem value=\"5\">{tc('packagingMaterial')}</SelectItem>\n <SelectItem value=\"6\">{tc('ink')}</SelectItem>\n <SelectItem value=\"7\">{tc('solvent')}</SelectItem>\n <SelectItem value=\"8\">{tc('screen')}</SelectItem>\n <SelectItem value=\"9\">{tc('tool')}</SelectItem>\n <SelectItem value=\"10\">{tc('equipmentPart')}</SelectItem>\n </SelectContent>\n </Select>\n </div>\n <div>\n <Label>{tc('sortOrder')}</Label>\n <Input\n type=\"number\"\n value={editItem.sort_order ?? 0}\n onChange={(e) => setEditItem({ ...editItem, sort_order: Number(e.target.value) })}\n />\n </div>\n <div className=\"col-span-2\">\n <Label>{tc('remark')}</Label>\n <Input\n value={editItem.remark || ''}\n onChange={(e) => setEditItem({ ...editItem, remark: e.target.value })}\n />\n </div>\n </div>\n <DialogFooter>\n <Button variant=\"outline\" onClick={() => setShowDialog(false)}>\n {tc('cancel')}\n </Button>\n <Button onClick={handleSave}>{tc('save')}</Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n </div>\n </MainLayout>\n );\n}\n","usedDeprecatedRules":[]},{"filePath":"D:\\dcprint\\erp-project\\src\\app\\[locale]\\business\\contract-review\\page.tsx","messages":[{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":138,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":138,"endColumn":38},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":139,"column":18,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":139,"endColumn":25},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction<ContractReviewRecord[]>`.","line":140,"column":17,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":140,"endColumn":39},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":140,"column":24,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":140,"endColumn":28},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction<number>`.","line":141,"column":18,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":141,"endColumn":40},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":141,"column":25,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":141,"endColumn":29},{"ruleId":"react-hooks/set-state-in-effect","severity":1,"message":"Error: Calling setState synchronously within an effect can trigger cascading renders\n\nEffects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following:\n* Update external systems with the latest state from React.\n* Subscribe for updates from some external system, calling setState in a callback function when external state changes.\n\nCalling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect).\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\business\\contract-review\\page.tsx:147:5\n 145 |\n 146 | useEffect(() => {\n> 147 | fetchData();\n | ^^^^^^^^^ Avoid calling setState() directly within an effect\n 148 | }, [fetchData]);\n 149 |\n 150 | const toggleSelect = (id: number) => {","line":147,"column":5,"nodeType":null,"endLine":147,"endColumn":14},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":198,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":198,"endColumn":38},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":199,"column":18,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":199,"endColumn":25},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"失败\"。建议使用: tc('text_fy1g')","line":204,"column":24,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":204,"endColumn":28},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":204,"column":30,"nodeType":"Property","messageId":"anyAssignment","endLine":204,"endColumn":57},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .message on an `any` value.","line":204,"column":50,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":204,"endColumn":57},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"失败\"。建议使用: tc('text_fy1g')","line":207,"column":22,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":207,"endColumn":26},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":218,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":218,"endColumn":38},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":219,"column":18,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":219,"endColumn":25},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"评审意见保存成功\"。建议使用: tc('text_id55ax')","line":220,"column":24,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":220,"endColumn":34},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"失败\"。建议使用: tc('text_fy1g')","line":223,"column":24,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":223,"endColumn":28},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":223,"column":30,"nodeType":"Property","messageId":"anyAssignment","endLine":223,"endColumn":57},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .message on an `any` value.","line":223,"column":50,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":223,"endColumn":57},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"失败\"。建议使用: tc('text_fy1g')","line":226,"column":22,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":226,"endColumn":26},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"确定删除此评审记录?\"。建议使用: tc('text_kuhz4n')","line":231,"column":18,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":231,"endColumn":30},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":234,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":234,"endColumn":38},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":235,"column":18,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":235,"endColumn":25},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"删除成功\"。建议使用: tc('text_azeh8z')","line":236,"column":24,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":236,"endColumn":30},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"失败\"。建议使用: tc('text_fy1g')","line":240,"column":22,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":240,"endColumn":26},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":257,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":257,"endColumn":38},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":258,"column":18,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":258,"endColumn":25},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":259,"column":69,"nodeType":"Property","messageId":"anyAssignment","endLine":259,"endColumn":89},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":259,"column":81,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":259,"endColumn":85},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"文件上传成功\"。建议使用: tc('text_ij2qj0')","line":261,"column":24,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":261,"endColumn":32},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"上传失败\"。建议使用: tc('text_a6dm22')","line":263,"column":24,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":263,"endColumn":30},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":263,"column":32,"nodeType":"Property","messageId":"anyAssignment","endLine":263,"endColumn":59},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .message on an `any` value.","line":263,"column":52,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":263,"endColumn":59},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"上传失败\"。建议使用: tc('text_a6dm22')","line":266,"column":22,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":266,"endColumn":28},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"全部状态\"。建议使用: {tc('text_avez63')}","line":306,"column":45,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":306,"endColumn":49},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"合同评审管理\"。建议使用: {tc('text_jfm8py')}","line":320,"column":28,"nodeType":"Literal","messageId":"noChineseInJSX","endLine":320,"endColumn":36},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Computed name [v] resolves to an `any` value.","line":334,"column":57,"nodeType":"Identifier","messageId":"unsafeComputedMemberAccess","endLine":334,"endColumn":58},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Computed name [v] resolves to an `any` value.","line":340,"column":51,"nodeType":"Identifier","messageId":"unsafeComputedMemberAccess","endLine":340,"endColumn":52},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"评审\"。建议使用: {tc('text_o9y5')}","line":410,"column":94,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":412,"endColumn":25},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"暂无数据\"。建议使用: {tc('text_dcv57g')}","line":432,"column":96,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":434,"endColumn":21},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"共\"。建议使用: {tc('text_g35')}","line":441,"column":63,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":442,"endColumn":18},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"条\"。建议使用: {tc('text_kf5')}","line":442,"column":25,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":442,"endColumn":26},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"上一页\"。建议使用: {tc('text_btlof')}","line":450,"column":18,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":452,"endColumn":17},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"下一页\"。建议使用: {tc('text_btmf4')}","line":458,"column":18,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":460,"endColumn":17},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"订单号\"。建议使用: {tc('text_kuwys')}","line":473,"column":24,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":473,"endColumn":27},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"客户名称 *\"。建议使用: {tc('text_555b6m')}","line":480,"column":24,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":480,"endColumn":30},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"产品编码\"。建议使用: {tc('text_aa5vdx')}","line":487,"column":24,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":487,"endColumn":28},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"交货日期\"。建议使用: {tc('text_ai8ysd')}","line":517,"column":24,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":517,"endColumn":28},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"样品状态\"。建议使用: {tc('text_di6511')}","line":525,"column":24,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":525,"endColumn":28},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"质量要求\"。建议使用: {tc('text_ieyjt4')}","line":543,"column":24,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":543,"endColumn":28},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"取消\"。建议使用: {tc('text_ev02')}","line":562,"column":78,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":564,"endColumn":15},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"客户:\"。建议使用: {tc('text_dxa79')}","line":580,"column":57,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":580,"endColumn":60},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"数量:\"。建议使用: {tc('text_fl2u3')}","line":588,"column":57,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":588,"endColumn":60},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"业务部\"。建议使用: {tc('text_buoe9')}","line":594,"column":42,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":594,"endColumn":45},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"工程技术部\"。建议使用: {tc('text_rs1ifn')}","line":595,"column":42,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":595,"endColumn":47},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"品质部\"。建议使用: {tc('text_d3pkx')}","line":596,"column":46,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":596,"endColumn":49},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"生产部\"。建议使用: {tc('text_hjr0g')}","line":597,"column":43,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":597,"endColumn":46},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"采购部\"。建议使用: {tc('text_m1hlu')}","line":598,"column":47,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":598,"endColumn":50},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"工程技术部评审意见\"。建议使用: {tc('text_b0b1s2')}","line":613,"column":47,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":613,"endColumn":56},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"品质部评审意见\"。建议使用: {tc('text_xfmrgw')}","line":636,"column":47,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":636,"endColumn":54},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"生产部评审意见\"。建议使用: {tc('text_58biun')}","line":659,"column":47,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":659,"endColumn":54},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"产能评估\"。建议使用: {tc('text_agp1uq')}","line":661,"column":26,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":661,"endColumn":30},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"采购部评审意见\"。建议使用: {tc('text_nzjk6n')}","line":682,"column":47,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":682,"endColumn":54},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"附件上传\"。建议使用: {tc('text_ja8tu0')}","line":707,"column":47,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":709,"endColumn":15},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"关闭\"。建议使用: {tc('text_eod6')}","line":755,"column":84,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":757,"endColumn":15}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":65,"fixableErrorCount":0,"fixableWarningCount":0,"source":"'use client';\r\n\r\nimport { authFetch } from '@/lib/auth-fetch';\r\nimport { useEffect, useState, useCallback } from 'react';\r\nimport { MainLayout } from '@/components/layout';\r\nimport { Card, CardContent } from '@/components/ui/card';\r\nimport { Button } from '@/components/ui/button';\r\nimport { Input } from '@/components/ui/input';\r\nimport { Badge } from '@/components/ui/badge';\r\nimport { Checkbox } from '@/components/ui/checkbox';\r\nimport {\r\n Table,\r\n TableBody,\r\n TableCell,\r\n TableHead,\r\n TableHeader,\r\n TableRow,\r\n} from '@/components/ui/table';\r\nimport {\r\n Dialog,\r\n DialogContent,\r\n DialogHeader,\r\n DialogTitle,\r\n DialogFooter,\r\n} from '@/components/ui/dialog';\r\nimport { Label } from '@/components/ui/label';\r\nimport {\r\n Select,\r\n SelectContent,\r\n SelectItem,\r\n SelectTrigger,\r\n SelectValue,\r\n} from '@/components/ui/select';\r\nimport { Textarea } from '@/components/ui/textarea';\r\nimport { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';\r\nimport {\r\n printTable,\r\n exportTableToPDF,\r\n exportTableToXLS,\r\n exportTableToWORD,\r\n} from '@/components/ui/table-export-toolbar';\r\nimport { GlobalExportToolbar } from '@/components/ui/global-export-toolbar';\r\nimport { Plus, Search, Edit, Trash2, Upload, FileText, X } from 'lucide-react';\r\nimport { useToast } from '@/hooks/use-toast';\r\nimport { useTranslations } from 'next-intl';\r\n\r\ninterface ContractReviewRecord {\r\n id?: number;\r\n review_no: string;\r\n order_id?: number;\r\n order_no: string;\r\n customer_id?: number;\r\n customer_name: string;\r\n product_id?: number;\r\n product_code: string;\r\n product_name: string;\r\n quantity: number;\r\n amount: number;\r\n delivery_date: string;\r\n sample_status: string;\r\n quality_requirement: string;\r\n production_capacity: string;\r\n material_availability: string;\r\n engineering_feasibility: string;\r\n biz_opinion: string;\r\n eng_opinion: string;\r\n quality_opinion: string;\r\n prod_opinion: string;\r\n purchase_opinion: string;\r\n review_date: string;\r\n status: number;\r\n remark: string;\r\n create_time: string;\r\n}\r\n\r\nexport default function ContractReviewPage() {\r\n const t = useTranslations('Business');\r\n const tc = useTranslations('Common');\r\n\r\n const sampleStatusMap: Record<\r\n string,\r\n { label: string; variant: 'default' | 'secondary' | 'destructive' | 'outline' }\r\n > = {\r\n pending: { label: t('samplePending'), variant: 'outline' },\r\n sampling: { label: t('sampleSampling'), variant: 'secondary' },\r\n approved: { label: t('sampleApproved'), variant: 'default' },\r\n rejected: { label: t('sampleRejected'), variant: 'destructive' },\r\n not_required: { label: t('sampleNotRequired'), variant: 'secondary' },\r\n };\r\n const statusMap: Record<\r\n number,\r\n { label: string; variant: 'default' | 'secondary' | 'destructive' | 'outline' }\r\n > = {\r\n 1: { label: t('pendingReview'), variant: 'outline' },\r\n 2: { label: t('reviewing'), variant: 'secondary' },\r\n 3: { label: t('approved'), variant: 'default' },\r\n 4: { label: t('rejected'), variant: 'destructive' },\r\n };\r\n\r\n const exportColumns = [\r\n { key: 'review_no', header: t('reviewNo') },\r\n { key: 'order_no', header: t('orderNo') },\r\n { key: 'customer_name', header: t('customerName') },\r\n { key: 'product_name', header: t('productName') },\r\n { key: 'quantity', header: t('quantity') },\r\n { key: 'amount', header: t('amount') },\r\n { key: 'delivery_date', header: t('deliveryDate') },\r\n { key: 'sample_status_label', header: t('sampleStatus') },\r\n { key: 'status_label', header: t('reviewStatus') },\r\n ];\r\n\r\n const { toast } = useToast();\r\n const [list, setList] = useState<ContractReviewRecord[]>([]);\r\n const [total, setTotal] = useState(0);\r\n const [page, setPage] = useState(1);\r\n const [searchCustomer, setSearchCustomer] = useState('');\r\n const [searchProduct, setSearchProduct] = useState('');\r\n const [searchStatus, setSearchStatus] = useState('');\r\n const [showDialog, setShowDialog] = useState(false);\r\n const [showReviewDialog, setShowReviewDialog] = useState(false);\r\n const [editItem, setEditItem] = useState<Partial<ContractReviewRecord>>({});\r\n const [activeReviewTab, setActiveReviewTab] = useState('biz');\r\n const [selectedIds, setSelectedIds] = useState<Set<number>>(new Set());\r\n const [uploadFile, setUploadFile] = useState<File | null>(null);\r\n const [uploading, setUploading] = useState(false);\r\n const [attachments, setAttachments] = useState<{ name: string; url: string }[]>([]);\r\n\r\n const fetchData = useCallback(async () => {\r\n try {\r\n const params = new URLSearchParams({\r\n page: String(page),\r\n pageSize: '20',\r\n customerName: searchCustomer,\r\n productName: searchProduct,\r\n status: searchStatus,\r\n });\r\n const res = await authFetch('/api/business/contract-review?' + params);\r\n const result = await res.json();\r\n if (result.success) {\r\n setList(result.data.list || []);\r\n setTotal(result.data.total || 0);\r\n }\r\n } catch {}\r\n }, [page, searchCustomer, searchProduct, searchStatus]);\r\n\r\n useEffect(() => {\r\n fetchData();\r\n }, [fetchData]);\r\n\r\n const toggleSelect = (id: number) => {\r\n setSelectedIds((prev) => {\r\n const next = new Set(prev);\r\n if (next.has(id)) next.delete(id);\r\n else next.add(id);\r\n return next;\r\n });\r\n };\r\n\r\n const toggleSelectAll = () => {\r\n if (selectedIds.size === list.length && list.length > 0) {\r\n setSelectedIds(new Set());\r\n } else {\r\n setSelectedIds(new Set(list.map((i) => i.id!)));\r\n }\r\n };\r\n\r\n const getExportData = () =>\r\n list.map((item) => ({\r\n ...item,\r\n sample_status_label: sampleStatusMap[item.sample_status]?.label || tc('unknown'),\r\n status_label: statusMap[item.status]?.label || tc('unknown'),\r\n }));\r\n\r\n const _handlePrint = () => {\r\n printTable(getExportData(), exportColumns, '合同评审管理');\r\n };\r\n\r\n const _handleExportPDF = () => {\r\n exportTableToPDF(getExportData(), '合同评审管理', exportColumns, '合同评审管理');\r\n };\r\n\r\n const _handleExportXLS = () => {\r\n exportTableToXLS(getExportData(), '合同评审管理', exportColumns);\r\n };\r\n\r\n const _handleExportWORD = () => {\r\n exportTableToWORD(getExportData(), '合同评审管理', exportColumns, '合同评审管理');\r\n };\r\n\r\n const handleSave = async () => {\r\n try {\r\n const method = editItem.id ? 'PUT' : 'POST';\r\n const res = await authFetch('/api/business/contract-review', {\r\n method,\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify(editItem),\r\n });\r\n const result = await res.json();\r\n if (result.success) {\r\n toast({ title: editItem.id ? '更新成功' : '创建成功' });\r\n setShowDialog(false);\r\n fetchData();\r\n } else {\r\n toast({ title: '失败', description: result.message, variant: 'destructive' });\r\n }\r\n } catch {\r\n toast({ title: '失败', variant: 'destructive' });\r\n }\r\n };\r\n\r\n const handleSaveReview = async () => {\r\n try {\r\n const res = await authFetch('/api/business/contract-review', {\r\n method: 'PUT',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify(editItem),\r\n });\r\n const result = await res.json();\r\n if (result.success) {\r\n toast({ title: '评审意见保存成功' });\r\n fetchData();\r\n } else {\r\n toast({ title: '失败', description: result.message, variant: 'destructive' });\r\n }\r\n } catch {\r\n toast({ title: '失败', variant: 'destructive' });\r\n }\r\n };\r\n\r\n const handleDelete = async (id: number) => {\r\n if (!confirm('确定删除此评审记录?')) return;\r\n try {\r\n const res = await authFetch('/api/business/contract-review?id=' + id, { method: 'DELETE' });\r\n const result = await res.json();\r\n if (result.success) {\r\n toast({ title: '删除成功' });\r\n fetchData();\r\n }\r\n } catch {\r\n toast({ title: '失败', variant: 'destructive' });\r\n }\r\n };\r\n\r\n const openReview = (item: ContractReviewRecord) => {\r\n setEditItem(item);\r\n setActiveReviewTab('biz');\r\n setShowReviewDialog(true);\r\n };\r\n\r\n const handleFileUpload = async () => {\r\n if (!uploadFile) return;\r\n setUploading(true);\r\n try {\r\n const formData = new FormData();\r\n formData.append('file', uploadFile);\r\n const res = await authFetch('/api/upload/contract', { method: 'POST', body: formData });\r\n const result = await res.json();\r\n if (result.success) {\r\n setAttachments((prev) => [...prev, { name: uploadFile.name, url: result.data.url }]);\r\n setUploadFile(null);\r\n toast({ title: '文件上传成功' });\r\n } else {\r\n toast({ title: '上传失败', description: result.message, variant: 'destructive' });\r\n }\r\n } catch {\r\n toast({ title: '上传失败', variant: 'destructive' });\r\n } finally {\r\n setUploading(false);\r\n }\r\n };\r\n\r\n const removeAttachment = (idx: number) => {\r\n setAttachments((prev) => prev.filter((_, i) => i !== idx));\r\n };\r\n\r\n return (\r\n <MainLayout title=\"合同评审管理\">\r\n <div className=\"p-6 space-y-6\">\r\n <Card>\r\n <CardContent className=\"pt-6\">\r\n <div className=\"flex items-center justify-between mb-4\">\r\n <div className=\"flex items-center gap-4\">\r\n <div className=\"relative\">\r\n <Search className=\"absolute left-2.5 top-2.5 h-4 w-4 text-muted-foreground\" />\r\n <Input\r\n placeholder=\"搜索客户名称\"\r\n className=\"pl-8 w-48\"\r\n value={searchCustomer}\r\n onChange={(e) => setSearchCustomer(e.target.value)}\r\n />\r\n </div>\r\n <div className=\"relative\">\r\n <Search className=\"absolute left-2.5 top-2.5 h-4 w-4 text-muted-foreground\" />\r\n <Input\r\n placeholder={tc('searchProductName')}\r\n className=\"pl-8 w-48\"\r\n value={searchProduct}\r\n onChange={(e) => setSearchProduct(e.target.value)}\r\n />\r\n </div>\r\n <Select value={searchStatus} onValueChange={setSearchStatus}>\r\n <SelectTrigger className=\"w-36\">\r\n <SelectValue placeholder=\"状态筛选\" />\r\n </SelectTrigger>\r\n <SelectContent>\r\n <SelectItem value=\"all\">全部状态</SelectItem>\r\n {Object.entries(statusMap).map(([k, v]) => (\r\n <SelectItem key={k} value={k}>\r\n {v.label}\r\n </SelectItem>\r\n ))}\r\n </SelectContent>\r\n </Select>\r\n <Button variant=\"outline\" onClick={fetchData}>\r\n {tc('query')}\r\n </Button>\r\n </div>\r\n <div className=\"flex items-center gap-2\">\r\n <GlobalExportToolbar\r\n filename=\"合同评审管理\"\r\n title=\"合同评审管理\"\r\n columns={[\r\n { key: 'review_no', label: t('reviewNo'), width: 18 },\r\n { key: 'order_no', label: t('orderNo'), width: 15 },\r\n { key: 'customer_name', label: t('customerName'), width: 18 },\r\n { key: 'product_name', label: t('productName'), width: 20 },\r\n { key: 'quantity', label: t('quantity'), width: 10 },\r\n { key: 'amount', label: t('amount'), width: 12 },\r\n { key: 'delivery_date', label: t('deliveryDate'), width: 12 },\r\n {\r\n key: 'sample_status',\r\n label: t('sampleStatus'),\r\n width: 12,\r\n formatter: (v) => sampleStatusMap[v]?.label || tc('unknown'),\r\n },\r\n {\r\n key: 'status',\r\n label: t('reviewStatus'),\r\n width: 12,\r\n formatter: (v) => statusMap[v]?.label || tc('unknown'),\r\n },\r\n ]}\r\n data={\r\n selectedIds.size > 0 ? list.filter((i) => i.id && selectedIds.has(i.id)) : list\r\n }\r\n />\r\n <Button\r\n onClick={() => {\r\n setEditItem({ sample_status: 'pending' });\r\n setShowDialog(true);\r\n }}\r\n >\r\n <Plus className=\"h-4 w-4 mr-2\" />\r\n {t('newReview')}\r\n </Button>\r\n </div>\r\n </div>\r\n\r\n <Table>\r\n <TableHeader>\r\n <TableRow>\r\n <TableHead className=\"w-10\">\r\n <Checkbox\r\n checked={list.length > 0 && selectedIds.size === list.length}\r\n onCheckedChange={toggleSelectAll}\r\n />\r\n </TableHead>\r\n <TableHead>{t('reviewNo')}</TableHead>\r\n <TableHead>{tc('orderNo')}</TableHead>\r\n <TableHead>{tc('customerName')}</TableHead>\r\n <TableHead>{tc('productName')}</TableHead>\r\n <TableHead>{tc('quantity')}</TableHead>\r\n <TableHead>{tc('amount')}</TableHead>\r\n <TableHead>{t('deliveryDate')}</TableHead>\r\n <TableHead>{t('sampleStatus')}</TableHead>\r\n <TableHead>{t('reviewStatus')}</TableHead>\r\n <TableHead>{tc('actions')}</TableHead>\r\n </TableRow>\r\n </TableHeader>\r\n <TableBody>\r\n {list.map((item) => (\r\n <TableRow key={item.id} className={selectedIds.has(item.id!) ? 'bg-blue-50' : ''}>\r\n <TableCell>\r\n <Checkbox\r\n checked={selectedIds.has(item.id!)}\r\n onCheckedChange={() => toggleSelect(item.id!)}\r\n />\r\n </TableCell>\r\n <TableCell className=\"font-mono text-sm\">{item.review_no}</TableCell>\r\n <TableCell>{item.order_no || '-'}</TableCell>\r\n <TableCell>{item.customer_name}</TableCell>\r\n <TableCell>{item.product_name}</TableCell>\r\n <TableCell>{item.quantity}</TableCell>\r\n <TableCell>\r\n {item.amount ? '¥' + Number(item.amount).toLocaleString() : '-'}\r\n </TableCell>\r\n <TableCell>{item.delivery_date?.substring(0, 10) || '-'}</TableCell>\r\n <TableCell>\r\n <Badge variant={sampleStatusMap[item.sample_status]?.variant || 'outline'}>\r\n {sampleStatusMap[item.sample_status]?.label || tc('unknown')}\r\n </Badge>\r\n </TableCell>\r\n <TableCell>\r\n <Badge variant={statusMap[item.status]?.variant || 'outline'}>\r\n {statusMap[item.status]?.label || tc('unknown')}\r\n </Badge>\r\n </TableCell>\r\n <TableCell>\r\n <div className=\"flex gap-1\">\r\n <Button size=\"sm\" variant=\"outline\" onClick={() => openReview(item)}>\r\n 评审\r\n </Button>\r\n <Button\r\n size=\"sm\"\r\n variant=\"ghost\"\r\n onClick={() => {\r\n setEditItem(item);\r\n setShowDialog(true);\r\n }}\r\n >\r\n <Edit className=\"h-3 w-3\" />\r\n </Button>\r\n <Button size=\"sm\" variant=\"ghost\" onClick={() => handleDelete(item.id!)}>\r\n <Trash2 className=\"h-3 w-3\" />\r\n </Button>\r\n </div>\r\n </TableCell>\r\n </TableRow>\r\n ))}\r\n {list.length === 0 && (\r\n <TableRow>\r\n <TableCell colSpan={11} className=\"text-center py-8 text-muted-foreground\">\r\n 暂无数据\r\n </TableCell>\r\n </TableRow>\r\n )}\r\n </TableBody>\r\n </Table>\r\n\r\n <div className=\"flex items-center justify-between mt-4\">\r\n <span className=\"text-sm text-muted-foreground\">\r\n 共{total}条{selectedIds.size > 0 && `,已选 ${selectedIds.size} 条`}\r\n </span>\r\n <div className=\"flex gap-2\">\r\n <Button\r\n variant=\"outline\"\r\n size=\"sm\"\r\n disabled={page <= 1}\r\n onClick={() => setPage((p) => p - 1)}\r\n >\r\n 上一页\r\n </Button>\r\n <Button\r\n variant=\"outline\"\r\n size=\"sm\"\r\n disabled={page * 20 >= total}\r\n onClick={() => setPage((p) => p + 1)}\r\n >\r\n 下一页\r\n </Button>\r\n </div>\r\n </div>\r\n </CardContent>\r\n </Card>\r\n\r\n <Dialog open={showDialog} onOpenChange={setShowDialog}>\r\n <DialogContent className=\"max-w-2xl max-h-[80vh] overflow-y-auto\" resizable>\r\n <DialogHeader>\r\n <DialogTitle>{editItem.id ? '编辑评审' : '新建评审'}</DialogTitle>\r\n </DialogHeader>\r\n <div className=\"grid grid-cols-2 gap-4 py-4\">\r\n <div>\r\n <Label>订单号</Label>\r\n <Input\r\n value={editItem.order_no || ''}\r\n onChange={(e) => setEditItem({ ...editItem, order_no: e.target.value })}\r\n />\r\n </div>\r\n <div>\r\n <Label>客户名称 *</Label>\r\n <Input\r\n value={editItem.customer_name || ''}\r\n onChange={(e) => setEditItem({ ...editItem, customer_name: e.target.value })}\r\n />\r\n </div>\r\n <div>\r\n <Label>产品编码</Label>\r\n <Input\r\n value={editItem.product_code || ''}\r\n onChange={(e) => setEditItem({ ...editItem, product_code: e.target.value })}\r\n />\r\n </div>\r\n <div>\r\n <Label>{tc('productNameLabel')}</Label>\r\n <Input\r\n value={editItem.product_name || ''}\r\n onChange={(e) => setEditItem({ ...editItem, product_name: e.target.value })}\r\n />\r\n </div>\r\n <div>\r\n <Label>{tc('quantity')}</Label>\r\n <Input\r\n type=\"number\"\r\n value={editItem.quantity || 0}\r\n onChange={(e) => setEditItem({ ...editItem, quantity: Number(e.target.value) })}\r\n />\r\n </div>\r\n <div>\r\n <Label>{tc('amount')}</Label>\r\n <Input\r\n type=\"number\"\r\n value={editItem.amount || 0}\r\n onChange={(e) => setEditItem({ ...editItem, amount: Number(e.target.value) })}\r\n />\r\n </div>\r\n <div>\r\n <Label>交货日期</Label>\r\n <Input\r\n type=\"date\"\r\n value={editItem.delivery_date || ''}\r\n onChange={(e) => setEditItem({ ...editItem, delivery_date: e.target.value })}\r\n />\r\n </div>\r\n <div>\r\n <Label>样品状态</Label>\r\n <Select\r\n value={editItem.sample_status || 'pending'}\r\n onValueChange={(v) => setEditItem({ ...editItem, sample_status: v })}\r\n >\r\n <SelectTrigger>\r\n <SelectValue />\r\n </SelectTrigger>\r\n <SelectContent>\r\n {Object.entries(sampleStatusMap).map(([k, v]) => (\r\n <SelectItem key={k} value={k}>\r\n {v.label}\r\n </SelectItem>\r\n ))}\r\n </SelectContent>\r\n </Select>\r\n </div>\r\n <div className=\"col-span-2\">\r\n <Label>质量要求</Label>\r\n <Textarea\r\n rows={2}\r\n value={editItem.quality_requirement || ''}\r\n onChange={(e) =>\r\n setEditItem({ ...editItem, quality_requirement: e.target.value })\r\n }\r\n />\r\n </div>\r\n <div className=\"col-span-2\">\r\n <Label>{tc('remark')}</Label>\r\n <Textarea\r\n rows={2}\r\n value={editItem.remark || ''}\r\n onChange={(e) => setEditItem({ ...editItem, remark: e.target.value })}\r\n />\r\n </div>\r\n </div>\r\n <DialogFooter>\r\n <Button variant=\"outline\" onClick={() => setShowDialog(false)}>\r\n 取消\r\n </Button>\r\n <Button onClick={handleSave}>{tc('save')}</Button>\r\n </DialogFooter>\r\n </DialogContent>\r\n </Dialog>\r\n\r\n <Dialog open={showReviewDialog} onOpenChange={setShowReviewDialog}>\r\n <DialogContent className=\"max-w-4xl max-h-[85vh] overflow-y-auto\" resizable>\r\n <DialogHeader>\r\n <DialogTitle>\r\n {tc('reviewTitlePrefix')}\r\n {editItem.review_no}\r\n </DialogTitle>\r\n </DialogHeader>\r\n <div className=\"mb-4 grid grid-cols-3 gap-4 text-sm\">\r\n <div>\r\n <span className=\"text-muted-foreground\">客户:</span>\r\n {editItem.customer_name}\r\n </div>\r\n <div>\r\n <span className=\"text-muted-foreground\">{tc('productNameLabel')}</span>\r\n {editItem.product_name}\r\n </div>\r\n <div>\r\n <span className=\"text-muted-foreground\">数量:</span>\r\n {editItem.quantity}\r\n </div>\r\n </div>\r\n <Tabs value={activeReviewTab} onValueChange={setActiveReviewTab}>\r\n <TabsList className=\"grid grid-cols-5\">\r\n <TabsTrigger value=\"biz\">业务部</TabsTrigger>\r\n <TabsTrigger value=\"eng\">工程技术部</TabsTrigger>\r\n <TabsTrigger value=\"quality\">品质部</TabsTrigger>\r\n <TabsTrigger value=\"prod\">生产部</TabsTrigger>\r\n <TabsTrigger value=\"purchase\">采购部</TabsTrigger>\r\n </TabsList>\r\n <TabsContent value=\"biz\" className=\"space-y-4 mt-4\">\r\n <h3 className=\"font-semibold\">{tc('bizReviewOpinionTitle')}</h3>\r\n <div>\r\n <Label>{tc('reviewOpinionLabel')}</Label>\r\n <Textarea\r\n rows={4}\r\n value={editItem.biz_opinion || ''}\r\n onChange={(e) => setEditItem({ ...editItem, biz_opinion: e.target.value })}\r\n placeholder=\"业务部对合同条款、交期、价格等方面的评审意见\"\r\n />\r\n </div>\r\n </TabsContent>\r\n <TabsContent value=\"eng\" className=\"space-y-4 mt-4\">\r\n <h3 className=\"font-semibold\">工程技术部评审意见</h3>\r\n <div>\r\n <Label>{tc('engineeringFeasibilityLabel')}</Label>\r\n <Textarea\r\n rows={3}\r\n value={editItem.engineering_feasibility || ''}\r\n onChange={(e) =>\r\n setEditItem({ ...editItem, engineering_feasibility: e.target.value })\r\n }\r\n placeholder=\"工艺可行性、打样状态、技术难点等评估\"\r\n />\r\n </div>\r\n <div>\r\n <Label>{tc('reviewOpinionLabel')}</Label>\r\n <Textarea\r\n rows={3}\r\n value={editItem.eng_opinion || ''}\r\n onChange={(e) => setEditItem({ ...editItem, eng_opinion: e.target.value })}\r\n placeholder=\"工程技术部评审意见\"\r\n />\r\n </div>\r\n </TabsContent>\r\n <TabsContent value=\"quality\" className=\"space-y-4 mt-4\">\r\n <h3 className=\"font-semibold\">品质部评审意见</h3>\r\n <div>\r\n <Label>{tc('qualityRequirementLabel')}</Label>\r\n <Textarea\r\n rows={3}\r\n value={editItem.quality_requirement || ''}\r\n onChange={(e) =>\r\n setEditItem({ ...editItem, quality_requirement: e.target.value })\r\n }\r\n placeholder=\"客户质量要求是否可达成、检验标准等评估\"\r\n />\r\n </div>\r\n <div>\r\n <Label>{tc('reviewOpinionLabel')}</Label>\r\n <Textarea\r\n rows={3}\r\n value={editItem.quality_opinion || ''}\r\n onChange={(e) => setEditItem({ ...editItem, quality_opinion: e.target.value })}\r\n placeholder=\"品质部评审意见\"\r\n />\r\n </div>\r\n </TabsContent>\r\n <TabsContent value=\"prod\" className=\"space-y-4 mt-4\">\r\n <h3 className=\"font-semibold\">生产部评审意见</h3>\r\n <div>\r\n <Label>产能评估</Label>\r\n <Textarea\r\n rows={3}\r\n value={editItem.production_capacity || ''}\r\n onChange={(e) =>\r\n setEditItem({ ...editItem, production_capacity: e.target.value })\r\n }\r\n placeholder=\"产能是否满足、排产计划等评估\"\r\n />\r\n </div>\r\n <div>\r\n <Label>{tc('reviewOpinionLabel')}</Label>\r\n <Textarea\r\n rows={3}\r\n value={editItem.prod_opinion || ''}\r\n onChange={(e) => setEditItem({ ...editItem, prod_opinion: e.target.value })}\r\n placeholder=\"生产部评审意见\"\r\n />\r\n </div>\r\n </TabsContent>\r\n <TabsContent value=\"purchase\" className=\"space-y-4 mt-4\">\r\n <h3 className=\"font-semibold\">采购部评审意见</h3>\r\n <div>\r\n <Label>{tc('materialAvailabilityLabel')}</Label>\r\n <Textarea\r\n rows={3}\r\n value={editItem.material_availability || ''}\r\n onChange={(e) =>\r\n setEditItem({ ...editItem, material_availability: e.target.value })\r\n }\r\n placeholder=\"物料供应能力、交期等评估\"\r\n />\r\n </div>\r\n <div>\r\n <Label>{tc('reviewOpinionLabel')}</Label>\r\n <Textarea\r\n rows={3}\r\n value={editItem.purchase_opinion || ''}\r\n onChange={(e) => setEditItem({ ...editItem, purchase_opinion: e.target.value })}\r\n placeholder=\"采购部评审意见\"\r\n />\r\n </div>\r\n </TabsContent>\r\n </Tabs>\r\n <div className=\"border-t pt-4 mt-4 space-y-3\">\r\n <h3 className=\"font-semibold flex items-center gap-2\">\r\n <Upload className=\"h-4 w-4\" />\r\n 附件上传\r\n </h3>\r\n <div className=\"flex items-center gap-2\">\r\n <Input\r\n type=\"file\"\r\n accept=\".pdf,.doc,.docx,.xls,.xlsx\"\r\n onChange={(e) => {\r\n if (e.target.files?.[0]) setUploadFile(e.target.files[0]);\r\n }}\r\n className=\"max-w-xs\"\r\n />\r\n <Button size=\"sm\" disabled={!uploadFile || uploading} onClick={handleFileUpload}>\r\n {uploading ? '上传中...' : tc('upload')}\r\n </Button>\r\n {uploadFile && (\r\n <span className=\"text-sm text-muted-foreground truncate max-w-[200px]\">\r\n {uploadFile.name}\r\n </span>\r\n )}\r\n </div>\r\n {attachments.length > 0 && (\r\n <div className=\"space-y-1\">\r\n {attachments.map((att, idx) => (\r\n <div key={idx} className=\"flex items-center gap-2 text-sm\">\r\n <FileText className=\"h-4 w-4 text-blue-500\" />\r\n <a\r\n href={att.url}\r\n target=\"_blank\"\r\n rel=\"noopener noreferrer\"\r\n className=\"text-blue-600 hover:underline truncate\"\r\n >\r\n {att.name}\r\n </a>\r\n <Button\r\n size=\"sm\"\r\n variant=\"ghost\"\r\n className=\"h-6 w-6 p-0\"\r\n onClick={() => removeAttachment(idx)}\r\n >\r\n <X className=\"h-3 w-3\" />\r\n </Button>\r\n </div>\r\n ))}\r\n </div>\r\n )}\r\n </div>\r\n <DialogFooter className=\"mt-4\">\r\n <Button variant=\"outline\" onClick={() => setShowReviewDialog(false)}>\r\n 关闭\r\n </Button>\r\n <Button onClick={handleSaveReview}>{tc('saveReview')}</Button>\r\n </DialogFooter>\r\n </DialogContent>\r\n </Dialog>\r\n </div>\r\n </MainLayout>\r\n );\r\n}\r\n","usedDeprecatedRules":[]},{"filePath":"D:\\dcprint\\erp-project\\src\\app\\[locale]\\business\\error.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\dcprint\\erp-project\\src\\app\\[locale]\\crm\\analysis\\page.tsx","messages":[{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":124,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":124,"endColumn":36},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .code on an `any` value.","line":125,"column":16,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":125,"endColumn":20},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":126,"column":15,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":126,"endColumn":45},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":126,"column":30,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":126,"endColumn":34},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":127,"column":15,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":127,"endColumn":51},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":127,"column":33,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":127,"endColumn":37},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction<AnalysisRecord[]>`.","line":129,"column":11,"nodeType":"CallExpression","messageId":"unsafeArgument","endLine":135,"endColumn":14},{"ruleId":"@typescript-eslint/no-unsafe-call","severity":1,"message":"Unsafe call of an `any` typed value.","line":129,"column":11,"nodeType":"MemberExpression","messageId":"unsafeCall","endLine":129,"endColumn":22},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .map on an `any` value.","line":129,"column":19,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":129,"endColumn":22},{"ruleId":"@typescript-eslint/no-unsafe-return","severity":1,"message":"Unsafe return of a value of type `any`.","line":129,"column":38,"nodeType":"ObjectExpression","messageId":"unsafeReturn","endLine":135,"endColumn":12},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .on_time_rate on an `any` value.","line":131,"column":29,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":131,"endColumn":41},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .on_time_rate on an `any` value.","line":131,"column":61,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":131,"endColumn":73},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .satisfaction_score on an `any` value.","line":132,"column":35,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":132,"endColumn":53},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .satisfaction_score on an `any` value.","line":132,"column":73,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":132,"endColumn":91},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .growth_rate on an `any` value.","line":133,"column":28,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":133,"endColumn":39},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .growth_rate on an `any` value.","line":133,"column":59,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":133,"endColumn":70},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .order_amount on an `any` value.","line":134,"column":36,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":134,"endColumn":48},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction<number>`.","line":137,"column":18,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":137,"endColumn":38},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":137,"column":23,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":137,"endColumn":27},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .total_customers on an `any` value.","line":139,"column":46,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":139,"endColumn":61},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .total_orders on an `any` value.","line":140,"column":43,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":140,"endColumn":55},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .total_amount on an `any` value.","line":141,"column":43,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":141,"endColumn":55},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .avg_satisfaction on an `any` value.","line":142,"column":47,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":142,"endColumn":63},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .avg_on_time_rate on an `any` value.","line":143,"column":47,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":143,"endColumn":63},{"ruleId":"react-hooks/set-state-in-effect","severity":1,"message":"Error: Calling setState synchronously within an effect can trigger cascading renders\n\nEffects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following:\n* Update external systems with the latest state from React.\n* Subscribe for updates from some external system, calling setState in a callback function when external state changes.\n\nCalling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect).\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\crm\\analysis\\page.tsx:152:5\n 150 |\n 151 | useEffect(() => {\n> 152 | fetchData();\n | ^^^^^^^^^ Avoid calling setState() directly within an effect\n 153 | }, [page]);\n 154 |\n 155 | const handleSave = async () => {","line":152,"column":5,"nodeType":null,"endLine":152,"endColumn":14},{"ruleId":"react-hooks/exhaustive-deps","severity":1,"message":"React Hook useEffect has a missing dependency: 'fetchData'. Either include it or remove the dependency array.","line":153,"column":6,"nodeType":"ArrayExpression","endLine":153,"endColumn":12,"suggestions":[{"desc":"Update the dependencies array to be: [fetchData, page]","fix":{"range":[4623,4629],"text":"[fetchData, page]"}}]},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":168,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":168,"endColumn":36},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .code on an `any` value.","line":169,"column":16,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":169,"endColumn":20},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":174,"column":17,"nodeType":"Property","messageId":"anyAssignment","endLine":174,"endColumn":51},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .message on an `any` value.","line":174,"column":29,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":174,"endColumn":36},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":185,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":185,"endColumn":36},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .code on an `any` value.","line":186,"column":16,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":186,"endColumn":20}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":32,"fixableErrorCount":0,"fixableWarningCount":0,"source":"'use client';\n\nimport { authFetch } from '@/lib/auth-fetch';\nimport { useEffect, useState } from 'react';\nimport { MainLayout } from '@/components/layout';\nimport { Card, CardContent } from '@/components/ui/card';\nimport { Button } from '@/components/ui/button';\nimport { Input } from '@/components/ui/input';\nimport { Badge } from '@/components/ui/badge';\nimport {\n Table,\n TableBody,\n TableCell,\n TableHead,\n TableHeader,\n TableRow,\n} from '@/components/ui/table';\nimport {\n Dialog,\n DialogContent,\n DialogHeader,\n DialogTitle,\n DialogFooter,\n} from '@/components/ui/dialog';\nimport { Label } from '@/components/ui/label';\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@/components/ui/select';\nimport { Textarea } from '@/components/ui/textarea';\nimport { Plus, Search, Edit, Trash2, BarChart3, TrendingUp, Users, DollarSign } from 'lucide-react';\nimport { useToast } from '@/hooks/use-toast';\nimport { useTranslations } from 'next-intl';\n\ninterface AnalysisRecord {\n id?: number;\n customer_id: number;\n customer_name: string;\n analysis_period: string;\n period_start: string;\n period_end: string;\n order_count: number;\n order_amount: number;\n delivery_count: number;\n return_count: number;\n complaint_count: number;\n on_time_rate: number;\n satisfaction_score: number;\n customer_level: string;\n growth_rate: number;\n remark: string;\n create_time: string;\n}\n\ninterface Summary {\n total_customers: number;\n total_orders: number;\n total_amount: number;\n avg_satisfaction: number;\n avg_on_time_rate: number;\n}\n\nexport default function CustomerAnalysisPage() {\n const t = useTranslations('Crm');\n const tc = useTranslations('Common');\n\n const periodMap: Record<string, string> = {\n month: t('monthly'),\n quarter: t('quarterly'),\n year: t('yearly'),\n };\n const levelMap: Record<\n string,\n { label: string; variant: 'default' | 'secondary' | 'destructive' | 'outline' }\n > = {\n A: { label: t('levelA'), variant: 'default' },\n B: { label: t('levelB'), variant: 'secondary' },\n C: { label: t('levelC'), variant: 'outline' },\n D: { label: t('levelD'), variant: 'outline' },\n };\n\n const { toast } = useToast();\n const [records, setRecords] = useState<AnalysisRecord[]>([]);\n const [total, setTotal] = useState(0);\n const [page, setPage] = useState(1);\n const [summary, setSummary] = useState<Summary>({\n total_customers: 0,\n total_orders: 0,\n total_amount: 0,\n avg_satisfaction: 0,\n avg_on_time_rate: 0,\n });\n const [searchName, setSearchName] = useState('');\n const [searchLevel, setSearchLevel] = useState('');\n const [dialogOpen, setDialogOpen] = useState(false);\n const [editRecord, setEditRecord] = useState<AnalysisRecord | null>(null);\n const [form, setForm] = useState<Partial<AnalysisRecord>>({\n customer_id: 0,\n customer_name: '',\n analysis_period: 'month',\n period_start: '',\n period_end: '',\n order_count: 0,\n order_amount: 0,\n delivery_count: 0,\n return_count: 0,\n complaint_count: 0,\n on_time_rate: 0,\n satisfaction_score: 0,\n customer_level: 'C',\n growth_rate: 0,\n remark: '',\n });\n\n const fetchData = async () => {\n try {\n const params = new URLSearchParams({ page: String(page), pageSize: '20' });\n if (searchName) params.set('customerName', searchName);\n if (searchLevel) params.set('customerLevel', searchLevel);\n const res = await authFetch('/api/crm/analysis?' + params);\n const data = await res.json();\n if (data.code === 200) {\n const rawList = data.data.list || [];\n const rawSummary = data.data.summary || {};\n setRecords(\n rawList.map((r: Loose) => ({\n ...r,\n on_time_rate: r.on_time_rate != null ? Number(r.on_time_rate) : null,\n satisfaction_score: r.satisfaction_score != null ? Number(r.satisfaction_score) : null,\n growth_rate: r.growth_rate != null ? Number(r.growth_rate) : null,\n order_amount: Number(r.order_amount || 0),\n }))\n );\n setTotal(data.data.total || 0);\n setSummary({\n total_customers: Number(rawSummary.total_customers || 0),\n total_orders: Number(rawSummary.total_orders || 0),\n total_amount: Number(rawSummary.total_amount || 0),\n avg_satisfaction: Number(rawSummary.avg_satisfaction || 0),\n avg_on_time_rate: Number(rawSummary.avg_on_time_rate || 0),\n });\n }\n } catch {\n toast({ title: t('fetchDataFail'), variant: 'destructive' });\n }\n };\n\n useEffect(() => {\n fetchData();\n }, [page]);\n\n const handleSave = async () => {\n if (!form.customer_name) {\n toast({ title: t('enterCustomerName'), variant: 'destructive' });\n return;\n }\n try {\n const method = editRecord ? 'PUT' : 'POST';\n const body = editRecord ? { id: editRecord.id, ...form } : form;\n const res = await authFetch('/api/crm/analysis', {\n method,\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(body),\n });\n const data = await res.json();\n if (data.code === 200) {\n toast({ title: editRecord ? tc('updateSuccess') : tc('createSuccess') });\n setDialogOpen(false);\n fetchData();\n } else {\n toast({ title: data.message || tc('error'), variant: 'destructive' });\n }\n } catch {\n toast({ title: tc('error'), variant: 'destructive' });\n }\n };\n\n const handleDelete = async (id: number) => {\n if (!confirm(t('confirmDelete'))) return;\n try {\n const res = await authFetch('/api/crm/analysis?id=' + id, { method: 'DELETE' });\n const data = await res.json();\n if (data.code === 200) {\n toast({ title: t('deleteSuccess') });\n fetchData();\n }\n } catch {\n toast({ title: t('deleteFail'), variant: 'destructive' });\n }\n };\n\n const openEdit = (record: AnalysisRecord) => {\n setEditRecord(record);\n setForm({ ...record });\n setDialogOpen(true);\n };\n const openCreate = () => {\n setEditRecord(null);\n setForm({\n customer_id: 0,\n customer_name: '',\n analysis_period: 'month',\n period_start: '',\n period_end: '',\n order_count: 0,\n order_amount: 0,\n delivery_count: 0,\n return_count: 0,\n complaint_count: 0,\n on_time_rate: 0,\n satisfaction_score: 0,\n customer_level: 'C',\n growth_rate: 0,\n remark: '',\n });\n setDialogOpen(true);\n };\n\n return (\n <MainLayout>\n <div className=\"p-6 space-y-4\">\n <div className=\"flex items-center justify-between\">\n <h1 className=\"text-2xl font-bold flex items-center gap-2\">\n <BarChart3 className=\"h-6 w-6\" />\n {t('customerAnalysisStats')}\n </h1>\n <Button onClick={openCreate}>\n <Plus className=\"h-4 w-4 mr-1\" />\n {t('newAnalysis')}\n </Button>\n </div>\n\n <div className=\"grid grid-cols-4 gap-4\">\n <Card>\n <CardContent className=\"p-4 flex items-center gap-3\">\n <Users className=\"h-8 w-8 text-blue-500\" />\n <div>\n <p className=\"text-sm text-muted-foreground\">{t('totalCustomers')}</p>\n <p className=\"text-2xl font-bold\">{summary.total_customers || 0}</p>\n </div>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"p-4 flex items-center gap-3\">\n <DollarSign className=\"h-8 w-8 text-green-500\" />\n <div>\n <p className=\"text-sm text-muted-foreground\">{t('totalOrderAmount')}</p>\n <p className=\"text-2xl font-bold\">\n ¥{(summary.total_amount || 0).toLocaleString()}\n </p>\n </div>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"p-4 flex items-center gap-3\">\n <TrendingUp className=\"h-8 w-8 text-orange-500\" />\n <div>\n <p className=\"text-sm text-muted-foreground\">{t('avgSatisfaction')}</p>\n <p className=\"text-2xl font-bold\">{(summary.avg_satisfaction || 0).toFixed(1)}</p>\n </div>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"p-4 flex items-center gap-3\">\n <BarChart3 className=\"h-8 w-8 text-purple-500\" />\n <div>\n <p className=\"text-sm text-muted-foreground\">{t('avgOnTimeRate')}</p>\n <p className=\"text-2xl font-bold\">{(summary.avg_on_time_rate || 0).toFixed(1)}%</p>\n </div>\n </CardContent>\n </Card>\n </div>\n\n <Card>\n <CardContent className=\"p-4\">\n <div className=\"flex gap-3 mb-4\">\n <Input\n placeholder={t('searchCustomerName')}\n value={searchName}\n onChange={(e) => setSearchName(e.target.value)}\n className=\"w-48\"\n onKeyDown={(e) => e.key === 'Enter' && fetchData()}\n />\n <Select value={searchLevel} onValueChange={(v) => setSearchLevel(v)}>\n <SelectTrigger className=\"w-32\">\n <SelectValue placeholder={t('customerLevel')} />\n </SelectTrigger>\n <SelectContent>\n {Object.entries(levelMap).map(([k, v]) => (\n <SelectItem key={k} value={k}>\n {v.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n <Button variant=\"outline\" onClick={fetchData}>\n <Search className=\"h-4 w-4 mr-1\" />\n {t('search')}\n </Button>\n </div>\n\n <Table>\n <TableHeader>\n <TableRow>\n <TableHead>{t('customerName')}</TableHead>\n <TableHead>{t('level')}</TableHead>\n <TableHead>{t('period')}</TableHead>\n <TableHead>{t('orderCount')}</TableHead>\n <TableHead>{t('orderAmount')}</TableHead>\n <TableHead>{t('returnCount')}</TableHead>\n <TableHead>{t('complaintCount')}</TableHead>\n <TableHead>{t('onTimeRateShort')}</TableHead>\n <TableHead>{t('satisfaction')}</TableHead>\n <TableHead>{t('growthRate')}</TableHead>\n <TableHead>{tc('actions')}</TableHead>\n </TableRow>\n </TableHeader>\n <TableBody>\n {records.map((r) => (\n <TableRow key={r.id}>\n <TableCell>{r.customer_name}</TableCell>\n <TableCell>\n <Badge variant={levelMap[r.customer_level]?.variant || 'outline'}>\n {levelMap[r.customer_level]?.label || r.customer_level}\n </Badge>\n </TableCell>\n <TableCell>{periodMap[r.analysis_period] || r.analysis_period}</TableCell>\n <TableCell>{r.order_count}</TableCell>\n <TableCell className=\"font-mono\">\n ¥{(r.order_amount || 0).toLocaleString()}\n </TableCell>\n <TableCell>{r.return_count}</TableCell>\n <TableCell>{r.complaint_count}</TableCell>\n <TableCell>{r.on_time_rate != null ? r.on_time_rate + '%' : '-'}</TableCell>\n <TableCell>\n {r.satisfaction_score != null ? r.satisfaction_score : '-'}\n </TableCell>\n <TableCell>\n {r.growth_rate != null\n ? (r.growth_rate > 0 ? '+' : '') + r.growth_rate + '%'\n : '-'}\n </TableCell>\n <TableCell>\n <div className=\"flex gap-1\">\n <Button size=\"sm\" variant=\"ghost\" onClick={() => openEdit(r)}>\n <Edit className=\"h-4 w-4\" />\n </Button>\n <Button size=\"sm\" variant=\"ghost\" onClick={() => handleDelete(r.id!)}>\n <Trash2 className=\"h-4 w-4 text-destructive\" />\n </Button>\n </div>\n </TableCell>\n </TableRow>\n ))}\n {records.length === 0 && (\n <TableRow>\n <TableCell colSpan={11} className=\"text-center py-8 text-muted-foreground\">\n {tc('noData')}\n </TableCell>\n </TableRow>\n )}\n </TableBody>\n </Table>\n <div className=\"flex justify-between items-center mt-4 text-sm\">\n <span>{t('totalRecords', { total })}</span>\n <div className=\"flex gap-2\">\n <Button\n size=\"sm\"\n variant=\"outline\"\n disabled={page <= 1}\n onClick={() => setPage((p) => p - 1)}\n >\n {tc('prevPage')}\n </Button>\n <Button\n size=\"sm\"\n variant=\"outline\"\n disabled={page * 20 >= total}\n onClick={() => setPage((p) => p + 1)}\n >\n {tc('nextPage')}\n </Button>\n </div>\n </div>\n </CardContent>\n </Card>\n\n <Dialog open={dialogOpen} onOpenChange={setDialogOpen}>\n <DialogContent className=\"max-w-2xl\" resizable>\n <DialogHeader>\n <DialogTitle>{editRecord ? t('editAnalysis') : t('newAnalysis')}</DialogTitle>\n </DialogHeader>\n <div className=\"grid gap-3 py-2\">\n <div className=\"grid grid-cols-3 gap-3\">\n <div>\n <Label>{t('customerName')} *</Label>\n <Input\n value={form.customer_name || ''}\n onChange={(e) => setForm({ ...form, customer_name: e.target.value })}\n />\n </div>\n <div>\n <Label>{t('analysisPeriod')}</Label>\n <Select\n value={form.analysis_period || 'month'}\n onValueChange={(v) => setForm({ ...form, analysis_period: v })}\n >\n <SelectTrigger>\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {Object.entries(periodMap).map(([k, v]) => (\n <SelectItem key={k} value={k}>\n {v}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n <div>\n <Label>{t('customerLevel')}</Label>\n <Select\n value={form.customer_level || 'C'}\n onValueChange={(v) => setForm({ ...form, customer_level: v })}\n >\n <SelectTrigger>\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {Object.entries(levelMap).map(([k, v]) => (\n <SelectItem key={k} value={k}>\n {v.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n </div>\n <div className=\"grid grid-cols-2 gap-3\">\n <div>\n <Label>{t('periodStart')}</Label>\n <Input\n type=\"date\"\n value={form.period_start || ''}\n onChange={(e) => setForm({ ...form, period_start: e.target.value })}\n />\n </div>\n <div>\n <Label>{t('periodEnd')}</Label>\n <Input\n type=\"date\"\n value={form.period_end || ''}\n onChange={(e) => setForm({ ...form, period_end: e.target.value })}\n />\n </div>\n </div>\n <div className=\"grid grid-cols-4 gap-3\">\n <div>\n <Label>{t('orderCount')}</Label>\n <Input\n type=\"number\"\n value={form.order_count || 0}\n onChange={(e) => setForm({ ...form, order_count: Number(e.target.value) })}\n />\n </div>\n <div>\n <Label>{t('orderAmount')}</Label>\n <Input\n type=\"number\"\n value={form.order_amount || 0}\n onChange={(e) => setForm({ ...form, order_amount: Number(e.target.value) })}\n />\n </div>\n <div>\n <Label>{t('deliveryCount')}</Label>\n <Input\n type=\"number\"\n value={form.delivery_count || 0}\n onChange={(e) => setForm({ ...form, delivery_count: Number(e.target.value) })}\n />\n </div>\n <div>\n <Label>{t('returnCount')}</Label>\n <Input\n type=\"number\"\n value={form.return_count || 0}\n onChange={(e) => setForm({ ...form, return_count: Number(e.target.value) })}\n />\n </div>\n </div>\n <div className=\"grid grid-cols-4 gap-3\">\n <div>\n <Label>{t('complaintCount')}</Label>\n <Input\n type=\"number\"\n value={form.complaint_count || 0}\n onChange={(e) => setForm({ ...form, complaint_count: Number(e.target.value) })}\n />\n </div>\n <div>\n <Label>{t('onTimeRatePct')}</Label>\n <Input\n type=\"number\"\n value={form.on_time_rate || 0}\n onChange={(e) => setForm({ ...form, on_time_rate: Number(e.target.value) })}\n />\n </div>\n <div>\n <Label>{t('satisfactionScore')}</Label>\n <Input\n type=\"number\"\n step=\"0.1\"\n value={form.satisfaction_score || 0}\n onChange={(e) =>\n setForm({ ...form, satisfaction_score: Number(e.target.value) })\n }\n />\n </div>\n <div>\n <Label>{t('growthRatePct')}</Label>\n <Input\n type=\"number\"\n value={form.growth_rate || 0}\n onChange={(e) => setForm({ ...form, growth_rate: Number(e.target.value) })}\n />\n </div>\n </div>\n <div>\n <Label>{tc('remark')}</Label>\n <Textarea\n value={form.remark || ''}\n onChange={(e) => setForm({ ...form, remark: e.target.value })}\n rows={2}\n />\n </div>\n </div>\n <DialogFooter>\n <Button variant=\"outline\" onClick={() => setDialogOpen(false)}>\n {tc('cancel')}\n </Button>\n <Button onClick={handleSave}>{t('save')}</Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n </div>\n </MainLayout>\n );\n}\n","usedDeprecatedRules":[]},{"filePath":"D:\\dcprint\\erp-project\\src\\app\\[locale]\\crm\\error.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\dcprint\\erp-project\\src\\app\\[locale]\\crm\\follow\\page.tsx","messages":[{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":104,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":104,"endColumn":36},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .code on an `any` value.","line":105,"column":16,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":105,"endColumn":20},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction<FollowRecord[]>`.","line":106,"column":20,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":106,"endColumn":40},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":106,"column":25,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":106,"endColumn":29},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction<number>`.","line":107,"column":18,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":107,"endColumn":38},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":107,"column":23,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":107,"endColumn":27},{"ruleId":"react-hooks/set-state-in-effect","severity":1,"message":"Error: Calling setState synchronously within an effect can trigger cascading renders\n\nEffects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following:\n* Update external systems with the latest state from React.\n* Subscribe for updates from some external system, calling setState in a callback function when external state changes.\n\nCalling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect).\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\crm\\follow\\page.tsx:116:5\n 114 |\n 115 | useEffect(() => {\n> 116 | fetchData();\n | ^^^^^^^^^ Avoid calling setState() directly within an effect\n 117 | }, [page]);\n 118 |\n 119 | const handleSave = async () => {","line":116,"column":5,"nodeType":null,"endLine":116,"endColumn":14},{"ruleId":"react-hooks/exhaustive-deps","severity":1,"message":"React Hook useEffect has a missing dependency: 'fetchData'. Either include it or remove the dependency array.","line":117,"column":6,"nodeType":"ArrayExpression","endLine":117,"endColumn":12,"suggestions":[{"desc":"Update the dependencies array to be: [fetchData, page]","fix":{"range":[3377,3383],"text":"[fetchData, page]"}}]},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":132,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":132,"endColumn":36},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .code on an `any` value.","line":133,"column":16,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":133,"endColumn":20},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":138,"column":17,"nodeType":"Property","messageId":"anyAssignment","endLine":138,"endColumn":51},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .message on an `any` value.","line":138,"column":29,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":138,"endColumn":36},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":149,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":149,"endColumn":36},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .code on an `any` value.","line":150,"column":16,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":150,"endColumn":20}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":14,"fixableErrorCount":0,"fixableWarningCount":0,"source":"'use client';\r\n\r\nimport { authFetch } from '@/lib/auth-fetch';\r\nimport { useEffect, useState } from 'react';\r\nimport { MainLayout } from '@/components/layout';\r\nimport { Card, CardContent } from '@/components/ui/card';\r\nimport { Button } from '@/components/ui/button';\r\nimport { Input } from '@/components/ui/input';\r\nimport { Badge } from '@/components/ui/badge';\r\nimport {\r\n Table,\r\n TableBody,\r\n TableCell,\r\n TableHead,\r\n TableHeader,\r\n TableRow,\r\n} from '@/components/ui/table';\r\nimport {\r\n Dialog,\r\n DialogContent,\r\n DialogHeader,\r\n DialogTitle,\r\n DialogFooter,\r\n} from '@/components/ui/dialog';\r\nimport { Label } from '@/components/ui/label';\r\nimport {\r\n Select,\r\n SelectContent,\r\n SelectItem,\r\n SelectTrigger,\r\n SelectValue,\r\n} from '@/components/ui/select';\r\nimport { Textarea } from '@/components/ui/textarea';\r\nimport { Plus, Search, Edit, Trash2, Phone } from 'lucide-react';\r\nimport { useToast } from '@/hooks/use-toast';\r\nimport { useTranslations } from 'next-intl';\r\n\r\ninterface FollowRecord {\r\n id?: number;\r\n customer_id: number;\r\n customer_name: string;\r\n follow_type: string;\r\n follow_content: string;\r\n contact_name: string;\r\n salesman_name: string;\r\n next_follow_date: string;\r\n opportunity: string;\r\n status: number;\r\n remark: string;\r\n create_time: string;\r\n}\r\n\r\nexport default function CustomerFollowPage() {\r\n\r\n const t = useTranslations('Crm');\r\n const tc = useTranslations('Common');\r\n\r\n const followTypeMap: Record<string, string> = {\r\n visit: t('visit'),\r\n phone: t('phone'),\r\n email: t('email'),\r\n wechat: t('wechat'),\r\n other: tc('other'),\r\n };\r\n\r\n const followStatusMap: Record<\r\n number,\r\n { label: string; variant: 'default' | 'secondary' | 'destructive' | 'outline' }\r\n > = {\r\n 1: { label: t('pendingFollow'), variant: 'outline' },\r\n 2: { label: t('followed'), variant: 'default' },\r\n 3: { label: t('converted'), variant: 'secondary' },\r\n };\r\n\r\n const { toast } = useToast();\r\n const [records, setRecords] = useState<FollowRecord[]>([]);\r\n const [total, setTotal] = useState(0);\r\n const [page, setPage] = useState(1);\r\n const [_loading, setLoading] = useState(false);\r\n const [searchName, setSearchName] = useState('');\r\n const [searchType, setSearchType] = useState('');\r\n const [dialogOpen, setDialogOpen] = useState(false);\r\n const [editRecord, setEditRecord] = useState<FollowRecord | null>(null);\r\n const [form, setForm] = useState<Partial<FollowRecord>>({\r\n customer_id: 0,\r\n customer_name: '',\r\n follow_type: 'phone',\r\n follow_content: '',\r\n contact_name: '',\r\n salesman_name: '',\r\n next_follow_date: '',\r\n opportunity: '',\r\n status: 1,\r\n remark: '',\r\n });\r\n\r\n const fetchData = async () => {\r\n setLoading(true);\r\n try {\r\n const params = new URLSearchParams({ page: String(page), pageSize: '20' });\r\n if (searchName) params.set('customerName', searchName);\r\n if (searchType) params.set('followType', searchType);\r\n const res = await authFetch('/api/crm/follow?' + params);\r\n const data = await res.json();\r\n if (data.code === 200) {\r\n setRecords(data.data.list || []);\r\n setTotal(data.data.total || 0);\r\n }\r\n } catch {\r\n toast({ title: tc('fetchFailed'), variant: 'destructive' });\r\n }\r\n setLoading(false);\r\n };\r\n\r\n useEffect(() => {\r\n fetchData();\r\n }, [page]);\r\n\r\n const handleSave = async () => {\r\n if (!form.customer_name) {\r\n toast({ title: t('enterCustomerName'), variant: 'destructive' });\r\n return;\r\n }\r\n try {\r\n const method = editRecord ? 'PUT' : 'POST';\r\n const body = editRecord ? { id: editRecord.id, ...form } : form;\r\n const res = await authFetch('/api/crm/follow', {\r\n method,\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify(body),\r\n });\r\n const data = await res.json();\r\n if (data.code === 200) {\r\n toast({ title: editRecord ? tc('updateSuccess') : tc('createSuccess') });\r\n setDialogOpen(false);\r\n fetchData();\r\n } else {\r\n toast({ title: data.message || tc('error'), variant: 'destructive' });\r\n }\r\n } catch {\r\n toast({ title: tc('error'), variant: 'destructive' });\r\n }\r\n };\r\n\r\n const handleDelete = async (id: number) => {\r\n if (!confirm(tc('confirmDelete'))) return;\r\n try {\r\n const res = await authFetch('/api/crm/follow?id=' + id, { method: 'DELETE' });\r\n const data = await res.json();\r\n if (data.code === 200) {\r\n toast({ title: tc('deleteSuccess') });\r\n fetchData();\r\n }\r\n } catch {\r\n toast({ title: tc('deleteFailed'), variant: 'destructive' });\r\n }\r\n };\r\n\r\n const openEdit = (record: FollowRecord) => {\r\n setEditRecord(record);\r\n setForm({ ...record });\r\n setDialogOpen(true);\r\n };\r\n const openCreate = () => {\r\n setEditRecord(null);\r\n setForm({\r\n customer_id: 0,\r\n customer_name: '',\r\n follow_type: 'phone',\r\n follow_content: '',\r\n contact_name: '',\r\n salesman_name: '',\r\n next_follow_date: '',\r\n opportunity: '',\r\n status: 1,\r\n remark: '',\r\n });\r\n setDialogOpen(true);\r\n };\r\n\r\n return (\r\n <MainLayout>\r\n <div className=\"p-6 space-y-4\">\r\n <div className=\"flex items-center justify-between\">\r\n <h1 className=\"text-2xl font-bold flex items-center gap-2\">\r\n <Phone className=\"h-6 w-6\" />\r\n {t('followRecords')}\r\n </h1>\r\n <Button onClick={openCreate}>\r\n <Plus className=\"h-4 w-4 mr-1\" />\r\n {t('newFollow')}\r\n </Button>\r\n </div>\r\n\r\n <Card>\r\n <CardContent className=\"p-4\">\r\n <div className=\"flex gap-3 mb-4\">\r\n <Input\r\n placeholder={t('searchCustomerPlaceholder')}\r\n value={searchName}\r\n onChange={(e) => setSearchName(e.target.value)}\r\n className=\"w-48\"\r\n onKeyDown={(e) => e.key === 'Enter' && fetchData()}\r\n />\r\n <Select value={searchType} onValueChange={(v) => setSearchType(v)}>\r\n <SelectTrigger className=\"w-32\">\r\n <SelectValue placeholder={t('followType')} />\r\n </SelectTrigger>\r\n <SelectContent>\r\n {Object.entries(followTypeMap).map(([k, v]) => (\r\n <SelectItem key={k} value={k}>\r\n {v}\r\n </SelectItem>\r\n ))}\r\n </SelectContent>\r\n </Select>\r\n <Button variant=\"outline\" onClick={fetchData}>\r\n <Search className=\"h-4 w-4 mr-1\" />\r\n {tc('search')}\r\n </Button>\r\n </div>\r\n\r\n <Table>\r\n <TableHeader>\r\n <TableRow>\r\n <TableHead>{t('customerName')}</TableHead>\r\n <TableHead>{t('followType')}</TableHead>\r\n <TableHead>{t('followContent')}</TableHead>\r\n <TableHead>{t('contactPerson')}</TableHead>\r\n <TableHead>{t('salesman')}</TableHead>\r\n <TableHead>{t('nextFollow')}</TableHead>\r\n <TableHead>{t('opportunity')}</TableHead>\r\n <TableHead>{tc(\"status\")}</TableHead>\r\n <TableHead>{tc(\"actions\")}</TableHead>\r\n </TableRow>\r\n </TableHeader>\r\n <TableBody>\r\n {records.map((r) => (\r\n <TableRow key={r.id}>\r\n <TableCell>{r.customer_name}</TableCell>\r\n <TableCell>\r\n <Badge variant=\"outline\">\r\n {followTypeMap[r.follow_type] || r.follow_type}\r\n </Badge>\r\n </TableCell>\r\n <TableCell className=\"max-w-48 truncate\">{r.follow_content || '-'}</TableCell>\r\n <TableCell>{r.contact_name || '-'}</TableCell>\r\n <TableCell>{r.salesman_name || '-'}</TableCell>\r\n <TableCell>{r.next_follow_date || '-'}</TableCell>\r\n <TableCell className=\"max-w-32 truncate\">{r.opportunity || '-'}</TableCell>\r\n <TableCell>\r\n <Badge variant={followStatusMap[r.status]?.variant || 'outline'}>\r\n {followStatusMap[r.status]?.label || '-'}\r\n </Badge>\r\n </TableCell>\r\n <TableCell>\r\n <div className=\"flex gap-1\">\r\n <Button size=\"sm\" variant=\"ghost\" onClick={() => openEdit(r)}>\r\n <Edit className=\"h-4 w-4\" />\r\n </Button>\r\n <Button size=\"sm\" variant=\"ghost\" onClick={() => handleDelete(r.id!)}>\r\n <Trash2 className=\"h-4 w-4 text-destructive\" />\r\n </Button>\r\n </div>\r\n </TableCell>\r\n </TableRow>\r\n ))}\r\n {records.length === 0 && (\r\n <TableRow>\r\n <TableCell colSpan={9} className=\"text-center py-8 text-muted-foreground\">\r\n {tc('noData')}\r\n </TableCell>\r\n </TableRow>\r\n )}\r\n </TableBody>\r\n </Table>\r\n <div className=\"flex justify-between items-center mt-4 text-sm\">\r\n <span>{tc('totalRecords', { total })}</span>\r\n <div className=\"flex gap-2\">\r\n <Button\r\n size=\"sm\"\r\n variant=\"outline\"\r\n disabled={page <= 1}\r\n onClick={() => setPage((p) => p - 1)}\r\n >\r\n {tc('prevPage')}\r\n </Button>\r\n <Button\r\n size=\"sm\"\r\n variant=\"outline\"\r\n disabled={page * 20 >= total}\r\n onClick={() => setPage((p) => p + 1)}\r\n >\r\n {tc('nextPage')}\r\n </Button>\r\n </div>\r\n </div>\r\n </CardContent>\r\n </Card>\r\n\r\n <Dialog open={dialogOpen} onOpenChange={setDialogOpen}>\r\n <DialogContent className=\"max-w-lg\" resizable>\r\n <DialogHeader>\r\n <DialogTitle>{editRecord ? t('editFollowRecord') : t('newFollowRecord')}</DialogTitle>\r\n </DialogHeader>\r\n <div className=\"grid gap-3 py-2\">\r\n <div className=\"grid grid-cols-2 gap-3\">\r\n <div>\r\n <Label>{t('customerNameRequired')}</Label>\r\n <Input\r\n value={form.customer_name || ''}\r\n onChange={(e) => setForm({ ...form, customer_name: e.target.value })}\r\n />\r\n </div>\r\n <div>\r\n <Label>{t('followType')}</Label>\r\n <Select\r\n value={form.follow_type || 'phone'}\r\n onValueChange={(v) => setForm({ ...form, follow_type: v })}\r\n >\r\n <SelectTrigger>\r\n <SelectValue />\r\n </SelectTrigger>\r\n <SelectContent>\r\n {Object.entries(followTypeMap).map(([k, v]) => (\r\n <SelectItem key={k} value={k}>\r\n {v}\r\n </SelectItem>\r\n ))}\r\n </SelectContent>\r\n </Select>\r\n </div>\r\n </div>\r\n <div className=\"grid grid-cols-2 gap-3\">\r\n <div>\r\n <Label>{t('contactPerson')}</Label>\r\n <Input\r\n value={form.contact_name || ''}\r\n onChange={(e) => setForm({ ...form, contact_name: e.target.value })}\r\n />\r\n </div>\r\n <div>\r\n <Label>{t('salesman')}</Label>\r\n <Input\r\n value={form.salesman_name || ''}\r\n onChange={(e) => setForm({ ...form, salesman_name: e.target.value })}\r\n />\r\n </div>\r\n </div>\r\n <div>\r\n <Label>{t('followContent')}</Label>\r\n <Textarea\r\n value={form.follow_content || ''}\r\n onChange={(e) => setForm({ ...form, follow_content: e.target.value })}\r\n rows={3}\r\n />\r\n </div>\r\n <div className=\"grid grid-cols-2 gap-3\">\r\n <div>\r\n <Label>{t('nextFollowDate')}</Label>\r\n <Input\r\n type=\"date\"\r\n value={form.next_follow_date || ''}\r\n onChange={(e) => setForm({ ...form, next_follow_date: e.target.value })}\r\n />\r\n </div>\r\n <div>\r\n <Label>{tc(\"status\")}</Label>\r\n <Select\r\n value={String(form.status || 1)}\r\n onValueChange={(v) => setForm({ ...form, status: Number(v) })}\r\n >\r\n <SelectTrigger>\r\n <SelectValue />\r\n </SelectTrigger>\r\n <SelectContent>\r\n <SelectItem value=\"1\">{t('pendingFollow')}</SelectItem>\r\n <SelectItem value=\"2\">{t('followed')}</SelectItem>\r\n <SelectItem value=\"3\">{t('converted')}</SelectItem>\r\n </SelectContent>\r\n </Select>\r\n </div>\r\n </div>\r\n <div>\r\n <Label>{t('opportunityDesc')}</Label>\r\n <Input\r\n value={form.opportunity || ''}\r\n onChange={(e) => setForm({ ...form, opportunity: e.target.value })}\r\n />\r\n </div>\r\n <div>\r\n <Label>{tc(\"remark\")}</Label>\r\n <Textarea\r\n value={form.remark || ''}\r\n onChange={(e) => setForm({ ...form, remark: e.target.value })}\r\n rows={2}\r\n />\r\n </div>\r\n </div>\r\n <DialogFooter>\r\n <Button variant=\"outline\" onClick={() => setDialogOpen(false)}>\r\n {tc(\"cancel\")}\r\n </Button>\r\n <Button onClick={handleSave}>{tc(\"save\")}</Button>\r\n </DialogFooter>\r\n </DialogContent>\r\n </Dialog>\r\n </div>\r\n </MainLayout>\r\n );\r\n}\r\n","usedDeprecatedRules":[]},{"filePath":"D:\\dcprint\\erp-project\\src\\app\\[locale]\\dashboard\\ceo\\page.tsx","messages":[{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"系统运行正常,所有产线在线\"。建议使用: tc('text_p3qj4f')","line":280,"column":33,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":280,"endColumn":48},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"原材料仓库 B-3 区油墨库存偏低,建议补货\"。建议使用: tc('text_dytf5x')","line":281,"column":36,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":281,"endColumn":60},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"今日已完成 3 批次出货,合计 12,500 PCS\"。建议使用: tc('text_axmi8k')","line":282,"column":33,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":282,"endColumn":61},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"设备 SP-003 计划保养倒计时 2 天\"。建议使用: tc('text_lxtkr1')","line":283,"column":36,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":283,"endColumn":59},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"客户「深圳电子」追加订单已确认,交期 7/28\"。建议使用: tc('text_7225f4')","line":284,"column":36,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":284,"endColumn":61},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"预警\"。建议使用: {tc('text_qpgi')}","line":301,"column":75,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":303,"endColumn":9},{"ruleId":"react-hooks/set-state-in-effect","severity":1,"message":"Error: Calling setState synchronously within an effect can trigger cascading renders\n\nEffects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following:\n* Update external systems with the latest state from React.\n* Subscribe for updates from some external system, calling setState in a callback function when external state changes.\n\nCalling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect).\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\dashboard\\ceo\\page.tsx:372:5\n 370 |\n 371 | useEffect(() => {\n> 372 | setCurrentTime(new Date());\n | ^^^^^^^^^^^^^^ Avoid calling setState() directly within an effect\n 373 | const fetchData = async () => {\n 374 | try {\n 375 | const res = await authFetch('/api/dashboard/ceo');","line":372,"column":5,"nodeType":null,"endLine":372,"endColumn":19},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":376,"column":15,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":376,"endColumn":40},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":377,"column":20,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":377,"endColumn":27},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":377,"column":38,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":377,"endColumn":42},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction<CEOData>`.","line":377,"column":52,"nodeType":"MemberExpression","messageId":"unsafeArgument","endLine":377,"endColumn":63},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":377,"column":59,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":377,"endColumn":63},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"VNERP丝网印刷管理系统\"。建议使用: {tc('text_p944b5')}","line":451,"column":153,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":453,"endColumn":17},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"● 实时\"。建议使用: {tc('text_4s52x5')}","line":481,"column":14,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":483,"endColumn":13},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"今日新增\"。建议使用: {tc('text_ad63yh')}","line":498,"column":81,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":500,"endColumn":19},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"待交付\"。建议使用: {tc('text_edhbt')}","line":515,"column":81,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":517,"endColumn":19},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"近 7 日趋势\"。建议使用: {tc('text_1r2e2p')}","line":529,"column":80,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":531,"endColumn":15},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"在产\"。建议使用: {tc('text_f98f')}","line":552,"column":80,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":554,"endColumn":19},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"完成\"。建议使用: {tc('text_g3yc')}","line":560,"column":79,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":562,"endColumn":19},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"预警\"。建议使用: {tc('text_qpgi')}","line":568,"column":80,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":570,"endColumn":19},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"急\"。建议使用: {tc('text_izp')}","line":610,"column":26,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":612,"endColumn":25},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"合格率\"。建议使用: {tc('text_ctgib')}","line":629,"column":25,"nodeType":"Literal","messageId":"noChineseInJSX","endLine":629,"endColumn":30},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"检验总数\"。建议使用: {tc('text_duqiqp')}","line":635,"column":62,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":635,"endColumn":66},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"合格\"。建议使用: {tc('text_ev5g')}","line":641,"column":61,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":641,"endColumn":63},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"不合格\"。建议使用: {tc('text_bufb5')}","line":647,"column":59,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":647,"endColumn":62},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"全息投影 · 丝网印刷机\"。建议使用: {tc('text_nyoqux')}","line":675,"column":69,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":677,"endColumn":17},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"● 在线\"。建议使用: {tc('text_4s4gi0')}","line":684,"column":50,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":684,"endColumn":54},{"ruleId":"@next/next/no-img-element","severity":1,"message":"Using `<img>` could result in slower LCP and higher bandwidth. Consider using `<Image />` from `next/image` or a custom image loader to automatically optimize images. This may incur additional usage or cost from your provider. See: https://nextjs.org/docs/messages/no-img-element","line":696,"column":17,"nodeType":"JSXOpeningElement","endLine":700,"endColumn":19},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"3D 全息丝网印刷机\"。建议使用: {tc('text_woggix')}","line":698,"column":23,"nodeType":"Literal","messageId":"noChineseInJSX","endLine":698,"endColumn":35},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"物料种类\"。建议使用: {tc('text_euyv8e')}","line":789,"column":80,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":791,"endColumn":19},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"低库存\"。建议使用: {tc('text_c2rcj')}","line":797,"column":80,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":799,"endColumn":19},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"仓库利用率\"。建议使用: {tc('text_yoasig')}","line":806,"column":82,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":808,"endColumn":17},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"库存正常\"。建议使用: {tc('text_cbbr4q')}","line":843,"column":100,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":845,"endColumn":19},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"运行\"。建议使用: {tc('text_p7jw')}","line":854,"column":79,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":856,"endColumn":19},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"保养\"。建议使用: {tc('text_e14u')}","line":862,"column":79,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":864,"endColumn":19},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"故障\"。建议使用: {tc('text_i1vb')}","line":870,"column":77,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":872,"endColumn":19},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"应收总额\"。建议使用: {tc('text_ccquec')}","line":929,"column":84,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":931,"endColumn":19},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"应付总额\"。建议使用: {tc('text_c9gwhy')}","line":937,"column":84,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":939,"endColumn":19},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"月度收入\"。建议使用: {tc('text_de6gp9')}","line":946,"column":84,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":948,"endColumn":19},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"月度支出\"。建议使用: {tc('text_de6gnd')}","line":954,"column":84,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":956,"endColumn":19},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"净利润\"。建议使用: {tc('text_cdn4t')}","line":963,"column":96,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":965,"endColumn":19}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":41,"fixableErrorCount":0,"fixableWarningCount":0,"source":"'use client';\n\nimport { authFetch } from '@/lib/auth-fetch';\nimport { MainLayout } from '@/components/layout';\nimport { useEffect, useState, useRef } from 'react';\nimport {\n Package,\n Factory,\n AlertTriangle,\n DollarSign,\n ShoppingCart,\n Activity,\n CheckCircle2,\n Cpu,\n Maximize,\n} from 'lucide-react';\nimport { useTranslations, useLocale } from 'next-intl';\n\n/* ═══ 数据接口 ═══ */\ninterface CEOData {\n overview: {\n todayOrders: number;\n todayProduction: number;\n todayDelivery: number;\n inventoryValue: number;\n orderChange: number;\n productionChange: number;\n deliveryChange: number;\n inventoryChange: number;\n };\n production: {\n efficiency: number;\n activeOrders: number;\n completedToday: number;\n warningCount: number;\n equipmentStatus: { name: string; status: string; efficiency: number }[];\n activeWorkOrders?: {\n work_order_no: string;\n product_name: string;\n customer_name: string;\n status: string;\n priority: string;\n }[];\n };\n quality: {\n passRate: number;\n totalInspections: number;\n passedInspections: number;\n failedInspections: number;\n recentDefects: { product_name?: string; defect_type?: string; quantity?: number }[];\n };\n finance: {\n totalReceivable: number;\n totalPayable: number;\n monthRevenue: number;\n monthExpense: number;\n revenueChange: number;\n expenseChange: number;\n };\n inventory: {\n totalItems: number;\n lowStock: number;\n totalValue: number;\n warehouseUtilization: number;\n };\n orderTrend: { date: string; count: number }[];\n topProducts: { product_name: string; total_qty: number }[];\n shiftData: {\n dayShift: { plan: number; actual: number; rate: number };\n middleShift: { plan: number; actual: number; rate: number };\n nightShift: { plan: number; actual: number; rate: number };\n };\n}\n\nconst emptyData: CEOData = {\n overview: {\n todayOrders: 0,\n todayProduction: 0,\n todayDelivery: 0,\n inventoryValue: 0,\n orderChange: 0,\n productionChange: 0,\n deliveryChange: 0,\n inventoryChange: 0,\n },\n production: {\n efficiency: 0,\n activeOrders: 0,\n completedToday: 0,\n warningCount: 0,\n equipmentStatus: [],\n activeWorkOrders: [],\n },\n quality: {\n passRate: 0,\n totalInspections: 0,\n passedInspections: 0,\n failedInspections: 0,\n recentDefects: [],\n },\n finance: {\n totalReceivable: 0,\n totalPayable: 0,\n monthRevenue: 0,\n monthExpense: 0,\n revenueChange: 0,\n expenseChange: 0,\n },\n inventory: { totalItems: 0, lowStock: 0, totalValue: 0, warehouseUtilization: 0 },\n orderTrend: [],\n topProducts: [],\n shiftData: {\n dayShift: { plan: 0, actual: 0, rate: 0 },\n middleShift: { plan: 0, actual: 0, rate: 0 },\n nightShift: { plan: 0, actual: 0, rate: 0 },\n },\n};\n\n/* ═══ 主题色 ═══ */\nconst C = {\n bg: '#0A1E3D',\n silver: '#C0D0E0',\n orange: '#FF6B35',\n cyan: '#22d3ee',\n blue: '#3b82f6',\n green: '#22c55e',\n red: '#ef4444',\n amber: '#f59e0b',\n};\n\n/* ═══ 玻璃卡片 ═══ */\nfunction GlassPanel({\n title,\n icon: Icon,\n children,\n accent = C.cyan,\n className = '',\n}: {\n title: string;\n icon: Loose;\n children: React.ReactNode;\n accent?: string;\n className?: string;\n}) {\n return (\n <div\n className={`relative overflow-hidden ${className}`}\n style={{\n background: 'linear-gradient(135deg, rgba(10,30,61,0.85) 0%, rgba(5,15,35,0.9) 100%)',\n border: `1px solid ${C.silver}22`,\n borderRadius: '12px',\n backdropFilter: 'blur(16px)',\n }}\n >\n {/* 顶部光带 */}\n <div\n className=\"absolute top-0 left-0 right-0 h-px\"\n style={{ background: `linear-gradient(90deg, transparent, ${accent}, transparent)` }}\n />\n {/* 标题栏 */}\n <div\n className=\"flex items-center gap-2 px-3 py-2\"\n style={{ borderBottom: `1px solid ${C.silver}11` }}\n >\n <div\n className=\"w-1 h-4 rounded-full\"\n style={{ background: accent, boxShadow: `0 0 8px ${accent}` }}\n />\n <Icon className=\"h-4 w-4\" style={{ color: accent }} />\n <span className=\"text-sm font-medium\" style={{ color: C.silver }}>\n {title}\n </span>\n </div>\n <div className=\"p-3\">{children}</div>\n </div>\n );\n}\n\n/* ═══ 迷你折线图 ═══ */\nfunction Sparkline({\n data,\n color = C.cyan,\n height = 60,\n}: {\n data: number[];\n color?: string;\n height?: number;\n}) {\n if (data.length < 2)\n return (\n <div className=\"flex items-center justify-center text-white/20 text-xs\" style={{ height }}>\n —\n </div>\n );\n const max = Math.max(...data, 1);\n const min = Math.min(...data, 0);\n const range = max - min || 1;\n const w = 100;\n const pts = data\n .map(\n (v, i) => `${(i / (data.length - 1)) * w},${height - ((v - min) / range) * (height - 8) - 4}`\n )\n .join(' ');\n return (\n <svg viewBox={`0 0 ${w} ${height}`} className=\"w-full\" style={{ height }}>\n <polygon points={`0,${height} ${pts} ${w},${height}`} fill={color} opacity={0.08} />\n <polyline\n points={pts}\n fill=\"none\"\n stroke={color}\n strokeWidth={1.5}\n vectorEffect=\"non-scaling-stroke\"\n />\n </svg>\n );\n}\n\n/* ═══ 环形进度 ═══ */\nfunction RingProgress({\n value,\n label,\n color = C.cyan,\n size = 72,\n}: {\n value: number;\n label: string;\n color?: string;\n size?: number;\n}) {\n const r = size / 2 - 5;\n const circ = 2 * Math.PI * r;\n const offset = circ - (value / 100) * circ;\n return (\n <div className=\"flex flex-col items-center gap-1\">\n <div className=\"relative\" style={{ width: size, height: size }}>\n <svg width={size} height={size} className=\"-rotate-90\">\n <circle\n cx={size / 2}\n cy={size / 2}\n r={r}\n fill=\"none\"\n stroke=\"rgba(255,255,255,0.06)\"\n strokeWidth={5}\n />\n <circle\n cx={size / 2}\n cy={size / 2}\n r={r}\n fill=\"none\"\n stroke={color}\n strokeWidth={5}\n strokeDasharray={circ}\n strokeDashoffset={offset}\n strokeLinecap=\"round\"\n style={{\n filter: `drop-shadow(0 0 4px ${color}88)`,\n transition: 'stroke-dashoffset 1s ease',\n }}\n />\n </svg>\n <div className=\"absolute inset-0 flex items-center justify-center\">\n <span className=\"text-lg font-bold\" style={{ color }}>\n {value}%\n </span>\n </div>\n </div>\n <span className=\"text-xs\" style={{ color: C.silver + '88' }}>\n {label}\n </span>\n </div>\n );\n}\n\n/* ═══ 滚动预警条 ═══ */\nfunction AlertTicker({ alerts }: { alerts: { level: string; msg: string }[] }) {\n const list =\n alerts.length > 0\n ? alerts\n : [\n { level: 'info', msg: '系统运行正常,所有产线在线' },\n { level: 'warning', msg: '原材料仓库 B-3 区油墨库存偏低,建议补货' },\n { level: 'info', msg: '今日已完成 3 批次出货,合计 12,500 PCS' },\n { level: 'warning', msg: '设备 SP-003 计划保养倒计时 2 天' },\n { level: 'success', msg: '客户「深圳电子」追加订单已确认,交期 7/28' },\n ];\n return (\n <div\n className=\"flex items-center gap-2 overflow-hidden\"\n style={{\n background: 'rgba(10,30,61,0.9)',\n border: `1px solid ${C.silver}11`,\n borderRadius: '8px',\n height: '36px',\n }}\n >\n <div\n className=\"flex items-center gap-1.5 px-3 shrink-0 h-full\"\n style={{ background: C.orange + '22', borderRight: `1px solid ${C.silver}11` }}\n >\n <AlertTriangle className=\"h-3.5 w-3.5\" style={{ color: C.orange }} />\n <span className=\"text-xs font-medium\" style={{ color: C.orange }}>\n 预警\n </span>\n </div>\n <div className=\"flex-1 overflow-hidden relative\">\n <div\n className=\"flex gap-8 whitespace-nowrap\"\n style={{ animation: 'tickerScroll 30s linear infinite' }}\n >\n {[...list, ...list].map((a, i) => (\n <span\n key={i}\n className=\"text-xs flex items-center gap-1.5\"\n style={{ color: C.silver + 'aa' }}\n >\n <span\n className=\"w-1.5 h-1.5 rounded-full\"\n style={{\n background:\n a.level === 'warning' ? C.amber : a.level === 'success' ? C.green : C.cyan,\n }}\n />\n {a.msg}\n </span>\n ))}\n </div>\n </div>\n <style>{`@keyframes tickerScroll { 0% { transform: translateX(0); } 100% { transform: translateX(-50%); } }`}</style>\n </div>\n );\n}\n\n/* ═══ 自动滚动列表 ═══ */\nfunction AutoScrollList({ children, speed = 30 }: { children: React.ReactNode; speed?: number }) {\n const ref = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n const el = ref.current;\n if (!el || el.scrollHeight <= el.clientHeight) return;\n const tick = () => {\n if (el.scrollTop + el.clientHeight >= el.scrollHeight) {\n el.scrollTo({ top: 0, behavior: 'instant' });\n } else {\n el.scrollBy({ top: 1, behavior: 'instant' });\n }\n };\n const id = setInterval(tick, speed);\n return () => clearInterval(id);\n }, [speed]);\n\n return (\n <div\n ref={ref}\n className=\"flex-1 overflow-y-auto min-h-0 space-y-1\"\n style={{ scrollBehavior: 'smooth' }}\n >\n {children}\n </div>\n );\n}\n\n/* ═══ 主组件 ═══ */\nexport default function CEODashboard() {\n const tc = useTranslations('Common');\n const locale = useLocale();\n\n const [data, setData] = useState<CEOData>(emptyData);\n const [currentTime, setCurrentTime] = useState<Date | null>(null);\n const dashboardRef = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n setCurrentTime(new Date());\n const fetchData = async () => {\n try {\n const res = await authFetch('/api/dashboard/ceo');\n const result = await res.json();\n if (result.success && result.data) setData(result.data);\n } catch {}\n };\n fetchData();\n const t1 = setInterval(fetchData, 60000);\n const t2 = setInterval(() => setCurrentTime(new Date()), 1000);\n return () => {\n clearInterval(t1);\n clearInterval(t2);\n };\n }, []);\n\n const toggleFullscreen = () => {\n if (!document.fullscreenElement) {\n dashboardRef.current?.requestFullscreen();\n } else {\n document.exitFullscreen();\n }\n };\n\n const fmtMoney = (v: number) =>\n '¥' + v.toLocaleString(locale, { minimumFractionDigits: 2, maximumFractionDigits: 2 });\n const fmtNum = (v: number) => v.toLocaleString(locale);\n const fmtQty = (v: number) => (v >= 10000 ? (v / 10000).toFixed(1) + '万' : fmtNum(v));\n const fmtPct = (v: number) => (v > 0 ? '+' : '') + v.toFixed(1) + '%';\n\n const wo = data.production.activeWorkOrders || [];\n const eqList = data.production.equipmentStatus || [];\n const defects = data.quality.recentDefects || [];\n\n return (\n <MainLayout>\n <div\n ref={dashboardRef}\n className=\"w-full h-full overflow-hidden relative flex flex-col\"\n style={{\n background: `radial-gradient(ellipse at 50% 30%, #0F2851 0%, ${C.bg} 40%, #050E22 100%)`,\n fontFamily: '-apple-system, \"Segoe UI\", \"PingFang SC\", \"Microsoft YaHei\", sans-serif',\n }}\n >\n {/* ── 背景网格 ── */}\n <div\n className=\"absolute inset-0 pointer-events-none\"\n style={{\n backgroundImage: `linear-gradient(${C.cyan}04 1px, transparent 1px), linear-gradient(90deg, ${C.cyan}04 1px, transparent 1px)`,\n backgroundSize: '50px 50px',\n }}\n />\n {/* ── 背景光斑 ── */}\n <div className=\"absolute inset-0 pointer-events-none overflow-hidden\">\n <div\n className=\"absolute top-1/4 left-1/4 w-[500px] h-[500px] rounded-full blur-[120px]\"\n style={{ background: C.cyan + '08' }}\n />\n <div\n className=\"absolute bottom-1/4 right-1/4 w-[400px] h-[400px] rounded-full blur-[100px]\"\n style={{ background: C.orange + '06' }}\n />\n </div>\n\n {/* ═════════ 科技标题 ═════════ */}\n <style>{`\n .tech-title-wrapper { display: flex; flex-direction: column; align-items: center; gap: 4px; }\n .tech-title-row { display: flex; align-items: center; gap: 12px; }\n .tech-title-line-left, .tech-title-line-right { width: 60px; height: 1px; background: linear-gradient(90deg, transparent, rgba(34,211,238,0.5)); }\n .tech-title-line-right { background: linear-gradient(90deg, rgba(34,211,238,0.5), transparent); }\n .tech-title-bottom-line { width: 100%; height: 1px; background: linear-gradient(90deg, transparent, rgba(34,211,238,0.3), transparent); }\n `}</style>\n <div className=\"relative z-10 flex items-center justify-between mb-3 px-1\">\n <div className=\"flex-1\" />\n <div className=\"tech-title-wrapper\">\n <div className=\"tech-title-row\">\n <div className=\"tech-title-line-left\" />\n <div className=\"text-center\">\n <h1 className=\"text-2xl font-bold tracking-wider bg-gradient-to-r from-cyan-300 via-blue-400 to-cyan-300 bg-clip-text text-transparent\">\n VNERP丝网印刷管理系统\n </h1>\n <p className=\"text-xs mt-0.5\" style={{ color: C.silver + '88' }}>\n CEO\n </p>\n </div>\n <div className=\"tech-title-line-right\" />\n </div>\n <div className=\"tech-title-bottom-line\" />\n </div>\n <div className=\"flex-1 flex justify-end items-center gap-4\">\n <div className=\"text-right\">\n {currentTime && (\n <div className=\"text-lg font-mono font-bold\" style={{ color: C.cyan }}>\n {currentTime.toLocaleString(locale)}\n </div>\n )}\n </div>\n <button\n onClick={toggleFullscreen}\n className=\"p-2 rounded-lg transition-colors\"\n style={{ background: 'rgba(255,255,255,0.08)', color: C.cyan }}\n title=\"全屏\"\n >\n <Maximize className=\"h-4 w-4\" />\n </button>\n <div\n className=\"px-3 py-1 rounded-full text-xs\"\n style={{ background: C.cyan + '22', border: `1px solid ${C.cyan}44`, color: C.cyan }}\n >\n ● 实时\n </div>\n </div>\n </div>\n\n {/* ═════════ 主体三栏 ═════════ */}\n <main className=\"relative z-10 grid grid-cols-12 gap-2 p-3 flex-1 min-h-0\">\n {/* ═══ 左列 ═══ */}\n <div className=\"col-span-2 flex flex-col gap-3 overflow-hidden\">\n {/* 1. 销售订单 */}\n <GlassPanel title=\"销售订单\" icon={ShoppingCart} accent={C.cyan} className=\"flex-1\">\n <div className=\"grid grid-cols-2 gap-2 mb-3\">\n <div\n className=\"rounded-lg p-2\"\n style={{ background: C.cyan + '11', border: `1px solid ${C.cyan}22` }}\n >\n <p className=\"text-[10px]\" style={{ color: C.silver + '88' }}>\n 今日新增\n </p>\n <p className=\"text-xl font-bold\" style={{ color: C.cyan }}>\n {data.overview.todayOrders}\n </p>\n <p\n className=\"text-[10px]\"\n style={{ color: data.overview.orderChange >= 0 ? C.green : C.red }}\n >\n {fmtPct(data.overview.orderChange)}\n </p>\n </div>\n <div\n className=\"rounded-lg p-2\"\n style={{ background: C.orange + '11', border: `1px solid ${C.orange}22` }}\n >\n <p className=\"text-[10px]\" style={{ color: C.silver + '88' }}>\n 待交付\n </p>\n <p className=\"text-xl font-bold\" style={{ color: C.orange }}>\n {data.overview.todayDelivery}\n </p>\n <p\n className=\"text-[10px]\"\n style={{ color: data.overview.deliveryChange >= 0 ? C.green : C.red }}\n >\n {fmtPct(data.overview.deliveryChange)}\n </p>\n </div>\n </div>\n <p className=\"text-xs mb-1.5\" style={{ color: C.silver + '66' }}>\n 近 7 日趋势\n </p>\n <Sparkline\n data={\n data.orderTrend.length > 0\n ? data.orderTrend.map((d) => d.count)\n : [12, 18, 15, 22, 19, 25, data.overview.todayOrders]\n }\n color={C.cyan}\n height={70}\n />\n </GlassPanel>\n\n {/* 2. 生产工单 */}\n <GlassPanel\n title=\"生产工单\"\n icon={Factory}\n accent={C.green}\n className=\"flex-1 flex flex-col\"\n >\n <div className=\"grid grid-cols-3 gap-2 mb-3 shrink-0\">\n <div className=\"text-center rounded-lg p-2\" style={{ background: C.green + '11' }}>\n <p className=\"text-[10px]\" style={{ color: C.green + '88' }}>\n 在产\n </p>\n <p className=\"text-lg font-bold\" style={{ color: C.green }}>\n {data.production.activeOrders}\n </p>\n </div>\n <div className=\"text-center rounded-lg p-2\" style={{ background: C.cyan + '11' }}>\n <p className=\"text-[10px]\" style={{ color: C.cyan + '88' }}>\n 完成\n </p>\n <p className=\"text-lg font-bold\" style={{ color: C.cyan }}>\n {data.production.completedToday}\n </p>\n </div>\n <div className=\"text-center rounded-lg p-2\" style={{ background: C.amber + '11' }}>\n <p className=\"text-[10px]\" style={{ color: C.amber + '88' }}>\n 预警\n </p>\n <p className=\"text-lg font-bold\" style={{ color: C.amber }}>\n {data.production.warningCount}\n </p>\n </div>\n </div>\n <AutoScrollList>\n {wo.length > 0 ? (\n wo.map((w, i) => (\n <div\n key={i}\n className=\"flex items-center gap-2 px-2 py-1.5 rounded\"\n style={{ background: 'rgba(255,255,255,0.03)' }}\n >\n <div\n className=\"w-1.5 h-1.5 rounded-full shrink-0\"\n style={{\n background:\n w.status === 'producing'\n ? C.green\n : w.status === 'pending'\n ? C.amber\n : C.cyan,\n boxShadow: w.status === 'producing' ? `0 0 6px ${C.green}` : 'none',\n animation: w.status === 'producing' ? 'pulse 2s infinite' : 'none',\n }}\n />\n <span className=\"text-[11px] font-mono truncate\" style={{ color: C.cyan }}>\n {w.work_order_no}\n </span>\n <span\n className=\"text-[10px] truncate flex-1\"\n style={{ color: C.silver + '88' }}\n >\n {w.product_name}\n </span>\n {w.priority === 'urgent' && (\n <span\n className=\"px-1 rounded text-[9px]\"\n style={{ background: C.red + '22', color: C.red }}\n >\n 急\n </span>\n )}\n </div>\n ))\n ) : (\n <div className=\"text-center py-4 text-xs\" style={{ color: C.silver + '44' }}>\n {tc('noData')}\n </div>\n )}\n </AutoScrollList>\n </GlassPanel>\n\n {/* 3. 质量合格率 */}\n <GlassPanel title=\"质量合格率\" icon={CheckCircle2} accent={C.cyan} className=\"flex-1\">\n <div className=\"flex items-center justify-around\">\n <RingProgress\n value={data.quality.passRate || 96}\n label=\"合格率\"\n color={C.cyan}\n size={68}\n />\n <div className=\"space-y-1.5 flex-1 ml-4\">\n <div className=\"flex justify-between text-xs\">\n <span style={{ color: C.silver + '88' }}>检验总数</span>\n <span className=\"font-mono\" style={{ color: C.silver }}>\n {data.quality.totalInspections || 0}\n </span>\n </div>\n <div className=\"flex justify-between text-xs\">\n <span style={{ color: C.green + '88' }}>合格</span>\n <span className=\"font-mono\" style={{ color: C.green }}>\n {data.quality.passedInspections || 0}\n </span>\n </div>\n <div className=\"flex justify-between text-xs\">\n <span style={{ color: C.red + '88' }}>不合格</span>\n <span className=\"font-mono\" style={{ color: C.red }}>\n {data.quality.failedInspections || 0}\n </span>\n </div>\n </div>\n </div>\n </GlassPanel>\n </div>\n\n {/* ═══ 中央 3D ═══ */}\n <div className=\"col-span-8 flex flex-col gap-3\">\n {/* 3D 全息投影 */}\n <div\n className=\"flex-1 relative overflow-hidden rounded-xl\"\n style={{\n border: `1px solid ${C.silver}22`,\n background:\n 'radial-gradient(ellipse at center, rgba(10,30,61,0.4) 0%, transparent 70%)',\n backdropFilter: 'blur(8px)',\n }}\n >\n {/* 角标装饰 */}\n <div className=\"absolute top-2 left-2 z-10 flex items-center gap-1.5\">\n <div\n className=\"w-1.5 h-1.5 rounded-full animate-pulse\"\n style={{ background: C.cyan, boxShadow: `0 0 8px ${C.cyan}` }}\n />\n <span className=\"text-xs\" style={{ color: C.cyan }}>\n 全息投影 · 丝网印刷机\n </span>\n </div>\n <div\n className=\"absolute top-2 right-2 z-10 flex items-center gap-2 text-xs\"\n style={{ color: C.silver + '88' }}\n >\n <span>SP-2026</span>\n <span style={{ color: C.green }}>● 在线</span>\n </div>\n\n {/* 3D 全息丝网印刷机 */}\n <div className=\"absolute inset-0 flex items-center justify-center overflow-hidden\">\n <div\n className=\"absolute inset-0\"\n style={{\n background: `linear-gradient(135deg, ${C.cyan}15 0%, transparent 30%, transparent 70%, ${C.blue}15 100%)`,\n mixBlendMode: 'overlay',\n }}\n />\n <img\n src=\"/bj.jpg\"\n alt=\"3D 全息丝网印刷机\"\n className=\"w-full h-full object-cover animate-hologram\"\n />\n {/* 扫描线 */}\n <div\n className=\"absolute inset-0 pointer-events-none\"\n style={{\n background: `repeating-linear-gradient(0deg, transparent, transparent 2px, rgba(34, 211, 238, 0.03) 2px, rgba(34, 211, 238, 0.03) 4px)`,\n }}\n />\n </div>\n\n {/* 底部数据光晕条 */}\n <div\n className=\"absolute bottom-0 left-0 right-0 p-3 flex justify-around\"\n style={{\n background: 'linear-gradient(0deg, rgba(10,30,61,0.9) 0%, transparent 100%)',\n }}\n >\n {[\n {\n label: '生产效率',\n val: data.production.efficiency || 78,\n unit: '%',\n color: C.green,\n },\n {\n label: '设备运转',\n val: data.inventory.warehouseUtilization || 85,\n unit: '%',\n color: C.cyan,\n },\n { label: '产能负载', val: 67, unit: '%', color: C.orange },\n ].map((m, i) => (\n <div key={i} className=\"text-center\">\n <p className=\"text-[10px]\" style={{ color: C.silver + '88' }}>\n {m.label}\n </p>\n <p\n className=\"text-2xl font-extrabold\"\n style={{ color: m.color, textShadow: `0 0 12px ${m.color}66` }}\n >\n {m.val}\n <span className=\"text-sm ml-0.5\">{m.unit}</span>\n </p>\n </div>\n ))}\n </div>\n\n {/* 右侧悬浮效率仪表 */}\n <div className=\"absolute bottom-16 right-3 z-10 flex flex-col gap-2\">\n <GlassPanel title=\"综合效率\" icon={Activity} accent={C.green} className=\"!p-2\">\n <RingProgress\n value={data.production.efficiency || 78}\n label=\"\"\n color={C.green}\n size={60}\n />\n </GlassPanel>\n <GlassPanel title=\"质量合格率\" icon={CheckCircle2} accent={C.cyan} className=\"!p-2\">\n <RingProgress\n value={data.quality.passRate || 96}\n label=\"\"\n color={C.cyan}\n size={60}\n />\n </GlassPanel>\n </div>\n\n {/* 四角科技装饰 */}\n {[\n { top: 0, left: 0, border: 'border-l border-t' },\n { top: 0, right: 0, border: 'border-r border-t' },\n { bottom: 0, left: 0, border: 'border-l border-b' },\n { bottom: 0, right: 0, border: 'border-r border-b' },\n ].map((c, i) => (\n <div\n key={i}\n className={`absolute w-4 h-4 ${c.border}`}\n style={{ borderColor: C.cyan + '44', ...c }}\n />\n ))}\n </div>\n </div>\n\n {/* ═══ 右列 ═══ */}\n <div className=\"col-span-2 flex flex-col gap-2 overflow-hidden\">\n {/* 4. 原材料库存 */}\n <GlassPanel title=\"原材料库存\" icon={Package} accent={C.blue} className=\"flex-1\">\n <div className=\"grid grid-cols-2 gap-1.5 mb-2\">\n <div className=\"rounded-md p-1.5\" style={{ background: C.blue + '11' }}>\n <p className=\"text-[9px]\" style={{ color: C.silver + '88' }}>\n 物料种类\n </p>\n <p className=\"text-base font-bold\" style={{ color: C.blue }}>\n {fmtQty(data.inventory.totalItems)}\n </p>\n </div>\n <div className=\"rounded-md p-1.5\" style={{ background: C.orange + '11' }}>\n <p className=\"text-[9px]\" style={{ color: C.orange + '88' }}>\n 低库存\n </p>\n <p className=\"text-base font-bold\" style={{ color: C.orange }}>\n {data.inventory.lowStock}\n </p>\n </div>\n </div>\n <div className=\"flex justify-between items-center mb-1.5\">\n <span className=\"text-[10px]\" style={{ color: C.silver + '88' }}>\n 仓库利用率\n </span>\n <span className=\"text-xs font-bold\" style={{ color: C.cyan }}>\n {data.inventory.warehouseUtilization}%\n </span>\n </div>\n <div\n className=\"w-full h-1.5 rounded-full overflow-hidden\"\n style={{ background: 'rgba(255,255,255,0.06)' }}\n >\n <div\n className=\"h-full rounded-full transition-all duration-1000\"\n style={{\n width: `${data.inventory.warehouseUtilization}%`,\n background: `linear-gradient(90deg, ${C.blue}, ${C.cyan})`,\n boxShadow: `0 0 8px ${C.cyan}66`,\n }}\n />\n </div>\n <div className=\"mt-2 space-y-0.5\">\n {defects.length > 0 ? (\n defects.slice(0, 2).map((d, i) => (\n <div\n key={i}\n className=\"flex items-center justify-between px-1.5 py-0.5 rounded\"\n style={{ background: 'rgba(255,255,255,0.03)' }}\n >\n <span className=\"text-[9px] truncate\" style={{ color: C.silver + '88' }}>\n {d.product_name || d.defect_type || '—'}\n </span>\n <span className=\"text-[9px] font-mono\" style={{ color: C.amber }}>\n {d.quantity || 0}\n </span>\n </div>\n ))\n ) : (\n <div className=\"text-center py-1 text-[10px]\" style={{ color: C.silver + '44' }}>\n 库存正常\n </div>\n )}\n </div>\n </GlassPanel>\n\n {/* 5. 设备状态 */}\n <GlassPanel title=\"设备状态\" icon={Cpu} accent={C.orange} className=\"flex-1\">\n <div className=\"grid grid-cols-3 gap-1.5 mb-1.5 text-center\">\n <div className=\"rounded p-1\" style={{ background: C.green + '11' }}>\n <p className=\"text-[8px]\" style={{ color: C.green + '88' }}>\n 运行\n </p>\n <p className=\"text-sm font-bold\" style={{ color: C.green }}>\n {eqList.filter((e) => e.status === 'running').length}\n </p>\n </div>\n <div className=\"rounded p-1\" style={{ background: C.amber + '11' }}>\n <p className=\"text-[8px]\" style={{ color: C.amber + '88' }}>\n 保养\n </p>\n <p className=\"text-sm font-bold\" style={{ color: C.amber }}>\n {eqList.filter((e) => e.status === 'maintenance').length}\n </p>\n </div>\n <div className=\"rounded p-1\" style={{ background: C.red + '11' }}>\n <p className=\"text-[8px]\" style={{ color: C.red + '88' }}>\n 故障\n </p>\n <p className=\"text-sm font-bold\" style={{ color: C.red }}>\n {eqList.filter((e) => e.status === 'fault').length}\n </p>\n </div>\n </div>\n <div className=\"space-y-0.5 overflow-y-auto\" style={{ maxHeight: '100px' }}>\n {eqList.length > 0 ? (\n eqList.slice(0, 4).map((eq, i) => (\n <div\n key={i}\n className=\"flex items-center gap-1.5 px-1.5 py-0.5 rounded\"\n style={{ background: 'rgba(255,255,255,0.03)' }}\n >\n <div\n className=\"w-1 h-1 rounded-full shrink-0\"\n style={{\n background:\n eq.status === 'running'\n ? C.green\n : eq.status === 'idle'\n ? C.blue\n : eq.status === 'maintenance'\n ? C.amber\n : C.red,\n animation: eq.status === 'running' ? 'pulse 2s infinite' : 'none',\n }}\n />\n <span\n className=\"text-[9px] truncate flex-1\"\n style={{ color: C.silver + 'aa' }}\n >\n {eq.name}\n </span>\n <span\n className=\"text-[9px] font-mono\"\n style={{\n color:\n eq.efficiency > 80 ? C.green : eq.efficiency > 60 ? C.amber : C.red,\n }}\n >\n {eq.efficiency}%\n </span>\n </div>\n ))\n ) : (\n <div className=\"text-center py-1 text-[10px]\" style={{ color: C.silver + '44' }}>\n {tc('noData')}\n </div>\n )}\n </div>\n </GlassPanel>\n\n {/* 6. 财务日报 */}\n <GlassPanel title=\"财务日报\" icon={DollarSign} accent={C.green} className=\"flex-1\">\n <div className=\"space-y-1.5\">\n <div className=\"flex justify-between items-center\">\n <span className=\"text-[10px]\" style={{ color: C.silver + '88' }}>\n 应收总额\n </span>\n <span className=\"text-xs font-mono font-bold\" style={{ color: C.green }}>\n {fmtMoney(data.finance.totalReceivable)}\n </span>\n </div>\n <div className=\"flex justify-between items-center\">\n <span className=\"text-[10px]\" style={{ color: C.silver + '88' }}>\n 应付总额\n </span>\n <span className=\"text-xs font-mono font-bold\" style={{ color: C.red + 'cc' }}>\n {fmtMoney(data.finance.totalPayable)}\n </span>\n </div>\n <div className=\"h-px\" style={{ background: C.silver + '11' }} />\n <div className=\"flex justify-between items-center\">\n <span className=\"text-[10px]\" style={{ color: C.silver + '88' }}>\n 月度收入\n </span>\n <span className=\"text-xs font-mono font-bold\" style={{ color: C.cyan }}>\n {fmtMoney(data.finance.monthRevenue)}\n </span>\n </div>\n <div className=\"flex justify-between items-center\">\n <span className=\"text-[10px]\" style={{ color: C.silver + '88' }}>\n 月度支出\n </span>\n <span className=\"text-xs font-mono font-bold\" style={{ color: C.amber }}>\n {fmtMoney(data.finance.monthExpense)}\n </span>\n </div>\n <div className=\"h-px\" style={{ background: C.silver + '11' }} />\n <div className=\"flex justify-between items-center\">\n <span className=\"text-[10px] font-medium\" style={{ color: C.silver + 'aa' }}>\n 净利润\n </span>\n <span className=\"text-sm font-mono font-extrabold\" style={{ color: C.orange }}>\n {fmtMoney(data.finance.monthRevenue - data.finance.monthExpense)}\n </span>\n </div>\n </div>\n </GlassPanel>\n </div>\n </main>\n\n {/* ═════════ 底部预警滚动条 ═════════ */}\n <footer className=\"relative z-10 px-3 pb-2\">\n <AlertTicker alerts={[]} />\n </footer>\n </div>\n </MainLayout>\n );\n}\n","usedDeprecatedRules":[]},{"filePath":"D:\\dcprint\\erp-project\\src\\app\\[locale]\\dashboard\\error.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\dcprint\\erp-project\\src\\app\\[locale]\\dashboard\\finance\\page.tsx","messages":[{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":268,"column":15,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":268,"endColumn":40},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":269,"column":20,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":269,"endColumn":27},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":269,"column":38,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":269,"endColumn":42},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction<FinanceData>`.","line":269,"column":52,"nodeType":"MemberExpression","messageId":"unsafeArgument","endLine":269,"endColumn":63},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":269,"column":59,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":269,"endColumn":63},{"ruleId":"react-hooks/set-state-in-effect","severity":1,"message":"Error: Calling setState synchronously within an effect can trigger cascading renders\n\nEffects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following:\n* Update external systems with the latest state from React.\n* Subscribe for updates from some external system, calling setState in a callback function when external state changes.\n\nCalling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect).\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\dashboard\\finance\\page.tsx:276:5\n 274 | };\n 275 | fetchData();\n> 276 | setCurrentTime(new Date());\n | ^^^^^^^^^^^^^^ Avoid calling setState() directly within an effect\n 277 | const timer1 = setInterval(fetchData, 60000);\n 278 | const timer2 = setInterval(() => setCurrentTime(new Date()), 1000);\n 279 | return () => {","line":276,"column":5,"nodeType":null,"endLine":276,"endColumn":19}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":6,"fixableErrorCount":0,"fixableWarningCount":0,"source":"'use client';\n\nimport { authFetch } from '@/lib/auth-fetch';\nimport { useEffect, useState, useRef } from 'react';\nimport { MainLayout } from '@/components/layout';\nimport { useCompanyName } from '@/hooks/useCompanyName';\nimport {\n DollarSign,\n TrendingUp,\n TrendingDown,\n AlertTriangle,\n Clock,\n Maximize,\n Minimize,\n Wallet,\n ArrowUpRight,\n ArrowDownRight,\n Activity,\n Target,\n} from 'lucide-react';\nimport { useTranslations, useLocale } from 'next-intl';\n\ninterface FinanceData {\n overview: {\n totalReceivable: number;\n totalPayable: number;\n monthRevenue: number;\n monthExpense: number;\n revenueChange: number;\n expenseChange: number;\n netProfit: number;\n };\n revenueTrend: { date: string; amount: number }[];\n expenseTrend: { date: string; amount: number }[];\n receivableAging: { aging: string; count: number; total: number }[];\n recentTransactions: { type: string; id: number; amount: number; date: string; remark: string }[];\n topPayables: { supplier_name: string; total: number; count: number }[];\n}\n\nfunction DonutChart({\n percentage,\n color,\n size = 120,\n}: {\n percentage: number;\n color: string;\n size?: number;\n}) {\n const t = useTranslations('Dashboard');\n const radius = 45;\n const circumference = 2 * Math.PI * radius;\n const offset = circumference - (percentage / 100) * circumference;\n\n return (\n <svg width={size} height={size} viewBox=\"0 0 120 120\">\n <circle\n cx=\"60\"\n cy=\"60\"\n r={radius}\n fill=\"none\"\n stroke=\"rgba(255,255,255,0.1)\"\n strokeWidth=\"12\"\n />\n <circle\n cx=\"60\"\n cy=\"60\"\n r={radius}\n fill=\"none\"\n stroke={color}\n strokeWidth=\"12\"\n strokeDasharray={circumference}\n strokeDashoffset={offset}\n strokeLinecap=\"round\"\n transform=\"rotate(-90 60 60)\"\n className=\"transition-all duration-1000 ease-out\"\n />\n <text x=\"60\" y=\"55\" textAnchor=\"middle\" fill=\"white\" fontSize=\"20\" fontWeight=\"bold\">\n {percentage.toFixed(1)}%\n </text>\n <text x=\"60\" y=\"75\" textAnchor=\"middle\" fill=\"rgba(255,255,255,0.6)\" fontSize=\"10\">\n {t('profitMargin')}\n </text>\n </svg>\n );\n}\n\nfunction DualLineChart({\n revenueData,\n expenseData,\n width = 400,\n height = 150,\n}: {\n revenueData: { date: string; amount: number }[];\n expenseData: { date: string; amount: number }[];\n width?: number;\n height?: number;\n}) {\n const tc = useTranslations('Common');\n const allData = [...revenueData, ...expenseData];\n if (allData.length === 0) return null;\n\n const padding = { top: 20, right: 20, bottom: 30, left: 40 };\n const chartWidth = width - padding.left - padding.right;\n const chartHeight = height - padding.top - padding.bottom;\n\n const maxVal = Math.max(\n ...revenueData.map((d) => d.amount),\n ...expenseData.map((d) => d.amount),\n 1\n );\n\n const getX = (i: number, total: number) => padding.left + (i / (total - 1 || 1)) * chartWidth;\n const getY = (val: number) => padding.top + chartHeight - (val / maxVal) * chartHeight;\n\n const revenuePath = revenueData\n .map((d, i) => `${i === 0 ? 'M' : 'L'} ${getX(i, revenueData.length)} ${getY(d.amount)}`)\n .join(' ');\n const expensePath = expenseData\n .map((d, i) => `${i === 0 ? 'M' : 'L'} ${getX(i, expenseData.length)} ${getY(d.amount)}`)\n .join(' ');\n\n return (\n <svg width={width} height={height} className=\"w-full\">\n {[0, 0.25, 0.5, 0.75, 1].map((ratio, i) => (\n <g key={i}>\n <line\n x1={padding.left}\n y1={padding.top + chartHeight * ratio}\n x2={width - padding.right}\n y2={padding.top + chartHeight * ratio}\n stroke=\"rgba(255,255,255,0.1)\"\n strokeDasharray=\"4 4\"\n />\n <text\n x={padding.left - 5}\n y={padding.top + chartHeight * ratio + 4}\n textAnchor=\"end\"\n fill=\"rgba(255,255,255,0.5)\"\n fontSize=\"10\"\n >\n {Math.round(maxVal * (1 - ratio))}\n </text>\n </g>\n ))}\n\n {revenueData\n .filter(\n (_, i) => i % Math.ceil(revenueData.length / 6) === 0 || i === revenueData.length - 1\n )\n .map((d, i) => {\n const idx = revenueData.indexOf(d);\n return (\n <text\n key={i}\n x={getX(idx, revenueData.length)}\n y={height - 5}\n textAnchor=\"middle\"\n fill=\"rgba(255,255,255,0.5)\"\n fontSize=\"10\"\n >\n {d.date.substring(5)}\n </text>\n );\n })}\n\n <path d={revenuePath} fill=\"none\" stroke=\"#22c55e\" strokeWidth=\"2\" strokeLinecap=\"round\" />\n <path d={expensePath} fill=\"none\" stroke=\"#ef4444\" strokeWidth=\"2\" strokeLinecap=\"round\" />\n\n {revenueData.map((d, i) => (\n <circle\n key={`r${i}`}\n cx={getX(i, revenueData.length)}\n cy={getY(d.amount)}\n r=\"3\"\n fill=\"#22c55e\"\n />\n ))}\n {expenseData.map((d, i) => (\n <circle\n key={`e${i}`}\n cx={getX(i, expenseData.length)}\n cy={getY(d.amount)}\n r=\"3\"\n fill=\"#ef4444\"\n />\n ))}\n\n <g transform={`translate(${width - 150}, 10)`}>\n <line x1=\"0\" y1=\"0\" x2=\"20\" y2=\"0\" stroke=\"#22c55e\" strokeWidth=\"2\" />\n <text x=\"25\" y=\"4\" fill=\"rgba(255,255,255,0.7)\" fontSize=\"10\">\n {tc('income')}\n </text>\n <line x1=\"0\" y1=\"15\" x2=\"20\" y2=\"15\" stroke=\"#ef4444\" strokeWidth=\"2\" />\n <text x=\"25\" y=\"19\" fill=\"rgba(255,255,255,0.7)\" fontSize=\"10\">\n {tc('expense')}\n </text>\n </g>\n </svg>\n );\n}\n\nfunction HorizontalBarChart({\n data,\n}: {\n data: { supplier_name: string; count: number; total: number }[];\n}) {\n const tc = useTranslations('Common');\n const locale = useLocale();\n if (data.length === 0) return null;\n const maxTotal = Math.max(...data.map((d) => d.total), 1);\n\n return (\n <div className=\"space-y-3\">\n {data.map((d, i) => (\n <div key={i} className=\"flex items-center gap-3\">\n <span className=\"text-xs w-24 text-right text-cyan-300 truncate\">\n {d.supplier_name || tc('unknown')}\n </span>\n <div className=\"flex-1 bg-white/10 rounded-full h-5 relative overflow-hidden\">\n <div\n className=\"h-full rounded-full transition-all duration-500\"\n style={{\n width: `${(d.total / maxTotal) * 100}%`,\n background: `linear-gradient(90deg, #f97316, #ef4444)`,\n }}\n />\n <span className=\"absolute inset-0 flex items-center justify-center text-xs font-medium text-white\">\n ¥{(d.total / 100).toLocaleString(locale, { minimumFractionDigits: 2 })}\n </span>\n </div>\n </div>\n ))}\n </div>\n );\n}\n\nexport default function FinanceDashboard() {\n const t = useTranslations('Dashboard');\n const tc = useTranslations('Common');\n const locale = useLocale();\n\n const { companyName } = useCompanyName();\n const [data, setData] = useState<FinanceData>({\n overview: {\n totalReceivable: 0,\n totalPayable: 0,\n monthRevenue: 0,\n monthExpense: 0,\n revenueChange: 0,\n expenseChange: 0,\n netProfit: 0,\n },\n revenueTrend: [],\n expenseTrend: [],\n receivableAging: [],\n recentTransactions: [],\n topPayables: [],\n });\n const [loading, setLoading] = useState(true);\n const [currentTime, setCurrentTime] = useState<Date | null>(null);\n const [isFullscreen, setIsFullscreen] = useState(false);\n const dashboardRef = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n const fetchData = async () => {\n try {\n const res = await authFetch('/api/dashboard/finance');\n const result = await res.json();\n if (result.success && result.data) setData(result.data);\n } catch {\n } finally {\n setLoading(false);\n }\n };\n fetchData();\n setCurrentTime(new Date());\n const timer1 = setInterval(fetchData, 60000);\n const timer2 = setInterval(() => setCurrentTime(new Date()), 1000);\n return () => {\n clearInterval(timer1);\n clearInterval(timer2);\n };\n }, []);\n\n const toggleFullscreen = () => {\n if (!document.fullscreenElement) {\n dashboardRef.current?.requestFullscreen();\n setIsFullscreen(true);\n } else {\n document.exitFullscreen();\n setIsFullscreen(false);\n }\n };\n\n useEffect(() => {\n const handleFullscreenChange = () => setIsFullscreen(!!document.fullscreenElement);\n document.addEventListener('fullscreenchange', handleFullscreenChange);\n return () => document.removeEventListener('fullscreenchange', handleFullscreenChange);\n }, []);\n\n const formatTime = (date: Date) =>\n date.toLocaleString(locale, {\n year: 'numeric',\n month: '2-digit',\n day: '2-digit',\n hour: '2-digit',\n minute: '2-digit',\n second: '2-digit',\n hour12: false,\n });\n\n const formatMoney = (v: number) =>\n '¥' + (v / 100).toLocaleString(locale, { minimumFractionDigits: 2, maximumFractionDigits: 2 });\n\n const profitRate =\n data.overview.monthRevenue > 0\n ? (data.overview.netProfit / data.overview.monthRevenue) * 100\n : 0;\n\n return (\n <MainLayout>\n <div\n ref={dashboardRef}\n className=\"min-h-screen text-white p-4 relative overflow-hidden\"\n style={{ background: 'linear-gradient(135deg, #091637 0%, #010205 100%)' }}\n >\n <div className=\"absolute inset-0 overflow-hidden pointer-events-none\">\n <div className=\"absolute top-0 left-0 w-96 h-96 bg-cyan-500/5 rounded-full blur-3xl animate-blob\" />\n <div className=\"absolute bottom-0 right-0 w-96 h-96 bg-blue-500/5 rounded-full blur-3xl animate-blob animation-delay-2000\" />\n <div className=\"absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-[600px] h-[600px] bg-indigo-500/5 rounded-full blur-3xl animate-blob animation-delay-4000\" />\n </div>\n <div className=\"absolute inset-0 tech-grid-bg pointer-events-none\" />\n\n <div className=\"relative z-10 flex items-center justify-between mb-6\">\n <div className=\"flex-1\" />\n <div className=\"tech-title-wrapper\">\n <div className=\"tech-title-row\">\n <div className=\"tech-title-line-left\" />\n <div className=\"text-center\">\n <h1 className=\"text-2xl font-bold tracking-wider bg-gradient-to-r from-cyan-300 via-blue-400 to-cyan-300 bg-clip-text text-transparent\">\n {companyName}\n </h1>\n <p className=\"text-xs text-white/50 mt-0.5\">{t('financeMonitorSubtitle')}</p>\n </div>\n <div className=\"tech-title-line-right\" />\n </div>\n <div className=\"tech-title-bottom-line\" />\n </div>\n\n <div className=\"flex-1 flex justify-end items-center gap-4\">\n <div className=\"text-right\">\n <div className=\"text-lg font-mono font-bold text-cyan-400\">\n {currentTime && formatTime(currentTime)}\n </div>\n </div>\n <button\n onClick={toggleFullscreen}\n className=\"p-2 rounded-lg bg-white/10 hover:bg-white/20 transition-colors\"\n title={isFullscreen ? t('exitFullscreen') : t('fullscreen')}\n >\n {isFullscreen ? (\n <Minimize className=\"h-4 w-4 text-cyan-400\" />\n ) : (\n <Maximize className=\"h-4 w-4 text-cyan-400\" />\n )}\n </button>\n <div className=\"px-3 py-1 rounded-full bg-cyan-500/20 border border-cyan-500/30 text-xs text-cyan-300\">\n {loading ? tc('loading') : '● ' + t('realtime')}\n </div>\n </div>\n </div>\n\n <div className=\"relative z-10 grid grid-cols-2 md:grid-cols-5 gap-4 mb-6\">\n {[\n {\n title: t('totalReceivable'),\n value: formatMoney(data.overview.totalReceivable),\n icon: ArrowUpRight,\n color: 'from-blue-500 to-cyan-500',\n },\n {\n title: t('totalPayable'),\n value: formatMoney(data.overview.totalPayable),\n icon: ArrowDownRight,\n color: 'from-orange-500 to-red-500',\n },\n {\n title: t('monthlyIncome'),\n value: formatMoney(data.overview.monthRevenue),\n icon: TrendingUp,\n color: 'from-green-500 to-emerald-500',\n },\n {\n title: t('monthlyExpense'),\n value: formatMoney(data.overview.monthExpense),\n icon: TrendingDown,\n color: 'from-red-500 to-pink-500',\n },\n {\n title: t('netProfit'),\n value: formatMoney(data.overview.netProfit),\n icon: Wallet,\n color:\n data.overview.netProfit >= 0\n ? 'from-emerald-500 to-green-500'\n : 'from-red-500 to-orange-500',\n },\n ].map((s, i) => (\n <div key={i} className={`tech-card tech-glow tech-card-delay-${i + 1} p-4`}>\n <div className=\"flex items-center justify-between\">\n <div>\n <p className=\"text-xs text-white/60\">{s.title}</p>\n <p className=\"text-xl font-bold mt-1 bg-gradient-to-r from-cyan-300 to-blue-300 bg-clip-text text-transparent\">\n {s.value}\n </p>\n </div>\n <div className={`p-3 rounded-lg bg-gradient-to-br ${s.color}`}>\n <s.icon className=\"h-5 w-5 text-white\" />\n </div>\n </div>\n </div>\n ))}\n </div>\n\n <div className=\"relative z-10 grid grid-cols-1 lg:grid-cols-3 gap-6 mb-6\">\n <div className=\"tech-card tech-glow p-0\">\n <div className=\"px-4 py-2 border-b border-white/10 flex items-center gap-2 bg-white/5\">\n <div className=\"w-1 h-4 rounded-full bg-gradient-to-b from-cyan-400 to-blue-600\" />\n <Target className=\"h-4 w-4 text-cyan-400\" />\n <span className=\"text-sm font-medium text-white/80\">{t('profitMargin')}</span>\n </div>\n <div className=\"p-4\">\n <div className=\"flex justify-center\">\n <DonutChart\n percentage={Math.max(profitRate, 0)}\n color={profitRate >= 0 ? '#22c55e' : '#ef4444'}\n size={160}\n />\n </div>\n <div className=\"mt-4 grid grid-cols-2 gap-4 text-center\">\n <div>\n <p className=\"text-xs text-white/50\">{t('monthlyIncome')}</p>\n <p className=\"text-lg font-bold text-green-400\">\n {formatMoney(data.overview.monthRevenue)}\n </p>\n </div>\n <div>\n <p className=\"text-xs text-white/50\">{t('monthlyExpense')}</p>\n <p className=\"text-lg font-bold text-red-400\">\n {formatMoney(data.overview.monthExpense)}\n </p>\n </div>\n </div>\n </div>\n </div>\n\n <div className=\"tech-card tech-glow p-0 lg:col-span-2\">\n <div className=\"px-4 py-2 border-b border-white/10 flex items-center gap-2 bg-white/5\">\n <div className=\"w-1 h-4 rounded-full bg-gradient-to-b from-cyan-400 to-blue-600\" />\n <Activity className=\"h-4 w-4 text-cyan-400\" />\n <span className=\"text-sm font-medium text-white/80\">{t('incomeExpenseTrend')}</span>\n </div>\n <div className=\"p-4\">\n {data.revenueTrend.length === 0 && data.expenseTrend.length === 0 ? (\n <p className=\"text-white/40 text-center py-8\">{tc('noData')}</p>\n ) : (\n <DualLineChart\n revenueData={data.revenueTrend}\n expenseData={data.expenseTrend}\n width={600}\n height={200}\n />\n )}\n </div>\n </div>\n </div>\n\n <div className=\"relative z-10 grid grid-cols-1 lg:grid-cols-2 gap-6 mb-6\">\n <div className=\"tech-card tech-glow p-0\">\n <div className=\"px-4 py-2 border-b border-white/10 flex items-center gap-2 bg-white/5\">\n <div className=\"w-1 h-4 rounded-full bg-gradient-to-b from-orange-400 to-red-500\" />\n <AlertTriangle className=\"h-4 w-4 text-orange-400\" />\n <span className=\"text-sm font-medium text-white/80\">{t('agingAnalysis')}</span>\n </div>\n <div className=\"p-4\">\n {data.receivableAging.length === 0 ? (\n <p className=\"text-white/40 text-center py-8\">{tc('noData')}</p>\n ) : (\n <div className=\"space-y-4\">\n {data.receivableAging.map((a, i) => {\n const total = data.receivableAging.reduce((s, x) => s + x.total, 0);\n const pct = total > 0 ? Math.round((a.total / total) * 100) : 0;\n const isOverdue = a.aging === '90天以上';\n return (\n <div key={i}>\n <div className=\"flex justify-between text-sm mb-1\">\n <span\n className={`px-2 py-0.5 rounded text-xs ${isOverdue ? 'bg-red-500/20 text-red-400 border border-red-500/30' : 'bg-white/10 text-white/80 border border-white/10'}`}\n >\n {a.aging}\n </span>\n <span className=\"text-white/50\">\n {a.count}\n {tc('agingRecordsUnit')}\n {formatMoney(a.total)} ({pct}%)\n </span>\n </div>\n <div className=\"bg-white/10 rounded-full h-3 relative overflow-hidden\">\n <div\n className=\"h-full rounded-full transition-all\"\n style={{\n width: `${pct}%`,\n background: isOverdue\n ? 'linear-gradient(90deg, #ef4444, #f97316)'\n : 'linear-gradient(90deg, #22c55e, #06b6d4)',\n }}\n />\n </div>\n </div>\n );\n })}\n </div>\n )}\n </div>\n </div>\n\n <div className=\"tech-card tech-glow p-0\">\n <div className=\"px-4 py-2 border-b border-white/10 flex items-center gap-2 bg-white/5\">\n <div className=\"w-1 h-4 rounded-full bg-gradient-to-b from-cyan-400 to-blue-600\" />\n <DollarSign className=\"h-4 w-4 text-cyan-400\" />\n <span className=\"text-sm font-medium text-white/80\">{t('topSuppliers')}</span>\n </div>\n <div className=\"p-4\">\n {data.topPayables.length === 0 ? (\n <p className=\"text-white/40 text-center py-8\">{tc('noData')}</p>\n ) : (\n <HorizontalBarChart data={data.topPayables.slice(0, 5)} />\n )}\n </div>\n </div>\n </div>\n\n <div className=\"relative z-10 tech-card tech-glow p-0\">\n <div className=\"px-4 py-2 border-b border-white/10 flex items-center gap-2 bg-white/5\">\n <div className=\"w-1 h-4 rounded-full bg-gradient-to-b from-cyan-400 to-blue-600\" />\n <Clock className=\"h-4 w-4 text-cyan-400\" />\n <span className=\"text-sm font-medium text-white/80\">{t('recentTransactions')}</span>\n </div>\n <div className=\"p-4\">\n {data.recentTransactions.length === 0 ? (\n <p className=\"text-white/40 text-center py-8\">{tc('noRecords')}</p>\n ) : (\n <div className=\"overflow-x-auto\">\n <table className=\"w-full text-sm\">\n <thead>\n <tr className=\"border-b border-white/10\">\n <th className=\"text-left py-2 px-3 text-white/60 font-medium\">\n {tc('inspectionType')}\n </th>\n <th className=\"text-left py-2 px-3 text-white/60 font-medium\">\n {tc('amount')}\n </th>\n <th className=\"text-left py-2 px-3 text-white/60 font-medium\">\n {tc('date')}\n </th>\n <th className=\"text-left py-2 px-3 text-white/60 font-medium\">\n {tc('remark')}\n </th>\n </tr>\n </thead>\n <tbody>\n {data.recentTransactions.slice(0, 10).map((t, i) => (\n <tr\n key={i}\n className=\"border-b border-white/5 hover:bg-white/5 transition-colors\"\n >\n <td className=\"py-2 px-3\">\n <span\n className={`px-2 py-0.5 rounded text-xs ${\n t.type === 'receipt'\n ? 'bg-green-500/20 text-green-400 border border-green-500/30'\n : 'bg-red-500/20 text-red-400 border border-red-500/30'\n }`}\n >\n {t.type === 'receipt' ? tc('income') : tc('expense')}\n </span>\n </td>\n <td\n className={`py-2 px-3 font-mono font-medium ${t.type === 'receipt' ? 'text-green-400' : 'text-red-400'}`}\n >\n {t.type === 'receipt' ? '+' : '-'}\n {formatMoney(t.amount)}\n </td>\n <td className=\"py-2 px-3 text-white/50\">{t.date?.substring(0, 10)}</td>\n <td className=\"py-2 px-3 text-white/60\">{t.remark || '-'}</td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n )}\n </div>\n </div>\n </div>\n </MainLayout>\n );\n}\n","usedDeprecatedRules":[]},{"filePath":"D:\\dcprint\\erp-project\\src\\app\\[locale]\\dashboard\\flow\\page.tsx","messages":[{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"用户表\"。建议使用: tc('text_hn7sp')","line":221,"column":36,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":221,"endColumn":41},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"部门表\"。建议使用: tc('text_lyc14')","line":222,"column":42,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":222,"endColumn":47},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"角色表\"。建议使用: tc('text_ktnrc')","line":223,"column":36,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":223,"endColumn":41},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"用户角色关联\"。建议使用: tc('text_tzwh1s')","line":226,"column":18,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":226,"endColumn":26},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"菜单表\"。建议使用: tc('text_jq7pr')","line":229,"column":36,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":229,"endColumn":41},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"角色菜单关联\"。建议使用: tc('text_sdy5uu')","line":230,"column":41,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":230,"endColumn":49},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"操作日志\"。建议使用: tc('text_d1tfy9')","line":231,"column":45,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":231,"endColumn":51},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"登录日志\"。建议使用: tc('text_fcfmmk')","line":232,"column":41,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":232,"endColumn":47},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"字典类型\"。建议使用: tc('text_bv9usx')","line":233,"column":41,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":233,"endColumn":47},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"字典数据\"。建议使用: tc('text_bv601r')","line":234,"column":41,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":234,"endColumn":47},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"系统配置\"。建议使用: tc('text_garzt1')","line":235,"column":38,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":235,"endColumn":44},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"客户表\"。建议使用: tc('text_dwmr7')","line":244,"column":40,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":244,"endColumn":45},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"客户联系人\"。建议使用: tc('text_g1y9ha')","line":247,"column":18,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":247,"endColumn":25},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"客户跟进\"。建议使用: tc('text_bz5bap')","line":252,"column":18,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":252,"endColumn":24},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"供应商表\"。建议使用: tc('text_afqgaj')","line":263,"column":40,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":263,"endColumn":46},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"供应商物料\"。建议使用: tc('text_vlvffn')","line":266,"column":18,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":266,"endColumn":25},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"物料分类\"。建议使用: tc('text_eus3np')","line":277,"column":49,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":277,"endColumn":55},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"物料表\"。建议使用: tc('text_h9b88')","line":280,"column":18,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":280,"endColumn":23},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"仓库表\"。建议使用: tc('text_c0h1k')","line":283,"column":41,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":283,"endColumn":46},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"库存表\"。建议使用: tc('text_eaz1f')","line":286,"column":18,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":286,"endColumn":23},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"库存日志\"。建议使用: tc('text_cbattj')","line":291,"column":18,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":291,"endColumn":24},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"采购申请\"。建议使用: tc('text_iz67sa')","line":302,"column":39,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":302,"endColumn":45},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"采购申请明细\"。建议使用: tc('text_i0i7tq')","line":305,"column":18,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":305,"endColumn":26},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"采购订单\"。建议使用: tc('text_iz9pyx')","line":308,"column":37,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":308,"endColumn":43},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"采购订单明细\"。建议使用: tc('text_fexslr')","line":311,"column":18,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":311,"endColumn":26},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"采购收货\"。建议使用: tc('text_iz3i47')","line":316,"column":18,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":316,"endColumn":24},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"收货明细\"。建议使用: tc('text_dcqind')","line":319,"column":46,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":319,"endColumn":52},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"销售订单\"。建议使用: tc('text_j5p8kh')","line":328,"column":37,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":328,"endColumn":43},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"销售订单明细\"。建议使用: tc('text_e71kux')","line":331,"column":18,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":331,"endColumn":26},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"销售发货\"。建议使用: tc('text_j5g278')","line":336,"column":18,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":336,"endColumn":24},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"发货明细\"。建议使用: tc('text_b5r626')","line":339,"column":47,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":339,"endColumn":53},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"标准工艺卡\"。建议使用: tc('text_8q6reb')","line":348,"column":45,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":348,"endColumn":52},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"生产工单\"。建议使用: tc('text_f3s1h4')","line":351,"column":18,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":351,"endColumn":24},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"BOM表\"。建议使用: tc('text_18ki0')","line":354,"column":35,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":354,"endColumn":41},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"BOM明细\"。建议使用: tc('text_128i6w')","line":357,"column":18,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":357,"endColumn":25},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"入库单\"。建议使用: tc('text_cdqh3')","line":368,"column":45,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":368,"endColumn":50},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"出库单\"。建议使用: tc('text_cgsyk')","line":369,"column":46,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":369,"endColumn":51},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"调拨单\"。建议使用: tc('text_kzk4w')","line":370,"column":46,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":370,"endColumn":51},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"物料标签\"。建议使用: tc('text_euvu7b')","line":371,"column":46,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":371,"endColumn":52},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"分切记录\"。建议使用: tc('text_ap4js6')","line":372,"column":46,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":372,"endColumn":52},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"扫码日志\"。建议使用: tc('text_cwy9e0')","line":373,"column":40,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":373,"endColumn":46},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"盘点记录\"。建议使用: tc('text_fgt4xy')","line":374,"column":43,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":374,"endColumn":49},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"检验记录\"。建议使用: tc('text_duxvn5')","line":383,"column":41,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":383,"endColumn":47},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"不合格记录\"。建议使用: tc('text_w3eqna')","line":384,"column":42,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":384,"endColumn":49},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"追溯记录\"。建议使用: tc('text_imokfr')","line":385,"column":44,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":385,"endColumn":50},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"应收款\"。建议使用: tc('text_ecifw')","line":396,"column":18,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":396,"endColumn":23},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"应付款\"。建议使用: tc('text_e8ph6')","line":401,"column":18,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":401,"endColumn":23},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"收款记录\"。建议使用: tc('text_d7xxt9')","line":404,"column":46,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":404,"endColumn":52},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"员工表\"。建议使用: tc('text_ctgmz')","line":413,"column":39,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":413,"endColumn":44},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"考勤记录\"。建议使用: tc('text_gi2g86')","line":414,"column":41,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":414,"endColumn":47},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"培训记录\"。建议使用: tc('text_bol01l')","line":415,"column":39,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":415,"endColumn":45},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"设备表\"。建议使用: tc('text_kwqyn')","line":424,"column":41,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":424,"endColumn":46},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"保养记录\"。建议使用: tc('text_af8k83')","line":425,"column":50,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":425,"endColumn":56},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"维修记录\"。建议使用: tc('text_gctspr')","line":426,"column":38,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":426,"endColumn":44},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"样式系统\"。建议使用: tc('text_djqe4c')","line":464,"column":49,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":464,"endColumn":55},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"接口规范\"。建议使用: tc('text_cxelul')","line":477,"column":46,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":477,"endColumn":52},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"认证中间件\"。建议使用: tc('text_4agksu')","line":478,"column":43,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":478,"endColumn":50},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"数据校验\"。建议使用: tc('text_d7o1zd')","line":480,"column":46,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":480,"endColumn":52},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"工单状态机\"。建议使用: tc('text_mylri7')","line":491,"column":49,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":491,"endColumn":56},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"库存分配策略\"。建议使用: tc('text_us4vgl')","line":492,"column":44,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":492,"endColumn":52},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"生产排程引擎\"。建议使用: tc('text_qggta2')","line":494,"column":46,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":494,"endColumn":54},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"操作日志服务\"。建议使用: tc('text_uxgwzv')","line":495,"column":42,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":495,"endColumn":50},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"数据库驱动\"。建议使用: tc('text_gmckgk')","line":506,"column":38,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":506,"endColumn":45},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"连接池管理\"。建议使用: tc('text_7aneny')","line":509,"column":40,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":509,"endColumn":47},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"业务表\"。建议使用: tc('text_bumpt')","line":521,"column":43,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":521,"endColumn":48},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"存储引擎\"。建议使用: tc('text_bv1a5l')","line":522,"column":38,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":522,"endColumn":44},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"字符集\"。建议使用: tc('text_dzenr')","line":523,"column":40,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":523,"endColumn":45}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":67,"fixableErrorCount":0,"fixableWarningCount":0,"source":"'use client';\r\n\r\nimport { useState } from 'react';\r\nimport { MainLayout } from '@/components/layout';\r\nimport { useTranslations } from 'next-intl';\r\nimport { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';\r\nimport { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';\r\nimport { Badge } from '@/components/ui/badge';\r\nimport {\r\n ArrowRight,\r\n Database,\r\n ChevronDown,\r\n ChevronRight,\r\n Link2,\r\n Layers,\r\n ShoppingCart,\r\n Scissors,\r\n Factory,\r\n Cog,\r\n QrCode,\r\n Send,\r\n ClipboardCheck,\r\n PackageCheck,\r\n ShieldCheck,\r\n Monitor,\r\n Server,\r\n HardDrive,\r\n Cpu,\r\n Globe,\r\n Lock,\r\n Shield,\r\n CheckSquare,\r\n Workflow,\r\n BarChart3,\r\n Wrench,\r\n Users,\r\n Truck,\r\n FileText,\r\n Tag,\r\n Warehouse,\r\n FlaskConical,\r\n CreditCard,\r\n UserCog,\r\n LayoutGrid,\r\n} from 'lucide-react';\r\n\r\nconst flowNodes = [\r\n {\r\n step: 1,\r\n nameKey: 'procurementInbound',\r\n icon: ShoppingCart,\r\n tables: ['pur_request', 'pur_order', 'pur_receipt'],\r\n fields: ['request_no', 'order_no', 'supplier_id', 'material_id'],\r\n color: 'from-blue-500 to-blue-600',\r\n bgColor: 'bg-blue-500/10',\r\n borderColor: 'border-blue-500/30',\r\n textColor: 'text-blue-400',\r\n },\r\n {\r\n step: 2,\r\n nameKey: 'cuttingManagement',\r\n icon: Scissors,\r\n tables: ['inv_cutting_record', 'inv_material_label'],\r\n fields: ['cutting_no', 'source_batch_no', 'material_id'],\r\n color: 'from-cyan-500 to-cyan-600',\r\n bgColor: 'bg-cyan-500/10',\r\n borderColor: 'border-cyan-500/30',\r\n textColor: 'text-cyan-400',\r\n },\r\n {\r\n step: 3,\r\n nameKey: 'workOrderProduction',\r\n icon: Factory,\r\n tables: ['prd_work_order', 'prd_process_card'],\r\n fields: ['work_order_no', 'sales_order_id', 'material_id', 'plan_qty'],\r\n color: 'from-amber-500 to-amber-600',\r\n bgColor: 'bg-amber-500/10',\r\n borderColor: 'border-amber-500/30',\r\n textColor: 'text-amber-400',\r\n },\r\n {\r\n step: 4,\r\n nameKey: 'processFlow',\r\n icon: Cog,\r\n tables: ['prd_process_card', 'prd_standard_card'],\r\n fields: ['card_no', 'process_flow', 'sequences'],\r\n color: 'from-purple-500 to-purple-600',\r\n bgColor: 'bg-purple-500/10',\r\n borderColor: 'border-purple-500/30',\r\n textColor: 'text-purple-400',\r\n },\r\n {\r\n step: 5,\r\n nameKey: 'scanBatching',\r\n icon: QrCode,\r\n tables: ['inv_scan_log', 'prd_material_issue'],\r\n fields: ['label_no', 'qr_content', 'material_id'],\r\n color: 'from-emerald-500 to-emerald-600',\r\n bgColor: 'bg-emerald-500/10',\r\n borderColor: 'border-emerald-500/30',\r\n textColor: 'text-emerald-400',\r\n },\r\n {\r\n step: 6,\r\n nameKey: 'scanIssuing',\r\n icon: Send,\r\n tables: ['prd_material_issue', 'inv_inventory_log'],\r\n fields: ['issue_no', 'material_id', 'quantity', 'warehouse_id'],\r\n color: 'from-teal-500 to-teal-600',\r\n bgColor: 'bg-teal-500/10',\r\n borderColor: 'border-teal-500/30',\r\n textColor: 'text-teal-400',\r\n },\r\n {\r\n step: 7,\r\n nameKey: 'productionReporting',\r\n icon: ClipboardCheck,\r\n tables: ['prd_work_report'],\r\n fields: ['report_no', 'work_order_no', 'completed_qty'],\r\n color: 'from-orange-500 to-orange-600',\r\n bgColor: 'bg-orange-500/10',\r\n borderColor: 'border-orange-500/30',\r\n textColor: 'text-orange-400',\r\n },\r\n {\r\n step: 8,\r\n nameKey: 'finishedGoodsInbound',\r\n icon: PackageCheck,\r\n tables: ['inv_production_inbound', 'inv_inventory'],\r\n fields: ['inbound_no', 'product_id', 'quantity', 'batch_no'],\r\n color: 'from-green-500 to-green-600',\r\n bgColor: 'bg-green-500/10',\r\n borderColor: 'border-green-500/30',\r\n textColor: 'text-green-400',\r\n },\r\n {\r\n step: 9,\r\n nameKey: 'qualityTraceability',\r\n icon: ShieldCheck,\r\n tables: ['inv_trace_record', 'qc_inspection'],\r\n fields: ['trace_no', 'batch_no', 'material_id'],\r\n color: 'from-rose-500 to-rose-600',\r\n bgColor: 'bg-rose-500/10',\r\n borderColor: 'border-rose-500/30',\r\n textColor: 'text-rose-400',\r\n },\r\n];\r\n\r\nconst fieldTracking = [\r\n {\r\n field: 'material_id',\r\n labelKey: 'materialLabel',\r\n color: 'text-blue-400',\r\n bgColor: 'bg-blue-500/10',\r\n borderColor: 'border-blue-500/30',\r\n path: [\r\n 'pur_request_detail.material_id',\r\n 'inv_cutting_record.material_id',\r\n 'prd_work_order.material_id',\r\n 'prd_material_issue.material_id',\r\n 'inv_inventory.material_id',\r\n 'inv_trace_record.material_id',\r\n ],\r\n },\r\n {\r\n field: 'order_id',\r\n labelKey: 'orderLabel',\r\n color: 'text-amber-400',\r\n bgColor: 'bg-amber-500/10',\r\n borderColor: 'border-amber-500/30',\r\n path: [\r\n 'sal_order.id',\r\n 'prd_work_order.sales_order_id',\r\n 'prd_work_report.work_order_id',\r\n 'fin_receivable.source_id',\r\n ],\r\n },\r\n {\r\n field: 'batch_no',\r\n labelKey: 'batchLabel',\r\n color: 'text-emerald-400',\r\n bgColor: 'bg-emerald-500/10',\r\n borderColor: 'border-emerald-500/30',\r\n path: [\r\n 'pur_receipt_detail.batch_no',\r\n 'inv_material_label.batch_no',\r\n 'inv_inventory.batch_no',\r\n 'inv_trace_record.batch_no',\r\n ],\r\n },\r\n {\r\n field: 'warehouse_id',\r\n labelKey: 'warehouseLabel',\r\n color: 'text-purple-400',\r\n bgColor: 'bg-purple-500/10',\r\n borderColor: 'border-purple-500/30',\r\n path: [\r\n 'pur_receipt.warehouse_id',\r\n 'inv_inventory.warehouse_id',\r\n 'prd_material_issue.warehouse_id',\r\n 'inv_production_inbound.warehouse_id',\r\n ],\r\n },\r\n];\r\n\r\ninterface ModuleGroup {\r\n nameKey: string;\r\n icon: React.ComponentType<{ className?: string }>;\r\n color: string;\r\n bgColor: string;\r\n tables: { name: string; comment: string; fks: string[] }[];\r\n}\r\n\r\nconst moduleGroups: ModuleGroup[] = [\r\n {\r\n nameKey: 'systemManagement',\r\n icon: Shield,\r\n color: 'text-slate-400',\r\n bgColor: 'bg-slate-500/10',\r\n tables: [\r\n { name: 'sys_user', comment: '用户表', fks: ['department_id → sys_department'] },\r\n { name: 'sys_department', comment: '部门表', fks: [] },\r\n { name: 'sys_role', comment: '角色表', fks: [] },\r\n {\r\n name: 'sys_user_role',\r\n comment: '用户角色关联',\r\n fks: ['user_id → sys_user', 'role_id → sys_role'],\r\n },\r\n { name: 'sys_menu', comment: '菜单表', fks: [] },\r\n { name: 'sys_role_menu', comment: '角色菜单关联', fks: [] },\r\n { name: 'sys_operation_log', comment: '操作日志', fks: [] },\r\n { name: 'sys_login_log', comment: '登录日志', fks: [] },\r\n { name: 'sys_dict_type', comment: '字典类型', fks: [] },\r\n { name: 'sys_dict_data', comment: '字典数据', fks: [] },\r\n { name: 'sys_config', comment: '系统配置', fks: [] },\r\n ],\r\n },\r\n {\r\n nameKey: 'customerManagement',\r\n icon: Users,\r\n color: 'text-pink-400',\r\n bgColor: 'bg-pink-500/10',\r\n tables: [\r\n { name: 'crm_customer', comment: '客户表', fks: [] },\r\n {\r\n name: 'crm_customer_contact',\r\n comment: '客户联系人',\r\n fks: ['customer_id → crm_customer'],\r\n },\r\n {\r\n name: 'crm_customer_follow_up',\r\n comment: '客户跟进',\r\n fks: ['customer_id → crm_customer'],\r\n },\r\n ],\r\n },\r\n {\r\n nameKey: 'supplierManagement',\r\n icon: Truck,\r\n color: 'text-indigo-400',\r\n bgColor: 'bg-indigo-500/10',\r\n tables: [\r\n { name: 'pur_supplier', comment: '供应商表', fks: [] },\r\n {\r\n name: 'pur_supplier_material',\r\n comment: '供应商物料',\r\n fks: ['supplier_id → pur_supplier', 'material_id → inv_material'],\r\n },\r\n ],\r\n },\r\n {\r\n nameKey: 'materialManagement',\r\n icon: Tag,\r\n color: 'text-teal-400',\r\n bgColor: 'bg-teal-500/10',\r\n tables: [\r\n { name: 'inv_material_category', comment: '物料分类', fks: [] },\r\n {\r\n name: 'inv_material',\r\n comment: '物料表',\r\n fks: ['category_id → inv_material_category'],\r\n },\r\n { name: 'inv_warehouse', comment: '仓库表', fks: [] },\r\n {\r\n name: 'inv_inventory',\r\n comment: '库存表',\r\n fks: ['material_id → inv_material', 'warehouse_id → inv_warehouse'],\r\n },\r\n {\r\n name: 'inv_inventory_log',\r\n comment: '库存日志',\r\n fks: ['material_id → inv_material', 'warehouse_id → inv_warehouse'],\r\n },\r\n ],\r\n },\r\n {\r\n nameKey: 'procurementManagement',\r\n icon: ShoppingCart,\r\n color: 'text-blue-400',\r\n bgColor: 'bg-blue-500/10',\r\n tables: [\r\n { name: 'pur_request', comment: '采购申请', fks: [] },\r\n {\r\n name: 'pur_request_detail',\r\n comment: '采购申请明细',\r\n fks: ['request_id → pur_request', 'material_id → inv_material'],\r\n },\r\n { name: 'pur_order', comment: '采购订单', fks: ['supplier_id → pur_supplier'] },\r\n {\r\n name: 'pur_order_detail',\r\n comment: '采购订单明细',\r\n fks: ['order_id → pur_order', 'material_id → inv_material'],\r\n },\r\n {\r\n name: 'pur_receipt',\r\n comment: '采购收货',\r\n fks: ['order_id → pur_order', 'supplier_id → pur_supplier', 'warehouse_id → inv_warehouse'],\r\n },\r\n { name: 'pur_receipt_detail', comment: '收货明细', fks: [] },\r\n ],\r\n },\r\n {\r\n nameKey: 'salesManagement',\r\n icon: FileText,\r\n color: 'text-amber-400',\r\n bgColor: 'bg-amber-500/10',\r\n tables: [\r\n { name: 'sal_order', comment: '销售订单', fks: ['customer_id → crm_customer'] },\r\n {\r\n name: 'sal_order_detail',\r\n comment: '销售订单明细',\r\n fks: ['order_id → sal_order', 'material_id → inv_material'],\r\n },\r\n {\r\n name: 'sal_delivery',\r\n comment: '销售发货',\r\n fks: ['order_id → sal_order', 'customer_id → crm_customer', 'warehouse_id → inv_warehouse'],\r\n },\r\n { name: 'sal_delivery_detail', comment: '发货明细', fks: [] },\r\n ],\r\n },\r\n {\r\n nameKey: 'productionManagement',\r\n icon: Factory,\r\n color: 'text-orange-400',\r\n bgColor: 'bg-orange-500/10',\r\n tables: [\r\n { name: 'prd_standard_card', comment: '标准工艺卡', fks: [] },\r\n {\r\n name: 'prd_work_order',\r\n comment: '生产工单',\r\n fks: ['sales_order_id → sal_order', 'material_id → inv_material'],\r\n },\r\n { name: 'prd_bom', comment: 'BOM表', fks: ['material_id → inv_material'] },\r\n {\r\n name: 'prd_bom_detail',\r\n comment: 'BOM明细',\r\n fks: ['bom_id → prd_bom', 'material_id → inv_material'],\r\n },\r\n ],\r\n },\r\n {\r\n nameKey: 'warehouseManagement',\r\n icon: Warehouse,\r\n color: 'text-cyan-400',\r\n bgColor: 'bg-cyan-500/10',\r\n tables: [\r\n { name: 'inv_inbound_order', comment: '入库单', fks: [] },\r\n { name: 'inv_outbound_order', comment: '出库单', fks: [] },\r\n { name: 'inv_transfer_order', comment: '调拨单', fks: [] },\r\n { name: 'inv_material_label', comment: '物料标签', fks: [] },\r\n { name: 'inv_cutting_record', comment: '分切记录', fks: [] },\r\n { name: 'inv_scan_log', comment: '扫码日志', fks: [] },\r\n { name: 'inv_stocktaking', comment: '盘点记录', fks: [] },\r\n ],\r\n },\r\n {\r\n nameKey: 'qualityManagement',\r\n icon: FlaskConical,\r\n color: 'text-rose-400',\r\n bgColor: 'bg-rose-500/10',\r\n tables: [\r\n { name: 'qc_inspection', comment: '检验记录', fks: [] },\r\n { name: 'qc_unqualified', comment: '不合格记录', fks: [] },\r\n { name: 'inv_trace_record', comment: '追溯记录', fks: [] },\r\n ],\r\n },\r\n {\r\n nameKey: 'financeManagement',\r\n icon: CreditCard,\r\n color: 'text-emerald-400',\r\n bgColor: 'bg-emerald-500/10',\r\n tables: [\r\n {\r\n name: 'fin_receivable',\r\n comment: '应收款',\r\n fks: ['customer_id → crm_customer', 'source_id → sal_order'],\r\n },\r\n {\r\n name: 'fin_payable',\r\n comment: '应付款',\r\n fks: ['supplier_id → pur_supplier', 'source_id → pur_order'],\r\n },\r\n { name: 'fin_receipt_record', comment: '收款记录', fks: [] },\r\n ],\r\n },\r\n {\r\n nameKey: 'hrManagement',\r\n icon: UserCog,\r\n color: 'text-violet-400',\r\n bgColor: 'bg-violet-500/10',\r\n tables: [\r\n { name: 'hr_employee', comment: '员工表', fks: [] },\r\n { name: 'hr_attendance', comment: '考勤记录', fks: [] },\r\n { name: 'hr_training', comment: '培训记录', fks: [] },\r\n ],\r\n },\r\n {\r\n nameKey: 'equipmentManagement',\r\n icon: Wrench,\r\n color: 'text-lime-400',\r\n bgColor: 'bg-lime-500/10',\r\n tables: [\r\n { name: 'eqp_equipment', comment: '设备表', fks: [] },\r\n { name: 'eqp_maintenance_record', comment: '保养记录', fks: [] },\r\n { name: 'eqp_repair', comment: '维修记录', fks: [] },\r\n ],\r\n },\r\n];\r\n\r\nconst fkConnections = [\r\n 'sys_user.department_id → sys_department.id',\r\n 'sys_user_role.user_id → sys_user.id, role_id → sys_role.id',\r\n 'crm_customer_contact.customer_id → crm_customer.id',\r\n 'pur_supplier_material.supplier_id → pur_supplier.id, material_id → inv_material.id',\r\n 'inv_material.category_id → inv_material_category.id',\r\n 'pur_request_detail.request_id → pur_request.id, material_id → inv_material.id',\r\n 'pur_order.supplier_id → pur_supplier.id',\r\n 'pur_order_detail.order_id → pur_order.id, material_id → inv_material.id',\r\n 'pur_receipt.order_id → pur_order.id, supplier_id → pur_supplier.id, warehouse_id → inv_warehouse.id',\r\n 'sal_order.customer_id → crm_customer.id',\r\n 'sal_order_detail.order_id → sal_order.id, material_id → inv_material.id',\r\n 'sal_delivery.order_id → sal_order.id, customer_id → crm_customer.id, warehouse_id → inv_warehouse.id',\r\n 'prd_work_order.sales_order_id → sal_order.id, material_id → inv_material.id',\r\n 'prd_bom.material_id → inv_material.id',\r\n 'prd_bom_detail.bom_id → prd_bom.id, material_id → inv_material.id',\r\n 'inv_inventory.material_id → inv_material.id, warehouse_id → inv_warehouse.id',\r\n 'inv_inventory_log.material_id → inv_material.id, warehouse_id → inv_warehouse.id',\r\n 'fin_receivable.customer_id → crm_customer.id, source_id → sal_order.id',\r\n 'fin_payable.supplier_id → pur_supplier.id, source_id → pur_order.id',\r\n];\r\n\r\nconst architectureLayers = [\r\n {\r\n nameKey: 'presentationLayer',\r\n subtitle: 'Presentation Layer',\r\n icon: Monitor,\r\n color: 'from-blue-600 to-blue-700',\r\n borderColor: 'border-blue-500/40',\r\n textColor: 'text-blue-400',\r\n items: [\r\n { name: 'Next.js App Router', desc: 'Pages & Layouts', icon: LayoutGrid },\r\n { name: 'React Components', desc: 'UI/UX', icon: Cog },\r\n { name: 'Tailwind CSS + shadcn/ui', desc: '样式系统', icon: Palette },\r\n { name: 'framer-motion', desc: 'Animations', icon: Workflow },\r\n ],\r\n },\r\n {\r\n nameKey: 'apiLayer',\r\n subtitle: 'API Layer',\r\n icon: Globe,\r\n color: 'from-cyan-600 to-cyan-700',\r\n borderColor: 'border-cyan-500/40',\r\n textColor: 'text-cyan-400',\r\n items: [\r\n { name: 'Next.js Route Handlers', desc: '/api/*', icon: Server },\r\n { name: 'RESTful API Endpoints', desc: '接口规范', icon: Link2 },\r\n { name: 'JWT Authentication', desc: '认证中间件', icon: Lock },\r\n { name: 'Permission Middleware', desc: 'RBAC', icon: Shield },\r\n { name: 'Zod Schema Validation', desc: '数据校验', icon: CheckSquare },\r\n ],\r\n },\r\n {\r\n nameKey: 'businessLogicLayer',\r\n subtitle: 'Business Logic Layer',\r\n icon: Cpu,\r\n color: 'from-purple-600 to-purple-700',\r\n borderColor: 'border-purple-500/40',\r\n textColor: 'text-purple-400',\r\n items: [\r\n { name: 'Work Order State Machine', desc: '工单状态机', icon: Workflow },\r\n { name: 'FIFO/FEFO Inventory', desc: '库存分配策略', icon: BarChart3 },\r\n { name: 'Label Service', desc: 'ZPL/Thermal', icon: QrCode },\r\n { name: 'Production Scheduling', desc: '生产排程引擎', icon: Factory },\r\n { name: 'Operation Logging', desc: '操作日志服务', icon: FileText },\r\n ],\r\n },\r\n {\r\n nameKey: 'dataAccessLayer',\r\n subtitle: 'Data Access Layer',\r\n icon: HardDrive,\r\n color: 'from-emerald-600 to-emerald-700',\r\n borderColor: 'border-emerald-500/40',\r\n textColor: 'text-emerald-400',\r\n items: [\r\n { name: 'MySQL2 Driver', desc: '数据库驱动', icon: Database },\r\n { name: 'Transaction Helper', desc: 'with FOR UPDATE', icon: Lock },\r\n { name: 'Optimistic Locking', desc: 'version field', icon: Shield },\r\n { name: 'Connection Pool', desc: '连接池管理', icon: Server },\r\n ],\r\n },\r\n {\r\n nameKey: 'databaseLayer',\r\n subtitle: 'Database Layer',\r\n icon: Database,\r\n color: 'from-rose-600 to-rose-700',\r\n borderColor: 'border-rose-500/40',\r\n textColor: 'text-rose-400',\r\n items: [\r\n { name: 'MySQL 8.0', desc: 'vnerpdacahng', icon: Database },\r\n { name: '48 Business Tables', desc: '业务表', icon: Table },\r\n { name: 'InnoDB Engine', desc: '存储引擎', icon: Cog },\r\n { name: 'utf8mb4 Charset', desc: '字符集', icon: FileText },\r\n ],\r\n },\r\n];\r\n\r\nfunction Palette(props: React.SVGProps<SVGSVGElement>) {\r\n return (\r\n <svg\r\n xmlns=\"http://www.w3.org/2000/svg\"\r\n width=\"24\"\r\n height=\"24\"\r\n viewBox=\"0 0 24 24\"\r\n fill=\"none\"\r\n stroke=\"currentColor\"\r\n strokeWidth=\"2\"\r\n strokeLinecap=\"round\"\r\n strokeLinejoin=\"round\"\r\n {...props}\r\n >\r\n <circle cx=\"13.5\" cy=\"6.5\" r=\".5\" fill=\"currentColor\" />\r\n <circle cx=\"17.5\" cy=\"10.5\" r=\".5\" fill=\"currentColor\" />\r\n <circle cx=\"8.5\" cy=\"7.5\" r=\".5\" fill=\"currentColor\" />\r\n <circle cx=\"6.5\" cy=\"12.5\" r=\".5\" fill=\"currentColor\" />\r\n <path d=\"M12 2C6.5 2 2 6.5 2 12s4.5 10 10 10c.926 0 1.648-.746 1.648-1.688 0-.437-.18-.835-.437-1.125-.29-.289-.438-.652-.438-1.125a1.64 1.64 0 0 1 1.668-1.668h1.996c3.051 0 5.555-2.503 5.555-5.554C21.965 6.012 17.461 2 12 2z\" />\r\n </svg>\r\n );\r\n}\r\n\r\nfunction Table(props: React.SVGProps<SVGSVGElement>) {\r\n return (\r\n <svg\r\n xmlns=\"http://www.w3.org/2000/svg\"\r\n width=\"24\"\r\n height=\"24\"\r\n viewBox=\"0 0 24 24\"\r\n fill=\"none\"\r\n stroke=\"currentColor\"\r\n strokeWidth=\"2\"\r\n strokeLinecap=\"round\"\r\n strokeLinejoin=\"round\"\r\n {...props}\r\n >\r\n <path d=\"M12 3v18\" />\r\n <rect width=\"18\" height=\"18\" x=\"3\" y=\"3\" rx=\"2\" />\r\n <path d=\"M3 9h18\" />\r\n <path d=\"M3 15h18\" />\r\n </svg>\r\n );\r\n}\r\n\r\nfunction FlowArrow() {\r\n return (\r\n <div className=\"flex items-center justify-center w-8 shrink-0\">\r\n <svg width=\"32\" height=\"24\" viewBox=\"0 0 32 24\" className=\"text-muted-foreground/50\">\r\n <line\r\n x1=\"0\"\r\n y1=\"12\"\r\n x2=\"24\"\r\n y2=\"12\"\r\n stroke=\"currentColor\"\r\n strokeWidth=\"2\"\r\n strokeDasharray=\"4 2\"\r\n />\r\n <polygon points=\"24,6 32,12 24,18\" fill=\"currentColor\" />\r\n </svg>\r\n </div>\r\n );\r\n}\r\n\r\nfunction FlowNodeCard({ node }: { node: (typeof flowNodes)[0] }) {\r\n const t = useTranslations('Dashboard');\r\n const Icon = node.icon;\r\n return (\r\n <Card\r\n className={`relative overflow-hidden border ${node.borderColor} ${node.bgColor} backdrop-blur-sm w-44 shrink-0`}\r\n >\r\n <div className={`absolute top-0 left-0 right-0 h-1 bg-gradient-to-r ${node.color}`} />\r\n <CardContent className=\"p-3 space-y-2\">\r\n <div className=\"flex items-center gap-2\">\r\n <div className={`p-1.5 rounded-md bg-gradient-to-br ${node.color}`}>\r\n <Icon className=\"h-3.5 w-3.5 text-white\" />\r\n </div>\r\n <div>\r\n <div className=\"text-[10px] text-muted-foreground\">Step {node.step}</div>\r\n <div className={`text-sm font-semibold ${node.textColor}`}>{t(node.nameKey)}</div>\r\n </div>\r\n </div>\r\n <div className=\"space-y-1\">\r\n <div className=\"flex items-center gap-1 text-[10px] text-muted-foreground\">\r\n <Database className=\"h-2.5 w-2.5\" />\r\n <span>{t('relatedTables')}</span>\r\n </div>\r\n <div className=\"flex flex-wrap gap-1\">\r\n {node.tables.map((t) => (\r\n <Badge key={t} variant=\"secondary\" className=\"text-[9px] px-1.5 py-0 h-4 font-mono\">\r\n {t}\r\n </Badge>\r\n ))}\r\n </div>\r\n </div>\r\n <div className=\"space-y-1\">\r\n <div className=\"text-[10px] text-muted-foreground\">{t('keyFields')}</div>\r\n <div className=\"flex flex-wrap gap-1\">\r\n {node.fields.map((f) => (\r\n <span\r\n key={f}\r\n className=\"text-[9px] px-1.5 py-0.5 rounded bg-muted/50 text-muted-foreground font-mono\"\r\n >\r\n {f}\r\n </span>\r\n ))}\r\n </div>\r\n </div>\r\n </CardContent>\r\n </Card>\r\n );\r\n}\r\n\r\nfunction FieldTrackingCard({ track }: { track: (typeof fieldTracking)[0] }) {\r\n const t = useTranslations('Dashboard');\r\n return (\r\n <Card className={`border ${track.borderColor} ${track.bgColor}`}>\r\n <CardHeader className=\"pb-2 pt-3 px-4\">\r\n <CardTitle className=\"text-sm flex items-center gap-2\">\r\n <Link2 className={`h-4 w-4 ${track.color}`} />\r\n <span className=\"font-mono\">{track.field}</span>\r\n <Badge variant=\"outline\" className=\"text-[10px] h-4 ml-auto\">\r\n {t(track.labelKey)}\r\n </Badge>\r\n </CardTitle>\r\n </CardHeader>\r\n <CardContent className=\"px-4 pb-3\">\r\n <div className=\"flex items-center gap-1 flex-wrap\">\r\n {track.path.map((p, i) => (\r\n <span key={i} className=\"flex items-center gap-1\">\r\n <span className=\"text-xs font-mono px-2 py-1 rounded bg-muted/50 text-foreground/80 whitespace-nowrap\">\r\n {p}\r\n </span>\r\n {i < track.path.length - 1 && (\r\n <ArrowRight className={`h-3 w-3 ${track.color} shrink-0`} />\r\n )}\r\n </span>\r\n ))}\r\n </div>\r\n </CardContent>\r\n </Card>\r\n );\r\n}\r\n\r\nfunction ModuleGroupSection({\r\n group,\r\n defaultOpen = false,\r\n}: {\r\n group: ModuleGroup;\r\n defaultOpen?: boolean;\r\n}) {\r\n const [open, setOpen] = useState(defaultOpen);\r\n const Icon = group.icon;\r\n const t = useTranslations('Dashboard');\r\n return (\r\n <div className=\"border border-border rounded-lg overflow-hidden\">\r\n <button\r\n onClick={() => setOpen(!open)}\r\n className=\"w-full flex items-center gap-3 px-4 py-3 bg-muted/30 hover:bg-muted/50 transition-colors\"\r\n >\r\n <div className={`p-1.5 rounded-md ${group.bgColor}`}>\r\n <Icon className={`h-4 w-4 ${group.color}`} />\r\n </div>\r\n <span className=\"font-semibold text-sm\">{t(group.nameKey)}</span>\r\n <Badge variant=\"secondary\" className=\"text-[10px] h-4\">\r\n {t('tableCount', { count: group.tables.length })}\r\n </Badge>\r\n <div className=\"ml-auto\">\r\n {open ? (\r\n <ChevronDown className=\"h-4 w-4 text-muted-foreground\" />\r\n ) : (\r\n <ChevronRight className=\"h-4 w-4 text-muted-foreground\" />\r\n )}\r\n </div>\r\n </button>\r\n {open && (\r\n <div className=\"p-3 grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-2\">\r\n {group.tables.map((table) => (\r\n <div\r\n key={table.name}\r\n className=\"border border-border rounded-md p-2.5 bg-card hover:bg-muted/30 transition-colors\"\r\n >\r\n <div className=\"flex items-center gap-2 mb-1.5\">\r\n <Database className=\"h-3.5 w-3.5 text-muted-foreground shrink-0\" />\r\n <span className=\"font-mono text-xs font-semibold text-foreground/90\">\r\n {table.name}\r\n </span>\r\n </div>\r\n <div className=\"text-[10px] text-muted-foreground mb-1.5\">{table.comment}</div>\r\n {table.fks.length > 0 && (\r\n <div className=\"flex flex-wrap gap-1\">\r\n {table.fks.map((fk) => (\r\n <Badge\r\n key={fk}\r\n variant=\"outline\"\r\n className=\"text-[8px] px-1 py-0 h-3.5 font-mono text-amber-500 border-amber-500/30\"\r\n >\r\n FK: {fk}\r\n </Badge>\r\n ))}\r\n </div>\r\n )}\r\n </div>\r\n ))}\r\n </div>\r\n )}\r\n </div>\r\n );\r\n}\r\n\r\nfunction ArchitectureLayerCard({\r\n layer,\r\n index,\r\n}: {\r\n layer: (typeof architectureLayers)[0];\r\n index: number;\r\n}) {\r\n const t = useTranslations('Dashboard');\r\n const Icon = layer.icon;\r\n return (\r\n <div className=\"space-y-0\">\r\n <Card className={`relative overflow-hidden border ${layer.borderColor}`}>\r\n <div className={`absolute top-0 left-0 right-0 h-1.5 bg-gradient-to-r ${layer.color}`} />\r\n <CardContent className=\"p-4 pt-5\">\r\n <div className=\"flex items-center gap-3 mb-4\">\r\n <div className={`p-2 rounded-lg bg-gradient-to-br ${layer.color}`}>\r\n <Icon className=\"h-5 w-5 text-white\" />\r\n </div>\r\n <div>\r\n <div className=\"flex items-center gap-2\">\r\n <span className=\"text-lg font-bold\">{t(layer.nameKey)}</span>\r\n <Badge variant=\"outline\" className=\"text-[10px] h-4\">\r\n {layer.subtitle}\r\n </Badge>\r\n </div>\r\n <span className=\"text-xs text-muted-foreground\">Layer {index + 1}</span>\r\n </div>\r\n </div>\r\n <div className=\"grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 gap-2\">\r\n {layer.items.map((item) => {\r\n const ItemIcon = item.icon;\r\n return (\r\n <div\r\n key={item.name}\r\n className={`flex items-center gap-2 p-2 rounded-md border border-border bg-muted/20`}\r\n >\r\n <ItemIcon className={`h-4 w-4 ${layer.textColor} shrink-0`} />\r\n <div className=\"min-w-0\">\r\n <div className=\"text-xs font-medium truncate\">{item.name}</div>\r\n <div className=\"text-[10px] text-muted-foreground truncate\">{item.desc}</div>\r\n </div>\r\n </div>\r\n );\r\n })}\r\n </div>\r\n </CardContent>\r\n </Card>\r\n {index < architectureLayers.length - 1 && (\r\n <div className=\"flex justify-center py-1\">\r\n <svg width=\"40\" height=\"28\" viewBox=\"0 0 40 28\" className=\"text-muted-foreground/40\">\r\n <line\r\n x1=\"20\"\r\n y1=\"0\"\r\n x2=\"20\"\r\n y2=\"20\"\r\n stroke=\"currentColor\"\r\n strokeWidth=\"2\"\r\n strokeDasharray=\"4 2\"\r\n />\r\n <polygon points=\"12,18 20,28 28,18\" fill=\"currentColor\" />\r\n </svg>\r\n </div>\r\n )}\r\n </div>\r\n );\r\n}\r\n\r\nexport default function FlowPage() {\r\n // 翻译钩子\r\n const t = useTranslations('Dashboard');\r\n const tc = useTranslations('Common');\r\n\r\n return (\r\n <MainLayout title={t('flowVisualization')}>\r\n <div className=\"space-y-6\">\r\n <div>\r\n <h2 className=\"text-3xl font-bold tracking-tight\">{t('flowVisualization')}</h2>\r\n <p className=\"text-muted-foreground mt-1\">{t('flowPageDescription')}</p>\r\n </div>\r\n\r\n <Tabs defaultValue=\"flow\" className=\"space-y-4\">\r\n <TabsList>\r\n <TabsTrigger value=\"flow\" className=\"gap-1.5\">\r\n <Workflow className=\"h-4 w-4\" />\r\n {t('businessFlowChain')}\r\n </TabsTrigger>\r\n <TabsTrigger value=\"relation\" className=\"gap-1.5\">\r\n <Database className=\"h-4 w-4\" />\r\n {t('tableRelations')}\r\n </TabsTrigger>\r\n <TabsTrigger value=\"architecture\" className=\"gap-1.5\">\r\n <Layers className=\"h-4 w-4\" />\r\n {t('systemArchitectureTabs')}\r\n </TabsTrigger>\r\n </TabsList>\r\n\r\n <TabsContent value=\"flow\" className=\"space-y-6\">\r\n <Card>\r\n <CardHeader>\r\n <CardTitle className=\"flex items-center gap-2\">\r\n <Workflow className=\"h-5 w-5 text-blue-400\" />\r\n {t('productionFlowChain')}\r\n </CardTitle>\r\n </CardHeader>\r\n <CardContent>\r\n <div className=\"overflow-x-auto pb-4\">\r\n <div className=\"flex items-center gap-0 min-w-max py-2\">\r\n {flowNodes.map((node, i) => (\r\n <div key={node.step} className=\"flex items-center\">\r\n <FlowNodeCard node={node} />\r\n {i < flowNodes.length - 1 && <FlowArrow />}\r\n </div>\r\n ))}\r\n </div>\r\n </div>\r\n </CardContent>\r\n </Card>\r\n\r\n <Card>\r\n <CardHeader>\r\n <CardTitle className=\"flex items-center gap-2\">\r\n <Link2 className=\"h-5 w-5 text-amber-400\" />\r\n {t('keyFieldPath')}\r\n </CardTitle>\r\n </CardHeader>\r\n <CardContent className=\"space-y-3\">\r\n {fieldTracking.map((track) => (\r\n <FieldTrackingCard key={track.field} track={track} />\r\n ))}\r\n </CardContent>\r\n </Card>\r\n </TabsContent>\r\n\r\n <TabsContent value=\"relation\" className=\"space-y-6\">\r\n <Card>\r\n <CardHeader>\r\n <CardTitle className=\"flex items-center gap-2\">\r\n <Database className=\"h-5 w-5 text-cyan-400\" />\r\n {t('businessTableModules')}\r\n <Badge variant=\"secondary\" className=\"ml-2\">\r\n {t('tableModuleCount', { tables: 48, modules: 12 })}\r\n </Badge>\r\n </CardTitle>\r\n </CardHeader>\r\n <CardContent className=\"space-y-3\">\r\n {moduleGroups.map((group) => (\r\n <ModuleGroupSection\r\n key={group.nameKey}\r\n group={group}\r\n defaultOpen={\r\n group.nameKey === 'procurementManagement' ||\r\n group.nameKey === 'salesManagement' ||\r\n group.nameKey === 'productionManagement'\r\n }\r\n />\r\n ))}\r\n </CardContent>\r\n </Card>\r\n\r\n <Card>\r\n <CardHeader>\r\n <CardTitle className=\"flex items-center gap-2\">\r\n <Link2 className=\"h-5 w-5 text-amber-400\" />\r\n {t('foreignKeyList')}\r\n <Badge variant=\"secondary\" className=\"ml-2\">\r\n {fkConnections.length}\r\n {tc('foreignKeyCountSuffix')}\r\n </Badge>\r\n </CardTitle>\r\n </CardHeader>\r\n <CardContent>\r\n <div className=\"grid grid-cols-1 md:grid-cols-2 gap-2\">\r\n {fkConnections.map((fk, i) => (\r\n <div\r\n key={i}\r\n className=\"flex items-center gap-2 p-2 rounded-md border border-border bg-muted/20\"\r\n >\r\n <ArrowRight className=\"h-3 w-3 text-amber-500 shrink-0\" />\r\n <span className=\"text-xs font-mono text-foreground/80\">{fk}</span>\r\n </div>\r\n ))}\r\n </div>\r\n </CardContent>\r\n </Card>\r\n </TabsContent>\r\n\r\n <TabsContent value=\"architecture\" className=\"space-y-0\">\r\n <Card className=\"mb-4\">\r\n <CardHeader>\r\n <CardTitle className=\"flex items-center gap-2\">\r\n <Layers className=\"h-5 w-5 text-purple-400\" />\r\n {t('systemArchitectureDiagram')}\r\n </CardTitle>\r\n </CardHeader>\r\n <CardContent>\r\n <div className=\"space-y-0\">\r\n {architectureLayers.map((layer, i) => (\r\n <ArchitectureLayerCard key={layer.nameKey} layer={layer} index={i} />\r\n ))}\r\n </div>\r\n </CardContent>\r\n </Card>\r\n </TabsContent>\r\n </Tabs>\r\n </div>\r\n </MainLayout>\r\n );\r\n}\r\n","usedDeprecatedRules":[]},{"filePath":"D:\\dcprint\\erp-project\\src\\app\\[locale]\\dashboard\\page.tsx","messages":[{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":91,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":91,"endColumn":38},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":92,"column":18,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":92,"endColumn":25},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":92,"column":36,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":92,"endColumn":40},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction<DashboardData>`.","line":92,"column":50,"nodeType":"MemberExpression","messageId":"unsafeArgument","endLine":92,"endColumn":61},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":92,"column":57,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":92,"endColumn":61},{"ruleId":"react-hooks/set-state-in-effect","severity":1,"message":"Error: Calling setState synchronously within an effect can trigger cascading renders\n\nEffects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following:\n* Update external systems with the latest state from React.\n* Subscribe for updates from some external system, calling setState in a callback function when external state changes.\n\nCalling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect).\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\dashboard\\page.tsx:100:5\n 98 |\n 99 | useEffect(() => {\n> 100 | fetchDashboard();\n | ^^^^^^^^^^^^^^ Avoid calling setState() directly within an effect\n 101 | const updateTime = () => {\n 102 | const now = new Date();\n 103 | setCurrentTime(","line":100,"column":5,"nodeType":null,"endLine":100,"endColumn":19},{"ruleId":"react-hooks/exhaustive-deps","severity":1,"message":"React Hook useEffect has a missing dependency: 'locale'. Either include it or remove the dependency array.","line":117,"column":6,"nodeType":"ArrayExpression","endLine":117,"endColumn":8,"suggestions":[{"desc":"Update the dependencies array to be: [locale]","fix":{"range":[2850,2852],"text":"[locale]"}}]}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":7,"fixableErrorCount":0,"fixableWarningCount":0,"source":"'use client';\n\nimport { authFetch } from '@/lib/auth-fetch';\nimport { useEffect, useState } from 'react';\nimport { MainLayout } from '@/components/layout';\nimport { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';\nimport { Badge } from '@/components/ui/badge';\nimport {\n Table,\n TableBody,\n TableCell,\n TableHead,\n TableHeader,\n TableRow,\n} from '@/components/ui/table';\nimport { Button } from '@/components/ui/button';\nimport { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';\nimport {\n FileText,\n Package,\n Factory,\n AlertTriangle,\n CheckCircle,\n Activity,\n TrendingUp,\n TrendingDown,\n Users,\n DollarSign,\n ArrowRight,\n RefreshCw,\n} from 'lucide-react';\nimport Link from 'next/link';\nimport { useTranslations, useLocale } from 'next-intl';\n\ninterface DashboardData {\n stats: {\n todayOrders: number;\n orderChange: number;\n pendingOrders: number;\n producingOrders: number;\n completedToday: number;\n inventoryAlert: number;\n totalCustomers: number;\n totalEmployees: number;\n todayProduction: number;\n productionChange: number;\n };\n recentOrders: {\n id: number;\n orderNo: string;\n customer: string;\n product: string;\n quantity: number;\n status: number;\n date: string;\n }[];\n alerts: { type: string; message: string; severity: string; time: string }[];\n orderStats: { date: string; count: number }[];\n}\n\nexport default function DashboardPage() {\n const t = useTranslations('Dashboard');\n const tc = useTranslations('Common');\n const locale = useLocale();\n\n const [currentTime, setCurrentTime] = useState<string>('');\n const [data, setData] = useState<DashboardData>({\n stats: {\n todayOrders: 0,\n orderChange: 0,\n pendingOrders: 0,\n producingOrders: 0,\n completedToday: 0,\n inventoryAlert: 0,\n totalCustomers: 0,\n totalEmployees: 0,\n todayProduction: 0,\n productionChange: 0,\n },\n recentOrders: [],\n alerts: [],\n orderStats: [],\n });\n const [loading, setLoading] = useState(true);\n\n const s = data.stats;\n\n const fetchDashboard = async () => {\n try {\n const res = await authFetch('/api/dashboard');\n const result = await res.json();\n if (result.success && result.data) setData(result.data);\n } catch {\n } finally {\n setLoading(false);\n }\n };\n\n useEffect(() => {\n fetchDashboard();\n const updateTime = () => {\n const now = new Date();\n setCurrentTime(\n now.toLocaleString(locale, {\n year: 'numeric',\n month: '2-digit',\n day: '2-digit',\n hour: '2-digit',\n minute: '2-digit',\n second: '2-digit',\n })\n );\n };\n updateTime();\n const timer = setInterval(updateTime, 1000);\n return () => clearInterval(timer);\n }, []);\n\n const getStatusBadge = (status: number) => {\n const map: Record<\n number,\n { label: string; variant: 'default' | 'secondary' | 'destructive' | 'outline' }\n > = {\n 0: { label: tc('draft'), variant: 'secondary' },\n 1: { label: t('statusConfirmed'), variant: 'default' },\n 2: { label: t('statusInProduction'), variant: 'default' },\n 3: { label: t('statusCompleted'), variant: 'outline' },\n };\n const cfg = map[status] || { label: tc('unknown'), variant: 'secondary' as const };\n return <Badge variant={cfg.variant}>{cfg.label}</Badge>;\n };\n\n return (\n <MainLayout title={t('title')}>\n <div className=\"space-y-6\">\n <div className=\"flex items-center justify-between\">\n <div>\n <h2 className=\"text-3xl font-bold tracking-tight\">{t('title')}</h2>\n <p className=\"text-muted-foreground mt-1\">{t('welcome', { time: currentTime })}</p>\n </div>\n <Button\n variant=\"outline\"\n size=\"sm\"\n data-testid=\"dashboard-refresh\"\n onClick={() => {\n setLoading(true);\n fetchDashboard();\n }}\n >\n <RefreshCw className=\"h-4 w-4 mr-2\" />\n {tc('refresh')}\n </Button>\n </div>\n\n <div className=\"grid gap-4 md:grid-cols-2 lg:grid-cols-4\">\n <Card>\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-2\">\n <CardTitle className=\"text-sm font-medium\">{t('todayOrders')}</CardTitle>\n <FileText className=\"h-4 w-4 text-blue-600\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold\">{s.todayOrders}</div>\n <div className=\"flex items-center text-xs\">\n {s.orderChange >= 0 ? (\n <TrendingUp className=\"h-3 w-3 text-green-600 mr-1\" />\n ) : (\n <TrendingDown className=\"h-3 w-3 text-red-600 mr-1\" />\n )}\n <span className={s.orderChange >= 0 ? 'text-green-600' : 'text-red-600'}>\n {s.orderChange >= 0 ? '+' : ''}\n {s.orderChange}%\n </span>\n <span className=\"text-muted-foreground ml-1\">{t('vsYesterday')}</span>\n </div>\n </CardContent>\n </Card>\n <Card>\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-2\">\n <CardTitle className=\"text-sm font-medium\">{t('pendingWorkOrders')}</CardTitle>\n <Factory className=\"h-4 w-4 text-orange-600\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold\">{s.pendingOrders}</div>\n <p className=\"text-xs text-muted-foreground\">\n {t('producingCount', { count: s.producingOrders })}\n </p>\n </CardContent>\n </Card>\n <Card>\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-2\">\n <CardTitle className=\"text-sm font-medium\">{t('lowStock')}</CardTitle>\n <AlertTriangle className=\"h-4 w-4 text-red-600\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold\">{s.inventoryAlert}</div>\n <p className=\"text-xs text-muted-foreground\">{t('needRestock')}</p>\n </CardContent>\n </Card>\n <Card>\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-2\">\n <CardTitle className=\"text-sm font-medium\">{t('todayCompleted')}</CardTitle>\n <CheckCircle className=\"h-4 w-4 text-green-600\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold\">{s.completedToday}</div>\n <p className=\"text-xs text-muted-foreground\">{t('workOrderCompletion')}</p>\n </CardContent>\n </Card>\n </div>\n\n <div className=\"grid gap-4 md:grid-cols-2 lg:grid-cols-4\">\n <Card>\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-2\">\n <CardTitle className=\"text-sm font-medium\">{t('customersTitle')}</CardTitle>\n <Users className=\"h-4 w-4 text-purple-600\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold\">{s.totalCustomers}</div>\n <p className=\"text-xs text-muted-foreground\">{t('activeCustomers')}</p>\n </CardContent>\n </Card>\n <Card>\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-2\">\n <CardTitle className=\"text-sm font-medium\">{t('totalEmployees')}</CardTitle>\n <Users className=\"h-4 w-4 text-cyan-600\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold\">{s.totalEmployees}</div>\n <p className=\"text-xs text-muted-foreground\">{t('activeEmployees')}</p>\n </CardContent>\n </Card>\n <Card>\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-2\">\n <CardTitle className=\"text-sm font-medium\">{t('todayProduction')}</CardTitle>\n <Package className=\"h-4 w-4 text-indigo-600\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold\">{s.todayProduction.toLocaleString()}</div>\n <div className=\"flex items-center text-xs\">\n {s.productionChange >= 0 ? (\n <TrendingUp className=\"h-3 w-3 text-green-600 mr-1\" />\n ) : (\n <TrendingDown className=\"h-3 w-3 text-red-600 mr-1\" />\n )}\n <span className={s.productionChange >= 0 ? 'text-green-600' : 'text-red-600'}>\n +{s.productionChange}%\n </span>\n <span className=\"text-muted-foreground ml-1\">{t('vsYesterday')}</span>\n </div>\n </CardContent>\n </Card>\n <Card>\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-2\">\n <CardTitle className=\"text-sm font-medium\">{t('todayRevenue')}</CardTitle>\n <DollarSign className=\"h-4 w-4 text-emerald-600\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold\">¥{(s.todayOrders * 15000).toLocaleString()}</div>\n <p className=\"text-xs text-muted-foreground\">{t('estimatedRevenue')}</p>\n </CardContent>\n </Card>\n </div>\n\n <Tabs defaultValue=\"orders\" className=\"space-y-4\">\n <TabsList>\n <TabsTrigger value=\"orders\">{t('recentOrderList')}</TabsTrigger>\n <TabsTrigger value=\"alerts\">{t('alertNotifications')}</TabsTrigger>\n </TabsList>\n\n <TabsContent value=\"orders\" className=\"space-y-4\">\n <Card>\n <CardHeader className=\"flex flex-row items-center justify-between\">\n <div>\n <CardTitle>{t('recentWorkOrderList')}</CardTitle>\n <CardDescription>{t('recentWorkOrderDesc')}</CardDescription>\n </div>\n <Link href=\"/production/orders\">\n <Button variant=\"outline\" size=\"sm\">\n {t('viewAll')}\n <ArrowRight className=\"h-4 w-4 ml-2\" />\n </Button>\n </Link>\n </CardHeader>\n <CardContent>\n {data.recentOrders.length === 0 ? (\n <p className=\"text-gray-400 text-center py-8\">\n {loading ? tc('loading') : t('noWorkOrderData')}\n </p>\n ) : (\n <Table>\n <TableHeader>\n <TableRow>\n <TableHead>{t('workOrderNo')}</TableHead>\n <TableHead>{t('customer')}</TableHead>\n <TableHead>{t('product')}</TableHead>\n <TableHead>{t('quantity')}</TableHead>\n <TableHead>{t('status')}</TableHead>\n <TableHead>{t('date')}</TableHead>\n </TableRow>\n </TableHeader>\n <TableBody>\n {data.recentOrders.map((o) => (\n <TableRow key={o.id}>\n <TableCell className=\"font-medium font-mono\">{o.orderNo}</TableCell>\n <TableCell>{o.customer || '-'}</TableCell>\n <TableCell>{o.product || '-'}</TableCell>\n <TableCell>{Number(o.quantity).toLocaleString()}</TableCell>\n <TableCell>{getStatusBadge(o.status)}</TableCell>\n <TableCell>{o.date?.substring(0, 10) || '-'}</TableCell>\n </TableRow>\n ))}\n </TableBody>\n </Table>\n )}\n </CardContent>\n </Card>\n </TabsContent>\n\n <TabsContent value=\"alerts\" className=\"space-y-4\">\n <Card>\n <CardHeader>\n <CardTitle>{t('alertNotifications')}</CardTitle>\n <CardDescription>{t('alertDesc')}</CardDescription>\n </CardHeader>\n <CardContent>\n {data.alerts.length === 0 ? (\n <p className=\"text-gray-400 text-center py-8\">{t('noAlerts')}</p>\n ) : (\n <div className=\"space-y-3\">\n {data.alerts.map((a, i) => {\n const iconMap: Record<string, typeof AlertTriangle> = {\n order: FileText,\n production: Factory,\n inventory: Package,\n quality: CheckCircle,\n material: Package,\n equipment: Factory,\n };\n const Icon = iconMap[a.type] || Activity;\n const colorMap: Record<string, string> = {\n high: 'text-red-600 bg-red-50',\n medium: 'text-orange-600 bg-orange-50',\n low: 'text-blue-600 bg-blue-50',\n };\n return (\n <div\n key={i}\n className=\"flex items-start space-x-4 p-3 rounded-lg border hover:bg-muted transition-colors\"\n >\n <div\n className={`p-2 rounded-lg ${colorMap[a.severity] || 'text-gray-600 bg-gray-50'}`}\n >\n <Icon className=\"h-4 w-4\" />\n </div>\n <div className=\"flex-1 space-y-1\">\n <div className=\"flex items-center justify-between\">\n <p className=\"font-medium\">{a.message}</p>\n <Badge variant={a.severity === 'high' ? 'destructive' : 'secondary'}>\n {a.severity === 'high' ? tc('high') : tc('medium')}\n </Badge>\n </div>\n <p className=\"text-xs text-muted-foreground\">{a.time}</p>\n </div>\n </div>\n );\n })}\n </div>\n )}\n </CardContent>\n </Card>\n </TabsContent>\n </Tabs>\n\n <div className=\"grid gap-4 md:grid-cols-2 lg:grid-cols-4\">\n <Link href=\"/dashboard/production\">\n <Card className=\"hover:bg-muted transition-colors cursor-pointer\">\n <CardContent className=\"p-4 flex items-center justify-between\">\n <div className=\"flex items-center space-x-3\">\n <div className=\"p-2 bg-blue-100 rounded-lg\">\n <Factory className=\"h-5 w-5 text-blue-600\" />\n </div>\n <div>\n <p className=\"font-medium\">{t('productionBoard')}</p>\n <p className=\"text-xs text-muted-foreground\">{t('productionMonitor')}</p>\n </div>\n </div>\n <ArrowRight className=\"h-4 w-4 text-muted-foreground\" />\n </CardContent>\n </Card>\n </Link>\n <Link href=\"/dashboard/warehouse\">\n <Card className=\"hover:bg-muted transition-colors cursor-pointer\">\n <CardContent className=\"p-4 flex items-center justify-between\">\n <div className=\"flex items-center space-x-3\">\n <div className=\"p-2 bg-green-100 rounded-lg\">\n <Package className=\"h-5 w-5 text-green-600\" />\n </div>\n <div>\n <p className=\"font-medium\">{t('warehouseBoard')}</p>\n <p className=\"text-xs text-muted-foreground\">{t('warehouseMonitor')}</p>\n </div>\n </div>\n <ArrowRight className=\"h-4 w-4 text-muted-foreground\" />\n </CardContent>\n </Card>\n </Link>\n <Link href=\"/dashboard/sales\">\n <Card className=\"hover:bg-muted transition-colors cursor-pointer\">\n <CardContent className=\"p-4 flex items-center justify-between\">\n <div className=\"flex items-center space-x-3\">\n <div className=\"p-2 bg-orange-100 rounded-lg\">\n <DollarSign className=\"h-5 w-5 text-orange-600\" />\n </div>\n <div>\n <p className=\"font-medium\">{t('salesBoard')}</p>\n <p className=\"text-xs text-muted-foreground\">{t('salesMonitor')}</p>\n </div>\n </div>\n <ArrowRight className=\"h-4 w-4 text-muted-foreground\" />\n </CardContent>\n </Card>\n </Link>\n <Link href=\"/dashboard/quality\">\n <Card className=\"hover:bg-muted transition-colors cursor-pointer\">\n <CardContent className=\"p-4 flex items-center justify-between\">\n <div className=\"flex items-center space-x-3\">\n <div className=\"p-2 bg-purple-100 rounded-lg\">\n <CheckCircle className=\"h-5 w-5 text-purple-600\" />\n </div>\n <div>\n <p className=\"font-medium\">{t('qualityBoard')}</p>\n <p className=\"text-xs text-muted-foreground\">{t('qualityMonitor')}</p>\n </div>\n </div>\n <ArrowRight className=\"h-4 w-4 text-muted-foreground\" />\n </CardContent>\n </Card>\n </Link>\n </div>\n </div>\n </MainLayout>\n );\n}\n","usedDeprecatedRules":[]},{"filePath":"D:\\dcprint\\erp-project\\src\\app\\[locale]\\dashboard\\production\\page.tsx","messages":[{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":194,"column":15,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":194,"endColumn":40},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":195,"column":20,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":195,"endColumn":27},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":195,"column":38,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":195,"endColumn":42},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":196,"column":17,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":196,"endColumn":32},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":196,"column":28,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":196,"endColumn":32},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":198,"column":13,"nodeType":"Property","messageId":"anyAssignment","endLine":205,"endColumn":14},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .overview on an `any` value.","line":198,"column":25,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":198,"endColumn":33},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":206,"column":13,"nodeType":"Property","messageId":"anyAssignment","endLine":206,"endColumn":53},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .equipmentStatus on an `any` value.","line":206,"column":32,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":206,"endColumn":47},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":207,"column":13,"nodeType":"Property","messageId":"anyAssignment","endLine":218,"endColumn":16},{"ruleId":"@typescript-eslint/no-unsafe-call","severity":1,"message":"Unsafe call of an `any` typed value.","line":207,"column":33,"nodeType":"MemberExpression","messageId":"unsafeCall","endLine":207,"endColumn":59},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .recentOrders on an `any` value.","line":207,"column":36,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":207,"endColumn":48},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .map on an `any` value.","line":207,"column":56,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":207,"endColumn":59},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":208,"column":15,"nodeType":"Property","messageId":"anyAssignment","endLine":208,"endColumn":39},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .orderNo on an `any` value.","line":208,"column":26,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":208,"endColumn":33},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":209,"column":15,"nodeType":"Property","messageId":"anyAssignment","endLine":209,"endColumn":41},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .customer on an `any` value.","line":209,"column":27,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":209,"endColumn":35},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":210,"column":15,"nodeType":"Property","messageId":"anyAssignment","endLine":210,"endColumn":43},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .product on an `any` value.","line":210,"column":30,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":210,"endColumn":37},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .status on an `any` value.","line":212,"column":27,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":212,"endColumn":33},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .status on an `any` value.","line":212,"column":50,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":212,"endColumn":56},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":213,"column":15,"nodeType":"Property","messageId":"anyAssignment","endLine":213,"endColumn":39},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .quantity on an `any` value.","line":213,"column":26,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":213,"endColumn":34},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":214,"column":15,"nodeType":"Property","messageId":"anyAssignment","endLine":214,"endColumn":87},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .status on an `any` value.","line":214,"column":31,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":214,"endColumn":37},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .quantity on an `any` value.","line":214,"column":48,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":214,"endColumn":56},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .quantity on an `any` value.","line":214,"column":72,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":214,"endColumn":80},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .status on an `any` value.","line":215,"column":25,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":215,"endColumn":31},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .status on an `any` value.","line":215,"column":56,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":215,"endColumn":62},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":219,"column":13,"nodeType":"Property","messageId":"anyAssignment","endLine":219,"endColumn":53},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .efficiencyTrend on an `any` value.","line":219,"column":32,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":219,"endColumn":47},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .inkStatus on an `any` value.","line":221,"column":21,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":221,"endColumn":30},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":226,"column":56,"nodeType":"Property","messageId":"anyAssignment","endLine":226,"endColumn":87},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .inkStatus on an `any` value.","line":226,"column":65,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":226,"endColumn":74},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .dieStatus on an `any` value.","line":232,"column":21,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":232,"endColumn":30},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":237,"column":53,"nodeType":"Property","messageId":"anyAssignment","endLine":237,"endColumn":79},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .dieStatus on an `any` value.","line":237,"column":62,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":237,"endColumn":71},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":244,"column":13,"nodeType":"Property","messageId":"anyAssignment","endLine":244,"endColumn":91},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .personnel on an `any` value.","line":244,"column":28,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":244,"endColumn":37},{"ruleId":"react-hooks/set-state-in-effect","severity":1,"message":"Error: Calling setState synchronously within an effect can trigger cascading renders\n\nEffects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following:\n* Update external systems with the latest state from React.\n* Subscribe for updates from some external system, calling setState in a callback function when external state changes.\n\nCalling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect).\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\dashboard\\production\\page.tsx:253:5\n 251 | };\n 252 | fetchData();\n> 253 | setCurrentTime(new Date());\n | ^^^^^^^^^^^^^^ Avoid calling setState() directly within an effect\n 254 | const timer1 = setInterval(fetchData, 60000);\n 255 | const timer2 = setInterval(() => setCurrentTime(new Date()), 1000);\n 256 | return () => {","line":253,"column":5,"nodeType":null,"endLine":253,"endColumn":19},{"ruleId":"react-hooks/exhaustive-deps","severity":1,"message":"React Hook useEffect has a missing dependency: 't'. Either include it or remove the dependency array.","line":260,"column":6,"nodeType":"ArrayExpression","endLine":260,"endColumn":8,"suggestions":[{"desc":"Update the dependencies array to be: [t]","fix":{"range":[8345,8347],"text":"[t]"}}]}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":41,"fixableErrorCount":0,"fixableWarningCount":0,"source":"'use client';\r\n\r\nimport { authFetch } from '@/lib/auth-fetch';\r\nimport { useEffect, useState, useRef } from 'react';\r\nimport { MainLayout } from '@/components/layout';\r\nimport { useCompanyName } from '@/hooks/useCompanyName';\r\nimport GlassGauge from '@/components/GlassGauge';\r\nimport {\r\n TrendingUp,\r\n TrendingDown,\r\n CheckCircle,\r\n AlertTriangle,\r\n PlayCircle,\r\n BarChart3,\r\n Activity,\r\n Users,\r\n Package,\r\n Settings,\r\n Maximize,\r\n Minimize,\r\n Target,\r\n} from 'lucide-react';\r\nimport { useTranslations, useLocale } from 'next-intl';\r\n\r\ninterface ProductionData {\r\n overview: {\r\n totalOrders: number;\r\n activeOrders: number;\r\n completedToday: number;\r\n efficiency: number;\r\n oee: number;\r\n qualityRate: number;\r\n };\r\n equipmentStatus: {\r\n id: string;\r\n name: string;\r\n type: string;\r\n status: string;\r\n efficiency: number;\r\n currentOrder: string;\r\n operator: string;\r\n runtime: number;\r\n }[];\r\n productionProgress: {\r\n orderNo: string;\r\n customer: string;\r\n productName: string;\r\n process: string;\r\n progress: number;\r\n planQty: number;\r\n completedQty: number;\r\n status: string;\r\n priority: string;\r\n estimatedComplete: string;\r\n }[];\r\n efficiencyTrend: { time: string; efficiency: number; target: number }[];\r\n alerts: { id: string; type: string; message: string; severity: string; timestamp: string }[];\r\n staffStatus: { total: number; onDuty: number; onLeave: number; attendance: number };\r\n}\r\n\r\nconst STATUS_MAP: Record<string, { labelKey: string; className: string }> = {\r\n running: {\r\n labelKey: 'running',\r\n className: 'bg-green-500/20 text-green-300 border border-green-500/30',\r\n },\r\n idle: {\r\n labelKey: 'standby',\r\n className: 'bg-amber-500/20 text-amber-300 border border-amber-500/30',\r\n },\r\n maintenance: {\r\n labelKey: 'underMaintenance',\r\n className: 'bg-blue-500/20 text-blue-300 border border-blue-500/30',\r\n },\r\n error: { labelKey: 'fault', className: 'bg-red-500/20 text-red-300 border border-red-500/30' },\r\n};\r\n\r\nconst PRIORITY_MAP: Record<string, { labelKey: string; className: string }> = {\r\n high: { labelKey: 'high', className: 'bg-red-500/20 text-red-300 border border-red-500/30' },\r\n medium: {\r\n labelKey: 'medium',\r\n className: 'bg-amber-500/20 text-amber-300 border border-amber-500/30',\r\n },\r\n low: { labelKey: 'low', className: 'bg-blue-500/20 text-blue-300 border border-blue-500/30' },\r\n};\r\n\r\nfunction AutoScroll({\r\n children,\r\n maxHeight = 320,\r\n _speed = 50,\r\n}: {\r\n children: React.ReactNode;\r\n maxHeight?: number;\r\n _speed?: number;\r\n}) {\r\n const containerRef = useRef<HTMLDivElement>(null);\r\n const innerRef = useRef<HTMLDivElement>(null);\r\n const [isPaused, setIsPaused] = useState(false);\r\n\r\n useEffect(() => {\r\n const container = containerRef.current;\r\n const inner = innerRef.current;\r\n if (!container || !inner) return;\r\n\r\n let animId: number;\r\n let scrollPos = 0;\r\n const contentHeight = inner.scrollHeight / 2;\r\n const viewportHeight = maxHeight;\r\n\r\n if (contentHeight <= viewportHeight) return;\r\n\r\n const step = () => {\r\n if (!isPaused) {\r\n scrollPos += 0.3;\r\n if (scrollPos >= contentHeight) {\r\n scrollPos = 0;\r\n }\r\n container.scrollTop = scrollPos;\r\n }\r\n animId = requestAnimationFrame(step);\r\n };\r\n\r\n animId = requestAnimationFrame(step);\r\n return () => cancelAnimationFrame(animId);\r\n }, [isPaused, maxHeight, children]);\r\n\r\n return (\r\n <div\r\n ref={containerRef}\r\n className=\"overflow-hidden\"\r\n style={{ maxHeight: `${maxHeight}px` }}\r\n onMouseEnter={() => setIsPaused(true)}\r\n onMouseLeave={() => setIsPaused(false)}\r\n >\r\n <div ref={innerRef}>\r\n {children}\r\n {children}\r\n </div>\r\n </div>\r\n );\r\n}\r\n\r\nexport default function ProductionDashboard() {\r\n // 翻译钩子\r\n const t = useTranslations('Dashboard');\r\n const tc = useTranslations('Common');\r\n const locale = useLocale();\r\n\r\n const { companyName } = useCompanyName();\r\n const [data, setData] = useState<ProductionData>({\r\n overview: {\r\n totalOrders: 0,\r\n activeOrders: 0,\r\n completedToday: 0,\r\n efficiency: 0,\r\n oee: 0,\r\n qualityRate: 0,\r\n },\r\n equipmentStatus: [],\r\n productionProgress: [],\r\n efficiencyTrend: [],\r\n alerts: [],\r\n staffStatus: { total: 0, onDuty: 0, onLeave: 0, attendance: 0 },\r\n });\r\n const [loading, setLoading] = useState(true);\r\n const [currentTime, setCurrentTime] = useState<Date | null>(null);\r\n const [isFullscreen, setIsFullscreen] = useState(false);\r\n const dashboardRef = useRef<HTMLDivElement>(null);\r\n const [efficiencyHistory, setEfficiencyHistory] = useState<number[]>([88, 90, 92, 91, 93, 95]);\r\n const [oeeHistory, setOeeHistory] = useState<number[]>([85, 87, 88, 86, 89, 90]);\r\n const [qualityHistory, setQualityHistory] = useState<number[]>([94, 95, 96, 95, 97, 96]);\r\n\r\n useEffect(() => {\r\n const t = setInterval(() => {\r\n setEfficiencyHistory((h) => {\r\n const val = Math.max(70, Math.min(100, h[h.length - 1] + (Math.random() * 6 - 3)));\r\n return h.length > 20 ? [...h.slice(1), Math.round(val)] : [...h, Math.round(val)];\r\n });\r\n setOeeHistory((h) => {\r\n const val = Math.max(60, Math.min(100, h[h.length - 1] + (Math.random() * 6 - 3)));\r\n return h.length > 20 ? [...h.slice(1), Math.round(val)] : [...h, Math.round(val)];\r\n });\r\n setQualityHistory((h) => {\r\n const val = Math.max(80, Math.min(100, h[h.length - 1] + (Math.random() * 4 - 2)));\r\n return h.length > 20 ? [...h.slice(1), Math.round(val)] : [...h, Math.round(val)];\r\n });\r\n }, 2500);\r\n return () => clearInterval(t);\r\n }, []);\r\n\r\n useEffect(() => {\r\n const fetchData = async () => {\r\n try {\r\n const res = await authFetch('/api/dashboard/production');\r\n const result = await res.json();\r\n if (result.success && result.data) {\r\n const d = result.data;\r\n setData({\r\n overview: d.overview || {\r\n totalOrders: 0,\r\n activeOrders: 0,\r\n completedToday: 0,\r\n efficiency: 0,\r\n oee: 0,\r\n qualityRate: 0,\r\n },\r\n equipmentStatus: d.equipmentStatus || [],\r\n productionProgress: (d.recentOrders || []).map((o: Loose) => ({\r\n orderNo: o.orderNo || '',\r\n customer: o.customer || '',\r\n productName: o.product || '',\r\n process: '',\r\n progress: o.status === 3 ? 100 : o.status === 2 ? 50 : 0,\r\n planQty: o.quantity || 0,\r\n completedQty: o.status === 3 ? o.quantity : Math.round(o.quantity * 0.5),\r\n status: o.status === 3 ? 'completed' : o.status === 2 ? 'producing' : 'pending',\r\n priority: 'medium',\r\n estimatedComplete: '',\r\n })),\r\n efficiencyTrend: d.efficiencyTrend || [],\r\n alerts: [\r\n ...(d.inkStatus?.expiringSoon > 0\r\n ? [\r\n {\r\n id: 'ink-exp',\r\n type: 'material',\r\n message: t('inkExpiringAlert', { count: d.inkStatus.expiringSoon }),\r\n severity: 'medium',\r\n timestamp: '',\r\n },\r\n ]\r\n : []),\r\n ...(d.dieStatus?.warning > 0\r\n ? [\r\n {\r\n id: 'die-warn',\r\n type: 'equipment',\r\n message: t('dieUsageAlert', { count: d.dieStatus.warning }),\r\n severity: 'medium',\r\n timestamp: '',\r\n },\r\n ]\r\n : []),\r\n ],\r\n staffStatus: d.personnel || { total: 0, onDuty: 0, onLeave: 0, attendance: 0 },\r\n });\r\n }\r\n } catch {\r\n } finally {\r\n setLoading(false);\r\n }\r\n };\r\n fetchData();\r\n setCurrentTime(new Date());\r\n const timer1 = setInterval(fetchData, 60000);\r\n const timer2 = setInterval(() => setCurrentTime(new Date()), 1000);\r\n return () => {\r\n clearInterval(timer1);\r\n clearInterval(timer2);\r\n };\r\n }, []);\r\n\r\n const toggleFullscreen = () => {\r\n if (!document.fullscreenElement) {\r\n dashboardRef.current?.requestFullscreen();\r\n setIsFullscreen(true);\r\n } else {\r\n document.exitFullscreen();\r\n setIsFullscreen(false);\r\n }\r\n };\r\n\r\n useEffect(() => {\r\n const handleFullscreenChange = () => setIsFullscreen(!!document.fullscreenElement);\r\n document.addEventListener('fullscreenchange', handleFullscreenChange);\r\n return () => document.removeEventListener('fullscreenchange', handleFullscreenChange);\r\n }, []);\r\n\r\n const formatTime = (date: Date) =>\r\n date.toLocaleString(locale, {\r\n year: 'numeric',\r\n month: '2-digit',\r\n day: '2-digit',\r\n hour: '2-digit',\r\n minute: '2-digit',\r\n second: '2-digit',\r\n hour12: false,\r\n });\r\n\r\n return (\r\n <MainLayout>\r\n <div\r\n ref={dashboardRef}\r\n className=\"min-h-screen text-white p-4 relative overflow-hidden\"\r\n style={{ background: 'linear-gradient(135deg, #091637 0%, #010205 100%)' }}\r\n >\r\n <div className=\"absolute inset-0 overflow-hidden pointer-events-none\">\r\n <div className=\"absolute top-0 left-0 w-96 h-96 bg-cyan-500/5 rounded-full blur-3xl animate-blob\" />\r\n <div className=\"absolute bottom-0 right-0 w-96 h-96 bg-blue-500/5 rounded-full blur-3xl animate-blob animation-delay-2000\" />\r\n <div className=\"absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-[600px] h-[600px] bg-indigo-500/5 rounded-full blur-3xl animate-blob animation-delay-4000\" />\r\n </div>\r\n <div className=\"absolute inset-0 tech-grid-bg pointer-events-none\" />\r\n\r\n <div className=\"relative z-10 flex flex-col items-center mb-3\">\r\n <div className=\"tech-title-wrapper\">\r\n <div className=\"tech-title-row\">\r\n <div className=\"tech-title-line-left\" />\r\n <div>\r\n <h1 className=\"text-lg font-bold tracking-wider bg-gradient-to-r from-cyan-300 via-blue-400 to-cyan-300 bg-clip-text text-transparent\">\r\n {companyName}\r\n </h1>\r\n <p className=\"text-[10px] text-white/50\">{t('productionSubtitle')}</p>\r\n </div>\r\n <div className=\"tech-title-line-right\" />\r\n </div>\r\n <div className=\"tech-title-bottom-line\" />\r\n </div>\r\n <div className=\"flex items-center gap-3 mt-1.5\">\r\n <div className=\"text-sm font-mono font-bold text-cyan-400\">\r\n {currentTime && formatTime(currentTime)}\r\n </div>\r\n <button\r\n onClick={toggleFullscreen}\r\n className=\"p-1.5 rounded-lg bg-white/10 hover:bg-white/20 transition-colors\"\r\n title={isFullscreen ? t('exitFullscreen') : t('fullscreen')}\r\n >\r\n {isFullscreen ? (\r\n <Minimize className=\"h-3.5 w-3.5 text-cyan-400\" />\r\n ) : (\r\n <Maximize className=\"h-3.5 w-3.5 text-cyan-400\" />\r\n )}\r\n </button>\r\n <div className=\"px-2 py-0.5 rounded-full bg-cyan-500/20 border border-cyan-500/30 text-[10px] text-cyan-300\">\r\n {loading ? tc('loading') : '● ' + t('realtime')}\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <div className=\"relative z-10 grid grid-cols-2 md:grid-cols-3 lg:grid-cols-6 gap-4 mb-6\">\r\n {[\r\n {\r\n title: t('todayWorkOrders'),\r\n value: data.overview.totalOrders,\r\n icon: Package,\r\n color: 'from-cyan-500 to-blue-500',\r\n },\r\n {\r\n title: tc('inProgress'),\r\n value: data.overview.activeOrders,\r\n icon: PlayCircle,\r\n color: 'from-amber-500 to-orange-500',\r\n },\r\n {\r\n title: tc('completed'),\r\n value: data.overview.completedToday,\r\n icon: CheckCircle,\r\n color: 'from-green-500 to-emerald-500',\r\n },\r\n {\r\n title: t('productionEfficiency'),\r\n value: `${data.overview.efficiency}%`,\r\n icon: TrendingUp,\r\n color: 'from-blue-500 to-indigo-500',\r\n },\r\n {\r\n title: 'OEE',\r\n value: `${data.overview.oee}%`,\r\n icon: BarChart3,\r\n color: 'from-purple-500 to-pink-500',\r\n },\r\n {\r\n title: t('qualityYieldRate'),\r\n value: `${data.overview.qualityRate}%`,\r\n icon: Activity,\r\n color: 'from-emerald-500 to-teal-500',\r\n },\r\n ].map((s, i) => (\r\n <div key={i} className={`tech-card tech-glow tech-card-delay-${i + 1} p-4`}>\r\n <div className=\"flex items-center justify-between\">\r\n <div>\r\n <p className=\"text-xs text-white/60\">{s.title}</p>\r\n <p className=\"text-xl font-bold mt-1 bg-gradient-to-r from-cyan-300 to-blue-300 bg-clip-text text-transparent\">\r\n {s.value}\r\n </p>\r\n </div>\r\n <div className={`p-2 rounded-lg bg-gradient-to-br ${s.color}`}>\r\n <s.icon className=\"h-5 w-5 text-white\" />\r\n </div>\r\n </div>\r\n </div>\r\n ))}\r\n </div>\r\n\r\n <div className=\"relative z-10 mb-6\">\r\n <div className=\"tech-card tech-glow p-0\">\r\n <div className=\"px-4 py-2 border-b border-white/10 flex items-center gap-2 bg-white/5\">\r\n <div className=\"w-1 h-4 rounded-full bg-gradient-to-b from-cyan-400 to-blue-600\" />\r\n <Target className=\"h-4 w-4 text-cyan-400\" />\r\n <span className=\"text-sm font-medium text-white/80\">{tc('coreMetrics')}</span>\r\n </div>\r\n <div className=\"p-4\">\r\n <div className=\"grid grid-cols-3 gap-2\">\r\n <div className=\"flex flex-col items-center relative\">\r\n <div\r\n className=\"absolute inset-0 pointer-events-none\"\r\n style={{\r\n background:\r\n 'radial-gradient(ellipse at 50% 15%, rgba(6,182,212,0.08) 0%, transparent 70%)',\r\n }}\r\n />\r\n <GlassGauge\r\n value={data.overview.efficiency}\r\n label={t('productionEfficiency')}\r\n unit=\"%\"\r\n colorArc=\"#06b6d4\"\r\n colorDanger=\"#ff5555\"\r\n colorSafe=\"#06b6d4\"\r\n dangerStart={85}\r\n showCurve\r\n history={efficiencyHistory}\r\n size={130}\r\n />\r\n </div>\r\n <div className=\"flex flex-col items-center relative\">\r\n <div\r\n className=\"absolute inset-0 pointer-events-none\"\r\n style={{\r\n background:\r\n 'radial-gradient(ellipse at 50% 15%, rgba(139,92,246,0.08) 0%, transparent 70%)',\r\n }}\r\n />\r\n <GlassGauge\r\n value={data.overview.oee}\r\n label=\"OEE\"\r\n unit=\"%\"\r\n colorArc=\"#8b5cf6\"\r\n colorDanger=\"#ff5555\"\r\n colorSafe=\"#8b5cf6\"\r\n dangerStart={80}\r\n showCurve\r\n history={oeeHistory}\r\n size={130}\r\n />\r\n </div>\r\n <div className=\"flex flex-col items-center relative\">\r\n <div\r\n className=\"absolute inset-0 pointer-events-none\"\r\n style={{\r\n background:\r\n 'radial-gradient(ellipse at 50% 15%, rgba(16,185,129,0.08) 0%, transparent 70%)',\r\n }}\r\n />\r\n <GlassGauge\r\n value={data.overview.qualityRate}\r\n label={t('qualityYieldRate')}\r\n unit=\"%\"\r\n colorArc=\"#10b981\"\r\n colorDanger=\"#ff5555\"\r\n colorSafe=\"#10b981\"\r\n dangerStart={90}\r\n showCurve\r\n history={qualityHistory}\r\n size={130}\r\n />\r\n </div>\r\n </div>\r\n <div className=\"mt-4 grid grid-cols-2 gap-3\">\r\n <div className=\"p-3 rounded-lg bg-white/5 border border-white/10 text-center\">\r\n <div className=\"flex items-center justify-center gap-1.5 mb-1\">\r\n <Users className=\"h-3 w-3 text-cyan-400\" />\r\n <p className=\"text-xs text-white/40\">{t('attendance')}</p>\r\n </div>\r\n <p className=\"text-lg font-bold text-cyan-300\">\r\n {data.staffStatus.onDuty}\r\n <span className=\"text-white/30 text-sm\">/{data.staffStatus.total}</span>\r\n </p>\r\n </div>\r\n <div className=\"p-3 rounded-lg bg-white/5 border border-white/10 text-center\">\r\n <div className=\"flex items-center justify-center gap-1.5 mb-1\">\r\n <Activity className=\"h-3 w-3 text-green-400\" />\r\n <p className=\"text-xs text-white/40\">{t('attendanceRate')}</p>\r\n </div>\r\n <p className=\"text-lg font-bold text-green-300\">\r\n {data.staffStatus.attendance}\r\n <span className=\"text-white/30 text-sm\">%</span>\r\n </p>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <div className=\"relative z-10 grid grid-cols-1 lg:grid-cols-3 gap-6 mb-6\">\r\n <div className=\"tech-card tech-glow p-0\">\r\n <div className=\"px-4 py-2 border-b border-white/10 flex items-center gap-2 bg-white/5\">\r\n <div className=\"w-1 h-4 rounded-full bg-gradient-to-b from-cyan-400 to-blue-600\" />\r\n <Settings className=\"h-4 w-4 text-cyan-400\" />\r\n <span className=\"text-sm font-medium text-white/80\">{t('equipmentMonitor')}</span>\r\n </div>\r\n <AutoScroll maxHeight={320}>\r\n <div className=\"p-4\">\r\n {data.equipmentStatus.length === 0 ? (\r\n <p className=\"text-white/40 text-center py-8\">{tc('noData')}</p>\r\n ) : (\r\n <div className=\"space-y-3\">\r\n {data.equipmentStatus.map((eq) => {\r\n const cfg = STATUS_MAP[eq.status] || STATUS_MAP.idle;\r\n return (\r\n <div\r\n key={eq.id}\r\n className=\"p-3 rounded-lg bg-white/5 border border-white/10 hover:bg-white/10 transition-colors\"\r\n >\r\n <div className=\"flex items-start justify-between mb-2\">\r\n <div>\r\n <h4 className=\"text-xs font-medium text-white/80\">{eq.name}</h4>\r\n <p className=\"text-[10px] text-white/40 mt-0.5\">\r\n {t('currentWorkOrder')}: {eq.currentOrder}\r\n </p>\r\n </div>\r\n <span className={`px-2 py-0.5 rounded text-[10px] ${cfg.className}`}>\r\n {tc(cfg.labelKey)}\r\n </span>\r\n </div>\r\n <div className=\"space-y-1\">\r\n <div className=\"flex items-center justify-between text-xs\">\r\n <span className=\"text-white/40\">{t('productionEfficiency')}</span>\r\n <span className=\"font-medium\">{eq.efficiency}%</span>\r\n </div>\r\n <div className=\"bg-white/10 rounded-full h-1.5 overflow-hidden\">\r\n <div\r\n className={`h-full rounded-full transition-all ${eq.efficiency > 80 ? 'bg-green-400' : eq.efficiency > 60 ? 'bg-yellow-400' : 'bg-red-400'}`}\r\n style={{ width: `${eq.efficiency}%` }}\r\n />\r\n </div>\r\n <div className=\"flex items-center justify-between text-[10px] text-white/30\">\r\n <span>\r\n {t('operator')}: {eq.operator}\r\n </span>\r\n <span>\r\n {t('runtime')}: {Math.floor(eq.runtime / 60)}h{eq.runtime % 60}m\r\n </span>\r\n </div>\r\n </div>\r\n </div>\r\n );\r\n })}\r\n </div>\r\n )}\r\n </div>\r\n </AutoScroll>\r\n </div>\r\n\r\n <div className=\"tech-card tech-glow p-0\">\r\n <div className=\"px-4 py-2 border-b border-white/10 flex items-center gap-2 bg-white/5\">\r\n <div className=\"w-1 h-4 rounded-full bg-gradient-to-b from-cyan-400 to-blue-600\" />\r\n <Package className=\"h-4 w-4 text-cyan-400\" />\r\n <span className=\"text-sm font-medium text-white/80\">\r\n {t('workOrderProductionProgress')}\r\n </span>\r\n </div>\r\n <AutoScroll maxHeight={320}>\r\n <div className=\"p-4\">\r\n {data.productionProgress.length === 0 ? (\r\n <p className=\"text-white/40 text-center py-8\">{tc('noData')}</p>\r\n ) : (\r\n <div className=\"space-y-3\">\r\n {data.productionProgress.map((order) => {\r\n const priCfg = PRIORITY_MAP[order.priority] || PRIORITY_MAP.medium;\r\n return (\r\n <div\r\n key={order.orderNo}\r\n className=\"p-3 rounded-lg bg-white/5 border border-white/10\"\r\n >\r\n <div className=\"flex items-center justify-between mb-2\">\r\n <div className=\"flex items-center gap-2\">\r\n <span className=\"text-xs font-mono text-cyan-300\">\r\n {order.orderNo}\r\n </span>\r\n <span\r\n className={`px-1.5 py-0.5 rounded text-[10px] ${priCfg.className}`}\r\n >\r\n {tc(priCfg.labelKey)}\r\n </span>\r\n {order.status === 'completed' && (\r\n <span className=\"px-1.5 py-0.5 rounded text-[10px] bg-green-500/20 text-green-300 border border-green-500/30\">\r\n {tc('completed')}\r\n </span>\r\n )}\r\n </div>\r\n <span className=\"text-[10px] text-white/40\">\r\n {tc('estimated')}: {order.estimatedComplete || '-'}\r\n </span>\r\n </div>\r\n <div className=\"grid grid-cols-2 gap-2 text-xs mb-2\">\r\n <div>\r\n <span className=\"text-white/40\">{tc('customer')}: </span>\r\n <span className=\"text-white/70\">{order.customer}</span>\r\n </div>\r\n <div>\r\n <span className=\"text-white/40\">{tc('product')}: </span>\r\n <span className=\"text-white/70\">{order.productName}</span>\r\n </div>\r\n <div>\r\n <span className=\"text-white/40\">{tc('process')}: </span>\r\n <span className=\"text-white/70\">{order.process || '-'}</span>\r\n </div>\r\n <div>\r\n <span className=\"text-white/40\">{tc('planQty')}: </span>\r\n <span className=\"text-white/70\">\r\n {(order.completedQty ?? 0).toLocaleString()} /{' '}\r\n {(order.planQty ?? 0).toLocaleString()}\r\n </span>\r\n </div>\r\n </div>\r\n <div className=\"space-y-1\">\r\n <div className=\"flex items-center justify-between text-xs\">\r\n <span className=\"text-white/40\">{tc('progress')}</span>\r\n <span className=\"font-medium\">{order.progress}%</span>\r\n </div>\r\n <div className=\"bg-white/10 rounded-full h-2 overflow-hidden\">\r\n <div\r\n className={`h-full rounded-full transition-all ${order.progress >= 100 ? 'bg-green-400' : order.progress >= 50 ? 'bg-cyan-400' : 'bg-amber-400'}`}\r\n style={{ width: `${order.progress}%` }}\r\n />\r\n </div>\r\n </div>\r\n </div>\r\n );\r\n })}\r\n </div>\r\n )}\r\n </div>\r\n </AutoScroll>\r\n </div>\r\n\r\n <div className=\"tech-card tech-glow p-0\">\r\n <div className=\"px-4 py-2 border-b border-white/10 flex items-center gap-2 bg-white/5\">\r\n <div className=\"w-1 h-4 rounded-full bg-gradient-to-b from-red-400 to-orange-500\" />\r\n <AlertTriangle className=\"h-4 w-4 text-red-400\" />\r\n <span className=\"text-sm font-medium text-white/80\">{t('alertNotifications')}</span>\r\n </div>\r\n <AutoScroll maxHeight={320}>\r\n <div className=\"p-4\">\r\n {data.alerts.length === 0 ? (\r\n <p className=\"text-white/40 text-center py-8\">{t('noAlerts')}</p>\r\n ) : (\r\n <div className=\"space-y-3\">\r\n {data.alerts.map((alert) => {\r\n const severityClass =\r\n alert.severity === 'high'\r\n ? 'border-red-500/30 bg-red-500/5'\r\n : alert.severity === 'medium'\r\n ? 'border-amber-500/30 bg-amber-500/5'\r\n : 'border-blue-500/30 bg-blue-500/5';\r\n const iconColor =\r\n alert.severity === 'high'\r\n ? 'text-red-400'\r\n : alert.severity === 'medium'\r\n ? 'text-amber-400'\r\n : 'text-blue-400';\r\n const AlertIcon =\r\n alert.type === 'efficiency'\r\n ? TrendingDown\r\n : alert.type === 'quality'\r\n ? AlertTriangle\r\n : alert.type === 'equipment'\r\n ? Settings\r\n : Package;\r\n return (\r\n <div key={alert.id} className={`p-3 rounded-lg border ${severityClass}`}>\r\n <div className=\"flex items-start gap-2\">\r\n <AlertIcon className={`h-4 w-4 mt-0.5 ${iconColor}`} />\r\n <div className=\"flex-1\">\r\n <p className=\"text-xs text-white/80\">{alert.message}</p>\r\n {alert.timestamp && (\r\n <p className=\"text-[10px] text-white/30 mt-1\">{alert.timestamp}</p>\r\n )}\r\n </div>\r\n </div>\r\n </div>\r\n );\r\n })}\r\n </div>\r\n )}\r\n </div>\r\n </AutoScroll>\r\n </div>\r\n </div>\r\n </div>\r\n </MainLayout>\r\n );\r\n}\r\n","usedDeprecatedRules":[]},{"filePath":"D:\\dcprint\\erp-project\\src\\app\\[locale]\\dashboard\\quality\\page.tsx","messages":[{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":285,"column":15,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":285,"endColumn":40},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":286,"column":20,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":286,"endColumn":27},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":286,"column":38,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":286,"endColumn":42},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction<QualityData>`.","line":286,"column":52,"nodeType":"MemberExpression","messageId":"unsafeArgument","endLine":286,"endColumn":63},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":286,"column":59,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":286,"endColumn":63},{"ruleId":"react-hooks/set-state-in-effect","severity":1,"message":"Error: Calling setState synchronously within an effect can trigger cascading renders\n\nEffects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following:\n* Update external systems with the latest state from React.\n* Subscribe for updates from some external system, calling setState in a callback function when external state changes.\n\nCalling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect).\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\dashboard\\quality\\page.tsx:293:5\n 291 | };\n 292 | fetchData();\n> 293 | setCurrentTime(new Date());\n | ^^^^^^^^^^^^^^ Avoid calling setState() directly within an effect\n 294 | const timer1 = setInterval(fetchData, 60000);\n 295 | const timer2 = setInterval(() => setCurrentTime(new Date()), 1000);\n 296 | return () => {","line":293,"column":5,"nodeType":null,"endLine":293,"endColumn":19},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"质量监控中心\"。建议使用: {tc('text_89iv2r')}","line":366,"column":85,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":368,"endColumn":15},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"实时\"。建议使用: {tc('text_g55k')}","line":417,"column":70,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":419,"endColumn":15}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":8,"fixableErrorCount":0,"fixableWarningCount":0,"source":"'use client';\r\n\r\nimport { useEffect, useState, useRef } from 'react';\r\nimport { MainLayout } from '@/components/layout';\r\nimport { useCompanyName } from '@/hooks/useCompanyName';\r\nimport GlassKnob from '@/components/GlassKnob';\r\nimport GlassGauge from '@/components/GlassGauge';\r\nimport {\r\n Shield,\r\n CheckCircle,\r\n AlertTriangle,\r\n TrendingDown,\r\n BarChart3,\r\n Maximize,\r\n Minimize,\r\n Activity,\r\n Target,\r\n AlertCircle,\r\n ClipboardCheck,\r\n Calendar,\r\n Factory,\r\n ShoppingCart as _ShoppingCart,\r\n Truck as _Truck,\r\n Package as _Package,\r\n} from 'lucide-react';\r\nimport { useTranslations, useLocale } from 'next-intl';\r\n\r\ninterface QualityData {\r\n overview: {\r\n totalInspections: number;\r\n passRate: number;\r\n todayInspections: number;\r\n todayPassRate: number;\r\n pendingInspections: number;\r\n defectRate: number;\r\n passedInspections: number;\r\n failedInspections: number;\r\n };\r\n byType: { inspect_type: string; total: number; passed: number }[];\r\n defectTrend: { date: string; total: number; defects: number }[];\r\n topDefects: { defect_type: string; count: number }[];\r\n recentInspections: {\r\n id: number;\r\n inspect_no: string;\r\n inspect_type: string;\r\n inspect_result: string;\r\n inspector: string;\r\n inspect_time: string;\r\n remark: string;\r\n }[];\r\n processQuality: {\r\n product_name: string;\r\n burdening_status: number;\r\n inspect_count: number;\r\n passed: number;\r\n }[];\r\n}\r\n\r\n// 环形图组件\r\nfunction DonutChart({\r\n percentage,\r\n color,\r\n size = 120,\r\n}: {\r\n percentage: number;\r\n color: string;\r\n size?: number;\r\n}) {\r\n const tc = useTranslations('Common');\r\n const radius = 45;\r\n const circumference = 2 * Math.PI * radius;\r\n const offset = circumference - (percentage / 100) * circumference;\r\n\r\n return (\r\n <svg width={size} height={size} viewBox=\"0 0 120 120\">\r\n <circle\r\n cx=\"60\"\r\n cy=\"60\"\r\n r={radius}\r\n fill=\"none\"\r\n stroke=\"rgba(255,255,255,0.1)\"\r\n strokeWidth=\"12\"\r\n />\r\n <circle\r\n cx=\"60\"\r\n cy=\"60\"\r\n r={radius}\r\n fill=\"none\"\r\n stroke={color}\r\n strokeWidth=\"12\"\r\n strokeDasharray={circumference}\r\n strokeDashoffset={offset}\r\n strokeLinecap=\"round\"\r\n transform=\"rotate(-90 60 60)\"\r\n className=\"transition-all duration-1000 ease-out\"\r\n />\r\n <text x=\"60\" y=\"55\" textAnchor=\"middle\" fill=\"white\" fontSize=\"20\" fontWeight=\"bold\">\r\n {percentage.toFixed(1)}%\r\n </text>\r\n <text x=\"60\" y=\"75\" textAnchor=\"middle\" fill=\"rgba(255,255,255,0.6)\" fontSize=\"10\">\r\n {tc('qualityPassRate')}\r\n </text>\r\n </svg>\r\n );\r\n}\r\n\r\n// 折线图组件\r\nfunction LineChart({\r\n data,\r\n width = 400,\r\n height = 150,\r\n}: {\r\n data: { date: string; total: number; defects: number }[];\r\n width?: number;\r\n height?: number;\r\n}) {\r\n const tc = useTranslations('Common');\r\n if (data.length === 0) return null;\r\n\r\n const padding = { top: 20, right: 20, bottom: 30, left: 40 };\r\n const chartWidth = width - padding.left - padding.right;\r\n const chartHeight = height - padding.top - padding.bottom;\r\n\r\n const maxTotal = Math.max(...data.map((d) => d.total), 1);\r\n const maxDefects = Math.max(...data.map((d) => d.defects), 1);\r\n const maxVal = Math.max(maxTotal, maxDefects);\r\n\r\n const getX = (i: number) => padding.left + (i / (data.length - 1 || 1)) * chartWidth;\r\n const getY = (val: number) => padding.top + chartHeight - (val / maxVal) * chartHeight;\r\n\r\n const totalPath = data\r\n .map((d, i) => `${i === 0 ? 'M' : 'L'} ${getX(i)} ${getY(d.total)}`)\r\n .join(' ');\r\n const defectsPath = data\r\n .map((d, i) => `${i === 0 ? 'M' : 'L'} ${getX(i)} ${getY(d.defects)}`)\r\n .join(' ');\r\n\r\n return (\r\n <svg width={width} height={height} className=\"w-full\">\r\n {[0, 0.25, 0.5, 0.75, 1].map((ratio, i) => (\r\n <g key={i}>\r\n <line\r\n x1={padding.left}\r\n y1={padding.top + chartHeight * ratio}\r\n x2={width - padding.right}\r\n y2={padding.top + chartHeight * ratio}\r\n stroke=\"rgba(255,255,255,0.1)\"\r\n strokeDasharray=\"4 4\"\r\n />\r\n <text\r\n x={padding.left - 5}\r\n y={padding.top + chartHeight * ratio + 4}\r\n textAnchor=\"end\"\r\n fill=\"rgba(255,255,255,0.5)\"\r\n fontSize=\"10\"\r\n >\r\n {Math.round(maxVal * (1 - ratio))}\r\n </text>\r\n </g>\r\n ))}\r\n\r\n {data\r\n .filter((_, i) => i % Math.ceil(data.length / 6) === 0 || i === data.length - 1)\r\n .map((d, i, _arr) => {\r\n const idx = data.indexOf(d);\r\n return (\r\n <text\r\n key={i}\r\n x={getX(idx)}\r\n y={height - 5}\r\n textAnchor=\"middle\"\r\n fill=\"rgba(255,255,255,0.5)\"\r\n fontSize=\"10\"\r\n >\r\n {d.date.substring(5)}\r\n </text>\r\n );\r\n })}\r\n\r\n <path d={totalPath} fill=\"none\" stroke=\"#06b6d4\" strokeWidth=\"2\" strokeLinecap=\"round\" />\r\n\r\n <path d={defectsPath} fill=\"none\" stroke=\"#ef4444\" strokeWidth=\"2\" strokeLinecap=\"round\" />\r\n\r\n {data.map((d, i) => (\r\n <g key={i}>\r\n <circle cx={getX(i)} cy={getY(d.total)} r=\"3\" fill=\"#06b6d4\" />\r\n <circle cx={getX(i)} cy={getY(d.defects)} r=\"3\" fill=\"#ef4444\" />\r\n </g>\r\n ))}\r\n\r\n <g transform={`translate(${width - 150}, 10)`}>\r\n <line x1=\"0\" y1=\"0\" x2=\"20\" y2=\"0\" stroke=\"#06b6d4\" strokeWidth=\"2\" />\r\n <text x=\"25\" y=\"4\" fill=\"rgba(255,255,255,0.7)\" fontSize=\"10\">\r\n {tc('totalInspections')}\r\n </text>\r\n <line x1=\"0\" y1=\"15\" x2=\"20\" y2=\"15\" stroke=\"#ef4444\" strokeWidth=\"2\" />\r\n <text x=\"25\" y=\"19\" fill=\"rgba(255,255,255,0.7)\" fontSize=\"10\">\r\n {tc('defectsCount')}\r\n </text>\r\n </g>\r\n </svg>\r\n );\r\n}\r\n\r\n// 水平条形图组件\r\nfunction HorizontalBarChart({ data }: { data: { defect_type: string; count: number }[] }) {\r\n const tc = useTranslations('Common');\r\n if (data.length === 0) return null;\r\n const maxCount = Math.max(...data.map((d) => d.count), 1);\r\n\r\n return (\r\n <div className=\"space-y-3\">\r\n {data.map((d, i) => (\r\n <div key={i} className=\"flex items-center gap-3\">\r\n <span className=\"text-xs w-20 text-right text-cyan-300 truncate\">\r\n {d.defect_type || tc('unknown')}\r\n </span>\r\n <div className=\"flex-1 bg-white/10 rounded-full h-5 relative overflow-hidden\">\r\n <div\r\n className=\"h-full rounded-full transition-all duration-500\"\r\n style={{\r\n width: `${(d.count / maxCount) * 100}%`,\r\n background: `linear-gradient(90deg, #06b6d4, #3b82f6)`,\r\n }}\r\n />\r\n <span className=\"absolute inset-0 flex items-center justify-center text-xs font-medium text-white\">\r\n {d.count}\r\n </span>\r\n </div>\r\n </div>\r\n ))}\r\n </div>\r\n );\r\n}\r\n\r\nexport default function QualityDashboard() {\r\n // 翻译钩子\r\n const t = useTranslations('Dashboard');\r\n const tc = useTranslations('Common');\r\n const locale = useLocale();\r\n\r\n const { companyName } = useCompanyName();\r\n const [data, setData] = useState<QualityData>({\r\n overview: {\r\n totalInspections: 0,\r\n passRate: 0,\r\n todayInspections: 0,\r\n todayPassRate: 0,\r\n pendingInspections: 0,\r\n defectRate: 0,\r\n passedInspections: 0,\r\n failedInspections: 0,\r\n },\r\n byType: [],\r\n defectTrend: [],\r\n topDefects: [],\r\n recentInspections: [],\r\n processQuality: [],\r\n });\r\n const [_loading, _setLoading] = useState(true);\r\n const [currentTime, setCurrentTime] = useState<Date | null>(null);\r\n const [isFullscreen, setIsFullscreen] = useState(false);\r\n const dashboardRef = useRef<HTMLDivElement>(null);\r\n const [passRateHistory, setPassRateHistory] = useState<number[]>([92, 94, 91, 95, 93, 96]);\r\n const [defectRateHistory, setDefectRateHistory] = useState<number[]>([8, 6, 9, 5, 7, 4]);\r\n\r\n useEffect(() => {\r\n const t = setInterval(() => {\r\n setPassRateHistory((h) => {\r\n const val = Math.max(70, Math.min(100, h[h.length - 1] + (Math.random() * 6 - 3)));\r\n return h.length > 20 ? [...h.slice(1), Math.round(val)] : [...h, Math.round(val)];\r\n });\r\n setDefectRateHistory((h) => {\r\n const val = Math.max(0, Math.min(30, h[h.length - 1] + (Math.random() * 6 - 3)));\r\n return h.length > 20 ? [...h.slice(1), Math.round(val)] : [...h, Math.round(val)];\r\n });\r\n }, 2000);\r\n return () => clearInterval(t);\r\n }, []);\r\n\r\n useEffect(() => {\r\n const fetchData = async () => {\r\n try {\r\n const res = await fetch('/api/dashboard/quality');\r\n const result = await res.json();\r\n if (result.success && result.data) setData(result.data);\r\n } catch {\r\n } finally {\r\n _setLoading(false);\r\n }\r\n };\r\n fetchData();\r\n setCurrentTime(new Date());\r\n const timer1 = setInterval(fetchData, 60000);\r\n const timer2 = setInterval(() => setCurrentTime(new Date()), 1000);\r\n return () => {\r\n clearInterval(timer1);\r\n clearInterval(timer2);\r\n };\r\n }, []);\r\n\r\n const toggleFullscreen = () => {\r\n if (!document.fullscreenElement) {\r\n dashboardRef.current?.requestFullscreen();\r\n setIsFullscreen(true);\r\n } else {\r\n document.exitFullscreen();\r\n setIsFullscreen(false);\r\n }\r\n };\r\n\r\n useEffect(() => {\r\n const handleFullscreenChange = () => {\r\n setIsFullscreen(!!document.fullscreenElement);\r\n };\r\n document.addEventListener('fullscreenchange', handleFullscreenChange);\r\n return () => document.removeEventListener('fullscreenchange', handleFullscreenChange);\r\n }, []);\r\n\r\n const _formatTime = (date: Date) => {\r\n return date.toLocaleString(locale, {\r\n year: 'numeric',\r\n month: '2-digit',\r\n day: '2-digit',\r\n hour: '2-digit',\r\n minute: '2-digit',\r\n second: '2-digit',\r\n hour12: false,\r\n });\r\n };\r\n\r\n return (\r\n <MainLayout>\r\n <div\r\n ref={dashboardRef}\r\n className=\"min-h-screen text-white p-4 relative overflow-hidden\"\r\n style={{\r\n background: 'linear-gradient(135deg, #091637 0%, #010205 100%)',\r\n }}\r\n >\r\n <div className=\"absolute inset-0 overflow-hidden pointer-events-none\">\r\n <div className=\"absolute top-0 left-0 w-96 h-96 bg-cyan-500/5 rounded-full blur-3xl animate-blob\" />\r\n <div className=\"absolute bottom-0 right-0 w-96 h-96 bg-blue-500/5 rounded-full blur-3xl animate-blob animation-delay-2000\" />\r\n <div className=\"absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-[600px] h-[600px] bg-indigo-500/5 rounded-full blur-3xl animate-blob animation-delay-4000\" />\r\n </div>\r\n <div className=\"absolute inset-0 tech-grid-bg pointer-events-none\" />\r\n\r\n <header\r\n className=\"relative z-20 flex items-center justify-between px-6 h-14 mb-6\"\r\n style={{ borderBottom: '1px solid rgba(192,208,224,0.15)' }}\r\n >\r\n <div className=\"flex items-center gap-3\">\r\n <div\r\n className=\"w-9 h-9 rounded-lg flex items-center justify-center\"\r\n style={{\r\n background: 'linear-gradient(135deg, #22d3ee, #3b82f6)',\r\n boxShadow: '0 0 20px rgba(34,211,238,0.25)',\r\n }}\r\n >\r\n <Factory className=\"h-5 w-5 text-white\" />\r\n </div>\r\n <div>\r\n <h1 className=\"text-base font-bold tracking-wide\" style={{ color: '#C0D0E0' }}>\r\n {companyName}\r\n </h1>\r\n <p className=\"text-[10px]\" style={{ color: 'rgba(192,208,224,0.4)' }}>\r\n 质量监控中心\r\n </p>\r\n </div>\r\n </div>\r\n\r\n <div className=\"flex items-center gap-6\">\r\n {[\r\n {\r\n icon: ClipboardCheck,\r\n label: t('totalInspections'),\r\n val: data.overview.totalInspections,\r\n color: '#22d3ee',\r\n },\r\n {\r\n icon: CheckCircle,\r\n label: t('overallPassRate'),\r\n val: `${data.overview.passRate}%`,\r\n color: '#22c55e',\r\n },\r\n {\r\n icon: AlertTriangle,\r\n label: t('defectRate'),\r\n val: `${data.overview.defectRate}%`,\r\n color: '#f59e0b',\r\n },\r\n {\r\n icon: Calendar,\r\n label: t('todayInspections'),\r\n val: data.overview.todayInspections,\r\n color: '#FF6B35',\r\n },\r\n ].map((m, i) => (\r\n <div key={i} className=\"flex items-center gap-2\">\r\n <m.icon className=\"h-4 w-4\" style={{ color: m.color }} />\r\n <span className=\"text-xs\" style={{ color: 'rgba(192,208,224,0.6)' }}>\r\n {m.label}\r\n </span>\r\n <span className=\"text-sm font-bold\" style={{ color: m.color }}>\r\n {m.val}\r\n </span>\r\n </div>\r\n ))}\r\n </div>\r\n\r\n <div className=\"flex items-center gap-4\">\r\n <div className=\"flex items-center gap-1.5\">\r\n <div\r\n className=\"w-2 h-2 rounded-full animate-pulse\"\r\n style={{ background: '#22c55e', boxShadow: '0 0 6px #22c55e' }}\r\n />\r\n <span className=\"text-xs\" style={{ color: '#22c55e' }}>\r\n 实时\r\n </span>\r\n </div>\r\n <button\r\n onClick={toggleFullscreen}\r\n className=\"p-1.5 rounded-lg hover:bg-white/10 transition-colors\"\r\n >\r\n {isFullscreen ? (\r\n <Minimize className=\"h-4 w-4\" style={{ color: '#22d3ee' }} />\r\n ) : (\r\n <Maximize className=\"h-4 w-4\" style={{ color: '#22d3ee' }} />\r\n )}\r\n </button>\r\n <div className=\"text-right\">\r\n {currentTime && (\r\n <>\r\n <div className=\"text-base font-mono font-bold\" style={{ color: '#22d3ee' }}>\r\n {currentTime.toLocaleTimeString(locale)}\r\n </div>\r\n <div className=\"text-xs font-mono\" style={{ color: 'rgba(192,208,224,0.4)' }}>\r\n {currentTime.toLocaleDateString(locale)}\r\n </div>\r\n </>\r\n )}\r\n </div>\r\n </div>\r\n </header>\r\n\r\n <div className=\"relative z-10 grid grid-cols-2 md:grid-cols-4 gap-4 mb-6\">\r\n {[\r\n {\r\n title: t('totalInspections'),\r\n value: data.overview.totalInspections,\r\n icon: ClipboardCheck,\r\n color: 'from-cyan-500 to-blue-500',\r\n },\r\n {\r\n title: t('overallPassRate'),\r\n value: `${data.overview.passRate}%`,\r\n icon: CheckCircle,\r\n color: 'from-green-500 to-emerald-500',\r\n },\r\n {\r\n title: t('todayInspections'),\r\n value: data.overview.todayInspections,\r\n icon: Calendar,\r\n color: 'from-amber-500 to-orange-500',\r\n },\r\n {\r\n title: t('defectRate'),\r\n value: `${data.overview.defectRate}%`,\r\n icon: AlertTriangle,\r\n color: 'from-red-500 to-orange-500',\r\n },\r\n ].map((s, i) => (\r\n <div key={i} className={`tech-card tech-glow tech-card-delay-${i + 1} p-4`}>\r\n <div className=\"flex items-center justify-between\">\r\n <div>\r\n <p className=\"text-xs text-white/60\">{s.title}</p>\r\n <p className=\"text-2xl font-bold mt-1 bg-gradient-to-r from-cyan-300 to-blue-300 bg-clip-text text-transparent\">\r\n {s.value}\r\n </p>\r\n </div>\r\n <div className={`p-3 rounded-lg bg-gradient-to-br ${s.color}`}>\r\n <s.icon className=\"h-5 w-5 text-white\" />\r\n </div>\r\n </div>\r\n </div>\r\n ))}\r\n </div>\r\n\r\n <div className=\"relative z-10 grid grid-cols-1 lg:grid-cols-3 gap-6 mb-6\">\r\n <div className=\"tech-card tech-glow p-0\">\r\n <div className=\"px-4 py-2 border-b border-white/10 flex items-center gap-2 bg-white/5\">\r\n <div className=\"w-1 h-4 rounded-full bg-gradient-to-b from-cyan-400 to-blue-600\" />\r\n <Target className=\"h-4 w-4 text-cyan-400\" />\r\n <span className=\"text-sm font-medium text-white/80\">{t('qualityPassRate')}</span>\r\n </div>\r\n <div className=\"p-4\">\r\n <div className=\"flex justify-center\">\r\n <DonutChart percentage={data.overview.passRate} color=\"#06b6d4\" size={160} />\r\n </div>\r\n <div className=\"mt-4 grid grid-cols-2 gap-4 text-center\">\r\n <div>\r\n <p className=\"text-xs text-white/50\">{tc('qualified')}</p>\r\n <p className=\"text-lg font-bold text-green-400\">\r\n {data.overview.passedInspections || '-'}\r\n </p>\r\n </div>\r\n <div>\r\n <p className=\"text-xs text-white/50\">{tc('unqualified')}</p>\r\n <p className=\"text-lg font-bold text-red-400\">\r\n {data.overview.failedInspections || '-'}\r\n </p>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <div className=\"tech-card tech-glow p-0 lg:col-span-2\">\r\n <div className=\"px-4 py-2 border-b border-white/10 flex items-center gap-2 bg-white/5\">\r\n <div className=\"w-1 h-4 rounded-full bg-gradient-to-b from-cyan-400 to-blue-600\" />\r\n <TrendingDown className=\"h-4 w-4 text-cyan-400\" />\r\n <span className=\"text-sm font-medium text-white/80\">{t('defectTrend')}</span>\r\n </div>\r\n <div className=\"p-4\">\r\n {data.defectTrend.length === 0 ? (\r\n <p className=\"text-white/40 text-center py-8\">{tc('noData')}</p>\r\n ) : (\r\n <LineChart data={data.defectTrend} width={600} height={200} />\r\n )}\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <div className=\"relative z-10 grid grid-cols-12 gap-4 mb-6\">\r\n <div className=\"col-span-3\">\r\n <div className=\"tech-card tech-glow p-0 h-full\">\r\n <div className=\"px-4 py-2 border-b border-white/10 flex items-center gap-2 bg-white/5\">\r\n <div className=\"w-1 h-4 rounded-full bg-gradient-to-b from-green-400 to-emerald-600\" />\r\n <CheckCircle className=\"h-4 w-4 text-green-400\" />\r\n <span className=\"text-sm font-medium text-white/80\">{t('passRateGauge')}</span>\r\n </div>\r\n <div className=\"p-3 flex flex-col items-center relative\">\r\n <div\r\n className=\"absolute inset-0 pointer-events-none\"\r\n style={{\r\n background:\r\n 'radial-gradient(ellipse at 50% 15%, rgba(21,255,187,0.08) 0%, transparent 70%)',\r\n }}\r\n />\r\n <GlassGauge\r\n value={data.overview.passRate}\r\n label={t('overallPassRate')}\r\n unit=\"%\"\r\n colorArc=\"#15ffbb\"\r\n colorDanger=\"#ff5555\"\r\n colorSafe=\"#15ffbb\"\r\n dangerStart={90}\r\n showCurve\r\n history={passRateHistory}\r\n size={140}\r\n />\r\n </div>\r\n </div>\r\n </div>\r\n <div className=\"col-span-3\">\r\n <div className=\"tech-card tech-glow p-0 h-full\">\r\n <div className=\"px-4 py-2 border-b border-white/10 flex items-center gap-2 bg-white/5\">\r\n <div className=\"w-1 h-4 rounded-full bg-gradient-to-b from-red-400 to-orange-500\" />\r\n <AlertTriangle className=\"h-4 w-4 text-red-400\" />\r\n <span className=\"text-sm font-medium text-white/80\">{t('defectRateGauge')}</span>\r\n </div>\r\n <div className=\"p-3 flex flex-col items-center relative\">\r\n <div\r\n className=\"absolute inset-0 pointer-events-none\"\r\n style={{\r\n background:\r\n 'radial-gradient(ellipse at 50% 15%, rgba(239,68,68,0.08) 0%, transparent 70%)',\r\n }}\r\n />\r\n <GlassGauge\r\n value={data.overview.defectRate}\r\n label={t('defectRate')}\r\n unit=\"%\"\r\n colorArc=\"#ef4444\"\r\n colorDanger=\"#ef4444\"\r\n colorSafe=\"#22d3ee\"\r\n dangerStart={10}\r\n showCurve\r\n history={defectRateHistory}\r\n size={140}\r\n />\r\n </div>\r\n </div>\r\n </div>\r\n <div className=\"col-span-3\">\r\n <div className=\"tech-card tech-glow p-0 h-full\">\r\n <div className=\"px-4 py-2 border-b border-white/10 flex items-center gap-2 bg-white/5\">\r\n <div className=\"w-1 h-4 rounded-full bg-gradient-to-b from-cyan-400 to-blue-600\" />\r\n <Activity className=\"h-4 w-4 text-cyan-400\" />\r\n <span className=\"text-sm font-medium text-white/80\">{t('todayPassRateGauge')}</span>\r\n </div>\r\n <div className=\"p-3 flex flex-col items-center relative\">\r\n <div\r\n className=\"absolute inset-0 pointer-events-none\"\r\n style={{\r\n background:\r\n 'radial-gradient(ellipse at 50% 15%, rgba(31,207,255,0.08) 0%, transparent 70%)',\r\n }}\r\n />\r\n <GlassGauge\r\n value={data.overview.todayPassRate}\r\n label={t('todayPassRate')}\r\n unit=\"%\"\r\n colorArc=\"#1fcfff\"\r\n colorDanger=\"#ff5555\"\r\n colorSafe=\"#22d3ee\"\r\n dangerStart={85}\r\n size={140}\r\n />\r\n </div>\r\n </div>\r\n </div>\r\n <div className=\"col-span-3\">\r\n <div className=\"tech-card tech-glow p-0 h-full\">\r\n <div className=\"px-4 py-2 border-b border-white/10 flex items-center gap-2 bg-white/5\">\r\n <div className=\"w-1 h-4 rounded-full bg-gradient-to-b from-purple-400 to-pink-500\" />\r\n <Shield className=\"h-4 w-4 text-purple-400\" />\r\n <span className=\"text-sm font-medium text-white/80\">{t('inspectionStandard')}</span>\r\n </div>\r\n <div className=\"p-3 flex flex-col items-center relative\">\r\n <div\r\n className=\"absolute inset-0 pointer-events-none\"\r\n style={{\r\n background:\r\n 'radial-gradient(ellipse at 50% 20%, rgba(168,85,247,0.08) 0%, transparent 70%)',\r\n }}\r\n />\r\n <GlassKnob\r\n value={data.overview.passRate}\r\n accent=\"#a855f7\"\r\n size={100}\r\n label={t('passThreshold')}\r\n />\r\n <div className=\"mt-3 text-purple-300 font-extrabold text-xl\">\r\n {Math.round(data.overview.passRate)}\r\n <span className=\"text-white/40 text-sm ml-1\">%</span>\r\n </div>\r\n <div className=\"text-white/30 text-xs mt-1\">{t('passBaseline')}</div>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <div className=\"relative z-10 grid grid-cols-1 lg:grid-cols-2 gap-6 mb-6\">\r\n <div className=\"tech-card tech-glow p-0\">\r\n <div className=\"px-4 py-2 border-b border-white/10 flex items-center gap-2 bg-white/5\">\r\n <div className=\"w-1 h-4 rounded-full bg-gradient-to-b from-red-400 to-orange-500\" />\r\n <AlertCircle className=\"h-4 w-4 text-red-400\" />\r\n <span className=\"text-sm font-medium text-white/80\">{t('defectTypeTop5')}</span>\r\n </div>\r\n <div className=\"p-4\">\r\n {data.topDefects.length === 0 ? (\r\n <p className=\"text-white/40 text-center py-8\">{tc('noData')}</p>\r\n ) : (\r\n <HorizontalBarChart data={data.topDefects.slice(0, 5)} />\r\n )}\r\n </div>\r\n </div>\r\n\r\n <div className=\"tech-card tech-glow p-0\">\r\n <div className=\"px-4 py-2 border-b border-white/10 flex items-center gap-2 bg-white/5\">\r\n <div className=\"w-1 h-4 rounded-full bg-gradient-to-b from-cyan-400 to-blue-600\" />\r\n <BarChart3 className=\"h-4 w-4 text-cyan-400\" />\r\n <span className=\"text-sm font-medium text-white/80\">\r\n {t('inspectionTypeDistribution')}\r\n </span>\r\n </div>\r\n <div className=\"p-4\">\r\n {data.processQuality.length === 0 ? (\r\n <p className=\"text-white/40 text-center py-8\">{tc('noData')}</p>\r\n ) : (\r\n <div className=\"overflow-x-auto\">\r\n <table className=\"w-full text-sm\">\r\n <thead>\r\n <tr className=\"border-b border-white/10\">\r\n <th className=\"text-left py-2 px-3 text-white/60 font-medium\">\r\n {tc('productName')}\r\n </th>\r\n <th className=\"text-left py-2 px-3 text-white/60 font-medium\">\r\n {tc('inspectionCount')}\r\n </th>\r\n <th className=\"text-left py-2 px-3 text-white/60 font-medium\">\r\n {tc('passCount')}\r\n </th>\r\n <th className=\"text-left py-2 px-3 text-white/60 font-medium\">\r\n {tc('passRate')}\r\n </th>\r\n <th className=\"text-left py-2 px-3 text-white/60 font-medium\">\r\n {tc('status')}\r\n </th>\r\n </tr>\r\n </thead>\r\n <tbody>\r\n {data.processQuality.map((p, i) => {\r\n const rate =\r\n p.inspect_count > 0 ? Math.round((p.passed / p.inspect_count) * 100) : 0;\r\n return (\r\n <tr\r\n key={i}\r\n className=\"border-b border-white/5 hover:bg-white/5 transition-colors\"\r\n >\r\n <td className=\"py-2 px-3 text-white/80\">{p.product_name}</td>\r\n <td className=\"py-2 px-3 text-white/60\">{p.inspect_count}</td>\r\n <td className=\"py-2 px-3 text-white/60\">{p.passed}</td>\r\n <td className=\"py-2 px-3\">\r\n <div className=\"flex items-center gap-2\">\r\n <div className=\"bg-white/10 rounded-full h-2 w-24 relative overflow-hidden\">\r\n <div\r\n className=\"h-full rounded-full transition-all\"\r\n style={{\r\n width: `${rate}%`,\r\n background:\r\n rate >= 95\r\n ? 'linear-gradient(90deg, #10b981, #34d399)'\r\n : rate >= 80\r\n ? 'linear-gradient(90deg, #f59e0b, #fbbf24)'\r\n : 'linear-gradient(90deg, #ef4444, #f87171)',\r\n }}\r\n />\r\n </div>\r\n <span className=\"text-xs text-white/60 w-10\">{rate}%</span>\r\n </div>\r\n </td>\r\n <td className=\"py-2 px-3\">\r\n <span\r\n className={`px-2 py-0.5 rounded text-xs ${\r\n rate >= 95\r\n ? 'bg-green-500/20 text-green-400 border border-green-500/30'\r\n : rate >= 80\r\n ? 'bg-yellow-500/20 text-yellow-400 border border-yellow-500/30'\r\n : 'bg-red-500/20 text-red-400 border border-red-500/30'\r\n }`}\r\n >\r\n {rate >= 95\r\n ? tc('excellent')\r\n : rate >= 80\r\n ? tc('good')\r\n : tc('needImprovement')}\r\n </span>\r\n </td>\r\n </tr>\r\n );\r\n })}\r\n </tbody>\r\n </table>\r\n </div>\r\n )}\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </MainLayout>\r\n );\r\n}\r\n","usedDeprecatedRules":[]},{"filePath":"D:\\dcprint\\erp-project\\src\\app\\[locale]\\dashboard\\sales\\page.tsx","messages":[{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":206,"column":15,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":206,"endColumn":40},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":207,"column":20,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":207,"endColumn":27},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":207,"column":38,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":207,"endColumn":42},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction<SalesData>`.","line":207,"column":52,"nodeType":"MemberExpression","messageId":"unsafeArgument","endLine":207,"endColumn":63},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":207,"column":59,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":207,"endColumn":63},{"ruleId":"react-hooks/set-state-in-effect","severity":1,"message":"Error: Calling setState synchronously within an effect can trigger cascading renders\n\nEffects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following:\n* Update external systems with the latest state from React.\n* Subscribe for updates from some external system, calling setState in a callback function when external state changes.\n\nCalling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect).\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\dashboard\\sales\\page.tsx:214:5\n 212 | };\n 213 | fetchData();\n> 214 | setCurrentTime(new Date());\n | ^^^^^^^^^^^^^^ Avoid calling setState() directly within an effect\n 215 | const timer1 = setInterval(fetchData, 60000);\n 216 | const timer2 = setInterval(() => setCurrentTime(new Date()), 1000);\n 217 | return () => {","line":214,"column":5,"nodeType":null,"endLine":214,"endColumn":19}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":6,"fixableErrorCount":0,"fixableWarningCount":0,"source":"'use client';\n\nimport { authFetch } from '@/lib/auth-fetch';\nimport { useEffect, useState, useRef } from 'react';\nimport { MainLayout } from '@/components/layout';\nimport { useCompanyName } from '@/hooks/useCompanyName';\nimport {\n FileText,\n DollarSign,\n Users,\n Package,\n TrendingUp,\n ShoppingCart,\n BarChart3,\n Maximize,\n Minimize,\n PieChart,\n} from 'lucide-react';\nimport { useTranslations, useLocale } from 'next-intl';\n\ninterface SalesData {\n overview: {\n totalOrders: number;\n todayOrders: number;\n monthRevenue: number;\n pendingDelivery: number;\n completedOrders: number;\n orderChange: number;\n };\n orderTrend: { date: string; count: number; amount: number }[];\n topCustomers: { customer_name: string; order_count: number; total_amount: number }[];\n topProducts: { product_name: string; total_qty: number; total_amount: number }[];\n recentOrders: {\n id: number;\n order_no: string;\n customer_name: string;\n total_amount: number;\n status: number;\n delivery_date: string;\n create_time: string;\n }[];\n statusDistribution: { status: number; count: number }[];\n}\n\ninterface ChartDataItem {\n [key: string]: string | number;\n}\n\nfunction LineChart({\n data,\n width = 400,\n height = 150,\n}: {\n data: { date: string; count: number; amount: number }[];\n width?: number;\n height?: number;\n}) {\n if (data.length === 0) return null;\n const padding = { top: 20, right: 20, bottom: 30, left: 40 };\n const chartWidth = width - padding.left - padding.right;\n const chartHeight = height - padding.top - padding.bottom;\n const maxVal = Math.max(...data.map((d) => d.count), 1);\n const getX = (i: number) => padding.left + (i / (data.length - 1 || 1)) * chartWidth;\n const getY = (val: number) => padding.top + chartHeight - (val / maxVal) * chartHeight;\n const path = data.map((d, i) => `${i === 0 ? 'M' : 'L'} ${getX(i)} ${getY(d.count)}`).join(' ');\n const areaPath = `${padding.left},${chartHeight + padding.top} ${path} ${getX(data.length - 1)},${chartHeight + padding.top}`;\n\n return (\n <svg width={width} height={height} className=\"w-full\">\n <defs>\n <linearGradient id=\"salesAreaGrad\" x1=\"0\" y1=\"0\" x2=\"0\" y2=\"1\">\n <stop offset=\"0%\" stopColor=\"#06b6d4\" stopOpacity=\"0.3\" />\n <stop offset=\"100%\" stopColor=\"#06b6d4\" stopOpacity=\"0\" />\n </linearGradient>\n </defs>\n {[0, 0.25, 0.5, 0.75, 1].map((ratio, i) => (\n <g key={i}>\n <line\n x1={padding.left}\n y1={padding.top + chartHeight * ratio}\n x2={width - padding.right}\n y2={padding.top + chartHeight * ratio}\n stroke=\"rgba(255,255,255,0.1)\"\n strokeDasharray=\"4 4\"\n />\n <text\n x={padding.left - 5}\n y={padding.top + chartHeight * ratio + 4}\n textAnchor=\"end\"\n fill=\"rgba(255,255,255,0.5)\"\n fontSize=\"10\"\n >\n {Math.round(maxVal * (1 - ratio))}\n </text>\n </g>\n ))}\n {data\n .filter((_, i) => i % Math.ceil(data.length / 6) === 0 || i === data.length - 1)\n .map((d, i) => {\n const idx = data.indexOf(d);\n return (\n <text\n key={i}\n x={getX(idx)}\n y={height - 5}\n textAnchor=\"middle\"\n fill=\"rgba(255,255,255,0.5)\"\n fontSize=\"10\"\n >\n {d.date?.substring(5)}\n </text>\n );\n })}\n <polygon points={areaPath} fill=\"url(#salesAreaGrad)\" />\n <path d={path} fill=\"none\" stroke=\"#06b6d4\" strokeWidth=\"2\" strokeLinecap=\"round\" />\n {data.map((d, i) => (\n <circle key={i} cx={getX(i)} cy={getY(d.count)} r=\"3\" fill=\"#06b6d4\" />\n ))}\n </svg>\n );\n}\n\nfunction HorizontalBarChart({\n data,\n valueKey,\n labelKey,\n unknownText,\n}: {\n data: ChartDataItem[];\n valueKey: string;\n labelKey: string;\n unknownText: string;\n}) {\n if (data.length === 0) return null;\n const maxVal = Math.max(...data.map((d) => Number(d[valueKey]) || 0), 1);\n return (\n <div className=\"space-y-3\">\n {data.slice(0, 5).map((d, i) => (\n <div key={i} className=\"flex items-center gap-3\">\n <span className=\"text-xs w-24 text-right text-cyan-300 truncate\">\n {d[labelKey] || unknownText}\n </span>\n <div className=\"flex-1 bg-white/10 rounded-full h-5 relative overflow-hidden\">\n <div\n className=\"h-full rounded-full transition-all duration-500\"\n style={{\n width: `${(Number(d[valueKey]) / maxVal) * 100}%`,\n background: 'linear-gradient(90deg, #06b6d4, #3b82f6)',\n }}\n />\n <span className=\"absolute inset-0 flex items-center justify-center text-xs font-medium text-white\">\n {typeof d[valueKey] === 'number' ? d[valueKey].toLocaleString() : d[valueKey]}\n </span>\n </div>\n </div>\n ))}\n </div>\n );\n}\n\nconst STATUS_MAP: Record<number, { labelKey: string; className: string }> = {\n 0: { labelKey: 'draft', className: 'bg-gray-500/20 text-gray-300 border border-gray-500/30' },\n 1: { labelKey: 'confirmed', className: 'bg-blue-500/20 text-blue-300 border border-blue-500/30' },\n 2: {\n labelKey: 'inProduction',\n className: 'bg-orange-500/20 text-orange-300 border border-orange-500/30',\n },\n 3: {\n labelKey: 'completed',\n className: 'bg-green-500/20 text-green-300 border border-green-500/30',\n },\n 4: { labelKey: 'cancelled', className: 'bg-red-500/20 text-red-300 border border-red-500/30' },\n};\n\nexport default function SalesDashboard() {\n // 翻译钩子\n const t = useTranslations('Dashboard');\n const tc = useTranslations('Common');\n const locale = useLocale();\n\n const { companyName } = useCompanyName();\n const [data, setData] = useState<SalesData>({\n overview: {\n totalOrders: 0,\n todayOrders: 0,\n monthRevenue: 0,\n pendingDelivery: 0,\n completedOrders: 0,\n orderChange: 0,\n },\n orderTrend: [],\n topCustomers: [],\n topProducts: [],\n recentOrders: [],\n statusDistribution: [],\n });\n const [loading, setLoading] = useState(true);\n const [currentTime, setCurrentTime] = useState<Date | null>(null);\n const [isFullscreen, setIsFullscreen] = useState(false);\n const dashboardRef = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n const fetchData = async () => {\n try {\n const res = await authFetch('/api/dashboard/sales');\n const result = await res.json();\n if (result.success && result.data) setData(result.data);\n } catch {\n } finally {\n setLoading(false);\n }\n };\n fetchData();\n setCurrentTime(new Date());\n const timer1 = setInterval(fetchData, 60000);\n const timer2 = setInterval(() => setCurrentTime(new Date()), 1000);\n return () => {\n clearInterval(timer1);\n clearInterval(timer2);\n };\n }, []);\n\n const toggleFullscreen = () => {\n if (!document.fullscreenElement) {\n dashboardRef.current?.requestFullscreen();\n setIsFullscreen(true);\n } else {\n document.exitFullscreen();\n setIsFullscreen(false);\n }\n };\n\n useEffect(() => {\n const handleFullscreenChange = () => setIsFullscreen(!!document.fullscreenElement);\n document.addEventListener('fullscreenchange', handleFullscreenChange);\n return () => document.removeEventListener('fullscreenchange', handleFullscreenChange);\n }, []);\n\n const formatTime = (date: Date) =>\n date.toLocaleString(locale, {\n year: 'numeric',\n month: '2-digit',\n day: '2-digit',\n hour: '2-digit',\n minute: '2-digit',\n second: '2-digit',\n hour12: false,\n });\n const formatMoney = (v: number) =>\n '¥' + (v / 100).toLocaleString(locale, { minimumFractionDigits: 2, maximumFractionDigits: 2 });\n\n return (\n <MainLayout>\n <div\n ref={dashboardRef}\n className=\"min-h-screen text-white p-4 relative overflow-hidden\"\n style={{ background: 'linear-gradient(135deg, #091637 0%, #010205 100%)' }}\n >\n <div className=\"absolute inset-0 overflow-hidden pointer-events-none\">\n <div className=\"absolute top-0 left-0 w-96 h-96 bg-cyan-500/5 rounded-full blur-3xl animate-blob\" />\n <div className=\"absolute bottom-0 right-0 w-96 h-96 bg-blue-500/5 rounded-full blur-3xl animate-blob animation-delay-2000\" />\n <div className=\"absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-[600px] h-[600px] bg-indigo-500/5 rounded-full blur-3xl animate-blob animation-delay-4000\" />\n </div>\n <div className=\"absolute inset-0 tech-grid-bg pointer-events-none\" />\n\n <div className=\"relative z-10 flex flex-col items-center mb-3\">\n <div className=\"tech-title-wrapper\">\n <div className=\"tech-title-row\">\n <div className=\"tech-title-line-left\" />\n <div>\n <h1 className=\"text-lg font-bold tracking-wider bg-gradient-to-r from-cyan-300 via-blue-400 to-cyan-300 bg-clip-text text-transparent\">\n {companyName}\n </h1>\n <p className=\"text-[10px] text-white/50\">{t('salesBoard')}</p>\n </div>\n <div className=\"tech-title-line-right\" />\n </div>\n <div className=\"tech-title-bottom-line\" />\n </div>\n <div className=\"flex items-center gap-3 mt-1.5\">\n <div className=\"text-sm font-mono font-bold text-cyan-400\">\n {currentTime && formatTime(currentTime)}\n </div>\n <button\n onClick={toggleFullscreen}\n className=\"p-1.5 rounded-lg bg-white/10 hover:bg-white/20 transition-colors\"\n title={isFullscreen ? t('exitFullscreen') : t('fullscreen')}\n >\n {isFullscreen ? (\n <Minimize className=\"h-3.5 w-3.5 text-cyan-400\" />\n ) : (\n <Maximize className=\"h-3.5 w-3.5 text-cyan-400\" />\n )}\n </button>\n <div className=\"px-2 py-0.5 rounded-full bg-cyan-500/20 border border-cyan-500/30 text-[10px] text-cyan-300\">\n {loading ? tc('loading') : '● ' + t('realtime')}\n </div>\n </div>\n </div>\n\n <div className=\"relative z-10 grid grid-cols-2 md:grid-cols-5 gap-4 mb-6\">\n {[\n {\n title: t('totalOrders'),\n value: data.overview.totalOrders,\n icon: FileText,\n color: 'from-cyan-500 to-blue-500',\n },\n {\n title: t('todayOrders'),\n value: data.overview.todayOrders,\n icon: ShoppingCart,\n color: 'from-green-500 to-emerald-500',\n },\n {\n title: t('monthlyRevenue'),\n value: formatMoney(data.overview.monthRevenue),\n icon: DollarSign,\n color: 'from-emerald-500 to-teal-500',\n },\n {\n title: t('pendingDelivery'),\n value: data.overview.pendingDelivery,\n icon: Package,\n color: 'from-orange-500 to-amber-500',\n },\n {\n title: tc('completed'),\n value: data.overview.completedOrders,\n icon: TrendingUp,\n color: 'from-purple-500 to-pink-500',\n },\n ].map((s, i) => (\n <div key={i} className={`tech-card tech-glow tech-card-delay-${i + 1} p-4`}>\n <div className=\"flex items-center justify-between\">\n <div>\n <p className=\"text-xs text-white/60\">{s.title}</p>\n <p className=\"text-xl font-bold mt-1 bg-gradient-to-r from-cyan-300 to-blue-300 bg-clip-text text-transparent\">\n {s.value}\n </p>\n </div>\n <div className={`p-3 rounded-lg bg-gradient-to-br ${s.color}`}>\n <s.icon className=\"h-5 w-5 text-white\" />\n </div>\n </div>\n </div>\n ))}\n </div>\n\n <div className=\"relative z-10 grid grid-cols-1 lg:grid-cols-2 gap-6 mb-6\">\n <div className=\"tech-card tech-glow p-5\">\n <h3 className=\"text-sm font-semibold text-cyan-300 mb-4 flex items-center gap-2\">\n <BarChart3 className=\"h-4 w-4\" />\n {t('orderTrend')}\n </h3>\n {data.orderTrend.length === 0 ? (\n <p className=\"text-white/40 text-center py-8\">{tc('noData')}</p>\n ) : (\n <LineChart data={data.orderTrend} width={600} height={200} />\n )}\n </div>\n\n <div className=\"tech-card tech-glow p-5\">\n <h3 className=\"text-sm font-semibold text-cyan-300 mb-4 flex items-center gap-2\">\n <PieChart className=\"h-4 w-4\" />\n {t('orderStatusDistribution')}\n </h3>\n {data.statusDistribution.length === 0 ? (\n <p className=\"text-white/40 text-center py-8\">{tc('noData')}</p>\n ) : (\n <div className=\"space-y-4\">\n {data.statusDistribution.map((s, i) => {\n const total = data.statusDistribution.reduce((a, b) => a + b.count, 0);\n const pct = total > 0 ? Math.round((s.count / total) * 100) : 0;\n const cfg = STATUS_MAP[s.status] || {\n labelKey: 'unknown',\n className: 'bg-gray-500/20 text-gray-300 border border-gray-500/30',\n };\n return (\n <div key={i}>\n <div className=\"flex justify-between text-sm mb-1\">\n <span className={`px-2 py-0.5 rounded text-xs ${cfg.className}`}>\n {tc(cfg.labelKey)}\n </span>\n <span className=\"text-white/50\">\n {s.count}\n {tc('percentSign')}\n {pct}%)\n </span>\n </div>\n <div className=\"bg-white/10 rounded-full h-3 relative overflow-hidden\">\n <div\n className=\"h-full rounded-full transition-all\"\n style={{\n width: `${pct}%`,\n background: 'linear-gradient(90deg, #06b6d4, #3b82f6)',\n }}\n />\n </div>\n </div>\n );\n })}\n </div>\n )}\n </div>\n </div>\n\n <div className=\"relative z-10 grid grid-cols-1 lg:grid-cols-2 gap-6 mb-6\">\n <div className=\"tech-card tech-glow p-5\">\n <h3 className=\"text-sm font-semibold text-cyan-300 mb-4 flex items-center gap-2\">\n <Users className=\"h-4 w-4\" />\n {t('customerTop5')}\n </h3>\n {data.topCustomers.length === 0 ? (\n <p className=\"text-white/40 text-center py-8\">{tc('noData')}</p>\n ) : (\n <div className=\"overflow-x-auto\">\n <table className=\"w-full text-sm\">\n <thead>\n <tr className=\"border-b border-white/10\">\n <th className=\"text-left py-2 px-3 text-white/60 font-medium\">\n {tc('rank')}\n </th>\n <th className=\"text-left py-2 px-3 text-white/60 font-medium\">\n {tc('customerName')}\n </th>\n <th className=\"text-left py-2 px-3 text-white/60 font-medium\">\n {tc('orderCount')}\n </th>\n <th className=\"text-left py-2 px-3 text-white/60 font-medium\">\n {tc('totalAmount')}\n </th>\n </tr>\n </thead>\n <tbody>\n {data.topCustomers.map((c, i) => (\n <tr\n key={i}\n className=\"border-b border-white/5 hover:bg-white/5 transition-colors\"\n >\n <td className=\"py-2 px-3 text-cyan-400 font-bold text-xs\">{i + 1}</td>\n <td className=\"py-2 px-3 text-white/80 text-xs\">{c.customer_name}</td>\n <td className=\"py-2 px-3 text-white/60 text-xs\">{c.order_count}</td>\n <td className=\"py-2 px-3 text-cyan-300 font-medium text-xs\">\n {formatMoney(c.total_amount)}\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n )}\n </div>\n\n <div className=\"tech-card tech-glow p-5\">\n <h3 className=\"text-sm font-semibold text-cyan-300 mb-4 flex items-center gap-2\">\n <Package className=\"h-4 w-4\" />\n {t('productTop5')}\n </h3>\n {data.topProducts.length === 0 ? (\n <p className=\"text-white/40 text-center py-8\">{tc('noData')}</p>\n ) : (\n <HorizontalBarChart\n data={data.topProducts}\n valueKey=\"total_qty\"\n labelKey=\"product_name\"\n unknownText={tc('unknown')}\n />\n )}\n </div>\n </div>\n\n <div className=\"relative z-10 tech-card tech-glow p-5\">\n <h3 className=\"text-sm font-semibold text-cyan-300 mb-4 flex items-center gap-2\">\n <FileText className=\"h-4 w-4\" />\n {t('recentOrders')}\n </h3>\n {data.recentOrders.length === 0 ? (\n <p className=\"text-white/40 text-center py-8\">{tc('noRecords')}</p>\n ) : (\n <div className=\"overflow-x-auto\">\n <table className=\"w-full text-sm\">\n <thead>\n <tr className=\"border-b border-white/10\">\n <th className=\"text-left py-2 px-3 text-white/60 font-medium\">\n {tc('orderNo')}\n </th>\n <th className=\"text-left py-2 px-3 text-white/60 font-medium\">\n {tc('customerName')}\n </th>\n <th className=\"text-left py-2 px-3 text-white/60 font-medium\">\n {tc('amount')}\n </th>\n <th className=\"text-left py-2 px-3 text-white/60 font-medium\">\n {tc('status')}\n </th>\n <th className=\"text-left py-2 px-3 text-white/60 font-medium\">\n {tc('deliveryDate')}\n </th>\n <th className=\"text-left py-2 px-3 text-white/60 font-medium\">\n {tc('createdAt')}\n </th>\n </tr>\n </thead>\n <tbody>\n {data.recentOrders.map((o, i) => {\n const cfg = STATUS_MAP[o.status] || {\n labelKey: 'unknown',\n className: 'bg-gray-500/20 text-gray-300 border border-gray-500/30',\n };\n return (\n <tr\n key={i}\n className=\"border-b border-white/5 hover:bg-white/5 transition-colors\"\n >\n <td className=\"py-2 px-3 font-mono text-cyan-300 text-xs\">{o.order_no}</td>\n <td className=\"py-2 px-3 text-white/80 text-xs\">{o.customer_name}</td>\n <td className=\"py-2 px-3 text-cyan-300 font-medium text-xs\">\n {formatMoney(o.total_amount)}\n </td>\n <td className=\"py-2 px-3\">\n <span className={`px-2 py-0.5 rounded text-xs ${cfg.className}`}>\n {tc(cfg.labelKey)}\n </span>\n </td>\n <td className=\"py-2 px-3 text-white/50 text-xs\">\n {o.delivery_date?.substring(0, 10) || '-'}\n </td>\n <td className=\"py-2 px-3 text-white/50 text-xs\">\n {o.create_time?.substring(5, 16)}\n </td>\n </tr>\n );\n })}\n </tbody>\n </table>\n </div>\n )}\n </div>\n </div>\n </MainLayout>\n );\n}\n","usedDeprecatedRules":[]},{"filePath":"D:\\dcprint\\erp-project\\src\\app\\[locale]\\dashboard\\warehouse\\page.tsx","messages":[{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":161,"column":15,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":161,"endColumn":40},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":162,"column":20,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":162,"endColumn":27},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":162,"column":38,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":162,"endColumn":42},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction<WarehouseData>`.","line":162,"column":52,"nodeType":"MemberExpression","messageId":"unsafeArgument","endLine":162,"endColumn":63},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":162,"column":59,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":162,"endColumn":63},{"ruleId":"react-hooks/set-state-in-effect","severity":1,"message":"Error: Calling setState synchronously within an effect can trigger cascading renders\n\nEffects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following:\n* Update external systems with the latest state from React.\n* Subscribe for updates from some external system, calling setState in a callback function when external state changes.\n\nCalling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect).\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\dashboard\\warehouse\\page.tsx:169:5\n 167 | };\n 168 | fetchData();\n> 169 | setCurrentTime(new Date());\n | ^^^^^^^^^^^^^^ Avoid calling setState() directly within an effect\n 170 | const timer1 = setInterval(fetchData, 60000);\n 171 | const timer2 = setInterval(() => setCurrentTime(new Date()), 1000);\n 172 | return () => {","line":169,"column":5,"nodeType":null,"endLine":169,"endColumn":19}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":6,"fixableErrorCount":0,"fixableWarningCount":0,"source":"'use client';\r\n\r\nimport { authFetch } from '@/lib/auth-fetch';\r\nimport { useEffect, useState, useRef, useMemo } from 'react';\r\nimport { MainLayout } from '@/components/layout';\r\nimport { useCompanyName } from '@/hooks/useCompanyName';\r\nimport GlassGauge from '@/components/GlassGauge';\r\nimport { ChartImage, ChartPlaceholder } from '@/components/WarehouseCharts';\r\nimport {\r\n Package,\r\n ArrowDown,\r\n ArrowUp,\r\n AlertTriangle,\r\n TrendingUp,\r\n Warehouse,\r\n BarChart3,\r\n Maximize,\r\n Minimize,\r\n Clock,\r\n PieChart as PieChartIcon,\r\n TrendingDown,\r\n} from 'lucide-react';\r\nimport { useTranslations, useLocale } from 'next-intl';\r\nimport { BarChart, Bar, XAxis, YAxis, Tooltip, ResponsiveContainer, Cell } from 'recharts';\r\n\r\ninterface WarehouseData {\r\n overview: {\r\n totalItems: number;\r\n totalValue: number;\r\n lowStock: number;\r\n todayInbound: number;\r\n todayOutbound: number;\r\n pendingInbound: number;\r\n pendingOutbound: number;\r\n };\r\n categoryDistribution: { material_type: string; count: number; value: number }[];\r\n lowStockItems: {\r\n material_code: string;\r\n material_name: string;\r\n stock_qty: number;\r\n min_stock: number;\r\n unit: string;\r\n specification: string;\r\n }[];\r\n recentTransactions: {\r\n transaction_type: string;\r\n material_code: string;\r\n material_name: string;\r\n quantity: number;\r\n unit: string;\r\n create_time: string;\r\n remark: string;\r\n }[];\r\n warehouseOccupancy: {\r\n warehouse_name: string;\r\n item_count: number;\r\n total_qty: number;\r\n capacity?: number;\r\n }[];\r\n}\r\n\r\nfunction AutoScroll({\r\n children,\r\n maxHeight = 320,\r\n}: {\r\n children: React.ReactNode;\r\n maxHeight?: number;\r\n}) {\r\n const containerRef = useRef<HTMLDivElement>(null);\r\n const innerRef = useRef<HTMLDivElement>(null);\r\n const [isPaused, setIsPaused] = useState(false);\r\n\r\n useEffect(() => {\r\n const container = containerRef.current;\r\n const inner = innerRef.current;\r\n if (!container || !inner) return;\r\n\r\n let animId: number;\r\n let scrollPos = 0;\r\n const contentHeight = inner.scrollHeight / 2;\r\n const viewportHeight = maxHeight;\r\n\r\n if (contentHeight <= viewportHeight) return;\r\n\r\n const step = () => {\r\n if (!isPaused) {\r\n scrollPos += 0.3;\r\n if (scrollPos >= contentHeight) {\r\n scrollPos = 0;\r\n }\r\n container.scrollTop = scrollPos;\r\n }\r\n animId = requestAnimationFrame(step);\r\n };\r\n\r\n animId = requestAnimationFrame(step);\r\n return () => cancelAnimationFrame(animId);\r\n }, [isPaused, maxHeight, children]);\r\n\r\n return (\r\n <div\r\n ref={containerRef}\r\n className=\"overflow-hidden\"\r\n style={{ maxHeight: `${maxHeight}px` }}\r\n onMouseEnter={() => setIsPaused(true)}\r\n onMouseLeave={() => setIsPaused(false)}\r\n >\r\n <div ref={innerRef}>\r\n {children}\r\n {children}\r\n </div>\r\n </div>\r\n );\r\n}\r\n\r\nconst COLORS = [\r\n '#06b6d4',\r\n '#3b82f6',\r\n '#8b5cf6',\r\n '#ec4899',\r\n '#f43f5e',\r\n '#f97316',\r\n '#eab308',\r\n '#22c55e',\r\n '#14b8a6',\r\n '#6366f1',\r\n];\r\n\r\nexport default function WarehouseDashboard() {\r\n // 翻译钩子\r\n const t = useTranslations('Dashboard');\r\n const tc = useTranslations('Common');\r\n const locale = useLocale();\r\n\r\n const { companyName } = useCompanyName();\r\n const [data, setData] = useState<WarehouseData>({\r\n overview: {\r\n totalItems: 0,\r\n totalValue: 0,\r\n lowStock: 0,\r\n todayInbound: 0,\r\n todayOutbound: 0,\r\n pendingInbound: 0,\r\n pendingOutbound: 0,\r\n },\r\n categoryDistribution: [],\r\n lowStockItems: [],\r\n recentTransactions: [],\r\n warehouseOccupancy: [],\r\n });\r\n const [loading, setLoading] = useState(true);\r\n const [currentTime, setCurrentTime] = useState<Date | null>(null);\r\n const [isFullscreen, setIsFullscreen] = useState(false);\r\n const dashboardRef = useRef<HTMLDivElement>(null);\r\n const [warehouseHistory] = useState<number[]>([65, 68, 70, 72, 69, 73]);\r\n\r\n useEffect(() => {\r\n const fetchData = async () => {\r\n try {\r\n const res = await authFetch('/api/dashboard/warehouse');\r\n const result = await res.json();\r\n if (result.success && result.data) setData(result.data);\r\n } catch {\r\n } finally {\r\n setLoading(false);\r\n }\r\n };\r\n fetchData();\r\n setCurrentTime(new Date());\r\n const timer1 = setInterval(fetchData, 60000);\r\n const timer2 = setInterval(() => setCurrentTime(new Date()), 1000);\r\n return () => {\r\n clearInterval(timer1);\r\n clearInterval(timer2);\r\n };\r\n }, []);\r\n\r\n const toggleFullscreen = () => {\r\n if (!document.fullscreenElement) {\r\n dashboardRef.current?.requestFullscreen();\r\n setIsFullscreen(true);\r\n } else {\r\n document.exitFullscreen();\r\n setIsFullscreen(false);\r\n }\r\n };\r\n\r\n useEffect(() => {\r\n const handleFullscreenChange = () => {\r\n setIsFullscreen(!!document.fullscreenElement);\r\n };\r\n document.addEventListener('fullscreenchange', handleFullscreenChange);\r\n return () => document.removeEventListener('fullscreenchange', handleFullscreenChange);\r\n }, []);\r\n\r\n const formatTime = (date: Date) => {\r\n return date.toLocaleString(locale, {\r\n year: 'numeric',\r\n month: '2-digit',\r\n day: '2-digit',\r\n hour: '2-digit',\r\n minute: '2-digit',\r\n second: '2-digit',\r\n hour12: false,\r\n });\r\n };\r\n\r\n const formatMoney = (v: number) =>\r\n '¥' + (v / 100).toLocaleString(locale, { minimumFractionDigits: 2, maximumFractionDigits: 2 });\r\n\r\n const warehouses3D = useMemo(() => {\r\n const maxQty = Math.max(...data.warehouseOccupancy.map((w) => w.total_qty), 1);\r\n return data.warehouseOccupancy.map((w) => ({\r\n name: w.warehouse_name,\r\n occupancy: w.capacity ? (w.total_qty / w.capacity) * 100 : (w.total_qty / maxQty) * 100,\r\n color: '#06b6d4',\r\n }));\r\n }, [data.warehouseOccupancy]);\r\n\r\n const avgOccupancy =\r\n warehouses3D.length > 0\r\n ? warehouses3D.reduce((sum, w) => sum + w.occupancy, 0) / warehouses3D.length\r\n : 0;\r\n\r\n const categoryChartUrl = useMemo(() => {\r\n if (data.categoryDistribution.length === 0) return '';\r\n const baseUrl = 'https://trae-api-cn.mchost.guru/api/ide/v1/text_to_image?prompt=';\r\n const prompt = `Business pie chart showing ${data.categoryDistribution.map((c) => `${c.material_type}: ${c.count} items`).join(', ')} with professional colors, dark theme`;\r\n return baseUrl + encodeURIComponent(prompt) + '&image_size=square';\r\n }, [data.categoryDistribution]);\r\n\r\n const _occupancyChartUrls = useMemo(() => {\r\n if (data.warehouseOccupancy.length === 0) return [];\r\n return data.warehouseOccupancy.map((w) => {\r\n const occupancy = w.capacity ? (w.total_qty / w.capacity) * 100 : avgOccupancy;\r\n return {\r\n name: w.warehouse_name,\r\n occupancy,\r\n color: occupancy > 80 ? '#ef4444' : occupancy > 50 ? '#f59e0b' : '#10b981',\r\n };\r\n });\r\n }, [data.warehouseOccupancy, avgOccupancy]);\r\n\r\n const trendChartUrl = useMemo(() => {\r\n if (data.recentTransactions.length === 0) return '';\r\n const baseUrl = 'https://trae-api-cn.mchost.guru/api/ide/v1/text_to_image?prompt=';\r\n const prompt = `Business line chart showing warehouse inbound and outbound trends over 7 days, professional style, dark theme, cyan and green colors`;\r\n return baseUrl + encodeURIComponent(prompt) + '&image_size=landscape_16_9';\r\n }, [data.recentTransactions]);\r\n\r\n return (\r\n <MainLayout>\r\n <div\r\n ref={dashboardRef}\r\n className=\"min-h-screen text-white p-4 relative overflow-hidden\"\r\n style={{\r\n background: 'linear-gradient(135deg, #091637 0%, #010205 100%)',\r\n }}\r\n >\r\n <div className=\"absolute inset-0 overflow-hidden pointer-events-none\">\r\n <div className=\"absolute top-0 left-0 w-96 h-96 bg-cyan-500/5 rounded-full blur-3xl animate-blob\" />\r\n <div className=\"absolute bottom-0 right-0 w-96 h-96 bg-blue-500/5 rounded-full blur-3xl animate-blob animation-delay-2000\" />\r\n <div className=\"absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-[600px] h-[600px] bg-indigo-500/5 rounded-full blur-3xl animate-blob animation-delay-4000\" />\r\n </div>\r\n <div className=\"absolute inset-0 tech-grid-bg pointer-events-none\" />\r\n\r\n <div className=\"relative z-10 flex flex-col items-center mb-3\">\r\n <div className=\"tech-title-wrapper\">\r\n <div className=\"tech-title-row\">\r\n <div className=\"tech-title-line-left\" />\r\n <div>\r\n <h1 className=\"text-lg font-bold tracking-wider bg-gradient-to-r from-cyan-300 via-blue-400 to-cyan-300 bg-clip-text text-transparent\">\r\n {companyName}\r\n </h1>\r\n <p className=\"text-[10px] text-white/50\">{t('warehouseBoard')}</p>\r\n </div>\r\n <div className=\"tech-title-line-right\" />\r\n </div>\r\n <div className=\"tech-title-bottom-line\" />\r\n </div>\r\n <div className=\"flex items-center gap-3 mt-1.5\">\r\n <div className=\"text-sm font-mono font-bold text-cyan-400\">\r\n {currentTime && formatTime(currentTime)}\r\n </div>\r\n <button\r\n onClick={toggleFullscreen}\r\n className=\"p-1.5 rounded-lg bg-white/10 hover:bg-white/20 transition-colors\"\r\n title={isFullscreen ? t('exitFullscreen') : t('fullscreen')}\r\n >\r\n {isFullscreen ? (\r\n <Minimize className=\"h-3.5 w-3.5 text-cyan-400\" />\r\n ) : (\r\n <Maximize className=\"h-3.5 w-3.5 text-cyan-400\" />\r\n )}\r\n </button>\r\n <div className=\"px-2 py-0.5 rounded-full bg-cyan-500/20 border border-cyan-500/30 text-[10px] text-cyan-300\">\r\n {loading ? tc('loading') : '● ' + t('realtime')}\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <div className=\"relative z-10 grid grid-cols-2 md:grid-cols-3 lg:grid-cols-6 gap-4 mb-6\">\r\n {[\r\n {\r\n title: t('materialTypes'),\r\n value: data.overview.totalItems,\r\n icon: Package,\r\n color: 'from-cyan-500 to-blue-500',\r\n },\r\n {\r\n title: t('totalStockValue'),\r\n value: formatMoney(data.overview.totalValue),\r\n icon: TrendingUp,\r\n color: 'from-green-500 to-emerald-500',\r\n },\r\n {\r\n title: t('stockWarning'),\r\n value: data.overview.lowStock,\r\n icon: AlertTriangle,\r\n color: 'from-red-500 to-orange-500',\r\n },\r\n {\r\n title: t('todayInbound'),\r\n value: data.overview.todayInbound,\r\n icon: ArrowDown,\r\n color: 'from-emerald-500 to-teal-500',\r\n },\r\n {\r\n title: t('todayOutbound'),\r\n value: data.overview.todayOutbound,\r\n icon: ArrowUp,\r\n color: 'from-orange-500 to-amber-500',\r\n },\r\n {\r\n title: t('warehouseCount'),\r\n value: data.warehouseOccupancy.length,\r\n icon: Warehouse,\r\n color: 'from-purple-500 to-pink-500',\r\n },\r\n ].map((s, i) => (\r\n <div key={i} className={`tech-card tech-glow tech-card-delay-${i + 1} p-4`}>\r\n <div className=\"flex items-center justify-between\">\r\n <div>\r\n <p className=\"text-xs text-white/60\">{s.title}</p>\r\n <p className=\"text-xl font-bold mt-1 bg-gradient-to-r from-cyan-300 to-blue-300 bg-clip-text text-transparent\">\r\n {s.value}\r\n </p>\r\n </div>\r\n <div className={`p-2 rounded-lg bg-gradient-to-br ${s.color}`}>\r\n <s.icon className=\"h-5 w-5 text-white\" />\r\n </div>\r\n </div>\r\n </div>\r\n ))}\r\n </div>\r\n\r\n <div className=\"relative z-10 grid grid-cols-1 lg:grid-cols-3 gap-6 mb-6\">\r\n <div className=\"tech-card tech-glow p-0 lg:col-span-2\">\r\n <div className=\"px-4 py-2 border-b border-white/10 flex items-center gap-2 bg-white/5\">\r\n <div className=\"w-1 h-4 rounded-full bg-gradient-to-b from-cyan-400 to-blue-600\" />\r\n <Warehouse className=\"h-4 w-4 text-cyan-400\" />\r\n <span className=\"text-sm font-medium text-white/80\">{t('warehouseUtilization')}</span>\r\n </div>\r\n <div className=\"p-4 h-[300px]\">\r\n {data.warehouseOccupancy.length === 0 ? (\r\n <p className=\"text-white/40 text-center py-8\">{tc('noData')}</p>\r\n ) : (\r\n <ResponsiveContainer width=\"100%\" height=\"100%\">\r\n <BarChart\r\n data={warehouses3D}\r\n layout=\"vertical\"\r\n margin={{ left: 10, right: 10, top: 5, bottom: 5 }}\r\n >\r\n <XAxis\r\n type=\"number\"\r\n domain={[0, 100]}\r\n tick={{ fill: 'rgba(255,255,255,0.6)', fontSize: 10 }}\r\n />\r\n <YAxis\r\n type=\"category\"\r\n dataKey=\"name\"\r\n width={80}\r\n tick={{ fill: 'rgba(255,255,255,0.8)', fontSize: 11 }}\r\n tickLine={{ stroke: 'rgba(255,255,255,0.1)' }}\r\n />\r\n <Tooltip\r\n formatter={(value: number) => [`${value.toFixed(1)}%`, '利用率']}\r\n contentStyle={{\r\n backgroundColor: 'rgba(15,23,42,0.95)',\r\n borderColor: 'rgba(6,182,212,0.3)',\r\n borderRadius: '8px',\r\n padding: '12px',\r\n fontSize: '12px',\r\n }}\r\n />\r\n <Bar\r\n dataKey=\"occupancy\"\r\n radius={[0, 4, 4, 0]}\r\n barSize={24}\r\n fill=\"#06b6d4\"\r\n stroke=\"#06b6d4\"\r\n strokeWidth={1}\r\n >\r\n {warehouses3D.map((entry, index) => (\r\n <Cell\r\n key={`cell-${index}`}\r\n fill={\r\n entry.occupancy > 80\r\n ? '#ef4444'\r\n : entry.occupancy > 50\r\n ? '#f59e0b'\r\n : '#10b981'\r\n }\r\n />\r\n ))}\r\n </Bar>\r\n </BarChart>\r\n </ResponsiveContainer>\r\n )}\r\n </div>\r\n </div>\r\n\r\n <div className=\"tech-card tech-glow p-0\">\r\n <div className=\"px-4 py-2 border-b border-white/10 flex items-center gap-2 bg-white/5\">\r\n <div className=\"w-1 h-4 rounded-full bg-gradient-to-b from-cyan-400 to-blue-600\" />\r\n <BarChart3 className=\"h-4 w-4 text-cyan-400\" />\r\n <span className=\"text-sm font-medium text-white/80\">{t('warehouseUtilization')}</span>\r\n </div>\r\n <div className=\"p-4 flex flex-col items-center\">\r\n <div className=\"relative\">\r\n <div\r\n className=\"absolute inset-0 pointer-events-none\"\r\n style={{\r\n background:\r\n 'radial-gradient(ellipse at 50% 15%, rgba(6,182,212,0.08) 0%, transparent 70%)',\r\n }}\r\n />\r\n <GlassGauge\r\n value={Math.round(avgOccupancy)}\r\n label={t('utilizationRate')}\r\n unit=\"%\"\r\n colorArc=\"#06b6d4\"\r\n colorDanger=\"#ff5555\"\r\n colorSafe=\"#10b981\"\r\n dangerStart={85}\r\n showCurve\r\n history={warehouseHistory}\r\n size={140}\r\n />\r\n </div>\r\n <div className=\"mt-3 w-full space-y-2\">\r\n {warehouses3D.slice(0, 5).map((w, i) => (\r\n <div key={i} className=\"flex items-center justify-between text-sm\">\r\n <span className=\"text-white/60 text-xs\">{w.name}</span>\r\n <span\r\n className={`font-bold text-xs ${\r\n w.occupancy > 80\r\n ? 'text-red-400'\r\n : w.occupancy > 50\r\n ? 'text-yellow-400'\r\n : 'text-green-400'\r\n }`}\r\n >\r\n {w.occupancy.toFixed(1)}%\r\n </span>\r\n </div>\r\n ))}\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <div className=\"relative z-10 grid grid-cols-1 lg:grid-cols-3 gap-6 mb-6\">\r\n <div className=\"tech-card tech-glow p-0\">\r\n <div className=\"px-4 py-2 border-b border-white/10 flex items-center gap-2 bg-white/5\">\r\n <div className=\"w-1 h-4 rounded-full bg-gradient-to-b from-cyan-400 to-blue-600\" />\r\n <PieChartIcon className=\"h-4 w-4 text-cyan-400\" />\r\n <span className=\"text-sm font-medium text-white/80\">\r\n {t('materialCategoryDistribution')}\r\n </span>\r\n </div>\r\n <div className=\"p-4 h-[300px]\">\r\n {data.categoryDistribution.length === 0 ? (\r\n <ChartPlaceholder title={t('materialTypes')} type=\"empty\" />\r\n ) : (\r\n <ChartImage\r\n url={categoryChartUrl}\r\n title={t('materialCategoryDistribution')}\r\n loading={loading}\r\n />\r\n )}\r\n </div>\r\n </div>\r\n\r\n <div className=\"tech-card tech-glow p-0 lg:col-span-2\">\r\n <div className=\"px-4 py-2 border-b border-white/10 flex items-center gap-2 bg-white/5\">\r\n <div className=\"w-1 h-4 rounded-full bg-gradient-to-b from-green-400 to-emerald-600\" />\r\n <TrendingDown className=\"h-4 w-4 text-green-400\" />\r\n <span className=\"text-sm font-medium text-white/80\">{t('inoutTrend')}</span>\r\n </div>\r\n <div className=\"p-4 h-[300px]\">\r\n {data.recentTransactions.length === 0 ? (\r\n <ChartPlaceholder title={t('inoutTrend')} type=\"empty\" />\r\n ) : (\r\n <ChartImage url={trendChartUrl} title={t('inoutTrend')} loading={loading} />\r\n )}\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <div className=\"relative z-10 grid grid-cols-1 lg:grid-cols-2 gap-6 mb-6\">\r\n <div className=\"tech-card tech-glow p-0\">\r\n <div className=\"px-4 py-2 border-b border-white/10 flex items-center gap-2 bg-white/5\">\r\n <div className=\"w-1 h-4 rounded-full bg-gradient-to-b from-cyan-400 to-blue-600\" />\r\n <BarChart3 className=\"h-4 w-4 text-cyan-400\" />\r\n <span className=\"text-sm font-medium text-white/80\">\r\n {t('materialCategoryDistribution')}\r\n </span>\r\n </div>\r\n <AutoScroll maxHeight={320}>\r\n <div className=\"p-4\">\r\n {data.categoryDistribution.length === 0 ? (\r\n <p className=\"text-white/40 text-center py-8\">{tc('noData')}</p>\r\n ) : (\r\n <div className=\"space-y-2\">\r\n {data.categoryDistribution.map((c, i) => {\r\n const total = data.categoryDistribution.reduce((a, b) => a + b.count, 0);\r\n const maxCount = Math.max(\r\n ...data.categoryDistribution.map((d) => d.count),\r\n 1\r\n );\r\n const _pct = total > 0 ? Math.round((c.count / total) * 100) : 0;\r\n return (\r\n <div key={i} className=\"flex items-center gap-3\">\r\n <div\r\n className=\"w-2 h-2 rounded-full\"\r\n style={{ backgroundColor: COLORS[i % COLORS.length] }}\r\n />\r\n <span className=\"text-sm flex-1 text-white/70\">\r\n {c.material_type || tc('unclassified')}\r\n </span>\r\n <div className=\"flex-1 bg-white/10 rounded-full h-5 relative overflow-hidden\">\r\n <div\r\n className=\"h-full rounded-full transition-all duration-500\"\r\n style={{\r\n width: `${(c.count / maxCount) * 100}%`,\r\n background: 'linear-gradient(90deg, #06b6d4, #3b82f6)',\r\n }}\r\n />\r\n <span className=\"absolute inset-0 flex items-center justify-center text-xs font-medium text-white\">\r\n {c.count}\r\n </span>\r\n </div>\r\n </div>\r\n );\r\n })}\r\n </div>\r\n )}\r\n </div>\r\n </AutoScroll>\r\n </div>\r\n\r\n <div className=\"tech-card tech-glow p-0\">\r\n <div className=\"px-4 py-2 border-b border-white/10 flex items-center gap-2 bg-white/5\">\r\n <div className=\"w-1 h-4 rounded-full bg-gradient-to-b from-red-400 to-orange-500\" />\r\n <AlertTriangle className=\"h-4 w-4 text-red-400\" />\r\n <span className=\"text-sm font-medium text-white/80\">{t('stockWarning')}</span>\r\n </div>\r\n <AutoScroll maxHeight={320}>\r\n <div className=\"p-4\">\r\n {data.lowStockItems.length === 0 ? (\r\n <p className=\"text-white/40 text-center py-8\">{t('noAlerts')}</p>\r\n ) : (\r\n <div className=\"space-y-2\">\r\n {data.lowStockItems.map((item, i) => (\r\n <div\r\n key={i}\r\n className=\"flex items-center justify-between p-2 rounded-lg bg-red-500/5 border border-red-500/20\"\r\n >\r\n <div className=\"flex-1 min-w-0\">\r\n <p className=\"text-xs text-white/80 truncate\">{item.material_name}</p>\r\n <p className=\"text-[10px] text-white/40 font-mono\">\r\n {item.material_code}\r\n </p>\r\n </div>\r\n <div className=\"text-right ml-2\">\r\n <p className=\"text-xs text-red-400 font-bold\">\r\n {Number(item.stock_qty).toLocaleString()} {item.unit}\r\n </p>\r\n <p className=\"text-[10px] text-white/30\">\r\n {tc('lowest')} {Number(item.min_stock).toLocaleString()}\r\n </p>\r\n </div>\r\n </div>\r\n ))}\r\n </div>\r\n )}\r\n </div>\r\n </AutoScroll>\r\n </div>\r\n </div>\r\n\r\n <div className=\"relative z-10 tech-card tech-glow p-0\">\r\n <div className=\"px-4 py-2 border-b border-white/10 flex items-center gap-2 bg-white/5\">\r\n <div className=\"w-1 h-4 rounded-full bg-gradient-to-b from-cyan-400 to-blue-600\" />\r\n <Clock className=\"h-4 w-4 text-cyan-400\" />\r\n <span className=\"text-sm font-medium text-white/80\">{t('recentInoutRecords')}</span>\r\n </div>\r\n <div className=\"p-4\">\r\n {data.recentTransactions.length === 0 ? (\r\n <p className=\"text-white/40 text-center py-8\">{tc('noRecords')}</p>\r\n ) : (\r\n <div className=\"overflow-x-auto\">\r\n <table className=\"w-full text-sm\">\r\n <thead>\r\n <tr className=\"border-b border-white/10\">\r\n <th className=\"text-left py-2 px-3 text-white/60 font-medium\">\r\n {tc('inspectionType')}\r\n </th>\r\n <th className=\"text-left py-2 px-3 text-white/60 font-medium\">\r\n {tc('materialCode')}\r\n </th>\r\n <th className=\"text-left py-2 px-3 text-white/60 font-medium\">\r\n {tc('materialName')}\r\n </th>\r\n <th className=\"text-left py-2 px-3 text-white/60 font-medium\">\r\n {tc('planQty')}\r\n </th>\r\n <th className=\"text-left py-2 px-3 text-white/60 font-medium\">\r\n {tc('time')}\r\n </th>\r\n <th className=\"text-left py-2 px-3 text-white/60 font-medium\">\r\n {tc('remark')}\r\n </th>\r\n </tr>\r\n </thead>\r\n <tbody>\r\n {data.recentTransactions.slice(0, 10).map((t, i) => (\r\n <tr\r\n key={i}\r\n className=\"border-b border-white/5 hover:bg-white/5 transition-colors\"\r\n >\r\n <td className=\"py-2 px-3\">\r\n <span\r\n className={`px-2 py-0.5 rounded text-xs ${\r\n t.transaction_type === 'inbound'\r\n ? 'bg-green-500/20 text-green-400 border border-green-500/30'\r\n : 'bg-orange-500/20 text-orange-400 border border-orange-500/30'\r\n }`}\r\n >\r\n {t.transaction_type === 'inbound'\r\n ? tc('inbound')\r\n : t.transaction_type === 'outbound'\r\n ? tc('outbound')\r\n : t.transaction_type}\r\n </span>\r\n </td>\r\n <td className=\"py-2 px-3 font-mono text-cyan-300 text-xs\">\r\n {t.material_code}\r\n </td>\r\n <td className=\"py-2 px-3 text-white/80 text-xs\">{t.material_name}</td>\r\n <td className=\"py-2 px-3 text-white/60 text-xs font-medium\">\r\n {Number(t.quantity).toLocaleString()} {t.unit}\r\n </td>\r\n <td className=\"py-2 px-3 text-white/50 text-xs\">\r\n {t.create_time?.substring(5, 16)}\r\n </td>\r\n <td className=\"py-2 px-3 text-white/40 text-xs\">{t.remark || '-'}</td>\r\n </tr>\r\n ))}\r\n </tbody>\r\n </table>\r\n </div>\r\n )}\r\n </div>\r\n </div>\r\n </div>\r\n </MainLayout>\r\n );\r\n}\r\n","usedDeprecatedRules":[]},{"filePath":"D:\\dcprint\\erp-project\\src\\app\\[locale]\\dcprint\\burdening\\page.tsx","messages":[{"ruleId":"@typescript-eslint/no-unused-vars","severity":1,"message":"'QrCode' is defined but never used. Allowed unused vars must match /^_/u.","line":12,"column":3,"nodeType":"Identifier","messageId":"unusedVar","endLine":12,"endColumn":9,"suggestions":[{"messageId":"removeUnusedVar","data":{"varName":"QrCode"},"fix":{"range":[473,480],"text":""},"desc":"Remove unused variable \"QrCode\"."}]},{"ruleId":"@typescript-eslint/no-unused-vars","severity":1,"message":"'Trash2' is defined but never used. Allowed unused vars must match /^_/u.","line":15,"column":3,"nodeType":"Identifier","messageId":"unusedVar","endLine":15,"endColumn":9,"suggestions":[{"messageId":"removeUnusedVar","data":{"varName":"Trash2"},"fix":{"range":[509,519],"text":""},"desc":"Remove unused variable \"Trash2\"."}]},{"ruleId":"@typescript-eslint/no-unused-vars","severity":1,"message":"'Lock' is defined but never used. Allowed unused vars must match /^_/u.","line":16,"column":3,"nodeType":"Identifier","messageId":"unusedVar","endLine":16,"endColumn":7,"suggestions":[{"messageId":"removeUnusedVar","data":{"varName":"Lock"},"fix":{"range":[519,527],"text":""},"desc":"Remove unused variable \"Lock\"."}]},{"ruleId":"react-hooks/immutability","severity":1,"message":"Error: Cannot access variable before it is declared\n\n`fetchPendingCards` is accessed before it is declared, which prevents the earlier access from updating when this value changes over time.\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\dcprint\\burdening\\page.tsx:50:5\n 48 |\n 49 | useEffect(() => {\n> 50 | fetchPendingCards();\n | ^^^^^^^^^^^^^^^^^ `fetchPendingCards` accessed before it is declared\n 51 | }, []);\n 52 |\n 53 | const fetchPendingCards = async () => {\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\dcprint\\burdening\\page.tsx:53:3\n 51 | }, []);\n 52 |\n> 53 | const fetchPendingCards = async () => {\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n> 54 | try {\n | ^^^^^^^^^\n> 55 | const res = await authFetch('/api/dcprint/process-cards?burdeningStatus=pending');\n | ^^^^^^^^^\n> 56 | const r = await res.json();\n | ^^^^^^^^^\n> 57 | if (r.success) setCards(r.data.list || []);\n | ^^^^^^^^^\n> 58 | } catch {}\n | ^^^^^^^^^\n> 59 | };\n | ^^^^^ `fetchPendingCards` is declared here\n 60 |\n 61 | const selectCard = (card: ProcessCard) => {\n 62 | setSelectedCard(card);","line":50,"column":5,"nodeType":null,"endLine":50,"endColumn":22},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":56,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":56,"endColumn":33},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":57,"column":13,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":57,"endColumn":20},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction<ProcessCard[]>`.","line":57,"column":31,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":57,"endColumn":48},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":57,"column":33,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":57,"endColumn":37},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":69,"column":17,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":69,"endColumn":24},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction<{ labelNo: string; materialName?: string | undefined; batchNo?: string | undefined; }[]>`.","line":69,"column":39,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":69,"endColumn":61},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":69,"column":41,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":69,"endColumn":45},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":86,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":86,"endColumn":38},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":87,"column":18,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":87,"endColumn":25},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":88,"column":15,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":88,"endColumn":38},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":88,"column":29,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":88,"endColumn":33},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":89,"column":15,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":89,"endColumn":38},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":89,"column":29,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":89,"endColumn":33},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .cardNo on an `any` value.","line":91,"column":60,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":91,"endColumn":66},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .id on an `any` value.","line":91,"column":84,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":91,"endColumn":86},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .labelNo on an `any` value.","line":99,"column":61,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":99,"endColumn":68},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":108,"column":17,"nodeType":"Property","messageId":"anyAssignment","endLine":108,"endColumn":33},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .id on an `any` value.","line":108,"column":31,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":108,"endColumn":33},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":109,"column":17,"nodeType":"Property","messageId":"anyAssignment","endLine":109,"endColumn":38},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .labelNo on an `any` value.","line":109,"column":31,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":109,"endColumn":38},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":112,"column":19,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":112,"endColumn":40},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":113,"column":21,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":113,"endColumn":28},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":116,"column":19,"nodeType":"Property","messageId":"anyAssignment","endLine":116,"endColumn":40},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .labelNo on an `any` value.","line":116,"column":33,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":116,"endColumn":40},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":116,"column":42,"nodeType":"Property","messageId":"anyAssignment","endLine":116,"endColumn":73},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .materialName on an `any` value.","line":116,"column":61,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":116,"endColumn":73},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":116,"column":75,"nodeType":"Property","messageId":"anyAssignment","endLine":116,"endColumn":96},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .batchNo on an `any` value.","line":116,"column":89,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":116,"endColumn":96},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction<string>`.","line":119,"column":29,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":119,"endColumn":66},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .message on an `any` value.","line":119,"column":33,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":119,"endColumn":40},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction<string>`.","line":122,"column":23,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":122,"endColumn":57},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .message on an `any` value.","line":122,"column":30,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":122,"endColumn":37},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":144,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":144,"endColumn":33},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":145,"column":13,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":145,"endColumn":20},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction<string>`.","line":155,"column":23,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":155,"endColumn":56},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .message on an `any` value.","line":155,"column":25,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":155,"endColumn":32}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":40,"fixableErrorCount":0,"fixableWarningCount":0,"source":"'use client';\n\nimport { authFetch } from '@/lib/auth-fetch';\nimport { useState, useEffect } from 'react';\nimport { MainLayout } from '@/components/layout';\nimport { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';\nimport { Badge } from '@/components/ui/badge';\nimport { Button } from '@/components/ui/button';\nimport { Input } from '@/components/ui/input';\nimport { Alert, AlertDescription } from '@/components/ui/alert';\nimport {\n QrCode,\n CheckCircle,\n AlertCircle,\n Trash2,\n Lock,\n Search,\n FileText,\n Package,\n CheckCheck,\n} from 'lucide-react';\nimport { useTranslations } from 'next-intl';\n\ninterface ProcessCard {\n id: number;\n cardNo: string;\n workOrderNo: string;\n productCode?: string;\n productName?: string;\n mainLabelNo?: string;\n mainMaterialName?: string;\n burdeningStatus: string;\n lockStatus: string;\n}\n\nexport default function BurdeningPage() {\n const t = useTranslations('Dcprint');\n const tc = useTranslations('Common');\n const [cards, setCards] = useState<ProcessCard[]>([]);\n const [selectedCard, setSelectedCard] = useState<ProcessCard | null>(null);\n const [materials, setMaterials] = useState<\n { labelNo: string; materialName?: string; batchNo?: string }[]\n >([]);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState('');\n const [success, setSuccess] = useState('');\n const [qrCode, setQrCode] = useState('');\n\n useEffect(() => {\n fetchPendingCards();\n }, []);\n\n const fetchPendingCards = async () => {\n try {\n const res = await authFetch('/api/dcprint/process-cards?burdeningStatus=pending');\n const r = await res.json();\n if (r.success) setCards(r.data.list || []);\n } catch {}\n };\n\n const selectCard = (card: ProcessCard) => {\n setSelectedCard(card);\n setError('');\n setSuccess('');\n try {\n authFetch(`/api/dcprint/process-cards/detail?cardNo=${card.cardNo}`)\n .then((r) => r.json())\n .then((r) => {\n if (r.success) setMaterials(r.data.materials || []);\n });\n } catch {\n setMaterials([]);\n }\n };\n\n const handleScan = async () => {\n if (!qrCode.trim()) return;\n setLoading(true);\n setError('');\n try {\n const res = await authFetch('/api/dcprint/scan', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ qrContent: qrCode, scanType: 'process' }),\n });\n const result = await res.json();\n if (result.success) {\n const type = result.data.type;\n const data = result.data.data;\n if (type === '4') {\n const card = cards.find((c) => c.cardNo === data.cardNo || c.id === data.id);\n if (card) {\n selectCard(card);\n setSuccess(t('cardSelected') || '已选择流程卡');\n } else setError(t('cardNotFound') || '流程卡未找到');\n } else if (type === '0' || type === '1' || type === '2') {\n if (!selectedCard) setError(t('pleaseSelectCardFirst') || '请先选择流程卡');\n else if (selectedCard.lockStatus) setError(t('cardLockedCannotAdd') || '流程卡已锁定');\n else if (materials.find((m) => m.labelNo === data.labelNo))\n setError(t('auxiliaryAlreadyAdded'));\n else {\n const r2 = await authFetch('/api/dcprint/process-cards', {\n method: 'PUT',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n id: selectedCard.id,\n action: 'addMaterial',\n labelId: data.id,\n labelNo: data.labelNo,\n }),\n });\n const r2j = await r2.json();\n if (r2j.success) {\n setMaterials([\n ...materials,\n { labelNo: data.labelNo, materialName: data.materialName, batchNo: data.batchNo },\n ]);\n setSuccess(t('auxiliaryAdded'));\n } else setError(r2j.message || t('addMaterialFailed'));\n }\n } else setError(t('scanInvalidType') || '请扫描流程卡或物料标签');\n } else setError(result.message || tc('scanFailed'));\n } catch {\n setError(t('scanQueryFailed'));\n } finally {\n setLoading(false);\n setQrCode('');\n }\n };\n\n const completeBurdening = async () => {\n if (!selectedCard) return;\n setLoading(true);\n try {\n const res = await authFetch('/api/dcprint/process-cards', {\n method: 'PUT',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n id: selectedCard.id,\n action: 'burdening',\n burdeningStatus: 'completed',\n }),\n });\n const r = await res.json();\n if (r.success) {\n await authFetch('/api/dcprint/process-cards', {\n method: 'PUT',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ id: selectedCard.id, action: 'lock', lockStatus: 'locked' }),\n });\n setSuccess(t('burdeningCompleted') || '配料完成');\n setSelectedCard(null);\n setMaterials([]);\n fetchPendingCards();\n } else setError(r.message || t('burdeningFailed'));\n } catch {\n setError(t('burdeningFailed'));\n } finally {\n setLoading(false);\n }\n };\n\n return (\n <MainLayout title={t('scanBatching')}>\n <div className=\"space-y-6\">\n <Card>\n <CardHeader>\n <CardTitle className=\"flex items-center gap-2\">\n <Search className=\"h-5 w-5\" />\n {t('scanBatching')}\n </CardTitle>\n <CardDescription>{t('scanSequenceDesc')}</CardDescription>\n </CardHeader>\n <CardContent>\n <div className=\"space-y-4\">\n <div className=\"flex gap-4\">\n <div className=\"flex-1\">\n <Input\n placeholder={t('pleaseScanQR')}\n value={qrCode}\n onChange={(e) => setQrCode(e.target.value)}\n onKeyDown={(e) => e.key === 'Enter' && handleScan()}\n disabled={loading}\n />\n </div>\n <Button\n variant=\"outline\"\n onClick={() => {\n setSelectedCard(null);\n setMaterials([]);\n setError('');\n setSuccess('');\n }}\n >\n {tc('reset')}\n </Button>\n </div>\n {error && (\n <Alert variant=\"destructive\">\n <AlertCircle className=\"h-4 w-4\" />\n <AlertDescription>{error}</AlertDescription>\n </Alert>\n )}\n {success && (\n <Alert className=\"bg-green-50 border-green-200\">\n <CheckCircle className=\"h-4 w-4 text-green-600\" />\n <AlertDescription className=\"text-green-700\">{success}</AlertDescription>\n </Alert>\n )}\n {selectedCard && (\n <Card className=\"border-blue-200\">\n <CardHeader className=\"pb-3\">\n <CardTitle className=\"text-sm flex items-center gap-2\">\n <FileText className=\"h-4 w-4\" />\n {t('processCard')}: {selectedCard.cardNo}\n </CardTitle>\n </CardHeader>\n <CardContent>\n <div className=\"grid grid-cols-2 gap-2 text-sm mb-2\">\n <span>\n {t('workOrderNo')}: {selectedCard.workOrderNo}\n </span>\n <span>\n {t('productName')}: {selectedCard.productName}\n </span>\n </div>\n {materials.length > 0 && (\n <div className=\"space-y-1 mb-3\">\n {materials.map((m) => (\n <div\n key={m.labelNo}\n className=\"flex items-center justify-between p-2 bg-muted rounded text-sm\"\n >\n <span>\n <Package className=\"h-4 w-4 inline mr-1\" />\n {m.materialName || m.labelNo}\n </span>\n </div>\n ))}\n </div>\n )}\n {!selectedCard.lockStatus && (\n <Button onClick={completeBurdening} disabled={loading} className=\"w-full\">\n <CheckCheck className=\"h-4 w-4 mr-2\" />\n {t('burdened')}\n </Button>\n )}\n </CardContent>\n </Card>\n )}\n </div>\n </CardContent>\n </Card>\n <Card>\n <CardHeader>\n <CardTitle>{t('processCardList')}</CardTitle>\n </CardHeader>\n <CardContent>\n {cards.length === 0 ? (\n <p className=\"text-center text-muted-foreground py-4\">{tc('noRecords')}</p>\n ) : (\n <div className=\"space-y-2\">\n {cards.map((card) => (\n <div\n key={card.id}\n className=\"flex items-center justify-between p-3 border rounded hover:bg-muted/30 cursor-pointer\"\n onClick={() => selectCard(card)}\n >\n <div>\n <span className=\"font-medium\">{card.cardNo}</span>\n <span className=\"ml-3 text-muted-foreground\">\n {card.workOrderNo} - {card.productName}\n </span>\n </div>\n <Badge\n className={\n card.burdeningStatus === 'completed'\n ? 'bg-green-100 text-green-700'\n : 'bg-yellow-100 text-yellow-700'\n }\n >\n {card.burdeningStatus === 'completed' ? t('burdened') : t('notBurdened')}\n </Badge>\n </div>\n ))}\n </div>\n )}\n </CardContent>\n </Card>\n </div>\n </MainLayout>\n );\n}\n","usedDeprecatedRules":[]},{"filePath":"D:\\dcprint\\erp-project\\src\\app\\[locale]\\dcprint\\die\\page.tsx","messages":[{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"模切刀\"。建议使用: tc('text_fy0my')","line":50,"column":6,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":50,"endColumn":11},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"分切刀\"。建议使用: tc('text_cewrj')","line":51,"column":6,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":51,"endColumn":11},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"压痕刀\"。建议使用: tc('text_ct0gm')","line":52,"column":6,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":52,"endColumn":11},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"冲孔刀\"。建议使用: tc('text_cerfi')","line":53,"column":6,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":53,"endColumn":11},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"在用\"。建议使用: tc('text_fgu8')","line":59,"column":15,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":59,"endColumn":19},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"待保养\"。建议使用: tc('text_edpc3')","line":60,"column":15,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":60,"endColumn":20},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"保养中\"。建议使用: tc('text_c3elr')","line":61,"column":15,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":61,"endColumn":20},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"已报废\"。建议使用: tc('text_e8o3w')","line":62,"column":15,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":62,"endColumn":20},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":87,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":87,"endColumn":38},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":88,"column":18,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":88,"endColumn":25},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction<Item[]>`.","line":89,"column":17,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":89,"endColumn":39},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":89,"column":24,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":89,"endColumn":28},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction<number>`.","line":90,"column":18,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":90,"endColumn":40},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":90,"column":25,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":90,"endColumn":29},{"ruleId":"react-hooks/set-state-in-effect","severity":1,"message":"Error: Calling setState synchronously within an effect can trigger cascading renders\n\nEffects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following:\n* Update external systems with the latest state from React.\n* Subscribe for updates from some external system, calling setState in a callback function when external state changes.\n\nCalling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect).\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\dcprint\\die\\page.tsx:95:5\n 93 | };\n 94 | useEffect(() => {\n> 95 | fetchData();\n | ^^^^^^^^^ Avoid calling setState() directly within an effect\n 96 | }, [page]);\n 97 |\n 98 | const handleSave = async () => {","line":95,"column":5,"nodeType":null,"endLine":95,"endColumn":14},{"ruleId":"react-hooks/exhaustive-deps","severity":1,"message":"React Hook useEffect has a missing dependency: 'fetchData'. Either include it or remove the dependency array.","line":96,"column":6,"nodeType":"ArrayExpression","endLine":96,"endColumn":12,"suggestions":[{"desc":"Update the dependencies array to be: [fetchData, page]","fix":{"range":[2502,2508],"text":"[fetchData, page]"}}]},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":106,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":106,"endColumn":38},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":107,"column":18,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":107,"endColumn":25},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":112,"column":37,"nodeType":"Property","messageId":"anyAssignment","endLine":112,"endColumn":64},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .message on an `any` value.","line":112,"column":57,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":112,"endColumn":64},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":122,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":122,"endColumn":38},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":123,"column":18,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":123,"endColumn":25},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"新增刀具\"。建议使用: {tc('text_d73qp1')}","line":162,"column":48,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":164,"endColumn":13},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"刀具编码\"。建议使用: {tc('text_aovqsi')}","line":172,"column":50,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":172,"endColumn":54},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"刀具名称\"。建议使用: {tc('text_aoofne')}","line":173,"column":50,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":173,"endColumn":54},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"尺寸规格\"。建议使用: {tc('text_c0winq')}","line":175,"column":50,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":175,"endColumn":54},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"已用\"。建议使用: {tc('text_gmeu')}","line":178,"column":50,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":178,"endColumn":52},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"暂无记录\"。建议使用: {tc('text_dd1mmb')}","line":237,"column":88,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":239,"endColumn":21},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"共\"。建议使用: {tc('text_g35')}","line":247,"column":51,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":247,"endColumn":52},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"条\"。建议使用: {tc('text_kf5')}","line":247,"column":59,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":247,"endColumn":60},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"上一页\"。建议使用: {tc('text_btlof')}","line":254,"column":14,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":256,"endColumn":13},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"下一页\"。建议使用: {tc('text_btmf4')}","line":262,"column":14,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":264,"endColumn":13},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"刀具编码\"。建议使用: {tc('text_aovqsi')}","line":274,"column":24,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":274,"endColumn":28},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"刀具名称\"。建议使用: {tc('text_aoofne')}","line":281,"column":24,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":281,"endColumn":28},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"模切刀\"。建议使用: {tc('text_fy0my')}","line":297,"column":43,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":297,"endColumn":46},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"分切刀\"。建议使用: {tc('text_cewrj')}","line":298,"column":43,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":298,"endColumn":46},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"压痕刀\"。建议使用: {tc('text_ct0gm')}","line":299,"column":43,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":299,"endColumn":46},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"冲孔刀\"。建议使用: {tc('text_cerfi')}","line":300,"column":43,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":300,"endColumn":46},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"尺寸规格\"。建议使用: {tc('text_c0winq')}","line":305,"column":24,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":305,"endColumn":28},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"产品名称\"。建议使用: {tc('text_a9yk8t')}","line":312,"column":24,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":312,"endColumn":28},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"最大使用次数\"。建议使用: {tc('text_cxnt0h')}","line":319,"column":24,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":319,"endColumn":30},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"取消\"。建议使用: {tc('text_ev02')}","line":330,"column":78,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":332,"endColumn":15}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":42,"fixableErrorCount":0,"fixableWarningCount":0,"source":"'use client';\n\nimport { authFetch } from '@/lib/auth-fetch';\nimport { useEffect, useState } from 'react';\nimport { MainLayout } from '@/components/layout';\nimport { Card, CardContent } from '@/components/ui/card';\nimport { Button } from '@/components/ui/button';\nimport { Input } from '@/components/ui/input';\nimport { Badge } from '@/components/ui/badge';\nimport {\n Table,\n TableBody,\n TableCell,\n TableHead,\n TableHeader,\n TableRow,\n} from '@/components/ui/table';\nimport {\n Dialog,\n DialogContent,\n DialogHeader,\n DialogTitle,\n DialogFooter,\n} from '@/components/ui/dialog';\nimport { Label } from '@/components/ui/label';\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@/components/ui/select';\nimport { Plus, Search, Edit, Trash2 } from 'lucide-react';\nimport { useToast } from '@/hooks/use-toast';\nimport { useTranslations } from 'next-intl';\n\ninterface Item {\n id: number;\n die_code: string;\n die_name: string;\n die_type: number;\n size_spec: string;\n product_name: string;\n max_use_count: number;\n used_count: number;\n remaining_count: number;\n status: number;\n}\nconst typeMap: Record<number, string> = {\n 1: '模切刀',\n 2: '分切刀',\n 3: '压痕刀',\n 4: '冲孔刀',\n};\nconst statusMap: Record<\n number,\n { label: string; variant: 'default' | 'secondary' | 'destructive' | 'outline' }\n> = {\n 1: { label: '在用', variant: 'default' },\n 2: { label: '待保养', variant: 'outline' },\n 3: { label: '保养中', variant: 'secondary' },\n 4: { label: '已报废', variant: 'destructive' },\n};\n\nexport default function DieManagementPage() {\n // 翻译钩子\n const tc = useTranslations('Common');\n\n const { toast } = useToast();\n const [list, setList] = useState<Item[]>([]);\n const [total, setTotal] = useState(0);\n const [page, setPage] = useState(1);\n const [searchCode, setSearchCode] = useState('');\n const [searchName, setSearchName] = useState('');\n const [showDialog, setShowDialog] = useState(false);\n const [editItem, setEditItem] = useState<Partial<Item>>({});\n\n const fetchData = async () => {\n try {\n const params = new URLSearchParams({\n page: String(page),\n pageSize: '20',\n dieCode: searchCode,\n dieName: searchName,\n });\n const res = await authFetch('/api/prepress/die?' + params);\n const result = await res.json();\n if (result.success) {\n setList(result.data.list || []);\n setTotal(result.data.total || 0);\n }\n } catch {}\n };\n useEffect(() => {\n fetchData();\n }, [page]);\n\n const handleSave = async () => {\n try {\n const method = editItem.id ? 'PUT' : 'POST';\n const res = await authFetch('/api/prepress/die', {\n method,\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(editItem),\n });\n const result = await res.json();\n if (result.success) {\n toast({ title: editItem.id ? tc('updateSuccess') : tc('createSuccess') });\n setShowDialog(false);\n fetchData();\n } else {\n toast({ title: tc('error'), description: result.message, variant: 'destructive' });\n }\n } catch {\n toast({ title: tc('error'), variant: 'destructive' });\n }\n };\n const handleDelete = async (id: number) => {\n if (!confirm(tc('confirmDelete'))) return;\n try {\n const res = await authFetch('/api/prepress/die?id=' + id, { method: 'DELETE' });\n const result = await res.json();\n if (result.success) {\n toast({ title: tc('deleteSuccess') });\n fetchData();\n }\n } catch {\n toast({ title: tc('error'), variant: 'destructive' });\n }\n };\n\n return (\n <MainLayout>\n <div className=\"p-6 space-y-6\">\n <div className=\"flex items-center justify-between\">\n <h1 className=\"text-2xl font-bold\">{tc('dcDieMgmtTitle')}</h1>\n <div className=\"flex gap-2\">\n <div className=\"flex items-center gap-2\">\n <Input\n placeholder={tc('code')}\n value={searchCode}\n onChange={(e) => setSearchCode(e.target.value)}\n className=\"w-28 h-8 text-sm\"\n />\n <Input\n placeholder={tc('name')}\n value={searchName}\n onChange={(e) => setSearchName(e.target.value)}\n className=\"w-28 h-8 text-sm\"\n />\n <Button size=\"sm\" variant=\"outline\" onClick={fetchData}>\n <Search className=\"h-3 w-3\" />\n </Button>\n </div>\n <Button\n size=\"sm\"\n onClick={() => {\n setEditItem({});\n setShowDialog(true);\n }}\n >\n <Plus className=\"h-3 w-3 mr-1\" />\n 新增刀具\n </Button>\n </div>\n </div>\n <Card>\n <CardContent className=\"p-0\">\n <Table>\n <TableHeader>\n <TableRow>\n <TableHead className=\"text-xs\">刀具编码</TableHead>\n <TableHead className=\"text-xs\">刀具名称</TableHead>\n <TableHead className=\"text-xs\">{tc('type')}</TableHead>\n <TableHead className=\"text-xs\">尺寸规格</TableHead>\n <TableHead className=\"text-xs\">{tc('product')}</TableHead>\n <TableHead className=\"text-xs\">{tc('dcMaxUseCountHead')}</TableHead>\n <TableHead className=\"text-xs\">已用</TableHead>\n <TableHead className=\"text-xs\">{tc('dcRemainingCountHead')}</TableHead>\n <TableHead className=\"text-xs\">{tc('status')}</TableHead>\n <TableHead className=\"text-xs\">{tc('actions')}</TableHead>\n </TableRow>\n </TableHeader>\n <TableBody>\n {list.map((item) => {\n const st = statusMap[item.status] || statusMap[1];\n const warn = item.remaining_count <= item.max_use_count * 0.2;\n return (\n <TableRow key={item.id}>\n <TableCell className=\"text-xs font-mono\">{item.die_code}</TableCell>\n <TableCell className=\"text-xs\">{item.die_name}</TableCell>\n <TableCell className=\"text-xs\">{typeMap[item.die_type] || '-'}</TableCell>\n <TableCell className=\"text-xs\">{item.size_spec || '-'}</TableCell>\n <TableCell className=\"text-xs\">{item.product_name || '-'}</TableCell>\n <TableCell className=\"text-xs\">{item.max_use_count}</TableCell>\n <TableCell className=\"text-xs\">{item.used_count ?? 0}</TableCell>\n <TableCell className=\"text-xs\">\n {warn ? (\n <span className=\"text-red-500 font-bold\">{item.remaining_count}</span>\n ) : (\n item.remaining_count\n )}\n </TableCell>\n <TableCell>\n <Badge variant={st.variant} className=\"text-xs\">\n {st.label}\n </Badge>\n </TableCell>\n <TableCell>\n <div className=\"flex gap-1\">\n <Button\n size=\"sm\"\n variant=\"ghost\"\n className=\"h-6 w-6 p-0\"\n onClick={() => {\n setEditItem(item);\n setShowDialog(true);\n }}\n >\n <Edit className=\"h-3 w-3\" />\n </Button>\n <Button\n size=\"sm\"\n variant=\"ghost\"\n className=\"h-6 w-6 p-0 text-red-600\"\n onClick={() => handleDelete(item.id)}\n >\n <Trash2 className=\"h-3 w-3\" />\n </Button>\n </div>\n </TableCell>\n </TableRow>\n );\n })}\n {list.length === 0 && (\n <TableRow>\n <TableCell colSpan={10} className=\"text-center text-gray-400 py-8\">\n 暂无记录\n </TableCell>\n </TableRow>\n )}\n </TableBody>\n </Table>\n </CardContent>\n </Card>\n <div className=\"flex items-center justify-between\">\n <span className=\"text-sm text-gray-500\">共{total}条</span>\n <div className=\"flex gap-2\">\n <Button\n size=\"sm\"\n variant=\"outline\"\n disabled={page <= 1}\n onClick={() => setPage((p) => p - 1)}\n >\n 上一页\n </Button>\n <Button\n size=\"sm\"\n variant=\"outline\"\n disabled={page * 20 >= total}\n onClick={() => setPage((p) => p + 1)}\n >\n 下一页\n </Button>\n </div>\n </div>\n <Dialog open={showDialog} onOpenChange={setShowDialog}>\n <DialogContent className=\"max-w-lg\" resizable>\n <DialogHeader>\n <DialogTitle>{editItem.id ? '编辑刀具' : '新增刀具'}</DialogTitle>\n </DialogHeader>\n <div className=\"grid grid-cols-2 gap-4\">\n <div>\n <Label>刀具编码</Label>\n <Input\n value={editItem.die_code || ''}\n onChange={(e) => setEditItem({ ...editItem, die_code: e.target.value })}\n />\n </div>\n <div>\n <Label>刀具名称</Label>\n <Input\n value={editItem.die_name || ''}\n onChange={(e) => setEditItem({ ...editItem, die_name: e.target.value })}\n />\n </div>\n <div>\n <Label>{tc('type')}</Label>\n <Select\n value={String(editItem.die_type || 1)}\n onValueChange={(v) => setEditItem({ ...editItem, die_type: Number(v) })}\n >\n <SelectTrigger>\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"1\">模切刀</SelectItem>\n <SelectItem value=\"2\">分切刀</SelectItem>\n <SelectItem value=\"3\">压痕刀</SelectItem>\n <SelectItem value=\"4\">冲孔刀</SelectItem>\n </SelectContent>\n </Select>\n </div>\n <div>\n <Label>尺寸规格</Label>\n <Input\n value={editItem.size_spec || ''}\n onChange={(e) => setEditItem({ ...editItem, size_spec: e.target.value })}\n />\n </div>\n <div>\n <Label>产品名称</Label>\n <Input\n value={editItem.product_name || ''}\n onChange={(e) => setEditItem({ ...editItem, product_name: e.target.value })}\n />\n </div>\n <div>\n <Label>最大使用次数</Label>\n <Input\n type=\"number\"\n value={editItem.max_use_count ?? ''}\n onChange={(e) =>\n setEditItem({ ...editItem, max_use_count: Number(e.target.value) })\n }\n />\n </div>\n </div>\n <DialogFooter>\n <Button variant=\"outline\" onClick={() => setShowDialog(false)}>\n 取消\n </Button>\n <Button onClick={handleSave}>{tc('save')}</Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n </div>\n </MainLayout>\n );\n}\n","usedDeprecatedRules":[]},{"filePath":"D:\\dcprint\\erp-project\\src\\app\\[locale]\\dcprint\\error.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\dcprint\\erp-project\\src\\app\\[locale]\\dcprint\\ink-formula\\page.tsx","messages":[{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"草稿\"。建议使用: tc('text_n02e')","line":76,"column":15,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":76,"endColumn":19},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"已生效\"。建议使用: tc('text_ebukb')","line":77,"column":15,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":77,"endColumn":20},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"已作废\"。建议使用: tc('text_e5e0l')","line":78,"column":15,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":78,"endColumn":20},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":108,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":108,"endColumn":36},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":109,"column":16,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":109,"endColumn":23},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction<InkColor[]>`.","line":110,"column":19,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":110,"endColumn":53},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":110,"column":24,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":110,"endColumn":28},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":110,"column":43,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":110,"endColumn":47},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"错误\"。建议使用: tc('text_q4mu')","line":113,"column":22,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":113,"endColumn":26},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"加载色号失败\"。建议使用: tc('text_95luk6')","line":113,"column":41,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":113,"endColumn":49},{"ruleId":"react-hooks/exhaustive-deps","severity":1,"message":"React Hook useCallback has a missing dependency: 'toast'. Either include it or remove the dependency array.","line":117,"column":6,"nodeType":"ArrayExpression","endLine":117,"endColumn":21,"suggestions":[{"desc":"Update the dependencies array to be: [searchKeyword, toast]","fix":{"range":[3319,3334],"text":"[searchKeyword, toast]"}}]},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":122,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":122,"endColumn":36},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":123,"column":16,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":123,"endColumn":23},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction<FormulaVersion[]>`.","line":124,"column":21,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":124,"endColumn":55},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":124,"column":26,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":124,"endColumn":30},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":124,"column":45,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":124,"endColumn":49},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"错误\"。建议使用: tc('text_q4mu')","line":127,"column":22,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":127,"endColumn":26},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"加载配方版本失败\"。建议使用: tc('text_h42xi9')","line":127,"column":41,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":127,"endColumn":51},{"ruleId":"react-hooks/exhaustive-deps","severity":1,"message":"React Hook useCallback has a missing dependency: 'toast'. Either include it or remove the dependency array.","line":129,"column":6,"nodeType":"ArrayExpression","endLine":129,"endColumn":8,"suggestions":[{"desc":"Update the dependencies array to be: [toast]","fix":{"range":[3743,3745],"text":"[toast]"}}]},{"ruleId":"react-hooks/set-state-in-effect","severity":1,"message":"Error: Calling setState synchronously within an effect can trigger cascading renders\n\nEffects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following:\n* Update external systems with the latest state from React.\n* Subscribe for updates from some external system, calling setState in a callback function when external state changes.\n\nCalling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect).\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\dcprint\\ink-formula\\page.tsx:132:5\n 130 |\n 131 | useEffect(() => {\n> 132 | fetchColors();\n | ^^^^^^^^^^^ Avoid calling setState() directly within an effect\n 133 | }, [fetchColors]);\n 134 | useEffect(() => {\n 135 | if (selectedColor) fetchVersions(selectedColor.id);","line":132,"column":5,"nodeType":null,"endLine":132,"endColumn":16},{"ruleId":"react-hooks/set-state-in-effect","severity":1,"message":"Error: Calling setState synchronously within an effect can trigger cascading renders\n\nEffects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following:\n* Update external systems with the latest state from React.\n* Subscribe for updates from some external system, calling setState in a callback function when external state changes.\n\nCalling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect).\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\dcprint\\ink-formula\\page.tsx:135:24\n 133 | }, [fetchColors]);\n 134 | useEffect(() => {\n> 135 | if (selectedColor) fetchVersions(selectedColor.id);\n | ^^^^^^^^^^^^^ Avoid calling setState() directly within an effect\n 136 | }, [selectedColor, fetchVersions]);\n 137 |\n 138 | const handleSaveColor = async () => {","line":135,"column":24,"nodeType":null,"endLine":135,"endColumn":37},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"警告\"。建议使用: tc('text_o690')","line":140,"column":22,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":140,"endColumn":26},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"色号编码和名称不能为空\"。建议使用: tc('text_pcjjhr')","line":140,"column":41,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":140,"endColumn":54},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":153,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":153,"endColumn":36},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":154,"column":16,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":154,"endColumn":23},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"成功\"。建议使用: tc('text_h4sv')","line":155,"column":24,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":155,"endColumn":28},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"错误\"。建议使用: tc('text_q4mu')","line":167,"column":24,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":167,"endColumn":28},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":167,"column":30,"nodeType":"Property","messageId":"anyAssignment","endLine":167,"endColumn":65},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .message on an `any` value.","line":167,"column":48,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":167,"endColumn":55},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"错误\"。建议使用: tc('text_q4mu')","line":170,"column":22,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":170,"endColumn":26},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"操作失败\"。建议使用: tc('text_d1rj43')","line":170,"column":41,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":170,"endColumn":47},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":179,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":179,"endColumn":36},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":180,"column":16,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":180,"endColumn":23},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"成功\"。建议使用: tc('text_h4sv')","line":181,"column":24,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":181,"endColumn":28},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"配方版本已生效\"。建议使用: tc('text_qu6w7f')","line":181,"column":43,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":181,"endColumn":52},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"错误\"。建议使用: tc('text_q4mu')","line":184,"column":24,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":184,"endColumn":28},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":184,"column":30,"nodeType":"Property","messageId":"anyAssignment","endLine":184,"endColumn":65},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .message on an `any` value.","line":184,"column":48,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":184,"endColumn":55},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"错误\"。建议使用: tc('text_q4mu')","line":187,"column":22,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":187,"endColumn":26},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"操作失败\"。建议使用: tc('text_d1rj43')","line":187,"column":41,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":187,"endColumn":47},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":196,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":196,"endColumn":36},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":197,"column":16,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":197,"endColumn":23},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"成功\"。建议使用: tc('text_h4sv')","line":198,"column":24,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":198,"endColumn":28},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"配方版本已作废\"。建议使用: tc('text_qu0fnp')","line":198,"column":43,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":198,"endColumn":52},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"错误\"。建议使用: tc('text_q4mu')","line":201,"column":24,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":201,"endColumn":28},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":201,"column":30,"nodeType":"Property","messageId":"anyAssignment","endLine":201,"endColumn":65},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .message on an `any` value.","line":201,"column":48,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":201,"endColumn":55},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"错误\"。建议使用: tc('text_q4mu')","line":204,"column":22,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":204,"endColumn":26},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"操作失败\"。建议使用: tc('text_d1rj43')","line":204,"column":41,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":204,"endColumn":47},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":213,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":213,"endColumn":36},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":214,"column":16,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":214,"endColumn":23},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"成功\"。建议使用: tc('text_h4sv')","line":215,"column":24,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":215,"endColumn":28},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"新版本已创建\"。建议使用: tc('text_5f4il')","line":215,"column":43,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":215,"endColumn":51},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"错误\"。建议使用: tc('text_q4mu')","line":218,"column":24,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":218,"endColumn":28},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":218,"column":30,"nodeType":"Property","messageId":"anyAssignment","endLine":218,"endColumn":65},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .message on an `any` value.","line":218,"column":48,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":218,"endColumn":55},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"错误\"。建议使用: tc('text_q4mu')","line":221,"column":22,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":221,"endColumn":26},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"操作失败\"。建议使用: tc('text_d1rj43')","line":221,"column":41,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":221,"endColumn":47},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"色号\"。建议使用: {tc('text_mnd1')}","line":263,"column":28,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":264,"endColumn":15},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"色号编码\"。建议使用: {tc('text_gt7wog')}","line":278,"column":32,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":278,"endColumn":36},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"色号名称\"。建议使用: {tc('text_gt0ljc')}","line":279,"column":32,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":279,"endColumn":36},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"色系\"。建议使用: {tc('text_mvgp')}","line":280,"column":32,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":280,"endColumn":34},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"基墨类型\"。建议使用: {tc('text_bh1zha')}","line":281,"column":32,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":281,"endColumn":36},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"状态\"。建议使用: {tc('text_k1e3')}","line":283,"column":32,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":283,"endColumn":34},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"- 配方版本\"。建议使用: {tc('text_uha29p')}","line":344,"column":49,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":344,"endColumn":56},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"新建版本\"。建议使用: {tc('text_d86vu6')}","line":350,"column":52,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":352,"endColumn":17},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"暂无配方版本\"。建议使用: {tc('text_elxunm')}","line":357,"column":73,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":357,"endColumn":79},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"版本号\"。建议使用: {tc('text_h8m1f')}","line":362,"column":34,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":362,"endColumn":37},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"版本名称\"。建议使用: {tc('text_eufntz')}","line":363,"column":34,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":363,"endColumn":38},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"总重量\"。建议使用: {tc('text_et0rh')}","line":364,"column":34,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":364,"endColumn":37},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"理论成本\"。建议使用: {tc('text_f7rh5c')}","line":365,"column":34,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":365,"endColumn":38},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"状态\"。建议使用: {tc('text_k1e3')}","line":366,"column":34,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":366,"endColumn":36},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"生效时间\"。建议使用: {tc('text_f753lz')}","line":367,"column":34,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":367,"endColumn":38},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"填写色号基础信息\"。建议使用: {tc('text_nqohdl')}","line":438,"column":32,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":438,"endColumn":40},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"色号编码\"。建议使用: {tc('text_gt7wog')}","line":442,"column":22,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":443,"endColumn":22},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"色号名称\"。建议使用: {tc('text_gt0ljc')}","line":451,"column":22,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":452,"endColumn":22},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"色系\"。建议使用: {tc('text_mvgp')}","line":460,"column":22,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":460,"endColumn":24},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"基墨类型\"。建议使用: {tc('text_bh1zha')}","line":467,"column":22,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":467,"endColumn":26},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"Pantone色号\"。建议使用: {tc('text_fsw5f8')}","line":474,"column":22,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":474,"endColumn":31}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":79,"fixableErrorCount":0,"fixableWarningCount":0,"source":"'use client';\r\n\r\nimport { useState, useEffect, useCallback } from 'react';\r\nimport { useTranslations } from 'next-intl';\r\nimport { useRouter } from 'next/navigation';\r\nimport { MainLayout } from '@/components/layout/main-layout';\r\nimport { useToastContext } from '@/components/ui/toast';\r\nimport { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';\r\nimport { Button } from '@/components/ui/button';\r\nimport { Input } from '@/components/ui/input';\r\nimport { Label } from '@/components/ui/label';\r\nimport { Badge } from '@/components/ui/badge';\r\nimport {\r\n Table,\r\n TableBody,\r\n TableCell,\r\n TableHead,\r\n TableHeader,\r\n TableRow,\r\n} from '@/components/ui/table';\r\nimport {\r\n Dialog,\r\n DialogContent,\r\n DialogHeader,\r\n DialogTitle,\r\n DialogDescription,\r\n} from '@/components/ui/dialog';\r\nimport {\r\n DropdownMenu,\r\n DropdownMenuContent,\r\n DropdownMenuItem,\r\n DropdownMenuTrigger,\r\n} from '@/components/ui/dropdown-menu';\r\nimport {\r\n Plus,\r\n Search,\r\n Beaker,\r\n MoreHorizontal,\r\n Eye,\r\n Edit,\r\n Copy,\r\n CheckCircle,\r\n XCircle,\r\n} from 'lucide-react';\r\nimport { authFetch } from '@/lib/auth-fetch';\r\n\r\ninterface InkColor {\r\n id: number;\r\n color_code: string;\r\n color_name: string;\r\n color_series: string;\r\n base_ink_type: string;\r\n pantone_code: string;\r\n status: number;\r\n create_time: string;\r\n}\r\n\r\ninterface FormulaVersion {\r\n id: number;\r\n color_id: number;\r\n version_no: string;\r\n version_name: string;\r\n status: number;\r\n total_weight: number;\r\n unit: string;\r\n theoretical_cost: number;\r\n cost_calc_status: number;\r\n activate_time: string;\r\n create_time: string;\r\n}\r\n\r\nconst STATUS_MAP: Record<\r\n number,\r\n { label: string; variant: 'default' | 'secondary' | 'destructive' | 'outline' }\r\n> = {\r\n 1: { label: '草稿', variant: 'secondary' },\r\n 2: { label: '已生效', variant: 'default' },\r\n 3: { label: '已作废', variant: 'destructive' },\r\n};\r\n\r\nexport default function InkFormulaPage() {\r\n const t = useTranslations('Dcprint');\r\n const tc = useTranslations('Common');\r\n const router = useRouter();\r\n const { addToast: toast } = useToastContext();\r\n\r\n const [colors, setColors] = useState<InkColor[]>([]);\r\n const [selectedColor, setSelectedColor] = useState<InkColor | null>(null);\r\n const [versions, setVersions] = useState<FormulaVersion[]>([]);\r\n const [loading, setLoading] = useState(false);\r\n const [searchKeyword, setSearchKeyword] = useState('');\r\n const [isColorDialogOpen, setIsColorDialogOpen] = useState(false);\r\n const [editingColor, setEditingColor] = useState<InkColor | null>(null);\r\n const [colorForm, setColorForm] = useState({\r\n color_code: '',\r\n color_name: '',\r\n color_series: '',\r\n base_ink_type: '',\r\n pantone_code: '',\r\n });\r\n\r\n const fetchColors = useCallback(async () => {\r\n setLoading(true);\r\n try {\r\n const params = new URLSearchParams({ page: '1', pageSize: '100' });\r\n if (searchKeyword) params.append('keyword', searchKeyword);\r\n const res = await authFetch(`/api/dcprint/formula/color?${params}`);\r\n const data = await res.json();\r\n if (data.success) {\r\n setColors(data.data?.list || data.data || []);\r\n }\r\n } catch {\r\n toast({ title: '错误', description: '加载色号失败', variant: 'destructive' });\r\n } finally {\r\n setLoading(false);\r\n }\r\n }, [searchKeyword]);\r\n\r\n const fetchVersions = useCallback(async (colorId: number) => {\r\n try {\r\n const res = await authFetch(`/api/dcprint/formula/version?colorId=${colorId}`);\r\n const data = await res.json();\r\n if (data.success) {\r\n setVersions(data.data?.list || data.data || []);\r\n }\r\n } catch {\r\n toast({ title: '错误', description: '加载配方版本失败', variant: 'destructive' });\r\n }\r\n }, []);\r\n\r\n useEffect(() => {\r\n fetchColors();\r\n }, [fetchColors]);\r\n useEffect(() => {\r\n if (selectedColor) fetchVersions(selectedColor.id);\r\n }, [selectedColor, fetchVersions]);\r\n\r\n const handleSaveColor = async () => {\r\n if (!colorForm.color_code || !colorForm.color_name) {\r\n toast({ title: '警告', description: '色号编码和名称不能为空', variant: 'destructive' });\r\n return;\r\n }\r\n try {\r\n const url = editingColor\r\n ? `/api/dcprint/formula/color/${editingColor.id}`\r\n : '/api/dcprint/formula/color';\r\n const method = editingColor ? 'PUT' : 'POST';\r\n const res = await authFetch(url, {\r\n method,\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify(colorForm),\r\n });\r\n const data = await res.json();\r\n if (data.success) {\r\n toast({ title: '成功', description: editingColor ? '更新成功' : '创建成功' });\r\n setIsColorDialogOpen(false);\r\n setEditingColor(null);\r\n setColorForm({\r\n color_code: '',\r\n color_name: '',\r\n color_series: '',\r\n base_ink_type: '',\r\n pantone_code: '',\r\n });\r\n fetchColors();\r\n } else {\r\n toast({ title: '错误', description: data.message || '操作失败', variant: 'destructive' });\r\n }\r\n } catch {\r\n toast({ title: '错误', description: '操作失败', variant: 'destructive' });\r\n }\r\n };\r\n\r\n const handleActivateVersion = async (versionId: number) => {\r\n try {\r\n const res = await authFetch(`/api/dcprint/formula/version/${versionId}/activate`, {\r\n method: 'POST',\r\n });\r\n const data = await res.json();\r\n if (data.success) {\r\n toast({ title: '成功', description: '配方版本已生效' });\r\n if (selectedColor) fetchVersions(selectedColor.id);\r\n } else {\r\n toast({ title: '错误', description: data.message || '操作失败', variant: 'destructive' });\r\n }\r\n } catch {\r\n toast({ title: '错误', description: '操作失败', variant: 'destructive' });\r\n }\r\n };\r\n\r\n const handleCancelVersion = async (versionId: number) => {\r\n try {\r\n const res = await authFetch(`/api/dcprint/formula/version/${versionId}/cancel`, {\r\n method: 'POST',\r\n });\r\n const data = await res.json();\r\n if (data.success) {\r\n toast({ title: '成功', description: '配方版本已作废' });\r\n if (selectedColor) fetchVersions(selectedColor.id);\r\n } else {\r\n toast({ title: '错误', description: data.message || '操作失败', variant: 'destructive' });\r\n }\r\n } catch {\r\n toast({ title: '错误', description: '操作失败', variant: 'destructive' });\r\n }\r\n };\r\n\r\n const handleDuplicateVersion = async (versionId: number) => {\r\n try {\r\n const res = await authFetch(`/api/dcprint/formula/version/${versionId}/duplicate`, {\r\n method: 'POST',\r\n });\r\n const data = await res.json();\r\n if (data.success) {\r\n toast({ title: '成功', description: '新版本已创建' });\r\n if (selectedColor) fetchVersions(selectedColor.id);\r\n } else {\r\n toast({ title: '错误', description: data.message || '操作失败', variant: 'destructive' });\r\n }\r\n } catch {\r\n toast({ title: '错误', description: '操作失败', variant: 'destructive' });\r\n }\r\n };\r\n\r\n return (\r\n <MainLayout title={t('inkFormulaManagement')}>\r\n <div className=\"space-y-6\">\r\n <Card>\r\n <CardHeader>\r\n <CardTitle className=\"flex items-center gap-2\">\r\n <Beaker className=\"h-5 w-5\" />\r\n {t('inkColorList')}\r\n </CardTitle>\r\n </CardHeader>\r\n <CardContent>\r\n <div className=\"flex items-center gap-4 mb-4\">\r\n <div className=\"flex-1 max-w-sm\">\r\n <Input\r\n placeholder={tc('search')}\r\n value={searchKeyword}\r\n onChange={(e) => setSearchKeyword(e.target.value)}\r\n onKeyDown={(e) => e.key === 'Enter' && fetchColors()}\r\n />\r\n </div>\r\n <Button onClick={() => fetchColors()} variant=\"outline\">\r\n <Search className=\"h-4 w-4 mr-2\" />\r\n {tc('search')}\r\n </Button>\r\n <Button\r\n onClick={() => {\r\n setEditingColor(null);\r\n setColorForm({\r\n color_code: '',\r\n color_name: '',\r\n color_series: '',\r\n base_ink_type: '',\r\n pantone_code: '',\r\n });\r\n setIsColorDialogOpen(true);\r\n }}\r\n >\r\n <Plus className=\"h-4 w-4 mr-2\" />\r\n {tc('add')}色号\r\n </Button>\r\n </div>\r\n\r\n {loading ? (\r\n <div className=\"text-center py-8 text-muted-foreground\">{tc('loading')}</div>\r\n ) : colors.length === 0 ? (\r\n <div className=\"text-center py-8 text-muted-foreground\">\r\n <Beaker className=\"h-12 w-12 mx-auto mb-2 opacity-50\" />\r\n <p>{tc('noData')}</p>\r\n </div>\r\n ) : (\r\n <Table>\r\n <TableHeader>\r\n <TableRow>\r\n <TableHead>色号编码</TableHead>\r\n <TableHead>色号名称</TableHead>\r\n <TableHead>色系</TableHead>\r\n <TableHead>基墨类型</TableHead>\r\n <TableHead>Pantone</TableHead>\r\n <TableHead>状态</TableHead>\r\n <TableHead>{tc('actions')}</TableHead>\r\n </TableRow>\r\n </TableHeader>\r\n <TableBody>\r\n {colors.map((color) => (\r\n <TableRow\r\n key={color.id}\r\n className={selectedColor?.id === color.id ? 'bg-muted' : 'cursor-pointer'}\r\n onClick={() => setSelectedColor(color)}\r\n >\r\n <TableCell className=\"font-mono\">{color.color_code}</TableCell>\r\n <TableCell>{color.color_name}</TableCell>\r\n <TableCell>{color.color_series}</TableCell>\r\n <TableCell>{color.base_ink_type}</TableCell>\r\n <TableCell>{color.pantone_code}</TableCell>\r\n <TableCell>\r\n <Badge variant={color.status === 1 ? 'default' : 'secondary'}>\r\n {color.status === 1 ? '正常' : '停用'}\r\n </Badge>\r\n </TableCell>\r\n <TableCell>\r\n <DropdownMenu>\r\n <DropdownMenuTrigger asChild onClick={(e) => e.stopPropagation()}>\r\n <Button variant=\"ghost\" size=\"sm\">\r\n <MoreHorizontal className=\"h-4 w-4\" />\r\n </Button>\r\n </DropdownMenuTrigger>\r\n <DropdownMenuContent>\r\n <DropdownMenuItem\r\n onClick={(e) => {\r\n e.stopPropagation();\r\n setEditingColor(color);\r\n setColorForm({\r\n color_code: color.color_code,\r\n color_name: color.color_name,\r\n color_series: color.color_series || '',\r\n base_ink_type: color.base_ink_type || '',\r\n pantone_code: color.pantone_code || '',\r\n });\r\n setIsColorDialogOpen(true);\r\n }}\r\n >\r\n <Edit className=\"h-4 w-4 mr-2\" />\r\n {tc('edit')}\r\n </DropdownMenuItem>\r\n </DropdownMenuContent>\r\n </DropdownMenu>\r\n </TableCell>\r\n </TableRow>\r\n ))}\r\n </TableBody>\r\n </Table>\r\n )}\r\n </CardContent>\r\n </Card>\r\n\r\n {selectedColor && (\r\n <Card>\r\n <CardHeader>\r\n <CardTitle className=\"flex items-center justify-between\">\r\n <span>{selectedColor.color_name} - 配方版本</span>\r\n <Button\r\n onClick={() =>\r\n router.push(`/dcprint/ink-formula/new?colorId=${selectedColor.id}`)\r\n }\r\n >\r\n <Plus className=\"h-4 w-4 mr-2\" />\r\n 新建版本\r\n </Button>\r\n </CardTitle>\r\n </CardHeader>\r\n <CardContent>\r\n {versions.length === 0 ? (\r\n <div className=\"text-center py-8 text-muted-foreground\">暂无配方版本</div>\r\n ) : (\r\n <Table>\r\n <TableHeader>\r\n <TableRow>\r\n <TableHead>版本号</TableHead>\r\n <TableHead>版本名称</TableHead>\r\n <TableHead>总重量</TableHead>\r\n <TableHead>理论成本</TableHead>\r\n <TableHead>状态</TableHead>\r\n <TableHead>生效时间</TableHead>\r\n <TableHead>{tc('actions')}</TableHead>\r\n </TableRow>\r\n </TableHeader>\r\n <TableBody>\r\n {versions.map((ver) => (\r\n <TableRow key={ver.id}>\r\n <TableCell className=\"font-mono\">{ver.version_no}</TableCell>\r\n <TableCell>{ver.version_name}</TableCell>\r\n <TableCell>\r\n {ver.total_weight} {ver.unit}\r\n </TableCell>\r\n <TableCell>¥{ver.theoretical_cost?.toFixed(2) || '0.00'}</TableCell>\r\n <TableCell>\r\n <Badge variant={STATUS_MAP[ver.status]?.variant || 'secondary'}>\r\n {STATUS_MAP[ver.status]?.label || '未知'}\r\n </Badge>\r\n </TableCell>\r\n <TableCell>{ver.activate_time || '-'}</TableCell>\r\n <TableCell>\r\n <div className=\"flex gap-1\">\r\n <Button\r\n variant=\"ghost\"\r\n size=\"sm\"\r\n onClick={() => router.push(`/dcprint/ink-formula/${ver.id}`)}\r\n >\r\n <Eye className=\"h-4 w-4\" />\r\n </Button>\r\n {ver.status === 1 && (\r\n <>\r\n <Button\r\n variant=\"ghost\"\r\n size=\"sm\"\r\n onClick={() => handleActivateVersion(ver.id)}\r\n >\r\n <CheckCircle className=\"h-4 w-4 text-green-500\" />\r\n </Button>\r\n <Button\r\n variant=\"ghost\"\r\n size=\"sm\"\r\n onClick={() => handleDuplicateVersion(ver.id)}\r\n >\r\n <Copy className=\"h-4 w-4\" />\r\n </Button>\r\n </>\r\n )}\r\n {ver.status === 2 && (\r\n <Button\r\n variant=\"ghost\"\r\n size=\"sm\"\r\n onClick={() => handleCancelVersion(ver.id)}\r\n >\r\n <XCircle className=\"h-4 w-4 text-red-500\" />\r\n </Button>\r\n )}\r\n </div>\r\n </TableCell>\r\n </TableRow>\r\n ))}\r\n </TableBody>\r\n </Table>\r\n )}\r\n </CardContent>\r\n </Card>\r\n )}\r\n </div>\r\n\r\n <Dialog open={isColorDialogOpen} onOpenChange={setIsColorDialogOpen}>\r\n <DialogContent>\r\n <DialogHeader>\r\n <DialogTitle>{editingColor ? '编辑色号' : '新建色号'}</DialogTitle>\r\n <DialogDescription>填写色号基础信息</DialogDescription>\r\n </DialogHeader>\r\n <div className=\"grid grid-cols-2 gap-4\">\r\n <div className=\"space-y-1\">\r\n <Label>\r\n 色号编码 <span className=\"text-red-500\">*</span>\r\n </Label>\r\n <Input\r\n value={colorForm.color_code}\r\n onChange={(e) => setColorForm({ ...colorForm, color_code: e.target.value })}\r\n />\r\n </div>\r\n <div className=\"space-y-1\">\r\n <Label>\r\n 色号名称 <span className=\"text-red-500\">*</span>\r\n </Label>\r\n <Input\r\n value={colorForm.color_name}\r\n onChange={(e) => setColorForm({ ...colorForm, color_name: e.target.value })}\r\n />\r\n </div>\r\n <div className=\"space-y-1\">\r\n <Label>色系</Label>\r\n <Input\r\n value={colorForm.color_series}\r\n onChange={(e) => setColorForm({ ...colorForm, color_series: e.target.value })}\r\n />\r\n </div>\r\n <div className=\"space-y-1\">\r\n <Label>基墨类型</Label>\r\n <Input\r\n value={colorForm.base_ink_type}\r\n onChange={(e) => setColorForm({ ...colorForm, base_ink_type: e.target.value })}\r\n />\r\n </div>\r\n <div className=\"space-y-1 col-span-2\">\r\n <Label>Pantone色号</Label>\r\n <Input\r\n value={colorForm.pantone_code}\r\n onChange={(e) => setColorForm({ ...colorForm, pantone_code: e.target.value })}\r\n />\r\n </div>\r\n </div>\r\n <div className=\"flex justify-end gap-2 mt-4\">\r\n <Button variant=\"outline\" onClick={() => setIsColorDialogOpen(false)}>\r\n {tc('cancel')}\r\n </Button>\r\n <Button onClick={handleSaveColor}>{tc('save')}</Button>\r\n </div>\r\n </DialogContent>\r\n </Dialog>\r\n </MainLayout>\r\n );\r\n}\r\n","usedDeprecatedRules":[]},{"filePath":"D:\\dcprint\\erp-project\\src\\app\\[locale]\\dcprint\\ink-mixed\\page.tsx","messages":[{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"已入库\"。建议使用: tc('text_e5qgw')","line":65,"column":15,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":65,"endColumn":20},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"已使用\"。建议使用: tc('text_e5jaz')","line":66,"column":15,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":66,"endColumn":20},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"已过期\"。建议使用: tc('text_ege5m')","line":67,"column":15,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":67,"endColumn":20},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":92,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":92,"endColumn":38},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":93,"column":18,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":93,"endColumn":25},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction<InkMixedRecord[]>`.","line":94,"column":17,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":94,"endColumn":39},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":94,"column":24,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":94,"endColumn":28},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction<number>`.","line":95,"column":18,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":95,"endColumn":40},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":95,"column":25,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":95,"endColumn":29},{"ruleId":"react-hooks/set-state-in-effect","severity":1,"message":"Error: Calling setState synchronously within an effect can trigger cascading renders\n\nEffects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following:\n* Update external systems with the latest state from React.\n* Subscribe for updates from some external system, calling setState in a callback function when external state changes.\n\nCalling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect).\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\dcprint\\ink-mixed\\page.tsx:101:5\n 99 |\n 100 | useEffect(() => {\n> 101 | fetchData();\n | ^^^^^^^^^ Avoid calling setState() directly within an effect\n 102 | }, [page]);\n 103 |\n 104 | const handleSave = async () => {","line":101,"column":5,"nodeType":null,"endLine":101,"endColumn":14},{"ruleId":"react-hooks/exhaustive-deps","severity":1,"message":"React Hook useEffect has a missing dependency: 'fetchData'. Either include it or remove the dependency array.","line":102,"column":6,"nodeType":"ArrayExpression","endLine":102,"endColumn":12,"suggestions":[{"desc":"Update the dependencies array to be: [fetchData, page]","fix":{"range":[2663,2669],"text":"[fetchData, page]"}}]},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":112,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":112,"endColumn":38},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":113,"column":18,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":113,"endColumn":25},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":118,"column":37,"nodeType":"Property","messageId":"anyAssignment","endLine":118,"endColumn":64},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .message on an `any` value.","line":118,"column":57,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":118,"endColumn":64},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":129,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":129,"endColumn":38},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":130,"column":18,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":130,"endColumn":25},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":146,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":146,"endColumn":38},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":147,"column":18,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":147,"endColumn":25},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"新增入库\"。建议使用: {tc('text_d73pks')}","line":186,"column":48,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":188,"endColumn":13},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"记录单号\"。建议使用: {tc('text_i0myef')}","line":197,"column":50,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":197,"endColumn":54},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"调色比例\"。建议使用: {tc('text_i7d8w6')}","line":199,"column":50,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":199,"endColumn":54},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"色彩名称\"。建议使用: {tc('text_guoy62')}","line":200,"column":50,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":200,"endColumn":54},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"操作员\"。建议使用: {tc('text_f5hc9')}","line":203,"column":50,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":203,"endColumn":53},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"调色时间\"。建议使用: {tc('text_i7cmvh')}","line":204,"column":50,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":204,"endColumn":54},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"过期时间\"。建议使用: {tc('text_ikg3cm')}","line":205,"column":50,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":205,"endColumn":54},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"已使用\"。建议使用: {tc('text_e5jaz')}","line":256,"column":32,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":258,"endColumn":31},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"已过期\"。建议使用: {tc('text_ege5m')}","line":264,"column":32,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":266,"endColumn":31},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"暂无调色油墨记录\"。建议使用: {tc('text_7va1sv')}","line":295,"column":88,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":297,"endColumn":21},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"共\"。建议使用: {tc('text_g35')}","line":306,"column":51,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":307,"endColumn":14},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"上一页\"。建议使用: {tc('text_btlof')}","line":316,"column":14,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":318,"endColumn":13},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"下一页\"。建议使用: {tc('text_btmf4')}","line":324,"column":14,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":326,"endColumn":13},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"原油墨编号\"。建议使用: {tc('text_e3uxo1')}","line":337,"column":24,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":337,"endColumn":29},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"原油墨名称\"。建议使用: {tc('text_e421ov')}","line":344,"column":24,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":344,"endColumn":29},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"调色比例\"。建议使用: {tc('text_i7d8w6')}","line":351,"column":24,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":351,"endColumn":28},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"色彩名称\"。建议使用: {tc('text_guoy62')}","line":359,"column":24,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":359,"endColumn":28},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"色彩编码\"。建议使用: {tc('text_guw9b6')}","line":366,"column":24,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":366,"endColumn":28},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"客户名称\"。建议使用: {tc('text_byvcwo')}","line":374,"column":24,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":374,"endColumn":28},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"调色时间\"。建议使用: {tc('text_i7cmvh')}","line":381,"column":24,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":381,"endColumn":28},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"操作员\"。建议使用: {tc('text_f5hc9')}","line":389,"column":24,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":389,"endColumn":27},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"过期时间\"。建议使用: {tc('text_ikg3cm')}","line":422,"column":24,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":422,"endColumn":28},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"取消\"。建议使用: {tc('text_ev02')}","line":438,"column":78,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":440,"endColumn":15}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":42,"fixableErrorCount":0,"fixableWarningCount":0,"source":"'use client';\n\nimport { authFetch } from '@/lib/auth-fetch';\nimport { useEffect, useState } from 'react';\nimport { MainLayout } from '@/components/layout';\nimport { Card, CardContent } from '@/components/ui/card';\nimport { Button } from '@/components/ui/button';\nimport { Input } from '@/components/ui/input';\nimport { Badge } from '@/components/ui/badge';\nimport {\n Table,\n TableBody,\n TableCell,\n TableHead,\n TableHeader,\n TableRow,\n} from '@/components/ui/table';\nimport {\n Dialog,\n DialogContent,\n DialogHeader,\n DialogTitle,\n DialogFooter,\n} from '@/components/ui/dialog';\nimport { Label } from '@/components/ui/label';\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@/components/ui/select';\nimport { Plus, Search, Edit, Trash2 } from 'lucide-react';\nimport { useToast } from '@/hooks/use-toast';\nimport { UserSelect } from '@/components/ui/user-select';\nimport { useTranslations } from 'next-intl';\n\ninterface InkMixedRecord {\n id: number;\n record_no: string;\n base_ink_id: number;\n base_ink_code: string;\n base_ink_name: string;\n mix_ratio: string;\n color_name: string;\n color_code: string;\n company_id: number;\n company_name: string;\n mix_time: string;\n operator_id: number;\n operator_name: string;\n quantity: number;\n unit: string;\n warehouse_id: number;\n location_id: number;\n status: number;\n expire_time: string;\n remark: string;\n}\n\nconst statusMap: Record<\n number,\n { label: string; variant: 'default' | 'secondary' | 'destructive' }\n> = {\n 1: { label: '已入库', variant: 'default' },\n 2: { label: '已使用', variant: 'secondary' },\n 3: { label: '已过期', variant: 'destructive' },\n};\n\nexport default function InkMixedPage() {\n // 翻译钩子\n const tc = useTranslations('Common');\n\n const { toast } = useToast();\n const [list, setList] = useState<InkMixedRecord[]>([]);\n const [total, setTotal] = useState(0);\n const [page, setPage] = useState(1);\n const [searchNo, setSearchNo] = useState('');\n const [searchColor, setSearchColor] = useState('');\n const [showDialog, setShowDialog] = useState(false);\n const [editItem, setEditItem] = useState<Partial<InkMixedRecord>>({});\n\n const fetchData = async () => {\n try {\n const params = new URLSearchParams({\n page: String(page),\n pageSize: '20',\n recordNo: searchNo,\n colorName: searchColor,\n });\n const res = await authFetch('/api/dcprint/ink-mixed?' + params);\n const result = await res.json();\n if (result.success) {\n setList(result.data.list || []);\n setTotal(result.data.total || 0);\n }\n } catch {}\n };\n\n useEffect(() => {\n fetchData();\n }, [page]);\n\n const handleSave = async () => {\n try {\n const method = editItem.id ? 'PUT' : 'POST';\n const res = await authFetch('/api/dcprint/ink-mixed', {\n method,\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(editItem),\n });\n const result = await res.json();\n if (result.success) {\n toast({ title: editItem.id ? '更新成功' : '入库成功' });\n setShowDialog(false);\n fetchData();\n } else {\n toast({ title: tc('error'), description: result.message, variant: 'destructive' });\n }\n } catch {\n toast({ title: tc('error'), variant: 'destructive' });\n }\n };\n\n const handleDelete = async (id: number) => {\n if (!confirm(tc('confirmDelete'))) return;\n try {\n const res = await authFetch('/api/dcprint/ink-mixed?id=' + id, { method: 'DELETE' });\n const result = await res.json();\n if (result.success) {\n toast({ title: tc('deleteSuccess') });\n fetchData();\n }\n } catch {\n toast({ title: tc('deleteFailed'), variant: 'destructive' });\n }\n };\n\n const handleStatusChange = async (id: number, status: number) => {\n try {\n const res = await authFetch('/api/dcprint/ink-mixed', {\n method: 'PUT',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ id, status }),\n });\n const result = await res.json();\n if (result.success) {\n toast({ title: tc('statusUpdateSuccess') });\n fetchData();\n }\n } catch {\n toast({ title: tc('statusUpdateFailed'), variant: 'destructive' });\n }\n };\n\n return (\n <MainLayout>\n <div className=\"p-6 space-y-6\">\n <div className=\"flex items-center justify-between\">\n <h1 className=\"text-2xl font-bold\">{tc('dcInkMixedTitle')}</h1>\n <div className=\"flex gap-2\">\n <div className=\"flex items-center gap-2\">\n <Input\n placeholder={tc('searchOrderNo')}\n value={searchNo}\n onChange={(e) => setSearchNo(e.target.value)}\n className=\"w-36 h-8 text-sm\"\n />\n <Input\n placeholder=\"搜索颜色\"\n value={searchColor}\n onChange={(e) => setSearchColor(e.target.value)}\n className=\"w-36 h-8 text-sm\"\n />\n <Button size=\"sm\" variant=\"outline\" onClick={fetchData}>\n <Search className=\"h-3 w-3\" />\n </Button>\n </div>\n <Button\n size=\"sm\"\n onClick={() => {\n setEditItem({ mix_time: new Date().toISOString().slice(0, 16), unit: 'kg' });\n setShowDialog(true);\n }}\n >\n <Plus className=\"h-3 w-3 mr-1\" />\n 新增入库\n </Button>\n </div>\n </div>\n\n <Card>\n <CardContent className=\"p-0\">\n <Table>\n <TableHeader>\n <TableRow>\n <TableHead className=\"text-xs\">记录单号</TableHead>\n <TableHead className=\"text-xs\">{tc('dcBaseInkHead')}</TableHead>\n <TableHead className=\"text-xs\">调色比例</TableHead>\n <TableHead className=\"text-xs\">色彩名称</TableHead>\n <TableHead className=\"text-xs\">{tc('customer')}</TableHead>\n <TableHead className=\"text-xs\">{tc('quantity')}</TableHead>\n <TableHead className=\"text-xs\">操作员</TableHead>\n <TableHead className=\"text-xs\">调色时间</TableHead>\n <TableHead className=\"text-xs\">过期时间</TableHead>\n <TableHead className=\"text-xs\">{tc('status')}</TableHead>\n <TableHead className=\"text-xs\">{tc('actions')}</TableHead>\n </TableRow>\n </TableHeader>\n <TableBody>\n {list.map((item) => {\n const st = statusMap[item.status] || statusMap[1];\n return (\n <TableRow key={item.id}>\n <TableCell className=\"text-xs font-mono\">{item.record_no}</TableCell>\n <TableCell className=\"text-xs\">\n {item.base_ink_name || item.base_ink_code || '-'}\n </TableCell>\n <TableCell className=\"text-xs\">{item.mix_ratio || '-'}</TableCell>\n <TableCell className=\"text-xs\">\n <div className=\"flex items-center gap-1\">\n {item.color_code && (\n <span\n className=\"w-3 h-3 rounded-full border\"\n style={{ backgroundColor: item.color_code }}\n />\n )}\n {item.color_name || '-'}\n </div>\n </TableCell>\n <TableCell className=\"text-xs\">{item.company_name || '-'}</TableCell>\n <TableCell className=\"text-xs\">\n {item.quantity} {item.unit}\n </TableCell>\n <TableCell className=\"text-xs\">{item.operator_name || '-'}</TableCell>\n <TableCell className=\"text-xs text-gray-500\">\n {item.mix_time || '-'}\n </TableCell>\n <TableCell className=\"text-xs text-gray-500\">\n {item.expire_time || '-'}\n </TableCell>\n <TableCell>\n <Badge variant={st.variant} className=\"text-xs\">\n {st.label}\n </Badge>\n </TableCell>\n <TableCell>\n <div className=\"flex gap-1\">\n {item.status === 1 && (\n <>\n <Button\n size=\"sm\"\n variant=\"ghost\"\n className=\"h-6 text-xs px-2\"\n onClick={() => handleStatusChange(item.id, 2)}\n >\n 已使用\n </Button>\n <Button\n size=\"sm\"\n variant=\"ghost\"\n className=\"h-6 text-xs px-2 text-orange-600\"\n onClick={() => handleStatusChange(item.id, 3)}\n >\n 已过期\n </Button>\n </>\n )}\n <Button\n size=\"sm\"\n variant=\"ghost\"\n className=\"h-6 w-6 p-0\"\n onClick={() => {\n setEditItem(item);\n setShowDialog(true);\n }}\n >\n <Edit className=\"h-3 w-3\" />\n </Button>\n <Button\n size=\"sm\"\n variant=\"ghost\"\n className=\"h-6 w-6 p-0 text-red-600\"\n onClick={() => handleDelete(item.id)}\n >\n <Trash2 className=\"h-3 w-3\" />\n </Button>\n </div>\n </TableCell>\n </TableRow>\n );\n })}\n {list.length === 0 && (\n <TableRow>\n <TableCell colSpan={11} className=\"text-center text-gray-400 py-8\">\n 暂无调色油墨记录\n </TableCell>\n </TableRow>\n )}\n </TableBody>\n </Table>\n </CardContent>\n </Card>\n\n <div className=\"flex items-center justify-between\">\n <span className=\"text-sm text-gray-500\">\n 共{total}\n {tc('dcRecordsSuffix')}\n </span>\n <div className=\"flex gap-2\">\n <Button\n size=\"sm\"\n variant=\"outline\"\n disabled={page <= 1}\n onClick={() => setPage((p) => p - 1)}\n >\n 上一页\n </Button>\n <Button\n size=\"sm\"\n variant=\"outline\"\n disabled={page * 20 >= total}\n onClick={() => setPage((p) => p + 1)}\n >\n 下一页\n </Button>\n </div>\n </div>\n\n <Dialog open={showDialog} onOpenChange={setShowDialog}>\n <DialogContent className=\"max-w-2xl\" resizable>\n <DialogHeader>\n <DialogTitle>{editItem.id ? '编辑调色油墨' : '新增调色油墨入库'}</DialogTitle>\n </DialogHeader>\n <div className=\"grid grid-cols-2 gap-4\">\n <div>\n <Label>原油墨编号</Label>\n <Input\n value={editItem.base_ink_code || ''}\n onChange={(e) => setEditItem({ ...editItem, base_ink_code: e.target.value })}\n />\n </div>\n <div>\n <Label>原油墨名称</Label>\n <Input\n value={editItem.base_ink_name || ''}\n onChange={(e) => setEditItem({ ...editItem, base_ink_name: e.target.value })}\n />\n </div>\n <div>\n <Label>调色比例</Label>\n <Input\n value={editItem.mix_ratio || ''}\n onChange={(e) => setEditItem({ ...editItem, mix_ratio: e.target.value })}\n placeholder=\"如: 3:1:0.5\"\n />\n </div>\n <div>\n <Label>色彩名称</Label>\n <Input\n value={editItem.color_name || ''}\n onChange={(e) => setEditItem({ ...editItem, color_name: e.target.value })}\n />\n </div>\n <div>\n <Label>色彩编码</Label>\n <Input\n value={editItem.color_code || ''}\n onChange={(e) => setEditItem({ ...editItem, color_code: e.target.value })}\n placeholder=\"如: #FF5500\"\n />\n </div>\n <div>\n <Label>客户名称</Label>\n <Input\n value={editItem.company_name || ''}\n onChange={(e) => setEditItem({ ...editItem, company_name: e.target.value })}\n />\n </div>\n <div>\n <Label>调色时间</Label>\n <Input\n type=\"datetime-local\"\n value={editItem.mix_time || ''}\n onChange={(e) => setEditItem({ ...editItem, mix_time: e.target.value })}\n />\n </div>\n <div>\n <Label>操作员</Label>\n <UserSelect\n value={editItem.operator_name || ''}\n onChange={(v) => setEditItem({ ...editItem, operator_name: v })}\n />\n </div>\n <div>\n <Label>{tc('quantity')}</Label>\n <Input\n type=\"number\"\n step=\"0.01\"\n value={editItem.quantity || ''}\n onChange={(e) => setEditItem({ ...editItem, quantity: Number(e.target.value) })}\n />\n </div>\n <div>\n <Label>{tc('unit')}</Label>\n <Select\n value={editItem.unit || 'kg'}\n onValueChange={(v) => setEditItem({ ...editItem, unit: v })}\n >\n <SelectTrigger>\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"kg\">kg</SelectItem>\n <SelectItem value=\"g\">g</SelectItem>\n <SelectItem value=\"L\">L</SelectItem>\n <SelectItem value=\"mL\">mL</SelectItem>\n </SelectContent>\n </Select>\n </div>\n <div>\n <Label>过期时间</Label>\n <Input\n type=\"datetime-local\"\n value={editItem.expire_time || ''}\n onChange={(e) => setEditItem({ ...editItem, expire_time: e.target.value })}\n />\n </div>\n <div>\n <Label>{tc('remark')}</Label>\n <Input\n value={editItem.remark || ''}\n onChange={(e) => setEditItem({ ...editItem, remark: e.target.value })}\n />\n </div>\n </div>\n <DialogFooter>\n <Button variant=\"outline\" onClick={() => setShowDialog(false)}>\n 取消\n </Button>\n <Button onClick={handleSave}>{tc('save')}</Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n </div>\n </MainLayout>\n );\n}\n","usedDeprecatedRules":[]},{"filePath":"D:\\dcprint\\erp-project\\src\\app\\[locale]\\dcprint\\ink-opening\\page.tsx","messages":[{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"24小时\"。建议使用: tc('text_1d7rd')","line":85,"column":23,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":85,"endColumn":29},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"48小时\"。建议使用: tc('text_1ekp7')","line":86,"column":23,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":86,"endColumn":29},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"72小时\"。建议使用: tc('text_1gd7m')","line":87,"column":23,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":87,"endColumn":29},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"96小时\"。建议使用: tc('text_1hq5g')","line":88,"column":23,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":88,"endColumn":29},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"120小时\"。建议使用: tc('text_sb1va')","line":89,"column":24,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":89,"endColumn":31},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"168小时(7天)\"。建议使用: tc('text_upeg2f')","line":90,"column":24,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":90,"endColumn":35},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"336小时(14天)\"。建议使用: tc('text_3alp3m')","line":91,"column":24,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":91,"endColumn":36},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"720小时(30天)\"。建议使用: tc('text_h9z2vr')","line":92,"column":24,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":92,"endColumn":36},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":142,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":142,"endColumn":36},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":143,"column":16,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":143,"endColumn":23},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction<InkOpeningRecord[]>`.","line":144,"column":20,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":144,"endColumn":41},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":144,"column":25,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":144,"endColumn":29},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":145,"column":18,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":145,"endColumn":22},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction<{ total_count: number; using_count: number; expired_count: number; scrapped_count: number; overdue_using_count: number; }>`.","line":145,"column":44,"nodeType":"MemberExpression","messageId":"unsafeArgument","endLine":145,"endColumn":61},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":145,"column":49,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":145,"endColumn":53},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":146,"column":18,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":146,"endColumn":22},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction<InkOpeningRecord[]>`.","line":146,"column":53,"nodeType":"MemberExpression","messageId":"unsafeArgument","endLine":146,"endColumn":75},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":146,"column":58,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":146,"endColumn":62},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"获取油墨开罐记录失败\"。建议使用: tc('text_q23b6x')","line":149,"column":22,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":149,"endColumn":34},{"ruleId":"react-hooks/exhaustive-deps","severity":1,"message":"React Hook useCallback has a missing dependency: 'toast'. Either include it or remove the dependency array.","line":153,"column":6,"nodeType":"ArrayExpression","endLine":153,"endColumn":44,"suggestions":[{"desc":"Update the dependencies array to be: [keyword, statusFilter, inkTypeFilter, toast]","fix":{"range":[4981,5019],"text":"[keyword, statusFilter, inkTypeFilter, toast]"}}]},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":158,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":158,"endColumn":36},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":159,"column":16,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":159,"endColumn":23},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction<any[]>`.","line":160,"column":22,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":160,"endColumn":56},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":160,"column":27,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":160,"endColumn":31},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":160,"column":46,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":160,"endColumn":50},{"ruleId":"react-hooks/set-state-in-effect","severity":1,"message":"Error: Calling setState synchronously within an effect can trigger cascading renders\n\nEffects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following:\n* Update external systems with the latest state from React.\n* Subscribe for updates from some external system, calling setState in a callback function when external state changes.\n\nCalling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect).\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\dcprint\\ink-opening\\page.tsx:166:5\n 164 |\n 165 | useEffect(() => {\n> 166 | fetchRecords();\n | ^^^^^^^^^^^^ Avoid calling setState() directly within an effect\n 167 | fetchMaterials();\n 168 | }, [fetchRecords]);\n 169 |","line":166,"column":5,"nodeType":null,"endLine":166,"endColumn":17},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"请填写必填字段\"。建议使用: tc('text_stv1kn')","line":172,"column":22,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":172,"endColumn":31},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":193,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":193,"endColumn":36},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":194,"column":16,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":194,"endColumn":23},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"油墨开罐记录创建成功\"。建议使用: tc('text_toikya')","line":195,"column":24,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":195,"endColumn":36},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":212,"column":17,"nodeType":"Property","messageId":"anyAssignment","endLine":212,"endColumn":46},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .message on an `any` value.","line":212,"column":29,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":212,"endColumn":36},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"创建油墨开罐记录失败\"。建议使用: tc('text_z7ufc9')","line":215,"column":22,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":215,"endColumn":34},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":226,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":226,"endColumn":36},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":227,"column":16,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":227,"endColumn":23},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"状态更新成功\"。建议使用: tc('text_flsaxi')","line":228,"column":24,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":228,"endColumn":32},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":231,"column":17,"nodeType":"Property","messageId":"anyAssignment","endLine":231,"endColumn":46},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .message on an `any` value.","line":231,"column":29,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":231,"endColumn":36},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"更新失败\"。建议使用: tc('text_det3dc')","line":234,"column":22,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":234,"endColumn":28},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"确定删除此记录?\"。建议使用: tc('text_x5zasm')","line":239,"column":18,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":239,"endColumn":28},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":242,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":242,"endColumn":36},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":243,"column":16,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":243,"endColumn":23},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"删除成功\"。建议使用: tc('text_azeh8z')","line":244,"column":24,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":244,"endColumn":30},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":247,"column":17,"nodeType":"Property","messageId":"anyAssignment","endLine":247,"endColumn":46},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .message on an `any` value.","line":247,"column":29,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":247,"endColumn":36},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"删除失败\"。建议使用: tc('text_azdahk')","line":250,"column":22,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":250,"endColumn":28},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"使用中\"。建议使用: {tc('text_c7jd0')}","line":276,"column":58,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":276,"endColumn":61},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"已过期\"。建议使用: {tc('text_ege5m')}","line":288,"column":58,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":288,"endColumn":61},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"即将过期\"。建议使用: {tc('text_ax323v')}","line":300,"column":58,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":300,"endColumn":62},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"已报废\"。建议使用: {tc('text_e8o3w')}","line":312,"column":58,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":312,"endColumn":61},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"过期预警\"。建议使用: {tc('text_ikomu2')}","line":328,"column":54,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":330,"endColumn":15},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"以下油墨已超过有效使用时间,请及时处理!\"。建议使用: {tc('text_4skp25')}","line":331,"column":75,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":333,"endColumn":15},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"记录单号\"。建议使用: {tc('text_i0myef')}","line":339,"column":32,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":339,"endColumn":36},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"油墨名称\"。建议使用: {tc('text_e32i1e')}","line":340,"column":32,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":340,"endColumn":36},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"油墨类型\"。建议使用: {tc('text_e396tb')}","line":341,"column":32,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":341,"endColumn":36},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"开罐时间\"。建议使用: {tc('text_ciieby')}","line":342,"column":32,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":342,"endColumn":36},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"过期时间\"。建议使用: {tc('text_ikg3cm')}","line":343,"column":32,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":343,"endColumn":36},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"标记过期\"。建议使用: {tc('text_dpi4xt')}","line":366,"column":26,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":368,"endColumn":25},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"标记报废\"。建议使用: {tc('text_dpaew3')}","line":374,"column":26,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":376,"endColumn":25},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"油墨开罐记录\"。建议使用: {tc('text_tjkjgc')}","line":390,"column":28,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":390,"endColumn":34},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"全部状态\"。建议使用: {tc('text_avez63')}","line":409,"column":45,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":409,"endColumn":49},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"使用中\"。建议使用: {tc('text_c7jd0')}","line":410,"column":43,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":410,"endColumn":46},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"已过期\"。建议使用: {tc('text_ege5m')}","line":411,"column":43,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":411,"endColumn":46},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"已报废\"。建议使用: {tc('text_e8o3w')}","line":412,"column":43,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":412,"endColumn":46},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"全部类型\"。建议使用: {tc('text_avglbk')}","line":420,"column":45,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":420,"endColumn":49},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"溶剂型\"。建议使用: {tc('text_gm8xr')}","line":421,"column":49,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":421,"endColumn":52},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"UV型\"。建议使用: {tc('text_2adm')}","line":422,"column":44,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":422,"endColumn":47},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"水性\"。建议使用: {tc('text_ixkj')}","line":423,"column":47,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":423,"endColumn":49},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"刷新\"。建议使用: {tc('text_ejix')}","line":427,"column":57,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":429,"endColumn":17},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"新增开罐\"。建议使用: {tc('text_d767cu')}","line":431,"column":52,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":433,"endColumn":17},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"记录单号\"。建议使用: {tc('text_i0myef')}","line":441,"column":30,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":441,"endColumn":34},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"油墨名称\"。建议使用: {tc('text_e32i1e')}","line":442,"column":30,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":442,"endColumn":34},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"油墨类型\"。建议使用: {tc('text_e396tb')}","line":443,"column":30,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":443,"endColumn":34},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"批号\"。建议使用: {tc('text_h7ku')}","line":444,"column":30,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":444,"endColumn":32},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"开罐时间\"。建议使用: {tc('text_ciieby')}","line":445,"column":30,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":445,"endColumn":34},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"过期时间\"。建议使用: {tc('text_ikg3cm')}","line":447,"column":30,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":447,"endColumn":34},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"剩余数量\"。建议使用: {tc('text_aqbejz')}","line":449,"column":30,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":449,"endColumn":34},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"暂无油墨开罐记录\"。建议使用: {tc('text_cd9ztu')}","line":457,"column":96,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":459,"endColumn":21},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"小时\"。建议使用: {tc('text_g7uv')}","line":487,"column":43,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":489,"endColumn":25},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"物料编码\"。建议使用: {tc('text_euzqpn')}","line":570,"column":26,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":570,"endColumn":30},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"物料名称\"。建议使用: {tc('text_eusfkj')}","line":580,"column":26,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":580,"endColumn":30},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"油墨类型\"。建议使用: {tc('text_e396tb')}","line":592,"column":26,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":592,"endColumn":30},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"溶剂型\"。建议使用: {tc('text_gm8xr')}","line":601,"column":51,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":601,"endColumn":54},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"UV型\"。建议使用: {tc('text_2adm')}","line":602,"column":46,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":602,"endColumn":49},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"水性\"。建议使用: {tc('text_ixkj')}","line":603,"column":49,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":603,"endColumn":51},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"批号\"。建议使用: {tc('text_h7ku')}","line":608,"column":26,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":608,"endColumn":28},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"剩余数量\"。建议使用: {tc('text_aqbejz')}","line":648,"column":26,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":648,"endColumn":30},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"罐\"。建议使用: {tc('text_p5c')}","line":671,"column":41,"nodeType":"Literal","messageId":"noChineseInJSX","endLine":671,"endColumn":44},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"罐\"。建议使用: {tc('text_p5c')}","line":671,"column":45,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":671,"endColumn":46},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"操作员\"。建议使用: {tc('text_f5hc9')}","line":677,"column":24,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":677,"endColumn":27},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"取消\"。建议使用: {tc('text_ev02')}","line":693,"column":78,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":695,"endColumn":15},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"物料名称:\"。建议使用: {tc('text_ybuh7r')}","line":718,"column":61,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":718,"endColumn":66},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"小时\"。建议使用: {tc('text_g7uv')}","line":733,"column":46,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":735,"endColumn":19},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"状态:\"。建议使用: {tc('text_halin')}","line":755,"column":61,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":755,"endColumn":64},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"备注:\"。建议使用: {tc('text_dld2x')}","line":763,"column":61,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":763,"endColumn":64}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":95,"fixableErrorCount":0,"fixableWarningCount":0,"source":"'use client';\r\n\r\nimport { authFetch } from '@/lib/auth-fetch';\r\nimport { INK_TYPE_LABEL, INK_STATUS_LABEL } from '@/lib/status-labels';\r\nimport { useState, useEffect, useCallback } from 'react';\r\nimport { MainLayout } from '@/components/layout';\r\nimport { Card, CardContent, CardHeader, CardTitle, CardDescription } from '@/components/ui/card';\r\nimport { Button } from '@/components/ui/button';\r\nimport { Input } from '@/components/ui/input';\r\nimport { Label } from '@/components/ui/label';\r\nimport { Badge } from '@/components/ui/badge';\r\nimport {\r\n Dialog,\r\n DialogContent,\r\n DialogHeader,\r\n DialogTitle,\r\n DialogDescription,\r\n DialogFooter,\r\n} from '@/components/ui/dialog';\r\nimport {\r\n Select,\r\n SelectContent,\r\n SelectItem,\r\n SelectTrigger,\r\n SelectValue,\r\n} from '@/components/ui/select';\r\nimport {\r\n Table,\r\n TableBody,\r\n TableCell,\r\n TableHead,\r\n TableHeader,\r\n TableRow,\r\n} from '@/components/ui/table';\r\nimport { Textarea } from '@/components/ui/textarea';\r\nimport {\r\n Plus,\r\n Search,\r\n RefreshCw,\r\n Clock,\r\n AlertTriangle,\r\n Droplets,\r\n Eye,\r\n Trash2,\r\n Timer,\r\n} from 'lucide-react';\r\nimport { useToast } from '@/hooks/use-toast';\r\nimport { UserSelect } from '@/components/ui/user-select';\r\nimport { useTranslations } from 'next-intl';\r\n\r\ninterface InkOpeningRecord {\r\n id: number;\r\n record_no: string;\r\n material_id: number;\r\n material_code: string;\r\n material_name: string;\r\n batch_no: string;\r\n label_id: number;\r\n ink_type: string;\r\n open_time: string;\r\n expire_hours: number;\r\n expire_time: string;\r\n remaining_qty: number;\r\n unit: string;\r\n status: number;\r\n operator_id: number;\r\n operator_name: string;\r\n remark: string;\r\n create_time: string;\r\n}\r\n\r\nconst INK_TYPE_MAP: Record<string, { label: string; color: string }> = {\r\n solvent: { label: INK_TYPE_LABEL['solvent'], color: 'bg-orange-100 text-orange-800 dark:bg-orange-900/30 dark:text-orange-300' },\r\n uv: { label: INK_TYPE_LABEL['uv'], color: 'bg-purple-100 text-purple-800 dark:bg-purple-900/30 dark:text-purple-300' },\r\n water: { label: INK_TYPE_LABEL['water'], color: 'bg-blue-100 text-blue-800 dark:bg-blue-900/30 dark:text-blue-300' },\r\n};\r\n\r\nconst STATUS_MAP: Record<number, { label: string; color: string }> = {\r\n 1: { label: INK_STATUS_LABEL[1], color: 'bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-300' },\r\n 2: { label: INK_STATUS_LABEL[2], color: 'bg-red-100 text-red-800 dark:bg-red-900/30 dark:text-red-300' },\r\n 3: { label: INK_STATUS_LABEL[3], color: 'bg-gray-100 text-gray-800 dark:bg-gray-700 dark:text-gray-200' },\r\n};\r\n\r\nconst EXPIRE_HOURS_OPTIONS = [\r\n { value: 24, label: '24小时' },\r\n { value: 48, label: '48小时' },\r\n { value: 72, label: '72小时' },\r\n { value: 96, label: '96小时' },\r\n { value: 120, label: '120小时' },\r\n { value: 168, label: '168小时(7天)' },\r\n { value: 336, label: '336小时(14天)' },\r\n { value: 720, label: '720小时(30天)' },\r\n];\r\n\r\nexport default function InkOpeningPage() {\r\n // 翻译钩子\r\n const tc = useTranslations('Common');\r\n\r\n const { toast } = useToast();\r\n const [records, setRecords] = useState<InkOpeningRecord[]>([]);\r\n const [overdueList, setOverdueList] = useState<InkOpeningRecord[]>([]);\r\n const [_loading, setLoading] = useState(false);\r\n const [keyword, setKeyword] = useState('');\r\n const [statusFilter, setStatusFilter] = useState('all');\r\n const [inkTypeFilter, setInkTypeFilter] = useState('all');\r\n const [dialogOpen, setDialogOpen] = useState(false);\r\n const [detailOpen, setDetailOpen] = useState(false);\r\n const [detailData, setDetailData] = useState<InkOpeningRecord | null>(null);\r\n const [summary, setSummary] = useState({\r\n total_count: 0,\r\n using_count: 0,\r\n expired_count: 0,\r\n scrapped_count: 0,\r\n overdue_using_count: 0,\r\n });\r\n\r\n const [form, setForm] = useState({\r\n material_id: '',\r\n material_code: '',\r\n material_name: '',\r\n batch_no: '',\r\n ink_type: 'solvent',\r\n open_time: new Date().toISOString().slice(0, 16),\r\n expire_hours: 48,\r\n remaining_qty: '',\r\n unit: 'kg',\r\n operator_name: '',\r\n remark: '',\r\n });\r\n\r\n const [_materials, setMaterials] = useState<Loose[]>([]);\r\n\r\n const fetchRecords = useCallback(async () => {\r\n setLoading(true);\r\n try {\r\n const params = new URLSearchParams();\r\n if (keyword) params.set('keyword', keyword);\r\n if (statusFilter !== 'all') params.set('status', statusFilter);\r\n if (inkTypeFilter !== 'all') params.set('ink_type', inkTypeFilter);\r\n params.set('pageSize', '50');\r\n const res = await authFetch(`/api/dcprint/ink-opening?${params}`);\r\n const data = await res.json();\r\n if (data.success) {\r\n setRecords(data.data?.list || []);\r\n if (data.data?.summary) setSummary(data.data.summary);\r\n if (data.data?.overdue_list) setOverdueList(data.data.overdue_list);\r\n }\r\n } catch {\r\n toast({ title: '获取油墨开罐记录失败', variant: 'destructive' });\r\n } finally {\r\n setLoading(false);\r\n }\r\n }, [keyword, statusFilter, inkTypeFilter]);\r\n\r\n const fetchMaterials = async () => {\r\n try {\r\n const res = await authFetch('/api/inventory/materials?category=ink&pageSize=100');\r\n const data = await res.json();\r\n if (data.success) {\r\n setMaterials(data.data?.list || data.data || []);\r\n }\r\n } catch {}\r\n };\r\n\r\n useEffect(() => {\r\n fetchRecords();\r\n fetchMaterials();\r\n }, [fetchRecords]);\r\n\r\n const handleCreate = async () => {\r\n if (!form.material_id || !form.open_time || !form.expire_hours) {\r\n toast({ title: '请填写必填字段', variant: 'destructive' });\r\n return;\r\n }\r\n try {\r\n const res = await authFetch('/api/dcprint/ink-opening', {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify({\r\n material_id: parseInt(form.material_id),\r\n material_code: form.material_code,\r\n material_name: form.material_name,\r\n batch_no: form.batch_no,\r\n ink_type: form.ink_type,\r\n open_time: form.open_time,\r\n expire_hours: form.expire_hours,\r\n remaining_qty: form.remaining_qty ? parseFloat(form.remaining_qty) : null,\r\n unit: form.unit,\r\n operator_name: form.operator_name,\r\n remark: form.remark,\r\n }),\r\n });\r\n const data = await res.json();\r\n if (data.success) {\r\n toast({ title: '油墨开罐记录创建成功' });\r\n setDialogOpen(false);\r\n setForm({\r\n material_id: '',\r\n material_code: '',\r\n material_name: '',\r\n batch_no: '',\r\n ink_type: 'solvent',\r\n open_time: new Date().toISOString().slice(0, 16),\r\n expire_hours: 48,\r\n remaining_qty: '',\r\n unit: 'kg',\r\n operator_name: '',\r\n remark: '',\r\n });\r\n fetchRecords();\r\n } else {\r\n toast({ title: data.message || '创建失败', variant: 'destructive' });\r\n }\r\n } catch {\r\n toast({ title: '创建油墨开罐记录失败', variant: 'destructive' });\r\n }\r\n };\r\n\r\n const handleStatusChange = async (id: number, status: number) => {\r\n try {\r\n const res = await authFetch('/api/dcprint/ink-opening', {\r\n method: 'PUT',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify({ id, status }),\r\n });\r\n const data = await res.json();\r\n if (data.success) {\r\n toast({ title: '状态更新成功' });\r\n fetchRecords();\r\n } else {\r\n toast({ title: data.message || '更新失败', variant: 'destructive' });\r\n }\r\n } catch {\r\n toast({ title: '更新失败', variant: 'destructive' });\r\n }\r\n };\r\n\r\n const _handleDelete = async (id: number) => {\r\n if (!confirm('确定删除此记录?')) return;\r\n try {\r\n const res = await authFetch(`/api/dcprint/ink-opening?id=${id}`, { method: 'DELETE' });\r\n const data = await res.json();\r\n if (data.success) {\r\n toast({ title: '删除成功' });\r\n fetchRecords();\r\n } else {\r\n toast({ title: data.message || '删除失败', variant: 'destructive' });\r\n }\r\n } catch {\r\n toast({ title: '删除失败', variant: 'destructive' });\r\n }\r\n };\r\n\r\n const getTimeRemaining = (expireTime: string) => {\r\n const now = new Date().getTime();\r\n const expire = new Date(expireTime).getTime();\r\n const diff = expire - now;\r\n if (diff <= 0) return { text: '已过期', isOverdue: true, isWarning: false };\r\n const hours = Math.floor(diff / (1000 * 60 * 60));\r\n const minutes = Math.floor((diff % (1000 * 60 * 60)) / (1000 * 60));\r\n if (hours < 4) return { text: `${hours}时${minutes}分`, isOverdue: false, isWarning: true };\r\n return { text: `${hours}时${minutes}分`, isOverdue: false, isWarning: false };\r\n };\r\n\r\n const handleViewDetail = (record: InkOpeningRecord) => {\r\n setDetailData(record);\r\n setDetailOpen(true);\r\n };\r\n\r\n return (\r\n <MainLayout title=\"油墨开罐计时管理\">\r\n <div className=\"space-y-6\">\r\n <div className=\"grid gap-4 md:grid-cols-4\">\r\n <Card>\r\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-2\">\r\n <CardTitle className=\"text-sm font-medium\">使用中</CardTitle>\r\n <Droplets className=\"h-4 w-4 text-green-600 dark:text-green-400\" />\r\n </CardHeader>\r\n <CardContent>\r\n <div className=\"text-2xl font-bold text-green-600 dark:text-green-400\">\r\n {summary.using_count}\r\n </div>\r\n <p className=\"text-xs text-muted-foreground mt-1\">{tc('dcInUseDesc')}</p>\r\n </CardContent>\r\n </Card>\r\n <Card>\r\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-2\">\r\n <CardTitle className=\"text-sm font-medium\">已过期</CardTitle>\r\n <AlertTriangle className=\"h-4 w-4 text-red-600 dark:text-red-400\" />\r\n </CardHeader>\r\n <CardContent>\r\n <div className=\"text-2xl font-bold text-red-600 dark:text-red-400\">\r\n {summary.expired_count}\r\n </div>\r\n <p className=\"text-xs text-muted-foreground mt-1\">{tc('dcExpiredDesc')}</p>\r\n </CardContent>\r\n </Card>\r\n <Card>\r\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-2\">\r\n <CardTitle className=\"text-sm font-medium\">即将过期</CardTitle>\r\n <Clock className=\"h-4 w-4 text-yellow-600 dark:text-yellow-400\" />\r\n </CardHeader>\r\n <CardContent>\r\n <div className=\"text-2xl font-bold text-yellow-600 dark:text-yellow-400\">\r\n {summary.overdue_using_count}\r\n </div>\r\n <p className=\"text-xs text-muted-foreground mt-1\">{tc('dcSoonExpiredDesc')}</p>\r\n </CardContent>\r\n </Card>\r\n <Card>\r\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-2\">\r\n <CardTitle className=\"text-sm font-medium\">已报废</CardTitle>\r\n <Trash2 className=\"h-4 w-4 text-gray-600 dark:text-gray-400\" />\r\n </CardHeader>\r\n <CardContent>\r\n <div className=\"text-2xl font-bold text-gray-600 dark:text-gray-400\">\r\n {summary.scrapped_count}\r\n </div>\r\n <p className=\"text-xs text-muted-foreground mt-1\">{tc('dcScrappedDesc')}</p>\r\n </CardContent>\r\n </Card>\r\n </div>\r\n\r\n {overdueList.length > 0 && (\r\n <Card className=\"border-red-200 bg-red-50 dark:border-red-900 dark:bg-red-950/30\">\r\n <CardHeader>\r\n <CardTitle className=\"flex items-center gap-2 text-red-700 dark:text-red-400\">\r\n <AlertTriangle className=\"h-5 w-5\" />\r\n 过期预警\r\n </CardTitle>\r\n <CardDescription className=\"text-red-600 dark:text-red-400\">\r\n 以下油墨已超过有效使用时间,请及时处理!\r\n </CardDescription>\r\n </CardHeader>\r\n <CardContent>\r\n <Table>\r\n <TableHeader>\r\n <TableRow>\r\n <TableHead>记录单号</TableHead>\r\n <TableHead>油墨名称</TableHead>\r\n <TableHead>油墨类型</TableHead>\r\n <TableHead>开罐时间</TableHead>\r\n <TableHead>过期时间</TableHead>\r\n <TableHead>{tc('actions')}</TableHead>\r\n </TableRow>\r\n </TableHeader>\r\n <TableBody>\r\n {overdueList.map((r) => (\r\n <TableRow key={r.id}>\r\n <TableCell className=\"font-mono\">{r.record_no}</TableCell>\r\n <TableCell>{r.material_name}</TableCell>\r\n <TableCell>\r\n <Badge className={INK_TYPE_MAP[r.ink_type]?.color || 'bg-gray-100'}>\r\n {INK_TYPE_MAP[r.ink_type]?.label || r.ink_type}\r\n </Badge>\r\n </TableCell>\r\n <TableCell>{r.open_time}</TableCell>\r\n <TableCell className=\"text-red-600 font-medium dark:text-red-400\">\r\n {r.expire_time}\r\n </TableCell>\r\n <TableCell>\r\n <Button\r\n size=\"sm\"\r\n variant=\"outline\"\r\n onClick={() => handleStatusChange(r.id, 2)}\r\n >\r\n 标记过期\r\n </Button>\r\n <Button\r\n size=\"sm\"\r\n variant=\"outline\"\r\n className=\"ml-1\"\r\n onClick={() => handleStatusChange(r.id, 3)}\r\n >\r\n 标记报废\r\n </Button>\r\n </TableCell>\r\n </TableRow>\r\n ))}\r\n </TableBody>\r\n </Table>\r\n </CardContent>\r\n </Card>\r\n )}\r\n\r\n <Card>\r\n <CardHeader>\r\n <div className=\"flex items-center justify-between\">\r\n <div>\r\n <CardTitle>油墨开罐记录</CardTitle>\r\n <CardDescription>{tc('dcOpeningRecordDesc')}</CardDescription>\r\n </div>\r\n <div className=\"flex gap-2\">\r\n <div className=\"relative w-64\">\r\n <Search className=\"absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground\" />\r\n <Input\r\n placeholder=\"搜索物料编码/名称/批号...\"\r\n className=\"pl-10\"\r\n value={keyword}\r\n onChange={(e) => setKeyword(e.target.value)}\r\n onKeyDown={(e) => e.key === 'Enter' && fetchRecords()}\r\n />\r\n </div>\r\n <Select value={statusFilter} onValueChange={setStatusFilter}>\r\n <SelectTrigger className=\"w-28\">\r\n <SelectValue placeholder={tc('status')} />\r\n </SelectTrigger>\r\n <SelectContent>\r\n <SelectItem value=\"all\">全部状态</SelectItem>\r\n <SelectItem value=\"1\">使用中</SelectItem>\r\n <SelectItem value=\"2\">已过期</SelectItem>\r\n <SelectItem value=\"3\">已报废</SelectItem>\r\n </SelectContent>\r\n </Select>\r\n <Select value={inkTypeFilter} onValueChange={setInkTypeFilter}>\r\n <SelectTrigger className=\"w-28\">\r\n <SelectValue placeholder=\"油墨类型\" />\r\n </SelectTrigger>\r\n <SelectContent>\r\n <SelectItem value=\"all\">全部类型</SelectItem>\r\n <SelectItem value=\"solvent\">溶剂型</SelectItem>\r\n <SelectItem value=\"uv\">UV型</SelectItem>\r\n <SelectItem value=\"water\">水性</SelectItem>\r\n </SelectContent>\r\n </Select>\r\n <Button variant=\"outline\" onClick={fetchRecords}>\r\n <RefreshCw className=\"h-4 w-4 mr-2\" />\r\n 刷新\r\n </Button>\r\n <Button onClick={() => setDialogOpen(true)}>\r\n <Plus className=\"h-4 w-4 mr-2\" />\r\n 新增开罐\r\n </Button>\r\n </div>\r\n </div>\r\n </CardHeader>\r\n <CardContent>\r\n <Table>\r\n <TableHeader>\r\n <TableRow>\r\n <TableHead>记录单号</TableHead>\r\n <TableHead>油墨名称</TableHead>\r\n <TableHead>油墨类型</TableHead>\r\n <TableHead>批号</TableHead>\r\n <TableHead>开罐时间</TableHead>\r\n <TableHead>{tc('dcValidHoursHead')}</TableHead>\r\n <TableHead>过期时间</TableHead>\r\n <TableHead>{tc('dcRemainingTimeHead')}</TableHead>\r\n <TableHead>剩余数量</TableHead>\r\n <TableHead>{tc('status')}</TableHead>\r\n <TableHead>{tc('actions')}</TableHead>\r\n </TableRow>\r\n </TableHeader>\r\n <TableBody>\r\n {records.length === 0 ? (\r\n <TableRow>\r\n <TableCell colSpan={11} className=\"text-center py-8 text-muted-foreground\">\r\n 暂无油墨开罐记录\r\n </TableCell>\r\n </TableRow>\r\n ) : (\r\n records.map((r) => {\r\n const timeRemaining = r.status === 1 ? getTimeRemaining(r.expire_time) : null;\r\n return (\r\n <TableRow\r\n key={r.id}\r\n className={\r\n timeRemaining?.isOverdue\r\n ? 'bg-red-50 dark:bg-red-950/30'\r\n : timeRemaining?.isWarning\r\n ? 'bg-yellow-50 dark:bg-yellow-950/30'\r\n : ''\r\n }\r\n >\r\n <TableCell className=\"font-mono\">{r.record_no}</TableCell>\r\n <TableCell className=\"font-medium\">\r\n {r.material_name || r.material_code}\r\n </TableCell>\r\n <TableCell>\r\n <Badge className={INK_TYPE_MAP[r.ink_type]?.color || 'bg-gray-100'}>\r\n {INK_TYPE_MAP[r.ink_type]?.label || r.ink_type || '-'}\r\n </Badge>\r\n </TableCell>\r\n <TableCell className=\"font-mono\">{r.batch_no || '-'}</TableCell>\r\n <TableCell>{r.open_time}</TableCell>\r\n <TableCell>\r\n {r.expire_hours}\r\n 小时\r\n </TableCell>\r\n <TableCell>{r.expire_time}</TableCell>\r\n <TableCell>\r\n {timeRemaining ? (\r\n <span\r\n className={`flex items-center gap-1 font-medium ${timeRemaining.isOverdue ? 'text-red-600' : timeRemaining.isWarning ? 'text-yellow-600' : 'text-green-600'}`}\r\n >\r\n {timeRemaining.isOverdue && <AlertTriangle className=\"h-3 w-3\" />}\r\n {timeRemaining.isWarning && <Clock className=\"h-3 w-3\" />}\r\n {!timeRemaining.isOverdue && !timeRemaining.isWarning && (\r\n <Timer className=\"h-3 w-3\" />\r\n )}\r\n {timeRemaining.text}\r\n </span>\r\n ) : (\r\n <span className=\"text-muted-foreground\">-</span>\r\n )}\r\n </TableCell>\r\n <TableCell>\r\n {r.remaining_qty ? `${r.remaining_qty} ${r.unit || ''}` : '-'}\r\n </TableCell>\r\n <TableCell>\r\n <Badge className={STATUS_MAP[r.status]?.color || 'bg-gray-100'}>\r\n {STATUS_MAP[r.status]?.label || r.status}\r\n </Badge>\r\n </TableCell>\r\n <TableCell>\r\n <div className=\"flex gap-1\">\r\n <Button variant=\"ghost\" size=\"sm\" onClick={() => handleViewDetail(r)}>\r\n <Eye className=\"h-4 w-4\" />\r\n </Button>\r\n {r.status === 1 && (\r\n <>\r\n <Button\r\n variant=\"ghost\"\r\n size=\"sm\"\r\n onClick={() => handleStatusChange(r.id, 2)}\r\n title=\"标记过期\"\r\n >\r\n <Clock className=\"h-4 w-4 text-yellow-500\" />\r\n </Button>\r\n <Button\r\n variant=\"ghost\"\r\n size=\"sm\"\r\n onClick={() => handleStatusChange(r.id, 3)}\r\n title=\"标记报废\"\r\n >\r\n <Trash2 className=\"h-4 w-4 text-red-500\" />\r\n </Button>\r\n </>\r\n )}\r\n {r.status === 2 && (\r\n <Button\r\n variant=\"ghost\"\r\n size=\"sm\"\r\n onClick={() => handleStatusChange(r.id, 3)}\r\n title=\"标记报废\"\r\n >\r\n <Trash2 className=\"h-4 w-4 text-red-500\" />\r\n </Button>\r\n )}\r\n </div>\r\n </TableCell>\r\n </TableRow>\r\n );\r\n })\r\n )}\r\n </TableBody>\r\n </Table>\r\n </CardContent>\r\n </Card>\r\n\r\n <Dialog open={dialogOpen} onOpenChange={setDialogOpen}>\r\n <DialogContent className=\"sm:max-w-[550px]\" resizable>\r\n <DialogHeader>\r\n <DialogTitle>{tc('dcAddOpeningTitle')}</DialogTitle>\r\n <DialogDescription>{tc('dcAddOpeningDesc')}</DialogDescription>\r\n </DialogHeader>\r\n <div className=\"space-y-4\">\r\n <div className=\"grid grid-cols-2 gap-4\">\r\n <div>\r\n <Label>物料编码</Label>\r\n <Input\r\n value={form.material_code}\r\n onChange={(e) =>\r\n setForm((prev) => ({ ...prev, material_code: e.target.value }))\r\n }\r\n placeholder=\"如:MAT006\"\r\n />\r\n </div>\r\n <div>\r\n <Label>物料名称</Label>\r\n <Input\r\n value={form.material_name}\r\n onChange={(e) =>\r\n setForm((prev) => ({ ...prev, material_name: e.target.value }))\r\n }\r\n placeholder=\"如:丝印油墨-黑色\"\r\n />\r\n </div>\r\n </div>\r\n <div className=\"grid grid-cols-2 gap-4\">\r\n <div>\r\n <Label>油墨类型</Label>\r\n <Select\r\n value={form.ink_type}\r\n onValueChange={(v) => setForm((prev) => ({ ...prev, ink_type: v }))}\r\n >\r\n <SelectTrigger>\r\n <SelectValue />\r\n </SelectTrigger>\r\n <SelectContent>\r\n <SelectItem value=\"solvent\">溶剂型</SelectItem>\r\n <SelectItem value=\"uv\">UV型</SelectItem>\r\n <SelectItem value=\"water\">水性</SelectItem>\r\n </SelectContent>\r\n </Select>\r\n </div>\r\n <div>\r\n <Label>批号</Label>\r\n <Input\r\n value={form.batch_no}\r\n onChange={(e) => setForm((prev) => ({ ...prev, batch_no: e.target.value }))}\r\n placeholder={tc('batchNo')}\r\n />\r\n </div>\r\n </div>\r\n <div className=\"grid grid-cols-2 gap-4\">\r\n <div>\r\n <Label>{tc('dcOpenTimeLabel')}</Label>\r\n <Input\r\n type=\"datetime-local\"\r\n value={form.open_time}\r\n onChange={(e) => setForm((prev) => ({ ...prev, open_time: e.target.value }))}\r\n />\r\n </div>\r\n <div>\r\n <Label>{tc('dcValidHoursLabel')}</Label>\r\n <Select\r\n value={String(form.expire_hours)}\r\n onValueChange={(v) =>\r\n setForm((prev) => ({ ...prev, expire_hours: parseInt(v) }))\r\n }\r\n >\r\n <SelectTrigger>\r\n <SelectValue />\r\n </SelectTrigger>\r\n <SelectContent>\r\n {EXPIRE_HOURS_OPTIONS.map((opt) => (\r\n <SelectItem key={opt.value} value={String(opt.value)}>\r\n {opt.label}\r\n </SelectItem>\r\n ))}\r\n </SelectContent>\r\n </Select>\r\n </div>\r\n </div>\r\n <div className=\"grid grid-cols-2 gap-4\">\r\n <div>\r\n <Label>剩余数量</Label>\r\n <Input\r\n type=\"number\"\r\n step=\"0.01\"\r\n value={form.remaining_qty}\r\n onChange={(e) =>\r\n setForm((prev) => ({ ...prev, remaining_qty: e.target.value }))\r\n }\r\n placeholder=\"剩余数量\"\r\n />\r\n </div>\r\n <div>\r\n <Label>{tc('unit')}</Label>\r\n <Select\r\n value={form.unit}\r\n onValueChange={(v) => setForm((prev) => ({ ...prev, unit: v }))}\r\n >\r\n <SelectTrigger>\r\n <SelectValue />\r\n </SelectTrigger>\r\n <SelectContent>\r\n <SelectItem value=\"kg\">kg</SelectItem>\r\n <SelectItem value=\"L\">L</SelectItem>\r\n <SelectItem value=\"罐\">罐</SelectItem>\r\n </SelectContent>\r\n </Select>\r\n </div>\r\n </div>\r\n <div>\r\n <Label>操作员</Label>\r\n <UserSelect\r\n value={form.operator_name}\r\n onChange={(v) => setForm((prev) => ({ ...prev, operator_name: v }))}\r\n />\r\n </div>\r\n <div>\r\n <Label>{tc('remark')}</Label>\r\n <Textarea\r\n value={form.remark}\r\n onChange={(e) => setForm((prev) => ({ ...prev, remark: e.target.value }))}\r\n placeholder=\"备注信息\"\r\n />\r\n </div>\r\n </div>\r\n <DialogFooter>\r\n <Button variant=\"outline\" onClick={() => setDialogOpen(false)}>\r\n 取消\r\n </Button>\r\n <Button onClick={handleCreate}>{tc('dcCreateBtn')}</Button>\r\n </DialogFooter>\r\n </DialogContent>\r\n </Dialog>\r\n\r\n <Dialog open={detailOpen} onOpenChange={setDetailOpen}>\r\n <DialogContent className=\"sm:max-w-[500px]\" resizable>\r\n <DialogHeader>\r\n <DialogTitle>{tc('dcDetailTitle')}</DialogTitle>\r\n </DialogHeader>\r\n {detailData && (\r\n <div className=\"space-y-4\">\r\n <div className=\"grid grid-cols-2 gap-4 text-sm\">\r\n <div>\r\n <span className=\"text-muted-foreground\">{tc('dcRecordNo')}</span>\r\n {detailData.record_no}\r\n </div>\r\n <div>\r\n <span className=\"text-muted-foreground\">{tc('dcMaterialCode')}</span>\r\n {detailData.material_code}\r\n </div>\r\n <div>\r\n <span className=\"text-muted-foreground\">物料名称:</span>\r\n {detailData.material_name}\r\n </div>\r\n <div>\r\n <span className=\"text-muted-foreground\">{tc('dcInkType')}</span>\r\n <Badge className={INK_TYPE_MAP[detailData.ink_type]?.color || 'bg-gray-100'}>\r\n {INK_TYPE_MAP[detailData.ink_type]?.label || detailData.ink_type}\r\n </Badge>\r\n </div>\r\n <div>\r\n <span className=\"text-muted-foreground\">{tc('dcBatchNo')}</span>\r\n {detailData.batch_no || '-'}\r\n </div>\r\n <div>\r\n <span className=\"text-muted-foreground\">{tc('dcValidHoursLabel')}</span>\r\n {detailData.expire_hours}\r\n 小时\r\n </div>\r\n <div>\r\n <span className=\"text-muted-foreground\">{tc('dcOpenTime')}</span>\r\n {detailData.open_time}\r\n </div>\r\n <div>\r\n <span className=\"text-muted-foreground\">{tc('dcExpireTime')}</span>\r\n {detailData.expire_time}\r\n </div>\r\n <div>\r\n <span className=\"text-muted-foreground\">{tc('dcRemainingQty')}</span>\r\n {detailData.remaining_qty\r\n ? `${detailData.remaining_qty} ${detailData.unit}`\r\n : '-'}\r\n </div>\r\n <div>\r\n <span className=\"text-muted-foreground\">{tc('dcOperator')}</span>\r\n {detailData.operator_name || '-'}\r\n </div>\r\n <div>\r\n <span className=\"text-muted-foreground\">状态:</span>\r\n <Badge className={STATUS_MAP[detailData.status]?.color || 'bg-gray-100'}>\r\n {STATUS_MAP[detailData.status]?.label || detailData.status}\r\n </Badge>\r\n </div>\r\n </div>\r\n {detailData.remark && (\r\n <div className=\"text-sm\">\r\n <span className=\"text-muted-foreground\">备注:</span>\r\n {detailData.remark}\r\n </div>\r\n )}\r\n </div>\r\n )}\r\n </DialogContent>\r\n </Dialog>\r\n </div>\r\n </MainLayout>\r\n );\r\n}\r\n","usedDeprecatedRules":[]},{"filePath":"D:\\dcprint\\erp-project\\src\\app\\[locale]\\dcprint\\ink-usage\\page.tsx","messages":[{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":79,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":79,"endColumn":38},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":80,"column":18,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":80,"endColumn":25},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction<Ink[]>`.","line":81,"column":20,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":81,"endColumn":57},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":81,"column":27,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":81,"endColumn":31},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":81,"column":47,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":81,"endColumn":51},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":93,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":93,"endColumn":38},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":94,"column":18,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":94,"endColumn":25},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction<InkUsage[]>`.","line":95,"column":17,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":95,"endColumn":39},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":95,"column":24,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":95,"endColumn":28},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction<number>`.","line":96,"column":18,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":96,"endColumn":40},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":96,"column":25,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":96,"endColumn":29},{"ruleId":"react-hooks/set-state-in-effect","severity":1,"message":"Error: Calling setState synchronously within an effect can trigger cascading renders\n\nEffects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following:\n* Update external systems with the latest state from React.\n* Subscribe for updates from some external system, calling setState in a callback function when external state changes.\n\nCalling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect).\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\dcprint\\ink-usage\\page.tsx:102:5\n 100 |\n 101 | useEffect(() => {\n> 102 | fetchData();\n | ^^^^^^^^^ Avoid calling setState() directly within an effect\n 103 | }, [page]);\n 104 | useEffect(() => {\n 105 | fetchInks();","line":102,"column":5,"nodeType":null,"endLine":102,"endColumn":14},{"ruleId":"react-hooks/exhaustive-deps","severity":1,"message":"React Hook useEffect has a missing dependency: 'fetchData'. Either include it or remove the dependency array.","line":103,"column":6,"nodeType":"ArrayExpression","endLine":103,"endColumn":12,"suggestions":[{"desc":"Update the dependencies array to be: [fetchData, page]","fix":{"range":[2750,2756],"text":"[fetchData, page]"}}]},{"ruleId":"react-hooks/set-state-in-effect","severity":1,"message":"Error: Calling setState synchronously within an effect can trigger cascading renders\n\nEffects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following:\n* Update external systems with the latest state from React.\n* Subscribe for updates from some external system, calling setState in a callback function when external state changes.\n\nCalling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect).\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\dcprint\\ink-usage\\page.tsx:105:5\n 103 | }, [page]);\n 104 | useEffect(() => {\n> 105 | fetchInks();\n | ^^^^^^^^^ Avoid calling setState() directly within an effect\n 106 | }, []);\n 107 |\n 108 | const handleSave = async () => {","line":105,"column":5,"nodeType":null,"endLine":105,"endColumn":14},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":119,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":119,"endColumn":38},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":120,"column":18,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":120,"endColumn":25},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":125,"column":37,"nodeType":"Property","messageId":"anyAssignment","endLine":125,"endColumn":64},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .message on an `any` value.","line":125,"column":57,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":125,"endColumn":64},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":136,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":136,"endColumn":38},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":137,"column":18,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":137,"endColumn":25},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"新增耗用\"。建议使用: {tc('text_d7brz3')}","line":188,"column":48,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":190,"endColumn":13},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"耗用日期\"。建议使用: {tc('text_gn9imj')}","line":199,"column":50,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":199,"endColumn":54},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"网版编码\"。建议使用: {tc('text_gjgb2a')}","line":200,"column":50,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":200,"endColumn":54},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"油墨编码\"。建议使用: {tc('text_e39t6i')}","line":201,"column":50,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":201,"endColumn":54},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"油墨名称\"。建议使用: {tc('text_e32i1e')}","line":202,"column":50,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":202,"endColumn":54},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"耗用数量\"。建议使用: {tc('text_gn9o9c')}","line":203,"column":50,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":203,"endColumn":54},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"操作人\"。建议使用: {tc('text_f5g8b')}","line":205,"column":50,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":205,"endColumn":53},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"暂无记录\"。建议使用: {tc('text_dd1mmb')}","line":235,"column":87,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":237,"endColumn":21},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"共\"。建议使用: {tc('text_g35')}","line":246,"column":51,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":246,"endColumn":52},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"条\"。建议使用: {tc('text_kf5')}","line":246,"column":59,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":246,"endColumn":60},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"上一页\"。建议使用: {tc('text_btlof')}","line":253,"column":14,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":255,"endColumn":13},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"下一页\"。建议使用: {tc('text_btmf4')}","line":261,"column":14,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":263,"endColumn":13},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"油墨\"。建议使用: {tc('text_iz9r')}","line":274,"column":24,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":274,"endColumn":26},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"耗用数量\"。建议使用: {tc('text_gn9o9c')}","line":301,"column":24,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":301,"endColumn":28},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"耗用日期\"。建议使用: {tc('text_gn9imj')}","line":329,"column":24,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":329,"endColumn":28},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"操作人\"。建议使用: {tc('text_f5g8b')}","line":337,"column":24,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":337,"endColumn":27},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"取消\"。建议使用: {tc('text_ev02')}","line":352,"column":78,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":354,"endColumn":15}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":37,"fixableErrorCount":0,"fixableWarningCount":0,"source":"'use client';\n\nimport { authFetch } from '@/lib/auth-fetch';\nimport { useEffect, useState } from 'react';\nimport { MainLayout } from '@/components/layout';\nimport { Card, CardContent } from '@/components/ui/card';\nimport { Button } from '@/components/ui/button';\nimport { Input } from '@/components/ui/input';\nimport {\n Table,\n TableBody,\n TableCell,\n TableHead,\n TableHeader,\n TableRow,\n} from '@/components/ui/table';\nimport {\n Dialog,\n DialogContent,\n DialogHeader,\n DialogTitle,\n DialogFooter,\n} from '@/components/ui/dialog';\nimport { Label } from '@/components/ui/label';\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@/components/ui/select';\nimport { Textarea } from '@/components/ui/textarea';\nimport { Plus, Search, Trash2, Calendar } from 'lucide-react';\nimport { useToast } from '@/hooks/use-toast';\nimport { UserSelect } from '@/components/ui/user-select';\nimport { useTranslations } from 'next-intl';\n\ninterface InkUsage {\n id: number;\n work_order_id: number;\n screen_plate_id: number;\n plate_code: string;\n ink_id: number;\n ink_code: string;\n ink_name: string;\n usage_qty: number;\n unit: string;\n usage_date: string;\n operator_name: string;\n remark: string;\n create_time: string;\n}\n\ninterface Ink {\n id: number;\n ink_code: string;\n ink_name: string;\n unit: string;\n}\n\nexport default function InkUsagePage() {\n // 翻译钩子\n const tc = useTranslations('Common');\n\n const { toast } = useToast();\n const [list, setList] = useState<InkUsage[]>([]);\n const [total, setTotal] = useState(0);\n const [page, setPage] = useState(1);\n const [plateId, setPlateId] = useState('');\n const [startDate, setStartDate] = useState('');\n const [endDate, setEndDate] = useState('');\n const [showDialog, setShowDialog] = useState(false);\n const [editItem, setEditItem] = useState<Partial<InkUsage>>({});\n const [inkList, setInkList] = useState<Ink[]>([]);\n\n const fetchInks = async () => {\n try {\n const res = await authFetch('/api/base-inks');\n const result = await res.json();\n if (result.success) {\n setInkList(result.data.list || result.data || []);\n }\n } catch {}\n };\n\n const fetchData = async () => {\n try {\n const params = new URLSearchParams({ page: String(page), pageSize: '20' });\n if (plateId) params.set('plateId', plateId);\n if (startDate) params.set('startDate', startDate);\n if (endDate) params.set('endDate', endDate);\n const res = await authFetch('/api/ink-usages?' + params);\n const result = await res.json();\n if (result.success) {\n setList(result.data.list || []);\n setTotal(result.data.total || 0);\n }\n } catch {}\n };\n\n useEffect(() => {\n fetchData();\n }, [page]);\n useEffect(() => {\n fetchInks();\n }, []);\n\n const handleSave = async () => {\n try {\n const res = await authFetch('/api/ink-usages', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n ...editItem,\n usageDate: editItem.usage_date || new Date().toISOString(),\n operatorName: editItem.operator_name || '',\n }),\n });\n const result = await res.json();\n if (result.success) {\n toast({ title: tc('createSuccess') });\n setShowDialog(false);\n fetchData();\n } else {\n toast({ title: tc('error'), description: result.message, variant: 'destructive' });\n }\n } catch {\n toast({ title: tc('error'), variant: 'destructive' });\n }\n };\n\n const handleDelete = async (id: number) => {\n if (!confirm(tc('confirmDelete'))) return;\n try {\n const res = await authFetch('/api/ink-usages?id=' + id, { method: 'DELETE' });\n const result = await res.json();\n if (result.success) {\n toast({ title: tc('deleteSuccess') });\n fetchData();\n }\n } catch {\n toast({ title: tc('error'), variant: 'destructive' });\n }\n };\n\n return (\n <MainLayout>\n <div className=\"p-6 space-y-6\">\n <div className=\"flex items-center justify-between\">\n <h1 className=\"text-2xl font-bold\">{tc('dcInkUsageTitle')}</h1>\n <div className=\"flex gap-2\">\n <div className=\"flex items-center gap-2\">\n <Input\n placeholder=\"网版ID\"\n value={plateId}\n onChange={(e) => setPlateId(e.target.value)}\n className=\"w-24 h-8 text-sm\"\n />\n <div className=\"flex items-center gap-1\">\n <Calendar className=\"h-3 w-3 text-gray-400\" />\n <Input\n type=\"date\"\n value={startDate}\n onChange={(e) => setStartDate(e.target.value)}\n className=\"w-28 h-8 text-sm\"\n />\n </div>\n <div className=\"flex items-center gap-1\">\n <Calendar className=\"h-3 w-3 text-gray-400\" />\n <Input\n type=\"date\"\n value={endDate}\n onChange={(e) => setEndDate(e.target.value)}\n className=\"w-28 h-8 text-sm\"\n />\n </div>\n <Button size=\"sm\" variant=\"outline\" onClick={fetchData}>\n <Search className=\"h-3 w-3\" />\n </Button>\n </div>\n <Button\n size=\"sm\"\n onClick={() => {\n setEditItem({});\n setShowDialog(true);\n }}\n >\n <Plus className=\"h-3 w-3 mr-1\" />\n 新增耗用\n </Button>\n </div>\n </div>\n\n <Card>\n <CardContent className=\"p-0\">\n <Table>\n <TableHeader>\n <TableRow>\n <TableHead className=\"text-xs\">耗用日期</TableHead>\n <TableHead className=\"text-xs\">网版编码</TableHead>\n <TableHead className=\"text-xs\">油墨编码</TableHead>\n <TableHead className=\"text-xs\">油墨名称</TableHead>\n <TableHead className=\"text-xs\">耗用数量</TableHead>\n <TableHead className=\"text-xs\">{tc('unit')}</TableHead>\n <TableHead className=\"text-xs\">操作人</TableHead>\n <TableHead className=\"text-xs\">{tc('remark')}</TableHead>\n <TableHead className=\"text-xs\">{tc('actions')}</TableHead>\n </TableRow>\n </TableHeader>\n <TableBody>\n {list.map((item) => (\n <TableRow key={item.id}>\n <TableCell className=\"text-xs\">{item.usage_date}</TableCell>\n <TableCell className=\"text-xs font-mono\">{item.plate_code || '-'}</TableCell>\n <TableCell className=\"text-xs font-mono\">{item.ink_code}</TableCell>\n <TableCell className=\"text-xs\">{item.ink_name}</TableCell>\n <TableCell className=\"text-xs font-bold\">{item.usage_qty}</TableCell>\n <TableCell className=\"text-xs\">{item.unit}</TableCell>\n <TableCell className=\"text-xs\">{item.operator_name || '-'}</TableCell>\n <TableCell className=\"text-xs\">{item.remark || '-'}</TableCell>\n <TableCell>\n <Button\n size=\"sm\"\n variant=\"ghost\"\n className=\"h-6 w-6 p-0 text-red-600\"\n onClick={() => handleDelete(item.id)}\n >\n <Trash2 className=\"h-3 w-3\" />\n </Button>\n </TableCell>\n </TableRow>\n ))}\n {list.length === 0 && (\n <TableRow>\n <TableCell colSpan={9} className=\"text-center text-gray-400 py-8\">\n 暂无记录\n </TableCell>\n </TableRow>\n )}\n </TableBody>\n </Table>\n </CardContent>\n </Card>\n\n <div className=\"flex items-center justify-between\">\n <span className=\"text-sm text-gray-500\">共{total}条</span>\n <div className=\"flex gap-2\">\n <Button\n size=\"sm\"\n variant=\"outline\"\n disabled={page <= 1}\n onClick={() => setPage((p) => p - 1)}\n >\n 上一页\n </Button>\n <Button\n size=\"sm\"\n variant=\"outline\"\n disabled={page * 20 >= total}\n onClick={() => setPage((p) => p + 1)}\n >\n 下一页\n </Button>\n </div>\n </div>\n\n <Dialog open={showDialog} onOpenChange={setShowDialog}>\n <DialogContent className=\"max-w-lg\">\n <DialogHeader>\n <DialogTitle>{tc('dcAddUsageTitle')}</DialogTitle>\n </DialogHeader>\n <div className=\"space-y-4\">\n <div>\n <Label>油墨</Label>\n <Select\n value={String(editItem.ink_id || '')}\n onValueChange={(v) => {\n const ink = inkList.find((i) => i.id === Number(v));\n setEditItem({\n ...editItem,\n ink_id: Number(v),\n ink_code: ink?.ink_code,\n ink_name: ink?.ink_name,\n unit: ink?.unit,\n });\n }}\n >\n <SelectTrigger>\n <SelectValue placeholder=\"请选择油墨\" />\n </SelectTrigger>\n <SelectContent>\n {inkList.map((ink) => (\n <SelectItem key={ink.id} value={String(ink.id)}>\n {ink.ink_code} - {ink.ink_name}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n <div>\n <Label>耗用数量</Label>\n <Input\n type=\"number\"\n value={editItem.usage_qty ?? ''}\n onChange={(e) => setEditItem({ ...editItem, usage_qty: Number(e.target.value) })}\n />\n </div>\n <div>\n <Label>{tc('dcPlateIdLabel')}</Label>\n <Input\n type=\"number\"\n value={editItem.screen_plate_id ?? ''}\n onChange={(e) =>\n setEditItem({ ...editItem, screen_plate_id: Number(e.target.value) })\n }\n />\n </div>\n <div>\n <Label>{tc('dcWorkOrderIdLabel')}</Label>\n <Input\n type=\"number\"\n value={editItem.work_order_id ?? ''}\n onChange={(e) =>\n setEditItem({ ...editItem, work_order_id: Number(e.target.value) })\n }\n />\n </div>\n <div>\n <Label>耗用日期</Label>\n <Input\n type=\"datetime-local\"\n value={editItem.usage_date?.slice(0, 16) || ''}\n onChange={(e) => setEditItem({ ...editItem, usage_date: e.target.value })}\n />\n </div>\n <div>\n <Label>操作人</Label>\n <UserSelect\n value={editItem.operator_name || ''}\n onChange={(v) => setEditItem({ ...editItem, operator_name: v })}\n />\n </div>\n <div>\n <Label>{tc('remark')}</Label>\n <Textarea\n value={editItem.remark || ''}\n onChange={(e) => setEditItem({ ...editItem, remark: e.target.value })}\n />\n </div>\n </div>\n <DialogFooter>\n <Button variant=\"outline\" onClick={() => setShowDialog(false)}>\n 取消\n </Button>\n <Button onClick={handleSave}>{tc('save')}</Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n </div>\n </MainLayout>\n );\n}\n","usedDeprecatedRules":[]},{"filePath":"D:\\dcprint\\erp-project\\src\\app\\[locale]\\dcprint\\ink\\page.tsx","messages":[{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":122,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":122,"endColumn":38},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":123,"column":18,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":123,"endColumn":25},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction<Item[]>`.","line":124,"column":17,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":124,"endColumn":39},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":124,"column":24,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":124,"endColumn":28},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction<number>`.","line":125,"column":18,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":125,"endColumn":40},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":125,"column":25,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":125,"endColumn":29},{"ruleId":"react-hooks/set-state-in-effect","severity":1,"message":"Error: Calling setState synchronously within an effect can trigger cascading renders\n\nEffects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following:\n* Update external systems with the latest state from React.\n* Subscribe for updates from some external system, calling setState in a callback function when external state changes.\n\nCalling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect).\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\dcprint\\ink\\page.tsx:131:5\n 129 |\n 130 | useEffect(() => {\n> 131 | fetchData();\n | ^^^^^^^^^ Avoid calling setState() directly within an effect\n 132 | }, [fetchData]);\n 133 |\n 134 | const handleSort = (field: SortField) => {","line":131,"column":5,"nodeType":null,"endLine":131,"endColumn":14},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":210,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":210,"endColumn":38},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":211,"column":18,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":211,"endColumn":25},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":216,"column":38,"nodeType":"Property","messageId":"anyAssignment","endLine":216,"endColumn":65},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .message on an `any` value.","line":216,"column":58,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":216,"endColumn":65},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":227,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":227,"endColumn":38},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":228,"column":18,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":228,"endColumn":25},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"油墨管理\"。建议使用: {tc('text_e39784')}","line":312,"column":28,"nodeType":"Literal","messageId":"noChineseInJSX","endLine":312,"endColumn":34},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Computed name [v] resolves to an `any` value.","line":321,"column":49,"nodeType":"Identifier","messageId":"unsafeComputedMemberAccess","endLine":321,"endColumn":50},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Computed name [v] resolves to an `any` value.","line":333,"column":51,"nodeType":"Identifier","messageId":"unsafeComputedMemberAccess","endLine":333,"endColumn":52},{"ruleId":"react-hooks/static-components","severity":1,"message":"Error: Cannot create components during render\n\nComponents created during render will reset their state each time they are created. Declare components outside of render.\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\dcprint\\ink\\page.tsx:361:24\n 359 | <span className=\"flex items-center\">\n 360 | {t('inkCode')}\n> 361 | <SortIcon field=\"ink_code\" />\n | ^^^^^^^^ This component is created during render\n 362 | </span>\n 363 | </TableHead>\n 364 | <TableHead\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\dcprint\\ink\\page.tsx:170:20\n 168 | };\n 169 |\n> 170 | const SortIcon = ({ field }: { field: SortField }) => {\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n> 171 | if (sortField !== field) return <ArrowUpDown className=\"h-3 w-3 ml-1 opacity-30\" />;\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n> 172 | return sortDir === 'asc' ? (\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n> 173 | <ArrowUp className=\"h-3 w-3 ml-1 text-blue-600\" />\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n> 174 | ) : (\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n> 175 | <ArrowDown className=\"h-3 w-3 ml-1 text-blue-600\" />\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n> 176 | );\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n> 177 | };\n | ^^^^ The component is created during render here\n 178 |\n 179 | const getExportData = () =>\n 180 | sortedList.map((item) => ({","line":361,"column":24,"nodeType":null,"endLine":361,"endColumn":32},{"ruleId":"react-hooks/static-components","severity":1,"message":"Error: Cannot create components during render\n\nComponents created during render will reset their state each time they are created. Declare components outside of render.\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\dcprint\\ink\\page.tsx:370:24\n 368 | <span className=\"flex items-center\">\n 369 | {t('inkName')}\n> 370 | <SortIcon field=\"ink_name\" />\n | ^^^^^^^^ This component is created during render\n 371 | </span>\n 372 | </TableHead>\n 373 | <TableHead\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\dcprint\\ink\\page.tsx:170:20\n 168 | };\n 169 |\n> 170 | const SortIcon = ({ field }: { field: SortField }) => {\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n> 171 | if (sortField !== field) return <ArrowUpDown className=\"h-3 w-3 ml-1 opacity-30\" />;\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n> 172 | return sortDir === 'asc' ? (\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n> 173 | <ArrowUp className=\"h-3 w-3 ml-1 text-blue-600\" />\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n> 174 | ) : (\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n> 175 | <ArrowDown className=\"h-3 w-3 ml-1 text-blue-600\" />\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n> 176 | );\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n> 177 | };\n | ^^^^ The component is created during render here\n 178 |\n 179 | const getExportData = () =>\n 180 | sortedList.map((item) => ({","line":370,"column":24,"nodeType":null,"endLine":370,"endColumn":32},{"ruleId":"react-hooks/static-components","severity":1,"message":"Error: Cannot create components during render\n\nComponents created during render will reset their state each time they are created. Declare components outside of render.\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\dcprint\\ink\\page.tsx:379:24\n 377 | <span className=\"flex items-center\">\n 378 | {tc('type')}\n> 379 | <SortIcon field=\"ink_type\" />\n | ^^^^^^^^ This component is created during render\n 380 | </span>\n 381 | </TableHead>\n 382 | <TableHead className=\"text-xs\">{tc('color')}</TableHead>\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\dcprint\\ink\\page.tsx:170:20\n 168 | };\n 169 |\n> 170 | const SortIcon = ({ field }: { field: SortField }) => {\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n> 171 | if (sortField !== field) return <ArrowUpDown className=\"h-3 w-3 ml-1 opacity-30\" />;\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n> 172 | return sortDir === 'asc' ? (\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n> 173 | <ArrowUp className=\"h-3 w-3 ml-1 text-blue-600\" />\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n> 174 | ) : (\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n> 175 | <ArrowDown className=\"h-3 w-3 ml-1 text-blue-600\" />\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n> 176 | );\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n> 177 | };\n | ^^^^ The component is created during render here\n 178 |\n 179 | const getExportData = () =>\n 180 | sortedList.map((item) => ({","line":379,"column":24,"nodeType":null,"endLine":379,"endColumn":32},{"ruleId":"react-hooks/static-components","severity":1,"message":"Error: Cannot create components during render\n\nComponents created during render will reset their state each time they are created. Declare components outside of render.\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\dcprint\\ink\\page.tsx:392:24\n 390 | <span className=\"flex items-center\">\n 391 | {t('stock')}\n> 392 | <SortIcon field=\"stock_qty\" />\n | ^^^^^^^^ This component is created during render\n 393 | </span>\n 394 | </TableHead>\n 395 | <TableHead\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\dcprint\\ink\\page.tsx:170:20\n 168 | };\n 169 |\n> 170 | const SortIcon = ({ field }: { field: SortField }) => {\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n> 171 | if (sortField !== field) return <ArrowUpDown className=\"h-3 w-3 ml-1 opacity-30\" />;\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n> 172 | return sortDir === 'asc' ? (\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n> 173 | <ArrowUp className=\"h-3 w-3 ml-1 text-blue-600\" />\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n> 174 | ) : (\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n> 175 | <ArrowDown className=\"h-3 w-3 ml-1 text-blue-600\" />\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n> 176 | );\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n> 177 | };\n | ^^^^ The component is created during render here\n 178 |\n 179 | const getExportData = () =>\n 180 | sortedList.map((item) => ({","line":392,"column":24,"nodeType":null,"endLine":392,"endColumn":32},{"ruleId":"react-hooks/static-components","severity":1,"message":"Error: Cannot create components during render\n\nComponents created during render will reset their state each time they are created. Declare components outside of render.\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\dcprint\\ink\\page.tsx:401:24\n 399 | <span className=\"flex items-center\">\n 400 | {t('safetyStock')}\n> 401 | <SortIcon field=\"safety_stock\" />\n | ^^^^^^^^ This component is created during render\n 402 | </span>\n 403 | </TableHead>\n 404 | <TableHead\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\dcprint\\ink\\page.tsx:170:20\n 168 | };\n 169 |\n> 170 | const SortIcon = ({ field }: { field: SortField }) => {\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n> 171 | if (sortField !== field) return <ArrowUpDown className=\"h-3 w-3 ml-1 opacity-30\" />;\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n> 172 | return sortDir === 'asc' ? (\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n> 173 | <ArrowUp className=\"h-3 w-3 ml-1 text-blue-600\" />\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n> 174 | ) : (\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n> 175 | <ArrowDown className=\"h-3 w-3 ml-1 text-blue-600\" />\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n> 176 | );\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n> 177 | };\n | ^^^^ The component is created during render here\n 178 |\n 179 | const getExportData = () =>\n 180 | sortedList.map((item) => ({","line":401,"column":24,"nodeType":null,"endLine":401,"endColumn":32},{"ruleId":"react-hooks/static-components","severity":1,"message":"Error: Cannot create components during render\n\nComponents created during render will reset their state each time they are created. Declare components outside of render.\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\dcprint\\ink\\page.tsx:410:24\n 408 | <span className=\"flex items-center\">\n 409 | {tc('status')}\n> 410 | <SortIcon field=\"status\" />\n | ^^^^^^^^ This component is created during render\n 411 | </span>\n 412 | </TableHead>\n 413 | <TableHead className=\"text-xs\">{tc('actions')}</TableHead>\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\dcprint\\ink\\page.tsx:170:20\n 168 | };\n 169 |\n> 170 | const SortIcon = ({ field }: { field: SortField }) => {\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n> 171 | if (sortField !== field) return <ArrowUpDown className=\"h-3 w-3 ml-1 opacity-30\" />;\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n> 172 | return sortDir === 'asc' ? (\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n> 173 | <ArrowUp className=\"h-3 w-3 ml-1 text-blue-600\" />\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n> 174 | ) : (\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n> 175 | <ArrowDown className=\"h-3 w-3 ml-1 text-blue-600\" />\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n> 176 | );\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n> 177 | };\n | ^^^^ The component is created during render here\n 178 |\n 179 | const getExportData = () =>\n 180 | sortedList.map((item) => ({","line":410,"column":24,"nodeType":null,"endLine":410,"endColumn":32}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":22,"fixableErrorCount":0,"fixableWarningCount":0,"source":"'use client';\r\n\r\nimport { authFetch } from '@/lib/auth-fetch';\r\nimport { useEffect, useState, useCallback } from 'react';\r\nimport { MainLayout } from '@/components/layout';\r\nimport { Card, CardContent } from '@/components/ui/card';\r\nimport { Button } from '@/components/ui/button';\r\nimport { Input } from '@/components/ui/input';\r\nimport { Badge } from '@/components/ui/badge';\r\nimport {\r\n Table,\r\n TableBody,\r\n TableCell,\r\n TableHead,\r\n TableHeader,\r\n TableRow,\r\n} from '@/components/ui/table';\r\nimport {\r\n Dialog,\r\n DialogContent,\r\n DialogHeader,\r\n DialogTitle,\r\n DialogFooter,\r\n} from '@/components/ui/dialog';\r\nimport { Label } from '@/components/ui/label';\r\nimport {\r\n Select,\r\n SelectContent,\r\n SelectItem,\r\n SelectTrigger,\r\n SelectValue,\r\n} from '@/components/ui/select';\r\nimport { Plus, Search, Edit, Trash2, ArrowUpDown, ArrowUp, ArrowDown } from 'lucide-react';\r\nimport { useToast } from '@/hooks/use-toast';\r\nimport { Checkbox } from '@/components/ui/checkbox';\r\nimport {\r\n printTable,\r\n exportTableToPDF,\r\n exportTableToXLS,\r\n exportTableToWORD,\r\n} from '@/components/ui/table-export-toolbar';\r\nimport { GlobalExportToolbar } from '@/components/ui/global-export-toolbar';\r\nimport { useTranslations } from 'next-intl';\r\n\r\ninterface Item {\r\n id: number;\r\n ink_code: string;\r\n ink_name: string;\r\n ink_type: number;\r\n color_name: string;\r\n color_code: string;\r\n brand: string;\r\n unit: string;\r\n stock_qty: number;\r\n safety_stock: number;\r\n status: number;\r\n}\r\n\r\n// typeMap will be populated dynamically with translations\r\ntype SortField = 'ink_code' | 'ink_name' | 'ink_type' | 'stock_qty' | 'safety_stock' | 'status';\r\ntype SortDir = 'asc' | 'desc';\r\n\r\nexport default function InkManagementPage() {\r\n // 翻译钩子\r\n const t = useTranslations('Dcprint');\r\n const tc = useTranslations('Common');\r\n\r\n const typeMap: Record<number, string> = {\r\n 1: t('waterInk'),\r\n 2: t('solventInk'),\r\n 3: t('uvInk'),\r\n 4: t('screenInk'),\r\n 5: t('specialInk'),\r\n };\r\n\r\n const statusMap: Record<\r\n number,\r\n { label: string; variant: 'default' | 'secondary' | 'destructive' | 'outline' }\r\n > = {\r\n 1: { label: tc('enabled'), variant: 'default' },\r\n 0: { label: tc('disabled'), variant: 'destructive' },\r\n };\r\n\r\n const exportColumns = [\r\n { key: 'ink_code', header: t('inkCode') },\r\n { key: 'ink_name', header: t('inkName') },\r\n { key: 'ink_type_label', header: tc('type') },\r\n { key: 'color_name', header: t('color') },\r\n { key: 'color_code', header: t('colorCode') },\r\n { key: 'brand', header: tc('brand') },\r\n { key: 'unit', header: tc('unit') },\r\n { key: 'stock_qty', header: t('stock') },\r\n { key: 'safety_stock', header: t('safetyStock') },\r\n { key: 'status_label', header: tc('status') },\r\n ];\r\n\r\n const { toast } = useToast();\r\n const [list, setList] = useState<Item[]>([]);\r\n const [total, setTotal] = useState(0);\r\n const [page, setPage] = useState(1);\r\n const [searchCode, setSearchCode] = useState('');\r\n const [searchName, setSearchName] = useState('');\r\n const [searchType, setSearchType] = useState('');\r\n const [searchStatus, setSearchStatus] = useState('');\r\n const [showDialog, setShowDialog] = useState(false);\r\n const [editItem, setEditItem] = useState<Partial<Item>>({});\r\n const [sortField, setSortField] = useState<SortField>('ink_code');\r\n const [sortDir, setSortDir] = useState<SortDir>('asc');\r\n const [selectedIds, setSelectedIds] = useState<Set<number>>(new Set());\r\n\r\n const fetchData = useCallback(async () => {\r\n try {\r\n const params = new URLSearchParams({\r\n page: String(page),\r\n pageSize: '20',\r\n inkCode: searchCode,\r\n inkName: searchName,\r\n });\r\n if (searchType) params.set('inkType', searchType);\r\n if (searchStatus) params.set('status', searchStatus);\r\n const res = await authFetch('/api/prepress/ink?' + params);\r\n const result = await res.json();\r\n if (result.success) {\r\n setList(result.data.list || []);\r\n setTotal(result.data.total || 0);\r\n }\r\n } catch {}\r\n }, [page, searchCode, searchName, searchType, searchStatus]);\r\n\r\n useEffect(() => {\r\n fetchData();\r\n }, [fetchData]);\r\n\r\n const handleSort = (field: SortField) => {\r\n if (sortField === field) {\r\n setSortDir((d) => (d === 'asc' ? 'desc' : 'asc'));\r\n } else {\r\n setSortField(field);\r\n setSortDir('asc');\r\n }\r\n };\r\n\r\n const sortedList = [...list].sort((a, b) => {\r\n const va = a[sortField];\r\n const vb = b[sortField];\r\n if (va == null && vb == null) return 0;\r\n if (va == null) return 1;\r\n if (vb == null) return -1;\r\n let cmp = 0;\r\n if (typeof va === 'string' && typeof vb === 'string') {\r\n cmp = va.localeCompare(vb, 'zh-CN');\r\n } else {\r\n cmp = (va as number) - (vb as number);\r\n }\r\n return sortDir === 'asc' ? cmp : -cmp;\r\n });\r\n\r\n const toggleSelect = (id: number) => {\r\n const next = new Set(selectedIds);\r\n if (next.has(id)) next.delete(id);\r\n else next.add(id);\r\n setSelectedIds(next);\r\n };\r\n\r\n const toggleSelectAll = () => {\r\n if (selectedIds.size === sortedList.length) setSelectedIds(new Set());\r\n else setSelectedIds(new Set(sortedList.map((s) => s.id)));\r\n };\r\n\r\n const SortIcon = ({ field }: { field: SortField }) => {\r\n if (sortField !== field) return <ArrowUpDown className=\"h-3 w-3 ml-1 opacity-30\" />;\r\n return sortDir === 'asc' ? (\r\n <ArrowUp className=\"h-3 w-3 ml-1 text-blue-600\" />\r\n ) : (\r\n <ArrowDown className=\"h-3 w-3 ml-1 text-blue-600\" />\r\n );\r\n };\r\n\r\n const getExportData = () =>\r\n sortedList.map((item) => ({\r\n ...item,\r\n ink_type_label: typeMap[item.ink_type] || '-',\r\n status_label: statusMap[item.status]?.label || '-',\r\n }));\r\n\r\n const _handlePrint = () => {\r\n printTable(getExportData(), exportColumns, t('inkManagement'));\r\n };\r\n\r\n const _handleExportPDF = () => {\r\n exportTableToPDF(getExportData(), t('inkManagement'), exportColumns, t('inkManagement'));\r\n };\r\n\r\n const _handleExportXLS = () => {\r\n exportTableToXLS(getExportData(), t('inkManagement'), exportColumns);\r\n };\r\n\r\n const _handleExportWORD = () => {\r\n exportTableToWORD(getExportData(), t('inkManagement'), exportColumns, t('inkManagement'));\r\n };\r\n\r\n const handleSave = async () => {\r\n try {\r\n const method = editItem.id ? 'PUT' : 'POST';\r\n const res = await authFetch('/api/prepress/ink', {\r\n method,\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify(editItem),\r\n });\r\n const result = await res.json();\r\n if (result.success) {\r\n toast({ title: editItem.id ? tc('updateSuccess') : tc('createSuccess') });\r\n setShowDialog(false);\r\n fetchData();\r\n } else {\r\n toast({ title: tc('failed'), description: result.message, variant: 'destructive' });\r\n }\r\n } catch {\r\n toast({ title: tc('failed'), variant: 'destructive' });\r\n }\r\n };\r\n\r\n const handleDelete = async (id: number) => {\r\n if (!confirm(tc('confirmDelete'))) return;\r\n try {\r\n const res = await authFetch('/api/prepress/ink?id=' + id, { method: 'DELETE' });\r\n const result = await res.json();\r\n if (result.success) {\r\n toast({ title: tc('deleteSuccess') });\r\n fetchData();\r\n }\r\n } catch {\r\n toast({ title: tc('failed'), variant: 'destructive' });\r\n }\r\n };\r\n\r\n return (\r\n <MainLayout>\r\n <div className=\"p-6 space-y-6\">\r\n <div className=\"flex items-center justify-between\">\r\n <h1 className=\"text-2xl font-bold\">{t('inkManagement')}</h1>\r\n <div className=\"flex gap-2\">\r\n <Button\r\n size=\"sm\"\r\n onClick={() => {\r\n setEditItem({});\r\n setShowDialog(true);\r\n }}\r\n >\r\n <Plus className=\"h-3 w-3 mr-1\" />\r\n {t('addInk')}\r\n </Button>\r\n </div>\r\n </div>\r\n\r\n <Card>\r\n <CardContent className=\"p-4\">\r\n <div className=\"flex flex-wrap items-center gap-2 mb-4\">\r\n <Input\r\n placeholder={t('inkCode')}\r\n value={searchCode}\r\n onChange={(e) => setSearchCode(e.target.value)}\r\n className=\"w-32 h-8 text-sm\"\r\n onKeyDown={(e) => e.key === 'Enter' && fetchData()}\r\n />\r\n <Input\r\n placeholder={t('inkName')}\r\n value={searchName}\r\n onChange={(e) => setSearchName(e.target.value)}\r\n className=\"w-32 h-8 text-sm\"\r\n onKeyDown={(e) => e.key === 'Enter' && fetchData()}\r\n />\r\n <Select\r\n value={searchType}\r\n onValueChange={(v) => {\r\n setSearchType(v === '_all' ? '' : v);\r\n }}\r\n >\r\n <SelectTrigger className=\"w-28 h-8 text-sm\">\r\n <SelectValue placeholder={t('allTypes')} />\r\n </SelectTrigger>\r\n <SelectContent>\r\n <SelectItem value=\"_all\">{t('allTypes')}</SelectItem>\r\n {Object.entries(typeMap).map(([k, v]) => (\r\n <SelectItem key={k} value={k}>\r\n {v}\r\n </SelectItem>\r\n ))}\r\n </SelectContent>\r\n </Select>\r\n <Select\r\n value={searchStatus}\r\n onValueChange={(v) => {\r\n setSearchStatus(v === '_all' ? '' : v);\r\n }}\r\n >\r\n <SelectTrigger className=\"w-24 h-8 text-sm\">\r\n <SelectValue placeholder={t('allStatus')} />\r\n </SelectTrigger>\r\n <SelectContent>\r\n <SelectItem value=\"_all\">{t('allStatus')}</SelectItem>\r\n <SelectItem value=\"1\">{tc('enable')}</SelectItem>\r\n <SelectItem value=\"0\">{tc('disable')}</SelectItem>\r\n </SelectContent>\r\n </Select>\r\n <Button size=\"sm\" variant=\"outline\" className=\"h-8\" onClick={fetchData}>\r\n <Search className=\"h-3 w-3 mr-1\" />\r\n {tc('search')}\r\n </Button>\r\n <div className=\"ml-auto\">\r\n <GlobalExportToolbar\r\n filename=\"油墨管理\"\r\n title=\"油墨管理\"\r\n columns={[\r\n { key: 'ink_code', label: t('inkCode'), width: 15 },\r\n { key: 'ink_name', label: t('inkName'), width: 20 },\r\n {\r\n key: 'ink_type',\r\n label: tc('type'),\r\n width: 10,\r\n formatter: (v) => typeMap[v] || '-',\r\n },\r\n { key: 'color_name', label: tc('color'), width: 10 },\r\n { key: 'color_code', label: t('colorCode'), width: 10 },\r\n { key: 'brand', label: tc('brand'), width: 12 },\r\n { key: 'unit', label: tc('unit'), width: 8 },\r\n { key: 'stock_qty', label: t('stock'), width: 10 },\r\n { key: 'safety_stock', label: t('safetyStock'), width: 10 },\r\n {\r\n key: 'status',\r\n label: tc('status'),\r\n width: 10,\r\n formatter: (v) => statusMap[v]?.label || '-',\r\n },\r\n ]}\r\n data={\r\n selectedIds.size > 0\r\n ? sortedList.filter((i) => selectedIds.has(i.id))\r\n : sortedList\r\n }\r\n />\r\n </div>\r\n </div>\r\n\r\n <Table>\r\n <TableHeader>\r\n <TableRow>\r\n <TableHead className=\"w-[40px]\">\r\n <Checkbox\r\n checked={selectedIds.size > 0 && selectedIds.size === sortedList.length}\r\n onCheckedChange={toggleSelectAll}\r\n />\r\n </TableHead>\r\n <TableHead className=\"w-[60px]\">{tc('serialNo')}</TableHead>\r\n <TableHead\r\n className=\"text-xs cursor-pointer select-none\"\r\n onClick={() => handleSort('ink_code')}\r\n >\r\n <span className=\"flex items-center\">\r\n {t('inkCode')}\r\n <SortIcon field=\"ink_code\" />\r\n </span>\r\n </TableHead>\r\n <TableHead\r\n className=\"text-xs cursor-pointer select-none\"\r\n onClick={() => handleSort('ink_name')}\r\n >\r\n <span className=\"flex items-center\">\r\n {t('inkName')}\r\n <SortIcon field=\"ink_name\" />\r\n </span>\r\n </TableHead>\r\n <TableHead\r\n className=\"text-xs cursor-pointer select-none\"\r\n onClick={() => handleSort('ink_type')}\r\n >\r\n <span className=\"flex items-center\">\r\n {tc('type')}\r\n <SortIcon field=\"ink_type\" />\r\n </span>\r\n </TableHead>\r\n <TableHead className=\"text-xs\">{tc('color')}</TableHead>\r\n <TableHead className=\"text-xs\">{t('colorCode')}</TableHead>\r\n <TableHead className=\"text-xs\">{tc('brand')}</TableHead>\r\n <TableHead className=\"text-xs\">{tc('unit')}</TableHead>\r\n <TableHead\r\n className=\"text-xs cursor-pointer select-none\"\r\n onClick={() => handleSort('stock_qty')}\r\n >\r\n <span className=\"flex items-center\">\r\n {t('stock')}\r\n <SortIcon field=\"stock_qty\" />\r\n </span>\r\n </TableHead>\r\n <TableHead\r\n className=\"text-xs cursor-pointer select-none\"\r\n onClick={() => handleSort('safety_stock')}\r\n >\r\n <span className=\"flex items-center\">\r\n {t('safetyStock')}\r\n <SortIcon field=\"safety_stock\" />\r\n </span>\r\n </TableHead>\r\n <TableHead\r\n className=\"text-xs cursor-pointer select-none\"\r\n onClick={() => handleSort('status')}\r\n >\r\n <span className=\"flex items-center\">\r\n {tc('status')}\r\n <SortIcon field=\"status\" />\r\n </span>\r\n </TableHead>\r\n <TableHead className=\"text-xs\">{tc('actions')}</TableHead>\r\n </TableRow>\r\n </TableHeader>\r\n <TableBody>\r\n {sortedList.map((item, index) => {\r\n const st = statusMap[item.status] ?? statusMap[1];\r\n return (\r\n <TableRow key={item.id}>\r\n <TableCell>\r\n <Checkbox\r\n checked={selectedIds.has(item.id)}\r\n onCheckedChange={() => toggleSelect(item.id)}\r\n />\r\n </TableCell>\r\n <TableCell className=\"text-xs text-muted-foreground\">{index + 1}</TableCell>\r\n <TableCell className=\"text-xs font-mono\">{item.ink_code}</TableCell>\r\n <TableCell className=\"text-xs\">{item.ink_name}</TableCell>\r\n <TableCell className=\"text-xs\">{typeMap[item.ink_type] || '-'}</TableCell>\r\n <TableCell className=\"text-xs\">{item.color_name || '-'}</TableCell>\r\n <TableCell className=\"text-xs\">{item.color_code || '-'}</TableCell>\r\n <TableCell className=\"text-xs\">{item.brand || '-'}</TableCell>\r\n <TableCell className=\"text-xs\">{item.unit}</TableCell>\r\n <TableCell className=\"text-xs\">\r\n {item.stock_qty ?? 0}\r\n {item.stock_qty < item.safety_stock && (\r\n <span className=\"text-red-500 ml-1\">⚠</span>\r\n )}\r\n </TableCell>\r\n <TableCell className=\"text-xs\">{item.safety_stock}</TableCell>\r\n <TableCell>\r\n <Badge variant={st.variant} className=\"text-xs\">\r\n {st.label}\r\n </Badge>\r\n </TableCell>\r\n <TableCell>\r\n <div className=\"flex gap-1\">\r\n <Button\r\n size=\"sm\"\r\n variant=\"ghost\"\r\n className=\"h-6 w-6 p-0\"\r\n onClick={() => {\r\n setEditItem(item);\r\n setShowDialog(true);\r\n }}\r\n >\r\n <Edit className=\"h-3 w-3\" />\r\n </Button>\r\n <Button\r\n size=\"sm\"\r\n variant=\"ghost\"\r\n className=\"h-6 w-6 p-0 text-red-600\"\r\n onClick={() => handleDelete(item.id)}\r\n >\r\n <Trash2 className=\"h-3 w-3\" />\r\n </Button>\r\n </div>\r\n </TableCell>\r\n </TableRow>\r\n );\r\n })}\r\n {sortedList.length === 0 && (\r\n <TableRow>\r\n <TableCell colSpan={13} className=\"text-center text-gray-400 py-8\">\r\n {tc('noRecords')}\r\n </TableCell>\r\n </TableRow>\r\n )}\r\n </TableBody>\r\n </Table>\r\n\r\n <div className=\"flex items-center justify-between mt-4\">\r\n <span className=\"text-sm text-gray-500\">{tc('total', { count: total })}</span>\r\n <div className=\"flex gap-2\">\r\n <Button\r\n size=\"sm\"\r\n variant=\"outline\"\r\n disabled={page <= 1}\r\n onClick={() => setPage((p) => p - 1)}\r\n >\r\n {tc('prevPage')}\r\n </Button>\r\n <Button\r\n size=\"sm\"\r\n variant=\"outline\"\r\n disabled={page * 20 >= total}\r\n onClick={() => setPage((p) => p + 1)}\r\n >\r\n {tc('nextPage')}\r\n </Button>\r\n </div>\r\n </div>\r\n </CardContent>\r\n </Card>\r\n\r\n <Dialog open={showDialog} onOpenChange={setShowDialog}>\r\n <DialogContent className=\"max-w-lg\" resizable>\r\n <DialogHeader>\r\n <DialogTitle>{editItem.id ? t('editInk') : t('addInk')}</DialogTitle>\r\n </DialogHeader>\r\n <div className=\"grid grid-cols-2 gap-4\">\r\n <div>\r\n <Label>{t('inkCode')}</Label>\r\n <Input\r\n value={editItem.ink_code || ''}\r\n onChange={(e) => setEditItem({ ...editItem, ink_code: e.target.value })}\r\n />\r\n </div>\r\n <div>\r\n <Label>{t('inkName')}</Label>\r\n <Input\r\n value={editItem.ink_name || ''}\r\n onChange={(e) => setEditItem({ ...editItem, ink_name: e.target.value })}\r\n />\r\n </div>\r\n <div>\r\n <Label>{tc('type')}</Label>\r\n <Select\r\n value={String(editItem.ink_type || 4)}\r\n onValueChange={(v) => setEditItem({ ...editItem, ink_type: Number(v) })}\r\n >\r\n <SelectTrigger>\r\n <SelectValue />\r\n </SelectTrigger>\r\n <SelectContent>\r\n <SelectItem value=\"1\">{t('waterInk')}</SelectItem>\r\n <SelectItem value=\"2\">{t('solventInk')}</SelectItem>\r\n <SelectItem value=\"3\">{t('uvInk')}</SelectItem>\r\n <SelectItem value=\"4\">{t('screenInk')}</SelectItem>\r\n <SelectItem value=\"5\">{t('specialInk')}</SelectItem>\r\n </SelectContent>\r\n </Select>\r\n </div>\r\n <div>\r\n <Label>{t('colorName')}</Label>\r\n <Input\r\n value={editItem.color_name || ''}\r\n onChange={(e) => setEditItem({ ...editItem, color_name: e.target.value })}\r\n />\r\n </div>\r\n <div>\r\n <Label>{t('colorCode')}</Label>\r\n <Input\r\n value={editItem.color_code || ''}\r\n onChange={(e) => setEditItem({ ...editItem, color_code: e.target.value })}\r\n />\r\n </div>\r\n <div>\r\n <Label>{tc('brand')}</Label>\r\n <Input\r\n value={editItem.brand || ''}\r\n onChange={(e) => setEditItem({ ...editItem, brand: e.target.value })}\r\n />\r\n </div>\r\n <div>\r\n <Label>{tc('unit')}</Label>\r\n <Input\r\n value={editItem.unit || 'kg'}\r\n onChange={(e) => setEditItem({ ...editItem, unit: e.target.value })}\r\n />\r\n </div>\r\n <div>\r\n <Label>{t('safetyStock')}</Label>\r\n <Input\r\n type=\"number\"\r\n value={editItem.safety_stock ?? ''}\r\n onChange={(e) =>\r\n setEditItem({ ...editItem, safety_stock: Number(e.target.value) })\r\n }\r\n />\r\n </div>\r\n </div>\r\n <DialogFooter>\r\n <Button variant=\"outline\" onClick={() => setShowDialog(false)}>\r\n {tc('cancel')}\r\n </Button>\r\n <Button onClick={handleSave}>{tc('save')}</Button>\r\n </DialogFooter>\r\n </DialogContent>\r\n </Dialog>\r\n </div>\r\n </MainLayout>\r\n );\r\n}\r\n","usedDeprecatedRules":[]},{"filePath":"D:\\dcprint\\erp-project\\src\\app\\[locale]\\dcprint\\labels\\page.tsx","messages":[{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"使用 mock 标签数据\"。建议使用: tc('text_zhs964')","line":141,"column":69,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":141,"endColumn":83},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":164,"column":15,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":164,"endColumn":45},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":166,"column":20,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":166,"endColumn":27},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction<MaterialLabel[]>`.","line":167,"column":21,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":167,"endColumn":44},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":167,"column":28,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":167,"endColumn":32},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction<number>`.","line":168,"column":20,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":168,"endColumn":55},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":168,"column":27,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":168,"endColumn":31},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"标签数据获取成功\"。建议使用: tc('text_fiuf4t')","line":169,"column":69,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":169,"endColumn":79},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":170,"column":13,"nodeType":"Property","messageId":"anyAssignment","endLine":170,"endColumn":52},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":170,"column":28,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":170,"endColumn":32},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .length on an `any` value.","line":170,"column":46,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":170,"endColumn":52},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"获取标签数据失败\"。建议使用: tc('text_appazc')","line":175,"column":70,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":175,"endColumn":80},{"ruleId":"react-hooks/exhaustive-deps","severity":1,"message":"React Hook useEffect has a missing dependency: 'pageSize'. Either include it or remove the dependency array.","line":187,"column":6,"nodeType":"ArrayExpression","endLine":187,"endColumn":44,"suggestions":[{"desc":"Update the dependencies array to be: [page, isMainMaterial, isCut, keyword, pageSize]","fix":{"range":[5831,5869],"text":"[page, isMainMaterial, isCut, keyword, pageSize]"}}]},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":236,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":236,"endColumn":43},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":237,"column":18,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":237,"endColumn":25},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `string | number | bigint | boolean | ReactElement<unknown, string | JSXElementConstructor<any>> | Iterable<ReactNode> | ... 4 more ... | undefined`.","line":242,"column":21,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":242,"endColumn":57},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .message on an `any` value.","line":242,"column":28,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":242,"endColumn":35}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":17,"fixableErrorCount":0,"fixableWarningCount":0,"source":"'use client';\n\nimport { authFetch } from '@/lib/auth-fetch';\nimport { useState, useEffect } from 'react';\nimport { MainLayout } from '@/components/layout';\nimport { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';\nimport { Badge } from '@/components/ui/badge';\nimport {\n Table,\n TableBody,\n TableCell,\n TableHead,\n TableHeader,\n TableRow,\n} from '@/components/ui/table';\nimport { Checkbox } from '@/components/ui/checkbox';\nimport { Button } from '@/components/ui/button';\nimport { Input } from '@/components/ui/input';\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@/components/ui/select';\nimport {\n Dialog,\n DialogContent,\n DialogHeader,\n DialogTitle,\n DialogTrigger,\n DialogFooter,\n} from '@/components/ui/dialog';\nimport { Search, QrCode, Scissors, Printer, RefreshCw, Trash2, Settings } from 'lucide-react';\nimport { useAuth } from '@/contexts/AuthContext';\nimport { toast } from 'sonner';\nimport { LabelPrintTrigger, LabelData } from '@/components/printing/LabelPrintPreview';\nimport { PrinterManagement } from '@/components/printing/PrinterManagement';\nimport { useTranslations } from 'next-intl';\nimport { logger } from '@/lib/logger';\nimport { mockLabels, USE_MOCK } from '@/lib/mock-data';\n\n// 物料标签类型\ninterface MaterialLabel {\n id: number;\n labelNo: string;\n qrCode?: string;\n purchaseOrderNo?: string;\n supplierName?: string;\n receiveDate?: string;\n materialCode: string;\n materialName?: string;\n specification?: string;\n unit?: string;\n batchNo?: string;\n quantity: number;\n width?: number;\n lengthPerRoll?: number;\n warehouseName?: string;\n locationName?: string;\n isMainMaterial: number;\n isUsed: number;\n isCut: number;\n status: string;\n createTime?: string;\n}\n\n// 是否徽章 - 需要在组件内部使用翻译\nexport default function MaterialLabelsPage() {\n // 翻译钩子\n const t = useTranslations('Dcprint');\n const tc = useTranslations('Common');\n const { user } = useAuth();\n\n // 是否徽章\n const getYesNoBadge = (value: number) => {\n return value === 1 ? (\n <Badge className=\"bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-300\">\n {tc('yes')}\n </Badge>\n ) : (\n <Badge className=\"bg-gray-100 text-gray-700 dark:bg-gray-700 dark:text-gray-200\">\n {tc('no')}\n </Badge>\n );\n };\n\n // 状态徽章\n const getStatusBadge = (status: string) => {\n const statusMap: Record<string, { label: string; className: string }> = {\n active: {\n label: tc('normal'),\n className: 'bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-300',\n },\n frozen: {\n label: tc('frozen'),\n className: 'bg-orange-100 text-orange-700 dark:bg-orange-900/30 dark:text-orange-300',\n },\n cut: {\n label: t('cut'),\n className: 'bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-300',\n },\n disabled: {\n label: tc('disabled'),\n className: 'bg-gray-100 text-gray-700 dark:bg-gray-700 dark:text-gray-200',\n },\n };\n const config = statusMap[status] || {\n label: status,\n className: 'bg-gray-100 text-gray-700 dark:bg-gray-700 dark:text-gray-200',\n };\n return <Badge className={config.className}>{config.label}</Badge>;\n };\n\n const [loading, setLoading] = useState(true);\n const [labels, setLabels] = useState<MaterialLabel[]>([]);\n const [keyword, setKeyword] = useState('');\n const [isMainMaterial, setIsMainMaterial] = useState('all');\n const [isCut, setIsCut] = useState('all');\n const [page, setPage] = useState(1);\n const [pageSize] = useState(20);\n const [total, setTotal] = useState(0);\n const [selectedLabels, setSelectedLabels] = useState<Set<number>>(new Set());\n const [showPrinterSettings, setShowPrinterSettings] = useState(false);\n\n // 分切相关状态\n const [cuttingDialogOpen, setCuttingDialogOpen] = useState(false);\n const [selectedLabel, setSelectedLabel] = useState<MaterialLabel | null>(null);\n const [cutWidthStr, setCutWidthStr] = useState('');\n const [cuttingLoading, setCuttingLoading] = useState(false);\n const [cutRemark, setCutRemark] = useState('');\n\n useEffect(() => {\n const controller = new AbortController();\n\n const fetchData = async () => {\n try {\n setLoading(true);\n\n if (USE_MOCK) {\n logger.info({ module: 'Dcprint', action: 'fetchLabels' }, '使用 mock 标签数据');\n setLabels(mockLabels);\n setTotal(mockLabels.length);\n setLoading(false);\n return;\n }\n\n const params = new URLSearchParams();\n if (keyword) params.append('keyword', keyword);\n if (isMainMaterial && isMainMaterial !== 'all')\n params.append('isMainMaterial', isMainMaterial);\n if (isCut && isCut !== 'all') params.append('isCut', isCut);\n params.append('page', page.toString());\n params.append('pageSize', pageSize.toString());\n\n const response = await authFetch(`/api/dcprint/labels?${params}`, {\n signal: controller.signal,\n });\n\n if (!response.ok) {\n throw new Error(`API 响应错误: ${response.status}`);\n }\n\n const result = await response.json();\n\n if (result.success) {\n setLabels(result.data?.list || []);\n setTotal(result.data?.pagination?.total || 0);\n logger.info({ module: 'Dcprint', action: 'fetchLabels' }, '标签数据获取成功', {\n count: (result.data?.list || []).length,\n });\n }\n } catch (error) {\n if ((error as Error).name !== 'AbortError') {\n logger.error({ module: 'Dcprint', action: 'fetchLabels' }, '获取标签数据失败', {\n error: (error as Error).message,\n });\n }\n } finally {\n setLoading(false);\n }\n };\n\n fetchData();\n\n return () => controller.abort();\n }, [page, isMainMaterial, isCut, keyword]);\n\n const handleSearch = () => {\n setPage(1);\n };\n\n const handleReset = () => {\n setKeyword('');\n setIsMainMaterial('all');\n setIsCut('all');\n setPage(1);\n };\n\n // 打开分切对话框\n const handleOpenCutDialog = (label: MaterialLabel) => {\n setSelectedLabel(label);\n setCutWidthStr('');\n setCutRemark('');\n setCuttingDialogOpen(true);\n };\n\n // 执行分切操作\n const handleCutting = async () => {\n if (!selectedLabel || !cutWidthStr || !user) {\n toast.error(t('pleaseFillCutWidth'));\n return;\n }\n\n try {\n setCuttingLoading(true);\n\n const response = await authFetch('/api/warehouse/inbound/cutting', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n sourceLabelId: selectedLabel.id,\n cutWidthStr,\n operatorId: user.id,\n operatorName: user.realName || user.username,\n remark: cutRemark,\n }),\n });\n\n if (!response.ok) {\n throw new Error(`${t('cuttingFailed')}: ${response.status}`);\n }\n\n const result = await response.json();\n if (result.success) {\n toast.success(t('cuttingSuccess'));\n setCuttingDialogOpen(false);\n setPage((prevPage) => prevPage);\n } else {\n toast.error(result.message || t('cuttingFailed'));\n }\n } catch {\n toast.error(t('cuttingFailedRetry'));\n } finally {\n setCuttingLoading(false);\n }\n };\n\n // 转换为打印数据格式\n const toPrintData = (label: MaterialLabel): LabelData => ({\n id: String(label.id),\n labelNo: label.labelNo,\n qrCode: label.qrCode,\n materialCode: label.materialCode,\n materialName: label.materialName,\n specification: label.specification,\n batchNo: label.batchNo,\n quantity: label.quantity,\n unit: label.unit,\n warehouseName: label.warehouseName,\n });\n\n // 获取选中的打印标签\n const selectedPrintLabels = labels.filter((l) => selectedLabels.has(l.id)).map(toPrintData);\n\n return (\n <MainLayout title={t('labelManagement')}>\n <div className=\"space-y-6\">\n {/* 搜索栏 */}\n <Card>\n <CardHeader>\n <CardTitle className=\"flex items-center gap-2\">\n <QrCode className=\"h-5 w-5\" />\n {t('labelQuery')}\n </CardTitle>\n <CardDescription>{t('labelQueryDesc')}</CardDescription>\n </CardHeader>\n <CardContent>\n <div className=\"flex flex-wrap gap-4 items-end\">\n <div className=\"flex-1 min-w-[200px]\">\n <label className=\"text-sm font-medium mb-2 block\">{tc('keyword')}</label>\n <div className=\"relative\">\n <Search className=\"absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground\" />\n <Input\n placeholder={t('labelNoMaterialCodeBatchNo')}\n className=\"pl-10\"\n value={keyword}\n onChange={(e) => setKeyword(e.target.value)}\n onKeyDown={(e) => e.key === 'Enter' && handleSearch()}\n />\n </div>\n </div>\n <div className=\"w-[150px]\">\n <label className=\"text-sm font-medium mb-2 block\">{t('mainMaterial')}</label>\n <Select value={isMainMaterial} onValueChange={setIsMainMaterial}>\n <SelectTrigger>\n <SelectValue placeholder={tc('all')} />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"all\">{tc('all')}</SelectItem>\n <SelectItem value=\"1\">{tc('yes')}</SelectItem>\n <SelectItem value=\"0\">{tc('no')}</SelectItem>\n </SelectContent>\n </Select>\n </div>\n <div className=\"w-[150px]\">\n <label className=\"text-sm font-medium mb-2 block\">{t('isCut')}</label>\n <Select value={isCut} onValueChange={setIsCut}>\n <SelectTrigger>\n <SelectValue placeholder={tc('all')} />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"all\">{tc('all')}</SelectItem>\n <SelectItem value=\"1\">{tc('yes')}</SelectItem>\n <SelectItem value=\"0\">{tc('no')}</SelectItem>\n </SelectContent>\n </Select>\n </div>\n <div className=\"flex gap-2\">\n <Button variant=\"outline\" onClick={handleReset}>\n <Trash2 className=\"h-4 w-4 mr-2\" />\n {tc('reset')}\n </Button>\n <Button onClick={handleSearch}>\n <Search className=\"h-4 w-4 mr-2\" />\n {tc('search')}\n </Button>\n </div>\n </div>\n </CardContent>\n </Card>\n\n {/* 标签列表 */}\n <Card>\n <CardHeader>\n <div className=\"flex items-center justify-between\">\n <div>\n <CardTitle>{t('labelList')}</CardTitle>\n <CardDescription>{tc('totalRecords', { count: total })}</CardDescription>\n </div>\n <div className=\"flex gap-2\">\n {selectedPrintLabels.length > 0 && (\n <LabelPrintTrigger labels={selectedPrintLabels}>\n <Button>\n <Printer className=\"h-4 w-4 mr-2\" />\n {t('printSelected')} ({selectedPrintLabels.length})\n </Button>\n </LabelPrintTrigger>\n )}\n <Button variant=\"outline\" onClick={() => setShowPrinterSettings(true)}>\n <Settings className=\"h-4 w-4 mr-2\" />\n {t('printerSettings')}\n </Button>\n <Button variant=\"outline\" onClick={() => setPage((prevPage) => prevPage)}>\n <RefreshCw className=\"h-4 w-4 mr-2\" />\n {tc('refresh')}\n </Button>\n </div>\n </div>\n </CardHeader>\n <CardContent>\n <div className=\"border rounded-lg\">\n <Table>\n <TableHeader>\n <TableRow>\n <TableHead className=\"w-12\">\n <Checkbox\n checked={labels.length > 0 && selectedLabels.size === labels.length}\n onCheckedChange={(checked) => {\n if (checked) {\n setSelectedLabels(new Set(labels.map((l) => l.id)));\n } else {\n setSelectedLabels(new Set());\n }\n }}\n />\n </TableHead>\n <TableHead>{t('labelNo')}</TableHead>\n <TableHead>{t('materialInfo')}</TableHead>\n <TableHead>{tc('specification')}</TableHead>\n <TableHead>{t('widthLength')}</TableHead>\n <TableHead>{tc('batchNo')}</TableHead>\n <TableHead>{tc('warehouse')}</TableHead>\n <TableHead>{t('mainMaterial')}</TableHead>\n <TableHead>{t('isCut')}</TableHead>\n <TableHead>{t('isUsed')}</TableHead>\n <TableHead>{tc('status')}</TableHead>\n <TableHead>{tc('actions')}</TableHead>\n </TableRow>\n </TableHeader>\n <TableBody>\n {loading ? (\n <TableRow>\n <TableCell colSpan={12} className=\"text-center py-8\">\n {tc('loading')}\n </TableCell>\n </TableRow>\n ) : labels.length === 0 ? (\n <TableRow>\n <TableCell colSpan={12} className=\"text-center py-8\">\n {tc('noRecords')}\n </TableCell>\n </TableRow>\n ) : (\n labels.map((label) => (\n <TableRow key={label.id}>\n <TableCell>\n <Checkbox\n checked={selectedLabels.has(label.id)}\n onCheckedChange={(checked) => {\n const newSelected = new Set(selectedLabels);\n if (checked) {\n newSelected.add(label.id);\n } else {\n newSelected.delete(label.id);\n }\n setSelectedLabels(newSelected);\n }}\n />\n </TableCell>\n <TableCell className=\"font-medium\">{label.labelNo}</TableCell>\n <TableCell>\n <div className=\"space-y-1\">\n <div className=\"font-medium\">{label.materialName}</div>\n <div className=\"text-sm text-muted-foreground\">\n {label.materialCode}\n </div>\n </div>\n </TableCell>\n <TableCell>{label.specification}</TableCell>\n <TableCell>\n {label.width && (\n <div>\n {label.width}mm / {label.lengthPerRoll}m\n </div>\n )}\n </TableCell>\n <TableCell>{label.batchNo}</TableCell>\n <TableCell>\n <div className=\"text-sm\">\n <div>{label.warehouseName}</div>\n <div className=\"text-muted-foreground\">{label.locationName}</div>\n </div>\n </TableCell>\n <TableCell>{getYesNoBadge(label.isMainMaterial)}</TableCell>\n <TableCell>{getYesNoBadge(label.isCut)}</TableCell>\n <TableCell>{getYesNoBadge(label.isUsed)}</TableCell>\n <TableCell>{getStatusBadge(label.status)}</TableCell>\n <TableCell>\n <div className=\"flex gap-1\">\n <Dialog>\n <DialogTrigger asChild>\n <Button variant=\"ghost\" size=\"sm\">\n <QrCode className=\"h-4 w-4\" />\n </Button>\n </DialogTrigger>\n <DialogContent>\n <DialogHeader>\n <DialogTitle>{t('qrCodeInfo')}</DialogTitle>\n </DialogHeader>\n <div className=\"flex flex-col items-center gap-4 py-4\">\n <div className=\"p-4 bg-white border rounded-lg\">\n <QrCode className=\"h-32 w-32\" />\n </div>\n <code className=\"text-xs bg-muted p-2 rounded\">\n {label.qrCode}\n </code>\n </div>\n </DialogContent>\n </Dialog>\n <Button\n variant=\"ghost\"\n size=\"sm\"\n onClick={(e) => {\n e.stopPropagation();\n handleOpenCutDialog(label);\n }}\n disabled={label.isCut === 1}\n title={label.isCut === 1 ? t('cut') : t('cutting')}\n >\n <Scissors className=\"h-4 w-4\" />\n </Button>\n <LabelPrintTrigger labels={[toPrintData(label)]}>\n <Button variant=\"ghost\" size=\"sm\">\n <Printer className=\"h-4 w-4\" />\n </Button>\n </LabelPrintTrigger>\n </div>\n </TableCell>\n </TableRow>\n ))\n )}\n </TableBody>\n </Table>\n </div>\n\n {/* 分页 */}\n {total > pageSize && (\n <div className=\"flex items-center justify-between mt-4\">\n <div className=\"text-sm text-muted-foreground\">\n {t('pageInfo', { page, total: Math.ceil(total / pageSize) })}\n </div>\n <div className=\"flex gap-2\">\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={() => setPage((p) => Math.max(1, p - 1))}\n disabled={page === 1}\n >\n {tc('prevPage')}\n </Button>\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={() => setPage((p) => p + 1)}\n disabled={page * pageSize >= total}\n >\n {tc('nextPage')}\n </Button>\n </div>\n </div>\n )}\n </CardContent>\n </Card>\n </div>\n\n {/* 分切对话框 */}\n <Dialog open={cuttingDialogOpen} onOpenChange={setCuttingDialogOpen}>\n <DialogContent className=\"sm:max-w-md\">\n <DialogHeader>\n <DialogTitle>{t('materialCutting')}</DialogTitle>\n </DialogHeader>\n <div className=\"space-y-4 py-4\">\n <div>\n <div className=\"bg-muted p-3 rounded-md\">\n <div className=\"grid grid-cols-2 gap-2 text-sm\">\n <div>\n <span className=\"text-muted-foreground\">{t('labelNo')}:</span>\n <span className=\"ml-2 font-medium\">{selectedLabel?.labelNo}</span>\n </div>\n <div>\n <span className=\"text-muted-foreground\">{t('material')}:</span>\n <span className=\"ml-2 font-medium\">{selectedLabel?.materialName}</span>\n </div>\n </div>\n </div>\n </div>\n <div>\n <label className=\"text-sm font-medium mb-2 block\">{t('cutWidth')}</label>\n <Input\n placeholder={t('cutWidthPlaceholder')}\n value={cutWidthStr}\n onChange={(e) => setCutWidthStr(e.target.value)}\n className=\"w-full\"\n />\n </div>\n <div>\n <label className=\"text-sm font-medium mb-2 block\">{tc('remark')}</label>\n <Input\n placeholder={t('cuttingRemark')}\n value={cutRemark}\n onChange={(e) => setCutRemark(e.target.value)}\n className=\"w-full\"\n />\n </div>\n </div>\n <DialogFooter>\n <Button variant=\"outline\" onClick={() => setCuttingDialogOpen(false)}>\n {tc('cancel')}\n </Button>\n <Button onClick={handleCutting} loading={cuttingLoading} disabled={!cutWidthStr}>\n {t('executeCutting')}\n </Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n\n {/* 打印机设置对话框 */}\n <Dialog open={showPrinterSettings} onOpenChange={setShowPrinterSettings}>\n <DialogContent className=\"max-w-4xl\">\n <DialogHeader>\n <DialogTitle>{t('printerManagement')}</DialogTitle>\n </DialogHeader>\n <PrinterManagement />\n </DialogContent>\n </Dialog>\n </MainLayout>\n );\n}\n","usedDeprecatedRules":[]},{"filePath":"D:\\dcprint\\erp-project\\src\\app\\[locale]\\dcprint\\page.tsx","messages":[{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"物料标签管理\"。建议使用: tc('text_18vvxo')","line":14,"column":12,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":14,"endColumn":20},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"管理物料标签,支持二维码追溯\"。建议使用: tc('text_q4gmqv')","line":15,"column":18,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":15,"endColumn":34},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"标签总数: 0\"。建议使用: tc('text_d7sljy')","line":19,"column":12,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":19,"endColumn":21},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"物料分切\"。建议使用: tc('text_eurv9t')","line":22,"column":12,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":22,"endColumn":18},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"扫描标签进行分切操作\"。建议使用: tc('text_mxiu5g')","line":23,"column":18,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":23,"endColumn":30},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"今日分切: 0\"。建议使用: tc('text_u9u8z2')","line":27,"column":12,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":27,"endColumn":21},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"生产流程卡\"。建议使用: tc('text_sw72y9')","line":30,"column":12,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":30,"endColumn":19},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"生成和管理生产流程卡\"。建议使用: tc('text_g5tro1')","line":31,"column":18,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":31,"endColumn":30},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"流程卡数: 0\"。建议使用: tc('text_ym8o7l')","line":35,"column":12,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":35,"endColumn":21},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"物料追溯\"。建议使用: tc('text_ev2kde')","line":38,"column":12,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":38,"endColumn":18},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"全程二维码追溯查询\"。建议使用: tc('text_h8mb5x')","line":39,"column":18,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":39,"endColumn":29},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"追溯记录: 0\"。建议使用: tc('text_r5mxot')","line":43,"column":12,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":43,"endColumn":21},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"扫描分切\"。建议使用: tc('text_ctwbd1')","line":49,"column":12,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":49,"endColumn":18},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"生成流程卡\"。建议使用: tc('text_qg294q')","line":50,"column":12,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":50,"endColumn":19},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"追溯查询\"。建议使用: tc('text_imiq27')","line":51,"column":12,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":51,"endColumn":18},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"标签管理\"。建议使用: tc('text_dn1dss')","line":52,"column":12,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":52,"endColumn":18},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"今日刀具\"。建议使用: tc('text_ad2pzm')","line":57,"column":12,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":57,"endColumn":18},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"今日流程卡\"。建议使用: tc('text_xs0tqc')","line":58,"column":12,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":58,"endColumn":19},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"今日追溯\"。建议使用: tc('text_addfcd')","line":59,"column":12,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":59,"endColumn":18},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"标签总数\"。建议使用: tc('text_dmwn58')","line":60,"column":12,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":60,"endColumn":18},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"全程二维码追溯系统\"。建议使用: {tc('text_h8ptpo')}","line":75,"column":57,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":75,"endColumn":66},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"开始分切\"。建议使用: {tc('text_ccwslo')}","line":81,"column":58,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":83,"endColumn":19},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"生成流程卡\"。建议使用: {tc('text_qg294q')}","line":90,"column":58,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":92,"endColumn":19},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"快捷操作\"。建议使用: {tc('text_cil0yj')}","line":146,"column":24,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":146,"endColumn":28},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"系统说明\"。建议使用: {tc('text_gaqy1q')}","line":166,"column":24,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":166,"endColumn":28},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"通过二维码标签管理物料入库、出库,实现先进先出的库存管理\"。建议使用: {tc('text_r0lwcf')}","line":177,"column":64,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":179,"endColumn":19},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"物料分切\"。建议使用: {tc('text_eurv9t')}","line":187,"column":47,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":187,"endColumn":51},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"支持母材分切操作,自动生成分切后的新标签,保持追溯链完整\"。建议使用: {tc('text_kg3hvm')}","line":188,"column":64,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":190,"endColumn":19},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"生产流程卡\"。建议使用: {tc('text_sw72y9')}","line":198,"column":47,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":198,"endColumn":52},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"扫描工单和物料生成流程卡,关联主材和辅料,支持配料管理\"。建议使用: {tc('text_4gbp8t')}","line":199,"column":64,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":201,"endColumn":19},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"通过流程卡二维码追溯产品使用的所有物料信息,支持正向和反向追溯\"。建议使用: {tc('text_t6539f')}","line":210,"column":64,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":212,"endColumn":19}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":31,"fixableErrorCount":0,"fixableWarningCount":0,"source":"'use client';\n\nimport { MainLayout } from '@/components/layout';\nimport { useTranslations } from 'next-intl';\nimport { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';\nimport { Button } from '@/components/ui/button';\nimport { Badge } from '@/components/ui/badge';\nimport { QrCode, Scissors, FileText, Search, Package, ArrowRight } from 'lucide-react';\nimport Link from 'next/link';\n\n// 功能模块\nconst modules = [\n {\n title: '物料标签管理',\n description: '管理物料标签,支持二维码追溯',\n icon: QrCode,\n href: '/dcprint/labels',\n color: 'bg-blue-500',\n stats: '标签总数: 0',\n },\n {\n title: '物料分切',\n description: '扫描标签进行分切操作',\n icon: Scissors,\n href: '/warehouse/inbound/cutting',\n color: 'bg-green-500',\n stats: '今日分切: 0',\n },\n {\n title: '生产流程卡',\n description: '生成和管理生产流程卡',\n icon: FileText,\n href: '/dcprint/process-cards',\n color: 'bg-purple-500',\n stats: '流程卡数: 0',\n },\n {\n title: '物料追溯',\n description: '全程二维码追溯查询',\n icon: Search,\n href: '/dcprint/trace',\n color: 'bg-orange-500',\n stats: '追溯记录: 0',\n },\n];\n\n// 快捷操作\nconst quickActions = [\n { label: '扫描分切', icon: Scissors, href: '/warehouse/inbound/cutting' },\n { label: '生成流程卡', icon: FileText, href: '/dcprint/process-cards' },\n { label: '追溯查询', icon: Search, href: '/dcprint/trace' },\n { label: '标签管理', icon: QrCode, href: '/dcprint/labels' },\n];\n\n// 统计数据\nconst stats = [\n { label: '今日刀具', value: '0', icon: Scissors, color: 'text-green-600' },\n { label: '今日流程卡', value: '0', icon: FileText, color: 'text-purple-600' },\n { label: '今日追溯', value: '0', icon: Search, color: 'text-orange-600' },\n { label: '标签总数', value: '0', icon: QrCode, color: 'text-blue-600' },\n];\n\nexport default function DCPrintPage() {\n // 翻译钩子\n const tc = useTranslations('Common');\n\n return (\n <MainLayout title=\"全程二维码追溯系统\">\n <div className=\"space-y-6\">\n {/* 欢迎区域 */}\n <Card className=\"bg-gradient-to-r from-blue-600 to-purple-600 text-white border-0\">\n <CardContent className=\"p-8\">\n <div className=\"flex flex-col md:flex-row items-start md:items-center justify-between gap-4\">\n <div>\n <h1 className=\"text-3xl font-bold mb-2\">全程二维码追溯系统</h1>\n <p className=\"text-blue-100\">{tc('dcWelcomeSubtitle')}</p>\n </div>\n <div className=\"flex gap-3\">\n <Link href=\"/warehouse/inbound/cutting\">\n <Button variant=\"secondary\" className=\"bg-white text-blue-600 hover:bg-blue-50\">\n <Scissors className=\"h-4 w-4 mr-2\" />\n 开始分切\n </Button>\n </Link>\n <Link href=\"/dcprint/process-cards\">\n <Button\n variant=\"secondary\"\n className=\"bg-white text-purple-600 hover:bg-purple-50\"\n >\n <FileText className=\"h-4 w-4 mr-2\" />\n 生成流程卡\n </Button>\n </Link>\n </div>\n </div>\n </CardContent>\n </Card>\n\n {/* 统计数据 */}\n <div className=\"grid gap-4 md:grid-cols-2 lg:grid-cols-4\">\n {stats.map((stat) => (\n <Card key={stat.label}>\n <CardContent className=\"p-6\">\n <div className=\"flex items-center justify-between\">\n <div>\n <p className=\"text-sm text-muted-foreground\">{stat.label}</p>\n <p className={`text-3xl font-bold ${stat.color}`}>{stat.value}</p>\n </div>\n <div className={`p-3 rounded-lg bg-muted`}>\n <stat.icon className={`h-6 w-6 ${stat.color}`} />\n </div>\n </div>\n </CardContent>\n </Card>\n ))}\n </div>\n\n {/* 功能模块 */}\n <div className=\"grid gap-6 md:grid-cols-2\">\n {modules.map((module) => (\n <Link key={module.title} href={module.href}>\n <Card className=\"hover:shadow-lg transition-shadow cursor-pointer h-full\">\n <CardContent className=\"p-6\">\n <div className=\"flex items-start gap-4\">\n <div className={`p-3 rounded-lg ${module.color} text-white`}>\n <module.icon className=\"h-6 w-6\" />\n </div>\n <div className=\"flex-1\">\n <h3 className=\"text-lg font-semibold mb-1\">{module.title}</h3>\n <p className=\"text-sm text-muted-foreground mb-3\">{module.description}</p>\n <div className=\"flex items-center justify-between\">\n <Badge variant=\"secondary\">{module.stats}</Badge>\n <ArrowRight className=\"h-4 w-4 text-muted-foreground\" />\n </div>\n </div>\n </div>\n </CardContent>\n </Card>\n </Link>\n ))}\n </div>\n\n {/* 快捷操作 */}\n <Card>\n <CardHeader>\n <CardTitle>快捷操作</CardTitle>\n <CardDescription>{tc('dcQuickActionsDesc')}</CardDescription>\n </CardHeader>\n <CardContent>\n <div className=\"grid gap-4 md:grid-cols-4\">\n {quickActions.map((action) => (\n <Link key={action.label} href={action.href}>\n <Button variant=\"outline\" className=\"w-full h-20 flex flex-col gap-2\">\n <action.icon className=\"h-5 w-5\" />\n <span>{action.label}</span>\n </Button>\n </Link>\n ))}\n </div>\n </CardContent>\n </Card>\n\n {/* 系统说明 */}\n <Card>\n <CardHeader>\n <CardTitle>系统说明</CardTitle>\n <CardDescription>{tc('dcSystemDesc')}</CardDescription>\n </CardHeader>\n <CardContent>\n <div className=\"space-y-4\">\n <div className=\"flex items-start gap-3\">\n <div className=\"p-2 rounded bg-blue-100 text-blue-600\">\n <Package className=\"h-4 w-4\" />\n </div>\n <div>\n <h4 className=\"font-medium\">{tc('dcQrLabelMgmt')}</h4>\n <p className=\"text-sm text-muted-foreground\">\n 通过二维码标签管理物料入库、出库,实现先进先出的库存管理\n </p>\n </div>\n </div>\n <div className=\"flex items-start gap-3\">\n <div className=\"p-2 rounded bg-green-100 text-green-600\">\n <Scissors className=\"h-4 w-4\" />\n </div>\n <div>\n <h4 className=\"font-medium\">物料分切</h4>\n <p className=\"text-sm text-muted-foreground\">\n 支持母材分切操作,自动生成分切后的新标签,保持追溯链完整\n </p>\n </div>\n </div>\n <div className=\"flex items-start gap-3\">\n <div className=\"p-2 rounded bg-purple-100 text-purple-600\">\n <FileText className=\"h-4 w-4\" />\n </div>\n <div>\n <h4 className=\"font-medium\">生产流程卡</h4>\n <p className=\"text-sm text-muted-foreground\">\n 扫描工单和物料生成流程卡,关联主材和辅料,支持配料管理\n </p>\n </div>\n </div>\n <div className=\"flex items-start gap-3\">\n <div className=\"p-2 rounded bg-orange-100 text-orange-600\">\n <Search className=\"h-4 w-4\" />\n </div>\n <div>\n <h4 className=\"font-medium\">{tc('dcMaterialTrace')}</h4>\n <p className=\"text-sm text-muted-foreground\">\n 通过流程卡二维码追溯产品使用的所有物料信息,支持正向和反向追溯\n </p>\n </div>\n </div>\n </div>\n </CardContent>\n </Card>\n </div>\n </MainLayout>\n );\n}\n","usedDeprecatedRules":[]},{"filePath":"D:\\dcprint\\erp-project\\src\\app\\[locale]\\dcprint\\process-card\\page.tsx","messages":[{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":90,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":90,"endColumn":36},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":91,"column":16,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":91,"endColumn":23},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":92,"column":15,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":92,"endColumn":56},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":92,"column":27,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":92,"endColumn":31},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":92,"column":46,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":92,"endColumn":50},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction<SampleCard[]>`.","line":93,"column":18,"nodeType":"Identifier","messageId":"unsafeArgument","endLine":93,"endColumn":22},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":95,"column":11,"nodeType":"Property","messageId":"anyAssignment","endLine":95,"endColumn":49},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":95,"column":23,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":95,"endColumn":27},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .length on an `any` value.","line":95,"column":43,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":95,"endColumn":49},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":96,"column":11,"nodeType":"Property","messageId":"anyAssignment","endLine":96,"endColumn":71},{"ruleId":"@typescript-eslint/no-unsafe-call","severity":1,"message":"Unsafe call of an `any` typed value.","line":96,"column":18,"nodeType":"MemberExpression","messageId":"unsafeCall","endLine":96,"endColumn":29},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .filter on an `any` value.","line":96,"column":23,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":96,"endColumn":29},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .length on an `any` value.","line":96,"column":65,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":96,"endColumn":71},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":97,"column":11,"nodeType":"Property","messageId":"anyAssignment","endLine":97,"endColumn":74},{"ruleId":"@typescript-eslint/no-unsafe-call","severity":1,"message":"Unsafe call of an `any` typed value.","line":97,"column":21,"nodeType":"MemberExpression","messageId":"unsafeCall","endLine":97,"endColumn":32},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .filter on an `any` value.","line":97,"column":26,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":97,"endColumn":32},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .length on an `any` value.","line":97,"column":68,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":97,"endColumn":74},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":98,"column":11,"nodeType":"Property","messageId":"anyAssignment","endLine":98,"endColumn":75},{"ruleId":"@typescript-eslint/no-unsafe-call","severity":1,"message":"Unsafe call of an `any` typed value.","line":98,"column":22,"nodeType":"MemberExpression","messageId":"unsafeCall","endLine":98,"endColumn":33},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .filter on an `any` value.","line":98,"column":27,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":98,"endColumn":33},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .length on an `any` value.","line":98,"column":69,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":98,"endColumn":75},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":99,"column":11,"nodeType":"Property","messageId":"anyAssignment","endLine":99,"endColumn":75},{"ruleId":"@typescript-eslint/no-unsafe-call","severity":1,"message":"Unsafe call of an `any` typed value.","line":99,"column":22,"nodeType":"MemberExpression","messageId":"unsafeCall","endLine":99,"endColumn":33},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .filter on an `any` value.","line":99,"column":27,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":99,"endColumn":33},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .length on an `any` value.","line":99,"column":69,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":99,"endColumn":75},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"加载工艺卡列表失败\"。建议使用: tc('text_vfyes4')","line":103,"column":19,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":103,"endColumn":30},{"ruleId":"react-hooks/set-state-in-effect","severity":1,"message":"Error: Calling setState synchronously within an effect can trigger cascading renders\n\nEffects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following:\n* Update external systems with the latest state from React.\n* Subscribe for updates from some external system, calling setState in a callback function when external state changes.\n\nCalling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect).\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\dcprint\\process-card\\page.tsx:110:5\n 108 |\n 109 | useEffect(() => {\n> 110 | fetchCards();\n | ^^^^^^^^^^ Avoid calling setState() directly within an effect\n 111 | }, [fetchCards]);\n 112 |\n 113 | const handleSubmit = async (id: number) => {","line":110,"column":5,"nodeType":null,"endLine":110,"endColumn":15},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":116,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":116,"endColumn":36},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":117,"column":16,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":117,"endColumn":23},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"工艺卡已提交\"。建议使用: tc('text_v89pmu')","line":118,"column":23,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":118,"endColumn":31},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `string | number | bigint | boolean | ReactElement<unknown, string | JSXElementConstructor<any>> | Iterable<ReactNode> | ... 4 more ... | undefined`.","line":121,"column":21,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":121,"endColumn":43},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .message on an `any` value.","line":121,"column":26,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":121,"endColumn":33},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"操作失败\"。建议使用: tc('text_d1rj43')","line":124,"column":19,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":124,"endColumn":25},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":131,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":131,"endColumn":36},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":132,"column":16,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":132,"endColumn":23},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"工艺卡已确认\"。建议使用: tc('text_v85vc4')","line":133,"column":23,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":133,"endColumn":31},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `string | number | bigint | boolean | ReactElement<unknown, string | JSXElementConstructor<any>> | Iterable<ReactNode> | ... 4 more ... | undefined`.","line":136,"column":21,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":136,"endColumn":43},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .message on an `any` value.","line":136,"column":26,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":136,"endColumn":33},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"操作失败\"。建议使用: tc('text_d1rj43')","line":139,"column":19,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":139,"endColumn":25},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"确认作废此工艺卡?\"。建议使用: tc('text_eohzmm')","line":144,"column":18,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":144,"endColumn":29},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":147,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":147,"endColumn":36},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":148,"column":16,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":148,"endColumn":23},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"工艺卡已作废\"。建议使用: tc('text_v8d3pz')","line":149,"column":23,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":149,"endColumn":31},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `string | number | bigint | boolean | ReactElement<unknown, string | JSXElementConstructor<any>> | Iterable<ReactNode> | ... 4 more ... | undefined`.","line":152,"column":21,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":152,"endColumn":43},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .message on an `any` value.","line":152,"column":26,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":152,"endColumn":33},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"操作失败\"。建议使用: tc('text_d1rj43')","line":155,"column":19,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":155,"endColumn":25},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"确认删除此工艺卡?\"。建议使用: tc('text_n7k18j')","line":160,"column":18,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":160,"endColumn":29},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":163,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":163,"endColumn":36},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":164,"column":16,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":164,"endColumn":23},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"删除成功\"。建议使用: tc('text_azeh8z')","line":165,"column":23,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":165,"endColumn":29},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `string | number | bigint | boolean | ReactElement<unknown, string | JSXElementConstructor<any>> | Iterable<ReactNode> | ... 4 more ... | undefined`.","line":168,"column":21,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":168,"endColumn":43},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .message on an `any` value.","line":168,"column":26,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":168,"endColumn":33},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"删除失败\"。建议使用: tc('text_azdahk')","line":171,"column":19,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":171,"endColumn":25},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"全部\"。建议使用: {tc('text_en40')}","line":182,"column":62,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":182,"endColumn":64},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"草稿\"。建议使用: {tc('text_n02e')}","line":188,"column":62,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":188,"endColumn":64},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"打样中\"。建议使用: {tc('text_ewm7d')}","line":194,"column":62,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":194,"endColumn":65},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"已确认\"。建议使用: {tc('text_ecmeg')}","line":200,"column":62,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":200,"endColumn":65},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"打样编号\"。建议使用: {tc('text_cubwf9')}","line":243,"column":32,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":243,"endColumn":36},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"打样名称\"。建议使用: {tc('text_cu4sef')}","line":244,"column":32,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":244,"endColumn":36},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"客户\"。建议使用: {tc('text_g4id')}","line":245,"column":32,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":245,"endColumn":34},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"产品\"。建议使用: {tc('text_dud6')}","line":246,"column":32,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":246,"endColumn":34},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"版本\"。建议使用: {tc('text_k06c')}","line":247,"column":32,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":247,"endColumn":34},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"基材\"。建议使用: {tc('text_fj4m')}","line":248,"column":32,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":248,"endColumn":34},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"印刷色\"。建议使用: {tc('text_cmnwr')}","line":249,"column":32,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":249,"endColumn":35},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"预估工时\"。建议使用: {tc('text_jkkmvx')}","line":250,"column":32,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":250,"endColumn":36},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"总成本\"。建议使用: {tc('text_eko0n')}","line":251,"column":32,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":251,"endColumn":35},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"状态\"。建议使用: {tc('text_k1e3')}","line":252,"column":32,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":252,"endColumn":34},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"查看\"。建议使用: {tc('text_ibpi')}","line":284,"column":63,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":286,"endColumn":29},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"提交\"。建议使用: {tc('text_heqc')}","line":298,"column":68,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":300,"endColumn":33},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"确认\"。建议使用: {tc('text_l912')}","line":310,"column":90,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":312,"endColumn":33},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"作废\"。建议使用: {tc('text_e0n7')}","line":314,"column":84,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":316,"endColumn":33}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":71,"fixableErrorCount":0,"fixableWarningCount":0,"source":"'use client';\r\n\r\nimport { useState, useEffect, useCallback } from 'react';\r\nimport { useTranslations } from 'next-intl';\r\nimport { useRouter } from 'next/navigation';\r\nimport { MainLayout } from '@/components/layout/main-layout';\r\nimport { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';\r\nimport { Button } from '@/components/ui/button';\r\nimport { Input } from '@/components/ui/input';\r\nimport { PROCESS_CARD_STATUS_LABEL } from '@/lib/status-labels';\r\nimport { Badge } from '@/components/ui/badge';\r\nimport {\r\n Table,\r\n TableBody,\r\n TableCell,\r\n TableHead,\r\n TableHeader,\r\n TableRow,\r\n} from '@/components/ui/table';\r\nimport { toast } from 'sonner';\r\nimport {\r\n Plus,\r\n Search,\r\n FileText,\r\n MoreHorizontal,\r\n Eye,\r\n Edit,\r\n Trash2,\r\n CheckCircle,\r\n XCircle,\r\n Send,\r\n} from 'lucide-react';\r\nimport {\r\n DropdownMenu,\r\n DropdownMenuContent,\r\n DropdownMenuItem,\r\n DropdownMenuTrigger,\r\n} from '@/components/ui/dropdown-menu';\r\nimport { authFetch } from '@/lib/auth-fetch';\r\n\r\ninterface SampleCard {\r\n id: number;\r\n sample_no: string;\r\n sample_name: string;\r\n customer_name: string;\r\n product_name: string;\r\n version_no: string;\r\n status: number;\r\n substrate_material_name: string;\r\n print_color: string;\r\n total_cost: number;\r\n estimated_hour: number;\r\n create_time: string;\r\n}\r\n\r\nconst STATUS_MAP: Record<\r\n number,\r\n { label: string; variant: 'default' | 'secondary' | 'destructive' | 'outline' }\r\n> = {\r\n 1: { label: PROCESS_CARD_STATUS_LABEL[1], variant: 'secondary' },\r\n 2: { label: PROCESS_CARD_STATUS_LABEL[2], variant: 'default' },\r\n 3: { label: PROCESS_CARD_STATUS_LABEL[3], variant: 'outline' },\r\n 4: { label: PROCESS_CARD_STATUS_LABEL[4], variant: 'destructive' },\r\n};\r\n\r\nexport default function ProcessCardPage() {\r\n const t = useTranslations('Dcprint');\r\n const tc = useTranslations('Common');\r\n const router = useRouter();\r\n\r\n const [cards, setCards] = useState<SampleCard[]>([]);\r\n const [loading, setLoading] = useState(false);\r\n const [searchKeyword, setSearchKeyword] = useState('');\r\n const [filterStatus, setFilterStatus] = useState<string>('all');\r\n const [stats, setStats] = useState({\r\n total: 0,\r\n draft: 0,\r\n sampling: 0,\r\n confirmed: 0,\r\n cancelled: 0,\r\n });\r\n\r\n const fetchCards = useCallback(async () => {\r\n setLoading(true);\r\n try {\r\n const params = new URLSearchParams({ page: '1', pageSize: '100' });\r\n if (searchKeyword) params.append('keyword', searchKeyword);\r\n if (filterStatus !== 'all') params.append('status', filterStatus);\r\n const res = await authFetch(`/api/dcprint/sample-card?${params}`);\r\n const data = await res.json();\r\n if (data.success) {\r\n const list = data.data?.list || data.data || [];\r\n setCards(list);\r\n setStats({\r\n total: data.data?.total || list.length,\r\n draft: list.filter((c: SampleCard) => c.status === 1).length,\r\n sampling: list.filter((c: SampleCard) => c.status === 2).length,\r\n confirmed: list.filter((c: SampleCard) => c.status === 3).length,\r\n cancelled: list.filter((c: SampleCard) => c.status === 4).length,\r\n });\r\n }\r\n } catch {\r\n toast.error('加载工艺卡列表失败');\r\n } finally {\r\n setLoading(false);\r\n }\r\n }, [searchKeyword, filterStatus]);\r\n\r\n useEffect(() => {\r\n fetchCards();\r\n }, [fetchCards]);\r\n\r\n const handleSubmit = async (id: number) => {\r\n try {\r\n const res = await authFetch(`/api/dcprint/sample-card/${id}/submit`, { method: 'POST' });\r\n const data = await res.json();\r\n if (data.success) {\r\n toast.success('工艺卡已提交');\r\n fetchCards();\r\n } else {\r\n toast.error(data.message || '操作失败');\r\n }\r\n } catch {\r\n toast.error('操作失败');\r\n }\r\n };\r\n\r\n const handleConfirm = async (id: number) => {\r\n try {\r\n const res = await authFetch(`/api/dcprint/sample-card/${id}/confirm`, { method: 'POST' });\r\n const data = await res.json();\r\n if (data.success) {\r\n toast.success('工艺卡已确认');\r\n fetchCards();\r\n } else {\r\n toast.error(data.message || '操作失败');\r\n }\r\n } catch {\r\n toast.error('操作失败');\r\n }\r\n };\r\n\r\n const handleCancel = async (id: number) => {\r\n if (!confirm('确认作废此工艺卡?')) return;\r\n try {\r\n const res = await authFetch(`/api/dcprint/sample-card/${id}/cancel`, { method: 'POST' });\r\n const data = await res.json();\r\n if (data.success) {\r\n toast.success('工艺卡已作废');\r\n fetchCards();\r\n } else {\r\n toast.error(data.message || '操作失败');\r\n }\r\n } catch {\r\n toast.error('操作失败');\r\n }\r\n };\r\n\r\n const handleDelete = async (id: number) => {\r\n if (!confirm('确认删除此工艺卡?')) return;\r\n try {\r\n const res = await authFetch(`/api/dcprint/sample-card/${id}`, { method: 'DELETE' });\r\n const data = await res.json();\r\n if (data.success) {\r\n toast.success('删除成功');\r\n fetchCards();\r\n } else {\r\n toast.error(data.message || '删除失败');\r\n }\r\n } catch {\r\n toast.error('删除失败');\r\n }\r\n };\r\n\r\n return (\r\n <MainLayout title={t('processCardManagement')}>\r\n <div className=\"space-y-6\">\r\n <div className=\"grid grid-cols-4 gap-4\">\r\n <Card className=\"cursor-pointer hover:bg-muted\" onClick={() => setFilterStatus('all')}>\r\n <CardContent className=\"p-4 text-center\">\r\n <div className=\"text-2xl font-bold\">{stats.total}</div>\r\n <div className=\"text-sm text-muted-foreground\">全部</div>\r\n </CardContent>\r\n </Card>\r\n <Card className=\"cursor-pointer hover:bg-muted\" onClick={() => setFilterStatus('1')}>\r\n <CardContent className=\"p-4 text-center\">\r\n <div className=\"text-2xl font-bold text-gray-500\">{stats.draft}</div>\r\n <div className=\"text-sm text-muted-foreground\">草稿</div>\r\n </CardContent>\r\n </Card>\r\n <Card className=\"cursor-pointer hover:bg-muted\" onClick={() => setFilterStatus('2')}>\r\n <CardContent className=\"p-4 text-center\">\r\n <div className=\"text-2xl font-bold text-blue-500\">{stats.sampling}</div>\r\n <div className=\"text-sm text-muted-foreground\">打样中</div>\r\n </CardContent>\r\n </Card>\r\n <Card className=\"cursor-pointer hover:bg-muted\" onClick={() => setFilterStatus('3')}>\r\n <CardContent className=\"p-4 text-center\">\r\n <div className=\"text-2xl font-bold text-green-500\">{stats.confirmed}</div>\r\n <div className=\"text-sm text-muted-foreground\">已确认</div>\r\n </CardContent>\r\n </Card>\r\n </div>\r\n\r\n <Card>\r\n <CardHeader>\r\n <CardTitle className=\"flex items-center gap-2\">\r\n <FileText className=\"h-5 w-5\" />\r\n {t('processCardList')}\r\n </CardTitle>\r\n </CardHeader>\r\n <CardContent>\r\n <div className=\"flex items-center gap-4 mb-4\">\r\n <div className=\"flex-1 max-w-sm\">\r\n <Input\r\n placeholder={tc('search')}\r\n value={searchKeyword}\r\n onChange={(e) => setSearchKeyword(e.target.value)}\r\n onKeyDown={(e) => e.key === 'Enter' && fetchCards()}\r\n />\r\n </div>\r\n <Button onClick={() => fetchCards()} variant=\"outline\">\r\n <Search className=\"h-4 w-4 mr-2\" />\r\n {tc('search')}\r\n </Button>\r\n <Button onClick={() => router.push('/dcprint/process-card/new')}>\r\n <Plus className=\"h-4 w-4 mr-2\" />\r\n {tc('add')}\r\n </Button>\r\n </div>\r\n\r\n {loading ? (\r\n <div className=\"text-center py-8 text-muted-foreground\">{tc('loading')}</div>\r\n ) : cards.length === 0 ? (\r\n <div className=\"text-center py-8 text-muted-foreground\">\r\n <FileText className=\"h-12 w-12 mx-auto mb-2 opacity-50\" />\r\n <p>{tc('noData')}</p>\r\n </div>\r\n ) : (\r\n <Table>\r\n <TableHeader>\r\n <TableRow>\r\n <TableHead>打样编号</TableHead>\r\n <TableHead>打样名称</TableHead>\r\n <TableHead>客户</TableHead>\r\n <TableHead>产品</TableHead>\r\n <TableHead>版本</TableHead>\r\n <TableHead>基材</TableHead>\r\n <TableHead>印刷色</TableHead>\r\n <TableHead>预估工时</TableHead>\r\n <TableHead>总成本</TableHead>\r\n <TableHead>状态</TableHead>\r\n <TableHead>{tc('actions')}</TableHead>\r\n </TableRow>\r\n </TableHeader>\r\n <TableBody>\r\n {cards.map((card) => (\r\n <TableRow key={card.id}>\r\n <TableCell className=\"font-mono\">{card.sample_no}</TableCell>\r\n <TableCell>{card.sample_name}</TableCell>\r\n <TableCell>{card.customer_name || '-'}</TableCell>\r\n <TableCell>{card.product_name || '-'}</TableCell>\r\n <TableCell>{card.version_no}</TableCell>\r\n <TableCell>{card.substrate_material_name || '-'}</TableCell>\r\n <TableCell>{card.print_color || '-'}</TableCell>\r\n <TableCell>{card.estimated_hour ? `${card.estimated_hour}h` : '-'}</TableCell>\r\n <TableCell>¥{card.total_cost?.toFixed(2) || '0.00'}</TableCell>\r\n <TableCell>\r\n <Badge variant={STATUS_MAP[card.status]?.variant || 'secondary'}>\r\n {STATUS_MAP[card.status]?.label || '未知'}\r\n </Badge>\r\n </TableCell>\r\n <TableCell>\r\n <DropdownMenu>\r\n <DropdownMenuTrigger asChild>\r\n <Button variant=\"ghost\" size=\"sm\">\r\n <MoreHorizontal className=\"h-4 w-4\" />\r\n </Button>\r\n </DropdownMenuTrigger>\r\n <DropdownMenuContent>\r\n <DropdownMenuItem\r\n onClick={() => router.push(`/dcprint/process-card/${card.id}`)}\r\n >\r\n <Eye className=\"h-4 w-4 mr-2\" />\r\n 查看\r\n </DropdownMenuItem>\r\n {card.status === 1 && (\r\n <>\r\n <DropdownMenuItem\r\n onClick={() =>\r\n router.push(`/dcprint/process-card/${card.id}/edit`)\r\n }\r\n >\r\n <Edit className=\"h-4 w-4 mr-2\" />\r\n {tc('edit')}\r\n </DropdownMenuItem>\r\n <DropdownMenuItem onClick={() => handleSubmit(card.id)}>\r\n <Send className=\"h-4 w-4 mr-2\" />\r\n 提交\r\n </DropdownMenuItem>\r\n <DropdownMenuItem onClick={() => handleDelete(card.id)}>\r\n <Trash2 className=\"h-4 w-4 mr-2 text-red-500\" />\r\n {tc('delete')}\r\n </DropdownMenuItem>\r\n </>\r\n )}\r\n {card.status === 2 && (\r\n <>\r\n <DropdownMenuItem onClick={() => handleConfirm(card.id)}>\r\n <CheckCircle className=\"h-4 w-4 mr-2 text-green-500\" />\r\n 确认\r\n </DropdownMenuItem>\r\n <DropdownMenuItem onClick={() => handleCancel(card.id)}>\r\n <XCircle className=\"h-4 w-4 mr-2 text-red-500\" />\r\n 作废\r\n </DropdownMenuItem>\r\n </>\r\n )}\r\n </DropdownMenuContent>\r\n </DropdownMenu>\r\n </TableCell>\r\n </TableRow>\r\n ))}\r\n </TableBody>\r\n </Table>\r\n )}\r\n </CardContent>\r\n </Card>\r\n </div>\r\n </MainLayout>\r\n );\r\n}\r\n","usedDeprecatedRules":[]},{"filePath":"D:\\dcprint\\erp-project\\src\\app\\[locale]\\dcprint\\process-cards\\page.tsx","messages":[{"ruleId":"react-hooks/immutability","severity":1,"message":"Error: Cannot access variable before it is declared\n\n`fetchCards` is accessed before it is declared, which prevents the earlier access from updating when this value changes over time.\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\dcprint\\process-cards\\page.tsx:94:5\n 92 |\n 93 | useEffect(() => {\n> 94 | fetchCards();\n | ^^^^^^^^^^ `fetchCards` accessed before it is declared\n 95 | qrInputRef.current?.focus();\n 96 | }, []);\n 97 |\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\dcprint\\process-cards\\page.tsx:98:3\n 96 | }, []);\n 97 |\n> 98 | const fetchCards = async () => {\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n> 99 | try {\n | ^^^^^^^^^\n> 100 | if (USE_MOCK) {\n …\n | ^^^^^^^^^\n> 118 | }\n | ^^^^^^^^^\n> 119 | };\n | ^^^^^ `fetchCards` is declared here\n 120 |\n 121 | const handleScanQRCode = async () => {\n 122 | if (!qrCode.trim()) return;","line":94,"column":5,"nodeType":null,"endLine":94,"endColumn":15},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"使用 mock 流程卡数据\"。建议使用: tc('text_eo5kx6')","line":101,"column":73,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":101,"endColumn":88},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":107,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":107,"endColumn":43},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":108,"column":18,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":108,"endColumn":25},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction<ProcessCard[]>`.","line":109,"column":18,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":109,"endColumn":40},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":109,"column":25,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":109,"endColumn":29},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"流程卡数据获取成功\"。建议使用: tc('text_3w3hvn')","line":110,"column":73,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":110,"endColumn":84},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":111,"column":11,"nodeType":"Property","messageId":"anyAssignment","endLine":111,"endColumn":49},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":111,"column":26,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":111,"endColumn":30},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .length on an `any` value.","line":111,"column":43,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":111,"endColumn":49},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"获取流程卡数据失败\"。建议使用: tc('text_oco9nu')","line":115,"column":72,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":115,"endColumn":83},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":137,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":137,"endColumn":43},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":139,"column":18,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":139,"endColumn":25},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":140,"column":15,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":140,"endColumn":38},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":140,"column":29,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":140,"endColumn":33},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":141,"column":15,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":141,"endColumn":38},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":141,"column":29,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":141,"endColumn":33},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction<WorkOrder | null>`.","line":145,"column":26,"nodeType":"Identifier","messageId":"unsafeArgument","endLine":145,"endColumn":30},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .isMainMaterial on an `any` value.","line":153,"column":22,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":153,"endColumn":36},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction<MaterialLabel | null>`.","line":154,"column":31,"nodeType":"Identifier","messageId":"unsafeArgument","endLine":154,"endColumn":35},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction<WorkOrder | null>`.","line":162,"column":26,"nodeType":"Identifier","messageId":"unsafeArgument","endLine":162,"endColumn":30},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .id on an `any` value.","line":172,"column":63,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":172,"endColumn":65},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":174,"column":48,"nodeType":"Property","messageId":"anyAssignment","endLine":174,"endColumn":71},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .materialName on an `any` value.","line":174,"column":59,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":174,"endColumn":71},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction<WorkOrder | null>`.","line":180,"column":26,"nodeType":"Identifier","messageId":"unsafeArgument","endLine":180,"endColumn":30},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction<ProcessCard | null>`.","line":187,"column":30,"nodeType":"Identifier","messageId":"unsafeArgument","endLine":187,"endColumn":34},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction<string>`.","line":192,"column":18,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":192,"endColumn":52},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .message on an `any` value.","line":192,"column":25,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":192,"endColumn":32},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":230,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":230,"endColumn":43},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":232,"column":18,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":232,"endColumn":25},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction<string>`.","line":237,"column":18,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":237,"endColumn":55},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .message on an `any` value.","line":237,"column":25,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":237,"endColumn":32}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":32,"fixableErrorCount":0,"fixableWarningCount":0,"source":"'use client';\n\nimport { authFetch } from '@/lib/auth-fetch';\nimport { useState, useRef, useEffect } from 'react';\nimport { MainLayout } from '@/components/layout';\nimport { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';\nimport { Badge } from '@/components/ui/badge';\nimport {\n Table,\n TableBody,\n TableCell,\n TableHead,\n TableHeader,\n TableRow,\n} from '@/components/ui/table';\nimport { Button } from '@/components/ui/button';\nimport { Input } from '@/components/ui/input';\nimport { Alert, AlertDescription } from '@/components/ui/alert';\nimport {\n QrCode,\n FileText,\n Printer,\n CheckCircle,\n AlertCircle,\n Plus,\n Trash2,\n RefreshCw,\n} from 'lucide-react';\nimport { useTranslations } from 'next-intl';\nimport { logger } from '@/lib/logger';\nimport { mockProcessCards, USE_MOCK } from '@/lib/mock-data';\n\n// 流程卡类型\ninterface ProcessCard {\n id: number;\n cardNo: string;\n workOrderNo: string;\n productCode?: string;\n productName?: string;\n materialSpec?: string;\n planQty?: number;\n mainLabelNo?: string;\n mainMaterialCode?: string;\n mainMaterialName?: string;\n mainBatchNo?: string;\n burdeningStatus: string;\n lockStatus: string;\n createUserName?: string;\n createTime?: string;\n}\n\n// 工单类型\ninterface WorkOrder {\n id: number;\n orderNo: string;\n productCode?: string;\n productName?: string;\n specification?: string;\n quantity?: number;\n}\n\n// 物料标签类型\ninterface MaterialLabel {\n id: number;\n labelNo: string;\n materialCode: string;\n materialName?: string;\n specification?: string;\n batchNo?: string;\n isMainMaterial: number;\n}\n\nexport default function ProcessCardsPage() {\n // 翻译钩子\n const t = useTranslations('Dcprint');\n const tc = useTranslations('Common');\n\n const [qrCode, setQrCode] = useState('');\n const [scanState, setScanState] = useState<'workOrder' | 'mainMaterial' | 'auxiliary'>(\n 'workOrder'\n );\n const [workOrder, setWorkOrder] = useState<WorkOrder | null>(null);\n const [mainMaterial, setMainMaterial] = useState<MaterialLabel | null>(null);\n const [auxiliaryMaterials, setAuxiliaryMaterials] = useState<MaterialLabel[]>([]);\n const [cards, setCards] = useState<ProcessCard[]>([]);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState('');\n const [success, setSuccess] = useState('');\n const [_showDetail, _setShowDetail] = useState(false);\n const [_selectedCard, _setSelectedCard] = useState<ProcessCard | null>(null);\n const qrInputRef = useRef<HTMLInputElement>(null);\n\n useEffect(() => {\n fetchCards();\n qrInputRef.current?.focus();\n }, []);\n\n const fetchCards = async () => {\n try {\n if (USE_MOCK) {\n logger.info({ module: 'Dcprint', action: 'fetchProcessCards' }, '使用 mock 流程卡数据');\n setCards(mockProcessCards);\n return;\n }\n\n const response = await authFetch('/api/dcprint/process-cards');\n const result = await response.json();\n if (result.success) {\n setCards(result.data.list || []);\n logger.info({ module: 'Dcprint', action: 'fetchProcessCards' }, '流程卡数据获取成功', {\n count: (result.data.list || []).length,\n });\n }\n } catch (error) {\n logger.error({ module: 'Dcprint', action: 'fetchProcessCards' }, '获取流程卡数据失败', {\n error: (error as Error).message,\n });\n }\n };\n\n const handleScanQRCode = async () => {\n if (!qrCode.trim()) return;\n\n setError('');\n setLoading(true);\n\n try {\n const response = await authFetch('/api/dcprint/scan', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n qrContent: qrCode,\n scanType: 'process',\n }),\n });\n\n const result = await response.json();\n\n if (result.success) {\n const type = result.data.type;\n const data = result.data.data;\n\n if (scanState === 'workOrder') {\n if (type === '3') {\n setWorkOrder(data);\n setScanState('mainMaterial');\n setSuccess(t('workOrderScanSuccess'));\n } else {\n setError(t('pleaseScanWorkOrderFirst'));\n }\n } else if (scanState === 'mainMaterial') {\n if (type === '0' || type === '1' || type === '2') {\n if (data.isMainMaterial === 1) {\n setMainMaterial(data);\n setScanState('auxiliary');\n setSuccess(t('mainMaterialScanSuccess'));\n } else {\n setError(t('notMainMaterialLabel'));\n }\n } else if (type === '3') {\n // 切换工单\n setWorkOrder(data);\n setMainMaterial(null);\n setAuxiliaryMaterials([]);\n setSuccess(t('workOrderSwitchSuccess'));\n } else {\n setError(t('pleaseScanMaterialLabel'));\n }\n } else if (scanState === 'auxiliary') {\n if (type === '0' || type === '1' || type === '2') {\n // 添加辅料\n if (!auxiliaryMaterials.find((m) => m.id === data.id)) {\n setAuxiliaryMaterials([...auxiliaryMaterials, data]);\n setSuccess(t('auxiliaryAdded', { name: data.materialName }));\n } else {\n setError(t('auxiliaryAlreadyAdded'));\n }\n } else if (type === '3') {\n // 切换工单\n setWorkOrder(data);\n setMainMaterial(null);\n setAuxiliaryMaterials([]);\n setScanState('mainMaterial');\n setSuccess(t('workOrderSwitchSuccess'));\n } else if (type === '4') {\n // 查看流程卡详情\n _setSelectedCard(data);\n _setShowDetail(true);\n }\n }\n } else {\n setError(result.message || tc('scanFailed'));\n }\n } catch {\n setError(t('scanQueryFailed'));\n } finally {\n setLoading(false);\n setQrCode('');\n qrInputRef.current?.focus();\n }\n };\n\n const handleGenerateCard = async () => {\n if (!workOrder || !mainMaterial) {\n setError(t('pleaseScanWorkOrderAndMain'));\n return;\n }\n\n setLoading(true);\n setError('');\n\n try {\n const response = await authFetch('/api/dcprint/process-cards', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n workOrderId: workOrder.id,\n workOrderNo: workOrder.orderNo,\n productCode: workOrder.productCode,\n productName: workOrder.productName,\n materialSpec: workOrder.specification,\n planQty: workOrder.quantity,\n mainLabelId: mainMaterial.id,\n mainLabelNo: mainMaterial.labelNo,\n createUserId: 1,\n createUserName: t('operator'),\n }),\n });\n\n const result = await response.json();\n\n if (result.success) {\n setSuccess(t('cardGeneratedSuccess'));\n fetchCards();\n handleReset();\n } else {\n setError(result.message || t('generateFailed'));\n }\n } catch {\n setError(t('generateCardFailed'));\n } finally {\n setLoading(false);\n }\n };\n\n const handleReset = () => {\n setQrCode('');\n setScanState('workOrder');\n setWorkOrder(null);\n setMainMaterial(null);\n setAuxiliaryMaterials([]);\n setError('');\n setSuccess('');\n qrInputRef.current?.focus();\n };\n\n const removeAuxiliaryMaterial = (id: number) => {\n setAuxiliaryMaterials(auxiliaryMaterials.filter((m) => m.id !== id));\n };\n\n const getStatusBadge = (status: string) => {\n const statusMap: Record<string, { label: string; className: string }> = {\n pending: {\n label: t('notBurdened'),\n className: 'bg-yellow-100 text-yellow-700 dark:bg-yellow-900/30 dark:text-yellow-300',\n },\n completed: {\n label: t('burdened'),\n className: 'bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-300',\n },\n };\n const config = statusMap[status] || {\n label: status,\n className: 'bg-gray-100 text-gray-700 dark:bg-gray-700 dark:text-gray-200',\n };\n return <Badge className={config.className}>{config.label}</Badge>;\n };\n\n const getLockBadge = (lockStatus: string) => {\n return lockStatus ? (\n <Badge className=\"bg-red-100 text-red-700 dark:bg-red-900/30 dark:text-red-300\">\n {t('locked')}\n </Badge>\n ) : (\n <Badge className=\"bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-300\">\n {t('unlocked')}\n </Badge>\n );\n };\n\n return (\n <MainLayout title={t('processCard')}>\n <div className=\"space-y-6\">\n {/* 扫码生成区域 */}\n <Card>\n <CardHeader>\n <CardTitle className=\"flex items-center gap-2\">\n <QrCode className=\"h-5 w-5\" />\n {t('scanGenerateCard')}\n </CardTitle>\n <CardDescription>{t('scanSequenceDesc')}</CardDescription>\n </CardHeader>\n <CardContent>\n <div className=\"space-y-4\">\n {/* 扫码步骤指示 */}\n <div className=\"flex items-center gap-4 mb-4\">\n <div\n className={`flex items-center gap-2 px-4 py-2 rounded-lg ${\n scanState === 'workOrder' ? 'bg-blue-100 text-blue-700' : 'bg-gray-100'\n }`}\n >\n <FileText className=\"h-4 w-4\" />\n <span className=\"font-medium\">1. {t('scanWorkOrder')}</span>\n </div>\n <div className=\"text-muted-foreground\">→</div>\n <div\n className={`flex items-center gap-2 px-4 py-2 rounded-lg ${\n scanState === 'mainMaterial'\n ? 'bg-blue-100 text-blue-700'\n : scanState === 'auxiliary'\n ? 'bg-green-100 text-green-700'\n : 'bg-gray-100'\n }`}\n >\n <QrCode className=\"h-4 w-4\" />\n <span className=\"font-medium\">2. {t('scanMainMaterial')}</span>\n </div>\n <div className=\"text-muted-foreground\">→</div>\n <div\n className={`flex items-center gap-2 px-4 py-2 rounded-lg ${\n scanState === 'auxiliary' ? 'bg-blue-100 text-blue-700' : 'bg-gray-100'\n }`}\n >\n <Plus className=\"h-4 w-4\" />\n <span className=\"font-medium\">3. {t('addAuxiliaryOptional')}</span>\n </div>\n </div>\n\n {/* 二维码输入 */}\n <div className=\"flex gap-4\">\n <div className=\"flex-1\">\n <Input\n ref={qrInputRef}\n placeholder={t('pleaseScanQR')}\n value={qrCode}\n onChange={(e) => setQrCode(e.target.value)}\n onKeyDown={(e) => e.key === 'Enter' && handleScanQRCode()}\n disabled={loading}\n />\n </div>\n <Button variant=\"outline\" onClick={handleReset}>\n <RefreshCw className=\"h-4 w-4 mr-2\" />\n {tc('reset')}\n </Button>\n <Button\n onClick={handleGenerateCard}\n disabled={!workOrder || !mainMaterial || loading}\n >\n <FileText className=\"h-4 w-4 mr-2\" />\n {t('generateCard')}\n </Button>\n </div>\n\n {/* 提示信息 */}\n {error && (\n <Alert variant=\"destructive\">\n <AlertCircle className=\"h-4 w-4\" />\n <AlertDescription>{error}</AlertDescription>\n </Alert>\n )}\n {success && (\n <Alert className=\"bg-green-50 border-green-200\">\n <CheckCircle className=\"h-4 w-4 text-green-600\" />\n <AlertDescription className=\"text-green-700\">{success}</AlertDescription>\n </Alert>\n )}\n\n {/* 已扫描信息 */}\n <div className=\"grid grid-cols-1 md:grid-cols-2 gap-4\">\n {/* 工单信息 */}\n <Card className={workOrder ? 'border-blue-200' : ''}>\n <CardHeader className=\"pb-3\">\n <CardTitle className=\"text-sm\">{t('workOrderInfo')}</CardTitle>\n </CardHeader>\n <CardContent>\n {workOrder ? (\n <div className=\"space-y-2\">\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">{t('workOrderNo')}</span>\n <span className=\"font-medium\">{workOrder.orderNo}</span>\n </div>\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">{t('productCode')}</span>\n <span className=\"font-medium\">{workOrder.productCode}</span>\n </div>\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">{t('productName')}</span>\n <span className=\"font-medium\">{workOrder.productName}</span>\n </div>\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">{t('productionQty')}</span>\n <span className=\"font-medium\">{workOrder.quantity}</span>\n </div>\n </div>\n ) : (\n <div className=\"text-muted-foreground text-center py-4\">\n {t('pleaseScanWorkOrder')}\n </div>\n )}\n </CardContent>\n </Card>\n\n {/* 主材信息 */}\n <Card className={mainMaterial ? 'border-green-200' : ''}>\n <CardHeader className=\"pb-3\">\n <CardTitle className=\"text-sm\">{t('mainMaterialInfo')}</CardTitle>\n </CardHeader>\n <CardContent>\n {mainMaterial ? (\n <div className=\"space-y-2\">\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">{t('labelNo')}</span>\n <span className=\"font-medium\">{mainMaterial.labelNo}</span>\n </div>\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">{t('materialCode')}</span>\n <span className=\"font-medium\">{mainMaterial.materialCode}</span>\n </div>\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">{t('materialName')}</span>\n <span className=\"font-medium\">{mainMaterial.materialName}</span>\n </div>\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">{tc('batchNo')}</span>\n <span className=\"font-medium\">{mainMaterial.batchNo}</span>\n </div>\n </div>\n ) : (\n <div className=\"text-muted-foreground text-center py-4\">\n {t('pleaseScanMainMaterial')}\n </div>\n )}\n </CardContent>\n </Card>\n </div>\n\n {/* 辅料列表 */}\n {auxiliaryMaterials.length > 0 && (\n <Card>\n <CardHeader className=\"pb-3\">\n <CardTitle className=\"text-sm\">\n {t('addedAuxiliary')} ({auxiliaryMaterials.length})\n </CardTitle>\n </CardHeader>\n <CardContent>\n <div className=\"space-y-2\">\n {auxiliaryMaterials.map((material) => (\n <div\n key={material.id}\n className=\"flex items-center justify-between p-2 bg-muted rounded\"\n >\n <div className=\"flex items-center gap-4\">\n <span className=\"font-medium\">{material.materialName}</span>\n <span className=\"text-sm text-muted-foreground\">\n {material.materialCode}\n </span>\n <span className=\"text-sm text-muted-foreground\">\n {tc('batchNo')}: {material.batchNo}\n </span>\n </div>\n <Button\n variant=\"ghost\"\n size=\"sm\"\n onClick={() => removeAuxiliaryMaterial(material.id)}\n >\n <Trash2 className=\"h-4 w-4 text-red-500\" />\n </Button>\n </div>\n ))}\n </div>\n </CardContent>\n </Card>\n )}\n </div>\n </CardContent>\n </Card>\n\n {/* 流程卡列表 */}\n <Card>\n <CardHeader>\n <CardTitle>{t('cardList')}</CardTitle>\n <CardDescription>{t('generatedCards')}</CardDescription>\n </CardHeader>\n <CardContent>\n <div className=\"border rounded-lg\">\n <Table>\n <TableHeader>\n <TableRow>\n <TableHead>{t('cardNo')}</TableHead>\n <TableHead>{t('workOrderNo')}</TableHead>\n <TableHead>{t('productCode')}</TableHead>\n <TableHead>{t('mainLabel')}</TableHead>\n <TableHead>{t('burdeningStatus')}</TableHead>\n <TableHead>{t('lockStatus')}</TableHead>\n <TableHead>{tc('createdBy')}</TableHead>\n <TableHead>{tc('createdAt')}</TableHead>\n <TableHead>{tc('actions')}</TableHead>\n </TableRow>\n </TableHeader>\n <TableBody>\n {cards.length === 0 ? (\n <TableRow>\n <TableCell colSpan={9} className=\"text-center py-8\">\n {tc('noRecords')}\n </TableCell>\n </TableRow>\n ) : (\n cards.map((card) => (\n <TableRow key={card.id}>\n <TableCell className=\"font-medium\">{card.cardNo}</TableCell>\n <TableCell>{card.workOrderNo}</TableCell>\n <TableCell>{card.productCode}</TableCell>\n <TableCell>{card.mainLabelNo}</TableCell>\n <TableCell>{getStatusBadge(card.burdeningStatus)}</TableCell>\n <TableCell>{getLockBadge(card.lockStatus)}</TableCell>\n <TableCell>{card.createUserName}</TableCell>\n <TableCell>{card.createTime}</TableCell>\n <TableCell>\n <div className=\"flex gap-1\">\n <Button variant=\"ghost\" size=\"sm\">\n <Printer className=\"h-4 w-4\" />\n </Button>\n </div>\n </TableCell>\n </TableRow>\n ))\n )}\n </TableBody>\n </Table>\n </div>\n </CardContent>\n </Card>\n </div>\n </MainLayout>\n );\n}\n","usedDeprecatedRules":[]},{"filePath":"D:\\dcprint\\erp-project\\src\\app\\[locale]\\dcprint\\screen-plate\\page.tsx","messages":[{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"丝网版\"。建议使用: tc('text_c267o')","line":81,"column":6,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":81,"endColumn":11},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"胶印版\"。建议使用: tc('text_jatse')","line":82,"column":6,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":82,"endColumn":11},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"柔版\"。建议使用: tc('text_iad0')","line":83,"column":6,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":83,"endColumn":10},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"凹版\"。建议使用: tc('text_ekj3')","line":84,"column":6,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":84,"endColumn":10},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"新制\"。建议使用: tc('text_hqx2')","line":90,"column":15,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":90,"endColumn":19},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"可用\"。建议使用: tc('text_ex3t')","line":91,"column":15,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":91,"endColumn":19},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"已曝光\"。建议使用: tc('text_e9bb2')","line":92,"column":15,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":92,"endColumn":20},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"生产中\"。建议使用: tc('text_hjdtx')","line":93,"column":15,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":93,"endColumn":20},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"清洗中\"。建议使用: tc('text_gn457')","line":94,"column":15,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":94,"endColumn":20},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"已再生\"。建议使用: tc('text_e5vvo')","line":95,"column":15,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":95,"endColumn":20},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"损坏\"。建议使用: tc('text_hdqo')","line":96,"column":15,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":96,"endColumn":19},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"已报废\"。建议使用: tc('text_e8o3w')","line":97,"column":15,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":97,"endColumn":20},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"创建\"。建议使用: tc('text_ehj3')","line":100,"column":12,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":100,"endColumn":16},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"曝光\"。建议使用: tc('text_hxxo')","line":101,"column":12,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":101,"endColumn":16},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"印刷\"。建议使用: tc('text_en5z')","line":102,"column":12,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":102,"endColumn":16},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"清洗\"。建议使用: tc('text_jb8y')","line":103,"column":12,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":103,"endColumn":16},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"再生\"。建议使用: tc('text_eiia')","line":104,"column":14,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":104,"endColumn":18},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"报废\"。建议使用: tc('text_haqi')","line":105,"column":13,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":105,"endColumn":17},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"张力调整\"。建议使用: tc('text_ccpaq4')","line":106,"column":20,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":106,"endColumn":26},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":135,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":135,"endColumn":38},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":136,"column":18,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":136,"endColumn":25},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction<ScreenPlate[]>`.","line":137,"column":17,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":137,"endColumn":39},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":137,"column":24,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":137,"endColumn":28},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction<number>`.","line":138,"column":18,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":138,"endColumn":40},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":138,"column":25,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":138,"endColumn":29},{"ruleId":"react-hooks/set-state-in-effect","severity":1,"message":"Error: Calling setState synchronously within an effect can trigger cascading renders\n\nEffects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following:\n* Update external systems with the latest state from React.\n* Subscribe for updates from some external system, calling setState in a callback function when external state changes.\n\nCalling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect).\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\dcprint\\screen-plate\\page.tsx:144:5\n 142 |\n 143 | useEffect(() => {\n> 144 | fetchData();\n | ^^^^^^^^^ Avoid calling setState() directly within an effect\n 145 | }, [page]);\n 146 |\n 147 | const fetchHistory = async (plateId: number) => {","line":144,"column":5,"nodeType":null,"endLine":144,"endColumn":14},{"ruleId":"react-hooks/exhaustive-deps","severity":1,"message":"React Hook useEffect has a missing dependency: 'fetchData'. Either include it or remove the dependency array.","line":145,"column":6,"nodeType":"ArrayExpression","endLine":145,"endColumn":12,"suggestions":[{"desc":"Update the dependencies array to be: [fetchData, page]","fix":{"range":[4128,4134],"text":"[fetchData, page]"}}]},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":150,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":150,"endColumn":38},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":151,"column":18,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":151,"endColumn":25},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction<HistoryRecord[]>`.","line":152,"column":24,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":152,"endColumn":41},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":152,"column":31,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":152,"endColumn":35},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":165,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":165,"endColumn":38},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":166,"column":18,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":166,"endColumn":25},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"失败\"。建议使用: tc('text_fy1g')","line":171,"column":24,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":171,"endColumn":28},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":171,"column":30,"nodeType":"Property","messageId":"anyAssignment","endLine":171,"endColumn":57},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .message on an `any` value.","line":171,"column":50,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":171,"endColumn":57},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"失败\"。建议使用: tc('text_fy1g')","line":174,"column":22,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":174,"endColumn":26},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"确定删除?\"。建议使用: tc('text_efhx5d')","line":179,"column":18,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":179,"endColumn":25},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":182,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":182,"endColumn":38},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":183,"column":18,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":183,"endColumn":25},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"删除成功\"。建议使用: tc('text_azeh8z')","line":184,"column":24,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":184,"endColumn":30},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"失败\"。建议使用: tc('text_fy1g')","line":188,"column":22,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":188,"endColumn":26},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"请选择操作类型\"。建议使用: tc('text_wuj0ye')","line":200,"column":22,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":200,"endColumn":31},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":216,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":216,"endColumn":38},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":217,"column":18,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":217,"endColumn":25},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"记录成功\"。建议使用: tc('text_i0pgc4')","line":218,"column":24,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":218,"endColumn":30},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"失败\"。建议使用: tc('text_fy1g')","line":226,"column":24,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":226,"endColumn":28},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":226,"column":30,"nodeType":"Property","messageId":"anyAssignment","endLine":226,"endColumn":57},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .message on an `any` value.","line":226,"column":50,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":226,"endColumn":57},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"失败\"。建议使用: tc('text_fy1g')","line":229,"column":22,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":229,"endColumn":26},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"网版管理\"。建议使用: {tc('text_gjfp3w')}","line":237,"column":46,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":237,"endColumn":50},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"新增网版\"。建议使用: {tc('text_d7bmo5')}","line":270,"column":48,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":272,"endColumn":13},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"网版编码\"。建议使用: {tc('text_gjgb2a')}","line":281,"column":50,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":281,"endColumn":54},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"网版名称\"。建议使用: {tc('text_gj8zx6')}","line":282,"column":50,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":282,"endColumn":54},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"目数\"。建议使用: {tc('text_ksaq')}","line":284,"column":50,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":284,"endColumn":52},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"暂无记录\"。建议使用: {tc('text_dd1mmb')}","line":363,"column":88,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":365,"endColumn":21},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"共\"。建议使用: {tc('text_g35')}","line":374,"column":51,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":374,"endColumn":52},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"条\"。建议使用: {tc('text_kf5')}","line":374,"column":59,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":374,"endColumn":60},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"上一页\"。建议使用: {tc('text_btlof')}","line":381,"column":14,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":383,"endColumn":13},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"下一页\"。建议使用: {tc('text_btmf4')}","line":389,"column":14,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":391,"endColumn":13},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"网版编码\"。建议使用: {tc('text_gjgb2a')}","line":402,"column":24,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":402,"endColumn":28},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"网版名称\"。建议使用: {tc('text_gj8zx6')}","line":409,"column":24,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":409,"endColumn":28},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"丝网版\"。建议使用: {tc('text_c267o')}","line":425,"column":43,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":425,"endColumn":46},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"胶印版\"。建议使用: {tc('text_jatse')}","line":426,"column":43,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":426,"endColumn":46},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"柔版\"。建议使用: {tc('text_iad0')}","line":427,"column":43,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":427,"endColumn":45},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"凹版\"。建议使用: {tc('text_ekj3')}","line":428,"column":43,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":428,"endColumn":45},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"目数\"。建议使用: {tc('text_ksaq')}","line":433,"column":24,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":433,"endColumn":26},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"丝网材质\"。建议使用: {tc('text_adu9bg')}","line":440,"column":24,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":440,"endColumn":28},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"框类型\"。建议使用: {tc('text_fvhh2')}","line":454,"column":24,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":454,"endColumn":27},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"张力值(N/cm)\"。建议使用: {tc('text_tt1rt5')}","line":461,"column":24,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":461,"endColumn":33},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"最大使用次数\"。建议使用: {tc('text_cxnt0h')}","line":471,"column":24,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":471,"endColumn":30},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"存放位置\"。建议使用: {tc('text_bxzajr')}","line":499,"column":24,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":499,"endColumn":28},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"取消\"。建议使用: {tc('text_ev02')}","line":514,"column":78,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":516,"endColumn":15},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"网版生命周期记录\"。建议使用: {tc('text_p82rm7')}","line":525,"column":28,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":525,"endColumn":36},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"历史记录\"。建议使用: {tc('text_aw7utt')}","line":529,"column":43,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":529,"endColumn":47},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"操作类型\"。建议使用: {tc('text_d1x8m7')}","line":536,"column":54,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":536,"endColumn":58},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"操作人\"。建议使用: {tc('text_f5g8b')}","line":540,"column":54,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":540,"endColumn":57},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"暂无记录\"。建议使用: {tc('text_dd1mmb')}","line":563,"column":91,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":565,"endColumn":25},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"操作类型\"。建议使用: {tc('text_d1x8m7')}","line":574,"column":28,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":574,"endColumn":32},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"提交记录\"。建议使用: {tc('text_cxej5l')}","line":609,"column":58,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":611,"endColumn":19}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":80,"fixableErrorCount":0,"fixableWarningCount":0,"source":"'use client';\n\nimport { authFetch } from '@/lib/auth-fetch';\nimport { useEffect, useState } from 'react';\nimport { MainLayout } from '@/components/layout';\nimport { Card, CardContent } from '@/components/ui/card';\nimport { Button } from '@/components/ui/button';\nimport { Input } from '@/components/ui/input';\nimport { Badge } from '@/components/ui/badge';\nimport {\n Table,\n TableBody,\n TableCell,\n TableHead,\n TableHeader,\n TableRow,\n} from '@/components/ui/table';\nimport {\n Dialog,\n DialogContent,\n DialogHeader,\n DialogTitle,\n DialogFooter,\n} from '@/components/ui/dialog';\nimport { Label } from '@/components/ui/label';\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@/components/ui/select';\nimport { Textarea } from '@/components/ui/textarea';\nimport { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';\nimport { Plus, Search, Edit, Trash2, History, Activity } from 'lucide-react';\nimport { useToast } from '@/hooks/use-toast';\nimport { useTranslations } from 'next-intl';\n\ninterface ScreenPlate {\n id: number;\n plate_code: string;\n plate_name: string;\n plate_type: number;\n mesh_count: string;\n mesh_material: string;\n size: string;\n tension_value: number;\n frame_type: string;\n customer_id: number;\n customer_name: string;\n max_use_count: number;\n life_count: number;\n remaining_count: number;\n reclaim_count: number;\n status: number;\n warehouse_name: string;\n location_name: string;\n storage_location: string;\n scrap_reason: string;\n exposure_date: string;\n last_used_date: string;\n last_clean_date: string;\n last_reclaim_date: string;\n tension_date: string;\n remark: string;\n create_time: string;\n}\n\ninterface HistoryRecord {\n id: number;\n screen_plate_id: number;\n action: string;\n tension_value: number;\n life_increment: number;\n remark: string;\n operator_name: string;\n created_at: string;\n}\n\nconst typeMap: Record<number, string> = {\n 1: '丝网版',\n 2: '胶印版',\n 3: '柔版',\n 4: '凹版',\n};\nconst statusMap: Record<\n number,\n { label: string; variant: 'default' | 'secondary' | 'destructive' | 'outline' }\n> = {\n 1: { label: '新制', variant: 'default' },\n 2: { label: '可用', variant: 'default' },\n 3: { label: '已曝光', variant: 'secondary' },\n 4: { label: '生产中', variant: 'default' },\n 5: { label: '清洗中', variant: 'outline' },\n 6: { label: '已再生', variant: 'secondary' },\n 7: { label: '损坏', variant: 'destructive' },\n 8: { label: '已报废', variant: 'destructive' },\n};\nconst actionMap: Record<string, string> = {\n Created: '创建',\n Exposed: '曝光',\n Printed: '印刷',\n Cleaned: '清洗',\n Reclaimed: '再生',\n Scrapped: '报废',\n TensionAdjusted: '张力调整',\n};\n\nexport default function ScreenPlatePage() {\n // 翻译钩子\n const tc = useTranslations('Common');\n\n const { toast } = useToast();\n const [list, setList] = useState<ScreenPlate[]>([]);\n const [total, setTotal] = useState(0);\n const [page, setPage] = useState(1);\n const [searchCode, setSearchCode] = useState('');\n const [searchStatus, setSearchStatus] = useState('');\n const [showDialog, setShowDialog] = useState(false);\n const [showHistoryDialog, setShowHistoryDialog] = useState(false);\n const [editItem, setEditItem] = useState<Partial<ScreenPlate>>({});\n const [historyList, setHistoryList] = useState<HistoryRecord[]>([]);\n const [currentPlateId, setCurrentPlateId] = useState<number | null>(null);\n const [lifeAction, setLifeAction] = useState('');\n const [lifeRemark, setLifeRemark] = useState('');\n const [lifeTension, setLifeTension] = useState('');\n const [lifeIncrement, setLifeIncrement] = useState('');\n\n const fetchData = async () => {\n try {\n const params = new URLSearchParams({ page: String(page), pageSize: '20' });\n if (searchCode) params.set('plateCode', searchCode);\n if (searchStatus) params.set('status', searchStatus);\n const res = await authFetch('/api/screen-plates?' + params);\n const result = await res.json();\n if (result.success) {\n setList(result.data.list || []);\n setTotal(result.data.total || 0);\n }\n } catch {}\n };\n\n useEffect(() => {\n fetchData();\n }, [page]);\n\n const fetchHistory = async (plateId: number) => {\n try {\n const res = await authFetch(`/api/screen-plates/history?plateId=${plateId}`);\n const result = await res.json();\n if (result.success) {\n setHistoryList(result.data || []);\n }\n } catch {}\n };\n\n const handleSave = async () => {\n try {\n const method = editItem.id ? 'PUT' : 'POST';\n const res = await authFetch('/api/screen-plates', {\n method,\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ ...editItem, operatorName: '系统' }),\n });\n const result = await res.json();\n if (result.success) {\n toast({ title: editItem.id ? '更新成功' : '创建成功' });\n setShowDialog(false);\n fetchData();\n } else {\n toast({ title: '失败', description: result.message, variant: 'destructive' });\n }\n } catch {\n toast({ title: '失败', variant: 'destructive' });\n }\n };\n\n const handleDelete = async (id: number) => {\n if (!confirm('确定删除?')) return;\n try {\n const res = await authFetch('/api/screen-plates?id=' + id, { method: 'DELETE' });\n const result = await res.json();\n if (result.success) {\n toast({ title: '删除成功' });\n fetchData();\n }\n } catch {\n toast({ title: '失败', variant: 'destructive' });\n }\n };\n\n const handleViewHistory = async (id: number) => {\n setCurrentPlateId(id);\n await fetchHistory(id);\n setShowHistoryDialog(true);\n };\n\n const handleAddLifeRecord = async () => {\n if (!currentPlateId || !lifeAction) {\n toast({ title: '请选择操作类型', variant: 'destructive' });\n return;\n }\n try {\n const res = await authFetch('/api/screen-plates/history', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n plateId: currentPlateId,\n action: lifeAction,\n tensionValue: lifeTension ? parseFloat(lifeTension) : undefined,\n lifeIncrement: lifeIncrement ? parseInt(lifeIncrement) : 0,\n remark: lifeRemark,\n operatorName: '系统',\n }),\n });\n const result = await res.json();\n if (result.success) {\n toast({ title: '记录成功' });\n setLifeAction('');\n setLifeRemark('');\n setLifeTension('');\n setLifeIncrement('');\n fetchHistory(currentPlateId);\n fetchData();\n } else {\n toast({ title: '失败', description: result.message, variant: 'destructive' });\n }\n } catch {\n toast({ title: '失败', variant: 'destructive' });\n }\n };\n\n return (\n <MainLayout>\n <div className=\"p-6 space-y-6\">\n <div className=\"flex items-center justify-between\">\n <h1 className=\"text-2xl font-bold\">网版管理</h1>\n <div className=\"flex gap-2\">\n <div className=\"flex items-center gap-2\">\n <Input\n placeholder={tc('code')}\n value={searchCode}\n onChange={(e) => setSearchCode(e.target.value)}\n className=\"w-28 h-8 text-sm\"\n />\n <Select value={searchStatus} onValueChange={setSearchStatus}>\n <SelectTrigger className=\"w-24 h-8 text-sm\">\n <SelectValue placeholder={tc('status')} />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"\">{tc('all')}</SelectItem>\n {Object.entries(statusMap).map(([k, v]) => (\n <SelectItem key={k} value={k}>\n {v.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n <Button size=\"sm\" variant=\"outline\" onClick={fetchData}>\n <Search className=\"h-3 w-3\" />\n </Button>\n </div>\n <Button\n size=\"sm\"\n onClick={() => {\n setEditItem({});\n setShowDialog(true);\n }}\n >\n <Plus className=\"h-3 w-3 mr-1\" />\n 新增网版\n </Button>\n </div>\n </div>\n\n <Card>\n <CardContent className=\"p-0\">\n <Table>\n <TableHeader>\n <TableRow>\n <TableHead className=\"text-xs\">网版编码</TableHead>\n <TableHead className=\"text-xs\">网版名称</TableHead>\n <TableHead className=\"text-xs\">{tc('type')}</TableHead>\n <TableHead className=\"text-xs\">目数</TableHead>\n <TableHead className=\"text-xs\">{tc('size')}</TableHead>\n <TableHead className=\"text-xs\">{tc('customer')}</TableHead>\n <TableHead className=\"text-xs\">{tc('dcLifeCountHead')}</TableHead>\n <TableHead className=\"text-xs\">{tc('dcReclaimCountHead')}</TableHead>\n <TableHead className=\"text-xs\">{tc('dcTensionHead')}</TableHead>\n <TableHead className=\"text-xs\">{tc('status')}</TableHead>\n <TableHead className=\"text-xs\">{tc('actions')}</TableHead>\n </TableRow>\n </TableHeader>\n <TableBody>\n {list.map((item) => {\n const st = statusMap[item.status] || statusMap[1];\n const warn = item.remaining_count <= item.max_use_count * 0.2;\n return (\n <TableRow key={item.id}>\n <TableCell className=\"text-xs font-mono\">{item.plate_code}</TableCell>\n <TableCell className=\"text-xs\">{item.plate_name}</TableCell>\n <TableCell className=\"text-xs\">{typeMap[item.plate_type] || '-'}</TableCell>\n <TableCell className=\"text-xs\">{item.mesh_count || '-'}</TableCell>\n <TableCell className=\"text-xs\">{item.size || '-'}</TableCell>\n <TableCell className=\"text-xs\">{item.customer_name || '-'}</TableCell>\n <TableCell className=\"text-xs\">\n {warn ? (\n <span className=\"text-red-500 font-bold\">\n {item.life_count}/{item.max_use_count}\n </span>\n ) : (\n `${item.life_count}/${item.max_use_count}`\n )}\n </TableCell>\n <TableCell className=\"text-xs\">{item.reclaim_count || 0}</TableCell>\n <TableCell className=\"text-xs\">\n {item.tension_value ? `${item.tension_value} N/cm` : '-'}\n </TableCell>\n <TableCell>\n <Badge variant={st.variant} className=\"text-xs\">\n {st.label}\n </Badge>\n </TableCell>\n <TableCell>\n <div className=\"flex gap-1\">\n <Button\n size=\"sm\"\n variant=\"ghost\"\n className=\"h-6 w-6 p-0\"\n onClick={() => {\n setEditItem(item);\n setShowDialog(true);\n }}\n title={tc('edit')}\n >\n <Edit className=\"h-3 w-3\" />\n </Button>\n <Button\n size=\"sm\"\n variant=\"ghost\"\n className=\"h-6 w-6 p-0\"\n onClick={() => handleViewHistory(item.id)}\n title=\"生命周期\"\n >\n <History className=\"h-3 w-3\" />\n </Button>\n <Button\n size=\"sm\"\n variant=\"ghost\"\n className=\"h-6 w-6 p-0 text-red-600\"\n onClick={() => handleDelete(item.id)}\n title={tc('delete')}\n >\n <Trash2 className=\"h-3 w-3\" />\n </Button>\n </div>\n </TableCell>\n </TableRow>\n );\n })}\n {list.length === 0 && (\n <TableRow>\n <TableCell colSpan={11} className=\"text-center text-gray-400 py-8\">\n 暂无记录\n </TableCell>\n </TableRow>\n )}\n </TableBody>\n </Table>\n </CardContent>\n </Card>\n\n <div className=\"flex items-center justify-between\">\n <span className=\"text-sm text-gray-500\">共{total}条</span>\n <div className=\"flex gap-2\">\n <Button\n size=\"sm\"\n variant=\"outline\"\n disabled={page <= 1}\n onClick={() => setPage((p) => p - 1)}\n >\n 上一页\n </Button>\n <Button\n size=\"sm\"\n variant=\"outline\"\n disabled={page * 20 >= total}\n onClick={() => setPage((p) => p + 1)}\n >\n 下一页\n </Button>\n </div>\n </div>\n\n <Dialog open={showDialog} onOpenChange={setShowDialog}>\n <DialogContent className=\"max-w-2xl\" resizable>\n <DialogHeader>\n <DialogTitle>{editItem.id ? '编辑网版' : '新增网版'}</DialogTitle>\n </DialogHeader>\n <div className=\"grid grid-cols-2 gap-4\">\n <div>\n <Label>网版编码</Label>\n <Input\n value={editItem.plate_code || ''}\n onChange={(e) => setEditItem({ ...editItem, plate_code: e.target.value })}\n />\n </div>\n <div>\n <Label>网版名称</Label>\n <Input\n value={editItem.plate_name || ''}\n onChange={(e) => setEditItem({ ...editItem, plate_name: e.target.value })}\n />\n </div>\n <div>\n <Label>{tc('type')}</Label>\n <Select\n value={String(editItem.plate_type || 1)}\n onValueChange={(v) => setEditItem({ ...editItem, plate_type: Number(v) })}\n >\n <SelectTrigger>\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"1\">丝网版</SelectItem>\n <SelectItem value=\"2\">胶印版</SelectItem>\n <SelectItem value=\"3\">柔版</SelectItem>\n <SelectItem value=\"4\">凹版</SelectItem>\n </SelectContent>\n </Select>\n </div>\n <div>\n <Label>目数</Label>\n <Input\n value={editItem.mesh_count || ''}\n onChange={(e) => setEditItem({ ...editItem, mesh_count: e.target.value })}\n />\n </div>\n <div>\n <Label>丝网材质</Label>\n <Input\n value={editItem.mesh_material || ''}\n onChange={(e) => setEditItem({ ...editItem, mesh_material: e.target.value })}\n />\n </div>\n <div>\n <Label>{tc('size')}</Label>\n <Input\n value={editItem.size || ''}\n onChange={(e) => setEditItem({ ...editItem, size: e.target.value })}\n />\n </div>\n <div>\n <Label>框类型</Label>\n <Input\n value={editItem.frame_type || ''}\n onChange={(e) => setEditItem({ ...editItem, frame_type: e.target.value })}\n />\n </div>\n <div>\n <Label>张力值(N/cm)</Label>\n <Input\n type=\"number\"\n value={editItem.tension_value ?? ''}\n onChange={(e) =>\n setEditItem({ ...editItem, tension_value: Number(e.target.value) })\n }\n />\n </div>\n <div>\n <Label>最大使用次数</Label>\n <Input\n type=\"number\"\n value={editItem.max_use_count ?? ''}\n onChange={(e) =>\n setEditItem({ ...editItem, max_use_count: Number(e.target.value) })\n }\n />\n </div>\n <div>\n <Label>{tc('status')}</Label>\n <Select\n value={String(editItem.status ?? 1)}\n onValueChange={(v) => setEditItem({ ...editItem, status: Number(v) })}\n >\n <SelectTrigger>\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {Object.entries(statusMap).map(([k, v]) => (\n <SelectItem key={k} value={k}>\n {v.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n <div className=\"col-span-2\">\n <Label>存放位置</Label>\n <Input\n value={editItem.storage_location || ''}\n onChange={(e) => setEditItem({ ...editItem, storage_location: e.target.value })}\n />\n </div>\n <div className=\"col-span-2\">\n <Label>{tc('remark')}</Label>\n <Textarea\n value={editItem.remark || ''}\n onChange={(e) => setEditItem({ ...editItem, remark: e.target.value })}\n />\n </div>\n </div>\n <DialogFooter>\n <Button variant=\"outline\" onClick={() => setShowDialog(false)}>\n 取消\n </Button>\n <Button onClick={handleSave}>{tc('save')}</Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n\n <Dialog open={showHistoryDialog} onOpenChange={setShowHistoryDialog}>\n <DialogContent className=\"max-w-4xl\" resizable>\n <DialogHeader>\n <DialogTitle>网版生命周期记录</DialogTitle>\n </DialogHeader>\n <Tabs defaultValue=\"list\">\n <TabsList>\n <TabsTrigger value=\"list\">历史记录</TabsTrigger>\n <TabsTrigger value=\"add\">{tc('dcAddRecordTab')}</TabsTrigger>\n </TabsList>\n <TabsContent value=\"list\">\n <Table>\n <TableHeader>\n <TableRow>\n <TableHead className=\"text-xs\">操作类型</TableHead>\n <TableHead className=\"text-xs\">{tc('dcTensionHead2')}</TableHead>\n <TableHead className=\"text-xs\">{tc('dcLifeIncHead')}</TableHead>\n <TableHead className=\"text-xs\">{tc('remark')}</TableHead>\n <TableHead className=\"text-xs\">操作人</TableHead>\n <TableHead className=\"text-xs\">{tc('time')}</TableHead>\n </TableRow>\n </TableHeader>\n <TableBody>\n {historyList.map((h) => (\n <TableRow key={h.id}>\n <TableCell className=\"text-xs\">\n <Badge variant=\"outline\">{actionMap[h.action] || h.action}</Badge>\n </TableCell>\n <TableCell className=\"text-xs\">\n {h.tension_value ? `${h.tension_value} N/cm` : '-'}\n </TableCell>\n <TableCell className=\"text-xs\">\n {h.life_increment > 0 ? h.life_increment : '-'}\n </TableCell>\n <TableCell className=\"text-xs\">{h.remark || '-'}</TableCell>\n <TableCell className=\"text-xs\">{h.operator_name || '-'}</TableCell>\n <TableCell className=\"text-xs\">{h.created_at}</TableCell>\n </TableRow>\n ))}\n {historyList.length === 0 && (\n <TableRow>\n <TableCell colSpan={6} className=\"text-center text-gray-400 py-8\">\n 暂无记录\n </TableCell>\n </TableRow>\n )}\n </TableBody>\n </Table>\n </TabsContent>\n <TabsContent value=\"add\">\n <div className=\"space-y-4\">\n <div>\n <Label>操作类型</Label>\n <Select value={lifeAction} onValueChange={setLifeAction}>\n <SelectTrigger>\n <SelectValue placeholder={tc('pleaseSelect')} />\n </SelectTrigger>\n <SelectContent>\n {Object.entries(actionMap).map(([k, v]) => (\n <SelectItem key={k} value={k}>\n {v}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n <div>\n <Label>{tc('dcTensionLabel')}</Label>\n <Input\n type=\"number\"\n value={lifeTension}\n onChange={(e) => setLifeTension(e.target.value)}\n />\n </div>\n <div>\n <Label>{tc('dcLifeIncLabel')}</Label>\n <Input\n type=\"number\"\n value={lifeIncrement}\n onChange={(e) => setLifeIncrement(e.target.value)}\n />\n </div>\n <div>\n <Label>{tc('remark')}</Label>\n <Textarea value={lifeRemark} onChange={(e) => setLifeRemark(e.target.value)} />\n </div>\n <Button onClick={handleAddLifeRecord}>\n <Activity className=\"h-3 w-3 mr-1\" />\n 提交记录\n </Button>\n </div>\n </TabsContent>\n </Tabs>\n </DialogContent>\n </Dialog>\n </div>\n </MainLayout>\n );\n}\n","usedDeprecatedRules":[]},{"filePath":"D:\\dcprint\\erp-project\\src\\app\\[locale]\\dcprint\\tool-manage\\page.tsx","messages":[{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":117,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":117,"endColumn":36},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":118,"column":16,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":118,"endColumn":23},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction<Tool[]>`.","line":119,"column":18,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":119,"endColumn":52},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":119,"column":23,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":119,"endColumn":27},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":119,"column":42,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":119,"endColumn":46},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"加载工装列表失败\"。建议使用: tc('text_ryok2q')","line":122,"column":19,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":122,"endColumn":29},{"ruleId":"react-hooks/set-state-in-effect","severity":1,"message":"Error: Calling setState synchronously within an effect can trigger cascading renders\n\nEffects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following:\n* Update external systems with the latest state from React.\n* Subscribe for updates from some external system, calling setState in a callback function when external state changes.\n\nCalling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect).\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\dcprint\\tool-manage\\page.tsx:129:5\n 127 |\n 128 | useEffect(() => {\n> 129 | fetchTools();\n | ^^^^^^^^^^ Avoid calling setState() directly within an effect\n 130 | }, [fetchTools]);\n 131 |\n 132 | const handleSave = async () => {","line":129,"column":5,"nodeType":null,"endLine":129,"endColumn":15},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"工装编号和名称不能为空\"。建议使用: tc('text_2zlxnm')","line":134,"column":21,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":134,"endColumn":34},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":145,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":145,"endColumn":36},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":146,"column":16,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":146,"endColumn":23},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `string | number | bigint | boolean | ReactElement<unknown, string | JSXElementConstructor<any>> | Iterable<ReactNode> | ... 4 more ... | undefined`.","line":152,"column":21,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":152,"endColumn":43},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .message on an `any` value.","line":152,"column":26,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":152,"endColumn":33},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"操作失败\"。建议使用: tc('text_d1rj43')","line":155,"column":19,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":155,"endColumn":25},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":162,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":162,"endColumn":36},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":163,"column":16,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":163,"endColumn":23},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"工装已启用\"。建议使用: tc('text_tm5cjv')","line":164,"column":23,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":164,"endColumn":30},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `string | number | bigint | boolean | ReactElement<unknown, string | JSXElementConstructor<any>> | Iterable<ReactNode> | ... 4 more ... | undefined`.","line":167,"column":21,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":167,"endColumn":43},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .message on an `any` value.","line":167,"column":26,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":167,"endColumn":33},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"操作失败\"。建议使用: tc('text_d1rj43')","line":170,"column":19,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":170,"endColumn":25},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"确认报废此工装?\"。建议使用: tc('text_cvmmgl')","line":175,"column":18,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":175,"endColumn":28},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":182,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":182,"endColumn":36},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":183,"column":16,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":183,"endColumn":23},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"工装已报废\"。建议使用: tc('text_tm7ong')","line":184,"column":23,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":184,"endColumn":30},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `string | number | bigint | boolean | ReactElement<unknown, string | JSXElementConstructor<any>> | Iterable<ReactNode> | ... 4 more ... | undefined`.","line":187,"column":21,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":187,"endColumn":43},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .message on an `any` value.","line":187,"column":26,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":187,"endColumn":33},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"操作失败\"。建议使用: tc('text_d1rj43')","line":190,"column":19,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":190,"endColumn":25},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"全部类型\"。建议使用: {tc('text_avglbk')}","line":226,"column":43,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":226,"endColumn":47},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"刀模\"。建议使用: {tc('text_ej35')}","line":227,"column":41,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":227,"endColumn":43},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"网版\"。建议使用: {tc('text_ma6v')}","line":228,"column":41,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":228,"endColumn":43},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"全部状态\"。建议使用: {tc('text_avez63')}","line":236,"column":43,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":236,"endColumn":47},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"待用\"。建议使用: {tc('text_gw1v')}","line":237,"column":41,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":237,"endColumn":43},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"在用\"。建议使用: {tc('text_fgu8')}","line":238,"column":41,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":238,"endColumn":43},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"维修\"。建议使用: {tc('text_m16i')}","line":239,"column":41,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":239,"endColumn":43},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"预警\"。建议使用: {tc('text_qpgi')}","line":240,"column":41,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":240,"endColumn":43},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"报废\"。建议使用: {tc('text_haqi')}","line":241,"column":41,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":241,"endColumn":43},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"工装编号\"。建议使用: {tc('text_cezh29')}","line":282,"column":32,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":282,"endColumn":36},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"工装名称\"。建议使用: {tc('text_cesd1f')}","line":283,"column":32,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":283,"endColumn":36},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"类型\"。建议使用: {tc('text_lnjk')}","line":284,"column":32,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":284,"endColumn":34},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"规格\"。建议使用: {tc('text_o06w')}","line":285,"column":32,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":285,"endColumn":34},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"额定寿命\"。建议使用: {tc('text_jmtn0b')}","line":286,"column":32,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":286,"endColumn":36},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"已用/剩余\"。建议使用: {tc('text_qyhqa1')}","line":287,"column":32,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":287,"endColumn":37},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"单次成本\"。建议使用: {tc('text_ayjs08')}","line":288,"column":32,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":288,"endColumn":36},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"状态\"。建议使用: {tc('text_k1e3')}","line":289,"column":32,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":289,"endColumn":34},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"寿命状态\"。建议使用: {tc('text_bynfmx')}","line":290,"column":32,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":290,"endColumn":36},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"次\"。建议使用: {tc('text_l5t')}","line":305,"column":53,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":305,"endColumn":54},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"启用\"。建议使用: {tc('text_eymx')}","line":354,"column":75,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":356,"endColumn":33},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"报废\"。建议使用: {tc('text_haqi')}","line":360,"column":84,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":362,"endColumn":33},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"填写工装基础信息\"。建议使用: {tc('text_ea7yv6')}","line":381,"column":32,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":381,"endColumn":40},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"工装类型\"。建议使用: {tc('text_cez1tc')}","line":385,"column":22,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":386,"endColumn":22},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"刀模\"。建议使用: {tc('text_ej35')}","line":396,"column":41,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":396,"endColumn":43},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"网版\"。建议使用: {tc('text_ma6v')}","line":397,"column":41,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":397,"endColumn":43},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"工装编号\"。建议使用: {tc('text_cezh29')}","line":402,"column":22,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":403,"endColumn":22},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"工装名称\"。建议使用: {tc('text_cesd1f')}","line":411,"column":22,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":412,"endColumn":22},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"规格\"。建议使用: {tc('text_o06w')}","line":420,"column":22,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":420,"endColumn":24},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"额定寿命(次)\"。建议使用: {tc('text_mjgakr')}","line":427,"column":22,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":427,"endColumn":29},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"原始成本\"。建议使用: {tc('text_axbm7c')}","line":435,"column":22,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":435,"endColumn":26},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"目数\"。建议使用: {tc('text_ksaq')}","line":446,"column":26,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":446,"endColumn":28},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"网材\"。建议使用: {tc('text_m80v')}","line":453,"column":26,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":453,"endColumn":28},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"尺寸\"。建议使用: {tc('text_g6wu')}","line":460,"column":26,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":460,"endColumn":28},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"张力值\"。建议使用: {tc('text_ec2zl')}","line":467,"column":26,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":467,"endColumn":29}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":60,"fixableErrorCount":0,"fixableWarningCount":0,"source":"'use client';\r\n\r\nimport { useState, useEffect, useCallback } from 'react';\r\nimport { TOOL_TYPE_LABEL, TOOL_STATUS_LABEL } from '@/lib/status-labels';\r\nimport { useTranslations } from 'next-intl';\r\nimport { MainLayout } from '@/components/layout/main-layout';\r\nimport { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';\r\nimport { Button } from '@/components/ui/button';\r\nimport { Input } from '@/components/ui/input';\r\nimport { Label } from '@/components/ui/label';\r\nimport { Badge } from '@/components/ui/badge';\r\nimport {\r\n Table,\r\n TableBody,\r\n TableCell,\r\n TableHead,\r\n TableHeader,\r\n TableRow,\r\n} from '@/components/ui/table';\r\nimport {\r\n Dialog,\r\n DialogContent,\r\n DialogHeader,\r\n DialogTitle,\r\n DialogDescription,\r\n} from '@/components/ui/dialog';\r\nimport {\r\n Select,\r\n SelectContent,\r\n SelectItem,\r\n SelectTrigger,\r\n SelectValue,\r\n} from '@/components/ui/select';\r\nimport { toast } from 'sonner';\r\nimport {\r\n Plus,\r\n Search,\r\n Wrench,\r\n MoreHorizontal,\r\n Edit,\r\n AlertTriangle,\r\n CheckCircle,\r\n XCircle,\r\n} from 'lucide-react';\r\nimport {\r\n DropdownMenu,\r\n DropdownMenuContent,\r\n DropdownMenuItem,\r\n DropdownMenuTrigger,\r\n} from '@/components/ui/dropdown-menu';\r\nimport { authFetch } from '@/lib/auth-fetch';\r\n\r\ninterface Tool {\r\n id: number;\r\n tool_type: number;\r\n tool_code: string;\r\n tool_name: string;\r\n spec: string;\r\n total_life: number;\r\n used_count: number;\r\n remain_life: number;\r\n original_cost: number;\r\n unit_cost: number;\r\n net_value: number;\r\n status: number;\r\n mesh_count: string;\r\n mesh_material: string;\r\n size: string;\r\n tension_value: number;\r\n create_time: string;\r\n}\r\n\r\nconst TOOL_TYPE_MAP = TOOL_TYPE_LABEL;\r\nconst STATUS_MAP: Record<\r\n number,\r\n { label: string; variant: 'default' | 'secondary' | 'destructive' | 'outline' }\r\n> = {\r\n 1: { label: TOOL_STATUS_LABEL[1], variant: 'secondary' },\r\n 2: { label: TOOL_STATUS_LABEL[2], variant: 'default' },\r\n 3: { label: TOOL_STATUS_LABEL[3], variant: 'outline' },\r\n 4: { label: TOOL_STATUS_LABEL[4], variant: 'destructive' },\r\n 5: { label: TOOL_STATUS_LABEL[5], variant: 'destructive' },\r\n};\r\n\r\nexport default function ToolManagePage() {\r\n const t = useTranslations('Dcprint');\r\n const tc = useTranslations('Common');\r\n\r\n const [tools, setTools] = useState<Tool[]>([]);\r\n const [loading, setLoading] = useState(false);\r\n const [searchKeyword, setSearchKeyword] = useState('');\r\n const [filterType, setFilterType] = useState<string>('all');\r\n const [filterStatus, setFilterStatus] = useState<string>('all');\r\n const [isDialogOpen, setIsDialogOpen] = useState(false);\r\n const [editingTool, setEditingTool] = useState<Tool | null>(null);\r\n const [form, setForm] = useState({\r\n tool_type: 1,\r\n tool_code: '',\r\n tool_name: '',\r\n spec: '',\r\n total_life: 0,\r\n original_cost: 0,\r\n mesh_count: '',\r\n mesh_material: '',\r\n size: '',\r\n tension_value: 0,\r\n });\r\n\r\n const fetchTools = useCallback(async () => {\r\n setLoading(true);\r\n try {\r\n const params = new URLSearchParams({ page: '1', pageSize: '200' });\r\n if (searchKeyword) params.append('keyword', searchKeyword);\r\n if (filterType !== 'all') params.append('toolType', filterType);\r\n if (filterStatus !== 'all') params.append('status', filterStatus);\r\n const res = await authFetch(`/api/dcprint/tool?${params}`);\r\n const data = await res.json();\r\n if (data.success) {\r\n setTools(data.data?.list || data.data || []);\r\n }\r\n } catch {\r\n toast.error('加载工装列表失败');\r\n } finally {\r\n setLoading(false);\r\n }\r\n }, [searchKeyword, filterType, filterStatus]);\r\n\r\n useEffect(() => {\r\n fetchTools();\r\n }, [fetchTools]);\r\n\r\n const handleSave = async () => {\r\n if (!form.tool_code || !form.tool_name) {\r\n toast.warning('工装编号和名称不能为空');\r\n return;\r\n }\r\n try {\r\n const url = editingTool ? `/api/dcprint/tool/${editingTool.id}` : '/api/dcprint/tool';\r\n const method = editingTool ? 'PUT' : 'POST';\r\n const res = await authFetch(url, {\r\n method,\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify(form),\r\n });\r\n const data = await res.json();\r\n if (data.success) {\r\n toast.success(editingTool ? '更新成功' : '创建成功');\r\n setIsDialogOpen(false);\r\n setEditingTool(null);\r\n fetchTools();\r\n } else {\r\n toast.error(data.message || '操作失败');\r\n }\r\n } catch {\r\n toast.error('操作失败');\r\n }\r\n };\r\n\r\n const handleActivate = async (id: number) => {\r\n try {\r\n const res = await authFetch(`/api/dcprint/tool/${id}/activate`, { method: 'POST' });\r\n const data = await res.json();\r\n if (data.success) {\r\n toast.success('工装已启用');\r\n fetchTools();\r\n } else {\r\n toast.error(data.message || '操作失败');\r\n }\r\n } catch {\r\n toast.error('操作失败');\r\n }\r\n };\r\n\r\n const handleScrap = async (id: number) => {\r\n if (!confirm('确认报废此工装?')) return;\r\n try {\r\n const res = await authFetch(`/api/dcprint/tool/${id}/scrap`, {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify({ reason: '手动报废' }),\r\n });\r\n const data = await res.json();\r\n if (data.success) {\r\n toast.success('工装已报废');\r\n fetchTools();\r\n } else {\r\n toast.error(data.message || '操作失败');\r\n }\r\n } catch {\r\n toast.error('操作失败');\r\n }\r\n };\r\n\r\n const getLifeStatus = (tool: Tool) => {\r\n const remainPercent = tool.total_life > 0 ? (tool.remain_life / tool.total_life) * 100 : 0;\r\n if (remainPercent <= 5) return { color: 'text-red-500', label: '红色预警' };\r\n if (remainPercent <= 20) return { color: 'text-yellow-500', label: '黄色预警' };\r\n return { color: 'text-green-500', label: '正常' };\r\n };\r\n\r\n return (\r\n <MainLayout title={t('toolManagement')}>\r\n <div className=\"space-y-6\">\r\n <Card>\r\n <CardHeader>\r\n <CardTitle className=\"flex items-center gap-2\">\r\n <Wrench className=\"h-5 w-5\" />\r\n {t('toolList')}\r\n </CardTitle>\r\n </CardHeader>\r\n <CardContent>\r\n <div className=\"flex items-center gap-4 mb-4\">\r\n <div className=\"flex-1 max-w-sm\">\r\n <Input\r\n placeholder={tc('search')}\r\n value={searchKeyword}\r\n onChange={(e) => setSearchKeyword(e.target.value)}\r\n onKeyDown={(e) => e.key === 'Enter' && fetchTools()}\r\n />\r\n </div>\r\n <Select value={filterType} onValueChange={setFilterType}>\r\n <SelectTrigger className=\"w-32\">\r\n <SelectValue placeholder=\"类型\" />\r\n </SelectTrigger>\r\n <SelectContent>\r\n <SelectItem value=\"all\">全部类型</SelectItem>\r\n <SelectItem value=\"1\">刀模</SelectItem>\r\n <SelectItem value=\"2\">网版</SelectItem>\r\n </SelectContent>\r\n </Select>\r\n <Select value={filterStatus} onValueChange={setFilterStatus}>\r\n <SelectTrigger className=\"w-32\">\r\n <SelectValue placeholder=\"状态\" />\r\n </SelectTrigger>\r\n <SelectContent>\r\n <SelectItem value=\"all\">全部状态</SelectItem>\r\n <SelectItem value=\"1\">待用</SelectItem>\r\n <SelectItem value=\"2\">在用</SelectItem>\r\n <SelectItem value=\"3\">维修</SelectItem>\r\n <SelectItem value=\"4\">预警</SelectItem>\r\n <SelectItem value=\"5\">报废</SelectItem>\r\n </SelectContent>\r\n </Select>\r\n <Button onClick={() => fetchTools()} variant=\"outline\">\r\n <Search className=\"h-4 w-4 mr-2\" />\r\n {tc('search')}\r\n </Button>\r\n <Button\r\n onClick={() => {\r\n setEditingTool(null);\r\n setForm({\r\n tool_type: 1,\r\n tool_code: '',\r\n tool_name: '',\r\n spec: '',\r\n total_life: 0,\r\n original_cost: 0,\r\n mesh_count: '',\r\n mesh_material: '',\r\n size: '',\r\n tension_value: 0,\r\n });\r\n setIsDialogOpen(true);\r\n }}\r\n >\r\n <Plus className=\"h-4 w-4 mr-2\" />\r\n {tc('add')}\r\n </Button>\r\n </div>\r\n\r\n {loading ? (\r\n <div className=\"text-center py-8 text-muted-foreground\">{tc('loading')}</div>\r\n ) : tools.length === 0 ? (\r\n <div className=\"text-center py-8 text-muted-foreground\">\r\n <Wrench className=\"h-12 w-12 mx-auto mb-2 opacity-50\" />\r\n <p>{tc('noData')}</p>\r\n </div>\r\n ) : (\r\n <Table>\r\n <TableHeader>\r\n <TableRow>\r\n <TableHead>工装编号</TableHead>\r\n <TableHead>工装名称</TableHead>\r\n <TableHead>类型</TableHead>\r\n <TableHead>规格</TableHead>\r\n <TableHead>额定寿命</TableHead>\r\n <TableHead>已用/剩余</TableHead>\r\n <TableHead>单次成本</TableHead>\r\n <TableHead>状态</TableHead>\r\n <TableHead>寿命状态</TableHead>\r\n <TableHead>{tc('actions')}</TableHead>\r\n </TableRow>\r\n </TableHeader>\r\n <TableBody>\r\n {tools.map((tool) => {\r\n const lifeStatus = getLifeStatus(tool);\r\n return (\r\n <TableRow key={tool.id}>\r\n <TableCell className=\"font-mono\">{tool.tool_code}</TableCell>\r\n <TableCell>{tool.tool_name}</TableCell>\r\n <TableCell>\r\n <Badge variant=\"outline\">{TOOL_TYPE_MAP[tool.tool_type]}</Badge>\r\n </TableCell>\r\n <TableCell>{tool.spec || tool.mesh_count || '-'}</TableCell>\r\n <TableCell>{tool.total_life}次</TableCell>\r\n <TableCell>\r\n <span className={lifeStatus.color}>\r\n {tool.used_count}/{tool.remain_life}\r\n </span>\r\n </TableCell>\r\n <TableCell>¥{tool.unit_cost?.toFixed(2) || '0.00'}</TableCell>\r\n <TableCell>\r\n <Badge variant={STATUS_MAP[tool.status]?.variant || 'secondary'}>\r\n {STATUS_MAP[tool.status]?.label || '未知'}\r\n </Badge>\r\n </TableCell>\r\n <TableCell>\r\n <span className={lifeStatus.color}>\r\n {tool.status === 4 && <AlertTriangle className=\"h-4 w-4 inline mr-1\" />}\r\n {lifeStatus.label}\r\n </span>\r\n </TableCell>\r\n <TableCell>\r\n <DropdownMenu>\r\n <DropdownMenuTrigger asChild>\r\n <Button variant=\"ghost\" size=\"sm\">\r\n <MoreHorizontal className=\"h-4 w-4\" />\r\n </Button>\r\n </DropdownMenuTrigger>\r\n <DropdownMenuContent>\r\n <DropdownMenuItem\r\n onClick={() => {\r\n setEditingTool(tool);\r\n setForm({\r\n tool_type: tool.tool_type,\r\n tool_code: tool.tool_code,\r\n tool_name: tool.tool_name,\r\n spec: tool.spec || '',\r\n total_life: tool.total_life,\r\n original_cost: tool.original_cost,\r\n mesh_count: tool.mesh_count || '',\r\n mesh_material: tool.mesh_material || '',\r\n size: tool.size || '',\r\n tension_value: tool.tension_value || 0,\r\n });\r\n setIsDialogOpen(true);\r\n }}\r\n >\r\n <Edit className=\"h-4 w-4 mr-2\" />\r\n {tc('edit')}\r\n </DropdownMenuItem>\r\n {tool.status === 1 && (\r\n <DropdownMenuItem onClick={() => handleActivate(tool.id)}>\r\n <CheckCircle className=\"h-4 w-4 mr-2\" />\r\n 启用\r\n </DropdownMenuItem>\r\n )}\r\n {tool.status !== 5 && (\r\n <DropdownMenuItem onClick={() => handleScrap(tool.id)}>\r\n <XCircle className=\"h-4 w-4 mr-2 text-red-500\" />\r\n 报废\r\n </DropdownMenuItem>\r\n )}\r\n </DropdownMenuContent>\r\n </DropdownMenu>\r\n </TableCell>\r\n </TableRow>\r\n );\r\n })}\r\n </TableBody>\r\n </Table>\r\n )}\r\n </CardContent>\r\n </Card>\r\n </div>\r\n\r\n <Dialog open={isDialogOpen} onOpenChange={setIsDialogOpen}>\r\n <DialogContent className=\"max-w-2xl\">\r\n <DialogHeader>\r\n <DialogTitle>{editingTool ? '编辑工装' : '新建工装'}</DialogTitle>\r\n <DialogDescription>填写工装基础信息</DialogDescription>\r\n </DialogHeader>\r\n <div className=\"grid grid-cols-2 gap-4\">\r\n <div className=\"space-y-1\">\r\n <Label>\r\n 工装类型 <span className=\"text-red-500\">*</span>\r\n </Label>\r\n <Select\r\n value={String(form.tool_type)}\r\n onValueChange={(v) => setForm({ ...form, tool_type: Number(v) })}\r\n >\r\n <SelectTrigger>\r\n <SelectValue />\r\n </SelectTrigger>\r\n <SelectContent>\r\n <SelectItem value=\"1\">刀模</SelectItem>\r\n <SelectItem value=\"2\">网版</SelectItem>\r\n </SelectContent>\r\n </Select>\r\n </div>\r\n <div className=\"space-y-1\">\r\n <Label>\r\n 工装编号 <span className=\"text-red-500\">*</span>\r\n </Label>\r\n <Input\r\n value={form.tool_code}\r\n onChange={(e) => setForm({ ...form, tool_code: e.target.value })}\r\n />\r\n </div>\r\n <div className=\"space-y-1\">\r\n <Label>\r\n 工装名称 <span className=\"text-red-500\">*</span>\r\n </Label>\r\n <Input\r\n value={form.tool_name}\r\n onChange={(e) => setForm({ ...form, tool_name: e.target.value })}\r\n />\r\n </div>\r\n <div className=\"space-y-1\">\r\n <Label>规格</Label>\r\n <Input\r\n value={form.spec}\r\n onChange={(e) => setForm({ ...form, spec: e.target.value })}\r\n />\r\n </div>\r\n <div className=\"space-y-1\">\r\n <Label>额定寿命(次)</Label>\r\n <Input\r\n type=\"number\"\r\n value={form.total_life}\r\n onChange={(e) => setForm({ ...form, total_life: Number(e.target.value) })}\r\n />\r\n </div>\r\n <div className=\"space-y-1\">\r\n <Label>原始成本</Label>\r\n <Input\r\n type=\"number\"\r\n step=\"0.01\"\r\n value={form.original_cost}\r\n onChange={(e) => setForm({ ...form, original_cost: Number(e.target.value) })}\r\n />\r\n </div>\r\n {form.tool_type === 2 && (\r\n <>\r\n <div className=\"space-y-1\">\r\n <Label>目数</Label>\r\n <Input\r\n value={form.mesh_count}\r\n onChange={(e) => setForm({ ...form, mesh_count: e.target.value })}\r\n />\r\n </div>\r\n <div className=\"space-y-1\">\r\n <Label>网材</Label>\r\n <Input\r\n value={form.mesh_material}\r\n onChange={(e) => setForm({ ...form, mesh_material: e.target.value })}\r\n />\r\n </div>\r\n <div className=\"space-y-1\">\r\n <Label>尺寸</Label>\r\n <Input\r\n value={form.size}\r\n onChange={(e) => setForm({ ...form, size: e.target.value })}\r\n />\r\n </div>\r\n <div className=\"space-y-1\">\r\n <Label>张力值</Label>\r\n <Input\r\n type=\"number\"\r\n step=\"0.1\"\r\n value={form.tension_value}\r\n onChange={(e) => setForm({ ...form, tension_value: Number(e.target.value) })}\r\n />\r\n </div>\r\n </>\r\n )}\r\n </div>\r\n <div className=\"flex justify-end gap-2 mt-4\">\r\n <Button variant=\"outline\" onClick={() => setIsDialogOpen(false)}>\r\n {tc('cancel')}\r\n </Button>\r\n <Button onClick={handleSave}>{tc('save')}</Button>\r\n </div>\r\n </DialogContent>\r\n </Dialog>\r\n </MainLayout>\r\n );\r\n}\r\n","usedDeprecatedRules":[]},{"filePath":"D:\\dcprint\\erp-project\\src\\app\\[locale]\\dcprint\\tool\\page.tsx","messages":[{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":206,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":206,"endColumn":36},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":207,"column":16,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":207,"endColumn":23},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction<Tool[]>`.","line":208,"column":18,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":208,"endColumn":39},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":208,"column":23,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":208,"endColumn":27},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction<number>`.","line":209,"column":19,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":209,"endColumn":40},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":209,"column":24,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":209,"endColumn":28},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":218,"column":11,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":218,"endColumn":34},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":219,"column":14,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":219,"endColumn":21},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction<{ totalTools: number; activeTools: number; warningTools: number; maintenanceTools: number; scrappedTools: number; totalNetValue: number; }>`.","line":219,"column":36,"nodeType":"MemberExpression","messageId":"unsafeArgument","endLine":219,"endColumn":45},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":219,"column":41,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":219,"endColumn":45},{"ruleId":"react-hooks/set-state-in-effect","severity":1,"message":"Error: Calling setState synchronously within an effect can trigger cascading renders\n\nEffects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following:\n* Update external systems with the latest state from React.\n* Subscribe for updates from some external system, calling setState in a callback function when external state changes.\n\nCalling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect).\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\dcprint\\tool\\page.tsx:223:5\n 221 |\n 222 | useEffect(() => {\n> 223 | fetchTools();\n | ^^^^^^^^^^ Avoid calling setState() directly within an effect\n 224 | }, [fetchTools]);\n 225 | useEffect(() => {\n 226 | fetchDashboard();","line":223,"column":5,"nodeType":null,"endLine":223,"endColumn":15},{"ruleId":"react-hooks/set-state-in-effect","severity":1,"message":"Error: Calling setState synchronously within an effect can trigger cascading renders\n\nEffects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following:\n* Update external systems with the latest state from React.\n* Subscribe for updates from some external system, calling setState in a callback function when external state changes.\n\nCalling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect).\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\dcprint\\tool\\page.tsx:226:5\n 224 | }, [fetchTools]);\n 225 | useEffect(() => {\n> 226 | fetchDashboard();\n | ^^^^^^^^^^^^^^ Avoid calling setState() directly within an effect\n 227 | }, [fetchDashboard]);\n 228 |\n 229 | const openCreate = () => {","line":226,"column":5,"nodeType":null,"endLine":226,"endColumn":19},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":327,"column":11,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":327,"endColumn":34},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":328,"column":14,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":328,"endColumn":21},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"操作失败\"。建议使用: tc('text_d1rj43')","line":334,"column":22,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":334,"endColumn":28},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":334,"column":30,"nodeType":"Property","messageId":"anyAssignment","endLine":334,"endColumn":55},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .message on an `any` value.","line":334,"column":48,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":334,"endColumn":55},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe array destructuring of a tuple element with an `any` value.","line":344,"column":12,"nodeType":"Identifier","messageId":"unsafeArrayPatternFromTuple","endLine":344,"endColumn":17},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe array destructuring of a tuple element with an `any` value.","line":344,"column":19,"nodeType":"Identifier","messageId":"unsafeArrayPatternFromTuple","endLine":344,"endColumn":24},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction<UsageRecord[]>`.","line":345,"column":21,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":345,"endColumn":43},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":345,"column":27,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":345,"endColumn":31},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction<MaintenanceRecord[]>`.","line":346,"column":27,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":346,"endColumn":49},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":346,"column":33,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":346,"endColumn":37},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":356,"column":11,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":356,"endColumn":34},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":357,"column":14,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":357,"endColumn":21},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"使用记录已登记\"。建议使用: tc('text_59m4pl')","line":358,"column":22,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":358,"endColumn":31},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"操作失败\"。建议使用: tc('text_d1rj43')","line":364,"column":22,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":364,"endColumn":28},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":364,"column":30,"nodeType":"Property","messageId":"anyAssignment","endLine":364,"endColumn":55},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .message on an `any` value.","line":364,"column":48,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":364,"endColumn":55},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":383,"column":11,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":383,"endColumn":34},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":384,"column":14,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":384,"endColumn":21},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"操作失败\"。建议使用: tc('text_d1rj43')","line":398,"column":22,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":398,"endColumn":28},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":398,"column":30,"nodeType":"Property","messageId":"anyAssignment","endLine":398,"endColumn":55},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .message on an `any` value.","line":398,"column":48,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":398,"endColumn":55},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":409,"column":11,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":409,"endColumn":34},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":410,"column":14,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":410,"endColumn":21},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"工装已报废\"。建议使用: tc('text_tm7ong')","line":411,"column":22,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":411,"endColumn":29},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"操作失败\"。建议使用: tc('text_d1rj43')","line":417,"column":22,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":417,"endColumn":28},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":417,"column":30,"nodeType":"Property","messageId":"anyAssignment","endLine":417,"endColumn":55},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .message on an `any` value.","line":417,"column":48,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":417,"endColumn":55},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":423,"column":11,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":423,"endColumn":34},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":424,"column":14,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":424,"endColumn":21},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"工装已激活\"。建议使用: tc('text_tm9zsd')","line":425,"column":22,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":425,"endColumn":29},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"操作失败\"。建议使用: tc('text_d1rj43')","line":429,"column":22,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":429,"endColumn":28},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":429,"column":30,"nodeType":"Property","messageId":"anyAssignment","endLine":429,"endColumn":55},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .message on an `any` value.","line":429,"column":48,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":429,"endColumn":55},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":436,"column":11,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":436,"endColumn":34},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":437,"column":14,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":437,"endColumn":21},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"删除成功\"。建议使用: tc('text_azeh8z')","line":438,"column":22,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":438,"endColumn":28},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"操作失败\"。建议使用: tc('text_d1rj43')","line":442,"column":22,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":442,"endColumn":28},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":442,"column":30,"nodeType":"Property","messageId":"anyAssignment","endLine":442,"endColumn":55},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .message on an `any` value.","line":442,"column":48,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":442,"endColumn":55},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"新增工装\"。建议使用: {tc('text_d762ge')}","line":457,"column":46,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":459,"endColumn":11},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"在用\"。建议使用: {tc('text_fgu8')}","line":472,"column":60,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":472,"endColumn":62},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"预警\"。建议使用: {tc('text_qpgi')}","line":478,"column":60,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":478,"endColumn":62},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"维修中\"。建议使用: {tc('text_izg1f')}","line":484,"column":60,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":484,"endColumn":63},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"已报废\"。建议使用: {tc('text_e8o3w')}","line":490,"column":60,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":490,"endColumn":63},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"全部\"。建议使用: {tc('text_en40')}","line":506,"column":37,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":506,"endColumn":39},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"刀模\"。建议使用: {tc('text_ej35')}","line":507,"column":38,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":507,"endColumn":40},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"网版\"。建议使用: {tc('text_ma6v')}","line":508,"column":38,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":508,"endColumn":40},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"全部状态\"。建议使用: {tc('text_avez63')}","line":516,"column":36,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":516,"endColumn":40},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"在用\"。建议使用: {tc('text_fgu8')}","line":518,"column":37,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":518,"endColumn":39},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"维修中\"。建议使用: {tc('text_izg1f')}","line":519,"column":37,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":519,"endColumn":40},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"预警\"。建议使用: {tc('text_qpgi')}","line":520,"column":37,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":520,"endColumn":39},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"已报废\"。建议使用: {tc('text_e8o3w')}","line":521,"column":37,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":521,"endColumn":40},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"类型\"。建议使用: {tc('text_lnjk')}","line":542,"column":30,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":542,"endColumn":32},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"编码\"。建议使用: {tc('text_m9wr')}","line":543,"column":30,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":543,"endColumn":32},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"名称\"。建议使用: {tc('text_eyrn')}","line":544,"column":30,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":544,"endColumn":32},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"净值\"。建议使用: {tc('text_ecfw')}","line":546,"column":30,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":546,"endColumn":32},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"状态\"。建议使用: {tc('text_k1e3')}","line":547,"column":30,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":547,"endColumn":32},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"操作\"。建议使用: {tc('text_hkxb')}","line":548,"column":30,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":548,"endColumn":32},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"暂无数据\"。建议使用: {tc('text_dcv57g')}","line":554,"column":95,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":556,"endColumn":21},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"刀模\"。建议使用: {tc('text_ej35')}","line":676,"column":49,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":676,"endColumn":51},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"网版\"。建议使用: {tc('text_ma6v')}","line":677,"column":49,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":677,"endColumn":51},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"规格\"。建议使用: {tc('text_o06w')}","line":699,"column":24,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":699,"endColumn":26},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"存放位置\"。建议使用: {tc('text_bxzajr')}","line":737,"column":24,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":737,"endColumn":28},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"备注\"。建议使用: {tc('text_fqo1')}","line":842,"column":24,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":842,"endColumn":26},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"取消\"。建议使用: {tc('text_ev02')}","line":850,"column":78,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":852,"endColumn":15},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"确定\"。建议使用: {tc('text_kzjg')}","line":853,"column":44,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":853,"endColumn":46},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"使用记录\"。建议使用: {tc('text_aisnou')}","line":921,"column":48,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":921,"endColumn":52},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"维修记录\"。建议使用: {tc('text_gctspr')}","line":922,"column":54,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":922,"endColumn":58},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"时间\"。建议使用: {tc('text_i5z2')}","line":928,"column":38,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":928,"endColumn":40},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"工单号\"。建议使用: {tc('text_e5qlj')}","line":929,"column":38,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":929,"endColumn":41},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"工序\"。建议使用: {tc('text_ghmy')}","line":930,"column":38,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":930,"endColumn":40},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"操作人\"。建议使用: {tc('text_f5g8b')}","line":933,"column":38,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":933,"endColumn":41},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"暂无记录\"。建议使用: {tc('text_dd1mmb')}","line":942,"column":30,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":944,"endColumn":29},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"类型\"。建议使用: {tc('text_lnjk')}","line":965,"column":38,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":965,"endColumn":40},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"费用\"。建议使用: {tc('text_onwv')}","line":966,"column":38,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":966,"endColumn":40},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"状态\"。建议使用: {tc('text_k1e3')}","line":968,"column":38,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":968,"endColumn":40},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"时间\"。建议使用: {tc('text_i5z2')}","line":969,"column":38,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":969,"endColumn":40},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"描述\"。建议使用: {tc('text_hrlt')}","line":970,"column":38,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":970,"endColumn":40},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"暂无记录\"。建议使用: {tc('text_dd1mmb')}","line":979,"column":30,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":981,"endColumn":29},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"进行中\"。建议使用: {tc('text_lq5q4')}","line":995,"column":60,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":995,"endColumn":63},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"已完成\"。建议使用: {tc('text_e7hbq')}","line":997,"column":42,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":997,"endColumn":45},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"工序名称\"。建议使用: {tc('text_c8ls99')}","line":1045,"column":24,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":1045,"endColumn":28},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"备注\"。建议使用: {tc('text_fqo1')}","line":1052,"column":24,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":1052,"endColumn":26},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"取消\"。建议使用: {tc('text_ev02')}","line":1060,"column":82,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":1062,"endColumn":15},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"确定\"。建议使用: {tc('text_kzjg')}","line":1063,"column":45,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":1063,"endColumn":47},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"维修类型\"。建议使用: {tc('text_gcr622')}","line":1095,"column":28,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":1095,"endColumn":32},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"维修\"。建议使用: {tc('text_m16i')}","line":1105,"column":49,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":1105,"endColumn":51},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"保养\"。建议使用: {tc('text_e14u')}","line":1106,"column":49,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":1106,"endColumn":51},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"维修费用\"。建议使用: {tc('text_gcu6fd')}","line":1132,"column":28,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":1132,"endColumn":32},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"取消\"。建议使用: {tc('text_ev02')}","line":1163,"column":82,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":1165,"endColumn":15},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"确定\"。建议使用: {tc('text_kzjg')}","line":1166,"column":51,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":1166,"endColumn":53},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"报废原因\"。建议使用: {tc('text_cu6am3')}","line":1186,"column":24,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":1186,"endColumn":28},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"取消\"。建议使用: {tc('text_ev02')}","line":1194,"column":82,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":1196,"endColumn":15},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"确认报废\"。建议使用: {tc('text_frrbww')}","line":1197,"column":67,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":1199,"endColumn":15}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":107,"fixableErrorCount":0,"fixableWarningCount":0,"source":"'use client';\n\nimport { authFetch } from '@/lib/auth-fetch';\nimport { TOOL_TYPE_LABEL, TOOL_STATUS_LABEL } from '@/lib/status-labels';\nimport { useEffect, useState, useCallback } from 'react';\nimport { MainLayout } from '@/components/layout';\nimport { Card, CardContent } from '@/components/ui/card';\nimport { Button } from '@/components/ui/button';\nimport { Input } from '@/components/ui/input';\nimport { Badge } from '@/components/ui/badge';\nimport { Label } from '@/components/ui/label';\nimport { Textarea } from '@/components/ui/textarea';\nimport {\n Table,\n TableBody,\n TableCell,\n TableHead,\n TableHeader,\n TableRow,\n} from '@/components/ui/table';\nimport {\n Dialog,\n DialogContent,\n DialogHeader,\n DialogTitle,\n DialogFooter,\n} from '@/components/ui/dialog';\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@/components/ui/select';\nimport { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';\nimport {\n Plus,\n Search,\n Edit,\n Trash2,\n Play,\n Wrench,\n Ban,\n AlertTriangle,\n Activity,\n} from 'lucide-react';\nimport { useToast } from '@/hooks/use-toast';\n\ninterface Tool {\n id: number;\n tool_type: number;\n tool_code: string;\n tool_name: string;\n spec: string | null;\n total_life: number;\n warning_threshold: number;\n used_count: number;\n remain_life: number;\n original_cost: string;\n accumulated_cost: string;\n net_value: string;\n unit_cost: string;\n status: number;\n manufacture_date: string | null;\n warehouse_location: string | null;\n // 体系B 字段\n asset_type: string | null;\n layout_type: string | null;\n pieces_per_impression: number | null;\n material: string | null;\n qr_code: string | null;\n supplier_id: number | null;\n maintenance_interval: number | null;\n maintenance_count: number | null;\n last_used_date: string | null;\n // 体系C 字段\n mesh_count: string | null;\n mesh_material: string | null;\n size: string | null;\n tension_value: number | null;\n frame_type: string | null;\n customer_id: number | null;\n reclaim_count: number | null;\n remark: string | null;\n}\n\ninterface UsageRecord {\n id: number;\n work_order_no: string | null;\n process_name: string | null;\n use_count: number;\n amortized_cost: string;\n operator_name: string | null;\n use_time: string;\n}\n\ninterface MaintenanceRecord {\n id: number;\n maintenance_type: number;\n maintenance_cost: string;\n description: string | null;\n life_before: number;\n life_after: number;\n life_adjustment: number;\n status: number;\n start_time: string;\n end_time: string | null;\n operator_name: string | null;\n}\n\nconst STATUS_MAP: Record<\n number,\n { label: string; variant: 'default' | 'secondary' | 'destructive' | 'outline' }\n> = {\n 1: { label: TOOL_STATUS_LABEL[1], variant: 'secondary' },\n 2: { label: TOOL_STATUS_LABEL[2], variant: 'default' },\n 3: { label: TOOL_STATUS_LABEL[3], variant: 'outline' },\n 4: { label: TOOL_STATUS_LABEL[4], variant: 'destructive' },\n 5: { label: TOOL_STATUS_LABEL[5], variant: 'destructive' },\n};\n\nconst TYPE_MAP = TOOL_TYPE_LABEL;\n\nexport default function ToolManagementPage() {\n const { toast } = useToast();\n\n const [tools, setTools] = useState<Tool[]>([]);\n const [_total, _setTotal] = useState(0);\n const [page, _setPage] = useState(1);\n const [pageSize] = useState(20);\n const [_loading, setLoading] = useState(false);\n const [filterType, setFilterType] = useState<string>('');\n const [filterStatus, setFilterStatus] = useState<string>('');\n const [keyword, setKeyword] = useState('');\n\n const [createOpen, setCreateOpen] = useState(false);\n const [editTool, setEditTool] = useState<Tool | null>(null);\n const [detailTool, setDetailTool] = useState<Tool | null>(null);\n const [usageRecords, setUsageRecords] = useState<UsageRecord[]>([]);\n const [maintenanceRecords, setMaintenanceRecords] = useState<MaintenanceRecord[]>([]);\n\n const [usageDialogTool, setUsageDialogTool] = useState<Tool | null>(null);\n const [maintDialogTool, setMaintDialogTool] = useState<Tool | null>(null);\n const [scrapDialogTool, setScrapDialogTool] = useState<Tool | null>(null);\n\n const [formData, setFormData] = useState({\n tool_type: 1,\n tool_code: '',\n tool_name: '',\n spec: '',\n total_life: 10000,\n warning_threshold: 8000,\n original_cost: 0,\n manufacture_date: '',\n warehouse_location: '',\n // 体系B 字段 (刀模)\n asset_type: '',\n layout_type: '',\n pieces_per_impression: 0,\n material: '',\n qr_code: '',\n supplier_id: 0,\n maintenance_interval: 0,\n // 体系C 字段 (网版)\n mesh_count: '',\n mesh_material: '',\n size: '',\n tension_value: 0,\n frame_type: '',\n customer_id: 0,\n remark: '',\n });\n const [usageForm, setUsageForm] = useState({\n useCount: 1,\n workOrderNo: '',\n processName: '',\n remark: '',\n });\n const [maintForm, setMaintForm] = useState({\n maintenanceType: 1,\n description: '',\n completeAction: false,\n maintenanceId: 0,\n maintenanceCost: 0,\n lifeAfter: 0,\n });\n const [scrapForm, setScrapForm] = useState({ scrapReason: '' });\n\n const [dashboard, setDashboard] = useState({\n totalTools: 0,\n activeTools: 0,\n warningTools: 0,\n maintenanceTools: 0,\n scrappedTools: 0,\n totalNetValue: 0,\n });\n\n const fetchTools = useCallback(async () => {\n setLoading(true);\n try {\n const params = new URLSearchParams({ page: String(page), pageSize: String(pageSize) });\n if (filterType) params.set('toolType', filterType);\n if (filterStatus) params.set('status', filterStatus);\n if (keyword) params.set('keyword', keyword);\n const res = await authFetch(`/api/dcprint/tool?${params}`);\n const data = await res.json();\n if (data.success) {\n setTools(data.data?.list || []);\n _setTotal(data.data?.total || 0);\n }\n } finally {\n setLoading(false);\n }\n }, [page, pageSize, filterType, filterStatus, keyword]);\n\n const fetchDashboard = useCallback(async () => {\n const res = await authFetch('/api/dcprint/tool/dashboard');\n const data = await res.json();\n if (data.success) setDashboard(data.data);\n }, []);\n\n useEffect(() => {\n fetchTools();\n }, [fetchTools]);\n useEffect(() => {\n fetchDashboard();\n }, [fetchDashboard]);\n\n const openCreate = () => {\n setEditTool(null);\n setFormData({\n tool_type: 1,\n tool_code: '',\n tool_name: '',\n spec: '',\n total_life: 10000,\n warning_threshold: 8000,\n original_cost: 0,\n manufacture_date: '',\n warehouse_location: '',\n asset_type: '',\n layout_type: '',\n pieces_per_impression: 0,\n material: '',\n qr_code: '',\n supplier_id: 0,\n maintenance_interval: 0,\n mesh_count: '',\n mesh_material: '',\n size: '',\n tension_value: 0,\n frame_type: '',\n customer_id: 0,\n remark: '',\n });\n setCreateOpen(true);\n };\n\n const openEdit = (tool: Tool) => {\n setEditTool(tool);\n setFormData({\n tool_type: tool.tool_type,\n tool_code: tool.tool_code,\n tool_name: tool.tool_name,\n spec: tool.spec || '',\n total_life: tool.total_life,\n warning_threshold: tool.warning_threshold,\n original_cost: Number(tool.original_cost),\n manufacture_date: tool.manufacture_date || '',\n warehouse_location: tool.warehouse_location || '',\n asset_type: tool.asset_type || '',\n layout_type: tool.layout_type || '',\n pieces_per_impression: tool.pieces_per_impression || 0,\n material: tool.material || '',\n qr_code: tool.qr_code || '',\n supplier_id: tool.supplier_id || 0,\n maintenance_interval: tool.maintenance_interval || 0,\n mesh_count: tool.mesh_count || '',\n mesh_material: tool.mesh_material || '',\n size: tool.size || '',\n tension_value: tool.tension_value || 0,\n frame_type: tool.frame_type || '',\n customer_id: tool.customer_id || 0,\n remark: tool.remark || '',\n });\n setCreateOpen(true);\n };\n\n const submitForm = async () => {\n const method = editTool ? 'PUT' : 'POST';\n const url = editTool ? `/api/dcprint/tool/${editTool.id}` : '/api/dcprint/tool';\n const commonFields = {\n toolName: formData.tool_name,\n spec: formData.spec,\n totalLife: formData.total_life,\n warningThreshold: formData.warning_threshold,\n warehouseLocation: formData.warehouse_location,\n assetType: formData.asset_type || undefined,\n layoutType: formData.layout_type || undefined,\n piecesPerImpression: formData.pieces_per_impression || undefined,\n material: formData.material || undefined,\n qrCode: formData.qr_code || undefined,\n supplierId: formData.supplier_id || undefined,\n maintenanceInterval: formData.maintenance_interval || undefined,\n meshCount: formData.mesh_count || undefined,\n meshMaterial: formData.mesh_material || undefined,\n size: formData.size || undefined,\n tensionValue: formData.tension_value || undefined,\n frameType: formData.frame_type || undefined,\n customerId: formData.customer_id || undefined,\n remark: formData.remark,\n };\n const body = editTool\n ? commonFields\n : {\n toolType: formData.tool_type,\n toolCode: formData.tool_code,\n originalCost: formData.original_cost,\n manufactureDate: formData.manufacture_date,\n ...commonFields,\n };\n const res = await authFetch(url, {\n method,\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(body),\n });\n const data = await res.json();\n if (data.success) {\n toast({ title: editTool ? '更新成功' : '创建成功' });\n setCreateOpen(false);\n fetchTools();\n fetchDashboard();\n } else {\n toast({ title: '操作失败', description: data.message, variant: 'destructive' });\n }\n };\n\n const openDetail = async (tool: Tool) => {\n setDetailTool(tool);\n const [uRes, mRes] = await Promise.all([\n authFetch(`/api/dcprint/tool/${tool.id}/usage`),\n authFetch(`/api/dcprint/tool/${tool.id}/maintenance`),\n ]);\n const [uData, mData] = await Promise.all([uRes.json(), mRes.json()]);\n setUsageRecords(uData.data?.list || []);\n setMaintenanceRecords(mData.data?.list || []);\n };\n\n const submitUsage = async () => {\n if (!usageDialogTool) return;\n const res = await authFetch(`/api/dcprint/tool/${usageDialogTool.id}/usage`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(usageForm),\n });\n const data = await res.json();\n if (data.success) {\n toast({ title: '使用记录已登记' });\n setUsageDialogTool(null);\n setUsageForm({ useCount: 1, workOrderNo: '', processName: '', remark: '' });\n fetchTools();\n fetchDashboard();\n } else {\n toast({ title: '操作失败', description: data.message, variant: 'destructive' });\n }\n };\n\n const submitMaintenance = async () => {\n if (!maintDialogTool) return;\n const body = maintForm.completeAction\n ? {\n action: 'complete',\n maintenanceId: maintForm.maintenanceId,\n maintenanceCost: maintForm.maintenanceCost,\n lifeAfter: maintForm.lifeAfter,\n }\n : { maintenanceType: maintForm.maintenanceType, description: maintForm.description };\n const res = await authFetch(`/api/dcprint/tool/${maintDialogTool.id}/maintenance`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(body),\n });\n const data = await res.json();\n if (data.success) {\n toast({ title: maintForm.completeAction ? '维修已完成' : '维修已开始' });\n setMaintDialogTool(null);\n setMaintForm({\n maintenanceType: 1,\n description: '',\n completeAction: false,\n maintenanceId: 0,\n maintenanceCost: 0,\n lifeAfter: 0,\n });\n fetchTools();\n fetchDashboard();\n } else {\n toast({ title: '操作失败', description: data.message, variant: 'destructive' });\n }\n };\n\n const submitScrap = async () => {\n if (!scrapDialogTool) return;\n const res = await authFetch(`/api/dcprint/tool/${scrapDialogTool.id}/scrap`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(scrapForm),\n });\n const data = await res.json();\n if (data.success) {\n toast({ title: '工装已报废' });\n setScrapDialogTool(null);\n setScrapForm({ scrapReason: '' });\n fetchTools();\n fetchDashboard();\n } else {\n toast({ title: '操作失败', description: data.message, variant: 'destructive' });\n }\n };\n\n const activateTool = async (tool: Tool) => {\n const res = await authFetch(`/api/dcprint/tool/${tool.id}/activate`, { method: 'POST' });\n const data = await res.json();\n if (data.success) {\n toast({ title: '工装已激活' });\n fetchTools();\n fetchDashboard();\n } else {\n toast({ title: '操作失败', description: data.message, variant: 'destructive' });\n }\n };\n\n const deleteTool = async (tool: Tool) => {\n if (!confirm(`确认删除工装 ${tool.tool_code}?`)) return;\n const res = await authFetch(`/api/dcprint/tool/${tool.id}`, { method: 'DELETE' });\n const data = await res.json();\n if (data.success) {\n toast({ title: '删除成功' });\n fetchTools();\n fetchDashboard();\n } else {\n toast({ title: '操作失败', description: data.message, variant: 'destructive' });\n }\n };\n\n const lifePercent = (tool: Tool) => {\n if (tool.total_life <= 0) return 0;\n return Math.round((tool.used_count / tool.total_life) * 100);\n };\n\n return (\n <MainLayout>\n <div className=\"p-6 space-y-6\">\n <div className=\"flex items-center justify-between\">\n <h1 className=\"text-2xl font-bold\">{'刀具管理'}</h1>\n <Button onClick={openCreate}>\n <Plus className=\"mr-2 h-4 w-4\" />\n 新增工装\n </Button>\n </div>\n\n {/* Dashboard */}\n <div className=\"grid grid-cols-2 md:grid-cols-6 gap-4\">\n <Card>\n <CardContent className=\"p-4\">\n <p className=\"text-sm text-muted-foreground\">{'刀具总数'}</p>\n <p className=\"text-2xl font-bold\">{dashboard.totalTools}</p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"p-4\">\n <p className=\"text-sm text-muted-foreground\">在用</p>\n <p className=\"text-2xl font-bold text-green-600\">{dashboard.activeTools}</p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"p-4\">\n <p className=\"text-sm text-muted-foreground\">预警</p>\n <p className=\"text-2xl font-bold text-orange-600\">{dashboard.warningTools}</p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"p-4\">\n <p className=\"text-sm text-muted-foreground\">维修中</p>\n <p className=\"text-2xl font-bold text-blue-600\">{dashboard.maintenanceTools}</p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"p-4\">\n <p className=\"text-sm text-muted-foreground\">已报废</p>\n <p className=\"text-2xl font-bold text-red-600\">{dashboard.scrappedTools}</p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"p-4\">\n <p className=\"text-sm text-muted-foreground\">{'净值合计'}</p>\n <p className=\"text-2xl font-bold\">¥{dashboard.totalNetValue.toFixed(2)}</p>\n </CardContent>\n </Card>\n </div>\n\n {/* Filter */}\n <div className=\"flex gap-4 items-center\">\n <Tabs value={filterType} onValueChange={setFilterType}>\n <TabsList>\n <TabsTrigger value=\"\">全部</TabsTrigger>\n <TabsTrigger value=\"1\">刀模</TabsTrigger>\n <TabsTrigger value=\"2\">网版</TabsTrigger>\n </TabsList>\n </Tabs>\n <Select value={filterStatus} onValueChange={setFilterStatus}>\n <SelectTrigger className=\"w-32\">\n <SelectValue placeholder=\"全部状态\" />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"\">全部状态</SelectItem>\n <SelectItem value=\"1\">{'闲置'}</SelectItem>\n <SelectItem value=\"2\">在用</SelectItem>\n <SelectItem value=\"3\">维修中</SelectItem>\n <SelectItem value=\"4\">预警</SelectItem>\n <SelectItem value=\"5\">已报废</SelectItem>\n </SelectContent>\n </Select>\n <Input\n placeholder=\"编码/名称搜索\"\n value={keyword}\n onChange={(e) => setKeyword(e.target.value)}\n className=\"w-64\"\n onKeyDown={(e) => e.key === 'Enter' && fetchTools()}\n />\n <Button variant=\"outline\" onClick={fetchTools}>\n <Search className=\"h-4 w-4\" />\n </Button>\n </div>\n\n {/* Table */}\n <Card>\n <CardContent className=\"p-0\">\n <Table>\n <TableHeader>\n <TableRow>\n <TableHead>类型</TableHead>\n <TableHead>编码</TableHead>\n <TableHead>名称</TableHead>\n <TableHead>{'使用寿命'}</TableHead>\n <TableHead>净值</TableHead>\n <TableHead>状态</TableHead>\n <TableHead>操作</TableHead>\n </TableRow>\n </TableHeader>\n <TableBody>\n {tools.length === 0 ? (\n <TableRow>\n <TableCell colSpan={7} className=\"text-center text-muted-foreground py-8\">\n 暂无数据\n </TableCell>\n </TableRow>\n ) : (\n tools.map((tool) => (\n <TableRow key={tool.id}>\n <TableCell>\n <Badge variant=\"outline\">{TYPE_MAP[tool.tool_type]}</Badge>\n </TableCell>\n <TableCell\n className=\"font-mono cursor-pointer hover:underline\"\n onClick={() => openDetail(tool)}\n >\n {tool.tool_code}\n </TableCell>\n <TableCell>{tool.tool_name}</TableCell>\n <TableCell>\n <div className=\"flex items-center gap-2\">\n <div className=\"w-24 h-2 bg-gray-200 rounded-full overflow-hidden\">\n <div\n className={`h-full ${lifePercent(tool) >= 80 ? 'bg-red-500' : lifePercent(tool) >= 60 ? 'bg-orange-500' : 'bg-green-500'}`}\n style={{ width: `${lifePercent(tool)}%` }}\n />\n </div>\n <span className=\"text-xs text-muted-foreground\">\n {tool.used_count}/{tool.total_life}\n </span>\n </div>\n </TableCell>\n <TableCell>¥{Number(tool.net_value).toFixed(2)}</TableCell>\n <TableCell>\n <Badge variant={STATUS_MAP[tool.status]?.variant || 'default'}>\n {STATUS_MAP[tool.status]?.label}\n </Badge>\n </TableCell>\n <TableCell>\n <div className=\"flex gap-1\">\n {tool.status === 1 && (\n <Button\n size=\"sm\"\n variant=\"ghost\"\n onClick={() => activateTool(tool)}\n title=\"激活\"\n >\n <Play className=\"h-4 w-4\" />\n </Button>\n )}\n {(tool.status === 2 || tool.status === 4) && (\n <Button\n size=\"sm\"\n variant=\"ghost\"\n onClick={() => setUsageDialogTool(tool)}\n title=\"登记使用\"\n >\n <Activity className=\"h-4 w-4\" />\n </Button>\n )}\n {(tool.status === 2 || tool.status === 4) && (\n <Button\n size=\"sm\"\n variant=\"ghost\"\n onClick={() => setMaintDialogTool(tool)}\n title=\"维修\"\n >\n <Wrench className=\"h-4 w-4\" />\n </Button>\n )}\n {tool.status !== 5 && (\n <Button\n size=\"sm\"\n variant=\"ghost\"\n onClick={() => setScrapDialogTool(tool)}\n title=\"报废\"\n >\n <Ban className=\"h-4 w-4\" />\n </Button>\n )}\n <Button\n size=\"sm\"\n variant=\"ghost\"\n onClick={() => openEdit(tool)}\n title=\"编辑\"\n >\n <Edit className=\"h-4 w-4\" />\n </Button>\n <Button\n size=\"sm\"\n variant=\"ghost\"\n onClick={() => deleteTool(tool)}\n title=\"删除\"\n >\n <Trash2 className=\"h-4 w-4\" />\n </Button>\n </div>\n </TableCell>\n </TableRow>\n ))\n )}\n </TableBody>\n </Table>\n </CardContent>\n </Card>\n\n {/* Create/Edit Dialog */}\n <Dialog open={createOpen} onOpenChange={setCreateOpen}>\n <DialogContent className=\"max-w-2xl\">\n <DialogHeader>\n <DialogTitle>{editTool ? '编辑工装' : '新增工装'}</DialogTitle>\n </DialogHeader>\n <div className=\"grid grid-cols-2 gap-4\">\n {!editTool && (\n <>\n <div>\n <Label>{'工装类型'}</Label>\n <Select\n value={String(formData.tool_type)}\n onValueChange={(v) => setFormData({ ...formData, tool_type: Number(v) })}\n >\n <SelectTrigger>\n <SelectValue />\n <SelectContent>\n <SelectItem value=\"1\">刀模</SelectItem>\n <SelectItem value=\"2\">网版</SelectItem>\n </SelectContent>\n </SelectTrigger>\n </Select>\n </div>\n <div>\n <Label>{'工装编码'}</Label>\n <Input\n value={formData.tool_code}\n onChange={(e) => setFormData({ ...formData, tool_code: e.target.value })}\n />\n </div>\n </>\n )}\n <div className=\"col-span-2\">\n <Label>{'工装名称'}</Label>\n <Input\n value={formData.tool_name}\n onChange={(e) => setFormData({ ...formData, tool_name: e.target.value })}\n />\n </div>\n <div className=\"col-span-2\">\n <Label>规格</Label>\n <Input\n value={formData.spec}\n onChange={(e) => setFormData({ ...formData, spec: e.target.value })}\n />\n </div>\n <div>\n <Label>{'总寿命(次)'}</Label>\n <Input\n type=\"number\"\n value={formData.total_life}\n onChange={(e) => setFormData({ ...formData, total_life: Number(e.target.value) })}\n />\n </div>\n <div>\n <Label>{'预警阈值(次)'}</Label>\n <Input\n type=\"number\"\n value={formData.warning_threshold}\n onChange={(e) =>\n setFormData({ ...formData, warning_threshold: Number(e.target.value) })\n }\n />\n </div>\n {!editTool && (\n <div>\n <Label>{'原值(成本)'}</Label>\n <Input\n type=\"number\"\n step=\"0.01\"\n value={formData.original_cost}\n onChange={(e) =>\n setFormData({ ...formData, original_cost: Number(e.target.value) })\n }\n />\n </div>\n )}\n <div>\n <Label>存放位置</Label>\n <Input\n value={formData.warehouse_location}\n onChange={(e) => setFormData({ ...formData, warehouse_location: e.target.value })}\n />\n </div>\n <div>\n <Label>{'生产日期'}</Label>\n <Input\n type=\"date\"\n value={formData.manufacture_date}\n onChange={(e) => setFormData({ ...formData, manufacture_date: e.target.value })}\n />\n </div>\n {/* 体系B 字段 — 仅刀模显示 */}\n {formData.tool_type === 1 && (\n <>\n <div>\n <Label>{'资产类型'}</Label>\n <Input\n value={formData.asset_type}\n onChange={(e) => setFormData({ ...formData, asset_type: e.target.value })}\n />\n </div>\n <div>\n <Label>{'版面类型'}</Label>\n <Input\n value={formData.layout_type}\n onChange={(e) => setFormData({ ...formData, layout_type: e.target.value })}\n />\n </div>\n <div>\n <Label>{'每版印张数'}</Label>\n <Input\n type=\"number\"\n value={formData.pieces_per_impression}\n onChange={(e) =>\n setFormData({ ...formData, pieces_per_impression: Number(e.target.value) })\n }\n />\n </div>\n <div>\n <Label>{'材质'}</Label>\n <Input\n value={formData.material}\n onChange={(e) => setFormData({ ...formData, material: e.target.value })}\n />\n </div>\n <div>\n <Label>{'保养间隔(印数)'}</Label>\n <Input\n type=\"number\"\n value={formData.maintenance_interval}\n onChange={(e) =>\n setFormData({ ...formData, maintenance_interval: Number(e.target.value) })\n }\n />\n </div>\n </>\n )}\n {/* 体系C 字段 — 仅网版显示 */}\n {formData.tool_type === 2 && (\n <>\n <div>\n <Label>{'目数'}</Label>\n <Input\n value={formData.mesh_count}\n onChange={(e) => setFormData({ ...formData, mesh_count: e.target.value })}\n />\n </div>\n <div>\n <Label>{'丝网材质'}</Label>\n <Input\n value={formData.mesh_material}\n onChange={(e) => setFormData({ ...formData, mesh_material: e.target.value })}\n />\n </div>\n <div>\n <Label>{'尺寸'}</Label>\n <Input\n value={formData.size}\n onChange={(e) => setFormData({ ...formData, size: e.target.value })}\n />\n </div>\n <div>\n <Label>{'张力值'}</Label>\n <Input\n type=\"number\"\n step=\"0.1\"\n value={formData.tension_value}\n onChange={(e) =>\n setFormData({ ...formData, tension_value: Number(e.target.value) })\n }\n />\n </div>\n <div>\n <Label>{'网框类型'}</Label>\n <Input\n value={formData.frame_type}\n onChange={(e) => setFormData({ ...formData, frame_type: e.target.value })}\n />\n </div>\n </>\n )}\n <div className=\"col-span-2\">\n <Label>备注</Label>\n <Textarea\n value={formData.remark}\n onChange={(e) => setFormData({ ...formData, remark: e.target.value })}\n />\n </div>\n </div>\n <DialogFooter>\n <Button variant=\"outline\" onClick={() => setCreateOpen(false)}>\n 取消\n </Button>\n <Button onClick={submitForm}>确定</Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n\n {/* Detail Dialog */}\n <Dialog open={!!detailTool} onOpenChange={(v) => !v && setDetailTool(null)}>\n <DialogContent className=\"max-w-4xl max-h-[80vh] overflow-y-auto\">\n <DialogHeader>\n <DialogTitle>\n {'刀具详情'}\n {detailTool?.tool_code}\n </DialogTitle>\n </DialogHeader>\n {detailTool && (\n <div className=\"space-y-4\">\n <div className=\"grid grid-cols-4 gap-4 text-sm\">\n <div>\n <span className=\"text-muted-foreground\">{'类型'}</span>{' '}\n {TYPE_MAP[detailTool.tool_type]}\n </div>\n <div>\n <span className=\"text-muted-foreground\">{'名称'}</span> {detailTool.tool_name}\n </div>\n <div>\n <span className=\"text-muted-foreground\">{'规格'}</span> {detailTool.spec || '-'}\n </div>\n <div>\n <span className=\"text-muted-foreground\">{'状态'}</span>{' '}\n <Badge variant={STATUS_MAP[detailTool.status]?.variant}>\n {STATUS_MAP[detailTool.status]?.label}\n </Badge>\n </div>\n <div>\n <span className=\"text-muted-foreground\">{'总寿命'}</span>{' '}\n {detailTool.total_life}\n </div>\n <div>\n <span className=\"text-muted-foreground\">{'已用次数'}</span>{' '}\n {detailTool.used_count}\n </div>\n <div>\n <span className=\"text-muted-foreground\">{'剩余寿命'}</span>{' '}\n {detailTool.remain_life}\n </div>\n <div>\n <span className=\"text-muted-foreground\">{'预警阈值'}</span>{' '}\n {detailTool.warning_threshold}\n </div>\n <div>\n <span className=\"text-muted-foreground\">{'原值'}</span> ¥\n {Number(detailTool.original_cost).toFixed(2)}\n </div>\n <div>\n <span className=\"text-muted-foreground\">{'累计折旧'}</span> ¥\n {Number(detailTool.accumulated_cost).toFixed(2)}\n </div>\n <div>\n <span className=\"text-muted-foreground\">{'净值'}</span> ¥\n {Number(detailTool.net_value).toFixed(2)}\n </div>\n <div>\n <span className=\"text-muted-foreground\">{'单位成本'}</span> ¥\n {Number(detailTool.unit_cost).toFixed(4)}\n </div>\n </div>\n <Tabs defaultValue=\"usage\">\n <TabsList>\n <TabsTrigger value=\"usage\">使用记录</TabsTrigger>\n <TabsTrigger value=\"maintenance\">维修记录</TabsTrigger>\n </TabsList>\n <TabsContent value=\"usage\">\n <Table>\n <TableHeader>\n <TableRow>\n <TableHead>时间</TableHead>\n <TableHead>工单号</TableHead>\n <TableHead>工序</TableHead>\n <TableHead>{'使用时长'}</TableHead>\n <TableHead>{'日期'}</TableHead>\n <TableHead>操作人</TableHead>\n </TableRow>\n </TableHeader>\n <TableBody>\n {usageRecords.length === 0 ? (\n <TableRow>\n <TableCell\n colSpan={6}\n className=\"text-center text-muted-foreground py-4\"\n >\n 暂无记录\n </TableCell>\n </TableRow>\n ) : (\n usageRecords.map((r) => (\n <TableRow key={r.id}>\n <TableCell>{new Date(r.use_time).toLocaleString()}</TableCell>\n <TableCell>{r.work_order_no || '-'}</TableCell>\n <TableCell>{r.process_name || '-'}</TableCell>\n <TableCell>{r.use_count}</TableCell>\n <TableCell>¥{Number(r.amortized_cost).toFixed(4)}</TableCell>\n <TableCell>{r.operator_name || '-'}</TableCell>\n </TableRow>\n ))\n )}\n </TableBody>\n </Table>\n </TabsContent>\n <TabsContent value=\"maintenance\">\n <Table>\n <TableHeader>\n <TableRow>\n <TableHead>类型</TableHead>\n <TableHead>费用</TableHead>\n <TableHead>{'备注'}</TableHead>\n <TableHead>状态</TableHead>\n <TableHead>时间</TableHead>\n <TableHead>描述</TableHead>\n </TableRow>\n </TableHeader>\n <TableBody>\n {maintenanceRecords.length === 0 ? (\n <TableRow>\n <TableCell\n colSpan={6}\n className=\"text-center text-muted-foreground py-4\"\n >\n 暂无记录\n </TableCell>\n </TableRow>\n ) : (\n maintenanceRecords.map((r) => (\n <TableRow key={r.id}>\n <TableCell>{r.maintenance_type === 1 ? '维修' : '保养'}</TableCell>\n <TableCell>¥{Number(r.maintenance_cost).toFixed(2)}</TableCell>\n <TableCell>\n {r.life_before} → {r.life_after} (\n {r.life_adjustment >= 0 ? '+' : ''}\n {r.life_adjustment})\n </TableCell>\n <TableCell>\n {r.status === 1 ? (\n <Badge variant=\"outline\">进行中</Badge>\n ) : (\n <Badge>已完成</Badge>\n )}\n </TableCell>\n <TableCell>\n {r.start_time} ~ {r.end_time || '-'}\n </TableCell>\n <TableCell>{r.description || '-'}</TableCell>\n </TableRow>\n ))\n )}\n </TableBody>\n </Table>\n </TabsContent>\n </Tabs>\n </div>\n )}\n </DialogContent>\n </Dialog>\n\n {/* Usage Dialog */}\n <Dialog open={!!usageDialogTool} onOpenChange={(v) => !v && setUsageDialogTool(null)}>\n <DialogContent>\n <DialogHeader>\n <DialogTitle>\n {'使用记录'}\n {usageDialogTool?.tool_code}\n </DialogTitle>\n </DialogHeader>\n <div className=\"space-y-4\">\n <div>\n <Label>\n {'剩余寿命'}\n {usageDialogTool?.remain_life})\n </Label>\n <Input\n type=\"number\"\n value={usageForm.useCount}\n onChange={(e) => setUsageForm({ ...usageForm, useCount: Number(e.target.value) })}\n />\n </div>\n <div>\n <Label>{'使用时长'}</Label>\n <Input\n value={usageForm.workOrderNo}\n onChange={(e) => setUsageForm({ ...usageForm, workOrderNo: e.target.value })}\n />\n </div>\n <div>\n <Label>工序名称</Label>\n <Input\n value={usageForm.processName}\n onChange={(e) => setUsageForm({ ...usageForm, processName: e.target.value })}\n />\n </div>\n <div>\n <Label>备注</Label>\n <Input\n value={usageForm.remark}\n onChange={(e) => setUsageForm({ ...usageForm, remark: e.target.value })}\n />\n </div>\n </div>\n <DialogFooter>\n <Button variant=\"outline\" onClick={() => setUsageDialogTool(null)}>\n 取消\n </Button>\n <Button onClick={submitUsage}>确定</Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n\n {/* Maintenance Dialog */}\n <Dialog open={!!maintDialogTool} onOpenChange={(v) => !v && setMaintDialogTool(null)}>\n <DialogContent>\n <DialogHeader>\n <DialogTitle>\n {'保养记录'}\n {maintDialogTool?.tool_code}\n </DialogTitle>\n </DialogHeader>\n <div className=\"space-y-4\">\n <div className=\"flex items-center gap-4\">\n <Button\n variant={!maintForm.completeAction ? 'default' : 'outline'}\n onClick={() => setMaintForm({ ...maintForm, completeAction: false })}\n >\n {'确认保养'}\n </Button>\n <Button\n variant={maintForm.completeAction ? 'default' : 'outline'}\n onClick={() => setMaintForm({ ...maintForm, completeAction: true })}\n >\n {'取消'}\n </Button>\n </div>\n {!maintForm.completeAction ? (\n <>\n <div>\n <Label>维修类型</Label>\n <Select\n value={String(maintForm.maintenanceType)}\n onValueChange={(v) =>\n setMaintForm({ ...maintForm, maintenanceType: Number(v) })\n }\n >\n <SelectTrigger>\n <SelectValue />\n <SelectContent>\n <SelectItem value=\"1\">维修</SelectItem>\n <SelectItem value=\"2\">保养</SelectItem>\n </SelectContent>\n </SelectTrigger>\n </Select>\n </div>\n <div>\n <Label>{'备注'}</Label>\n <Textarea\n value={maintForm.description}\n onChange={(e) => setMaintForm({ ...maintForm, description: e.target.value })}\n />\n </div>\n </>\n ) : (\n <>\n <div>\n <Label>{'保养费用'}</Label>\n <Input\n type=\"number\"\n value={maintForm.maintenanceId}\n onChange={(e) =>\n setMaintForm({ ...maintForm, maintenanceId: Number(e.target.value) })\n }\n />\n </div>\n <div>\n <Label>维修费用</Label>\n <Input\n type=\"number\"\n step=\"0.01\"\n value={maintForm.maintenanceCost}\n onChange={(e) =>\n setMaintForm({ ...maintForm, maintenanceCost: Number(e.target.value) })\n }\n />\n </div>\n <div>\n <Label>{'经办人'}</Label>\n <Input\n type=\"number\"\n value={maintForm.lifeAfter}\n onChange={(e) =>\n setMaintForm({ ...maintForm, lifeAfter: Number(e.target.value) })\n }\n />\n </div>\n <div>\n <Label>{'备注'}</Label>\n <Input\n value={maintForm.description}\n onChange={(e) => setMaintForm({ ...maintForm, description: e.target.value })}\n />\n </div>\n </>\n )}\n </div>\n <DialogFooter>\n <Button variant=\"outline\" onClick={() => setMaintDialogTool(null)}>\n 取消\n </Button>\n <Button onClick={submitMaintenance}>确定</Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n\n {/* Scrap Dialog */}\n <Dialog open={!!scrapDialogTool} onOpenChange={(v) => !v && setScrapDialogTool(null)}>\n <DialogContent>\n <DialogHeader>\n <DialogTitle>\n {'刀具报废'}\n {scrapDialogTool?.tool_code}\n </DialogTitle>\n </DialogHeader>\n <div className=\"space-y-4\">\n <div className=\"flex items-center gap-2 text-orange-600\">\n <AlertTriangle className=\"h-5 w-5\" />\n <span>{'确认报废此刀具?报废后不可恢复。'}</span>\n </div>\n <div>\n <Label>报废原因</Label>\n <Textarea\n value={scrapForm.scrapReason}\n onChange={(e) => setScrapForm({ ...scrapForm, scrapReason: e.target.value })}\n />\n </div>\n </div>\n <DialogFooter>\n <Button variant=\"outline\" onClick={() => setScrapDialogTool(null)}>\n 取消\n </Button>\n <Button variant=\"destructive\" onClick={submitScrap}>\n 确认报废\n </Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n </div>\n </MainLayout>\n );\n}\n","usedDeprecatedRules":[]},{"filePath":"D:\\dcprint\\erp-project\\src\\app\\[locale]\\dcprint\\trace\\page.tsx","messages":[{"ruleId":"react-hooks/immutability","severity":1,"message":"Error: Cannot access variable before it is declared\n\n`fetchRecords` is accessed before it is declared, which prevents the earlier access from updating when this value changes over time.\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\dcprint\\trace\\page.tsx:79:5\n 77 |\n 78 | useEffect(() => {\n> 79 | fetchRecords();\n | ^^^^^^^^^^^^ `fetchRecords` accessed before it is declared\n 80 | qrInputRef.current?.focus();\n 81 | }, []);\n 82 |\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\dcprint\\trace\\page.tsx:83:3\n 81 | }, []);\n 82 |\n> 83 | const fetchRecords = async () => {\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n> 84 | try {\n | ^^^^^^^^^\n> 85 | const response = await authFetch('/api/dcprint/trace');\n | ^^^^^^^^^\n> 86 | const result = await response.json();\n | ^^^^^^^^^\n> 87 | if (result.success) {\n | ^^^^^^^^^\n> 88 | setRecords(result.data.list || []);\n | ^^^^^^^^^\n> 89 | }\n | ^^^^^^^^^\n> 90 | } catch {}\n | ^^^^^^^^^\n> 91 | };\n | ^^^^^ `fetchRecords` is declared here\n 92 |\n 93 | const handleScanQRCode = async () => {\n 94 | if (!qrCode.trim()) return;","line":79,"column":5,"nodeType":null,"endLine":79,"endColumn":17},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":86,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":86,"endColumn":43},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":87,"column":18,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":87,"endColumn":25},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction<TraceRecord[]>`.","line":88,"column":20,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":88,"endColumn":42},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":88,"column":27,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":88,"endColumn":31},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":104,"column":9,"nodeType":"AssignmentExpression","messageId":"anyAssignment","endLine":104,"endColumn":36},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":114,"column":11,"nodeType":"Property","messageId":"anyAssignment","endLine":114,"endColumn":28},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .ID on an `any` value.","line":114,"column":26,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":114,"endColumn":28},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":121,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":121,"endColumn":43},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":123,"column":18,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":123,"endColumn":25},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction<TraceResult | null>`.","line":124,"column":24,"nodeType":"MemberExpression","messageId":"unsafeArgument","endLine":124,"endColumn":35},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":124,"column":31,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":124,"endColumn":35},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction<string>`.","line":128,"column":18,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":128,"endColumn":44},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .message on an `any` value.","line":128,"column":25,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":128,"endColumn":32},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"打印功能待实现\"。建议使用: tc('text_vx4p8c')","line":151,"column":11,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":151,"endColumn":20},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"扫码追溯\"。建议使用: {tc('text_cx5i2w')}","line":161,"column":45,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":163,"endColumn":13},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"重置\"。建议使用: {tc('text_phz5')}","line":183,"column":59,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":185,"endColumn":19},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"追溯\"。建议使用: {tc('text_p3ki')}","line":187,"column":56,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":189,"endColumn":19},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"追溯结果\"。建议使用: {tc('text_immfaz')}","line":218,"column":32,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":218,"endColumn":36},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"打印追溯单\"。建议使用: {tc('text_um5lcq')}","line":225,"column":57,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":227,"endColumn":19},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"流程卡信息\"。建议使用: {tc('text_gqkiw5')}","line":235,"column":54,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":235,"endColumn":59},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"流程卡卡号\"。建议使用: {tc('text_gql1v1')}","line":240,"column":67,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":240,"endColumn":72},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"工单号\"。建议使用: {tc('text_e5qlj')}","line":244,"column":67,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":244,"endColumn":70},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"成品料号\"。建议使用: {tc('text_cq660f')}","line":248,"column":67,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":248,"endColumn":71},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"成品名称\"。建议使用: {tc('text_cq3e2c')}","line":252,"column":67,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":252,"endColumn":71},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"主材信息\"。建议使用: {tc('text_aaqm03')}","line":262,"column":54,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":262,"endColumn":58},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"标签编号\"。建议使用: {tc('text_dn1smw')}","line":267,"column":67,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":267,"endColumn":71},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"物料代号\"。建议使用: {tc('text_eurcg4')}","line":271,"column":67,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":271,"endColumn":71},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"物料名称\"。建议使用: {tc('text_eusfkj')}","line":277,"column":67,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":277,"endColumn":71},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"批号\"。建议使用: {tc('text_h7ku')}","line":289,"column":67,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":289,"endColumn":69},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"进料日期\"。建议使用: {tc('text_ikkk8o')}","line":299,"column":67,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":299,"endColumn":71},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"物料总数\"。建议使用: {tc('text_euue3p')}","line":331,"column":74,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":331,"endColumn":78},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"物料明细\"。建议使用: {tc('text_euvirs')}","line":344,"column":28,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":344,"endColumn":32},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"标签编号\"。建议使用: {tc('text_dn1smw')}","line":352,"column":36,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":352,"endColumn":40},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"物料代号\"。建议使用: {tc('text_eurcg4')}","line":354,"column":36,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":354,"endColumn":40},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"物料名称\"。建议使用: {tc('text_eusfkj')}","line":355,"column":36,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":355,"endColumn":40},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"批号\"。建议使用: {tc('text_h7ku')}","line":357,"column":36,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":357,"endColumn":38},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"进料日期\"。建议使用: {tc('text_ikkk8o')}","line":359,"column":36,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":359,"endColumn":40},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"暂无物料数据\"。建议使用: {tc('text_iia61g')}","line":365,"column":79,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":367,"endColumn":27},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"主材\"。建议使用: {tc('text_dvg5')}","line":375,"column":80,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":375,"endColumn":82},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"辅料\"。建议使用: {tc('text_oywk')}","line":377,"column":78,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":377,"endColumn":80},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"追溯记录\"。建议使用: {tc('text_imokfr')}","line":400,"column":24,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":400,"endColumn":28},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"流程卡卡号\"。建议使用: {tc('text_gql1v1')}","line":409,"column":32,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":409,"endColumn":37},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"工单号\"。建议使用: {tc('text_e5qlj')}","line":410,"column":32,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":410,"endColumn":35},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"成品料号\"。建议使用: {tc('text_cq660f')}","line":411,"column":32,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":411,"endColumn":36},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"操作员\"。建议使用: {tc('text_f5hc9')}","line":413,"column":32,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":413,"endColumn":35},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"追溯时间\"。建议使用: {tc('text_imig7k')}","line":414,"column":32,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":414,"endColumn":36},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"暂无数据\"。建议使用: {tc('text_dcv57g')}","line":420,"column":75,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":422,"endColumn":23},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"正向追溯\"。建议使用: {tc('text_dwm21s')}","line":433,"column":74,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":433,"endColumn":78},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"反向追溯\"。建议使用: {tc('text_axin92')}","line":435,"column":78,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":435,"endColumn":82}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":50,"fixableErrorCount":0,"fixableWarningCount":0,"source":"'use client';\n\nimport { authFetch } from '@/lib/auth-fetch';\nimport { useState, useRef, useEffect } from 'react';\nimport { MainLayout } from '@/components/layout';\nimport { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';\nimport {\n Table,\n TableBody,\n TableCell,\n TableHead,\n TableHeader,\n TableRow,\n} from '@/components/ui/table';\nimport { Button } from '@/components/ui/button';\nimport { Input } from '@/components/ui/input';\nimport { Badge } from '@/components/ui/badge';\nimport { Alert, AlertDescription } from '@/components/ui/alert';\nimport { QrCode, Search, Printer, CheckCircle, AlertCircle, RefreshCw } from 'lucide-react';\nimport { useTranslations } from 'next-intl';\n\n// 追溯结果类型\ninterface TraceResult {\n traceNo: string;\n card: {\n cardNo: string;\n workOrderNo: string;\n productCode?: string;\n productName?: string;\n };\n mainMaterial: {\n labelNo: string;\n materialCode?: string;\n materialName?: string;\n specification?: string;\n batchNo?: string;\n supplierName?: string;\n receiveDate?: string;\n };\n materials: {\n labelNo: string;\n materialType: string;\n materialCode?: string;\n materialName?: string;\n specification?: string;\n batchNo?: string;\n supplierName?: string;\n receiveDate?: string;\n quantity?: number;\n unit?: string;\n }[];\n}\n\n// 追溯记录类型\ninterface TraceRecord {\n id: number;\n traceNo: string;\n cardNo?: string;\n workOrderNo?: string;\n productCode?: string;\n traceType: string;\n operatorName?: string;\n traceTime?: string;\n}\n\nexport default function TracePage() {\n // 翻译钩子\n const tc = useTranslations('Common');\n\n const [qrCode, setQrCode] = useState('');\n const [traceResult, setTraceResult] = useState<TraceResult | null>(null);\n const [records, setRecords] = useState<TraceRecord[]>([]);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState('');\n const [success, setSuccess] = useState('');\n const qrInputRef = useRef<HTMLInputElement>(null);\n\n useEffect(() => {\n fetchRecords();\n qrInputRef.current?.focus();\n }, []);\n\n const fetchRecords = async () => {\n try {\n const response = await authFetch('/api/dcprint/trace');\n const result = await response.json();\n if (result.success) {\n setRecords(result.data.list || []);\n }\n } catch {}\n };\n\n const handleScanQRCode = async () => {\n if (!qrCode.trim()) return;\n\n setError('');\n setSuccess('');\n setLoading(true);\n\n try {\n // 解析二维码内容\n let qrData: Loose;\n try {\n qrData = JSON.parse(qrCode);\n } catch {\n qrData = { ID: qrCode, TYPE: '4' };\n }\n\n // 执行追溯查询\n const response = await authFetch('/api/dcprint/trace', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n cardNo: qrData.ID,\n traceType: 'forward',\n operatorId: 1,\n operatorName: '操作员',\n }),\n });\n\n const result = await response.json();\n\n if (result.success) {\n setTraceResult(result.data);\n setSuccess('追溯查询成功!');\n fetchRecords();\n } else {\n setError(result.message || '追溯查询失败');\n setTraceResult(null);\n }\n } catch {\n setError('追溯查询失败');\n setTraceResult(null);\n } finally {\n setLoading(false);\n setQrCode('');\n qrInputRef.current?.focus();\n }\n };\n\n const handleReset = () => {\n setQrCode('');\n setTraceResult(null);\n setError('');\n setSuccess('');\n qrInputRef.current?.focus();\n };\n\n const handlePrint = () => {\n // TODO: 实现打印功能\n alert('打印功能待实现');\n };\n\n return (\n <MainLayout title=\"物料追溯\">\n <div className=\"space-y-6\">\n {/* 扫码追溯区域 */}\n <Card>\n <CardHeader>\n <CardTitle className=\"flex items-center gap-2\">\n <QrCode className=\"h-5 w-5\" />\n 扫码追溯\n </CardTitle>\n <CardDescription>{tc('dcTraceScanDesc')}</CardDescription>\n </CardHeader>\n <CardContent>\n <div className=\"space-y-4\">\n {/* 二维码输入 */}\n <div className=\"flex gap-4\">\n <div className=\"flex-1\">\n <label className=\"text-sm font-medium mb-2 block\">{tc('dcQrInputLabel')}</label>\n <Input\n ref={qrInputRef}\n placeholder=\"请扫描流程卡二维码...\"\n value={qrCode}\n onChange={(e) => setQrCode(e.target.value)}\n onKeyDown={(e) => e.key === 'Enter' && handleScanQRCode()}\n disabled={loading}\n />\n </div>\n <div className=\"flex items-end gap-2\">\n <Button variant=\"outline\" onClick={handleReset}>\n <RefreshCw className=\"h-4 w-4 mr-2\" />\n 重置\n </Button>\n <Button onClick={handleScanQRCode} disabled={loading}>\n <Search className=\"h-4 w-4 mr-2\" />\n 追溯\n </Button>\n </div>\n </div>\n\n {/* 提示信息 */}\n {error && (\n <Alert variant=\"destructive\">\n <AlertCircle className=\"h-4 w-4\" />\n <AlertDescription>{error}</AlertDescription>\n </Alert>\n )}\n {success && (\n <Alert className=\"bg-green-50 border-green-200\">\n <CheckCircle className=\"h-4 w-4 text-green-600\" />\n <AlertDescription className=\"text-green-700\">{success}</AlertDescription>\n </Alert>\n )}\n </div>\n </CardContent>\n </Card>\n\n {/* 追溯结果 */}\n {traceResult && (\n <div className=\"space-y-6\">\n {/* 追溯单信息 */}\n <Card>\n <CardHeader>\n <div className=\"flex items-center justify-between\">\n <div>\n <CardTitle>追溯结果</CardTitle>\n <CardDescription>\n {tc('dcTraceNoPrefix')}\n {traceResult.traceNo}\n </CardDescription>\n </div>\n <Button onClick={handlePrint}>\n <Printer className=\"h-4 w-4 mr-2\" />\n 打印追溯单\n </Button>\n </div>\n </CardHeader>\n <CardContent>\n <div className=\"grid grid-cols-1 md:grid-cols-3 gap-6\">\n {/* 流程卡信息 */}\n <Card>\n <CardHeader className=\"pb-3\">\n <CardTitle className=\"text-sm\">流程卡信息</CardTitle>\n </CardHeader>\n <CardContent>\n <div className=\"space-y-2\">\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">流程卡卡号</span>\n <span className=\"font-medium\">{traceResult.card.cardNo}</span>\n </div>\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">工单号</span>\n <span className=\"font-medium\">{traceResult.card.workOrderNo}</span>\n </div>\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">成品料号</span>\n <span className=\"font-medium\">{traceResult.card.productCode}</span>\n </div>\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">成品名称</span>\n <span className=\"font-medium\">{traceResult.card.productName}</span>\n </div>\n </div>\n </CardContent>\n </Card>\n\n {/* 主材信息 */}\n <Card className=\"border-green-200\">\n <CardHeader className=\"pb-3\">\n <CardTitle className=\"text-sm\">主材信息</CardTitle>\n </CardHeader>\n <CardContent>\n <div className=\"space-y-2\">\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">标签编号</span>\n <span className=\"font-medium\">{traceResult.mainMaterial.labelNo}</span>\n </div>\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">物料代号</span>\n <span className=\"font-medium\">\n {traceResult.mainMaterial.materialCode}\n </span>\n </div>\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">物料名称</span>\n <span className=\"font-medium\">\n {traceResult.mainMaterial.materialName}\n </span>\n </div>\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">{tc('specification')}</span>\n <span className=\"font-medium\">\n {traceResult.mainMaterial.specification}\n </span>\n </div>\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">批号</span>\n <span className=\"font-medium\">{traceResult.mainMaterial.batchNo}</span>\n </div>\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">{tc('supplier')}</span>\n <span className=\"font-medium\">\n {traceResult.mainMaterial.supplierName}\n </span>\n </div>\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">进料日期</span>\n <span className=\"font-medium\">\n {traceResult.mainMaterial.receiveDate}\n </span>\n </div>\n </div>\n </CardContent>\n </Card>\n\n {/* 统计信息 */}\n <Card>\n <CardHeader className=\"pb-3\">\n <CardTitle className=\"text-sm\">{tc('dcStatsTitle')}</CardTitle>\n </CardHeader>\n <CardContent>\n <div className=\"space-y-4\">\n <div className=\"bg-muted p-3 rounded-lg\">\n <div className=\"text-sm text-muted-foreground\">{tc('dcMainCount')}</div>\n <div className=\"text-2xl font-bold text-green-600\">\n {traceResult.materials.filter((m) => m.materialType === 'main').length}\n </div>\n </div>\n <div className=\"bg-muted p-3 rounded-lg\">\n <div className=\"text-sm text-muted-foreground\">{tc('dcAuxCount')}</div>\n <div className=\"text-2xl font-bold text-blue-600\">\n {\n traceResult.materials.filter((m) => m.materialType === 'auxiliary')\n .length\n }\n </div>\n </div>\n <div className=\"bg-muted p-3 rounded-lg\">\n <div className=\"text-sm text-muted-foreground\">物料总数</div>\n <div className=\"text-2xl font-bold\">{traceResult.materials.length}</div>\n </div>\n </div>\n </CardContent>\n </Card>\n </div>\n </CardContent>\n </Card>\n\n {/* 物料明细 */}\n <Card>\n <CardHeader>\n <CardTitle>物料明细</CardTitle>\n <CardDescription>{tc('dcMaterialDetailDesc')}</CardDescription>\n </CardHeader>\n <CardContent>\n <div className=\"border rounded-lg\">\n <Table>\n <TableHeader>\n <TableRow>\n <TableHead>标签编号</TableHead>\n <TableHead>{tc('type')}</TableHead>\n <TableHead>物料代号</TableHead>\n <TableHead>物料名称</TableHead>\n <TableHead>{tc('specification')}</TableHead>\n <TableHead>批号</TableHead>\n <TableHead>{tc('supplier')}</TableHead>\n <TableHead>进料日期</TableHead>\n </TableRow>\n </TableHeader>\n <TableBody>\n {traceResult.materials.length === 0 ? (\n <TableRow>\n <TableCell colSpan={8} className=\"text-center py-8\">\n 暂无物料数据\n </TableCell>\n </TableRow>\n ) : (\n traceResult.materials.map((material, index) => (\n <TableRow key={index}>\n <TableCell className=\"font-medium\">{material.labelNo}</TableCell>\n <TableCell>\n {material.materialType === 'main' ? (\n <Badge className=\"bg-green-100 text-green-700\">主材</Badge>\n ) : (\n <Badge className=\"bg-blue-100 text-blue-700\">辅料</Badge>\n )}\n </TableCell>\n <TableCell>{material.materialCode}</TableCell>\n <TableCell>{material.materialName}</TableCell>\n <TableCell>{material.specification}</TableCell>\n <TableCell>{material.batchNo}</TableCell>\n <TableCell>{material.supplierName}</TableCell>\n <TableCell>{material.receiveDate}</TableCell>\n </TableRow>\n ))\n )}\n </TableBody>\n </Table>\n </div>\n </CardContent>\n </Card>\n </div>\n )}\n\n {/* 追溯记录列表 */}\n <Card>\n <CardHeader>\n <CardTitle>追溯记录</CardTitle>\n <CardDescription>{tc('dcTraceRecordDesc')}</CardDescription>\n </CardHeader>\n <CardContent>\n <div className=\"border rounded-lg\">\n <Table>\n <TableHeader>\n <TableRow>\n <TableHead>{tc('dcTraceNoHead')}</TableHead>\n <TableHead>流程卡卡号</TableHead>\n <TableHead>工单号</TableHead>\n <TableHead>成品料号</TableHead>\n <TableHead>{tc('dcTraceTypeHead')}</TableHead>\n <TableHead>操作员</TableHead>\n <TableHead>追溯时间</TableHead>\n </TableRow>\n </TableHeader>\n <TableBody>\n {records.length === 0 ? (\n <TableRow>\n <TableCell colSpan={7} className=\"text-center py-8\">\n 暂无数据\n </TableCell>\n </TableRow>\n ) : (\n records.map((record) => (\n <TableRow key={record.id}>\n <TableCell className=\"font-medium\">{record.traceNo}</TableCell>\n <TableCell>{record.cardNo}</TableCell>\n <TableCell>{record.workOrderNo}</TableCell>\n <TableCell>{record.productCode}</TableCell>\n <TableCell>\n {record.traceType === 'forward' ? (\n <Badge className=\"bg-blue-100 text-blue-700\">正向追溯</Badge>\n ) : (\n <Badge className=\"bg-purple-100 text-purple-700\">反向追溯</Badge>\n )}\n </TableCell>\n <TableCell>{record.operatorName}</TableCell>\n <TableCell>{record.traceTime}</TableCell>\n </TableRow>\n ))\n )}\n </TableBody>\n </Table>\n </div>\n </CardContent>\n </Card>\n </div>\n </MainLayout>\n );\n}\n","usedDeprecatedRules":[]},{"filePath":"D:\\dcprint\\erp-project\\src\\app\\[locale]\\delivery\\error.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\dcprint\\erp-project\\src\\app\\[locale]\\delivery\\vehicles\\new\\page.tsx","messages":[{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":87,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":87,"endColumn":43},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":89,"column":18,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":89,"endColumn":25},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `string | number | bigint | boolean | ReactElement<unknown, string | JSXElementConstructor<any>> | Iterable<ReactNode> | ... 4 more ... | undefined`.","line":93,"column":21,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":93,"endColumn":56},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .message on an `any` value.","line":93,"column":28,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":93,"endColumn":35},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":103,"column":39,"nodeType":"Property","messageId":"anyAssignment","endLine":103,"endColumn":53}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":5,"fixableErrorCount":0,"fixableWarningCount":0,"source":"'use client';\n\nimport { authFetch } from '@/lib/auth-fetch';\nimport { useState } from 'react';\nimport { useRouter } from '@/i18n/navigation';\nimport { MainLayout } from '@/components/layout';\nimport { Button } from '@/components/ui/button';\nimport { Input } from '@/components/ui/input';\nimport { Label } from '@/components/ui/label';\nimport { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@/components/ui/select';\nimport { ArrowLeft, Save, Car } from 'lucide-react';\nimport { toast } from 'sonner';\nimport { useTranslations } from 'next-intl';\n\ninterface VehicleForm {\n vehicle_no: string;\n vehicle_type: string;\n brand: string;\n model: string;\n color: string;\n engine_no: string;\n frame_no: string;\n buy_date: string;\n mileage: number;\n fuel_type: string;\n capacity: number;\n status: number;\n driver_name: string;\n driver_phone: string;\n insurance_expire: string;\n annual_inspect_expire: string;\n remark: string;\n}\n\nconst initialForm: VehicleForm = {\n vehicle_no: '',\n vehicle_type: '',\n brand: '',\n model: '',\n color: '',\n engine_no: '',\n frame_no: '',\n buy_date: '',\n mileage: 0,\n fuel_type: '',\n capacity: 0,\n status: 1,\n driver_name: '',\n driver_phone: '',\n insurance_expire: '',\n annual_inspect_expire: '',\n remark: '',\n};\n\nexport default function NewVehiclePage() {\n // 翻译钩子\n const t = useTranslations('Delivery');\n const tc = useTranslations('Common');\n\n const router = useRouter();\n const [formData, setFormData] = useState<VehicleForm>(initialForm);\n const [saving, setSaving] = useState(false);\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault();\n\n if (!formData.vehicle_no) {\n toast.error(t('inputPlateNo'));\n return;\n }\n\n try {\n setSaving(true);\n const response = await authFetch('/api/delivery/vehicles', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(formData),\n });\n\n const result = await response.json();\n\n if (result.success) {\n toast.success(t('createSuccess'));\n router.push('/delivery/vehicles');\n } else {\n toast.error(result.message || t('createFailed'));\n }\n } catch {\n toast.error(t('createFailed'));\n } finally {\n setSaving(false);\n }\n };\n\n const updateField = (field: keyof VehicleForm, value: Loose) => {\n setFormData((prev) => ({ ...prev, [field]: value }));\n };\n\n return (\n <MainLayout>\n <div className=\"container mx-auto py-6 max-w-4xl\">\n <div className=\"flex items-center justify-between mb-6\">\n <div className=\"flex items-center gap-4\">\n <Button variant=\"outline\" size=\"icon\" onClick={() => router.back()}>\n <ArrowLeft className=\"h-4 w-4\" />\n </Button>\n <div>\n <h1 className=\"text-2xl font-bold flex items-center gap-2\">\n <Car className=\"h-6 w-6\" />\n {t('addVehicle')}\n </h1>\n <p className=\"text-sm text-muted-foreground\">{t('newVehicleDesc')}</p>\n </div>\n </div>\n <Button onClick={handleSubmit} disabled={saving}>\n <Save className=\"h-4 w-4 mr-2\" />\n {saving ? t('saving') : tc('save')}\n </Button>\n </div>\n\n <form onSubmit={handleSubmit} className=\"space-y-6\">\n <Card>\n <CardHeader>\n <CardTitle>{t('basicInfo')}</CardTitle>\n </CardHeader>\n <CardContent className=\"grid grid-cols-2 md:grid-cols-3 gap-4\">\n <div className=\"space-y-2\">\n <Label htmlFor=\"vehicle_no\">\n {t('plateNo')} <span className=\"text-red-500\">*</span>\n </Label>\n <Input\n id=\"vehicle_no\"\n value={formData.vehicle_no}\n onChange={(e) => updateField('vehicle_no', e.target.value)}\n placeholder={t('plateNoPlaceholder')}\n />\n </div>\n <div className=\"space-y-2\">\n <Label>{t('vehicleType')}</Label>\n <Select\n value={formData.vehicle_type}\n onValueChange={(v) => updateField('vehicle_type', v)}\n >\n <SelectTrigger>\n <SelectValue placeholder={t('selectType')} />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"truck\">{t('truck')}</SelectItem>\n <SelectItem value=\"van\">{t('van')}</SelectItem>\n <SelectItem value=\"sedan\">{t('sedan')}</SelectItem>\n <SelectItem value=\"pickup\">{t('pickup')}</SelectItem>\n <SelectItem value=\"other\">{t('otherType')}</SelectItem>\n </SelectContent>\n </Select>\n </div>\n <div className=\"space-y-2\">\n <Label>{tc('status')}</Label>\n <Select\n value={formData.status.toString()}\n onValueChange={(v) => updateField('status', parseInt(v))}\n >\n <SelectTrigger>\n <SelectValue placeholder={t('selectStatus')} />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"1\">{tc('enabled')}</SelectItem>\n <SelectItem value=\"2\">{t('underRepair')}</SelectItem>\n <SelectItem value=\"0\">{tc('disabled')}</SelectItem>\n <SelectItem value=\"3\">{t('scrapped')}</SelectItem>\n </SelectContent>\n </Select>\n </div>\n <div className=\"space-y-2\">\n <Label>{t('brand')}</Label>\n <Input\n value={formData.brand}\n onChange={(e) => updateField('brand', e.target.value)}\n placeholder={t('brandPlaceholder')}\n />\n </div>\n <div className=\"space-y-2\">\n <Label>{t('vehicleModel')}</Label>\n <Input\n value={formData.model}\n onChange={(e) => updateField('model', e.target.value)}\n placeholder={t('modelPlaceholder')}\n />\n </div>\n <div className=\"space-y-2\">\n <Label>{t('color')}</Label>\n <Input\n value={formData.color}\n onChange={(e) => updateField('color', e.target.value)}\n placeholder={t('colorPlaceholder')}\n />\n </div>\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle>{t('techParams')}</CardTitle>\n </CardHeader>\n <CardContent className=\"grid grid-cols-2 md:grid-cols-3 gap-4\">\n <div className=\"space-y-2\">\n <Label>{t('engineNo')}</Label>\n <Input\n value={formData.engine_no}\n onChange={(e) => updateField('engine_no', e.target.value)}\n />\n </div>\n <div className=\"space-y-2\">\n <Label>{t('frameNo')}</Label>\n <Input\n value={formData.frame_no}\n onChange={(e) => updateField('frame_no', e.target.value)}\n />\n </div>\n <div className=\"space-y-2\">\n <Label>{t('buyDate')}</Label>\n <Input\n type=\"date\"\n value={formData.buy_date}\n onChange={(e) => updateField('buy_date', e.target.value)}\n />\n </div>\n <div className=\"space-y-2\">\n <Label>{t('currentMileage')}</Label>\n <Input\n type=\"number\"\n value={formData.mileage}\n onChange={(e) => updateField('mileage', parseInt(e.target.value) || 0)}\n />\n </div>\n <div className=\"space-y-2\">\n <Label>{t('fuelType')}</Label>\n <Select\n value={formData.fuel_type}\n onValueChange={(v) => updateField('fuel_type', v)}\n >\n <SelectTrigger>\n <SelectValue placeholder={t('selectFuelType')} />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"gasoline\">{t('gasoline')}</SelectItem>\n <SelectItem value=\"diesel\">{t('diesel')}</SelectItem>\n <SelectItem value=\"electric\">{t('electric')}</SelectItem>\n <SelectItem value=\"hybrid\">{t('hybrid')}</SelectItem>\n </SelectContent>\n </Select>\n </div>\n <div className=\"space-y-2\">\n <Label>{t('capacityLabel')}</Label>\n <Input\n type=\"number\"\n step=\"0.01\"\n value={formData.capacity}\n onChange={(e) => updateField('capacity', parseFloat(e.target.value) || 0)}\n />\n </div>\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle>{t('driverInfo')}</CardTitle>\n </CardHeader>\n <CardContent className=\"grid grid-cols-2 gap-4\">\n <div className=\"space-y-2\">\n <Label>{t('defaultDriver')}</Label>\n <Input\n value={formData.driver_name}\n onChange={(e) => updateField('driver_name', e.target.value)}\n placeholder={t('driverName')}\n />\n </div>\n <div className=\"space-y-2\">\n <Label>{t('driverPhone')}</Label>\n <Input\n value={formData.driver_phone}\n onChange={(e) => updateField('driver_phone', e.target.value)}\n placeholder={t('contactPhone')}\n />\n </div>\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle>{t('insuranceAndInspect')}</CardTitle>\n </CardHeader>\n <CardContent className=\"grid grid-cols-2 gap-4\">\n <div className=\"space-y-2\">\n <Label>{t('insuranceExpireDate')}</Label>\n <Input\n type=\"date\"\n value={formData.insurance_expire}\n onChange={(e) => updateField('insurance_expire', e.target.value)}\n />\n </div>\n <div className=\"space-y-2\">\n <Label>{t('annualInspectExpireDate')}</Label>\n <Input\n type=\"date\"\n value={formData.annual_inspect_expire}\n onChange={(e) => updateField('annual_inspect_expire', e.target.value)}\n />\n </div>\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle>{tc('remark')}</CardTitle>\n </CardHeader>\n <CardContent>\n <textarea\n className=\"w-full min-h-[100px] p-3 border rounded-md\"\n value={formData.remark}\n onChange={(e) => updateField('remark', e.target.value)}\n placeholder={t('remarkPlaceholder')}\n />\n </CardContent>\n </Card>\n </form>\n </div>\n </MainLayout>\n );\n}\n","usedDeprecatedRules":[]},{"filePath":"D:\\dcprint\\erp-project\\src\\app\\[locale]\\delivery\\vehicles\\page.tsx","messages":[{"ruleId":"react-hooks/immutability","severity":1,"message":"Error: Cannot access variable before it is declared\n\n`fetchVehicles` is accessed before it is declared, which prevents the earlier access from updating when this value changes over time.\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\delivery\\vehicles\\page.tsx:70:5\n 68 |\n 69 | useEffect(() => {\n> 70 | fetchVehicles();\n | ^^^^^^^^^^^^^ `fetchVehicles` accessed before it is declared\n 71 | }, [page, status, keyword]);\n 72 |\n 73 | const fetchVehicles = async () => {\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\delivery\\vehicles\\page.tsx:73:3\n 71 | }, [page, status, keyword]);\n 72 |\n> 73 | const fetchVehicles = async () => {\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n> 74 | try {\n | ^^^^^^^^^\n> 75 | setLoading(true);\n …\n | ^^^^^^^^^\n> 95 | }\n | ^^^^^^^^^\n> 96 | };\n | ^^^^^ `fetchVehicles` is declared here\n 97 |\n 98 | const handleDelete = async (id: number) => {\n 99 | if (!confirm(t('confirmDeleteVehicle'))) return;","line":70,"column":5,"nodeType":null,"endLine":70,"endColumn":18},{"ruleId":"react-hooks/exhaustive-deps","severity":1,"message":"React Hook useEffect has a missing dependency: 'fetchVehicles'. Either include it or remove the dependency array.","line":71,"column":6,"nodeType":"ArrayExpression","endLine":71,"endColumn":29,"suggestions":[{"desc":"Update the dependencies array to be: [page, status, keyword, fetchVehicles]","fix":{"range":[1974,1997],"text":"[page, status, keyword, fetchVehicles]"}}]},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":83,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":83,"endColumn":43},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":85,"column":18,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":85,"endColumn":25},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction<Vehicle[]>`.","line":86,"column":21,"nodeType":"MemberExpression","messageId":"unsafeArgument","endLine":86,"endColumn":32},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":86,"column":28,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":86,"endColumn":32},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction<number>`.","line":87,"column":18,"nodeType":"MemberExpression","messageId":"unsafeArgument","endLine":87,"endColumn":41},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .pagination on an `any` value.","line":87,"column":25,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":87,"endColumn":35},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `string | number | bigint | boolean | ReactElement<unknown, string | JSXElementConstructor<any>> | Iterable<ReactNode> | ... 4 more ... | undefined`.","line":89,"column":21,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":89,"endColumn":55},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .message on an `any` value.","line":89,"column":28,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":89,"endColumn":35},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":105,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":105,"endColumn":43},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":107,"column":18,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":107,"endColumn":25},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `string | number | bigint | boolean | ReactElement<unknown, string | JSXElementConstructor<any>> | Iterable<ReactNode> | ... 4 more ... | undefined`.","line":111,"column":21,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":111,"endColumn":57},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .message on an `any` value.","line":111,"column":28,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":111,"endColumn":35}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":14,"fixableErrorCount":0,"fixableWarningCount":0,"source":"'use client';\n\nimport { authFetch } from '@/lib/auth-fetch';\nimport { useState, useEffect } from 'react';\nimport { useRouter } from '@/i18n/navigation';\nimport { MainLayout } from '@/components/layout';\nimport { Button } from '@/components/ui/button';\nimport { Input } from '@/components/ui/input';\nimport { Badge } from '@/components/ui/badge';\nimport {\n Table,\n TableBody,\n TableCell,\n TableHead,\n TableHeader,\n TableRow,\n} from '@/components/ui/table';\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuTrigger,\n} from '@/components/ui/dropdown-menu';\nimport { Plus, Search, MoreHorizontal, Edit, Trash2, Car, Wrench, FileText } from 'lucide-react';\nimport { toast } from 'sonner';\nimport { useTranslations } from 'next-intl';\n\ninterface Vehicle {\n id: number;\n vehicle_no: string;\n vehicle_type: string;\n brand: string;\n model: string;\n color: string;\n mileage: number;\n fuel_type: string;\n capacity: number;\n status: number;\n driver_name: string;\n driver_phone: string;\n insurance_expire: string;\n annual_inspect_expire: string;\n create_time: string;\n}\n\nexport default function VehiclesPage() {\n const t = useTranslations('Delivery');\n const tc = useTranslations('Common');\n\n const statusMap: Record<\n number,\n { label: string; variant: 'default' | 'secondary' | 'destructive' | 'outline' }\n > = {\n 0: { label: tc('disabled'), variant: 'secondary' },\n 1: { label: tc('enabled'), variant: 'default' },\n 2: { label: t('underRepair'), variant: 'outline' },\n 3: { label: t('scrapped'), variant: 'destructive' },\n };\n\n const router = useRouter();\n const [vehicles, setVehicles] = useState<Vehicle[]>([]);\n const [loading, setLoading] = useState(true);\n const [keyword, setKeyword] = useState('');\n const [status, setStatus] = useState('all');\n const [page, setPage] = useState(1);\n const [total, setTotal] = useState(0);\n const pageSize = 10;\n\n useEffect(() => {\n fetchVehicles();\n }, [page, status, keyword]);\n\n const fetchVehicles = async () => {\n try {\n setLoading(true);\n const params = new URLSearchParams();\n params.append('page', page.toString());\n params.append('pageSize', pageSize.toString());\n if (status !== 'all') params.append('status', status);\n if (keyword) params.append('keyword', keyword);\n\n const response = await authFetch(`/api/delivery/vehicles?${params}`);\n const result = await response.json();\n\n if (result.success) {\n setVehicles(result.data);\n setTotal(result.pagination.total);\n } else {\n toast.error(result.message || t('fetchFailed'));\n }\n } catch {\n toast.error(t('fetchFailed'));\n } finally {\n setLoading(false);\n }\n };\n\n const handleDelete = async (id: number) => {\n if (!confirm(t('confirmDeleteVehicle'))) return;\n\n try {\n const response = await authFetch(`/api/delivery/vehicles?id=${id}`, {\n method: 'DELETE',\n });\n const result = await response.json();\n\n if (result.success) {\n toast.success(tc('deleteSuccess'));\n fetchVehicles();\n } else {\n toast.error(result.message || tc('deleteFailed'));\n }\n } catch {\n toast.error(tc('deleteFailed'));\n }\n };\n\n const handleSearch = () => {\n setPage(1);\n fetchVehicles();\n };\n\n return (\n <MainLayout>\n <div className=\"container mx-auto py-6\">\n <div className=\"flex items-center justify-between mb-6\">\n <div>\n <h1 className=\"text-2xl font-bold flex items-center gap-2\">\n <Car className=\"h-6 w-6\" />\n {t('vehicleManagement')}\n </h1>\n <p className=\"text-sm text-muted-foreground mt-1\">{t('vehicleManagementDesc')}</p>\n </div>\n <Button onClick={() => router.push('/delivery/vehicles/new')}>\n <Plus className=\"h-4 w-4 mr-2\" />\n {t('addVehicle')}\n </Button>\n </div>\n\n <div className=\"flex gap-4 mb-6\">\n <div className=\"flex-1 relative\">\n <Search className=\"absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground\" />\n <Input\n placeholder={t('searchVehiclePlaceholder')}\n value={keyword}\n onChange={(e) => setKeyword(e.target.value)}\n onKeyDown={(e) => e.key === 'Enter' && handleSearch()}\n className=\"pl-10\"\n />\n </div>\n <select\n value={status}\n onChange={(e) => {\n setStatus(e.target.value);\n setPage(1);\n }}\n className=\"border rounded-md px-3 py-2\"\n >\n <option value=\"all\">{tc('all')}</option>\n <option value=\"1\">{tc('enabled')}</option>\n <option value=\"2\">{t('underRepair')}</option>\n <option value=\"0\">{tc('disabled')}</option>\n <option value=\"3\">{t('scrapped')}</option>\n </select>\n <Button variant=\"outline\" onClick={handleSearch}>\n {tc('search')}\n </Button>\n </div>\n\n <div className=\"border rounded-lg\">\n <Table>\n <TableHeader>\n <TableRow>\n <TableHead>{t('plateNo')}</TableHead>\n <TableHead>{t('vehicleInfo')}</TableHead>\n <TableHead>{t('driver')}</TableHead>\n <TableHead>{t('mileage')}</TableHead>\n <TableHead>{tc('status')}</TableHead>\n <TableHead>{t('insuranceExpire')}</TableHead>\n <TableHead>{t('annualInspectExpire')}</TableHead>\n <TableHead className=\"w-[100px]\">{tc('actions')}</TableHead>\n </TableRow>\n </TableHeader>\n <TableBody>\n {loading ? (\n <TableRow>\n <TableCell colSpan={8} className=\"text-center py-8\">\n {tc('loading')}\n </TableCell>\n </TableRow>\n ) : vehicles.length === 0 ? (\n <TableRow>\n <TableCell colSpan={8} className=\"text-center py-8 text-muted-foreground\">\n {tc('noData')}\n </TableCell>\n </TableRow>\n ) : (\n vehicles.map((vehicle) => (\n <TableRow key={vehicle.id}>\n <TableCell className=\"font-medium\">{vehicle.vehicle_no}</TableCell>\n <TableCell>\n <div className=\"text-sm\">\n <div>\n {vehicle.brand} {vehicle.model}\n </div>\n <div className=\"text-muted-foreground\">\n {vehicle.vehicle_type} · {vehicle.color} · {vehicle.fuel_type}\n </div>\n </div>\n </TableCell>\n <TableCell>\n <div className=\"text-sm\">\n <div>{vehicle.driver_name || '-'}</div>\n {vehicle.driver_phone && (\n <div className=\"text-muted-foreground text-xs\">\n {vehicle.driver_phone}\n </div>\n )}\n </div>\n </TableCell>\n <TableCell>{vehicle.mileage?.toLocaleString()} km</TableCell>\n <TableCell>\n <Badge variant={statusMap[vehicle.status]?.variant || 'default'}>\n {statusMap[vehicle.status]?.label || tc('unknown')}\n </Badge>\n </TableCell>\n <TableCell>{vehicle.insurance_expire || '-'}</TableCell>\n <TableCell>{vehicle.annual_inspect_expire || '-'}</TableCell>\n <TableCell>\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button variant=\"ghost\" size=\"icon\">\n <MoreHorizontal className=\"h-4 w-4\" />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\">\n <DropdownMenuItem\n onClick={() => router.push(`/delivery/vehicles/${vehicle.id}`)}\n >\n <Edit className=\"h-4 w-4 mr-2\" />\n {tc('edit')}\n </DropdownMenuItem>\n <DropdownMenuItem\n onClick={() => router.push(`/delivery/vehicles/${vehicle.id}/repair`)}\n >\n <Wrench className=\"h-4 w-4 mr-2\" />\n {t('repairRecords')}\n </DropdownMenuItem>\n <DropdownMenuItem\n onClick={() => router.push(`/delivery/vehicles/${vehicle.id}/cost`)}\n >\n <FileText className=\"h-4 w-4 mr-2\" />\n {t('costRecords')}\n </DropdownMenuItem>\n <DropdownMenuItem\n onClick={() => handleDelete(vehicle.id)}\n className=\"text-red-600\"\n >\n <Trash2 className=\"h-4 w-4 mr-2\" />\n {tc('delete')}\n </DropdownMenuItem>\n </DropdownMenuContent>\n </DropdownMenu>\n </TableCell>\n </TableRow>\n ))\n )}\n </TableBody>\n </Table>\n </div>\n\n {total > pageSize && (\n <div className=\"flex items-center justify-between mt-4\">\n <div className=\"text-sm text-muted-foreground\">\n {t('paginationInfo', { total, page, pages: Math.ceil(total / pageSize) })}\n </div>\n <div className=\"flex gap-2\">\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={() => setPage((p) => Math.max(1, p - 1))}\n disabled={page === 1}\n >\n {tc('prevPage')}\n </Button>\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={() => setPage((p) => p + 1)}\n disabled={page >= Math.ceil(total / pageSize)}\n >\n {tc('nextPage')}\n </Button>\n </div>\n </div>\n )}\n </div>\n </MainLayout>\n );\n}\n","usedDeprecatedRules":[]},{"filePath":"D:\\dcprint\\erp-project\\src\\app\\[locale]\\engineering\\error.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\dcprint\\erp-project\\src\\app\\[locale]\\engineering\\sample-to-mass\\page.tsx","messages":[{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":100,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":100,"endColumn":38},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":101,"column":18,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":101,"endColumn":25},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction<{ id: number; customer_name: string; customer_code: string; }[]>`.","line":102,"column":22,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":102,"endColumn":60},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":102,"column":29,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":102,"endColumn":33},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":102,"column":50,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":102,"endColumn":54},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":116,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":116,"endColumn":38},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":117,"column":18,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":117,"endColumn":25},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction<TransferRecord[]>`.","line":118,"column":17,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":118,"endColumn":39},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":118,"column":24,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":118,"endColumn":28},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction<number>`.","line":119,"column":18,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":119,"endColumn":40},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":119,"column":25,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":119,"endColumn":29},{"ruleId":"react-hooks/set-state-in-effect","severity":1,"message":"Error: Calling setState synchronously within an effect can trigger cascading renders\n\nEffects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following:\n* Update external systems with the latest state from React.\n* Subscribe for updates from some external system, calling setState in a callback function when external state changes.\n\nCalling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect).\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\engineering\\sample-to-mass\\page.tsx:125:5\n 123 |\n 124 | useEffect(() => {\n> 125 | fetchData();\n | ^^^^^^^^^ Avoid calling setState() directly within an effect\n 126 | fetchCustomers();\n 127 | }, [page]);\n 128 |","line":125,"column":5,"nodeType":null,"endLine":125,"endColumn":14},{"ruleId":"react-hooks/exhaustive-deps","severity":1,"message":"React Hook useEffect has a missing dependency: 'fetchData'. Either include it or remove the dependency array.","line":127,"column":6,"nodeType":"ArrayExpression","endLine":127,"endColumn":12,"suggestions":[{"desc":"Update the dependencies array to be: [fetchData, page]","fix":{"range":[3694,3700],"text":"[fetchData, page]"}}]},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":137,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":137,"endColumn":38},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":138,"column":18,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":138,"endColumn":25},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":143,"column":37,"nodeType":"Property","messageId":"anyAssignment","endLine":143,"endColumn":64},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .message on an `any` value.","line":143,"column":57,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":143,"endColumn":64},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":154,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":154,"endColumn":38},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":155,"column":18,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":155,"endColumn":25},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":171,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":171,"endColumn":38},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":172,"column":18,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":172,"endColumn":25},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":176,"column":37,"nodeType":"Property","messageId":"anyAssignment","endLine":176,"endColumn":64},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .message on an `any` value.","line":176,"column":57,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":176,"endColumn":64}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":23,"fixableErrorCount":0,"fixableWarningCount":0,"source":"'use client';\n\nimport { authFetch } from '@/lib/auth-fetch';\nimport { useEffect, useState } from 'react';\nimport { MainLayout } from '@/components/layout';\nimport { Card, CardContent } from '@/components/ui/card';\nimport { Button } from '@/components/ui/button';\nimport { Input } from '@/components/ui/input';\nimport { Badge } from '@/components/ui/badge';\nimport {\n Table,\n TableBody,\n TableCell,\n TableHead,\n TableHeader,\n TableRow,\n} from '@/components/ui/table';\nimport {\n Dialog,\n DialogContent,\n DialogHeader,\n DialogTitle,\n DialogFooter,\n} from '@/components/ui/dialog';\nimport { Label } from '@/components/ui/label';\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@/components/ui/select';\nimport { Textarea } from '@/components/ui/textarea';\nimport { Plus, Search, Edit, Trash2, ArrowRightLeft } from 'lucide-react';\nimport { useToast } from '@/hooks/use-toast';\nimport { useTranslations } from 'next-intl';\n\ninterface TransferRecord {\n id?: number;\n transfer_no: string;\n sample_order_id?: number;\n sample_order_no: string;\n product_id?: number;\n product_code: string;\n product_name: string;\n customer_id?: number;\n customer_name: string;\n sample_params: string;\n mass_params: string;\n sop_file: string;\n bom_version: string;\n process_route: string;\n check_standard: string;\n special_note: string;\n sample_confirmer: string;\n sample_confirm_date: string;\n eng_confirmer: string;\n eng_confirm_date: string;\n prod_confirmer: string;\n prod_confirm_date: string;\n quality_confirmer: string;\n quality_confirm_date: string;\n status: number;\n remark: string;\n create_time: string;\n}\n\nexport default function SampleToMassPage() {\n const t = useTranslations('Engineering');\n const tc = useTranslations('Common');\n\n const statusMap: Record<\n number,\n { label: string; variant: 'default' | 'secondary' | 'destructive' | 'outline' }\n > = {\n 1: { label: tc('draft'), variant: 'outline' },\n 2: { label: t('sampleConfirmed'), variant: 'secondary' },\n 3: { label: t('engConfirmed'), variant: 'secondary' },\n 4: { label: t('prodConfirmed'), variant: 'secondary' },\n 5: { label: t('qualityConfirmed'), variant: 'secondary' },\n 6: { label: t('transferredToMass'), variant: 'default' },\n 7: { label: t('returned'), variant: 'destructive' },\n };\n\n const { toast } = useToast();\n const [list, setList] = useState<TransferRecord[]>([]);\n const [total, setTotal] = useState(0);\n const [page, setPage] = useState(1);\n const [searchProduct, setSearchProduct] = useState('');\n const [searchStatus, setSearchStatus] = useState('');\n const [showDialog, setShowDialog] = useState(false);\n const [editItem, setEditItem] = useState<Partial<TransferRecord>>({});\n const [customers, setCustomers] = useState<\n { id: number; customer_name: string; customer_code: string }[]\n >([]);\n\n const fetchCustomers = async () => {\n try {\n const res = await authFetch('/api/customers?pageSize=999');\n const result = await res.json();\n if (result.success) {\n setCustomers(result.data?.list || result.data || []);\n }\n } catch {}\n };\n\n const fetchData = async () => {\n try {\n const params = new URLSearchParams({\n page: String(page),\n pageSize: '20',\n productName: searchProduct,\n status: searchStatus,\n });\n const res = await authFetch('/api/engineering/sample-to-mass?' + params);\n const result = await res.json();\n if (result.success) {\n setList(result.data.list || []);\n setTotal(result.data.total || 0);\n }\n } catch {}\n };\n\n useEffect(() => {\n fetchData();\n fetchCustomers();\n }, [page]);\n\n const handleSave = async () => {\n try {\n const method = editItem.id ? 'PUT' : 'POST';\n const res = await authFetch('/api/engineering/sample-to-mass', {\n method,\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(editItem),\n });\n const result = await res.json();\n if (result.success) {\n toast({ title: editItem.id ? tc('updateSuccess') : tc('createSuccess') });\n setShowDialog(false);\n fetchData();\n } else {\n toast({ title: tc('error'), description: result.message, variant: 'destructive' });\n }\n } catch {\n toast({ title: tc('error'), variant: 'destructive' });\n }\n };\n\n const handleDelete = async (id: number) => {\n if (!confirm(tc('confirmDelete'))) return;\n try {\n const res = await authFetch('/api/engineering/sample-to-mass?id=' + id, { method: 'DELETE' });\n const result = await res.json();\n if (result.success) {\n toast({ title: tc('deleteSuccess') });\n fetchData();\n }\n } catch {\n toast({ title: tc('error'), variant: 'destructive' });\n }\n };\n\n const handleConfirm = async (id: number, currentStatus: number) => {\n try {\n const res = await authFetch('/api/engineering/sample-to-mass', {\n method: 'PUT',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ id, status: currentStatus + 1 }),\n });\n const result = await res.json();\n if (result.success) {\n toast({ title: t('confirmSuccess') });\n fetchData();\n } else {\n toast({ title: tc('error'), description: result.message, variant: 'destructive' });\n }\n } catch {\n toast({ title: tc('error'), variant: 'destructive' });\n }\n };\n\n return (\n <MainLayout title={t('sampleToMassManagement')}>\n <div className=\"p-6 space-y-6\">\n <Card>\n <CardContent className=\"pt-6\">\n <div className=\"flex items-center justify-between mb-4\">\n <div className=\"flex items-center gap-4\">\n <div className=\"relative\">\n <Search className=\"absolute left-2.5 top-2.5 h-4 w-4 text-muted-foreground\" />\n <Input\n placeholder={t('searchProductPlaceholder')}\n className=\"pl-8 w-60\"\n value={searchProduct}\n onChange={(e) => setSearchProduct(e.target.value)}\n />\n </div>\n <Select value={searchStatus} onValueChange={setSearchStatus}>\n <SelectTrigger className=\"w-40\">\n <SelectValue placeholder={t('statusFilter')} />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"all\">{tc('all')}</SelectItem>\n {Object.entries(statusMap).map(([k, v]) => (\n <SelectItem key={k} value={k}>\n {v.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n <Button variant=\"outline\" onClick={fetchData}>\n {tc('search')}\n </Button>\n </div>\n <Button\n onClick={() => {\n setEditItem({});\n setShowDialog(true);\n }}\n >\n <Plus className=\"h-4 w-4 mr-2\" />\n {t('newTransferRecord')}\n </Button>\n </div>\n\n <Table>\n <TableHeader>\n <TableRow>\n <TableHead>{t('transferNo')}</TableHead>\n <TableHead>{t('sampleOrderNo')}</TableHead>\n <TableHead>{t('productCode')}</TableHead>\n <TableHead>{t('productName')}</TableHead>\n <TableHead>{t('customerName')}</TableHead>\n <TableHead>{t('bomVersion')}</TableHead>\n <TableHead>{tc('status')}</TableHead>\n <TableHead>{tc('createTime')}</TableHead>\n <TableHead>{tc('actions')}</TableHead>\n </TableRow>\n </TableHeader>\n <TableBody>\n {list.map((item) => (\n <TableRow key={item.id}>\n <TableCell className=\"font-mono text-sm\">{item.transfer_no}</TableCell>\n <TableCell>{item.sample_order_no || '-'}</TableCell>\n <TableCell>{item.product_code || '-'}</TableCell>\n <TableCell>{item.product_name}</TableCell>\n <TableCell>{item.customer_name || '-'}</TableCell>\n <TableCell>{item.bom_version || '-'}</TableCell>\n <TableCell>\n <Badge variant={statusMap[item.status]?.variant || 'outline'}>\n {statusMap[item.status]?.label || tc('unknown')}\n </Badge>\n </TableCell>\n <TableCell>{item.create_time?.substring(0, 10)}</TableCell>\n <TableCell>\n <div className=\"flex gap-1\">\n {item.status < 6 && item.status !== 7 && (\n <Button\n size=\"sm\"\n variant=\"outline\"\n onClick={() => handleConfirm(item.id!, item.status)}\n >\n <ArrowRightLeft className=\"h-3 w-3 mr-1\" />\n {tc('confirm')}\n </Button>\n )}\n <Button\n size=\"sm\"\n variant=\"ghost\"\n onClick={() => {\n setEditItem(item);\n setShowDialog(true);\n }}\n >\n <Edit className=\"h-3 w-3\" />\n </Button>\n <Button size=\"sm\" variant=\"ghost\" onClick={() => handleDelete(item.id!)}>\n <Trash2 className=\"h-3 w-3\" />\n </Button>\n </div>\n </TableCell>\n </TableRow>\n ))}\n {list.length === 0 && (\n <TableRow>\n <TableCell colSpan={9} className=\"text-center py-8 text-muted-foreground\">\n {tc('noData')}\n </TableCell>\n </TableRow>\n )}\n </TableBody>\n </Table>\n\n <div className=\"flex items-center justify-between mt-4\">\n <span className=\"text-sm text-muted-foreground\">{tc('total', { count: total })}</span>\n <div className=\"flex gap-2\">\n <Button\n variant=\"outline\"\n size=\"sm\"\n disabled={page <= 1}\n onClick={() => setPage((p) => p - 1)}\n >\n {tc('prevPage')}\n </Button>\n <Button\n variant=\"outline\"\n size=\"sm\"\n disabled={page * 20 >= total}\n onClick={() => setPage((p) => p + 1)}\n >\n {tc('nextPage')}\n </Button>\n </div>\n </div>\n </CardContent>\n </Card>\n\n <Dialog open={showDialog} onOpenChange={setShowDialog}>\n <DialogContent className=\"max-w-2xl max-h-[80vh] overflow-y-auto\" resizable>\n <DialogHeader>\n <DialogTitle>\n {editItem.id ? t('editTransferRecord') : t('newTransferRecord')}\n </DialogTitle>\n </DialogHeader>\n <div className=\"grid grid-cols-2 gap-4 py-4\">\n <div>\n <Label>{t('sampleOrderNo')}</Label>\n <Input\n value={editItem.sample_order_no || ''}\n onChange={(e) => setEditItem({ ...editItem, sample_order_no: e.target.value })}\n />\n </div>\n <div>\n <Label>{t('productCode')}</Label>\n <Input\n value={editItem.product_code || ''}\n onChange={(e) => setEditItem({ ...editItem, product_code: e.target.value })}\n />\n </div>\n <div>\n <Label>{t('productName')} *</Label>\n <Input\n value={editItem.product_name || ''}\n onChange={(e) => setEditItem({ ...editItem, product_name: e.target.value })}\n />\n </div>\n <div>\n <Label>{t('customerName')}</Label>\n <Select\n value={editItem.customer_id ? String(editItem.customer_id) : ''}\n onValueChange={(v) => {\n const c = customers.find((c) => c.id === Number(v));\n setEditItem({\n ...editItem,\n customer_id: Number(v),\n customer_name: c?.customer_name || '',\n });\n }}\n >\n <SelectTrigger>\n <SelectValue placeholder={t('selectCustomer')} />\n </SelectTrigger>\n <SelectContent>\n {customers.map((c) => (\n <SelectItem key={c.id} value={String(c.id)}>\n {c.customer_name}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n <div>\n <Label>{t('bomVersion')}</Label>\n <Input\n value={editItem.bom_version || ''}\n onChange={(e) => setEditItem({ ...editItem, bom_version: e.target.value })}\n />\n </div>\n <div>\n <Label>{t('processRoute')}</Label>\n <Input\n value={editItem.process_route || ''}\n onChange={(e) => setEditItem({ ...editItem, process_route: e.target.value })}\n />\n </div>\n <div className=\"col-span-2\">\n <Label>{t('sampleParams')}</Label>\n <Textarea\n rows={3}\n value={editItem.sample_params || ''}\n onChange={(e) => setEditItem({ ...editItem, sample_params: e.target.value })}\n />\n </div>\n <div className=\"col-span-2\">\n <Label>{t('massParams')}</Label>\n <Textarea\n rows={3}\n value={editItem.mass_params || ''}\n onChange={(e) => setEditItem({ ...editItem, mass_params: e.target.value })}\n />\n </div>\n <div className=\"col-span-2\">\n <Label>{t('checkStandard')}</Label>\n <Textarea\n rows={2}\n value={editItem.check_standard || ''}\n onChange={(e) => setEditItem({ ...editItem, check_standard: e.target.value })}\n />\n </div>\n <div className=\"col-span-2\">\n <Label>{t('specialNote')}</Label>\n <Textarea\n rows={2}\n value={editItem.special_note || ''}\n onChange={(e) => setEditItem({ ...editItem, special_note: e.target.value })}\n />\n </div>\n <div className=\"col-span-2\">\n <Label>{tc('remark')}</Label>\n <Textarea\n rows={2}\n value={editItem.remark || ''}\n onChange={(e) => setEditItem({ ...editItem, remark: e.target.value })}\n />\n </div>\n </div>\n <DialogFooter>\n <Button variant=\"outline\" onClick={() => setShowDialog(false)}>\n {tc('cancel')}\n </Button>\n <Button onClick={handleSave}>{tc('save')}</Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n </div>\n </MainLayout>\n );\n}\n","usedDeprecatedRules":[]},{"filePath":"D:\\dcprint\\erp-project\\src\\app\\[locale]\\engineering\\sop\\page.tsx","messages":[{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":105,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":105,"endColumn":38},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":106,"column":18,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":106,"endColumn":25},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":107,"column":36,"nodeType":"Property","messageId":"anyAssignment","endLine":107,"endColumn":61},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":107,"column":53,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":107,"endColumn":57},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":110,"column":43,"nodeType":"Property","messageId":"anyAssignment","endLine":110,"endColumn":70},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .message on an `any` value.","line":110,"column":63,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":110,"endColumn":70},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":139,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":139,"endColumn":38},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":140,"column":18,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":140,"endColumn":25},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction<SOPRecord[]>`.","line":141,"column":17,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":141,"endColumn":39},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":141,"column":24,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":141,"endColumn":28},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction<number>`.","line":142,"column":18,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":142,"endColumn":40},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":142,"column":25,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":142,"endColumn":29},{"ruleId":"react-hooks/set-state-in-effect","severity":1,"message":"Error: Calling setState synchronously within an effect can trigger cascading renders\n\nEffects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following:\n* Update external systems with the latest state from React.\n* Subscribe for updates from some external system, calling setState in a callback function when external state changes.\n\nCalling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect).\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\engineering\\sop\\page.tsx:148:5\n 146 |\n 147 | useEffect(() => {\n> 148 | fetchData();\n | ^^^^^^^^^ Avoid calling setState() directly within an effect\n 149 | }, [page]);\n 150 |\n 151 | const handleSave = async () => {","line":148,"column":5,"nodeType":null,"endLine":148,"endColumn":14},{"ruleId":"react-hooks/exhaustive-deps","severity":1,"message":"React Hook useEffect has a missing dependency: 'fetchData'. Either include it or remove the dependency array.","line":149,"column":6,"nodeType":"ArrayExpression","endLine":149,"endColumn":12,"suggestions":[{"desc":"Update the dependencies array to be: [fetchData, page]","fix":{"range":[4515,4521],"text":"[fetchData, page]"}}]},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":159,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":159,"endColumn":38},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":160,"column":18,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":160,"endColumn":25},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":165,"column":37,"nodeType":"Property","messageId":"anyAssignment","endLine":165,"endColumn":64},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .message on an `any` value.","line":165,"column":57,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":165,"endColumn":64},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":176,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":176,"endColumn":38},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":177,"column":18,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":177,"endColumn":25}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":20,"fixableErrorCount":0,"fixableWarningCount":0,"source":"'use client';\r\n\r\nimport { authFetch } from '@/lib/auth-fetch';\r\nimport { useEffect, useState } from 'react';\r\nimport { MainLayout } from '@/components/layout';\r\nimport { Card, CardContent } from '@/components/ui/card';\r\nimport { Button } from '@/components/ui/button';\r\nimport { Input } from '@/components/ui/input';\r\nimport { Badge } from '@/components/ui/badge';\r\nimport {\r\n Table,\r\n TableBody,\r\n TableCell,\r\n TableHead,\r\n TableHeader,\r\n TableRow,\r\n} from '@/components/ui/table';\r\nimport {\r\n Dialog,\r\n DialogContent,\r\n DialogHeader,\r\n DialogTitle,\r\n DialogFooter,\r\n} from '@/components/ui/dialog';\r\nimport { Label } from '@/components/ui/label';\r\nimport {\r\n Select,\r\n SelectContent,\r\n SelectItem,\r\n SelectTrigger,\r\n SelectValue,\r\n} from '@/components/ui/select';\r\nimport { Textarea } from '@/components/ui/textarea';\r\nimport { Plus, Search, Edit, Trash2, FileText, Download } from 'lucide-react';\r\nimport { useToast } from '@/hooks/use-toast';\r\nimport { useTranslations } from 'next-intl';\r\n\r\ninterface SOPRecord {\r\n id?: number;\r\n sop_no: string;\r\n sop_name: string;\r\n product_id?: number;\r\n product_code: string;\r\n product_name: string;\r\n process_code: string;\r\n process_name: string;\r\n version: string;\r\n sop_type: string;\r\n content: string;\r\n file_url: string;\r\n workshop: string;\r\n equipment_type: string;\r\n effective_date: string;\r\n status: number;\r\n remark: string;\r\n create_time: string;\r\n}\r\n\r\nexport default function SOPManagementPage() {\r\n const t = useTranslations('Engineering');\r\n const tc = useTranslations('Common');\r\n\r\n const sopTypeMap: Record<string, string> = {\r\n printing: t('silkScreen'),\r\n die_cut: t('dieCut'),\r\n trademark: t('trademark'),\r\n inspection: t('inspection'),\r\n packaging: t('packaging'),\r\n other: tc('other'),\r\n };\r\n const workshopMap: Record<string, string> = {\r\n die_cut: t('dieCutWorkshop'),\r\n trademark: t('trademarkWorkshop'),\r\n printing: t('silkScreenWorkshop'),\r\n all: tc('all'),\r\n };\r\n const statusMap: Record<\r\n number,\r\n { label: string; variant: 'default' | 'secondary' | 'destructive' | 'outline' }\r\n > = {\r\n 1: { label: tc('draft'), variant: 'outline' },\r\n 2: { label: t('published'), variant: 'default' },\r\n 3: { label: t('obsolete'), variant: 'destructive' },\r\n };\r\n\r\n const { toast } = useToast();\r\n const [list, setList] = useState<SOPRecord[]>([]);\r\n const [total, setTotal] = useState(0);\r\n const [page, setPage] = useState(1);\r\n const [searchProduct, setSearchProduct] = useState('');\r\n const [searchType, setSearchType] = useState('');\r\n const [showDialog, setShowDialog] = useState(false);\r\n const [editItem, setEditItem] = useState<Partial<SOPRecord>>({});\r\n const [uploading, setUploading] = useState(false);\r\n const _fileInputRef = useState<HTMLInputElement | null>(null);\r\n\r\n const handleFileUpload = async (e: React.ChangeEvent<HTMLInputElement>) => {\r\n const file = e.target.files?.[0];\r\n if (!file) return;\r\n setUploading(true);\r\n try {\r\n const formData = new FormData();\r\n formData.append('file', file);\r\n const res = await authFetch('/api/upload/sop', { method: 'POST', body: formData });\r\n const result = await res.json();\r\n if (result.success) {\r\n setEditItem({ ...editItem, file_url: result.data.url });\r\n toast({ title: t('uploadSuccess') });\r\n } else {\r\n toast({ title: t('uploadFailed'), description: result.message, variant: 'destructive' });\r\n }\r\n } catch {\r\n toast({ title: t('uploadFailed'), variant: 'destructive' });\r\n } finally {\r\n setUploading(false);\r\n e.target.value = '';\r\n }\r\n };\r\n\r\n const handleDownload = (fileUrl: string, fileName: string) => {\r\n const link = document.createElement('a');\r\n link.href = fileUrl;\r\n link.download = fileName || t('defaultSopFileName');\r\n link.target = '_blank';\r\n document.body.appendChild(link);\r\n link.click();\r\n document.body.removeChild(link);\r\n };\r\n\r\n const fetchData = async () => {\r\n try {\r\n const params = new URLSearchParams({\r\n page: String(page),\r\n pageSize: '20',\r\n productName: searchProduct,\r\n sopType: searchType,\r\n });\r\n const res = await authFetch('/api/engineering/sop?' + params);\r\n const result = await res.json();\r\n if (result.success) {\r\n setList(result.data.list || []);\r\n setTotal(result.data.total || 0);\r\n }\r\n } catch {}\r\n };\r\n\r\n useEffect(() => {\r\n fetchData();\r\n }, [page]);\r\n\r\n const handleSave = async () => {\r\n try {\r\n const method = editItem.id ? 'PUT' : 'POST';\r\n const res = await authFetch('/api/engineering/sop', {\r\n method,\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify(editItem),\r\n });\r\n const result = await res.json();\r\n if (result.success) {\r\n toast({ title: editItem.id ? tc('updateSuccess') : tc('createSuccess') });\r\n setShowDialog(false);\r\n fetchData();\r\n } else {\r\n toast({ title: tc('error'), description: result.message, variant: 'destructive' });\r\n }\r\n } catch {\r\n toast({ title: tc('error'), variant: 'destructive' });\r\n }\r\n };\r\n\r\n const handleDelete = async (id: number) => {\r\n if (!confirm(tc('confirmDelete'))) return;\r\n try {\r\n const res = await authFetch('/api/engineering/sop?id=' + id, { method: 'DELETE' });\r\n const result = await res.json();\r\n if (result.success) {\r\n toast({ title: tc('deleteSuccess') });\r\n fetchData();\r\n }\r\n } catch {\r\n toast({ title: tc('error'), variant: 'destructive' });\r\n }\r\n };\r\n\r\n return (\r\n <MainLayout title={t('sopManagement')}>\r\n <div className=\"p-6 space-y-6\">\r\n <Card>\r\n <CardContent className=\"pt-6\">\r\n <div className=\"flex items-center justify-between mb-4\">\r\n <div className=\"flex items-center gap-4\">\r\n <div className=\"relative\">\r\n <Search className=\"absolute left-2.5 top-2.5 h-4 w-4 text-muted-foreground\" />\r\n <Input\r\n placeholder={t('searchProductPlaceholder')}\r\n className=\"pl-8 w-60\"\r\n value={searchProduct}\r\n onChange={(e) => setSearchProduct(e.target.value)}\r\n />\r\n </div>\r\n <Select value={searchType} onValueChange={setSearchType}>\r\n <SelectTrigger className=\"w-40\">\r\n <SelectValue placeholder={t('sopType')} />\r\n </SelectTrigger>\r\n <SelectContent>\r\n <SelectItem value=\"all\">{tc('all')}</SelectItem>\r\n {Object.entries(sopTypeMap).map(([k, v]) => (\r\n <SelectItem key={k} value={k}>\r\n {v}\r\n </SelectItem>\r\n ))}\r\n </SelectContent>\r\n </Select>\r\n <Button variant=\"outline\" onClick={fetchData}>\r\n {tc('search')}\r\n </Button>\r\n </div>\r\n <Button\r\n onClick={() => {\r\n setEditItem({ version: 'V1.0', sop_type: 'printing' });\r\n setShowDialog(true);\r\n }}\r\n >\r\n <Plus className=\"h-4 w-4 mr-2\" />\r\n {t('newSop')}\r\n </Button>\r\n </div>\r\n\r\n <Table>\r\n <TableHeader>\r\n <TableRow>\r\n <TableHead>{t('sopNo')}</TableHead>\r\n <TableHead>{t('sopName')}</TableHead>\r\n <TableHead>{t('productName')}</TableHead>\r\n <TableHead>{t('process')}</TableHead>\r\n <TableHead>{t('version')}</TableHead>\r\n <TableHead>{t('type')}</TableHead>\r\n <TableHead>{t('workshop')}</TableHead>\r\n <TableHead>{tc('status')}</TableHead>\r\n <TableHead>{t('effectiveDate')}</TableHead>\r\n <TableHead>{t('sopFile')}</TableHead>\r\n <TableHead>{tc('actions')}</TableHead>\r\n </TableRow>\r\n </TableHeader>\r\n <TableBody>\r\n {list.map((item) => (\r\n <TableRow key={item.id}>\r\n <TableCell className=\"font-mono text-sm\">{item.sop_no}</TableCell>\r\n <TableCell>{item.sop_name}</TableCell>\r\n <TableCell>{item.product_name}</TableCell>\r\n <TableCell>{item.process_name || '-'}</TableCell>\r\n <TableCell>{item.version}</TableCell>\r\n <TableCell>{sopTypeMap[item.sop_type] || item.sop_type}</TableCell>\r\n <TableCell>{workshopMap[item.workshop] || item.workshop || '-'}</TableCell>\r\n <TableCell>\r\n <Badge variant={statusMap[item.status]?.variant || 'outline'}>\r\n {statusMap[item.status]?.label || tc('unknown')}\r\n </Badge>\r\n </TableCell>\r\n <TableCell>{item.effective_date?.substring(0, 10) || '-'}</TableCell>\r\n <TableCell>\r\n {item.file_url ? (\r\n <Button\r\n size=\"sm\"\r\n variant=\"outline\"\r\n className=\"h-6 text-xs\"\r\n onClick={() => handleDownload(item.file_url, item.sop_name + '.pdf')}\r\n >\r\n <FileText className=\"h-3 w-3 mr-1\" />\r\n {t('viewPdf')}\r\n </Button>\r\n ) : (\r\n <span className=\"text-muted-foreground text-xs\">{tc('none')}</span>\r\n )}\r\n </TableCell>\r\n <TableCell>\r\n <div className=\"flex gap-1\">\r\n {item.file_url && (\r\n <Button\r\n size=\"sm\"\r\n variant=\"ghost\"\r\n title={t('downloadSopFile')}\r\n onClick={() => handleDownload(item.file_url, item.sop_name + '.pdf')}\r\n >\r\n <Download className=\"h-3 w-3\" />\r\n </Button>\r\n )}\r\n <Button\r\n size=\"sm\"\r\n variant=\"ghost\"\r\n onClick={() => {\r\n setEditItem(item);\r\n setShowDialog(true);\r\n }}\r\n >\r\n <Edit className=\"h-3 w-3\" />\r\n </Button>\r\n <Button size=\"sm\" variant=\"ghost\" onClick={() => handleDelete(item.id!)}>\r\n <Trash2 className=\"h-3 w-3\" />\r\n </Button>\r\n </div>\r\n </TableCell>\r\n </TableRow>\r\n ))}\r\n {list.length === 0 && (\r\n <TableRow>\r\n <TableCell colSpan={11} className=\"text-center py-8 text-muted-foreground\">\r\n {tc('noData')}\r\n </TableCell>\r\n </TableRow>\r\n )}\r\n </TableBody>\r\n </Table>\r\n\r\n <div className=\"flex items-center justify-between mt-4\">\r\n <span className=\"text-sm text-muted-foreground\">{tc('total', { count: total })}</span>\r\n <div className=\"flex gap-2\">\r\n <Button\r\n variant=\"outline\"\r\n size=\"sm\"\r\n disabled={page <= 1}\r\n onClick={() => setPage((p) => p - 1)}\r\n >\r\n {tc('prevPage')}\r\n </Button>\r\n <Button\r\n variant=\"outline\"\r\n size=\"sm\"\r\n disabled={page * 20 >= total}\r\n onClick={() => setPage((p) => p + 1)}\r\n >\r\n {tc('nextPage')}\r\n </Button>\r\n </div>\r\n </div>\r\n </CardContent>\r\n </Card>\r\n\r\n <Dialog open={showDialog} onOpenChange={setShowDialog}>\r\n <DialogContent className=\"max-w-2xl max-h-[80vh] overflow-y-auto\" resizable>\r\n <DialogHeader>\r\n <DialogTitle>{editItem.id ? t('editSop') : t('newSop')}</DialogTitle>\r\n </DialogHeader>\r\n <div className=\"grid grid-cols-2 gap-4 py-4\">\r\n <div>\r\n <Label>{t('productCode')}</Label>\r\n <Input\r\n value={editItem.product_code || ''}\r\n onChange={(e) => setEditItem({ ...editItem, product_code: e.target.value })}\r\n />\r\n </div>\r\n <div>\r\n <Label>{t('productName')} *</Label>\r\n <Input\r\n value={editItem.product_name || ''}\r\n onChange={(e) => setEditItem({ ...editItem, product_name: e.target.value })}\r\n />\r\n </div>\r\n <div>\r\n <Label>{t('processCode')}</Label>\r\n <Input\r\n value={editItem.process_code || ''}\r\n onChange={(e) => setEditItem({ ...editItem, process_code: e.target.value })}\r\n />\r\n </div>\r\n <div>\r\n <Label>{t('processName')}</Label>\r\n <Input\r\n value={editItem.process_name || ''}\r\n onChange={(e) => setEditItem({ ...editItem, process_name: e.target.value })}\r\n />\r\n </div>\r\n <div>\r\n <Label>{t('version')}</Label>\r\n <Input\r\n value={editItem.version || 'V1.0'}\r\n onChange={(e) => setEditItem({ ...editItem, version: e.target.value })}\r\n />\r\n </div>\r\n <div>\r\n <Label>{t('sopType')}</Label>\r\n <Select\r\n value={editItem.sop_type || 'printing'}\r\n onValueChange={(v) => setEditItem({ ...editItem, sop_type: v })}\r\n >\r\n <SelectTrigger>\r\n <SelectValue />\r\n </SelectTrigger>\r\n <SelectContent>\r\n {Object.entries(sopTypeMap).map(([k, v]) => (\r\n <SelectItem key={k} value={k}>\r\n {v}\r\n </SelectItem>\r\n ))}\r\n </SelectContent>\r\n </Select>\r\n </div>\r\n <div>\r\n <Label>{t('applicableWorkshop')}</Label>\r\n <Select\r\n value={editItem.workshop || 'die_cut'}\r\n onValueChange={(v) => setEditItem({ ...editItem, workshop: v })}\r\n >\r\n <SelectTrigger>\r\n <SelectValue />\r\n </SelectTrigger>\r\n <SelectContent>\r\n {Object.entries(workshopMap).map(([k, v]) => (\r\n <SelectItem key={k} value={k}>\r\n {v}\r\n </SelectItem>\r\n ))}\r\n </SelectContent>\r\n </Select>\r\n </div>\r\n <div>\r\n <Label>{t('equipmentType')}</Label>\r\n <Input\r\n value={editItem.equipment_type || ''}\r\n onChange={(e) => setEditItem({ ...editItem, equipment_type: e.target.value })}\r\n />\r\n </div>\r\n <div>\r\n <Label>{t('effectiveDate')}</Label>\r\n <Input\r\n type=\"date\"\r\n value={editItem.effective_date || ''}\r\n onChange={(e) => setEditItem({ ...editItem, effective_date: e.target.value })}\r\n />\r\n </div>\r\n <div>\r\n <Label>{t('sopPdf')}</Label>\r\n <div className=\"flex gap-2 items-center\">\r\n <Input\r\n type=\"file\"\r\n accept=\".pdf\"\r\n onChange={handleFileUpload}\r\n disabled={uploading}\r\n className=\"flex-1\"\r\n />\r\n {editItem.file_url && (\r\n <Button\r\n size=\"sm\"\r\n variant=\"outline\"\r\n onClick={() =>\r\n handleDownload(\r\n editItem.file_url!,\r\n editItem.sop_name || t('defaultSopFileName')\r\n )\r\n }\r\n >\r\n <Download className=\"h-3 w-3 mr-1\" />\r\n {tc('download')}\r\n </Button>\r\n )}\r\n </div>\r\n {editItem.file_url && (\r\n <p className=\"text-xs text-muted-foreground mt-1\">\r\n {t('uploaded')}: {editItem.file_url}\r\n </p>\r\n )}\r\n {uploading && <p className=\"text-xs text-blue-500 mt-1\">{t('uploading')}...</p>}\r\n </div>\r\n <div className=\"col-span-2\">\r\n <Label>{t('sopContent')}</Label>\r\n <Textarea\r\n rows={5}\r\n value={editItem.content || ''}\r\n onChange={(e) => setEditItem({ ...editItem, content: e.target.value })}\r\n />\r\n </div>\r\n <div className=\"col-span-2\">\r\n <Label>{tc('remark')}</Label>\r\n <Textarea\r\n rows={2}\r\n value={editItem.remark || ''}\r\n onChange={(e) => setEditItem({ ...editItem, remark: e.target.value })}\r\n />\r\n </div>\r\n </div>\r\n <DialogFooter>\r\n <Button variant=\"outline\" onClick={() => setShowDialog(false)}>\r\n {tc('cancel')}\r\n </Button>\r\n <Button onClick={handleSave}>{tc('save')}</Button>\r\n </DialogFooter>\r\n </DialogContent>\r\n </Dialog>\r\n </div>\r\n </MainLayout>\r\n );\r\n}\r\n","usedDeprecatedRules":[]},{"filePath":"D:\\dcprint\\erp-project\\src\\app\\[locale]\\equipment\\calibration\\page.tsx","messages":[{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"待检定\"。建议使用: tc('text_ehzq7')","line":46,"column":15,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":46,"endColumn":20},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"检定中\"。建议使用: tc('text_fscr7')","line":47,"column":15,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":47,"endColumn":20},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"已合格\"。建议使用: tc('text_e68iu')","line":48,"column":15,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":48,"endColumn":20},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"不合格\"。建议使用: tc('text_bufb5')","line":49,"column":15,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":49,"endColumn":20},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":72,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":72,"endColumn":38},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":73,"column":18,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":73,"endColumn":25},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction<Item[]>`.","line":74,"column":17,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":74,"endColumn":39},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":74,"column":24,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":74,"endColumn":28},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction<number>`.","line":75,"column":18,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":75,"endColumn":40},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":75,"column":25,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":75,"endColumn":29},{"ruleId":"react-hooks/set-state-in-effect","severity":1,"message":"Error: Calling setState synchronously within an effect can trigger cascading renders\n\nEffects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following:\n* Update external systems with the latest state from React.\n* Subscribe for updates from some external system, calling setState in a callback function when external state changes.\n\nCalling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect).\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\equipment\\calibration\\page.tsx:80:5\n 78 | };\n 79 | useEffect(() => {\n> 80 | fetchData();\n | ^^^^^^^^^ Avoid calling setState() directly within an effect\n 81 | }, [page]);\n 82 |\n 83 | const handleSave = async () => {","line":80,"column":5,"nodeType":null,"endLine":80,"endColumn":14},{"ruleId":"react-hooks/exhaustive-deps","severity":1,"message":"React Hook useEffect has a missing dependency: 'fetchData'. Either include it or remove the dependency array.","line":81,"column":6,"nodeType":"ArrayExpression","endLine":81,"endColumn":12,"suggestions":[{"desc":"Update the dependencies array to be: [fetchData, page]","fix":{"range":[2273,2279],"text":"[fetchData, page]"}}]},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":90,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":90,"endColumn":38},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":91,"column":18,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":91,"endColumn":25},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":98,"column":11,"nodeType":"Property","messageId":"anyAssignment","endLine":98,"endColumn":38},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .message on an `any` value.","line":98,"column":31,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":98,"endColumn":38},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":113,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":113,"endColumn":38},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":114,"column":18,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":114,"endColumn":25},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":126,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":126,"endColumn":38},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":127,"column":18,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":127,"endColumn":25},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"设备检定\"。建议使用: {tc('text_i02c2r')}","line":140,"column":46,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":140,"endColumn":50},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"新增检定\"。建议使用: {tc('text_d77o08')}","line":160,"column":48,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":162,"endColumn":13},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"检定单号\"。建议使用: {tc('text_dlgbks')}","line":170,"column":50,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":170,"endColumn":54},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"设备编码\"。建议使用: {tc('text_i06agk')}","line":171,"column":50,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":171,"endColumn":54},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"设备名称\"。建议使用: {tc('text_hzyzbg')}","line":172,"column":50,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":172,"endColumn":54},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"检定日期\"。建议使用: {tc('text_dljl10')}","line":173,"column":50,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":173,"endColumn":54},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"检定机构\"。建议使用: {tc('text_dljt9g')}","line":175,"column":50,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":175,"endColumn":54},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"开始检定\"。建议使用: {tc('text_cd0pnp')}","line":206,"column":30,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":208,"endColumn":29},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"合格\"。建议使用: {tc('text_ev5g')}","line":216,"column":30,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":218,"endColumn":29},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"暂无记录\"。建议使用: {tc('text_dd1mmb')}","line":246,"column":87,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":248,"endColumn":21},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"共\"。建议使用: {tc('text_g35')}","line":256,"column":51,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":256,"endColumn":52},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"条\"。建议使用: {tc('text_kf5')}","line":256,"column":59,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":256,"endColumn":60},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"上一页\"。建议使用: {tc('text_btlof')}","line":263,"column":14,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":265,"endColumn":13},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"下一页\"。建议使用: {tc('text_btmf4')}","line":271,"column":14,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":273,"endColumn":13},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"设备编码\"。建议使用: {tc('text_i06agk')}","line":283,"column":24,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":283,"endColumn":28},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"设备名称\"。建议使用: {tc('text_hzyzbg')}","line":290,"column":24,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":290,"endColumn":28},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"检定日期\"。建议使用: {tc('text_dljl10')}","line":297,"column":24,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":297,"endColumn":28},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"下次检定日期\"。建议使用: {tc('text_ipgqzu')}","line":305,"column":24,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":305,"endColumn":30},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"检定机构\"。建议使用: {tc('text_dljt9g')}","line":315,"column":24,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":315,"endColumn":28},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"取消\"。建议使用: {tc('text_ev02')}","line":330,"column":78,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":332,"endColumn":15}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":40,"fixableErrorCount":0,"fixableWarningCount":0,"source":"'use client';\n\nimport { authFetch } from '@/lib/auth-fetch';\nimport { useEffect, useState } from 'react';\nimport { MainLayout } from '@/components/layout';\nimport { Card, CardContent } from '@/components/ui/card';\nimport { Button } from '@/components/ui/button';\nimport { Input } from '@/components/ui/input';\nimport { Badge } from '@/components/ui/badge';\nimport {\n Table,\n TableBody,\n TableCell,\n TableHead,\n TableHeader,\n TableRow,\n} from '@/components/ui/table';\nimport {\n Dialog,\n DialogContent,\n DialogHeader,\n DialogTitle,\n DialogFooter,\n} from '@/components/ui/dialog';\nimport { Label } from '@/components/ui/label';\nimport { Plus, Search, Edit, Trash2 } from 'lucide-react';\nimport { useToast } from '@/hooks/use-toast';\nimport { useTranslations } from 'next-intl';\n\ninterface Item {\n id: number;\n calibration_no: string;\n equipment_code: string;\n equipment_name: string;\n calibration_date: string;\n next_calibration_date: string;\n calibration_org: string;\n calibration_result: string;\n certificate_no: string;\n status: number;\n}\nconst statusMap: Record<\n number,\n { label: string; variant: 'default' | 'secondary' | 'destructive' | 'outline' }\n> = {\n 1: { label: '待检定', variant: 'outline' },\n 2: { label: '检定中', variant: 'default' },\n 3: { label: '已合格', variant: 'secondary' },\n 4: { label: '不合格', variant: 'destructive' },\n};\n\nexport default function EquipmentCalibrationPage() {\n // 翻译钩子\n const tc = useTranslations('Common');\n\n const { toast } = useToast();\n const [list, setList] = useState<Item[]>([]);\n const [total, setTotal] = useState(0);\n const [page, setPage] = useState(1);\n const [searchNo, setSearchNo] = useState('');\n const [showDialog, setShowDialog] = useState(false);\n const [editItem, setEditItem] = useState<Partial<Item>>({});\n\n const fetchData = async () => {\n try {\n const params = new URLSearchParams({\n page: String(page),\n pageSize: '20',\n calibrationNo: searchNo,\n });\n const res = await authFetch('/api/equipment/calibration?' + params);\n const result = await res.json();\n if (result.success) {\n setList(result.data.list || []);\n setTotal(result.data.total || 0);\n }\n } catch {}\n };\n useEffect(() => {\n fetchData();\n }, [page]);\n\n const handleSave = async () => {\n try {\n const res = await authFetch('/api/equipment/calibration', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(editItem),\n });\n const result = await res.json();\n if (result.success) {\n toast({ title: tc('createSuccess') });\n setShowDialog(false);\n fetchData();\n } else {\n toast({\n title: tc('operationFailed'),\n description: result.message,\n variant: 'destructive',\n });\n }\n } catch {\n toast({ title: tc('operationFailed'), variant: 'destructive' });\n }\n };\n const handleStatusChange = async (id: number, status: number) => {\n try {\n const res = await authFetch('/api/equipment/calibration', {\n method: 'PUT',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ id, status }),\n });\n const result = await res.json();\n if (result.success) {\n toast({ title: tc('updateSuccess') });\n fetchData();\n }\n } catch {\n toast({ title: tc('operationFailed'), variant: 'destructive' });\n }\n };\n const handleDelete = async (id: number) => {\n if (!confirm(tc('confirmDeleteMsg'))) return;\n try {\n const res = await authFetch('/api/equipment/calibration?id=' + id, { method: 'DELETE' });\n const result = await res.json();\n if (result.success) {\n toast({ title: tc('deleteSuccess') });\n fetchData();\n }\n } catch {\n toast({ title: tc('operationFailed'), variant: 'destructive' });\n }\n };\n\n return (\n <MainLayout>\n <div className=\"p-6 space-y-6\">\n <div className=\"flex items-center justify-between\">\n <h1 className=\"text-2xl font-bold\">设备检定</h1>\n <div className=\"flex gap-2\">\n <div className=\"flex items-center gap-2\">\n <Input\n placeholder={tc('searchOrderNo')}\n value={searchNo}\n onChange={(e) => setSearchNo(e.target.value)}\n className=\"w-36 h-8 text-sm\"\n />\n <Button size=\"sm\" variant=\"outline\" onClick={fetchData}>\n <Search className=\"h-3 w-3\" />\n </Button>\n </div>\n <Button\n size=\"sm\"\n onClick={() => {\n setEditItem({});\n setShowDialog(true);\n }}\n >\n <Plus className=\"h-3 w-3 mr-1\" />\n 新增检定\n </Button>\n </div>\n </div>\n <Card>\n <CardContent className=\"p-0\">\n <Table>\n <TableHeader>\n <TableRow>\n <TableHead className=\"text-xs\">检定单号</TableHead>\n <TableHead className=\"text-xs\">设备编码</TableHead>\n <TableHead className=\"text-xs\">设备名称</TableHead>\n <TableHead className=\"text-xs\">检定日期</TableHead>\n <TableHead className=\"text-xs\">{tc('nextCalibrationDate')}</TableHead>\n <TableHead className=\"text-xs\">检定机构</TableHead>\n <TableHead className=\"text-xs\">{tc('certNo')}</TableHead>\n <TableHead className=\"text-xs\">{tc('status')}</TableHead>\n <TableHead className=\"text-xs\">{tc('actions')}</TableHead>\n </TableRow>\n </TableHeader>\n <TableBody>\n {list.map((item) => {\n const st = statusMap[item.status] || statusMap[1];\n return (\n <TableRow key={item.id}>\n <TableCell className=\"text-xs font-mono\">{item.calibration_no}</TableCell>\n <TableCell className=\"text-xs\">{item.equipment_code || '-'}</TableCell>\n <TableCell className=\"text-xs\">{item.equipment_name || '-'}</TableCell>\n <TableCell className=\"text-xs\">{item.calibration_date || '-'}</TableCell>\n <TableCell className=\"text-xs\">{item.next_calibration_date || '-'}</TableCell>\n <TableCell className=\"text-xs\">{item.calibration_org || '-'}</TableCell>\n <TableCell className=\"text-xs\">{item.certificate_no || '-'}</TableCell>\n <TableCell>\n <Badge variant={st.variant} className=\"text-xs\">\n {st.label}\n </Badge>\n </TableCell>\n <TableCell>\n <div className=\"flex gap-1\">\n {item.status === 1 && (\n <Button\n size=\"sm\"\n variant=\"ghost\"\n className=\"h-6 text-xs px-2\"\n onClick={() => handleStatusChange(item.id, 2)}\n >\n 开始检定\n </Button>\n )}\n {item.status === 2 && (\n <Button\n size=\"sm\"\n variant=\"ghost\"\n className=\"h-6 text-xs px-2\"\n onClick={() => handleStatusChange(item.id, 3)}\n >\n 合格\n </Button>\n )}\n <Button\n size=\"sm\"\n variant=\"ghost\"\n className=\"h-6 w-6 p-0\"\n onClick={() => {\n setEditItem(item);\n setShowDialog(true);\n }}\n >\n <Edit className=\"h-3 w-3\" />\n </Button>\n <Button\n size=\"sm\"\n variant=\"ghost\"\n className=\"h-6 w-6 p-0 text-red-600\"\n onClick={() => handleDelete(item.id)}\n >\n <Trash2 className=\"h-3 w-3\" />\n </Button>\n </div>\n </TableCell>\n </TableRow>\n );\n })}\n {list.length === 0 && (\n <TableRow>\n <TableCell colSpan={9} className=\"text-center text-gray-400 py-8\">\n 暂无记录\n </TableCell>\n </TableRow>\n )}\n </TableBody>\n </Table>\n </CardContent>\n </Card>\n <div className=\"flex items-center justify-between\">\n <span className=\"text-sm text-gray-500\">共{total}条</span>\n <div className=\"flex gap-2\">\n <Button\n size=\"sm\"\n variant=\"outline\"\n disabled={page <= 1}\n onClick={() => setPage((p) => p - 1)}\n >\n 上一页\n </Button>\n <Button\n size=\"sm\"\n variant=\"outline\"\n disabled={page * 20 >= total}\n onClick={() => setPage((p) => p + 1)}\n >\n 下一页\n </Button>\n </div>\n </div>\n <Dialog open={showDialog} onOpenChange={setShowDialog}>\n <DialogContent className=\"max-w-lg\" resizable>\n <DialogHeader>\n <DialogTitle>{tc('calibrationTitle')}</DialogTitle>\n </DialogHeader>\n <div className=\"grid grid-cols-2 gap-4\">\n <div>\n <Label>设备编码</Label>\n <Input\n value={editItem.equipment_code || ''}\n onChange={(e) => setEditItem({ ...editItem, equipment_code: e.target.value })}\n />\n </div>\n <div>\n <Label>设备名称</Label>\n <Input\n value={editItem.equipment_name || ''}\n onChange={(e) => setEditItem({ ...editItem, equipment_name: e.target.value })}\n />\n </div>\n <div>\n <Label>检定日期</Label>\n <Input\n type=\"date\"\n value={editItem.calibration_date || ''}\n onChange={(e) => setEditItem({ ...editItem, calibration_date: e.target.value })}\n />\n </div>\n <div>\n <Label>下次检定日期</Label>\n <Input\n type=\"date\"\n value={editItem.next_calibration_date || ''}\n onChange={(e) =>\n setEditItem({ ...editItem, next_calibration_date: e.target.value })\n }\n />\n </div>\n <div>\n <Label>检定机构</Label>\n <Input\n value={editItem.calibration_org || ''}\n onChange={(e) => setEditItem({ ...editItem, calibration_org: e.target.value })}\n />\n </div>\n <div>\n <Label>{tc('certNo')}</Label>\n <Input\n value={editItem.certificate_no || ''}\n onChange={(e) => setEditItem({ ...editItem, certificate_no: e.target.value })}\n />\n </div>\n </div>\n <DialogFooter>\n <Button variant=\"outline\" onClick={() => setShowDialog(false)}>\n 取消\n </Button>\n <Button onClick={handleSave}>{tc('save')}</Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n </div>\n </MainLayout>\n );\n}\n","usedDeprecatedRules":[]},{"filePath":"D:\\dcprint\\erp-project\\src\\app\\[locale]\\equipment\\error.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\dcprint\\erp-project\\src\\app\\[locale]\\equipment\\maintenance\\page.tsx","messages":[{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":144,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":144,"endColumn":38},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":145,"column":18,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":145,"endColumn":25},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction<EquipmentItem[]>`.","line":146,"column":26,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":146,"endColumn":49},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":146,"column":33,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":146,"endColumn":37},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":157,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":157,"endColumn":38},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":158,"column":18,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":158,"endColumn":25},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction<MaintenancePlan[]>`.","line":159,"column":18,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":159,"endColumn":41},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":159,"column":25,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":159,"endColumn":29},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction<number>`.","line":160,"column":22,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":160,"endColumn":45},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":160,"column":29,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":160,"endColumn":33},{"ruleId":"react-hooks/exhaustive-deps","severity":1,"message":"React Hook useCallback has missing dependencies: 'tc' and 'toast'. Either include them or remove the dependency array.","line":167,"column":6,"nodeType":"ArrayExpression","endLine":167,"endColumn":26,"suggestions":[{"desc":"Update the dependencies array to be: [planPage, searchNo, tc, toast]","fix":{"range":[5320,5340],"text":"[planPage, searchNo, tc, toast]"}}]},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":179,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":179,"endColumn":38},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":180,"column":18,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":180,"endColumn":25},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction<MaintenanceRecord[]>`.","line":181,"column":20,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":181,"endColumn":43},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":181,"column":27,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":181,"endColumn":31},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction<number>`.","line":182,"column":24,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":182,"endColumn":47},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":182,"column":31,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":182,"endColumn":35},{"ruleId":"react-hooks/exhaustive-deps","severity":1,"message":"React Hook useCallback has missing dependencies: 'tc' and 'toast'. Either include them or remove the dependency array.","line":189,"column":6,"nodeType":"ArrayExpression","endLine":189,"endColumn":28,"suggestions":[{"desc":"Update the dependencies array to be: [recordPage, searchNo, tc, toast]","fix":{"range":[6012,6034],"text":"[recordPage, searchNo, tc, toast]"}}]},{"ruleId":"react-hooks/set-state-in-effect","severity":1,"message":"Error: Calling setState synchronously within an effect can trigger cascading renders\n\nEffects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following:\n* Update external systems with the latest state from React.\n* Subscribe for updates from some external system, calling setState in a callback function when external state changes.\n\nCalling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect).\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\equipment\\maintenance\\page.tsx:192:5\n 190 |\n 191 | useEffect(() => {\n> 192 | fetchEquipment();\n | ^^^^^^^^^^^^^^ Avoid calling setState() directly within an effect\n 193 | }, [fetchEquipment]);\n 194 | useEffect(() => {\n 195 | if (activeTab === 'plan') fetchPlans();","line":192,"column":5,"nodeType":null,"endLine":192,"endColumn":19},{"ruleId":"react-hooks/set-state-in-effect","severity":1,"message":"Error: Calling setState synchronously within an effect can trigger cascading renders\n\nEffects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following:\n* Update external systems with the latest state from React.\n* Subscribe for updates from some external system, calling setState in a callback function when external state changes.\n\nCalling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect).\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\equipment\\maintenance\\page.tsx:195:31\n 193 | }, [fetchEquipment]);\n 194 | useEffect(() => {\n> 195 | if (activeTab === 'plan') fetchPlans();\n | ^^^^^^^^^^ Avoid calling setState() directly within an effect\n 196 | else fetchRecords();\n 197 | }, [activeTab, fetchPlans, fetchRecords]);\n 198 |","line":195,"column":31,"nodeType":null,"endLine":195,"endColumn":41},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":206,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":206,"endColumn":38},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":207,"column":18,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":207,"endColumn":25},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":215,"column":17,"nodeType":"Property","messageId":"anyAssignment","endLine":215,"endColumn":53},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .message on an `any` value.","line":215,"column":31,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":215,"endColumn":38},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":228,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":228,"endColumn":38},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":229,"column":18,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":229,"endColumn":25},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":233,"column":17,"nodeType":"Property","messageId":"anyAssignment","endLine":233,"endColumn":63},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .message on an `any` value.","line":233,"column":31,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":233,"endColumn":38},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":246,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":246,"endColumn":38},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":247,"column":18,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":247,"endColumn":25},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":252,"column":17,"nodeType":"Property","messageId":"anyAssignment","endLine":252,"endColumn":63},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .message on an `any` value.","line":252,"column":31,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":252,"endColumn":38},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"保养计划\"。建议使用: {tc('text_af8h8v')}","line":286,"column":54,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":288,"endColumn":15},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"保养记录\"。建议使用: {tc('text_af8k83')}","line":290,"column":47,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":292,"endColumn":15},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"新增保养计划\"。建议使用: {tc('text_w5b743')}","line":312,"column":52,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":314,"endColumn":17},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"新增保养记录\"。建议使用: {tc('text_w5b44v')}","line":317,"column":52,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":319,"endColumn":17},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"保养计划\"。建议使用: {tc('text_af8h8v')}","line":327,"column":28,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":327,"endColumn":32},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"计划编号\"。建议使用: {tc('text_hymw36')}","line":339,"column":36,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":339,"endColumn":40},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"设备编码\"。建议使用: {tc('text_i06agk')}","line":340,"column":36,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":340,"endColumn":40},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"设备名称\"。建议使用: {tc('text_hzyzbg')}","line":341,"column":36,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":341,"endColumn":40},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"开始执行\"。建议使用: {tc('text_cczvm8')}","line":379,"column":36,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":381,"endColumn":35},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"填写记录\"。建议使用: {tc('text_bi3jqr')}","line":389,"column":36,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":391,"endColumn":35},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"暂无保养计划\"。建议使用: {tc('text_mxwydv')}","line":420,"column":93,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":422,"endColumn":27},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"共\"。建议使用: {tc('text_g35')}","line":429,"column":59,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":429,"endColumn":60},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"条\"。建议使用: {tc('text_kf5')}","line":429,"column":71,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":429,"endColumn":72},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"上一页\"。建议使用: {tc('text_btlof')}","line":436,"column":22,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":438,"endColumn":21},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"下一页\"。建议使用: {tc('text_btmf4')}","line":444,"column":22,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":446,"endColumn":21},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"保养记录\"。建议使用: {tc('text_af8k83')}","line":456,"column":28,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":456,"endColumn":32},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"记录编号\"。建议使用: {tc('text_i0uebq')}","line":468,"column":36,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":468,"endColumn":40},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"设备编码\"。建议使用: {tc('text_i06agk')}","line":469,"column":36,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":469,"endColumn":40},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"设备名称\"。建议使用: {tc('text_hzyzbg')}","line":470,"column":36,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":470,"endColumn":40},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"开始时间\"。建议使用: {tc('text_cd0k3t')}","line":472,"column":36,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":472,"endColumn":40},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"结束时间\"。建议使用: {tc('text_gfi7qi')}","line":473,"column":36,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":473,"endColumn":40},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"停机时长\"。建议使用: {tc('text_aki78n')}","line":474,"column":36,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":474,"endColumn":40},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"费用\"。建议使用: {tc('text_onwv')}","line":475,"column":36,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":475,"endColumn":38},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"暂无保养记录\"。建议使用: {tc('text_mxwven')}","line":529,"column":94,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":531,"endColumn":27},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"共\"。建议使用: {tc('text_g35')}","line":538,"column":59,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":538,"endColumn":60},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"条\"。建议使用: {tc('text_kf5')}","line":538,"column":73,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":538,"endColumn":74},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"上一页\"。建议使用: {tc('text_btlof')}","line":545,"column":22,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":547,"endColumn":21},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"下一页\"。建议使用: {tc('text_btmf4')}","line":553,"column":22,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":555,"endColumn":21},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"设备\"。建议使用: {tc('text_o9ah')}","line":585,"column":28,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":587,"endColumn":23},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .id on an `any` value.","line":592,"column":71,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":592,"endColumn":73},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":606,"column":44,"nodeType":"MemberExpression","messageId":"anyAssignment","endLine":606,"endColumn":49},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .id on an `any` value.","line":606,"column":47,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":606,"endColumn":49},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .id on an `any` value.","line":606,"column":68,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":606,"endColumn":70},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .equipment_code on an `any` value.","line":607,"column":33,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":607,"endColumn":47},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .equipment_name on an `any` value.","line":607,"column":55,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":607,"endColumn":69},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"计划日期\"。建议使用: {tc('text_hyipm3')}","line":663,"column":28,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":663,"endColumn":32},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"设备\"。建议使用: {tc('text_o9ah')}","line":693,"column":28,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":695,"endColumn":23},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .id on an `any` value.","line":700,"column":71,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":700,"endColumn":73},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":715,"column":44,"nodeType":"MemberExpression","messageId":"anyAssignment","endLine":715,"endColumn":49},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .id on an `any` value.","line":715,"column":47,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":715,"endColumn":49},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .id on an `any` value.","line":715,"column":68,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":715,"endColumn":70},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .equipment_code on an `any` value.","line":716,"column":33,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":716,"endColumn":47},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .equipment_name on an `any` value.","line":716,"column":55,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":716,"endColumn":69},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"开始时间\"。建议使用: {tc('text_cd0k3t')}","line":743,"column":28,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":743,"endColumn":32},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"结束时间\"。建议使用: {tc('text_gfi7qi')}","line":751,"column":28,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":751,"endColumn":32},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"故障描述\"。建议使用: {tc('text_dedoag')}","line":800,"column":26,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":800,"endColumn":30},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"取消\"。建议使用: {tc('text_ev02')}","line":829,"column":76,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":831,"endColumn":13},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"保存\"。建议使用: {tc('text_e32z')}","line":832,"column":84,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":834,"endColumn":13}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":80,"fixableErrorCount":0,"fixableWarningCount":0,"source":"'use client';\r\n\r\nimport { authFetch } from '@/lib/auth-fetch';\r\nimport { EQUIPMENT_MAINT_TYPE_LABEL, EQUIPMENT_CYCLE_TYPE_LABEL, EQUIPMENT_PLAN_STATUS_LABEL } from '@/lib/status-labels';\r\nimport { useEffect, useState, useCallback } from 'react';\r\nimport { MainLayout } from '@/components/layout';\r\nimport { Card, CardContent, CardHeader, CardTitle, CardDescription } from '@/components/ui/card';\r\nimport { Button } from '@/components/ui/button';\r\nimport { Input } from '@/components/ui/input';\r\nimport { Label } from '@/components/ui/label';\r\nimport { Badge } from '@/components/ui/badge';\r\nimport {\r\n Dialog,\r\n DialogContent,\r\n DialogHeader,\r\n DialogTitle,\r\n DialogDescription,\r\n DialogFooter,\r\n} from '@/components/ui/dialog';\r\nimport {\r\n Select,\r\n SelectContent,\r\n SelectItem,\r\n SelectTrigger,\r\n SelectValue,\r\n} from '@/components/ui/select';\r\nimport {\r\n Table,\r\n TableBody,\r\n TableCell,\r\n TableHead,\r\n TableHeader,\r\n TableRow,\r\n} from '@/components/ui/table';\r\nimport { Textarea } from '@/components/ui/textarea';\r\nimport { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';\r\nimport { Plus, Search, Edit, Trash2, RefreshCw, Wrench, ClipboardList } from 'lucide-react';\r\nimport { useToast } from '@/hooks/use-toast';\r\nimport { useTranslations } from 'next-intl';\r\n\r\ninterface MaintenancePlan {\r\n id: number;\r\n plan_no: string;\r\n equipment_id: number;\r\n equipment_code: string;\r\n equipment_name: string;\r\n maintenance_type: number;\r\n cycle_type: number;\r\n cycle_value: number;\r\n plan_date: string;\r\n responsible_id: number;\r\n content: string;\r\n status: number;\r\n complete_date: string;\r\n remark: string;\r\n}\r\n\r\ninterface EquipmentItem {\r\n id: number;\r\n equipment_code: string;\r\n equipment_name: string;\r\n equipment_type?: string;\r\n status?: number;\r\n}\r\n\r\ninterface MaintenanceForm {\r\n id?: number;\r\n plan_id?: number;\r\n equipment_id?: number;\r\n equipment_code?: string;\r\n equipment_name?: string;\r\n maintenance_type?: number;\r\n cycle_value?: number;\r\n cycle_type?: number;\r\n plan_date?: string;\r\n content?: string;\r\n fault_desc?: string;\r\n maintenance_content?: string;\r\n start_time?: string;\r\n end_time?: string;\r\n downtime_hours?: number;\r\n cost?: number;\r\n responsible_id?: number;\r\n result?: number;\r\n remark?: string;\r\n}\r\n\r\ninterface MaintenanceRecord {\r\n id: number;\r\n record_no: string;\r\n plan_id: number;\r\n equipment_id: number;\r\n equipment_code: string;\r\n equipment_name: string;\r\n maintenance_type: number;\r\n fault_desc: string;\r\n maintenance_content: string;\r\n start_time: string;\r\n end_time: string;\r\n downtime_hours: number;\r\n cost: number;\r\n responsible_id: number;\r\n result: number;\r\n remark: string;\r\n}\r\n\r\nconst MAINT_TYPE = EQUIPMENT_MAINT_TYPE_LABEL;\r\nconst CYCLE_TYPE = EQUIPMENT_CYCLE_TYPE_LABEL;\r\n\r\nconst PLAN_STATUS: Record<number, { label: string; color: string }> = {\r\n 1: { label: EQUIPMENT_PLAN_STATUS_LABEL[1], color: 'bg-yellow-100 text-yellow-800' },\r\n 2: { label: EQUIPMENT_PLAN_STATUS_LABEL[2], color: 'bg-blue-100 text-blue-800' },\r\n 3: { label: EQUIPMENT_PLAN_STATUS_LABEL[3], color: 'bg-green-100 text-green-800' },\r\n 4: { label: EQUIPMENT_PLAN_STATUS_LABEL[4], color: 'bg-red-100 text-red-800' },\r\n};\r\nexport default function EquipmentMaintenancePage() {\r\n // 翻译钩子\r\n const tc = useTranslations('Common');\r\n\r\n const RECORD_RESULT: Record<number, { label: string; color: string }> = {\r\n 1: { label: tc('normal'), color: 'bg-green-100 text-green-800' },\r\n 2: { label: '异常', color: 'bg-red-100 text-red-800' },\r\n 3: { label: '需跟进', color: 'bg-yellow-100 text-yellow-800' },\r\n };\r\n\r\n const { toast } = useToast();\r\n const [activeTab, setActiveTab] = useState('plan');\r\n const [plans, setPlans] = useState<MaintenancePlan[]>([]);\r\n const [records, setRecords] = useState<MaintenanceRecord[]>([]);\r\n const [planTotal, setPlanTotal] = useState(0);\r\n const [recordTotal, setRecordTotal] = useState(0);\r\n const [planPage, setPlanPage] = useState(1);\r\n const [recordPage, setRecordPage] = useState(1);\r\n const [searchNo, setSearchNo] = useState('');\r\n const [dialogOpen, setDialogOpen] = useState(false);\r\n const [dialogType, setDialogType] = useState<'plan' | 'record'>('plan');\r\n const [form, setForm] = useState<MaintenanceForm>({});\r\n const [equipmentList, setEquipmentList] = useState<EquipmentItem[]>([]);\r\n const [loading, setLoading] = useState(false);\r\n\r\n const fetchEquipment = useCallback(async () => {\r\n try {\r\n const res = await fetch('/api/equipment?pageSize=100');\r\n const result = await res.json();\r\n if (result.success) {\r\n setEquipmentList(result.data?.list || []);\r\n }\r\n } catch {}\r\n }, []);\r\n\r\n const fetchPlans = useCallback(async () => {\r\n setLoading(true);\r\n try {\r\n const params = new URLSearchParams({ page: String(planPage), pageSize: '20', type: 'plan' });\r\n if (searchNo) params.append('planNo', searchNo);\r\n const res = await authFetch('/api/equipment/maintenance?' + params);\r\n const result = await res.json();\r\n if (result.success) {\r\n setPlans(result.data?.list || []);\r\n setPlanTotal(result.data?.total || 0);\r\n }\r\n } catch {\r\n toast({ title: tc('fetchFailed'), variant: 'destructive' });\r\n } finally {\r\n setLoading(false);\r\n }\r\n }, [planPage, searchNo]);\r\n\r\n const fetchRecords = useCallback(async () => {\r\n setLoading(true);\r\n try {\r\n const params = new URLSearchParams({\r\n page: String(recordPage),\r\n pageSize: '20',\r\n type: 'record',\r\n });\r\n if (searchNo) params.append('recordNo', searchNo);\r\n const res = await fetch('/api/equipment/maintenance?' + params);\r\n const result = await res.json();\r\n if (result.success) {\r\n setRecords(result.data?.list || []);\r\n setRecordTotal(result.data?.total || 0);\r\n }\r\n } catch {\r\n toast({ title: tc('fetchFailed'), variant: 'destructive' });\r\n } finally {\r\n setLoading(false);\r\n }\r\n }, [recordPage, searchNo]);\r\n\r\n useEffect(() => {\r\n fetchEquipment();\r\n }, [fetchEquipment]);\r\n useEffect(() => {\r\n if (activeTab === 'plan') fetchPlans();\r\n else fetchRecords();\r\n }, [activeTab, fetchPlans, fetchRecords]);\r\n\r\n const handleSave = async () => {\r\n try {\r\n const payload = { ...form, type: dialogType };\r\n const res = await authFetch('/api/equipment/maintenance', {\r\n method: 'POST',\r\n body: JSON.stringify(payload),\r\n });\r\n const result = await res.json();\r\n if (result.success) {\r\n toast({\r\n title: dialogType === 'plan' ? tc('createPlanSuccess') : tc('createRecordSuccess'),\r\n });\r\n setDialogOpen(false);\r\n if (activeTab === 'plan') fetchPlans();\r\n else fetchRecords();\r\n } else {\r\n toast({ title: result.message || tc('error'), variant: 'destructive' });\r\n }\r\n } catch {\r\n toast({ title: tc('operationFailed'), variant: 'destructive' });\r\n }\r\n };\r\n\r\n const handlePlanStatus = async (id: number, status: number) => {\r\n try {\r\n const res = await authFetch('/api/equipment/maintenance', {\r\n method: 'PUT',\r\n body: JSON.stringify({ type: 'plan', id, status }),\r\n });\r\n const result = await res.json();\r\n if (result.success) {\r\n toast({ title: tc('statusUpdateSuccess') });\r\n fetchPlans();\r\n } else {\r\n toast({ title: result.message || tc('operationFailed'), variant: 'destructive' });\r\n }\r\n } catch {\r\n toast({ title: tc('operationFailed'), variant: 'destructive' });\r\n }\r\n };\r\n\r\n const handleDelete = async (id: number, type: string) => {\r\n if (!confirm(tc('confirmDeleteMsg'))) return;\r\n try {\r\n const res = await authFetch(`/api/equipment/maintenance?id=${id}&type=${type}`, {\r\n method: 'DELETE',\r\n });\r\n const result = await res.json();\r\n if (result.success) {\r\n toast({ title: tc('deleteSuccess') });\r\n if (type === 'plan') fetchPlans();\r\n else fetchRecords();\r\n } else {\r\n toast({ title: result.message || tc('operationFailed'), variant: 'destructive' });\r\n }\r\n } catch {\r\n toast({ title: tc('operationFailed'), variant: 'destructive' });\r\n }\r\n };\r\n\r\n const openNewPlan = () => {\r\n setForm({});\r\n setDialogType('plan');\r\n setDialogOpen(true);\r\n };\r\n\r\n const openNewRecord = (plan?: MaintenancePlan) => {\r\n if (plan) {\r\n setForm({\r\n plan_id: plan.id,\r\n equipment_id: plan.equipment_id,\r\n maintenance_type: plan.maintenance_type,\r\n });\r\n } else {\r\n setForm({});\r\n }\r\n setDialogType('record');\r\n setDialogOpen(true);\r\n };\r\n\r\n return (\r\n <MainLayout title=\"设备保养\">\r\n <div className=\"space-y-6\">\r\n <Tabs value={activeTab} onValueChange={setActiveTab}>\r\n <div className=\"flex items-center justify-between\">\r\n <TabsList>\r\n <TabsTrigger value=\"plan\" className=\"gap-1\">\r\n <ClipboardList className=\"w-4 h-4\" />\r\n 保养计划\r\n </TabsTrigger>\r\n <TabsTrigger value=\"record\" className=\"gap-1\">\r\n <Wrench className=\"w-4 h-4\" />\r\n 保养记录\r\n </TabsTrigger>\r\n </TabsList>\r\n <div className=\"flex gap-2\">\r\n <div className=\"relative\">\r\n <Search className=\"absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-gray-400\" />\r\n <Input\r\n placeholder=\"搜索单号...\"\r\n value={searchNo}\r\n onChange={(e) => setSearchNo(e.target.value)}\r\n className=\"pl-9 w-48\"\r\n />\r\n </div>\r\n <Button\r\n variant=\"outline\"\r\n onClick={() => (activeTab === 'plan' ? fetchPlans() : fetchRecords())}\r\n >\r\n <RefreshCw className=\"w-4 h-4\" />\r\n </Button>\r\n {activeTab === 'plan' ? (\r\n <Button onClick={openNewPlan} className=\"bg-blue-600 hover:bg-blue-700\">\r\n <Plus className=\"w-4 h-4 mr-2\" />\r\n 新增保养计划\r\n </Button>\r\n ) : (\r\n <Button onClick={() => openNewRecord()} className=\"bg-blue-600 hover:bg-blue-700\">\r\n <Plus className=\"w-4 h-4 mr-2\" />\r\n 新增保养记录\r\n </Button>\r\n )}\r\n </div>\r\n </div>\r\n\r\n <TabsContent value=\"plan\">\r\n <Card>\r\n <CardHeader>\r\n <CardTitle>保养计划</CardTitle>\r\n <CardDescription>{tc('maintenancePlanDesc')}</CardDescription>\r\n </CardHeader>\r\n <CardContent>\r\n {loading ? (\r\n <div className=\"flex justify-center py-8\">\r\n <RefreshCw className=\"w-6 h-6 animate-spin\" />\r\n </div>\r\n ) : (\r\n <Table>\r\n <TableHeader>\r\n <TableRow>\r\n <TableHead>计划编号</TableHead>\r\n <TableHead>设备编码</TableHead>\r\n <TableHead>设备名称</TableHead>\r\n <TableHead>{tc('maintenanceType')}</TableHead>\r\n <TableHead>{tc('cycleValue')}</TableHead>\r\n <TableHead>{tc('content')}</TableHead>\r\n <TableHead>{tc('status')}</TableHead>\r\n <TableHead className=\"text-right\">{tc('actions')}</TableHead>\r\n </TableRow>\r\n </TableHeader>\r\n <TableBody>\r\n {plans.map((p) => {\r\n const st = PLAN_STATUS[p.status] || PLAN_STATUS[1];\r\n return (\r\n <TableRow key={p.id}>\r\n <TableCell className=\"font-mono text-sm\">{p.plan_no}</TableCell>\r\n <TableCell>{p.equipment_code || '-'}</TableCell>\r\n <TableCell>{p.equipment_name || '-'}</TableCell>\r\n <TableCell>\r\n <Badge variant=\"outline\">\r\n {MAINT_TYPE[p.maintenance_type] || '-'}\r\n </Badge>\r\n </TableCell>\r\n <TableCell>\r\n {p.cycle_value}\r\n {CYCLE_TYPE[p.cycle_type] || ''}\r\n </TableCell>\r\n <TableCell>{p.plan_date || '-'}</TableCell>\r\n <TableCell className=\"max-w-40 truncate\">{p.content || '-'}</TableCell>\r\n <TableCell>\r\n <Badge className={st.color}>{st.label}</Badge>\r\n </TableCell>\r\n <TableCell className=\"text-right\">\r\n <div className=\"flex justify-end gap-1\">\r\n {p.status === 1 && (\r\n <Button\r\n size=\"sm\"\r\n variant=\"ghost\"\r\n className=\"h-7 text-xs\"\r\n onClick={() => handlePlanStatus(p.id, 2)}\r\n >\r\n 开始执行\r\n </Button>\r\n )}\r\n {p.status === 2 && (\r\n <Button\r\n size=\"sm\"\r\n variant=\"ghost\"\r\n className=\"h-7 text-xs\"\r\n onClick={() => openNewRecord(p)}\r\n >\r\n 填写记录\r\n </Button>\r\n )}\r\n <Button\r\n size=\"sm\"\r\n variant=\"ghost\"\r\n className=\"h-7 w-7 p-0\"\r\n onClick={() => {\r\n setForm(p);\r\n setDialogType('plan');\r\n setDialogOpen(true);\r\n }}\r\n >\r\n <Edit className=\"w-3 h-3\" />\r\n </Button>\r\n <Button\r\n size=\"sm\"\r\n variant=\"ghost\"\r\n className=\"h-7 w-7 p-0 text-red-500\"\r\n onClick={() => handleDelete(p.id, 'plan')}\r\n >\r\n <Trash2 className=\"w-3 h-3\" />\r\n </Button>\r\n </div>\r\n </TableCell>\r\n </TableRow>\r\n );\r\n })}\r\n {plans.length === 0 && (\r\n <TableRow>\r\n <TableCell colSpan={9} className=\"text-center text-gray-400 py-8\">\r\n 暂无保养计划\r\n </TableCell>\r\n </TableRow>\r\n )}\r\n </TableBody>\r\n </Table>\r\n )}\r\n <div className=\"flex items-center justify-between mt-4\">\r\n <span className=\"text-sm text-gray-500\">共{planTotal}条</span>\r\n <div className=\"flex gap-2\">\r\n <Button\r\n size=\"sm\"\r\n variant=\"outline\"\r\n disabled={planPage <= 1}\r\n onClick={() => setPlanPage((p) => p - 1)}\r\n >\r\n 上一页\r\n </Button>\r\n <Button\r\n size=\"sm\"\r\n variant=\"outline\"\r\n disabled={planPage * 20 >= planTotal}\r\n onClick={() => setPlanPage((p) => p + 1)}\r\n >\r\n 下一页\r\n </Button>\r\n </div>\r\n </div>\r\n </CardContent>\r\n </Card>\r\n </TabsContent>\r\n\r\n <TabsContent value=\"record\">\r\n <Card>\r\n <CardHeader>\r\n <CardTitle>保养记录</CardTitle>\r\n <CardDescription>{tc('recordDesc')}</CardDescription>\r\n </CardHeader>\r\n <CardContent>\r\n {loading ? (\r\n <div className=\"flex justify-center py-8\">\r\n <RefreshCw className=\"w-6 h-6 animate-spin\" />\r\n </div>\r\n ) : (\r\n <Table>\r\n <TableHeader>\r\n <TableRow>\r\n <TableHead>记录编号</TableHead>\r\n <TableHead>设备编码</TableHead>\r\n <TableHead>设备名称</TableHead>\r\n <TableHead>{tc('maintenanceType')}</TableHead>\r\n <TableHead>开始时间</TableHead>\r\n <TableHead>结束时间</TableHead>\r\n <TableHead>停机时长</TableHead>\r\n <TableHead>费用</TableHead>\r\n <TableHead>{tc('maintenanceResult')}</TableHead>\r\n <TableHead className=\"text-right\">{tc('actions')}</TableHead>\r\n </TableRow>\r\n </TableHeader>\r\n <TableBody>\r\n {records.map((r) => {\r\n const rs = RECORD_RESULT[r.result] || RECORD_RESULT[1];\r\n return (\r\n <TableRow key={r.id}>\r\n <TableCell className=\"font-mono text-sm\">{r.record_no}</TableCell>\r\n <TableCell>{r.equipment_code || '-'}</TableCell>\r\n <TableCell>{r.equipment_name || '-'}</TableCell>\r\n <TableCell>\r\n <Badge variant=\"outline\">\r\n {MAINT_TYPE[r.maintenance_type] || '-'}\r\n </Badge>\r\n </TableCell>\r\n <TableCell className=\"text-sm\">{r.start_time || '-'}</TableCell>\r\n <TableCell className=\"text-sm\">{r.end_time || '-'}</TableCell>\r\n <TableCell>{r.downtime_hours || 0}h</TableCell>\r\n <TableCell>¥{Number(r.cost || 0).toFixed(2)}</TableCell>\r\n <TableCell>\r\n <Badge className={rs.color}>{rs.label}</Badge>\r\n </TableCell>\r\n <TableCell className=\"text-right\">\r\n <div className=\"flex justify-end gap-1\">\r\n <Button\r\n size=\"sm\"\r\n variant=\"ghost\"\r\n className=\"h-7 w-7 p-0\"\r\n onClick={() => {\r\n setForm(r);\r\n setDialogType('record');\r\n setDialogOpen(true);\r\n }}\r\n >\r\n <Edit className=\"w-3 h-3\" />\r\n </Button>\r\n <Button\r\n size=\"sm\"\r\n variant=\"ghost\"\r\n className=\"h-7 w-7 p-0 text-red-500\"\r\n onClick={() => handleDelete(r.id, 'record')}\r\n >\r\n <Trash2 className=\"w-3 h-3\" />\r\n </Button>\r\n </div>\r\n </TableCell>\r\n </TableRow>\r\n );\r\n })}\r\n {records.length === 0 && (\r\n <TableRow>\r\n <TableCell colSpan={10} className=\"text-center text-gray-400 py-8\">\r\n 暂无保养记录\r\n </TableCell>\r\n </TableRow>\r\n )}\r\n </TableBody>\r\n </Table>\r\n )}\r\n <div className=\"flex items-center justify-between mt-4\">\r\n <span className=\"text-sm text-gray-500\">共{recordTotal}条</span>\r\n <div className=\"flex gap-2\">\r\n <Button\r\n size=\"sm\"\r\n variant=\"outline\"\r\n disabled={recordPage <= 1}\r\n onClick={() => setRecordPage((p) => p - 1)}\r\n >\r\n 上一页\r\n </Button>\r\n <Button\r\n size=\"sm\"\r\n variant=\"outline\"\r\n disabled={recordPage * 20 >= recordTotal}\r\n onClick={() => setRecordPage((p) => p + 1)}\r\n >\r\n 下一页\r\n </Button>\r\n </div>\r\n </div>\r\n </CardContent>\r\n </Card>\r\n </TabsContent>\r\n </Tabs>\r\n </div>\r\n\r\n <Dialog open={dialogOpen} onOpenChange={setDialogOpen}>\r\n <DialogContent className=\"max-w-lg\" resizable>\r\n <DialogHeader>\r\n <DialogTitle>\r\n {dialogType === 'plan'\r\n ? form.id\r\n ? '编辑保养计划'\r\n : '新增保养计划'\r\n : form.id\r\n ? '编辑保养记录'\r\n : '新增保养记录'}\r\n </DialogTitle>\r\n <DialogDescription>\r\n {dialogType === 'plan' ? '设置设备定期保养计划' : '记录设备保养执行情况'}\r\n </DialogDescription>\r\n </DialogHeader>\r\n <div className=\"space-y-4 py-4\">\r\n {dialogType === 'plan' ? (\r\n <>\r\n <div className=\"grid grid-cols-2 gap-4\">\r\n <div className=\"space-y-2\">\r\n <Label>\r\n 设备\r\n <span className=\"text-red-500\">*</span>\r\n </Label>\r\n <Select\r\n value={String(form.equipment_id || '')}\r\n onValueChange={(v) => {\r\n const eq = equipmentList.find((e: Loose) => e.id === Number(v));\r\n setForm({\r\n ...form,\r\n equipment_id: Number(v),\r\n equipment_code: eq?.equipment_code,\r\n equipment_name: eq?.equipment_name,\r\n });\r\n }}\r\n >\r\n <SelectTrigger>\r\n <SelectValue placeholder=\"选择设备\" />\r\n </SelectTrigger>\r\n <SelectContent>\r\n {equipmentList.map((eq: Loose) => (\r\n <SelectItem key={eq.id} value={String(eq.id)}>\r\n {eq.equipment_code} - {eq.equipment_name}\r\n </SelectItem>\r\n ))}\r\n </SelectContent>\r\n </Select>\r\n </div>\r\n <div className=\"space-y-2\">\r\n <Label>{tc('maintenanceType')}</Label>\r\n <Select\r\n value={String(form.maintenance_type ?? 1)}\r\n onValueChange={(v) => setForm({ ...form, maintenance_type: Number(v) })}\r\n >\r\n <SelectTrigger>\r\n <SelectValue />\r\n </SelectTrigger>\r\n <SelectContent>\r\n {Object.entries(MAINT_TYPE).map(([k, v]) => (\r\n <SelectItem key={k} value={k}>\r\n {v}\r\n </SelectItem>\r\n ))}\r\n </SelectContent>\r\n </Select>\r\n </div>\r\n </div>\r\n <div className=\"grid grid-cols-3 gap-4\">\r\n <div className=\"space-y-2\">\r\n <Label>{tc('cycleValue')}</Label>\r\n <Input\r\n type=\"number\"\r\n value={form.cycle_value || ''}\r\n onChange={(e) =>\r\n setForm({ ...form, cycle_value: parseInt(e.target.value) || 0 })\r\n }\r\n placeholder=\"30\"\r\n />\r\n </div>\r\n <div className=\"space-y-2\">\r\n <Label>{tc('cycleValue')}</Label>\r\n <Select\r\n value={String(form.cycle_type ?? 3)}\r\n onValueChange={(v) => setForm({ ...form, cycle_type: Number(v) })}\r\n >\r\n <SelectTrigger>\r\n <SelectValue />\r\n </SelectTrigger>\r\n <SelectContent>\r\n {Object.entries(CYCLE_TYPE).map(([k, v]) => (\r\n <SelectItem key={k} value={k}>\r\n {v}\r\n </SelectItem>\r\n ))}\r\n </SelectContent>\r\n </Select>\r\n </div>\r\n <div className=\"space-y-2\">\r\n <Label>计划日期</Label>\r\n <Input\r\n type=\"date\"\r\n value={form.plan_date || ''}\r\n onChange={(e) => setForm({ ...form, plan_date: e.target.value })}\r\n />\r\n </div>\r\n </div>\r\n <div className=\"space-y-2\">\r\n <Label>{tc('content')}</Label>\r\n <Textarea\r\n value={form.content || ''}\r\n onChange={(e) => setForm({ ...form, content: e.target.value })}\r\n placeholder={tc('content')}\r\n rows={3}\r\n />\r\n </div>\r\n <div className=\"space-y-2\">\r\n <Label>{tc('remark')}</Label>\r\n <Input\r\n value={form.remark || ''}\r\n onChange={(e) => setForm({ ...form, remark: e.target.value })}\r\n placeholder={tc('remark')}\r\n />\r\n </div>\r\n </>\r\n ) : (\r\n <>\r\n <div className=\"grid grid-cols-2 gap-4\">\r\n <div className=\"space-y-2\">\r\n <Label>\r\n 设备\r\n <span className=\"text-red-500\">*</span>\r\n </Label>\r\n <Select\r\n value={String(form.equipment_id || '')}\r\n onValueChange={(v) => {\r\n const eq = equipmentList.find((e: Loose) => e.id === Number(v));\r\n setForm({\r\n ...form,\r\n equipment_id: Number(v),\r\n equipment_code: eq?.equipment_code,\r\n equipment_name: eq?.equipment_name,\r\n });\r\n }}\r\n disabled={!!form.plan_id}\r\n >\r\n <SelectTrigger>\r\n <SelectValue placeholder=\"选择设备\" />\r\n </SelectTrigger>\r\n <SelectContent>\r\n {equipmentList.map((eq: Loose) => (\r\n <SelectItem key={eq.id} value={String(eq.id)}>\r\n {eq.equipment_code} - {eq.equipment_name}\r\n </SelectItem>\r\n ))}\r\n </SelectContent>\r\n </Select>\r\n </div>\r\n <div className=\"space-y-2\">\r\n <Label>{tc('maintenanceType')}</Label>\r\n <Select\r\n value={String(form.maintenance_type ?? 1)}\r\n onValueChange={(v) => setForm({ ...form, maintenance_type: Number(v) })}\r\n >\r\n <SelectTrigger>\r\n <SelectValue />\r\n </SelectTrigger>\r\n <SelectContent>\r\n {Object.entries(MAINT_TYPE).map(([k, v]) => (\r\n <SelectItem key={k} value={k}>\r\n {v}\r\n </SelectItem>\r\n ))}\r\n </SelectContent>\r\n </Select>\r\n </div>\r\n </div>\r\n <div className=\"grid grid-cols-2 gap-4\">\r\n <div className=\"space-y-2\">\r\n <Label>开始时间</Label>\r\n <Input\r\n type=\"datetime-local\"\r\n value={form.start_time || ''}\r\n onChange={(e) => setForm({ ...form, start_time: e.target.value })}\r\n />\r\n </div>\r\n <div className=\"space-y-2\">\r\n <Label>结束时间</Label>\r\n <Input\r\n type=\"datetime-local\"\r\n value={form.end_time || ''}\r\n onChange={(e) => setForm({ ...form, end_time: e.target.value })}\r\n />\r\n </div>\r\n </div>\r\n <div className=\"grid grid-cols-3 gap-4\">\r\n <div className=\"space-y-2\">\r\n <Label>{tc('downtimeHours')}</Label>\r\n <Input\r\n type=\"number\"\r\n step=\"0.5\"\r\n value={form.downtime_hours || ''}\r\n onChange={(e) =>\r\n setForm({ ...form, downtime_hours: parseFloat(e.target.value) || 0 })\r\n }\r\n />\r\n </div>\r\n <div className=\"space-y-2\">\r\n <Label>{tc('cost')}</Label>\r\n <Input\r\n type=\"number\"\r\n step=\"0.01\"\r\n value={form.cost || ''}\r\n onChange={(e) => setForm({ ...form, cost: parseFloat(e.target.value) || 0 })}\r\n />\r\n </div>\r\n <div className=\"space-y-2\">\r\n <Label>{tc('maintenanceResult')}</Label>\r\n <Select\r\n value={String(form.result ?? 1)}\r\n onValueChange={(v) => setForm({ ...form, result: Number(v) })}\r\n >\r\n <SelectTrigger>\r\n <SelectValue />\r\n </SelectTrigger>\r\n <SelectContent>\r\n {Object.entries(RECORD_RESULT).map(([k, v]) => (\r\n <SelectItem key={k} value={k}>\r\n {v.label}\r\n </SelectItem>\r\n ))}\r\n </SelectContent>\r\n </Select>\r\n </div>\r\n </div>\r\n <div className=\"space-y-2\">\r\n <Label>故障描述</Label>\r\n <Textarea\r\n value={form.fault_desc || ''}\r\n onChange={(e) => setForm({ ...form, fault_desc: e.target.value })}\r\n placeholder=\"描述故障情况...\"\r\n rows={2}\r\n />\r\n </div>\r\n <div className=\"space-y-2\">\r\n <Label>{tc('content')}</Label>\r\n <Textarea\r\n value={form.maintenance_content || ''}\r\n onChange={(e) => setForm({ ...form, maintenance_content: e.target.value })}\r\n placeholder={tc('content')}\r\n rows={2}\r\n />\r\n </div>\r\n <div className=\"space-y-2\">\r\n <Label>{tc('remark')}</Label>\r\n <Input\r\n value={form.remark || ''}\r\n onChange={(e) => setForm({ ...form, remark: e.target.value })}\r\n placeholder={tc('remark')}\r\n />\r\n </div>\r\n </>\r\n )}\r\n </div>\r\n <DialogFooter>\r\n <Button variant=\"outline\" onClick={() => setDialogOpen(false)}>\r\n 取消\r\n </Button>\r\n <Button onClick={handleSave} className=\"bg-blue-600 hover:bg-blue-700\">\r\n 保存\r\n </Button>\r\n </DialogFooter>\r\n </DialogContent>\r\n </Dialog>\r\n </MainLayout>\r\n );\r\n}\r\n","usedDeprecatedRules":[]},{"filePath":"D:\\dcprint\\erp-project\\src\\app\\[locale]\\equipment\\page.tsx","messages":[{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":89,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":89,"endColumn":38},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":90,"column":18,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":90,"endColumn":25},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction<Equipment[]>`.","line":91,"column":17,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":91,"endColumn":40},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":91,"column":24,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":91,"endColumn":28},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction<any[]>`.","line":92,"column":22,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":92,"endColumn":50},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":92,"column":29,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":92,"endColumn":33},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"获取设备列表失败\"。建议使用: tc('text_9m2enn')","line":95,"column":19,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":95,"endColumn":29},{"ruleId":"react-hooks/set-state-in-effect","severity":1,"message":"Error: Calling setState synchronously within an effect can trigger cascading renders\n\nEffects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following:\n* Update external systems with the latest state from React.\n* Subscribe for updates from some external system, calling setState in a callback function when external state changes.\n\nCalling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect).\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\equipment\\page.tsx:102:5\n 100 |\n 101 | useEffect(() => {\n> 102 | fetchData();\n | ^^^^^^^^^ Avoid calling setState() directly within an effect\n 103 | }, [fetchData]);\n 104 |\n 105 | const saveEquipment = async () => {","line":102,"column":5,"nodeType":null,"endLine":102,"endColumn":14},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"请填写设备编码和名称\"。建议使用: tc('text_dx7gwa')","line":107,"column":19,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":107,"endColumn":31},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":117,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":117,"endColumn":38},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":118,"column":18,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":118,"endColumn":25},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `string | number | bigint | boolean | ReactElement<unknown, string | JSXElementConstructor<any>> | Iterable<ReactNode> | ... 4 more ... | undefined`.","line":123,"column":21,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":123,"endColumn":50},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .message on an `any` value.","line":123,"column":28,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":123,"endColumn":35},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"保存设备失败\"。建议使用: tc('text_v2brew')","line":126,"column":19,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":126,"endColumn":27},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":134,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":134,"endColumn":38},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":135,"column":18,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":135,"endColumn":25},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"设备删除成功\"。建议使用: tc('text_yes810')","line":136,"column":23,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":136,"endColumn":31},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `string | number | bigint | boolean | ReactElement<unknown, string | JSXElementConstructor<any>> | Iterable<ReactNode> | ... 4 more ... | undefined`.","line":139,"column":21,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":139,"endColumn":45},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .message on an `any` value.","line":139,"column":28,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":139,"endColumn":35},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"删除设备失败\"。建议使用: tc('text_n9mxnj')","line":142,"column":19,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":142,"endColumn":27},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":151,"column":24,"nodeType":"MemberExpression","messageId":"anyAssignment","endLine":151,"endColumn":40},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .equipment_type on an `any` value.","line":151,"column":26,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":151,"endColumn":40},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Computed name [s.equipment_type] resolves to an `any` value.","line":154,"column":36,"nodeType":"MemberExpression","messageId":"unsafeComputedMemberAccess","endLine":154,"endColumn":52},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .equipment_type on an `any` value.","line":154,"column":38,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":154,"endColumn":52},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .count on an `any` value.","line":156,"column":56,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":156,"endColumn":61},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `string`.","line":159,"column":31,"nodeType":"MemberExpression","messageId":"unsafeArgument","endLine":159,"endColumn":40},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .avg_oee on an `any` value.","line":159,"column":33,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":159,"endColumn":40},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"设备台账\"。建议使用: {tc('text_hzz2f3')}","line":170,"column":44,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":172,"endColumn":15},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"新增设备\"。建议使用: {tc('text_d7dlrr')}","line":183,"column":48,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":185,"endColumn":13},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"全部类型\"。建议使用: {tc('text_avglbk')}","line":203,"column":43,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":203,"endColumn":47},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"设备编码\"。建议使用: {tc('text_i06agk')}","line":224,"column":32,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":224,"endColumn":36},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"设备名称\"。建议使用: {tc('text_hzyzbg')}","line":225,"column":32,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":225,"endColumn":36},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"位置\"。建议使用: {tc('text_e6rl')}","line":228,"column":32,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":228,"endColumn":34},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"产能\"。建议使用: {tc('text_e33q')}","line":229,"column":32,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":229,"endColumn":34},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"设备编码\"。建议使用: {tc('text_i06agk')}","line":300,"column":24,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":302,"endColumn":19},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"设备名称\"。建议使用: {tc('text_hzyzbg')}","line":312,"column":24,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":314,"endColumn":19},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"设备类型\"。建议使用: {tc('text_i05o3d')}","line":325,"column":24,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":325,"endColumn":28},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"型号\"。建议使用: {tc('text_fcng')}","line":353,"column":24,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":353,"endColumn":26},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"安装位置\"。建议使用: {tc('text_c41wyl')}","line":361,"column":24,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":361,"endColumn":28},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"额定产能\"。建议使用: {tc('text_jmrm37')}","line":371,"column":24,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":371,"endColumn":28},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"运行状态\"。建议使用: {tc('text_ipinbb')}","line":382,"column":24,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":382,"endColumn":28},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"取消\"。建议使用: {tc('text_ev02')}","line":411,"column":76,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":413,"endColumn":13},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"保存\"。建议使用: {tc('text_e32z')}","line":414,"column":87,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":416,"endColumn":13}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":43,"fixableErrorCount":0,"fixableWarningCount":0,"source":"'use client';\r\n\r\nimport { authFetch } from '@/lib/auth-fetch';\r\nimport { EQUIPMENT_TYPE_LABEL, EQUIPMENT_STATUS_LABEL } from '@/lib/status-labels';\r\nimport { useState, useEffect, useCallback } from 'react';\r\nimport { MainLayout } from '@/components/layout';\r\nimport { Card, CardContent, CardHeader, CardTitle, CardDescription } from '@/components/ui/card';\r\nimport { Button } from '@/components/ui/button';\r\nimport { Input } from '@/components/ui/input';\r\nimport { Label } from '@/components/ui/label';\r\nimport { Badge } from '@/components/ui/badge';\r\nimport {\r\n Dialog,\r\n DialogContent,\r\n DialogHeader,\r\n DialogTitle,\r\n DialogDescription,\r\n DialogFooter,\r\n} from '@/components/ui/dialog';\r\nimport {\r\n Select,\r\n SelectContent,\r\n SelectItem,\r\n SelectTrigger,\r\n SelectValue,\r\n} from '@/components/ui/select';\r\nimport {\r\n Table,\r\n TableBody,\r\n TableCell,\r\n TableHead,\r\n TableHeader,\r\n TableRow,\r\n} from '@/components/ui/table';\r\nimport { Textarea } from '@/components/ui/textarea';\r\nimport { Plus, Edit, Trash2, Search, RefreshCw, Cpu } from 'lucide-react';\r\nimport { toast } from 'sonner';\r\nimport { useTranslations } from 'next-intl';\r\n\r\ninterface Equipment {\r\n id: number;\r\n equipment_code: string;\r\n equipment_name: string;\r\n equipment_type: number;\r\n brand: string;\r\n model: string;\r\n serial_no: string;\r\n location: string;\r\n purchase_date: string;\r\n rated_capacity: number;\r\n oee: number;\r\n availability: number;\r\n performance: number;\r\n quality_rate: number;\r\n current_status: number;\r\n status: number;\r\n remark: string;\r\n}\r\n\r\nconst EQUIPMENT_TYPES = EQUIPMENT_TYPE_LABEL;\r\n\r\nconst CURRENT_STATUS: Record<number, { label: string; color: string }> = {\r\n 1: { label: EQUIPMENT_STATUS_LABEL[1], color: 'bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-300' },\r\n 2: { label: EQUIPMENT_STATUS_LABEL[2], color: 'bg-yellow-100 text-yellow-800 dark:bg-yellow-900/30 dark:text-yellow-300' },\r\n 3: { label: EQUIPMENT_STATUS_LABEL[3], color: 'bg-red-100 text-red-800 dark:bg-red-900/30 dark:text-red-300' },\r\n 4: { label: EQUIPMENT_STATUS_LABEL[4], color: 'bg-gray-100 text-gray-800 dark:bg-gray-700 dark:text-gray-200' },\r\n};\r\n\r\nexport default function EquipmentPage() {\r\n // 翻译钩子\r\n const tc = useTranslations('Common');\r\n\r\n const [list, setList] = useState<Equipment[]>([]);\r\n const [loading, setLoading] = useState(false);\r\n const [keyword, setKeyword] = useState('');\r\n const [typeFilter, setTypeFilter] = useState('all');\r\n const [dialogOpen, setDialogOpen] = useState(false);\r\n const [editing, setEditing] = useState(false);\r\n const [form, setForm] = useState<Partial<Equipment>>({});\r\n const [typeStats, setTypeStats] = useState<Loose[]>([]);\r\n\r\n const fetchData = useCallback(async () => {\r\n setLoading(true);\r\n try {\r\n const params = new URLSearchParams();\r\n if (keyword) params.append('keyword', keyword);\r\n if (typeFilter !== 'all') params.append('equipment_type', typeFilter);\r\n const res = await authFetch(`/api/equipment?${params.toString()}`);\r\n const result = await res.json();\r\n if (result.success) {\r\n setList(result.data?.list || []);\r\n setTypeStats(result.data?.typeStats || []);\r\n }\r\n } catch {\r\n toast.error('获取设备列表失败');\r\n } finally {\r\n setLoading(false);\r\n }\r\n }, [keyword, typeFilter]);\r\n\r\n useEffect(() => {\r\n fetchData();\r\n }, [fetchData]);\r\n\r\n const saveEquipment = async () => {\r\n if (!form.equipment_code || !form.equipment_name) {\r\n toast.error('请填写设备编码和名称');\r\n return;\r\n }\r\n try {\r\n const method = editing ? 'PUT' : 'POST';\r\n const res = await authFetch('/api/equipment', {\r\n method,\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify(form),\r\n });\r\n const result = await res.json();\r\n if (result.success) {\r\n toast.success(editing ? '设备更新成功' : '设备创建成功');\r\n setDialogOpen(false);\r\n fetchData();\r\n } else {\r\n toast.error(result.message || tc('error'));\r\n }\r\n } catch {\r\n toast.error('保存设备失败');\r\n }\r\n };\r\n\r\n const deleteEquipment = async (id: number) => {\r\n if (!confirm(tc('confirmDeleteDevice'))) return;\r\n try {\r\n const res = await authFetch(`/api/equipment?id=${id}`, { method: 'DELETE' });\r\n const result = await res.json();\r\n if (result.success) {\r\n toast.success('设备删除成功');\r\n fetchData();\r\n } else {\r\n toast.error(result.message || '删除失败');\r\n }\r\n } catch {\r\n toast.error('删除设备失败');\r\n }\r\n };\r\n\r\n return (\r\n <MainLayout title=\"设备管理\">\r\n <div className=\"space-y-6\">\r\n <div className=\"grid grid-cols-1 md:grid-cols-5 gap-4\">\r\n {typeStats.map((s: Loose) => (\r\n <Card key={s.equipment_type}>\r\n <CardContent className=\"pt-4\">\r\n <div className=\"text-sm text-gray-500\">\r\n {EQUIPMENT_TYPES[s.equipment_type] || '其他'}\r\n </div>\r\n <div className=\"text-2xl font-bold\">{s.count}</div>\r\n <div className=\"text-xs text-gray-400\">\r\n {tc('avgOee')}\r\n {parseFloat(s.avg_oee).toFixed(1)}%\r\n </div>\r\n </CardContent>\r\n </Card>\r\n ))}\r\n </div>\r\n\r\n <Card>\r\n <CardHeader className=\"flex flex-row items-center justify-between\">\r\n <div>\r\n <CardTitle className=\"flex items-center gap-2\">\r\n <Cpu className=\"w-5 h-5\" />\r\n 设备台账\r\n </CardTitle>\r\n <CardDescription>{tc('equipmentAccount')}</CardDescription>\r\n </div>\r\n <Button\r\n onClick={() => {\r\n setForm({});\r\n setEditing(false);\r\n setDialogOpen(true);\r\n }}\r\n className=\"bg-blue-600 hover:bg-blue-700\"\r\n >\r\n <Plus className=\"w-4 h-4 mr-2\" />\r\n 新增设备\r\n </Button>\r\n </CardHeader>\r\n <CardContent>\r\n <div className=\"flex gap-3 mb-4\">\r\n <div className=\"relative flex-1\">\r\n <Search className=\"absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-gray-400\" />\r\n <Input\r\n placeholder=\"搜索设备编码/名称/品牌...\"\r\n value={keyword}\r\n onChange={(e) => setKeyword(e.target.value)}\r\n className=\"pl-9\"\r\n />\r\n </div>\r\n <Select value={typeFilter} onValueChange={setTypeFilter}>\r\n <SelectTrigger className=\"w-[140px]\">\r\n <SelectValue placeholder=\"设备类型\" />\r\n </SelectTrigger>\r\n <SelectContent>\r\n <SelectItem value=\"all\">全部类型</SelectItem>\r\n {Object.entries(EQUIPMENT_TYPES).map(([k, v]) => (\r\n <SelectItem key={k} value={k}>\r\n {v}\r\n </SelectItem>\r\n ))}\r\n </SelectContent>\r\n </Select>\r\n <Button variant=\"outline\" onClick={fetchData}>\r\n <RefreshCw className=\"w-4 h-4\" />\r\n </Button>\r\n </div>\r\n\r\n {loading ? (\r\n <div className=\"flex justify-center py-8\">\r\n <RefreshCw className=\"w-6 h-6 animate-spin\" />\r\n </div>\r\n ) : (\r\n <Table>\r\n <TableHeader>\r\n <TableRow>\r\n <TableHead>设备编码</TableHead>\r\n <TableHead>设备名称</TableHead>\r\n <TableHead>{tc('type')}</TableHead>\r\n <TableHead>{tc('brandModel')}</TableHead>\r\n <TableHead>位置</TableHead>\r\n <TableHead>产能</TableHead>\r\n <TableHead>OEE</TableHead>\r\n <TableHead>{tc('status')}</TableHead>\r\n <TableHead className=\"text-right\">{tc('actions')}</TableHead>\r\n </TableRow>\r\n </TableHeader>\r\n <TableBody>\r\n {list.map((eq) => (\r\n <TableRow key={eq.id}>\r\n <TableCell className=\"font-medium\">{eq.equipment_code}</TableCell>\r\n <TableCell>{eq.equipment_name}</TableCell>\r\n <TableCell>\r\n <Badge variant=\"outline\">\r\n {EQUIPMENT_TYPES[eq.equipment_type] || '其他'}\r\n </Badge>\r\n </TableCell>\r\n <TableCell>\r\n {eq.brand} {eq.model}\r\n </TableCell>\r\n <TableCell>{eq.location || '-'}</TableCell>\r\n <TableCell>{eq.rated_capacity || '-'}</TableCell>\r\n <TableCell>\r\n <span\r\n className={`font-medium ${(eq.oee || 0) >= 85 ? 'text-green-600' : (eq.oee || 0) >= 70 ? 'text-yellow-600' : 'text-red-600'}`}\r\n >\r\n {eq.oee?.toFixed(1) || 0}%\r\n </span>\r\n </TableCell>\r\n <TableCell>\r\n <Badge\r\n className={CURRENT_STATUS[eq.current_status]?.color || 'bg-gray-100'}\r\n >\r\n {CURRENT_STATUS[eq.current_status]?.label || tc('unknown')}\r\n </Badge>\r\n </TableCell>\r\n <TableCell className=\"text-right\">\r\n <div className=\"flex justify-end gap-1\">\r\n <Button\r\n variant=\"ghost\"\r\n size=\"sm\"\r\n onClick={() => {\r\n setForm(eq);\r\n setEditing(true);\r\n setDialogOpen(true);\r\n }}\r\n >\r\n <Edit className=\"w-4 h-4\" />\r\n </Button>\r\n <Button variant=\"ghost\" size=\"sm\" onClick={() => deleteEquipment(eq.id)}>\r\n <Trash2 className=\"w-4 h-4 text-red-500\" />\r\n </Button>\r\n </div>\r\n </TableCell>\r\n </TableRow>\r\n ))}\r\n </TableBody>\r\n </Table>\r\n )}\r\n </CardContent>\r\n </Card>\r\n </div>\r\n\r\n <Dialog open={dialogOpen} onOpenChange={setDialogOpen}>\r\n <DialogContent className=\"max-w-lg\" resizable>\r\n <DialogHeader>\r\n <DialogTitle>{editing ? '编辑设备' : '新增设备'}</DialogTitle>\r\n <DialogDescription>{editing ? '修改设备信息' : '填写设备基本信息'}</DialogDescription>\r\n </DialogHeader>\r\n <div className=\"space-y-4 py-4\">\r\n <div className=\"grid grid-cols-2 gap-4\">\r\n <div className=\"space-y-2\">\r\n <Label>\r\n 设备编码\r\n <span className=\"text-red-500\">*</span>\r\n </Label>\r\n <Input\r\n value={form.equipment_code || ''}\r\n onChange={(e) => setForm({ ...form, equipment_code: e.target.value })}\r\n placeholder=\"如: EQP001\"\r\n disabled={editing}\r\n />\r\n </div>\r\n <div className=\"space-y-2\">\r\n <Label>\r\n 设备名称\r\n <span className=\"text-red-500\">*</span>\r\n </Label>\r\n <Input\r\n value={form.equipment_name || ''}\r\n onChange={(e) => setForm({ ...form, equipment_name: e.target.value })}\r\n placeholder=\"请输入设备名称\"\r\n />\r\n </div>\r\n </div>\r\n <div className=\"grid grid-cols-2 gap-4\">\r\n <div className=\"space-y-2\">\r\n <Label>设备类型</Label>\r\n <Select\r\n value={String(form.equipment_type ?? 1)}\r\n onValueChange={(v) => setForm({ ...form, equipment_type: parseInt(v) })}\r\n >\r\n <SelectTrigger>\r\n <SelectValue />\r\n </SelectTrigger>\r\n <SelectContent>\r\n {Object.entries(EQUIPMENT_TYPES).map(([k, v]) => (\r\n <SelectItem key={k} value={k}>\r\n {v}\r\n </SelectItem>\r\n ))}\r\n </SelectContent>\r\n </Select>\r\n </div>\r\n <div className=\"space-y-2\">\r\n <Label>{tc('brand')}</Label>\r\n <Input\r\n value={form.brand || ''}\r\n onChange={(e) => setForm({ ...form, brand: e.target.value })}\r\n placeholder={tc('brand')}\r\n />\r\n </div>\r\n </div>\r\n <div className=\"grid grid-cols-2 gap-4\">\r\n <div className=\"space-y-2\">\r\n <Label>型号</Label>\r\n <Input\r\n value={form.model || ''}\r\n onChange={(e) => setForm({ ...form, model: e.target.value })}\r\n placeholder=\"型号\"\r\n />\r\n </div>\r\n <div className=\"space-y-2\">\r\n <Label>安装位置</Label>\r\n <Input\r\n value={form.location || ''}\r\n onChange={(e) => setForm({ ...form, location: e.target.value })}\r\n placeholder=\"位置\"\r\n />\r\n </div>\r\n </div>\r\n <div className=\"grid grid-cols-2 gap-4\">\r\n <div className=\"space-y-2\">\r\n <Label>额定产能</Label>\r\n <Input\r\n type=\"number\"\r\n value={form.rated_capacity || ''}\r\n onChange={(e) =>\r\n setForm({ ...form, rated_capacity: parseFloat(e.target.value) || 0 })\r\n }\r\n placeholder=\"产能/小时\"\r\n />\r\n </div>\r\n <div className=\"space-y-2\">\r\n <Label>运行状态</Label>\r\n <Select\r\n value={String(form.current_status ?? 1)}\r\n onValueChange={(v) => setForm({ ...form, current_status: parseInt(v) })}\r\n >\r\n <SelectTrigger>\r\n <SelectValue />\r\n </SelectTrigger>\r\n <SelectContent>\r\n {Object.entries(CURRENT_STATUS).map(([k, v]) => (\r\n <SelectItem key={k} value={k}>\r\n {v.label}\r\n </SelectItem>\r\n ))}\r\n </SelectContent>\r\n </Select>\r\n </div>\r\n </div>\r\n <div className=\"space-y-2\">\r\n <Label>{tc('remark')}</Label>\r\n <Textarea\r\n value={form.remark || ''}\r\n onChange={(e) => setForm({ ...form, remark: e.target.value })}\r\n placeholder={tc('remark')}\r\n rows={2}\r\n />\r\n </div>\r\n </div>\r\n <DialogFooter>\r\n <Button variant=\"outline\" onClick={() => setDialogOpen(false)}>\r\n 取消\r\n </Button>\r\n <Button onClick={saveEquipment} className=\"bg-blue-600 hover:bg-blue-700\">\r\n 保存\r\n </Button>\r\n </DialogFooter>\r\n </DialogContent>\r\n </Dialog>\r\n </MainLayout>\r\n );\r\n}\r\n","usedDeprecatedRules":[]},{"filePath":"D:\\dcprint\\erp-project\\src\\app\\[locale]\\equipment\\repair\\page.tsx","messages":[{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"预防性维修\"。建议使用: tc('text_nld3bh')","line":50,"column":6,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":50,"endColumn":13},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"故障维修\"。建议使用: tc('text_dehxv5')","line":51,"column":6,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":51,"endColumn":12},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"紧急维修\"。建议使用: tc('text_g6yorc')","line":52,"column":6,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":52,"endColumn":12},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":85,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":85,"endColumn":38},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":86,"column":18,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":86,"endColumn":25},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction<Item[]>`.","line":87,"column":17,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":87,"endColumn":39},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":87,"column":24,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":87,"endColumn":28},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction<number>`.","line":88,"column":18,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":88,"endColumn":40},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":88,"column":25,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":88,"endColumn":29},{"ruleId":"react-hooks/set-state-in-effect","severity":1,"message":"Error: Calling setState synchronously within an effect can trigger cascading renders\n\nEffects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following:\n* Update external systems with the latest state from React.\n* Subscribe for updates from some external system, calling setState in a callback function when external state changes.\n\nCalling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect).\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\equipment\\repair\\page.tsx:93:5\n 91 | };\n 92 | useEffect(() => {\n> 93 | fetchData();\n | ^^^^^^^^^ Avoid calling setState() directly within an effect\n 94 | }, [page]);\n 95 |\n 96 | const handleSave = async () => {","line":93,"column":5,"nodeType":null,"endLine":93,"endColumn":14},{"ruleId":"react-hooks/exhaustive-deps","severity":1,"message":"React Hook useEffect has a missing dependency: 'fetchData'. Either include it or remove the dependency array.","line":94,"column":6,"nodeType":"ArrayExpression","endLine":94,"endColumn":12,"suggestions":[{"desc":"Update the dependencies array to be: [fetchData, page]","fix":{"range":[2485,2491],"text":"[fetchData, page]"}}]},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":103,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":103,"endColumn":38},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":104,"column":18,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":104,"endColumn":25},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":111,"column":11,"nodeType":"Property","messageId":"anyAssignment","endLine":111,"endColumn":38},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .message on an `any` value.","line":111,"column":31,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":111,"endColumn":38},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":126,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":126,"endColumn":38},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":127,"column":18,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":127,"endColumn":25},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":139,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":139,"endColumn":38},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":140,"column":18,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":140,"endColumn":25},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"设备维修\"。建议使用: {tc('text_i061qb')}","line":153,"column":46,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":153,"endColumn":50},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"新增维修单\"。建议使用: {tc('text_gvv3wz')}","line":173,"column":48,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":175,"endColumn":13},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"维修单号\"。建议使用: {tc('text_gck5do')}","line":183,"column":50,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":183,"endColumn":54},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"设备编码\"。建议使用: {tc('text_i06agk')}","line":184,"column":50,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":184,"endColumn":54},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"设备名称\"。建议使用: {tc('text_hzyzbg')}","line":185,"column":50,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":185,"endColumn":54},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"故障日期\"。建议使用: {tc('text_dedt01')}","line":186,"column":50,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":186,"endColumn":54},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"故障描述\"。建议使用: {tc('text_dedoag')}","line":187,"column":50,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":187,"endColumn":54},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"维修类型\"。建议使用: {tc('text_gcr622')}","line":188,"column":50,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":188,"endColumn":54},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"维修人\"。建议使用: {tc('text_izg5c')}","line":189,"column":50,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":189,"endColumn":53},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"开始维修\"。建议使用: {tc('text_cd4fb9')}","line":221,"column":30,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":223,"endColumn":29},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"完成\"。建议使用: {tc('text_g3yc')}","line":231,"column":30,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":233,"endColumn":29},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"暂无记录\"。建议使用: {tc('text_dd1mmb')}","line":261,"column":87,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":263,"endColumn":21},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"共\"。建议使用: {tc('text_g35')}","line":271,"column":51,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":271,"endColumn":52},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"条\"。建议使用: {tc('text_kf5')}","line":271,"column":59,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":271,"endColumn":60},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"上一页\"。建议使用: {tc('text_btlof')}","line":278,"column":14,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":280,"endColumn":13},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"下一页\"。建议使用: {tc('text_btmf4')}","line":286,"column":14,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":288,"endColumn":13},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"新增维修单\"。建议使用: {tc('text_gvv3wz')}","line":294,"column":28,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":294,"endColumn":33},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"设备编码\"。建议使用: {tc('text_i06agk')}","line":298,"column":24,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":298,"endColumn":28},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"设备名称\"。建议使用: {tc('text_hzyzbg')}","line":305,"column":24,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":305,"endColumn":28},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"故障日期\"。建议使用: {tc('text_dedt01')}","line":312,"column":24,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":312,"endColumn":28},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"维修类型\"。建议使用: {tc('text_gcr622')}","line":320,"column":24,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":320,"endColumn":28},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"预防性维修\"。建议使用: {tc('text_nld3bh')}","line":329,"column":43,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":329,"endColumn":48},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"故障维修\"。建议使用: {tc('text_dehxv5')}","line":330,"column":43,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":330,"endColumn":47},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"紧急维修\"。建议使用: {tc('text_g6yorc')}","line":331,"column":43,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":331,"endColumn":47},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"维修人\"。建议使用: {tc('text_izg5c')}","line":336,"column":24,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":336,"endColumn":27},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"故障描述\"。建议使用: {tc('text_dedoag')}","line":343,"column":24,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":343,"endColumn":28},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"取消\"。建议使用: {tc('text_ev02')}","line":351,"column":78,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":353,"endColumn":15}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":46,"fixableErrorCount":0,"fixableWarningCount":0,"source":"'use client';\n\nimport { authFetch } from '@/lib/auth-fetch';\nimport { useEffect, useState } from 'react';\nimport { MainLayout } from '@/components/layout';\nimport { Card, CardContent } from '@/components/ui/card';\nimport { Button } from '@/components/ui/button';\nimport { Input } from '@/components/ui/input';\nimport { Badge } from '@/components/ui/badge';\nimport {\n Table,\n TableBody,\n TableCell,\n TableHead,\n TableHeader,\n TableRow,\n} from '@/components/ui/table';\nimport {\n Dialog,\n DialogContent,\n DialogHeader,\n DialogTitle,\n DialogFooter,\n} from '@/components/ui/dialog';\nimport { Label } from '@/components/ui/label';\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@/components/ui/select';\nimport { Plus, Search, Edit, Trash2 } from 'lucide-react';\nimport { useToast } from '@/hooks/use-toast';\nimport { UserSelect } from '@/components/ui/user-select';\nimport { useTranslations } from 'next-intl';\n\ninterface Item {\n id: number;\n repair_no: string;\n equipment_code: string;\n equipment_name: string;\n fault_date: string;\n fault_desc: string;\n repair_type: number;\n repair_person: string;\n status: number;\n}\nconst typeMap: Record<number, string> = {\n 1: '预防性维修',\n 2: '故障维修',\n 3: '紧急维修',\n};\n\nexport default function EquipmentRepairPage() {\n // 翻译钩子\n const tc = useTranslations('Common');\n\n const statusMap: Record<\n number,\n { label: string; variant: 'default' | 'secondary' | 'destructive' | 'outline' }\n > = {\n 1: { label: '待维修', variant: 'outline' },\n 2: { label: '维修中', variant: 'default' },\n 3: { label: '已完成', variant: 'secondary' },\n 4: { label: tc('closed'), variant: 'destructive' },\n };\n\n const { toast } = useToast();\n const [list, setList] = useState<Item[]>([]);\n const [total, setTotal] = useState(0);\n const [page, setPage] = useState(1);\n const [searchNo, setSearchNo] = useState('');\n const [showDialog, setShowDialog] = useState(false);\n const [editItem, setEditItem] = useState<Partial<Item>>({});\n\n const fetchData = async () => {\n try {\n const params = new URLSearchParams({\n page: String(page),\n pageSize: '20',\n repairNo: searchNo,\n });\n const res = await authFetch('/api/equipment/repair?' + params);\n const result = await res.json();\n if (result.success) {\n setList(result.data.list || []);\n setTotal(result.data.total || 0);\n }\n } catch {}\n };\n useEffect(() => {\n fetchData();\n }, [page]);\n\n const handleSave = async () => {\n try {\n const res = await authFetch('/api/equipment/repair', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(editItem),\n });\n const result = await res.json();\n if (result.success) {\n toast({ title: tc('createSuccess') });\n setShowDialog(false);\n fetchData();\n } else {\n toast({\n title: tc('operationFailed'),\n description: result.message,\n variant: 'destructive',\n });\n }\n } catch {\n toast({ title: tc('operationFailed'), variant: 'destructive' });\n }\n };\n const handleStatusChange = async (id: number, status: number) => {\n try {\n const res = await authFetch('/api/equipment/repair', {\n method: 'PUT',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ id, status }),\n });\n const result = await res.json();\n if (result.success) {\n toast({ title: tc('updateSuccess') });\n fetchData();\n }\n } catch {\n toast({ title: tc('operationFailed'), variant: 'destructive' });\n }\n };\n const handleDelete = async (id: number) => {\n if (!confirm(tc('confirmDeleteMsg'))) return;\n try {\n const res = await authFetch('/api/equipment/repair?id=' + id, { method: 'DELETE' });\n const result = await res.json();\n if (result.success) {\n toast({ title: tc('deleteSuccess') });\n fetchData();\n }\n } catch {\n toast({ title: tc('operationFailed'), variant: 'destructive' });\n }\n };\n\n return (\n <MainLayout>\n <div className=\"p-6 space-y-6\">\n <div className=\"flex items-center justify-between\">\n <h1 className=\"text-2xl font-bold\">设备维修</h1>\n <div className=\"flex gap-2\">\n <div className=\"flex items-center gap-2\">\n <Input\n placeholder={tc('searchOrderNo')}\n value={searchNo}\n onChange={(e) => setSearchNo(e.target.value)}\n className=\"w-36 h-8 text-sm\"\n />\n <Button size=\"sm\" variant=\"outline\" onClick={fetchData}>\n <Search className=\"h-3 w-3\" />\n </Button>\n </div>\n <Button\n size=\"sm\"\n onClick={() => {\n setEditItem({});\n setShowDialog(true);\n }}\n >\n <Plus className=\"h-3 w-3 mr-1\" />\n 新增维修单\n </Button>\n </div>\n </div>\n <Card>\n <CardContent className=\"p-0\">\n <Table>\n <TableHeader>\n <TableRow>\n <TableHead className=\"text-xs\">维修单号</TableHead>\n <TableHead className=\"text-xs\">设备编码</TableHead>\n <TableHead className=\"text-xs\">设备名称</TableHead>\n <TableHead className=\"text-xs\">故障日期</TableHead>\n <TableHead className=\"text-xs\">故障描述</TableHead>\n <TableHead className=\"text-xs\">维修类型</TableHead>\n <TableHead className=\"text-xs\">维修人</TableHead>\n <TableHead className=\"text-xs\">{tc('status')}</TableHead>\n <TableHead className=\"text-xs\">{tc('actions')}</TableHead>\n </TableRow>\n </TableHeader>\n <TableBody>\n {list.map((item) => {\n const st = statusMap[item.status] || statusMap[1];\n return (\n <TableRow key={item.id}>\n <TableCell className=\"text-xs font-mono\">{item.repair_no}</TableCell>\n <TableCell className=\"text-xs\">{item.equipment_code || '-'}</TableCell>\n <TableCell className=\"text-xs\">{item.equipment_name || '-'}</TableCell>\n <TableCell className=\"text-xs\">{item.fault_date || '-'}</TableCell>\n <TableCell className=\"text-xs max-w-32 truncate\">\n {item.fault_desc || '-'}\n </TableCell>\n <TableCell className=\"text-xs\">{typeMap[item.repair_type] || '-'}</TableCell>\n <TableCell className=\"text-xs\">{item.repair_person || '-'}</TableCell>\n <TableCell>\n <Badge variant={st.variant} className=\"text-xs\">\n {st.label}\n </Badge>\n </TableCell>\n <TableCell>\n <div className=\"flex gap-1\">\n {item.status === 1 && (\n <Button\n size=\"sm\"\n variant=\"ghost\"\n className=\"h-6 text-xs px-2\"\n onClick={() => handleStatusChange(item.id, 2)}\n >\n 开始维修\n </Button>\n )}\n {item.status === 2 && (\n <Button\n size=\"sm\"\n variant=\"ghost\"\n className=\"h-6 text-xs px-2\"\n onClick={() => handleStatusChange(item.id, 3)}\n >\n 完成\n </Button>\n )}\n <Button\n size=\"sm\"\n variant=\"ghost\"\n className=\"h-6 w-6 p-0\"\n onClick={() => {\n setEditItem(item);\n setShowDialog(true);\n }}\n >\n <Edit className=\"h-3 w-3\" />\n </Button>\n <Button\n size=\"sm\"\n variant=\"ghost\"\n className=\"h-6 w-6 p-0 text-red-600\"\n onClick={() => handleDelete(item.id)}\n >\n <Trash2 className=\"h-3 w-3\" />\n </Button>\n </div>\n </TableCell>\n </TableRow>\n );\n })}\n {list.length === 0 && (\n <TableRow>\n <TableCell colSpan={9} className=\"text-center text-gray-400 py-8\">\n 暂无记录\n </TableCell>\n </TableRow>\n )}\n </TableBody>\n </Table>\n </CardContent>\n </Card>\n <div className=\"flex items-center justify-between\">\n <span className=\"text-sm text-gray-500\">共{total}条</span>\n <div className=\"flex gap-2\">\n <Button\n size=\"sm\"\n variant=\"outline\"\n disabled={page <= 1}\n onClick={() => setPage((p) => p - 1)}\n >\n 上一页\n </Button>\n <Button\n size=\"sm\"\n variant=\"outline\"\n disabled={page * 20 >= total}\n onClick={() => setPage((p) => p + 1)}\n >\n 下一页\n </Button>\n </div>\n </div>\n <Dialog open={showDialog} onOpenChange={setShowDialog}>\n <DialogContent className=\"max-w-lg\" resizable>\n <DialogHeader>\n <DialogTitle>新增维修单</DialogTitle>\n </DialogHeader>\n <div className=\"grid grid-cols-2 gap-4\">\n <div>\n <Label>设备编码</Label>\n <Input\n value={editItem.equipment_code || ''}\n onChange={(e) => setEditItem({ ...editItem, equipment_code: e.target.value })}\n />\n </div>\n <div>\n <Label>设备名称</Label>\n <Input\n value={editItem.equipment_name || ''}\n onChange={(e) => setEditItem({ ...editItem, equipment_name: e.target.value })}\n />\n </div>\n <div>\n <Label>故障日期</Label>\n <Input\n type=\"date\"\n value={editItem.fault_date || ''}\n onChange={(e) => setEditItem({ ...editItem, fault_date: e.target.value })}\n />\n </div>\n <div>\n <Label>维修类型</Label>\n <Select\n value={String(editItem.repair_type || 2)}\n onValueChange={(v) => setEditItem({ ...editItem, repair_type: Number(v) })}\n >\n <SelectTrigger>\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"1\">预防性维修</SelectItem>\n <SelectItem value=\"2\">故障维修</SelectItem>\n <SelectItem value=\"3\">紧急维修</SelectItem>\n </SelectContent>\n </Select>\n </div>\n <div>\n <Label>维修人</Label>\n <UserSelect\n value={editItem.repair_person || ''}\n onChange={(v) => setEditItem({ ...editItem, repair_person: v })}\n />\n </div>\n <div className=\"col-span-2\">\n <Label>故障描述</Label>\n <Input\n value={editItem.fault_desc || ''}\n onChange={(e) => setEditItem({ ...editItem, fault_desc: e.target.value })}\n />\n </div>\n </div>\n <DialogFooter>\n <Button variant=\"outline\" onClick={() => setShowDialog(false)}>\n 取消\n </Button>\n <Button onClick={handleSave}>{tc('save')}</Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n </div>\n </MainLayout>\n );\n}\n","usedDeprecatedRules":[]},{"filePath":"D:\\dcprint\\erp-project\\src\\app\\[locale]\\equipment\\scrap\\page.tsx","messages":[{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":67,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":67,"endColumn":38},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":68,"column":18,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":68,"endColumn":25},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction<Item[]>`.","line":69,"column":17,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":69,"endColumn":39},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":69,"column":24,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":69,"endColumn":28},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction<number>`.","line":70,"column":18,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":70,"endColumn":40},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":70,"column":25,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":70,"endColumn":29},{"ruleId":"react-hooks/set-state-in-effect","severity":1,"message":"Error: Calling setState synchronously within an effect can trigger cascading renders\n\nEffects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following:\n* Update external systems with the latest state from React.\n* Subscribe for updates from some external system, calling setState in a callback function when external state changes.\n\nCalling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect).\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\equipment\\scrap\\page.tsx:75:5\n 73 | };\n 74 | useEffect(() => {\n> 75 | fetchData();\n | ^^^^^^^^^ Avoid calling setState() directly within an effect\n 76 | }, [page]);\n 77 |\n 78 | const handleSave = async () => {","line":75,"column":5,"nodeType":null,"endLine":75,"endColumn":14},{"ruleId":"react-hooks/exhaustive-deps","severity":1,"message":"React Hook useEffect has a missing dependency: 'fetchData'. Either include it or remove the dependency array.","line":76,"column":6,"nodeType":"ArrayExpression","endLine":76,"endColumn":12,"suggestions":[{"desc":"Update the dependencies array to be: [fetchData, page]","fix":{"range":[2182,2188],"text":"[fetchData, page]"}}]},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":85,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":85,"endColumn":38},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":86,"column":18,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":86,"endColumn":25},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":93,"column":11,"nodeType":"Property","messageId":"anyAssignment","endLine":93,"endColumn":38},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .message on an `any` value.","line":93,"column":31,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":93,"endColumn":38},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":108,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":108,"endColumn":38},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":109,"column":18,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":109,"endColumn":25},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":121,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":121,"endColumn":38},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":122,"column":18,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":122,"endColumn":25},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"设备报废\"。建议使用: {tc('text_i01bab')}","line":135,"column":46,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":135,"endColumn":50},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"新增报废\"。建议使用: {tc('text_d76n7s')}","line":155,"column":48,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":157,"endColumn":13},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"报废单号\"。建议使用: {tc('text_cu689o')}","line":165,"column":50,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":165,"endColumn":54},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"设备编码\"。建议使用: {tc('text_i06agk')}","line":166,"column":50,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":166,"endColumn":54},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"设备名称\"。建议使用: {tc('text_hzyzbg')}","line":167,"column":50,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":167,"endColumn":54},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"报废日期\"。建议使用: {tc('text_cu9hpw')}","line":168,"column":50,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":168,"endColumn":54},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"报废原因\"。建议使用: {tc('text_cu6am3')}","line":169,"column":50,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":169,"endColumn":54},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"原值\"。建议使用: {tc('text_enwd')}","line":170,"column":50,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":170,"endColumn":52},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"净值\"。建议使用: {tc('text_ecfw')}","line":171,"column":50,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":171,"endColumn":52},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"审批\"。建议使用: {tc('text_g4jc')}","line":209,"column":30,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":211,"endColumn":29},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"确认报废\"。建议使用: {tc('text_frrbww')}","line":219,"column":30,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":221,"endColumn":29},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"暂无记录\"。建议使用: {tc('text_dd1mmb')}","line":249,"column":88,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":251,"endColumn":21},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"共\"。建议使用: {tc('text_g35')}","line":259,"column":51,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":259,"endColumn":52},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"条\"。建议使用: {tc('text_kf5')}","line":259,"column":59,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":259,"endColumn":60},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"上一页\"。建议使用: {tc('text_btlof')}","line":266,"column":14,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":268,"endColumn":13},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"下一页\"。建议使用: {tc('text_btmf4')}","line":274,"column":14,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":276,"endColumn":13},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"设备编码\"。建议使用: {tc('text_i06agk')}","line":286,"column":24,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":286,"endColumn":28},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"设备名称\"。建议使用: {tc('text_hzyzbg')}","line":293,"column":24,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":293,"endColumn":28},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"报废日期\"。建议使用: {tc('text_cu9hpw')}","line":300,"column":24,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":300,"endColumn":28},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"原值\"。建议使用: {tc('text_enwd')}","line":315,"column":24,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":315,"endColumn":26},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"净值\"。建议使用: {tc('text_ecfw')}","line":325,"column":24,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":325,"endColumn":26},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"报废原因\"。建议使用: {tc('text_cu6am3')}","line":333,"column":24,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":333,"endColumn":28},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"取消\"。建议使用: {tc('text_ev02')}","line":341,"column":78,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":343,"endColumn":15}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":39,"fixableErrorCount":0,"fixableWarningCount":0,"source":"'use client';\n\nimport { authFetch } from '@/lib/auth-fetch';\nimport { useEffect, useState } from 'react';\nimport { MainLayout } from '@/components/layout';\nimport { Card, CardContent } from '@/components/ui/card';\nimport { Button } from '@/components/ui/button';\nimport { Input } from '@/components/ui/input';\nimport { Badge } from '@/components/ui/badge';\nimport {\n Table,\n TableBody,\n TableCell,\n TableHead,\n TableHeader,\n TableRow,\n} from '@/components/ui/table';\nimport {\n Dialog,\n DialogContent,\n DialogHeader,\n DialogTitle,\n DialogFooter,\n} from '@/components/ui/dialog';\nimport { Label } from '@/components/ui/label';\nimport { Plus, Search, Edit, Trash2 } from 'lucide-react';\nimport { useToast } from '@/hooks/use-toast';\nimport { useTranslations } from 'next-intl';\n\ninterface Item {\n id: number;\n scrap_no: string;\n equipment_code: string;\n equipment_name: string;\n scrap_date: string;\n scrap_reason: string;\n original_value: number;\n net_value: number;\n approval_person: string;\n status: number;\n}\nexport default function EquipmentScrapPage() {\n // 翻译钩子\n const tc = useTranslations('Common');\n\n const statusMap: Record<\n number,\n { label: string; variant: 'default' | 'secondary' | 'destructive' | 'outline' }\n > = {\n 1: { label: tc('pending'), variant: 'outline' },\n 2: { label: tc('approved'), variant: 'default' },\n 3: { label: '已报废', variant: 'destructive' },\n };\n\n const { toast } = useToast();\n const [list, setList] = useState<Item[]>([]);\n const [total, setTotal] = useState(0);\n const [page, setPage] = useState(1);\n const [searchNo, setSearchNo] = useState('');\n const [showDialog, setShowDialog] = useState(false);\n const [editItem, setEditItem] = useState<Partial<Item>>({});\n\n const fetchData = async () => {\n try {\n const params = new URLSearchParams({ page: String(page), pageSize: '20', scrapNo: searchNo });\n const res = await authFetch('/api/equipment/scrap?' + params);\n const result = await res.json();\n if (result.success) {\n setList(result.data.list || []);\n setTotal(result.data.total || 0);\n }\n } catch {}\n };\n useEffect(() => {\n fetchData();\n }, [page]);\n\n const handleSave = async () => {\n try {\n const res = await authFetch('/api/equipment/scrap', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(editItem),\n });\n const result = await res.json();\n if (result.success) {\n toast({ title: tc('createSuccess') });\n setShowDialog(false);\n fetchData();\n } else {\n toast({\n title: tc('operationFailed'),\n description: result.message,\n variant: 'destructive',\n });\n }\n } catch {\n toast({ title: tc('operationFailed'), variant: 'destructive' });\n }\n };\n const handleStatusChange = async (id: number, status: number) => {\n try {\n const res = await authFetch('/api/equipment/scrap', {\n method: 'PUT',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ id, status }),\n });\n const result = await res.json();\n if (result.success) {\n toast({ title: tc('updateSuccess') });\n fetchData();\n }\n } catch {\n toast({ title: tc('operationFailed'), variant: 'destructive' });\n }\n };\n const handleDelete = async (id: number) => {\n if (!confirm(tc('confirmDeleteMsg'))) return;\n try {\n const res = await authFetch('/api/equipment/scrap?id=' + id, { method: 'DELETE' });\n const result = await res.json();\n if (result.success) {\n toast({ title: tc('deleteSuccess') });\n fetchData();\n }\n } catch {\n toast({ title: tc('operationFailed'), variant: 'destructive' });\n }\n };\n\n return (\n <MainLayout>\n <div className=\"p-6 space-y-6\">\n <div className=\"flex items-center justify-between\">\n <h1 className=\"text-2xl font-bold\">设备报废</h1>\n <div className=\"flex gap-2\">\n <div className=\"flex items-center gap-2\">\n <Input\n placeholder={tc('searchOrderNo')}\n value={searchNo}\n onChange={(e) => setSearchNo(e.target.value)}\n className=\"w-36 h-8 text-sm\"\n />\n <Button size=\"sm\" variant=\"outline\" onClick={fetchData}>\n <Search className=\"h-3 w-3\" />\n </Button>\n </div>\n <Button\n size=\"sm\"\n onClick={() => {\n setEditItem({});\n setShowDialog(true);\n }}\n >\n <Plus className=\"h-3 w-3 mr-1\" />\n 新增报废\n </Button>\n </div>\n </div>\n <Card>\n <CardContent className=\"p-0\">\n <Table>\n <TableHeader>\n <TableRow>\n <TableHead className=\"text-xs\">报废单号</TableHead>\n <TableHead className=\"text-xs\">设备编码</TableHead>\n <TableHead className=\"text-xs\">设备名称</TableHead>\n <TableHead className=\"text-xs\">报废日期</TableHead>\n <TableHead className=\"text-xs\">报废原因</TableHead>\n <TableHead className=\"text-xs\">原值</TableHead>\n <TableHead className=\"text-xs\">净值</TableHead>\n <TableHead className=\"text-xs\">{tc('approver')}</TableHead>\n <TableHead className=\"text-xs\">{tc('status')}</TableHead>\n <TableHead className=\"text-xs\">{tc('actions')}</TableHead>\n </TableRow>\n </TableHeader>\n <TableBody>\n {list.map((item) => {\n const st = statusMap[item.status] || statusMap[1];\n return (\n <TableRow key={item.id}>\n <TableCell className=\"text-xs font-mono\">{item.scrap_no}</TableCell>\n <TableCell className=\"text-xs\">{item.equipment_code || '-'}</TableCell>\n <TableCell className=\"text-xs\">{item.equipment_name || '-'}</TableCell>\n <TableCell className=\"text-xs\">{item.scrap_date || '-'}</TableCell>\n <TableCell className=\"text-xs max-w-28 truncate\">\n {item.scrap_reason || '-'}\n </TableCell>\n <TableCell className=\"text-xs\">\n ¥{Number(item.original_value || 0).toFixed(2)}\n </TableCell>\n <TableCell className=\"text-xs\">\n ¥{Number(item.net_value || 0).toFixed(2)}\n </TableCell>\n <TableCell className=\"text-xs\">{item.approval_person || '-'}</TableCell>\n <TableCell>\n <Badge variant={st.variant} className=\"text-xs\">\n {st.label}\n </Badge>\n </TableCell>\n <TableCell>\n <div className=\"flex gap-1\">\n {item.status === 1 && (\n <Button\n size=\"sm\"\n variant=\"ghost\"\n className=\"h-6 text-xs px-2\"\n onClick={() => handleStatusChange(item.id, 2)}\n >\n 审批\n </Button>\n )}\n {item.status === 2 && (\n <Button\n size=\"sm\"\n variant=\"ghost\"\n className=\"h-6 text-xs px-2\"\n onClick={() => handleStatusChange(item.id, 3)}\n >\n 确认报废\n </Button>\n )}\n <Button\n size=\"sm\"\n variant=\"ghost\"\n className=\"h-6 w-6 p-0\"\n onClick={() => {\n setEditItem(item);\n setShowDialog(true);\n }}\n >\n <Edit className=\"h-3 w-3\" />\n </Button>\n <Button\n size=\"sm\"\n variant=\"ghost\"\n className=\"h-6 w-6 p-0 text-red-600\"\n onClick={() => handleDelete(item.id)}\n >\n <Trash2 className=\"h-3 w-3\" />\n </Button>\n </div>\n </TableCell>\n </TableRow>\n );\n })}\n {list.length === 0 && (\n <TableRow>\n <TableCell colSpan={10} className=\"text-center text-gray-400 py-8\">\n 暂无记录\n </TableCell>\n </TableRow>\n )}\n </TableBody>\n </Table>\n </CardContent>\n </Card>\n <div className=\"flex items-center justify-between\">\n <span className=\"text-sm text-gray-500\">共{total}条</span>\n <div className=\"flex gap-2\">\n <Button\n size=\"sm\"\n variant=\"outline\"\n disabled={page <= 1}\n onClick={() => setPage((p) => p - 1)}\n >\n 上一页\n </Button>\n <Button\n size=\"sm\"\n variant=\"outline\"\n disabled={page * 20 >= total}\n onClick={() => setPage((p) => p + 1)}\n >\n 下一页\n </Button>\n </div>\n </div>\n <Dialog open={showDialog} onOpenChange={setShowDialog}>\n <DialogContent className=\"max-w-lg\" resizable>\n <DialogHeader>\n <DialogTitle>{tc('scrapTitle')}</DialogTitle>\n </DialogHeader>\n <div className=\"grid grid-cols-2 gap-4\">\n <div>\n <Label>设备编码</Label>\n <Input\n value={editItem.equipment_code || ''}\n onChange={(e) => setEditItem({ ...editItem, equipment_code: e.target.value })}\n />\n </div>\n <div>\n <Label>设备名称</Label>\n <Input\n value={editItem.equipment_name || ''}\n onChange={(e) => setEditItem({ ...editItem, equipment_name: e.target.value })}\n />\n </div>\n <div>\n <Label>报废日期</Label>\n <Input\n type=\"date\"\n value={editItem.scrap_date || ''}\n onChange={(e) => setEditItem({ ...editItem, scrap_date: e.target.value })}\n />\n </div>\n <div>\n <Label>{tc('approver')}</Label>\n <Input\n value={editItem.approval_person || ''}\n onChange={(e) => setEditItem({ ...editItem, approval_person: e.target.value })}\n />\n </div>\n <div>\n <Label>原值</Label>\n <Input\n type=\"number\"\n value={editItem.original_value || ''}\n onChange={(e) =>\n setEditItem({ ...editItem, original_value: Number(e.target.value) })\n }\n />\n </div>\n <div>\n <Label>净值</Label>\n <Input\n type=\"number\"\n value={editItem.net_value || ''}\n onChange={(e) => setEditItem({ ...editItem, net_value: Number(e.target.value) })}\n />\n </div>\n <div className=\"col-span-2\">\n <Label>报废原因</Label>\n <Input\n value={editItem.scrap_reason || ''}\n onChange={(e) => setEditItem({ ...editItem, scrap_reason: e.target.value })}\n />\n </div>\n </div>\n <DialogFooter>\n <Button variant=\"outline\" onClick={() => setShowDialog(false)}>\n 取消\n </Button>\n <Button onClick={handleSave}>{tc('save')}</Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n </div>\n </MainLayout>\n );\n}\n","usedDeprecatedRules":[]},{"filePath":"D:\\dcprint\\erp-project\\src\\app\\[locale]\\error.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\dcprint\\erp-project\\src\\app\\[locale]\\finance\\cost\\page.tsx","messages":[{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":76,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":76,"endColumn":38},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":77,"column":18,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":77,"endColumn":25},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction<CostItem[]>`.","line":78,"column":17,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":78,"endColumn":40},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":78,"column":24,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":78,"endColumn":28},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction<number>`.","line":79,"column":18,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":79,"endColumn":41},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":79,"column":25,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":79,"endColumn":29},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":87,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":87,"endColumn":38},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":88,"column":18,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":88,"endColumn":25},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":88,"column":36,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":88,"endColumn":40},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction<{ material: number; labor: number; overhead: number; outsource: number; total: number; }>`.","line":89,"column":20,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":89,"endColumn":55},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":89,"column":27,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":89,"endColumn":31},{"ruleId":"react-hooks/exhaustive-deps","severity":1,"message":"React Hook useCallback has a missing dependency: 'summary'. Either include it or remove the dependency array. You can also do a functional update 'setSummary(s => ...)' if you only need 'summary' in the 'setSummary' call.","line":92,"column":6,"nodeType":"ArrayExpression","endLine":92,"endColumn":8,"suggestions":[{"desc":"Update the dependencies array to be: [summary]","fix":{"range":[2357,2359],"text":"[summary]"}}]},{"ruleId":"react-hooks/set-state-in-effect","severity":1,"message":"Error: Calling setState synchronously within an effect can trigger cascading renders\n\nEffects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following:\n* Update external systems with the latest state from React.\n* Subscribe for updates from some external system, calling setState in a callback function when external state changes.\n\nCalling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect).\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\finance\\cost\\page.tsx:95:5\n 93 |\n 94 | useEffect(() => {\n> 95 | fetchData();\n | ^^^^^^^^^ Avoid calling setState() directly within an effect\n 96 | fetchSummary();\n 97 | }, [fetchData, fetchSummary]);\n 98 |","line":95,"column":5,"nodeType":null,"endLine":95,"endColumn":14}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":13,"fixableErrorCount":0,"fixableWarningCount":0,"source":"'use client';\n\nimport { useState, useEffect, useCallback } from 'react';\nimport { MainLayout } from '@/components/layout';\nimport { Card, CardContent } from '@/components/ui/card';\nimport { Button } from '@/components/ui/button';\nimport { Input } from '@/components/ui/input';\nimport { Badge } from '@/components/ui/badge';\nimport {\n Table,\n TableBody,\n TableCell,\n TableHead,\n TableHeader,\n TableRow,\n} from '@/components/ui/table';\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@/components/ui/select';\nimport { Search, RefreshCw } from 'lucide-react';\nimport { useTranslations } from 'next-intl';\nimport { authFetch } from '@/lib/auth-fetch';\n\ninterface CostItem {\n id: number;\n cost_no: string;\n cost_type: string;\n source_type: string;\n source_no: string;\n department: string;\n amount: number;\n cost_date: string;\n description: string;\n status: number;\n}\n\nconst costTypeMap: Record<string, string> = {\n material: 'materialCost',\n labor: 'laborCost',\n overhead: 'overheadCost',\n outsource: 'outsourceCost',\n other: 'otherCost',\n};\n\nexport default function CostPage() {\n // 翻译钩子\n const t = useTranslations('Finance');\n const tc = useTranslations('Common');\n\n const [list, setList] = useState<CostItem[]>([]);\n const [total, setTotal] = useState(0);\n const [page, setPage] = useState(1);\n const [keyword, setSearch] = useState('');\n const [typeFilter, setTypeFilter] = useState('');\n const [summary, setSummary] = useState({\n material: 0,\n labor: 0,\n overhead: 0,\n outsource: 0,\n total: 0,\n });\n\n const fetchData = useCallback(async () => {\n try {\n const params = new URLSearchParams({\n page: String(page),\n pageSize: '20',\n keyword,\n cost_type: typeFilter,\n });\n const res = await authFetch('/api/finance/cost?' + params);\n const result = await res.json();\n if (result.success) {\n setList(result.data?.list || []);\n setTotal(result.data?.total || 0);\n }\n } catch {}\n }, [page, keyword, typeFilter]);\n\n const fetchSummary = useCallback(async () => {\n try {\n const res = await authFetch('/api/finance/stats');\n const result = await res.json();\n if (result.success && result.data) {\n setSummary(result.data.cost_summary || summary);\n }\n } catch {}\n }, []);\n\n useEffect(() => {\n fetchData();\n fetchSummary();\n }, [fetchData, fetchSummary]);\n\n const formatAmount = (amount: number) => ((amount || 0) / 100).toFixed(2);\n\n return (\n <MainLayout>\n <div className=\"space-y-4\">\n <div className=\"flex items-center justify-between\">\n <h2 className=\"text-2xl font-bold\">{t('costManagement')}</h2>\n <div className=\"flex items-center gap-2\">\n <div className=\"relative w-64\">\n <Search className=\"absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground\" />\n <Input\n placeholder={t('searchNoOrDesc')}\n value={keyword}\n onChange={(e) => setSearch(e.target.value)}\n className=\"pl-10 h-9\"\n />\n </div>\n <Select\n value={typeFilter}\n onValueChange={(v) => {\n setTypeFilter(v === 'all' ? '' : v);\n setPage(1);\n }}\n >\n <SelectTrigger className=\"w-32 h-9\">\n <SelectValue placeholder={t('costType')} />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"all\">{tc('all')}</SelectItem>\n <SelectItem value=\"material\">{t('materialCost')}</SelectItem>\n <SelectItem value=\"labor\">{t('laborCost')}</SelectItem>\n <SelectItem value=\"overhead\">{t('overheadCost')}</SelectItem>\n <SelectItem value=\"outsource\">{t('outsourceCost')}</SelectItem>\n </SelectContent>\n </Select>\n <Button variant=\"outline\" size=\"sm\" onClick={fetchData}>\n <RefreshCw className=\"h-4 w-4\" />\n {tc('refresh')}\n </Button>\n </div>\n </div>\n\n <div className=\"grid grid-cols-5 gap-4\">\n <Card>\n <CardContent className=\"pt-6\">\n <div className=\"text-sm text-muted-foreground\">{t('materialCost')}</div>\n <div className=\"text-2xl font-bold\">¥{formatAmount(summary.material)}</div>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"pt-6\">\n <div className=\"text-sm text-muted-foreground\">{t('laborCost')}</div>\n <div className=\"text-2xl font-bold\">¥{formatAmount(summary.labor)}</div>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"pt-6\">\n <div className=\"text-sm text-muted-foreground\">{t('overheadCost')}</div>\n <div className=\"text-2xl font-bold\">¥{formatAmount(summary.overhead)}</div>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"pt-6\">\n <div className=\"text-sm text-muted-foreground\">{t('outsourceCost')}</div>\n <div className=\"text-2xl font-bold\">¥{formatAmount(summary.outsource)}</div>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"pt-6\">\n <div className=\"text-sm text-muted-foreground\">{t('totalCost')}</div>\n <div className=\"text-2xl font-bold text-red-600\">¥{formatAmount(summary.total)}</div>\n </CardContent>\n </Card>\n </div>\n\n <Card>\n <CardContent className=\"p-0\">\n <Table>\n <TableHeader>\n <TableRow>\n <TableHead>{t('costNo')}</TableHead>\n <TableHead>{t('costType')}</TableHead>\n <TableHead>{t('sourceNo')}</TableHead>\n <TableHead>{tc('department')}</TableHead>\n <TableHead className=\"text-right\">{tc('amount')}</TableHead>\n <TableHead>{tc('date')}</TableHead>\n <TableHead>{t('description')}</TableHead>\n </TableRow>\n </TableHeader>\n <TableBody>\n {list.length === 0 ? (\n <TableRow>\n <TableCell colSpan={7} className=\"text-center py-8 text-muted-foreground\">\n {t('noData')}\n </TableCell>\n </TableRow>\n ) : (\n list.map((c) => (\n <TableRow key={c.id}>\n <TableCell className=\"font-mono text-sm\">{c.cost_no}</TableCell>\n <TableCell>\n <Badge variant=\"outline\">\n {t(costTypeMap[c.cost_type]) || c.cost_type}\n </Badge>\n </TableCell>\n <TableCell className=\"font-mono text-sm\">{c.source_no}</TableCell>\n <TableCell>{c.department}</TableCell>\n <TableCell className=\"text-right\">¥{formatAmount(c.amount)}</TableCell>\n <TableCell>{c.cost_date?.slice(0, 10)}</TableCell>\n <TableCell className=\"max-w-xs truncate\">{c.description}</TableCell>\n </TableRow>\n ))\n )}\n </TableBody>\n </Table>\n </CardContent>\n </Card>\n\n <div className=\"flex items-center justify-between text-sm text-muted-foreground\">\n <span>{tc('totalRecords', { total })}</span>\n <div className=\"flex gap-2\">\n <Button\n variant=\"outline\"\n size=\"sm\"\n disabled={page <= 1}\n onClick={() => setPage((p) => p - 1)}\n >\n {t('previousPage')}\n </Button>\n <Button\n variant=\"outline\"\n size=\"sm\"\n disabled={page * 20 >= total}\n onClick={() => setPage((p) => p + 1)}\n >\n {t('nextPage')}\n </Button>\n </div>\n </div>\n </div>\n </MainLayout>\n );\n}\n","usedDeprecatedRules":[]},{"filePath":"D:\\dcprint\\erp-project\\src\\app\\[locale]\\finance\\costs\\page.tsx","messages":[{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":61,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":61,"endColumn":83},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":62,"column":18,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":62,"endColumn":25},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction<CostRecord[]>`.","line":63,"column":18,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":63,"endColumn":40},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":63,"column":25,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":63,"endColumn":29},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction<number>`.","line":64,"column":18,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":64,"endColumn":40},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":64,"column":25,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":64,"endColumn":29},{"ruleId":"react-hooks/set-state-in-effect","severity":1,"message":"Error: Calling setState synchronously within an effect can trigger cascading renders\n\nEffects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following:\n* Update external systems with the latest state from React.\n* Subscribe for updates from some external system, calling setState in a callback function when external state changes.\n\nCalling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect).\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\finance\\costs\\page.tsx:74:5\n 72 |\n 73 | useEffect(() => {\n> 74 | loadCosts();\n | ^^^^^^^^^ Avoid calling setState() directly within an effect\n 75 | }, [page]);\n 76 |\n 77 | const handleCalculate = async () => {","line":74,"column":5,"nodeType":null,"endLine":74,"endColumn":14},{"ruleId":"react-hooks/exhaustive-deps","severity":1,"message":"React Hook useEffect has a missing dependency: 'loadCosts'. Either include it or remove the dependency array.","line":75,"column":6,"nodeType":"ArrayExpression","endLine":75,"endColumn":12,"suggestions":[{"desc":"Update the dependencies array to be: [loadCosts, page]","fix":{"range":[1891,1897],"text":"[loadCosts, page]"}}]},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":83,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":85,"endColumn":9},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":86,"column":18,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":86,"endColumn":25},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `string | number | bigint | boolean | ReactElement<unknown, string | JSXElementConstructor<any>> | Iterable<ReactNode> | ... 4 more ... | undefined`.","line":87,"column":23,"nodeType":"MemberExpression","messageId":"unsafeArgument","endLine":87,"endColumn":37},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .message on an `any` value.","line":87,"column":30,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":87,"endColumn":37},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `string | number | bigint | boolean | ReactElement<unknown, string | JSXElementConstructor<any>> | Iterable<ReactNode> | ... 4 more ... | undefined`.","line":92,"column":21,"nodeType":"MemberExpression","messageId":"unsafeArgument","endLine":92,"endColumn":35},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .message on an `any` value.","line":92,"column":28,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":92,"endColumn":35}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":14,"fixableErrorCount":0,"fixableWarningCount":0,"source":"'use client';\n\nimport React, { useState, useEffect } from 'react';\nimport { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';\nimport { useTranslations } from 'next-intl';\nimport { Button } from '@/components/ui/button';\nimport {\n Table,\n TableBody,\n TableCell,\n TableHead,\n TableHeader,\n TableRow,\n} from '@/components/ui/table';\nimport {\n Dialog,\n DialogContent,\n DialogHeader,\n DialogTitle,\n DialogFooter,\n} from '@/components/ui/dialog';\nimport { Input } from '@/components/ui/input';\nimport { Label } from '@/components/ui/label';\nimport { ApiClient } from '@/lib/api-client';\nimport { formatDate, formatAmount } from '@/lib/utils';\nimport { toast } from 'sonner';\nimport { RefreshCw, Calculator } from 'lucide-react';\n\ninterface CostRecord {\n id: number;\n work_order_id: number;\n work_order_no: string;\n plan_qty: number;\n completed_qty: number;\n material_cost: number;\n labor_cost: number;\n manufacturing_cost: number;\n total_cost: number;\n unit_cost: number;\n calculate_time: string;\n status: number;\n}\n\nexport default function CostsPage() {\n // 翻译钩子\n const tc = useTranslations('Common');\n\n const [costs, setCosts] = useState<CostRecord[]>([]);\n const [loading, setLoading] = useState(false);\n const [page, _setPage] = useState(1);\n const [_total, setTotal] = useState(0);\n\n const [showCalc, setShowCalc] = useState(false);\n const [calcWorkOrderId, setCalcWorkOrderId] = useState('');\n\n const pageSize = 20;\n\n const loadCosts = async () => {\n setLoading(true);\n try {\n const result = await ApiClient.get('/api/finance/costs', { page, pageSize });\n if (result.success) {\n setCosts(result.data.list || []);\n setTotal(result.data.total || 0);\n }\n } catch {\n toast.error(tc('loadCostFailed'));\n } finally {\n setLoading(false);\n }\n };\n\n useEffect(() => {\n loadCosts();\n }, [page]);\n\n const handleCalculate = async () => {\n if (!calcWorkOrderId) {\n toast.error(tc('enterWorkOrderId'));\n return;\n }\n try {\n const result = await ApiClient.post('/api/finance/costs', {\n workOrderId: Number(calcWorkOrderId),\n });\n if (result.success) {\n toast.success(result.message);\n setShowCalc(false);\n setCalcWorkOrderId('');\n loadCosts();\n } else {\n toast.error(result.message);\n }\n } catch {\n toast.error(tc('calcFailed'));\n }\n };\n\n return (\n <div className=\"container mx-auto py-6 space-y-6\">\n <div className=\"flex justify-between items-center\">\n <h1 className=\"text-2xl font-bold\">{tc('costAccountingTitle')}</h1>\n <div className=\"flex gap-2\">\n <Button variant=\"outline\" onClick={() => setShowCalc(true)}>\n <Calculator className=\"w-4 h-4 mr-2\" />\n {tc('calcCostButton')}\n </Button>\n <Button onClick={loadCosts} disabled={loading}>\n <RefreshCw className={`w-4 h-4 mr-2 ${loading ? 'animate-spin' : ''}`} />\n {tc('refresh')}\n </Button>\n </div>\n </div>\n\n <Card>\n <CardHeader>\n <CardTitle>{tc('costListTitle')}</CardTitle>\n </CardHeader>\n <CardContent>\n <Table>\n <TableHeader>\n <TableRow>\n <TableHead>{tc('workOrderNo')}</TableHead>\n <TableHead>{tc('planQty')}</TableHead>\n <TableHead>{tc('completedQty')}</TableHead>\n <TableHead>{tc('materialCost')}</TableHead>\n <TableHead>{tc('laborCost')}</TableHead>\n <TableHead>{tc('manufacturingCost')}</TableHead>\n <TableHead>{tc('totalCost')}</TableHead>\n <TableHead>{tc('unitCost')}</TableHead>\n <TableHead>{tc('calculateTime')}</TableHead>\n </TableRow>\n </TableHeader>\n <TableBody>\n {costs.map((cost) => (\n <TableRow key={cost.id}>\n <TableCell className=\"font-medium\">{cost.work_order_no}</TableCell>\n <TableCell>{cost.plan_qty}</TableCell>\n <TableCell>{cost.completed_qty}</TableCell>\n <TableCell>{formatAmount(cost.material_cost)}</TableCell>\n <TableCell>{formatAmount(cost.labor_cost)}</TableCell>\n <TableCell>{formatAmount(cost.manufacturing_cost)}</TableCell>\n <TableCell className=\"font-bold\">{formatAmount(cost.total_cost)}</TableCell>\n <TableCell className=\"text-blue-600 font-medium\">\n {formatAmount(cost.unit_cost)}\n </TableCell>\n <TableCell>{formatDate(cost.calculate_time)}</TableCell>\n </TableRow>\n ))}\n {costs.length === 0 && (\n <TableRow>\n <TableCell colSpan={9} className=\"text-center text-muted-foreground py-8\">\n {tc('noData')}\n </TableCell>\n </TableRow>\n )}\n </TableBody>\n </Table>\n </CardContent>\n </Card>\n\n {/* 计算成本弹窗 */}\n <Dialog open={showCalc} onOpenChange={setShowCalc}>\n <DialogContent>\n <DialogHeader>\n <DialogTitle>{tc('calcCostTitle')}</DialogTitle>\n </DialogHeader>\n <div className=\"space-y-4 py-4\">\n <div>\n <Label>{tc('workOrderId')}</Label>\n <Input\n value={calcWorkOrderId}\n onChange={(e) => setCalcWorkOrderId(e.target.value)}\n placeholder={tc('enterWorkOrderId')}\n />\n </div>\n </div>\n <DialogFooter>\n <Button variant=\"outline\" onClick={() => setShowCalc(false)}>\n {tc('cancel')}\n </Button>\n <Button onClick={handleCalculate}>\n <Calculator className=\"w-4 h-4 mr-2\" />\n {tc('calculate')}\n </Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n </div>\n );\n}\n","usedDeprecatedRules":[]},{"filePath":"D:\\dcprint\\erp-project\\src\\app\\[locale]\\finance\\error.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\dcprint\\erp-project\\src\\app\\[locale]\\finance\\page.tsx","messages":[{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe array destructuring of a tuple element with an `any` value.","line":143,"column":10,"nodeType":"Identifier","messageId":"unsafeArrayPatternFromTuple","endLine":143,"endColumn":20},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":191,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":191,"endColumn":36},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":192,"column":16,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":192,"endColumn":23},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction<Receivable[]>`.","line":193,"column":24,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":193,"endColumn":45},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":193,"column":29,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":193,"endColumn":33},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":194,"column":18,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":194,"endColumn":22},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":195,"column":44,"nodeType":"Property","messageId":"anyAssignment","endLine":195,"endColumn":73},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":195,"column":61,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":195,"endColumn":65},{"ruleId":"react-hooks/exhaustive-deps","severity":1,"message":"React Hook useCallback has a missing dependency: 'tc'. Either include it or remove the dependency array.","line":203,"column":6,"nodeType":"ArrayExpression","endLine":203,"endColumn":29,"suggestions":[{"desc":"Update the dependencies array to be: [keyword, statusFilter, tc]","fix":{"range":[6082,6105],"text":"[keyword, statusFilter, tc]"}}]},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":213,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":213,"endColumn":36},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":214,"column":16,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":214,"endColumn":23},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction<Payable[]>`.","line":215,"column":21,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":215,"endColumn":42},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":215,"column":26,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":215,"endColumn":30},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":216,"column":18,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":216,"endColumn":22},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":217,"column":44,"nodeType":"Property","messageId":"anyAssignment","endLine":217,"endColumn":70},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":217,"column":58,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":217,"endColumn":62},{"ruleId":"react-hooks/exhaustive-deps","severity":1,"message":"React Hook useCallback has a missing dependency: 'tc'. Either include it or remove the dependency array.","line":225,"column":6,"nodeType":"ArrayExpression","endLine":225,"endColumn":29,"suggestions":[{"desc":"Update the dependencies array to be: [keyword, statusFilter, tc]","fix":{"range":[6837,6860],"text":"[keyword, statusFilter, tc]"}}]},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":231,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":231,"endColumn":36},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":232,"column":16,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":232,"endColumn":23},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction<ReceiptRecord[]>`.","line":233,"column":21,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":233,"endColumn":42},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":233,"column":26,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":233,"endColumn":30},{"ruleId":"react-hooks/exhaustive-deps","severity":1,"message":"React Hook useCallback has a missing dependency: 'tc'. Either include it or remove the dependency array.","line":240,"column":6,"nodeType":"ArrayExpression","endLine":240,"endColumn":8,"suggestions":[{"desc":"Update the dependencies array to be: [tc]","fix":{"range":[7267,7269],"text":"[tc]"}}]},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":246,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":246,"endColumn":36},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":247,"column":16,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":247,"endColumn":23},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction<PaymentRecord[]>`.","line":248,"column":21,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":248,"endColumn":42},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":248,"column":26,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":248,"endColumn":30},{"ruleId":"react-hooks/exhaustive-deps","severity":1,"message":"React Hook useCallback has a missing dependency: 'tc'. Either include it or remove the dependency array.","line":255,"column":6,"nodeType":"ArrayExpression","endLine":255,"endColumn":8,"suggestions":[{"desc":"Update the dependencies array to be: [tc]","fix":{"range":[7676,7678],"text":"[tc]"}}]},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":260,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":260,"endColumn":36},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":261,"column":16,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":261,"endColumn":23},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction<any[]>`.","line":262,"column":22,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":262,"endColumn":56},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":262,"column":27,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":262,"endColumn":31},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":262,"column":46,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":262,"endColumn":50},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":270,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":270,"endColumn":36},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":271,"column":16,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":271,"endColumn":23},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction<any[]>`.","line":272,"column":22,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":272,"endColumn":56},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":272,"column":27,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":272,"endColumn":31},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":272,"column":46,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":272,"endColumn":50},{"ruleId":"react-hooks/set-state-in-effect","severity":1,"message":"Error: Calling setState synchronously within an effect can trigger cascading renders\n\nEffects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following:\n* Update external systems with the latest state from React.\n* Subscribe for updates from some external system, calling setState in a callback function when external state changes.\n\nCalling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect).\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\finance\\page.tsx:278:5\n 276 |\n 277 | useEffect(() => {\n> 278 | fetchCustomers();\n | ^^^^^^^^^^^^^^ Avoid calling setState() directly within an effect\n 279 | fetchSuppliers();\n 280 | }, []);\n 281 |","line":278,"column":5,"nodeType":null,"endLine":278,"endColumn":19},{"ruleId":"react-hooks/set-state-in-effect","severity":1,"message":"Error: Calling setState synchronously within an effect can trigger cascading renders\n\nEffects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following:\n* Update external systems with the latest state from React.\n* Subscribe for updates from some external system, calling setState in a callback function when external state changes.\n\nCalling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect).\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\finance\\page.tsx:283:37\n 281 |\n 282 | useEffect(() => {\n> 283 | if (activeTab === 'receivable') fetchReceivables();\n | ^^^^^^^^^^^^^^^^ Avoid calling setState() directly within an effect\n 284 | else if (activeTab === 'payable') fetchPayables();\n 285 | else if (activeTab === 'receipt') fetchReceipts();\n 286 | else if (activeTab === 'payment') fetchPayments();","line":283,"column":37,"nodeType":null,"endLine":283,"endColumn":53},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":306,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":306,"endColumn":36},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":307,"column":16,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":307,"endColumn":23},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `string | number | bigint | boolean | ReactElement<unknown, string | JSXElementConstructor<any>> | Iterable<ReactNode> | ... 4 more ... | undefined`.","line":313,"column":21,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":313,"endColumn":43},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .message on an `any` value.","line":313,"column":26,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":313,"endColumn":33},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":337,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":337,"endColumn":36},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":338,"column":16,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":338,"endColumn":23},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `string | number | bigint | boolean | ReactElement<unknown, string | JSXElementConstructor<any>> | Iterable<ReactNode> | ... 4 more ... | undefined`.","line":344,"column":21,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":344,"endColumn":43},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .message on an `any` value.","line":344,"column":26,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":344,"endColumn":33},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":368,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":368,"endColumn":36},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":369,"column":16,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":369,"endColumn":23},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `string | number | bigint | boolean | ReactElement<unknown, string | JSXElementConstructor<any>> | Iterable<ReactNode> | ... 4 more ... | undefined`.","line":382,"column":21,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":382,"endColumn":43},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .message on an `any` value.","line":382,"column":26,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":382,"endColumn":33},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":406,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":406,"endColumn":36},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":407,"column":16,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":407,"endColumn":23},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `string | number | bigint | boolean | ReactElement<unknown, string | JSXElementConstructor<any>> | Iterable<ReactNode> | ... 4 more ... | undefined`.","line":420,"column":21,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":420,"endColumn":43},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .message on an `any` value.","line":420,"column":26,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":420,"endColumn":33},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":431,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":431,"endColumn":36},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":432,"column":16,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":432,"endColumn":23},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `string | number | bigint | boolean | ReactElement<unknown, string | JSXElementConstructor<any>> | Iterable<ReactNode> | ... 4 more ... | undefined`.","line":436,"column":21,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":436,"endColumn":55},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .message on an `any` value.","line":436,"column":26,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":436,"endColumn":33},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"删除失败\"。建议使用: tc('text_azdahk')","line":439,"column":19,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":439,"endColumn":25},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":447,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":447,"endColumn":36},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":448,"column":16,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":448,"endColumn":23},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `string | number | bigint | boolean | ReactElement<unknown, string | JSXElementConstructor<any>> | Iterable<ReactNode> | ... 4 more ... | undefined`.","line":452,"column":21,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":452,"endColumn":55},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .message on an `any` value.","line":452,"column":26,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":452,"endColumn":33},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"删除失败\"。建议使用: tc('text_azdahk')","line":455,"column":19,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":455,"endColumn":25},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":466,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":466,"endColumn":36},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":467,"column":16,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":467,"endColumn":23},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":468,"column":28,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":468,"endColumn":32},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `string`.","line":478,"column":28,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":478,"endColumn":36},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"暂无付款记录\"。建议使用: {tc('text_myropj')}","line":843,"column":99,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":845,"endColumn":25},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":886,"column":40,"nodeType":"MemberExpression","messageId":"anyAssignment","endLine":886,"endColumn":44},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .id on an `any` value.","line":886,"column":42,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":886,"endColumn":44},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .id on an `any` value.","line":886,"column":62,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":886,"endColumn":64},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .customer_name on an `any` value.","line":887,"column":28,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":887,"endColumn":41},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":963,"column":40,"nodeType":"MemberExpression","messageId":"anyAssignment","endLine":963,"endColumn":44},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .id on an `any` value.","line":963,"column":42,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":963,"endColumn":44},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .id on an `any` value.","line":963,"column":62,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":963,"endColumn":64},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .supplier_name on an `any` value.","line":964,"column":28,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":964,"endColumn":41},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .receivable_no on an `any` value.","line":1204,"column":37,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":1204,"endColumn":50},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .customer_name on an `any` value.","line":1208,"column":37,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":1208,"endColumn":50},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .source_no on an `any` value.","line":1212,"column":37,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":1212,"endColumn":46},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .due_date on an `any` value.","line":1216,"column":37,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":1216,"endColumn":45},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .amount on an `any` value.","line":1220,"column":50,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":1220,"endColumn":56},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .received_amount on an `any` value.","line":1224,"column":50,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":1224,"endColumn":65},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .balance on an `any` value.","line":1228,"column":50,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":1228,"endColumn":57},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Computed name [detailData.status] resolves to an `any` value.","line":1233,"column":56,"nodeType":"MemberExpression","messageId":"unsafeComputedMemberAccess","endLine":1233,"endColumn":73},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .status on an `any` value.","line":1233,"column":67,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":1233,"endColumn":73},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Computed name [detailData.status] resolves to an `any` value.","line":1235,"column":46,"nodeType":"MemberExpression","messageId":"unsafeComputedMemberAccess","endLine":1235,"endColumn":63},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .status on an `any` value.","line":1235,"column":57,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":1235,"endColumn":63},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .status on an `any` value.","line":1235,"column":86,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":1235,"endColumn":92},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .payable_no on an `any` value.","line":1243,"column":37,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":1243,"endColumn":47},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .supplier_name on an `any` value.","line":1247,"column":37,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":1247,"endColumn":50},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .source_no on an `any` value.","line":1251,"column":37,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":1251,"endColumn":46},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .due_date on an `any` value.","line":1255,"column":37,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":1255,"endColumn":45},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .amount on an `any` value.","line":1259,"column":50,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":1259,"endColumn":56},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .paid_amount on an `any` value.","line":1263,"column":50,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":1263,"endColumn":61},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .balance on an `any` value.","line":1267,"column":50,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":1267,"endColumn":57},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Computed name [detailData.status] resolves to an `any` value.","line":1272,"column":53,"nodeType":"MemberExpression","messageId":"unsafeComputedMemberAccess","endLine":1272,"endColumn":70},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .status on an `any` value.","line":1272,"column":64,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":1272,"endColumn":70},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Computed name [detailData.status] resolves to an `any` value.","line":1274,"column":43,"nodeType":"MemberExpression","messageId":"unsafeComputedMemberAccess","endLine":1274,"endColumn":60},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .status on an `any` value.","line":1274,"column":54,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":1274,"endColumn":60},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .status on an `any` value.","line":1274,"column":83,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":1274,"endColumn":89},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .receipts on an `any` value.","line":1280,"column":29,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":1280,"endColumn":37},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .receipts on an `any` value.","line":1280,"column":52,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":1280,"endColumn":60},{"ruleId":"@typescript-eslint/no-unsafe-call","severity":1,"message":"Unsafe call of an `any` typed value.","line":1293,"column":26,"nodeType":"MemberExpression","messageId":"unsafeCall","endLine":1293,"endColumn":49},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .receipts on an `any` value.","line":1293,"column":37,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":1293,"endColumn":45},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":1294,"column":42,"nodeType":"MemberExpression","messageId":"anyAssignment","endLine":1294,"endColumn":46},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .id on an `any` value.","line":1294,"column":44,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":1294,"endColumn":46},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .receipt_no on an `any` value.","line":1295,"column":43,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":1295,"endColumn":53},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .amount on an `any` value.","line":1296,"column":57,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":1296,"endColumn":63},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Computed name [r.payment_method] resolves to an `any` value.","line":1298,"column":48,"nodeType":"MemberExpression","messageId":"unsafeComputedMemberAccess","endLine":1298,"endColumn":64},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .payment_method on an `any` value.","line":1298,"column":50,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":1298,"endColumn":64},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .payment_method on an `any` value.","line":1298,"column":71,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":1298,"endColumn":85},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .receipt_date on an `any` value.","line":1300,"column":43,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":1300,"endColumn":55},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .payments on an `any` value.","line":1307,"column":29,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":1307,"endColumn":37},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .payments on an `any` value.","line":1307,"column":52,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":1307,"endColumn":60},{"ruleId":"@typescript-eslint/no-unsafe-call","severity":1,"message":"Unsafe call of an `any` typed value.","line":1320,"column":26,"nodeType":"MemberExpression","messageId":"unsafeCall","endLine":1320,"endColumn":49},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .payments on an `any` value.","line":1320,"column":37,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":1320,"endColumn":45},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":1321,"column":42,"nodeType":"MemberExpression","messageId":"anyAssignment","endLine":1321,"endColumn":46},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .id on an `any` value.","line":1321,"column":44,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":1321,"endColumn":46},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .payment_no on an `any` value.","line":1322,"column":43,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":1322,"endColumn":53},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .amount on an `any` value.","line":1323,"column":57,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":1323,"endColumn":63},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Computed name [p.payment_method] resolves to an `any` value.","line":1325,"column":48,"nodeType":"MemberExpression","messageId":"unsafeComputedMemberAccess","endLine":1325,"endColumn":64},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .payment_method on an `any` value.","line":1325,"column":50,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":1325,"endColumn":64},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .payment_method on an `any` value.","line":1325,"column":71,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":1325,"endColumn":85},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .payment_date on an `any` value.","line":1327,"column":43,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":1327,"endColumn":55}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":126,"fixableErrorCount":0,"fixableWarningCount":0,"source":"'use client';\r\n\r\nimport { authFetch } from '@/lib/auth-fetch';\r\nimport { PAYMENT_METHOD_LABEL, RECEIVABLE_STATUS_LABEL, PAYABLE_STATUS_LABEL } from '@/lib/status-labels';\r\nimport { useState, useEffect, useCallback } from 'react';\r\nimport { MainLayout } from '@/components/layout';\r\nimport { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';\r\nimport { Button } from '@/components/ui/button';\r\nimport { Input } from '@/components/ui/input';\r\nimport { Label } from '@/components/ui/label';\r\nimport { Badge } from '@/components/ui/badge';\r\nimport {\r\n Dialog,\r\n DialogContent,\r\n DialogHeader,\r\n DialogTitle,\r\n DialogDescription,\r\n DialogFooter,\r\n} from '@/components/ui/dialog';\r\nimport {\r\n Select,\r\n SelectContent,\r\n SelectItem,\r\n SelectTrigger,\r\n SelectValue,\r\n} from '@/components/ui/select';\r\nimport {\r\n Table,\r\n TableBody,\r\n TableCell,\r\n TableHead,\r\n TableHeader,\r\n TableRow,\r\n} from '@/components/ui/table';\r\nimport { Textarea } from '@/components/ui/textarea';\r\nimport { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';\r\nimport {\r\n Plus,\r\n Search,\r\n RefreshCw,\r\n DollarSign,\r\n TrendingUp,\r\n TrendingDown,\r\n Eye,\r\n Trash2,\r\n} from 'lucide-react';\r\nimport { toast } from 'sonner';\r\nimport { useTranslations, useLocale } from 'next-intl';\r\n\r\ninterface Receivable {\r\n id: number;\r\n receivable_no: string;\r\n source_type: number;\r\n source_no: string;\r\n customer_id: number;\r\n customer_name: string;\r\n amount: number;\r\n received_amount: number;\r\n balance: number;\r\n due_date: string;\r\n status: number;\r\n remark: string;\r\n create_time: string;\r\n}\r\n\r\ninterface Payable {\r\n id: number;\r\n payable_no: string;\r\n source_type: number;\r\n source_no: string;\r\n supplier_id: number;\r\n supplier_name: string;\r\n amount: number;\r\n paid_amount: number;\r\n balance: number;\r\n due_date: string;\r\n status: number;\r\n remark: string;\r\n create_time: string;\r\n}\r\n\r\ninterface ReceiptRecord {\r\n id: number;\r\n receipt_no: string;\r\n receivable_id: number;\r\n customer_id: number;\r\n customer_name: string;\r\n amount: number;\r\n payment_method: string;\r\n receipt_date: string;\r\n remark: string;\r\n create_time: string;\r\n}\r\n\r\ninterface PaymentRecord {\r\n id: number;\r\n payment_no: string;\r\n payable_id: number;\r\n supplier_id: number;\r\n supplier_name: string;\r\n amount: number;\r\n payment_method: string;\r\n payment_date: string;\r\n remark: string;\r\n create_time: string;\r\n}\r\n\r\n\r\nconst RECEIVABLE_STATUS: Record<number, { label: string; color: string }> = {\r\n 1: { label: RECEIVABLE_STATUS_LABEL[1], color: 'bg-yellow-100 text-yellow-800' },\r\n 2: { label: RECEIVABLE_STATUS_LABEL[2], color: 'bg-blue-100 text-blue-800' },\r\n 3: { label: RECEIVABLE_STATUS_LABEL[3], color: 'bg-green-100 text-green-800' },\r\n};\r\n\r\nconst PAYABLE_STATUS: Record<number, { label: string; color: string }> = {\r\n 1: { label: PAYABLE_STATUS_LABEL[1], color: 'bg-yellow-100 text-yellow-800' },\r\n 2: { label: PAYABLE_STATUS_LABEL[2], color: 'bg-blue-100 text-blue-800' },\r\n 3: { label: PAYABLE_STATUS_LABEL[3], color: 'bg-green-100 text-green-800' },\r\n};\r\n\r\nconst PAYMENT_METHODS = PAYMENT_METHOD_LABEL;\r\n\r\nexport default function FinancePage() {\r\n // 翻译钩子\r\n const tc = useTranslations('Common');\r\n const t = useTranslations('Finance');\r\n const locale = useLocale();\r\n\r\n const [activeTab, setActiveTab] = useState('receivable');\r\n const [receivables, setReceivables] = useState<Receivable[]>([]);\r\n const [payables, setPayables] = useState<Payable[]>([]);\r\n const [receipts, setReceipts] = useState<ReceiptRecord[]>([]);\r\n const [payments, setPayments] = useState<PaymentRecord[]>([]);\r\n const [_loading, setLoading] = useState(false);\r\n const [keyword, setKeyword] = useState('');\r\n const [statusFilter, setStatusFilter] = useState('all');\r\n\r\n const [receivableDialogOpen, setReceivableDialogOpen] = useState(false);\r\n const [payableDialogOpen, setPayableDialogOpen] = useState(false);\r\n const [receiptDialogOpen, setReceiptDialogOpen] = useState(false);\r\n const [paymentDialogOpen, setPaymentDialogOpen] = useState(false);\r\n const [detailDialogOpen, setDetailDialogOpen] = useState(false);\r\n const [detailData, setDetailData] = useState<Loose>(null);\r\n const [detailType, setDetailType] = useState<'receivable' | 'payable'>('receivable');\r\n\r\n const [customers, setCustomers] = useState<Loose[]>([]);\r\n const [suppliers, setSuppliers] = useState<Loose[]>([]);\r\n\r\n const [receivableForm, setReceivableForm] = useState({\r\n customer_id: '',\r\n amount: '',\r\n source_no: '',\r\n due_date: '',\r\n remark: '',\r\n });\r\n const [payableForm, setPayableForm] = useState({\r\n supplier_id: '',\r\n amount: '',\r\n source_no: '',\r\n due_date: '',\r\n remark: '',\r\n });\r\n const [receiptForm, setReceiptForm] = useState({\r\n receivable_id: '',\r\n amount: '',\r\n payment_method: 'bank_transfer',\r\n receipt_date: '',\r\n remark: '',\r\n });\r\n const [paymentForm, setPaymentForm] = useState({\r\n payable_id: '',\r\n amount: '',\r\n payment_method: 'bank_transfer',\r\n payment_date: '',\r\n remark: '',\r\n });\r\n\r\n const [summary, setSummary] = useState({\r\n receivable: { total_amount: 0, total_received: 0, total_balance: 0, overdue_balance: 0 },\r\n payable: { total_amount: 0, total_paid: 0, total_balance: 0, overdue_balance: 0 },\r\n });\r\n\r\n const fetchReceivables = useCallback(async () => {\r\n setLoading(true);\r\n try {\r\n const params = new URLSearchParams();\r\n if (keyword) params.set('keyword', keyword);\r\n if (statusFilter !== 'all') params.set('status', statusFilter);\r\n params.set('pageSize', '50');\r\n const res = await authFetch(`/api/finance/receivables?${params}`);\r\n const data = await res.json();\r\n if (data.success) {\r\n setReceivables(data.data?.list || []);\r\n if (data.data?.summary) {\r\n setSummary((prev) => ({ ...prev, receivable: data.data.summary }));\r\n }\r\n }\r\n } catch (_e) {\r\n toast.error(tc('fetchReceivableListFailed'));\r\n } finally {\r\n setLoading(false);\r\n }\r\n }, [keyword, statusFilter]);\r\n\r\n const fetchPayables = useCallback(async () => {\r\n setLoading(true);\r\n try {\r\n const params = new URLSearchParams();\r\n if (keyword) params.set('keyword', keyword);\r\n if (statusFilter !== 'all') params.set('status', statusFilter);\r\n params.set('pageSize', '50');\r\n const res = await authFetch(`/api/finance/payable?${params}`);\r\n const data = await res.json();\r\n if (data.success) {\r\n setPayables(data.data?.list || []);\r\n if (data.data?.summary) {\r\n setSummary((prev) => ({ ...prev, payable: data.data.summary }));\r\n }\r\n }\r\n } catch (_e) {\r\n toast.error(tc('fetchPayableListFailed'));\r\n } finally {\r\n setLoading(false);\r\n }\r\n }, [keyword, statusFilter]);\r\n\r\n const fetchReceipts = useCallback(async () => {\r\n setLoading(true);\r\n try {\r\n const res = await authFetch('/api/finance/receipt?pageSize=50');\r\n const data = await res.json();\r\n if (data.success) {\r\n setReceipts(data.data?.list || []);\r\n }\r\n } catch (_e) {\r\n toast.error(tc('fetchReceiptListFailed'));\r\n } finally {\r\n setLoading(false);\r\n }\r\n }, []);\r\n\r\n const fetchPayments = useCallback(async () => {\r\n setLoading(true);\r\n try {\r\n const res = await authFetch('/api/finance/payment?pageSize=50');\r\n const data = await res.json();\r\n if (data.success) {\r\n setPayments(data.data?.list || []);\r\n }\r\n } catch (_e) {\r\n toast.error(tc('fetchPaymentListFailed'));\r\n } finally {\r\n setLoading(false);\r\n }\r\n }, []);\r\n\r\n const fetchCustomers = async () => {\r\n try {\r\n const res = await authFetch('/api/customers?pageSize=100');\r\n const data = await res.json();\r\n if (data.success) {\r\n setCustomers(data.data?.list || data.data || []);\r\n }\r\n } catch (_e) {}\r\n };\r\n\r\n const fetchSuppliers = async () => {\r\n try {\r\n const res = await authFetch('/api/purchase/suppliers?pageSize=100');\r\n const data = await res.json();\r\n if (data.success) {\r\n setSuppliers(data.data?.list || data.data || []);\r\n }\r\n } catch (_e) {}\r\n };\r\n\r\n useEffect(() => {\r\n fetchCustomers();\r\n fetchSuppliers();\r\n }, []);\r\n\r\n useEffect(() => {\r\n if (activeTab === 'receivable') fetchReceivables();\r\n else if (activeTab === 'payable') fetchPayables();\r\n else if (activeTab === 'receipt') fetchReceipts();\r\n else if (activeTab === 'payment') fetchPayments();\r\n }, [activeTab, fetchReceivables, fetchPayables, fetchReceipts, fetchPayments]);\r\n\r\n const handleCreateReceivable = async () => {\r\n if (!receivableForm.customer_id || !receivableForm.amount) {\r\n toast.error(tc('fillCustomerAndAmount'));\r\n return;\r\n }\r\n try {\r\n const res = await authFetch('/api/finance/receivables', {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify({\r\n customer_id: parseInt(receivableForm.customer_id),\r\n amount: parseFloat(receivableForm.amount),\r\n source_no: receivableForm.source_no || null,\r\n due_date: receivableForm.due_date || null,\r\n remark: receivableForm.remark || null,\r\n }),\r\n });\r\n const data = await res.json();\r\n if (data.success) {\r\n toast.success(tc('receivableCreated'));\r\n setReceivableDialogOpen(false);\r\n setReceivableForm({ customer_id: '', amount: '', source_no: '', due_date: '', remark: '' });\r\n fetchReceivables();\r\n } else {\r\n toast.error(data.message || '创建失败');\r\n }\r\n } catch (_e) {\r\n toast.error(tc('createReceivableFailed'));\r\n }\r\n };\r\n\r\n const handleCreatePayable = async () => {\r\n if (!payableForm.supplier_id || !payableForm.amount) {\r\n toast.error(tc('fillSupplierAndAmount'));\r\n return;\r\n }\r\n try {\r\n const res = await authFetch('/api/finance/payable', {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify({\r\n supplier_id: parseInt(payableForm.supplier_id),\r\n amount: parseFloat(payableForm.amount),\r\n source_no: payableForm.source_no || null,\r\n due_date: payableForm.due_date || null,\r\n remark: payableForm.remark || null,\r\n }),\r\n });\r\n const data = await res.json();\r\n if (data.success) {\r\n toast.success(tc('payableCreated'));\r\n setPayableDialogOpen(false);\r\n setPayableForm({ supplier_id: '', amount: '', source_no: '', due_date: '', remark: '' });\r\n fetchPayables();\r\n } else {\r\n toast.error(data.message || '创建失败');\r\n }\r\n } catch (_e) {\r\n toast.error(tc('createPayableFailed'));\r\n }\r\n };\r\n\r\n const handleCreateReceipt = async () => {\r\n if (!receiptForm.receivable_id || !receiptForm.amount) {\r\n toast.error(tc('selectReceivableAndAmount'));\r\n return;\r\n }\r\n try {\r\n const res = await authFetch('/api/finance/receipt', {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify({\r\n receivable_id: parseInt(receiptForm.receivable_id),\r\n amount: parseFloat(receiptForm.amount),\r\n payment_method: receiptForm.payment_method,\r\n receipt_date: receiptForm.receipt_date || null,\r\n remark: receiptForm.remark || null,\r\n }),\r\n });\r\n const data = await res.json();\r\n if (data.success) {\r\n toast.success(tc('receiptCreated'));\r\n setReceiptDialogOpen(false);\r\n setReceiptForm({\r\n receivable_id: '',\r\n amount: '',\r\n payment_method: 'bank_transfer',\r\n receipt_date: '',\r\n remark: '',\r\n });\r\n fetchReceipts();\r\n fetchReceivables();\r\n } else {\r\n toast.error(data.message || '创建失败');\r\n }\r\n } catch (_e) {\r\n toast.error(tc('createReceiptFailed'));\r\n }\r\n };\r\n\r\n const handleCreatePayment = async () => {\r\n if (!paymentForm.payable_id || !paymentForm.amount) {\r\n toast.error(tc('selectPayableAndAmount'));\r\n return;\r\n }\r\n try {\r\n const res = await authFetch('/api/finance/payment', {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify({\r\n payable_id: parseInt(paymentForm.payable_id),\r\n amount: parseFloat(paymentForm.amount),\r\n payment_method: paymentForm.payment_method,\r\n payment_date: paymentForm.payment_date || null,\r\n remark: paymentForm.remark || null,\r\n }),\r\n });\r\n const data = await res.json();\r\n if (data.success) {\r\n toast.success(tc('paymentCreated'));\r\n setPaymentDialogOpen(false);\r\n setPaymentForm({\r\n payable_id: '',\r\n amount: '',\r\n payment_method: 'bank_transfer',\r\n payment_date: '',\r\n remark: '',\r\n });\r\n fetchPayments();\r\n fetchPayables();\r\n } else {\r\n toast.error(data.message || '创建失败');\r\n }\r\n } catch (_e) {\r\n toast.error(tc('createPaymentFailed'));\r\n }\r\n };\r\n\r\n const handleDeleteReceivable = async (id: number) => {\r\n if (!confirm(tc('confirmDeleteReceivable'))) return;\r\n try {\r\n const res = await authFetch(`/api/finance/receivables?id=${id}`, { method: 'DELETE' });\r\n const data = await res.json();\r\n if (data.success) {\r\n toast.success(tc('deleteSuccess'));\r\n fetchReceivables();\r\n } else {\r\n toast.error(data.message || tc('deleteFailed'));\r\n }\r\n } catch (_e) {\r\n toast.error('删除失败');\r\n }\r\n };\r\n\r\n const handleDeletePayable = async (id: number) => {\r\n if (!confirm(tc('confirmDeletePayable'))) return;\r\n try {\r\n const res = await authFetch(`/api/finance/payable?id=${id}`, { method: 'DELETE' });\r\n const data = await res.json();\r\n if (data.success) {\r\n toast.success(tc('deleteSuccess'));\r\n fetchPayables();\r\n } else {\r\n toast.error(data.message || tc('deleteFailed'));\r\n }\r\n } catch (_e) {\r\n toast.error('删除失败');\r\n }\r\n };\r\n\r\n const handleViewDetail = async (id: number, type: 'receivable' | 'payable') => {\r\n try {\r\n const url =\r\n type === 'receivable'\r\n ? `/api/finance/receivables?id=${id}`\r\n : `/api/finance/payable?id=${id}`;\r\n const res = await authFetch(url);\r\n const data = await res.json();\r\n if (data.success) {\r\n setDetailData(data.data);\r\n setDetailType(type);\r\n setDetailDialogOpen(true);\r\n }\r\n } catch (_e) {\r\n toast.error(tc('fetchDetailFailed'));\r\n }\r\n };\r\n\r\n const formatAmount = (val: Loose) => {\r\n const num = parseFloat(val || 0);\r\n return isNaN(num)\r\n ? '0.00'\r\n : num.toLocaleString(locale, { minimumFractionDigits: 2, maximumFractionDigits: 2 });\r\n };\r\n\r\n return (\r\n <MainLayout title={t('title')}>\r\n <div className=\"space-y-6\">\r\n <div className=\"grid gap-4 md:grid-cols-4\">\r\n <Card>\r\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-2\">\r\n <CardTitle className=\"text-sm font-medium\">{t('totalReceivable')}</CardTitle>\r\n <TrendingUp className=\"h-4 w-4 text-green-600\" />\r\n </CardHeader>\r\n <CardContent>\r\n <div className=\"text-2xl font-bold text-green-600\">\r\n ¥{formatAmount(summary.receivable.total_amount)}\r\n </div>\r\n <p className=\"text-xs text-muted-foreground mt-1\">\r\n {tc('outstandingReceivableBalance')}\r\n {formatAmount(summary.receivable.total_balance)}\r\n </p>\r\n </CardContent>\r\n </Card>\r\n <Card>\r\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-2\">\r\n <CardTitle className=\"text-sm font-medium\">{t('totalPayable')}</CardTitle>\r\n <TrendingDown className=\"h-4 w-4 text-red-600\" />\r\n </CardHeader>\r\n <CardContent>\r\n <div className=\"text-2xl font-bold text-red-600\">\r\n ¥{formatAmount(summary.payable.total_amount)}\r\n </div>\r\n <p className=\"text-xs text-muted-foreground mt-1\">\r\n {tc('outstandingPayableBalance')}\r\n {formatAmount(summary.payable.total_balance)}\r\n </p>\r\n </CardContent>\r\n </Card>\r\n <Card>\r\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-2\">\r\n <CardTitle className=\"text-sm font-medium\">{tc('receivedAmount')}</CardTitle>\r\n <DollarSign className=\"h-4 w-4 text-blue-600\" />\r\n </CardHeader>\r\n <CardContent>\r\n <div className=\"text-2xl font-bold text-blue-600\">\r\n ¥{formatAmount(summary.receivable.total_received)}\r\n </div>\r\n <p className=\"text-xs text-muted-foreground mt-1\">\r\n {tc('overdueBalance')}\r\n {formatAmount(summary.receivable.overdue_balance)}\r\n </p>\r\n </CardContent>\r\n </Card>\r\n <Card>\r\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-2\">\r\n <CardTitle className=\"text-sm font-medium\">{tc('paidAmount')}</CardTitle>\r\n <DollarSign className=\"h-4 w-4 text-orange-600\" />\r\n </CardHeader>\r\n <CardContent>\r\n <div className=\"text-2xl font-bold text-orange-600\">\r\n ¥{formatAmount(summary.payable.total_paid)}\r\n </div>\r\n <p className=\"text-xs text-muted-foreground mt-1\">\r\n {tc('overdueBalance')}\r\n {formatAmount(summary.payable.overdue_balance)}\r\n </p>\r\n </CardContent>\r\n </Card>\r\n </div>\r\n\r\n <Tabs value={activeTab} onValueChange={setActiveTab}>\r\n <div className=\"flex items-center justify-between\">\r\n <TabsList>\r\n <TabsTrigger value=\"receivable\">{tc('tabReceivable')}</TabsTrigger>\r\n <TabsTrigger value=\"payable\">{tc('tabPayable')}</TabsTrigger>\r\n <TabsTrigger value=\"receipt\">{tc('tabReceipt')}</TabsTrigger>\r\n <TabsTrigger value=\"payment\">{tc('tabPayment')}</TabsTrigger>\r\n </TabsList>\r\n <div className=\"flex gap-2\">\r\n <div className=\"relative w-64\">\r\n <Search className=\"absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground\" />\r\n <Input\r\n placeholder={tc('searchPlaceholder')}\r\n className=\"pl-10\"\r\n value={keyword}\r\n onChange={(e) => setKeyword(e.target.value)}\r\n onKeyDown={(e) =>\r\n e.key === 'Enter' &&\r\n (activeTab === 'receivable' ? fetchReceivables() : fetchPayables())\r\n }\r\n />\r\n </div>\r\n <Select value={statusFilter} onValueChange={setStatusFilter}>\r\n <SelectTrigger className=\"w-32\">\r\n <SelectValue placeholder={tc('statusFilterLabel')} />\r\n </SelectTrigger>\r\n <SelectContent>\r\n <SelectItem value=\"all\">{tc('allStatus')}</SelectItem>\r\n {activeTab === 'receivable' || activeTab === 'receipt' ? (\r\n <>\r\n <SelectItem value=\"1\">{t('notReceived')}</SelectItem>\r\n <SelectItem value=\"2\">{t('partialReceived')}</SelectItem>\r\n <SelectItem value=\"3\">{t('fullyReceived')}</SelectItem>\r\n </>\r\n ) : (\r\n <>\r\n <SelectItem value=\"1\">{t('unpaid')}</SelectItem>\r\n <SelectItem value=\"2\">{t('partial')}</SelectItem>\r\n <SelectItem value=\"3\">{t('paid')}</SelectItem>\r\n </>\r\n )}\r\n </SelectContent>\r\n </Select>\r\n <Button\r\n variant=\"outline\"\r\n onClick={() => {\r\n if (activeTab === 'receivable') {\r\n fetchReceivables();\r\n } else if (activeTab === 'payable') {\r\n fetchPayables();\r\n } else if (activeTab === 'receipt') {\r\n fetchReceipts();\r\n } else {\r\n fetchPayments();\r\n }\r\n }}\r\n >\r\n <RefreshCw className=\"h-4 w-4 mr-2\" />\r\n {tc('refresh')}\r\n </Button>\r\n {activeTab === 'receivable' && (\r\n <Button onClick={() => setReceivableDialogOpen(true)}>\r\n <Plus className=\"h-4 w-4 mr-2\" />\r\n {tc('addReceivable')}\r\n </Button>\r\n )}\r\n {activeTab === 'payable' && (\r\n <Button onClick={() => setPayableDialogOpen(true)}>\r\n <Plus className=\"h-4 w-4 mr-2\" />\r\n {tc('addPayable')}\r\n </Button>\r\n )}\r\n {activeTab === 'receipt' && (\r\n <Button onClick={() => setReceiptDialogOpen(true)}>\r\n <Plus className=\"h-4 w-4 mr-2\" />\r\n {tc('addReceipt')}\r\n </Button>\r\n )}\r\n {activeTab === 'payment' && (\r\n <Button onClick={() => setPaymentDialogOpen(true)}>\r\n <Plus className=\"h-4 w-4 mr-2\" />\r\n {tc('addPayment')}\r\n </Button>\r\n )}\r\n </div>\r\n </div>\r\n\r\n <TabsContent value=\"receivable\" className=\"mt-4\">\r\n <Card>\r\n <CardContent className=\"p-0\">\r\n <Table>\r\n <TableHeader>\r\n <TableRow>\r\n <TableHead>{tc('receivableNoLabel')}</TableHead>\r\n <TableHead>{tc('customer')}</TableHead>\r\n <TableHead>{tc('sourceNo')}</TableHead>\r\n <TableHead>{t('receivableAmount')}</TableHead>\r\n <TableHead>{tc('receivedAmount')}</TableHead>\r\n <TableHead>{tc('balance')}</TableHead>\r\n <TableHead>{tc('dueDate')}</TableHead>\r\n <TableHead>{tc('status')}</TableHead>\r\n <TableHead>{tc('actions')}</TableHead>\r\n </TableRow>\r\n </TableHeader>\r\n <TableBody>\r\n {receivables.length === 0 ? (\r\n <TableRow>\r\n <TableCell colSpan={9} className=\"text-center py-8 text-muted-foreground\">\r\n {tc('noReceivableRecords')}\r\n </TableCell>\r\n </TableRow>\r\n ) : (\r\n receivables.map((r) => (\r\n <TableRow key={r.id}>\r\n <TableCell className=\"font-medium\">{r.receivable_no}</TableCell>\r\n <TableCell>{r.customer_name || '-'}</TableCell>\r\n <TableCell>{r.source_no || '-'}</TableCell>\r\n <TableCell className=\"text-green-600\">\r\n ¥{formatAmount(r.amount)}\r\n </TableCell>\r\n <TableCell>¥{formatAmount(r.received_amount)}</TableCell>\r\n <TableCell\r\n className={Number(r.balance) > 0 ? 'text-red-600 font-medium' : ''}\r\n >\r\n ¥{formatAmount(r.balance)}\r\n </TableCell>\r\n <TableCell>{r.due_date || '-'}</TableCell>\r\n <TableCell>\r\n <Badge className={RECEIVABLE_STATUS[r.status]?.color || 'bg-gray-100'}>\r\n {RECEIVABLE_STATUS[r.status]?.label || r.status}\r\n </Badge>\r\n </TableCell>\r\n <TableCell>\r\n <div className=\"flex gap-1\">\r\n <Button\r\n variant=\"ghost\"\r\n size=\"sm\"\r\n onClick={() => handleViewDetail(r.id, 'receivable')}\r\n >\r\n <Eye className=\"h-4 w-4\" />\r\n </Button>\r\n {r.status !== 3 && (\r\n <Button\r\n variant=\"ghost\"\r\n size=\"sm\"\r\n onClick={() => handleDeleteReceivable(r.id)}\r\n >\r\n <Trash2 className=\"h-4 w-4 text-red-500\" />\r\n </Button>\r\n )}\r\n </div>\r\n </TableCell>\r\n </TableRow>\r\n ))\r\n )}\r\n </TableBody>\r\n </Table>\r\n </CardContent>\r\n </Card>\r\n </TabsContent>\r\n\r\n <TabsContent value=\"payable\" className=\"mt-4\">\r\n <Card>\r\n <CardContent className=\"p-0\">\r\n <Table>\r\n <TableHeader>\r\n <TableRow>\r\n <TableHead>{tc('payableNoLabel')}</TableHead>\r\n <TableHead>{tc('supplier')}</TableHead>\r\n <TableHead>{tc('sourceNo')}</TableHead>\r\n <TableHead>{tc('amount')}</TableHead>\r\n <TableHead>{tc('paidAmount')}</TableHead>\r\n <TableHead>{tc('balance')}</TableHead>\r\n <TableHead>{tc('dueDate')}</TableHead>\r\n <TableHead>{tc('status')}</TableHead>\r\n <TableHead>{tc('actions')}</TableHead>\r\n </TableRow>\r\n </TableHeader>\r\n <TableBody>\r\n {payables.length === 0 ? (\r\n <TableRow>\r\n <TableCell colSpan={9} className=\"text-center py-8 text-muted-foreground\">\r\n {tc('noPayableRecords')}\r\n </TableCell>\r\n </TableRow>\r\n ) : (\r\n payables.map((p) => (\r\n <TableRow key={p.id}>\r\n <TableCell className=\"font-medium\">{p.payable_no}</TableCell>\r\n <TableCell>{p.supplier_name || '-'}</TableCell>\r\n <TableCell>{p.source_no || '-'}</TableCell>\r\n <TableCell className=\"text-red-600\">¥{formatAmount(p.amount)}</TableCell>\r\n <TableCell>¥{formatAmount(p.paid_amount)}</TableCell>\r\n <TableCell\r\n className={Number(p.balance) > 0 ? 'text-orange-600 font-medium' : ''}\r\n >\r\n ¥{formatAmount(p.balance)}\r\n </TableCell>\r\n <TableCell>{p.due_date || '-'}</TableCell>\r\n <TableCell>\r\n <Badge className={PAYABLE_STATUS[p.status]?.color || 'bg-gray-100'}>\r\n {PAYABLE_STATUS[p.status]?.label || p.status}\r\n </Badge>\r\n </TableCell>\r\n <TableCell>\r\n <div className=\"flex gap-1\">\r\n <Button\r\n variant=\"ghost\"\r\n size=\"sm\"\r\n onClick={() => handleViewDetail(p.id, 'payable')}\r\n >\r\n <Eye className=\"h-4 w-4\" />\r\n </Button>\r\n {p.status !== 3 && (\r\n <Button\r\n variant=\"ghost\"\r\n size=\"sm\"\r\n onClick={() => handleDeletePayable(p.id)}\r\n >\r\n <Trash2 className=\"h-4 w-4 text-red-500\" />\r\n </Button>\r\n )}\r\n </div>\r\n </TableCell>\r\n </TableRow>\r\n ))\r\n )}\r\n </TableBody>\r\n </Table>\r\n </CardContent>\r\n </Card>\r\n </TabsContent>\r\n\r\n <TabsContent value=\"receipt\" className=\"mt-4\">\r\n <Card>\r\n <CardContent className=\"p-0\">\r\n <Table>\r\n <TableHeader>\r\n <TableRow>\r\n <TableHead>{tc('receiptNo')}</TableHead>\r\n <TableHead>{tc('customer')}</TableHead>\r\n <TableHead>{tc('receiptAmount')}</TableHead>\r\n <TableHead>{tc('paymentMethod')}</TableHead>\r\n <TableHead>{tc('receiptDate')}</TableHead>\r\n <TableHead>{tc('remark')}</TableHead>\r\n </TableRow>\r\n </TableHeader>\r\n <TableBody>\r\n {receipts.length === 0 ? (\r\n <TableRow>\r\n <TableCell colSpan={6} className=\"text-center py-8 text-muted-foreground\">\r\n {tc('noReceiptRecords')}\r\n </TableCell>\r\n </TableRow>\r\n ) : (\r\n receipts.map((r) => (\r\n <TableRow key={r.id}>\r\n <TableCell className=\"font-medium\">{r.receipt_no}</TableCell>\r\n <TableCell>{r.customer_name || '-'}</TableCell>\r\n <TableCell className=\"text-green-600\">\r\n ¥{formatAmount(r.amount)}\r\n </TableCell>\r\n <TableCell>\r\n {PAYMENT_METHODS[r.payment_method] || r.payment_method || '-'}\r\n </TableCell>\r\n <TableCell>{r.receipt_date || '-'}</TableCell>\r\n <TableCell>{r.remark || '-'}</TableCell>\r\n </TableRow>\r\n ))\r\n )}\r\n </TableBody>\r\n </Table>\r\n </CardContent>\r\n </Card>\r\n </TabsContent>\r\n\r\n <TabsContent value=\"payment\" className=\"mt-4\">\r\n <Card>\r\n <CardContent className=\"p-0\">\r\n <Table>\r\n <TableHeader>\r\n <TableRow>\r\n <TableHead>{tc('paymentNo')}</TableHead>\r\n <TableHead>{tc('supplier')}</TableHead>\r\n <TableHead>{tc('paymentAmount')}</TableHead>\r\n <TableHead>{tc('paymentMethod')}</TableHead>\r\n <TableHead>{tc('paymentDate')}</TableHead>\r\n <TableHead>{tc('remark')}</TableHead>\r\n </TableRow>\r\n </TableHeader>\r\n <TableBody>\r\n {payments.length === 0 ? (\r\n <TableRow>\r\n <TableCell colSpan={6} className=\"text-center py-8 text-muted-foreground\">\r\n 暂无付款记录\r\n </TableCell>\r\n </TableRow>\r\n ) : (\r\n payments.map((p) => (\r\n <TableRow key={p.id}>\r\n <TableCell className=\"font-medium\">{p.payment_no}</TableCell>\r\n <TableCell>{p.supplier_name || '-'}</TableCell>\r\n <TableCell className=\"text-red-600\">¥{formatAmount(p.amount)}</TableCell>\r\n <TableCell>\r\n {PAYMENT_METHODS[p.payment_method] || p.payment_method || '-'}\r\n </TableCell>\r\n <TableCell>{p.payment_date || '-'}</TableCell>\r\n <TableCell>{p.remark || '-'}</TableCell>\r\n </TableRow>\r\n ))\r\n )}\r\n </TableBody>\r\n </Table>\r\n </CardContent>\r\n </Card>\r\n </TabsContent>\r\n </Tabs>\r\n\r\n <Dialog open={receivableDialogOpen} onOpenChange={setReceivableDialogOpen}>\r\n <DialogContent className=\"sm:max-w-[500px]\" resizable>\r\n <DialogHeader>\r\n <DialogTitle>{tc('newReceivableTitle')}</DialogTitle>\r\n <DialogDescription>{tc('newReceivableDesc')}</DialogDescription>\r\n </DialogHeader>\r\n <div className=\"space-y-4\">\r\n <div>\r\n <Label>{tc('customer')}</Label>\r\n <Select\r\n value={receivableForm.customer_id}\r\n onValueChange={(v) => setReceivableForm((prev) => ({ ...prev, customer_id: v }))}\r\n >\r\n <SelectTrigger>\r\n <SelectValue placeholder={tc('selectCustomer')} />\r\n </SelectTrigger>\r\n <SelectContent>\r\n {customers.map((c: Loose) => (\r\n <SelectItem key={c.id} value={String(c.id)}>\r\n {c.customer_name}\r\n </SelectItem>\r\n ))}\r\n </SelectContent>\r\n </Select>\r\n </div>\r\n <div>\r\n <Label>{tc('amount')}</Label>\r\n <Input\r\n type=\"number\"\r\n step=\"0.01\"\r\n value={receivableForm.amount}\r\n onChange={(e) =>\r\n setReceivableForm((prev) => ({ ...prev, amount: e.target.value }))\r\n }\r\n placeholder={tc('enterAmount')}\r\n />\r\n </div>\r\n <div>\r\n <Label>{tc('sourceNo')}</Label>\r\n <Input\r\n value={receivableForm.source_no}\r\n onChange={(e) =>\r\n setReceivableForm((prev) => ({ ...prev, source_no: e.target.value }))\r\n }\r\n placeholder={tc('sourceNoPlaceholder')}\r\n />\r\n </div>\r\n <div>\r\n <Label>{tc('dueDate')}</Label>\r\n <Input\r\n type=\"date\"\r\n value={receivableForm.due_date}\r\n onChange={(e) =>\r\n setReceivableForm((prev) => ({ ...prev, due_date: e.target.value }))\r\n }\r\n />\r\n </div>\r\n <div>\r\n <Label>{tc('remark')}</Label>\r\n <Textarea\r\n value={receivableForm.remark}\r\n onChange={(e) =>\r\n setReceivableForm((prev) => ({ ...prev, remark: e.target.value }))\r\n }\r\n placeholder={tc('enterRemark')}\r\n />\r\n </div>\r\n </div>\r\n <DialogFooter>\r\n <Button variant=\"outline\" onClick={() => setReceivableDialogOpen(false)}>\r\n {tc('cancel')}\r\n </Button>\r\n <Button onClick={handleCreateReceivable}>{tc('create')}</Button>\r\n </DialogFooter>\r\n </DialogContent>\r\n </Dialog>\r\n\r\n <Dialog open={payableDialogOpen} onOpenChange={setPayableDialogOpen}>\r\n <DialogContent className=\"sm:max-w-[500px]\" resizable>\r\n <DialogHeader>\r\n <DialogTitle>{tc('newPayableTitle')}</DialogTitle>\r\n <DialogDescription>{tc('newPayableDesc')}</DialogDescription>\r\n </DialogHeader>\r\n <div className=\"space-y-4\">\r\n <div>\r\n <Label>{tc('supplier')}</Label>\r\n <Select\r\n value={payableForm.supplier_id}\r\n onValueChange={(v) => setPayableForm((prev) => ({ ...prev, supplier_id: v }))}\r\n >\r\n <SelectTrigger>\r\n <SelectValue placeholder={tc('selectSupplier')} />\r\n </SelectTrigger>\r\n <SelectContent>\r\n {suppliers.map((s: Loose) => (\r\n <SelectItem key={s.id} value={String(s.id)}>\r\n {s.supplier_name}\r\n </SelectItem>\r\n ))}\r\n </SelectContent>\r\n </Select>\r\n </div>\r\n <div>\r\n <Label>{tc('amount')}</Label>\r\n <Input\r\n type=\"number\"\r\n step=\"0.01\"\r\n value={payableForm.amount}\r\n onChange={(e) => setPayableForm((prev) => ({ ...prev, amount: e.target.value }))}\r\n placeholder={tc('enterAmount')}\r\n />\r\n </div>\r\n <div>\r\n <Label>{tc('sourceNo')}</Label>\r\n <Input\r\n value={payableForm.source_no}\r\n onChange={(e) =>\r\n setPayableForm((prev) => ({ ...prev, source_no: e.target.value }))\r\n }\r\n placeholder={tc('sourceNoPlaceholder')}\r\n />\r\n </div>\r\n <div>\r\n <Label>{tc('dueDate')}</Label>\r\n <Input\r\n type=\"date\"\r\n value={payableForm.due_date}\r\n onChange={(e) =>\r\n setPayableForm((prev) => ({ ...prev, due_date: e.target.value }))\r\n }\r\n />\r\n </div>\r\n <div>\r\n <Label>{tc('remark')}</Label>\r\n <Textarea\r\n value={payableForm.remark}\r\n onChange={(e) => setPayableForm((prev) => ({ ...prev, remark: e.target.value }))}\r\n placeholder={tc('enterRemark')}\r\n />\r\n </div>\r\n </div>\r\n <DialogFooter>\r\n <Button variant=\"outline\" onClick={() => setPayableDialogOpen(false)}>\r\n {tc('cancel')}\r\n </Button>\r\n <Button onClick={handleCreatePayable}>{tc('create')}</Button>\r\n </DialogFooter>\r\n </DialogContent>\r\n </Dialog>\r\n\r\n <Dialog open={receiptDialogOpen} onOpenChange={setReceiptDialogOpen}>\r\n <DialogContent className=\"sm:max-w-[500px]\" resizable>\r\n <DialogHeader>\r\n <DialogTitle>{tc('newReceiptTitle')}</DialogTitle>\r\n <DialogDescription>{tc('newReceiptDesc')}</DialogDescription>\r\n </DialogHeader>\r\n <div className=\"space-y-4\">\r\n <div>\r\n <Label>{tc('selectReceivable')}</Label>\r\n <Select\r\n value={receiptForm.receivable_id}\r\n onValueChange={(v) => setReceiptForm((prev) => ({ ...prev, receivable_id: v }))}\r\n >\r\n <SelectTrigger>\r\n <SelectValue placeholder={tc('selectReceivable')} />\r\n </SelectTrigger>\r\n <SelectContent>\r\n {receivables\r\n .filter((r) => r.status !== 3)\r\n .map((r) => (\r\n <SelectItem key={r.id} value={String(r.id)}>\r\n {r.receivable_no} - {r.customer_name}\r\n {tc('balance')}\r\n {formatAmount(r.balance)})\r\n </SelectItem>\r\n ))}\r\n </SelectContent>\r\n </Select>\r\n </div>\r\n <div>\r\n <Label>{tc('receiptAmount')}</Label>\r\n <Input\r\n type=\"number\"\r\n step=\"0.01\"\r\n value={receiptForm.amount}\r\n onChange={(e) => setReceiptForm((prev) => ({ ...prev, amount: e.target.value }))}\r\n placeholder={tc('enterAmount')}\r\n />\r\n </div>\r\n <div>\r\n <Label>{tc('paymentMethod')}</Label>\r\n <Select\r\n value={receiptForm.payment_method}\r\n onValueChange={(v) => setReceiptForm((prev) => ({ ...prev, payment_method: v }))}\r\n >\r\n <SelectTrigger>\r\n <SelectValue />\r\n </SelectTrigger>\r\n <SelectContent>\r\n {Object.entries(PAYMENT_METHODS).map(([k, v]) => (\r\n <SelectItem key={k} value={k}>\r\n {v}\r\n </SelectItem>\r\n ))}\r\n </SelectContent>\r\n </Select>\r\n </div>\r\n <div>\r\n <Label>{tc('receiptDate')}</Label>\r\n <Input\r\n type=\"date\"\r\n value={receiptForm.receipt_date}\r\n onChange={(e) =>\r\n setReceiptForm((prev) => ({ ...prev, receipt_date: e.target.value }))\r\n }\r\n />\r\n </div>\r\n <div>\r\n <Label>{tc('remark')}</Label>\r\n <Textarea\r\n value={receiptForm.remark}\r\n onChange={(e) => setReceiptForm((prev) => ({ ...prev, remark: e.target.value }))}\r\n placeholder={tc('enterRemark')}\r\n />\r\n </div>\r\n </div>\r\n <DialogFooter>\r\n <Button variant=\"outline\" onClick={() => setReceiptDialogOpen(false)}>\r\n {tc('cancel')}\r\n </Button>\r\n <Button onClick={handleCreateReceipt}>{tc('createReceipt')}</Button>\r\n </DialogFooter>\r\n </DialogContent>\r\n </Dialog>\r\n\r\n <Dialog open={paymentDialogOpen} onOpenChange={setPaymentDialogOpen}>\r\n <DialogContent className=\"sm:max-w-[500px]\" resizable>\r\n <DialogHeader>\r\n <DialogTitle>{tc('newPaymentTitle')}</DialogTitle>\r\n <DialogDescription>{tc('newPaymentDesc')}</DialogDescription>\r\n </DialogHeader>\r\n <div className=\"space-y-4\">\r\n <div>\r\n <Label>{tc('selectPayable')}</Label>\r\n <Select\r\n value={paymentForm.payable_id}\r\n onValueChange={(v) => setPaymentForm((prev) => ({ ...prev, payable_id: v }))}\r\n >\r\n <SelectTrigger>\r\n <SelectValue placeholder={tc('selectPayable')} />\r\n </SelectTrigger>\r\n <SelectContent>\r\n {payables\r\n .filter((p) => p.status !== 3)\r\n .map((p) => (\r\n <SelectItem key={p.id} value={String(p.id)}>\r\n {p.payable_no} - {p.supplier_name}\r\n {tc('balance')}\r\n {formatAmount(p.balance)})\r\n </SelectItem>\r\n ))}\r\n </SelectContent>\r\n </Select>\r\n </div>\r\n <div>\r\n <Label>{tc('paymentAmount')}</Label>\r\n <Input\r\n type=\"number\"\r\n step=\"0.01\"\r\n value={paymentForm.amount}\r\n onChange={(e) => setPaymentForm((prev) => ({ ...prev, amount: e.target.value }))}\r\n placeholder={tc('enterAmount')}\r\n />\r\n </div>\r\n <div>\r\n <Label>{tc('paymentMethod')}</Label>\r\n <Select\r\n value={paymentForm.payment_method}\r\n onValueChange={(v) => setPaymentForm((prev) => ({ ...prev, payment_method: v }))}\r\n >\r\n <SelectTrigger>\r\n <SelectValue />\r\n </SelectTrigger>\r\n <SelectContent>\r\n {Object.entries(PAYMENT_METHODS).map(([k, v]) => (\r\n <SelectItem key={k} value={k}>\r\n {v}\r\n </SelectItem>\r\n ))}\r\n </SelectContent>\r\n </Select>\r\n </div>\r\n <div>\r\n <Label>{tc('paymentDate')}</Label>\r\n <Input\r\n type=\"date\"\r\n value={paymentForm.payment_date}\r\n onChange={(e) =>\r\n setPaymentForm((prev) => ({ ...prev, payment_date: e.target.value }))\r\n }\r\n />\r\n </div>\r\n <div>\r\n <Label>{tc('remark')}</Label>\r\n <Textarea\r\n value={paymentForm.remark}\r\n onChange={(e) => setPaymentForm((prev) => ({ ...prev, remark: e.target.value }))}\r\n placeholder={tc('enterRemark')}\r\n />\r\n </div>\r\n </div>\r\n <DialogFooter>\r\n <Button variant=\"outline\" onClick={() => setPaymentDialogOpen(false)}>\r\n {tc('cancel')}\r\n </Button>\r\n <Button onClick={handleCreatePayment}>{tc('createPayment')}</Button>\r\n </DialogFooter>\r\n </DialogContent>\r\n </Dialog>\r\n\r\n <Dialog open={detailDialogOpen} onOpenChange={setDetailDialogOpen}>\r\n <DialogContent className=\"sm:max-w-[600px]\" resizable>\r\n <DialogHeader>\r\n <DialogTitle>\r\n {detailType === 'receivable'\r\n ? tc('detailReceivableTitle')\r\n : tc('detailPayableTitle')}\r\n </DialogTitle>\r\n </DialogHeader>\r\n {detailData && (\r\n <div className=\"space-y-4\">\r\n <div className=\"grid grid-cols-2 gap-4 text-sm\">\r\n {detailType === 'receivable' ? (\r\n <>\r\n <div>\r\n <span className=\"text-muted-foreground\">{tc('receivableNoLabel')}:</span>\r\n {detailData.receivable_no}\r\n </div>\r\n <div>\r\n <span className=\"text-muted-foreground\">{tc('customer')}:</span>\r\n {detailData.customer_name}\r\n </div>\r\n <div>\r\n <span className=\"text-muted-foreground\">{tc('sourceNo')}</span>\r\n {detailData.source_no || '-'}\r\n </div>\r\n <div>\r\n <span className=\"text-muted-foreground\">{tc('dueDate')}</span>\r\n {detailData.due_date || '-'}\r\n </div>\r\n <div>\r\n <span className=\"text-muted-foreground\">{tc('amount')}</span>¥\r\n {formatAmount(detailData.amount)}\r\n </div>\r\n <div>\r\n <span className=\"text-muted-foreground\">{tc('receivedAmount')}</span>¥\r\n {formatAmount(detailData.received_amount)}\r\n </div>\r\n <div>\r\n <span className=\"text-muted-foreground\">{tc('balance')}</span>¥\r\n {formatAmount(detailData.balance)}\r\n </div>\r\n <div>\r\n <span className=\"text-muted-foreground\">{tc('status')}:</span>\r\n <Badge\r\n className={RECEIVABLE_STATUS[detailData.status]?.color || 'bg-gray-100'}\r\n >\r\n {RECEIVABLE_STATUS[detailData.status]?.label || detailData.status}\r\n </Badge>\r\n </div>\r\n </>\r\n ) : (\r\n <>\r\n <div>\r\n <span className=\"text-muted-foreground\">{tc('payableNoLabel')}:</span>\r\n {detailData.payable_no}\r\n </div>\r\n <div>\r\n <span className=\"text-muted-foreground\">{tc('supplier')}:</span>\r\n {detailData.supplier_name}\r\n </div>\r\n <div>\r\n <span className=\"text-muted-foreground\">{tc('sourceNo')}</span>\r\n {detailData.source_no || '-'}\r\n </div>\r\n <div>\r\n <span className=\"text-muted-foreground\">{tc('dueDate')}</span>\r\n {detailData.due_date || '-'}\r\n </div>\r\n <div>\r\n <span className=\"text-muted-foreground\">{tc('amount')}</span>¥\r\n {formatAmount(detailData.amount)}\r\n </div>\r\n <div>\r\n <span className=\"text-muted-foreground\">{tc('paidAmount')}</span>¥\r\n {formatAmount(detailData.paid_amount)}\r\n </div>\r\n <div>\r\n <span className=\"text-muted-foreground\">{tc('balance')}</span>¥\r\n {formatAmount(detailData.balance)}\r\n </div>\r\n <div>\r\n <span className=\"text-muted-foreground\">{tc('status')}:</span>\r\n <Badge\r\n className={PAYABLE_STATUS[detailData.status]?.color || 'bg-gray-100'}\r\n >\r\n {PAYABLE_STATUS[detailData.status]?.label || detailData.status}\r\n </Badge>\r\n </div>\r\n </>\r\n )}\r\n </div>\r\n {detailData.receipts && detailData.receipts.length > 0 && (\r\n <div>\r\n <h4 className=\"font-medium mb-2\">{tc('receiptRecords')}</h4>\r\n <Table>\r\n <TableHeader>\r\n <TableRow>\r\n <TableHead>{tc('receiptNo')}</TableHead>\r\n <TableHead>{tc('amount')}</TableHead>\r\n <TableHead>{tc('paymentMethod')}</TableHead>\r\n <TableHead>{tc('date')}</TableHead>\r\n </TableRow>\r\n </TableHeader>\r\n <TableBody>\r\n {detailData.receipts.map((r: Loose) => (\r\n <TableRow key={r.id}>\r\n <TableCell>{r.receipt_no}</TableCell>\r\n <TableCell>¥{formatAmount(r.amount)}</TableCell>\r\n <TableCell>\r\n {PAYMENT_METHODS[r.payment_method] || r.payment_method}\r\n </TableCell>\r\n <TableCell>{r.receipt_date}</TableCell>\r\n </TableRow>\r\n ))}\r\n </TableBody>\r\n </Table>\r\n </div>\r\n )}\r\n {detailData.payments && detailData.payments.length > 0 && (\r\n <div>\r\n <h4 className=\"font-medium mb-2\">{tc('paymentRecords')}</h4>\r\n <Table>\r\n <TableHeader>\r\n <TableRow>\r\n <TableHead>{tc('paymentNo')}</TableHead>\r\n <TableHead>{tc('amount')}</TableHead>\r\n <TableHead>{tc('paymentMethod')}</TableHead>\r\n <TableHead>{tc('date')}</TableHead>\r\n </TableRow>\r\n </TableHeader>\r\n <TableBody>\r\n {detailData.payments.map((p: Loose) => (\r\n <TableRow key={p.id}>\r\n <TableCell>{p.payment_no}</TableCell>\r\n <TableCell>¥{formatAmount(p.amount)}</TableCell>\r\n <TableCell>\r\n {PAYMENT_METHODS[p.payment_method] || p.payment_method}\r\n </TableCell>\r\n <TableCell>{p.payment_date}</TableCell>\r\n </TableRow>\r\n ))}\r\n </TableBody>\r\n </Table>\r\n </div>\r\n )}\r\n </div>\r\n )}\r\n </DialogContent>\r\n </Dialog>\r\n </div>\r\n </MainLayout>\r\n );\r\n}\r\n","usedDeprecatedRules":[]},{"filePath":"D:\\dcprint\\erp-project\\src\\app\\[locale]\\finance\\payables\\page.tsx","messages":[{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":67,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":67,"endColumn":86},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":68,"column":18,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":68,"endColumn":25},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction<Payable[]>`.","line":69,"column":21,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":69,"endColumn":43},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":69,"column":28,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":69,"endColumn":32},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction<number>`.","line":70,"column":18,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":70,"endColumn":40},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":70,"column":25,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":70,"endColumn":29},{"ruleId":"react-hooks/set-state-in-effect","severity":1,"message":"Error: Calling setState synchronously within an effect can trigger cascading renders\n\nEffects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following:\n* Update external systems with the latest state from React.\n* Subscribe for updates from some external system, calling setState in a callback function when external state changes.\n\nCalling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect).\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\finance\\payables\\page.tsx:80:5\n 78 |\n 79 | useEffect(() => {\n> 80 | loadPayables();\n | ^^^^^^^^^^^^ Avoid calling setState() directly within an effect\n 81 | }, [page]);\n 82 |\n 83 | const getStatusBadge = (status: number) => {","line":80,"column":5,"nodeType":null,"endLine":80,"endColumn":17},{"ruleId":"react-hooks/exhaustive-deps","severity":1,"message":"React Hook useEffect has a missing dependency: 'loadPayables'. Either include it or remove the dependency array.","line":81,"column":6,"nodeType":"ArrayExpression","endLine":81,"endColumn":12,"suggestions":[{"desc":"Update the dependencies array to be: [loadPayables, page]","fix":{"range":[2268,2274],"text":"[loadPayables, page]"}}]},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":90,"column":28,"nodeType":"MemberExpression","messageId":"anyAssignment","endLine":90,"endColumn":37},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":99,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":103,"endColumn":9},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":104,"column":18,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":104,"endColumn":25},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `string | number | bigint | boolean | ReactElement<unknown, string | JSXElementConstructor<any>> | Iterable<ReactNode> | ... 4 more ... | undefined`.","line":105,"column":23,"nodeType":"MemberExpression","messageId":"unsafeArgument","endLine":105,"endColumn":37},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .message on an `any` value.","line":105,"column":30,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":105,"endColumn":37},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `string | number | bigint | boolean | ReactElement<unknown, string | JSXElementConstructor<any>> | Iterable<ReactNode> | ... 4 more ... | undefined`.","line":111,"column":21,"nodeType":"MemberExpression","messageId":"unsafeArgument","endLine":111,"endColumn":35},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .message on an `any` value.","line":111,"column":28,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":111,"endColumn":35}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":15,"fixableErrorCount":0,"fixableWarningCount":0,"source":"'use client';\n\nimport React, { useState, useEffect } from 'react';\nimport { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';\nimport { Badge } from '@/components/ui/badge';\nimport { useTranslations } from 'next-intl';\nimport { Button } from '@/components/ui/button';\nimport {\n Table,\n TableBody,\n TableCell,\n TableHead,\n TableHeader,\n TableRow,\n} from '@/components/ui/table';\nimport {\n Dialog,\n DialogContent,\n DialogHeader,\n DialogTitle,\n DialogFooter,\n} from '@/components/ui/dialog';\nimport { Input } from '@/components/ui/input';\nimport { Label } from '@/components/ui/label';\nimport { ApiClient } from '@/lib/api-client';\nimport { formatDate, formatAmount } from '@/lib/utils';\nimport { toast } from 'sonner';\nimport { MoneyDisplay } from '@/components/ui/money-display';\nimport { RefreshCw, CreditCard, FileText } from 'lucide-react';\n\ninterface Payable {\n id: number;\n payable_no: string;\n source_no: string;\n supplier_name: string;\n amount: number;\n paid_amount: number;\n balance: number;\n due_date: string;\n status: number;\n create_time: string;\n currency?: string;\n source_currency?: string;\n source_amount?: number;\n}\n\nexport default function PayablesPage() {\n // 翻译钩子\n const tc = useTranslations('Common');\n\n const [payables, setPayables] = useState<Payable[]>([]);\n const [loading, setLoading] = useState(false);\n const [page, _setPage] = useState(1);\n const [_total, setTotal] = useState(0);\n\n const [showPayment, setShowPayment] = useState(false);\n const [selectedPay, setSelectedPay] = useState<Payable | null>(null);\n const [paymentAmount, setPaymentAmount] = useState('');\n const [paymentMethod, setPaymentMethod] = useState('bank_transfer');\n const [paymentDate, setPaymentDate] = useState(new Date().toISOString().split('T')[0]);\n\n const pageSize = 20;\n\n const loadPayables = async () => {\n setLoading(true);\n try {\n const result = await ApiClient.get('/api/finance/payables', { page, pageSize });\n if (result.success) {\n setPayables(result.data.list || []);\n setTotal(result.data.total || 0);\n }\n } catch {\n toast.error(tc('loadPayableFailed'));\n } finally {\n setLoading(false);\n }\n };\n\n useEffect(() => {\n loadPayables();\n }, [page]);\n\n const getStatusBadge = (status: number) => {\n const map: Record<number, { label: string; variant: Loose }> = {\n 1: { label: '未付款', variant: 'secondary' },\n 2: { label: '部分付款', variant: 'warning' },\n 3: { label: '已结清', variant: 'success' },\n };\n const s = map[status] || { label: tc('unknown'), variant: 'default' };\n return <Badge variant={s.variant}>{s.label}</Badge>;\n };\n\n const handlePayment = async () => {\n if (!selectedPay || !paymentAmount || !paymentDate) {\n toast.error(tc('fillCompleteInfo'));\n return;\n }\n try {\n const result = await ApiClient.post(`/api/finance/payables/${selectedPay.id}/payment`, {\n amount: Number(paymentAmount),\n paymentMethod,\n paymentDate,\n });\n if (result.success) {\n toast.success(result.message);\n setShowPayment(false);\n setSelectedPay(null);\n setPaymentAmount('');\n loadPayables();\n } else {\n toast.error(result.message);\n }\n } catch {\n toast.error(tc('paymentFailed'));\n }\n };\n\n return (\n <div className=\"container mx-auto py-6 space-y-6\">\n <div className=\"flex justify-between items-center\">\n <h1 className=\"text-2xl font-bold\">{tc('tabPayable')}</h1>\n <Button onClick={loadPayables} disabled={loading}>\n <RefreshCw className={`w-4 h-4 mr-2 ${loading ? 'animate-spin' : ''}`} />\n {tc('refresh')}\n </Button>\n </div>\n\n <Card>\n <CardHeader>\n <CardTitle>{tc('payableListTitle')}</CardTitle>\n </CardHeader>\n <CardContent>\n <Table>\n <TableHeader>\n <TableRow>\n <TableHead>{tc('payableNoLabel')}</TableHead>\n <TableHead>{tc('sourceNo')}</TableHead>\n <TableHead>{tc('supplier')}</TableHead>\n <TableHead>{tc('amount')}</TableHead>\n <TableHead>{tc('paidAmount')}</TableHead>\n <TableHead>{tc('balance')}</TableHead>\n <TableHead>{tc('currency')}</TableHead>\n <TableHead>{tc('dueDate')}</TableHead>\n <TableHead>{tc('status')}</TableHead>\n <TableHead>{tc('actions')}</TableHead>\n </TableRow>\n </TableHeader>\n <TableBody>\n {payables.map((pay) => (\n <TableRow key={pay.id}>\n <TableCell className=\"font-medium\">{pay.payable_no}</TableCell>\n <TableCell>\n {pay.source_currency ? (\n <span title={`${tc('sourceCurrency', { currency: pay.source_currency })}`}>\n <FileText className=\"w-3 h-3 inline mr-1 text-muted-foreground\" />\n {pay.source_no}\n <span className=\"text-xs text-muted-foreground ml-1\">\n ({pay.source_currency})\n </span>\n </span>\n ) : (\n pay.source_no\n )}\n </TableCell>\n <TableCell>{pay.supplier_name}</TableCell>\n <TableCell>\n <MoneyDisplay amount={pay.amount} currency={pay.currency || 'CNY'} />\n </TableCell>\n <TableCell>\n <MoneyDisplay amount={pay.paid_amount} currency={pay.currency || 'CNY'} />\n </TableCell>\n <TableCell className={pay.balance > 0 ? 'text-orange-600 font-medium' : ''}>\n <MoneyDisplay amount={pay.balance} currency={pay.currency || 'CNY'} />\n </TableCell>\n <TableCell>\n {pay.currency || <span className=\"text-muted-foreground\">-</span>}\n </TableCell>\n <TableCell>{formatDate(pay.due_date)}</TableCell>\n <TableCell>{getStatusBadge(pay.status)}</TableCell>\n <TableCell>\n {pay.status !== 3 && (\n <Button\n size=\"sm\"\n variant=\"outline\"\n onClick={() => {\n <TableCell>{getStatusBadge(pay.status)}</TableCell>;\n setShowPayment(true);\n }}\n >\n <CreditCard className=\"w-3 h-3 mr-1\" />\n {tc('paymentTitle')}\n </Button>\n )}\n </TableCell>\n </TableRow>\n ))}\n {payables.length === 0 && (\n <TableRow>\n <TableCell colSpan={10} className=\"text-center text-muted-foreground py-8\">\n {tc('noData')}\n </TableCell>\n </TableRow>\n )}\n </TableBody>\n </Table>\n </CardContent>\n </Card>\n\n {/* 付款弹窗 */}\n <Dialog open={showPayment} onOpenChange={setShowPayment}>\n <DialogContent>\n <DialogHeader>\n <DialogTitle>\n {tc('paymentTitle')}\n {selectedPay?.payable_no}\n </DialogTitle>\n </DialogHeader>\n <div className=\"space-y-4 py-4\">\n <div>\n <Label>{tc('amount')}</Label>\n <Input value={selectedPay ? formatAmount(selectedPay.amount) : ''} disabled />\n </div>\n <div>\n <Label>{tc('balance')}</Label>\n <Input value={selectedPay ? formatAmount(selectedPay.balance) : ''} disabled />\n </div>\n <div>\n <Label>{tc('paymentAmount')}</Label>\n <Input\n type=\"number\"\n value={paymentAmount}\n onChange={(e) => setPaymentAmount(e.target.value)}\n />\n </div>\n <div>\n <Label>{tc('paymentMethod')}</Label>\n <select\n className=\"w-full border rounded-md p-2\"\n value={paymentMethod}\n onChange={(e) => setPaymentMethod(e.target.value)}\n >\n <option value=\"bank_transfer\">{tc('bankTransfer')}</option>\n <option value=\"cash\">{tc('cash')}</option>\n <option value=\"check\">{tc('check')}</option>\n <option value=\"wechat\">{tc('wechat')}</option>\n <option value=\"alipay\">{tc('alipay')}</option>\n </select>\n </div>\n <div>\n <Label>{tc('paymentDate')}</Label>\n <Input\n type=\"date\"\n value={paymentDate}\n onChange={(e) => setPaymentDate(e.target.value)}\n />\n </div>\n </div>\n <DialogFooter>\n <Button variant=\"outline\" onClick={() => setShowPayment(false)}>\n {tc('cancel')}\n </Button>\n <Button onClick={handlePayment}>{tc('createPayment')}</Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n </div>\n );\n}\n","usedDeprecatedRules":[]},{"filePath":"D:\\dcprint\\erp-project\\src\\app\\[locale]\\finance\\receivable\\page.tsx","messages":[{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":109,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":109,"endColumn":38},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":110,"column":18,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":110,"endColumn":25},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":112,"column":15,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":112,"endColumn":36},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":112,"column":32,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":112,"endColumn":36},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":113,"column":15,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":113,"endColumn":79},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .list on an `any` value.","line":113,"column":69,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":113,"endColumn":73},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":114,"column":15,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":130,"endColumn":12},{"ruleId":"@typescript-eslint/no-unsafe-call","severity":1,"message":"Unsafe call of an `any` typed value.","line":114,"column":22,"nodeType":"MemberExpression","messageId":"unsafeCall","endLine":114,"endColumn":33},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .map on an `any` value.","line":114,"column":30,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":114,"endColumn":33},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":115,"column":11,"nodeType":"Property","messageId":"anyAssignment","endLine":115,"endColumn":22},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .id on an `any` value.","line":115,"column":20,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":115,"endColumn":22},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":116,"column":11,"nodeType":"Property","messageId":"anyAssignment","endLine":116,"endColumn":65},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .receivableNo on an `any` value.","line":116,"column":31,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":116,"endColumn":43},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .receivable_no on an `any` value.","line":116,"column":52,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":116,"endColumn":65},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":117,"column":11,"nodeType":"Property","messageId":"anyAssignment","endLine":117,"endColumn":59},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .sourceType on an `any` value.","line":117,"column":29,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":117,"endColumn":39},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .source_type on an `any` value.","line":117,"column":48,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":117,"endColumn":59},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":118,"column":11,"nodeType":"Property","messageId":"anyAssignment","endLine":118,"endColumn":53},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .sourceNo on an `any` value.","line":118,"column":27,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":118,"endColumn":35},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .source_no on an `any` value.","line":118,"column":44,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":118,"endColumn":53},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":119,"column":11,"nodeType":"Property","messageId":"anyAssignment","endLine":119,"endColumn":59},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .customerId on an `any` value.","line":119,"column":29,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":119,"endColumn":39},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .customer_id on an `any` value.","line":119,"column":48,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":119,"endColumn":59},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":120,"column":11,"nodeType":"Property","messageId":"anyAssignment","endLine":120,"endColumn":65},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .customerName on an `any` value.","line":120,"column":31,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":120,"endColumn":43},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .customer_name on an `any` value.","line":120,"column":52,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":120,"endColumn":65},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":121,"column":11,"nodeType":"Property","messageId":"anyAssignment","endLine":121,"endColumn":30},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .amount on an `any` value.","line":121,"column":24,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":121,"endColumn":30},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":122,"column":11,"nodeType":"Property","messageId":"anyAssignment","endLine":122,"endColumn":71},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .receivedAmount on an `any` value.","line":122,"column":33,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":122,"endColumn":47},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .received_amount on an `any` value.","line":122,"column":56,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":122,"endColumn":71},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":123,"column":11,"nodeType":"Property","messageId":"anyAssignment","endLine":123,"endColumn":32},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .balance on an `any` value.","line":123,"column":25,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":123,"endColumn":32},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":124,"column":11,"nodeType":"Property","messageId":"anyAssignment","endLine":124,"endColumn":50},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .dueDate on an `any` value.","line":124,"column":26,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":124,"endColumn":33},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .due_date on an `any` value.","line":124,"column":42,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":124,"endColumn":50},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":125,"column":11,"nodeType":"Property","messageId":"anyAssignment","endLine":125,"endColumn":30},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .status on an `any` value.","line":125,"column":24,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":125,"endColumn":30},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":126,"column":11,"nodeType":"Property","messageId":"anyAssignment","endLine":126,"endColumn":30},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .remark on an `any` value.","line":126,"column":24,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":126,"endColumn":30},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":127,"column":11,"nodeType":"Property","messageId":"anyAssignment","endLine":127,"endColumn":34},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .currency on an `any` value.","line":127,"column":26,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":127,"endColumn":34},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":128,"column":11,"nodeType":"Property","messageId":"anyAssignment","endLine":128,"endColumn":71},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .sourceCurrency on an `any` value.","line":128,"column":33,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":128,"endColumn":47},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .source_currency on an `any` value.","line":128,"column":56,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":128,"endColumn":71},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":129,"column":11,"nodeType":"Property","messageId":"anyAssignment","endLine":129,"endColumn":65},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .sourceAmount on an `any` value.","line":129,"column":31,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":129,"endColumn":43},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .source_amount on an `any` value.","line":129,"column":52,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":129,"endColumn":65},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction<Receivable[]>`.","line":131,"column":17,"nodeType":"Identifier","messageId":"unsafeArgument","endLine":131,"endColumn":21},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction<number>`.","line":132,"column":18,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":132,"endColumn":52},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .total on an `any` value.","line":132,"column":27,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":132,"endColumn":32},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .length on an `any` value.","line":132,"column":41,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":132,"endColumn":47},{"ruleId":"react-hooks/set-state-in-effect","severity":1,"message":"Error: Calling setState synchronously within an effect can trigger cascading renders\n\nEffects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following:\n* Update external systems with the latest state from React.\n* Subscribe for updates from some external system, calling setState in a callback function when external state changes.\n\nCalling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect).\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\finance\\receivable\\page.tsx:138:5\n 136 |\n 137 | useEffect(() => {\n> 138 | fetchData();\n | ^^^^^^^^^ Avoid calling setState() directly within an effect\n 139 | }, [fetchData]);\n 140 |\n 141 | const handleViewDetail = async (id: number) => {","line":138,"column":5,"nodeType":null,"endLine":138,"endColumn":14},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":144,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":144,"endColumn":38},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":145,"column":18,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":145,"endColumn":25},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction<ReceivableDetail | null>`.","line":146,"column":23,"nodeType":"MemberExpression","messageId":"unsafeArgument","endLine":146,"endColumn":34},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":146,"column":30,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":146,"endColumn":34},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"获取详情失败\"。建议使用: tc('text_q73iq6')","line":150,"column":22,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":150,"endColumn":30},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":166,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":166,"endColumn":38},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":167,"column":18,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":167,"endColumn":25},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"收款成功\"。建议使用: tc('text_d7qsev')","line":168,"column":24,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":168,"endColumn":30},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"收款失败\"。建议使用: tc('text_d7plng')","line":172,"column":24,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":172,"endColumn":30},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":172,"column":32,"nodeType":"Property","messageId":"anyAssignment","endLine":172,"endColumn":59},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .message on an `any` value.","line":172,"column":52,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":172,"endColumn":59},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":401,"column":33,"nodeType":"MemberExpression","messageId":"anyAssignment","endLine":401,"endColumn":38},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .id on an `any` value.","line":401,"column":36,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":401,"endColumn":38},{"ruleId":"@typescript-eslint/no-unsafe-call","severity":1,"message":"Unsafe call of an `any` typed value.","line":402,"column":32,"nodeType":"MemberExpression","messageId":"unsafeCall","endLine":402,"endColumn":54},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .receipt_date on an `any` value.","line":402,"column":35,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":402,"endColumn":47},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `number`.","line":405,"column":46,"nodeType":"MemberExpression","messageId":"unsafeArgument","endLine":405,"endColumn":55},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .amount on an `any` value.","line":405,"column":49,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":405,"endColumn":55},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .remark on an `any` value.","line":409,"column":69,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":409,"endColumn":75}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":71,"fixableErrorCount":0,"fixableWarningCount":0,"source":"'use client';\r\n\r\nimport { useState, useEffect, useCallback } from 'react';\r\nimport { MainLayout } from '@/components/layout';\r\nimport { Card, CardContent } from '@/components/ui/card';\r\nimport { Button } from '@/components/ui/button';\r\nimport { Input } from '@/components/ui/input';\r\nimport { Badge } from '@/components/ui/badge';\r\nimport {\r\n Dialog,\r\n DialogContent,\r\n DialogHeader,\r\n DialogTitle,\r\n DialogFooter,\r\n} from '@/components/ui/dialog';\r\nimport {\r\n Table,\r\n TableBody,\r\n TableCell,\r\n TableHead,\r\n TableHeader,\r\n TableRow,\r\n} from '@/components/ui/table';\r\nimport {\r\n Select,\r\n SelectContent,\r\n SelectItem,\r\n SelectTrigger,\r\n SelectValue,\r\n} from '@/components/ui/select';\r\nimport { Label } from '@/components/ui/label';\r\nimport { MoneyDisplay } from '@/components/ui/money-display';\r\nimport { Search, RefreshCw, Eye, FileText } from 'lucide-react';\r\nimport { useToast } from '@/hooks/use-toast';\r\nimport { useTranslations } from 'next-intl';\r\nimport { authFetch } from '@/lib/auth-fetch';\r\n\r\ninterface Receivable {\r\n id: number;\r\n receivable_no: string;\r\n source_type: number;\r\n source_no: string;\r\n customer_id: number;\r\n customer_name: string;\r\n amount: number;\r\n received_amount: number;\r\n balance: number;\r\n due_date: string;\r\n status: number;\r\n remark: string;\r\n currency?: string;\r\n source_currency?: string;\r\n source_amount?: number;\r\n}\r\n\r\ninterface ReceivableDetail extends Receivable {\r\n create_time?: string;\r\n update_time?: string;\r\n receipts?: Array<{\r\n id: number;\r\n receipt_no: string;\r\n amount: number;\r\n receipt_date: string;\r\n remark?: string;\r\n }>;\r\n receipt_records?: Array<{\r\n id: number;\r\n receipt_no: string;\r\n amount: number;\r\n receipt_date: string;\r\n remark?: string;\r\n }>;\r\n}\r\n\r\nexport default function ReceivablePage() {\r\n // 翻译钩子\r\n const t = useTranslations('Finance');\r\n const tc = useTranslations('Common');\r\n\r\n const statusMap: Record<\r\n number,\r\n { label: string; variant: 'default' | 'secondary' | 'destructive' | 'outline' }\r\n > = {\r\n 1: { label: t('notReceived'), variant: 'outline' },\r\n 2: { label: t('partialReceived'), variant: 'secondary' },\r\n 3: { label: t('fullyReceived'), variant: 'default' },\r\n 9: { label: tc('cancelled'), variant: 'destructive' },\r\n };\r\n\r\n const { toast } = useToast();\r\n const [list, setList] = useState<Receivable[]>([]);\r\n const [total, setTotal] = useState(0);\r\n const [page, setPage] = useState(1);\r\n const [keyword, setSearch] = useState('');\r\n const [statusFilter, setStatusFilter] = useState('');\r\n const [showDialog, setShowDialog] = useState(false);\r\n const [detailItem, setDetailItem] = useState<ReceivableDetail | null>(null);\r\n const [receiptForm, setReceiptForm] = useState({ amount: 0, receipt_date: '', remark: '' });\r\n\r\n const fetchData = useCallback(async () => {\r\n try {\r\n const params = new URLSearchParams({\r\n page: String(page),\r\n pageSize: '20',\r\n keyword,\r\n status: statusFilter,\r\n });\r\n const res = await authFetch('/api/finance/receivables?' + params);\r\n const result = await res.json();\r\n if (result.success) {\r\n // 统一处理API返回的数据结构\r\n const rawData = result.data;\r\n const rawList = Array.isArray(rawData) ? rawData : rawData?.list || [];\r\n const list = rawList.map((item: Loose) => ({\r\n id: item.id,\r\n receivable_no: item.receivableNo || item.receivable_no,\r\n source_type: item.sourceType || item.source_type,\r\n source_no: item.sourceNo || item.source_no,\r\n customer_id: item.customerId || item.customer_id,\r\n customer_name: item.customerName || item.customer_name,\r\n amount: item.amount,\r\n received_amount: item.receivedAmount || item.received_amount,\r\n balance: item.balance,\r\n due_date: item.dueDate || item.due_date,\r\n status: item.status,\r\n remark: item.remark,\r\n currency: item.currency,\r\n source_currency: item.sourceCurrency || item.source_currency,\r\n source_amount: item.sourceAmount || item.source_amount,\r\n }));\r\n setList(list);\r\n setTotal(rawData?.total || list.length || 0);\r\n }\r\n } catch {}\r\n }, [page, keyword, statusFilter]);\r\n\r\n useEffect(() => {\r\n fetchData();\r\n }, [fetchData]);\r\n\r\n const handleViewDetail = async (id: number) => {\r\n try {\r\n const res = await authFetch(`/api/finance/receivables?id=${id}`);\r\n const result = await res.json();\r\n if (result.success) {\r\n setDetailItem(result.data);\r\n setShowDialog(true);\r\n }\r\n } catch {\r\n toast({ title: '获取详情失败', variant: 'destructive' });\r\n }\r\n };\r\n\r\n const handleReceipt = async () => {\r\n if (!detailItem) return;\r\n try {\r\n const res = await authFetch('/api/finance/receipt', {\r\n method: 'POST',\r\n body: JSON.stringify({\r\n receivable_id: detailItem.id,\r\n amount: receiptForm.amount,\r\n receipt_date: receiptForm.receipt_date,\r\n remark: receiptForm.remark,\r\n }),\r\n });\r\n const result = await res.json();\r\n if (result.success) {\r\n toast({ title: '收款成功' });\r\n setShowDialog(false);\r\n fetchData();\r\n } else {\r\n toast({ title: '收款失败', description: result.message, variant: 'destructive' });\r\n }\r\n } catch {\r\n toast({ title: tc('error'), variant: 'destructive' });\r\n }\r\n };\r\n\r\n const _formatAmount = (amount: number) => ((amount || 0) / 100).toFixed(2);\r\n const toAmount = (amount: number) => (amount || 0) / 100;\r\n\r\n return (\r\n <MainLayout>\r\n <div className=\"space-y-4\">\r\n <div className=\"flex items-center justify-between\">\r\n <h2 className=\"text-2xl font-bold\">{t('receivableManagement')}</h2>\r\n <div className=\"flex items-center gap-2\">\r\n <div className=\"relative w-64\">\r\n <Search className=\"absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground\" />\r\n <Input\r\n placeholder={t('searchNoOrCustomer')}\r\n value={keyword}\r\n onChange={(e) => setSearch(e.target.value)}\r\n className=\"pl-10 h-9\"\r\n />\r\n </div>\r\n <Select\r\n value={statusFilter}\r\n onValueChange={(v) => {\r\n setStatusFilter(v === 'all' ? '' : v);\r\n setPage(1);\r\n }}\r\n >\r\n <SelectTrigger className=\"w-32 h-9\">\r\n <SelectValue placeholder={t('statusFilter')} />\r\n </SelectTrigger>\r\n <SelectContent>\r\n <SelectItem value=\"all\">{tc('all')}</SelectItem>\r\n <SelectItem value=\"1\">{t('notReceived')}</SelectItem>\r\n <SelectItem value=\"2\">{t('partialReceived')}</SelectItem>\r\n <SelectItem value=\"3\">{t('fullyReceived')}</SelectItem>\r\n </SelectContent>\r\n </Select>\r\n <Button variant=\"outline\" size=\"sm\" onClick={fetchData}>\r\n <RefreshCw className=\"h-4 w-4\" />\r\n {tc('refresh')}\r\n </Button>\r\n </div>\r\n </div>\r\n\r\n <Card>\r\n <CardContent className=\"p-0\">\r\n <Table>\r\n <TableHeader>\r\n <TableRow>\r\n <TableHead>{t('receivableNo')}</TableHead>\r\n <TableHead>{t('sourceNo')}</TableHead>\r\n <TableHead>{t('customerName')}</TableHead>\r\n <TableHead className=\"text-right\">{t('receivableAmount')}</TableHead>\r\n <TableHead className=\"text-right\">{t('receivedAmount')}</TableHead>\r\n <TableHead className=\"text-right\">{tc('balance')}</TableHead>\r\n <TableHead>{tc('currency')}</TableHead>\r\n <TableHead>{t('dueDate')}</TableHead>\r\n <TableHead>{tc('status')}</TableHead>\r\n <TableHead>{tc('actions')}</TableHead>\r\n </TableRow>\r\n </TableHeader>\r\n <TableBody>\r\n {list.length === 0 ? (\r\n <TableRow>\r\n <TableCell colSpan={10} className=\"text-center py-8 text-muted-foreground\">\r\n {t('noData')}\r\n </TableCell>\r\n </TableRow>\r\n ) : (\r\n list.map((r) => (\r\n <TableRow key={r.id}>\r\n <TableCell className=\"font-mono text-sm\">{r.receivable_no}</TableCell>\r\n <TableCell className=\"font-mono text-sm\">\r\n {r.source_currency && r.source_amount != null ? (\r\n <span title={`${t('sourceCurrency')}: ${r.source_currency}`}>\r\n <FileText className=\"w-3 h-3 inline mr-1 text-muted-foreground\" />\r\n {r.source_no}\r\n <span className=\"text-xs text-muted-foreground ml-1\">\r\n ({r.source_currency})\r\n </span>\r\n </span>\r\n ) : (\r\n r.source_no\r\n )}\r\n </TableCell>\r\n <TableCell>{r.customer_name}</TableCell>\r\n <TableCell className=\"text-right\">\r\n <MoneyDisplay amount={toAmount(r.amount)} currency={r.currency || 'CNY'} />\r\n </TableCell>\r\n <TableCell className=\"text-right\">\r\n <MoneyDisplay\r\n amount={toAmount(r.received_amount)}\r\n currency={r.currency || 'CNY'}\r\n />\r\n </TableCell>\r\n <TableCell\r\n className={`text-right ${Number(r.balance) > 0 ? 'text-red-600 font-medium' : ''}`}\r\n >\r\n <MoneyDisplay amount={toAmount(r.balance)} currency={r.currency || 'CNY'} />\r\n </TableCell>\r\n <TableCell>\r\n {r.currency || <span className=\"text-muted-foreground\">-</span>}\r\n </TableCell>\r\n <TableCell>{r.due_date ? r.due_date.slice(0, 10) : ''}</TableCell>\r\n <TableCell>\r\n <Badge variant={statusMap[r.status]?.variant || 'outline'}>\r\n {statusMap[r.status]?.label || tc('unknown')}\r\n </Badge>\r\n </TableCell>\r\n <TableCell>\r\n <Button variant=\"ghost\" size=\"sm\" onClick={() => handleViewDetail(r.id)}>\r\n <Eye className=\"h-4 w-4\" />\r\n </Button>\r\n </TableCell>\r\n </TableRow>\r\n ))\r\n )}\r\n </TableBody>\r\n </Table>\r\n </CardContent>\r\n </Card>\r\n\r\n <div className=\"flex items-center justify-between text-sm text-muted-foreground\">\r\n <span>{tc('totalRecords', { total })}</span>\r\n <div className=\"flex gap-2\">\r\n <Button\r\n variant=\"outline\"\r\n size=\"sm\"\r\n disabled={page <= 1}\r\n onClick={() => setPage((p) => p - 1)}\r\n >\r\n {t('previousPage')}\r\n </Button>\r\n <Button\r\n variant=\"outline\"\r\n size=\"sm\"\r\n disabled={page * 20 >= total}\r\n onClick={() => setPage((p) => p + 1)}\r\n >\r\n {t('nextPage')}\r\n </Button>\r\n </div>\r\n </div>\r\n\r\n <Dialog open={showDialog} onOpenChange={setShowDialog}>\r\n <DialogContent className=\"max-w-lg\" resizable>\r\n <DialogHeader>\r\n <DialogTitle>{t('receivableDetail')}</DialogTitle>\r\n </DialogHeader>\r\n {detailItem && (\r\n <div className=\"space-y-4\">\r\n <div className=\"grid grid-cols-2 gap-4 text-sm\">\r\n <div>\r\n <span className=\"text-muted-foreground\">{t('receivableNo')}:</span>\r\n {detailItem.receivable_no}\r\n </div>\r\n <div>\r\n <span className=\"text-muted-foreground\">{t('customerName')}:</span>\r\n {detailItem.customer_name}\r\n </div>\r\n <div>\r\n <span className=\"text-muted-foreground\">{t('receivableAmount')}:</span>\r\n <MoneyDisplay\r\n amount={toAmount(detailItem.amount)}\r\n currency={detailItem.currency || 'CNY'}\r\n />\r\n </div>\r\n <div>\r\n <span className=\"text-muted-foreground\">{t('receivedAmount')}:</span>\r\n <MoneyDisplay\r\n amount={toAmount(detailItem.received_amount)}\r\n currency={detailItem.currency || 'CNY'}\r\n />\r\n </div>\r\n <div>\r\n <span className=\"text-muted-foreground\">{tc('balance')}:</span>\r\n <MoneyDisplay\r\n amount={toAmount(detailItem.balance)}\r\n currency={detailItem.currency || 'CNY'}\r\n />\r\n </div>\r\n <div>\r\n <span className=\"text-muted-foreground\">{t('dueDate')}:</span>\r\n {detailItem.due_date?.slice(0, 10)}\r\n </div>\r\n </div>\r\n {Number(detailItem.balance) > 0 && (\r\n <div className=\"border-t pt-4 space-y-3\">\r\n <h4 className=\"font-medium\">{t('registerReceipt')}</h4>\r\n <div className=\"grid grid-cols-2 gap-3\">\r\n <div>\r\n <Label>{t('receiptAmount')}</Label>\r\n <Input\r\n type=\"number\"\r\n value={receiptForm.amount}\r\n onChange={(e) =>\r\n setReceiptForm({ ...receiptForm, amount: Number(e.target.value) })\r\n }\r\n />\r\n </div>\r\n <div>\r\n <Label>{t('receiptDate')}</Label>\r\n <Input\r\n type=\"date\"\r\n value={receiptForm.receipt_date}\r\n onChange={(e) =>\r\n setReceiptForm({ ...receiptForm, receipt_date: e.target.value })\r\n }\r\n />\r\n </div>\r\n </div>\r\n <div>\r\n <Label>{tc('remark')}</Label>\r\n <Input\r\n value={receiptForm.remark}\r\n onChange={(e) => setReceiptForm({ ...receiptForm, remark: e.target.value })}\r\n />\r\n </div>\r\n </div>\r\n )}\r\n {detailItem.receipts && detailItem.receipts.length > 0 && (\r\n <div className=\"border-t pt-4\">\r\n <h4 className=\"font-medium mb-2\">{t('receiptRecords')}</h4>\r\n {detailItem.receipts.map((rc: Loose) => (\r\n <div key={rc.id} className=\"flex justify-between text-sm py-1 border-b\">\r\n <span>{rc.receipt_date?.slice(0, 10)}</span>\r\n <span>\r\n <MoneyDisplay\r\n amount={toAmount(rc.amount)}\r\n currency={detailItem.currency || 'CNY'}\r\n />\r\n </span>\r\n <span className=\"text-muted-foreground\">{rc.remark || ''}</span>\r\n </div>\r\n ))}\r\n </div>\r\n )}\r\n </div>\r\n )}\r\n <DialogFooter>\r\n {detailItem && Number(detailItem.balance) > 0 && (\r\n <Button onClick={handleReceipt}>{t('confirmReceipt')}</Button>\r\n )}\r\n <Button variant=\"outline\" onClick={() => setShowDialog(false)}>\r\n {tc('close')}\r\n </Button>\r\n </DialogFooter>\r\n </DialogContent>\r\n </Dialog>\r\n </div>\r\n </MainLayout>\r\n );\r\n}\r\n","usedDeprecatedRules":[]},{"filePath":"D:\\dcprint\\erp-project\\src\\app\\[locale]\\finance\\receivables\\page.tsx","messages":[{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":63,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":63,"endColumn":89},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":64,"column":18,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":64,"endColumn":25},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction<Receivable[]>`.","line":65,"column":24,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":65,"endColumn":46},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":65,"column":31,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":65,"endColumn":35},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction<number>`.","line":66,"column":18,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":66,"endColumn":40},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":66,"column":25,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":66,"endColumn":29},{"ruleId":"react-hooks/set-state-in-effect","severity":1,"message":"Error: Calling setState synchronously within an effect can trigger cascading renders\n\nEffects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following:\n* Update external systems with the latest state from React.\n* Subscribe for updates from some external system, calling setState in a callback function when external state changes.\n\nCalling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect).\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\finance\\receivables\\page.tsx:76:5\n 74 |\n 75 | useEffect(() => {\n> 76 | loadReceivables();\n | ^^^^^^^^^^^^^^^ Avoid calling setState() directly within an effect\n 77 | }, [page]);\n 78 |\n 79 | const getStatusBadge = (status: number) => {","line":76,"column":5,"nodeType":null,"endLine":76,"endColumn":20},{"ruleId":"react-hooks/exhaustive-deps","severity":1,"message":"React Hook useEffect has a missing dependency: 'loadReceivables'. Either include it or remove the dependency array.","line":77,"column":6,"nodeType":"ArrayExpression","endLine":77,"endColumn":12,"suggestions":[{"desc":"Update the dependencies array to be: [loadReceivables, page]","fix":{"range":[2161,2167],"text":"[loadReceivables, page]"}}]},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":86,"column":28,"nodeType":"MemberExpression","messageId":"anyAssignment","endLine":86,"endColumn":37},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":95,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":99,"endColumn":9},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":100,"column":18,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":100,"endColumn":25},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `string | number | bigint | boolean | ReactElement<unknown, string | JSXElementConstructor<any>> | Iterable<ReactNode> | ... 4 more ... | undefined`.","line":101,"column":23,"nodeType":"MemberExpression","messageId":"unsafeArgument","endLine":101,"endColumn":37},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .message on an `any` value.","line":101,"column":30,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":101,"endColumn":37},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `string | number | bigint | boolean | ReactElement<unknown, string | JSXElementConstructor<any>> | Iterable<ReactNode> | ... 4 more ... | undefined`.","line":107,"column":21,"nodeType":"MemberExpression","messageId":"unsafeArgument","endLine":107,"endColumn":35},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .message on an `any` value.","line":107,"column":28,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":107,"endColumn":35}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":15,"fixableErrorCount":0,"fixableWarningCount":0,"source":"'use client';\n\nimport React, { useState, useEffect } from 'react';\nimport { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';\nimport { Badge } from '@/components/ui/badge';\nimport { useTranslations } from 'next-intl';\nimport { Button } from '@/components/ui/button';\nimport {\n Table,\n TableBody,\n TableCell,\n TableHead,\n TableHeader,\n TableRow,\n} from '@/components/ui/table';\nimport {\n Dialog,\n DialogContent,\n DialogHeader,\n DialogTitle,\n DialogFooter,\n} from '@/components/ui/dialog';\nimport { Input } from '@/components/ui/input';\nimport { Label } from '@/components/ui/label';\nimport { ApiClient } from '@/lib/api-client';\nimport { formatDate, formatAmount } from '@/lib/utils';\nimport { toast } from 'sonner';\nimport { RefreshCw, DollarSign } from 'lucide-react';\n\ninterface Receivable {\n id: number;\n receivable_no: string;\n source_no: string;\n customer_name: string;\n amount: number;\n received_amount: number;\n balance: number;\n due_date: string;\n status: number;\n create_time: string;\n}\n\nexport default function ReceivablesPage() {\n // 翻译钩子\n const tc = useTranslations('Common');\n\n const [receivables, setReceivables] = useState<Receivable[]>([]);\n const [loading, setLoading] = useState(false);\n const [page, _setPage] = useState(1);\n const [_total, setTotal] = useState(0);\n\n const [showReceipt, setShowReceipt] = useState(false);\n const [selectedRec, setSelectedRec] = useState<Receivable | null>(null);\n const [receiptAmount, setReceiptAmount] = useState('');\n const [receiptMethod, setReceiptMethod] = useState('bank_transfer');\n const [receiptDate, setReceiptDate] = useState(new Date().toISOString().split('T')[0]);\n\n const pageSize = 20;\n\n const loadReceivables = async () => {\n setLoading(true);\n try {\n const result = await ApiClient.get('/api/finance/receivables', { page, pageSize });\n if (result.success) {\n setReceivables(result.data.list || []);\n setTotal(result.data.total || 0);\n }\n } catch {\n toast.error(tc('loadReceivableFailed'));\n } finally {\n setLoading(false);\n }\n };\n\n useEffect(() => {\n loadReceivables();\n }, [page]);\n\n const getStatusBadge = (status: number) => {\n const map: Record<number, { label: string; variant: Loose }> = {\n 1: { label: '未收款', variant: 'secondary' },\n 2: { label: '部分收款', variant: 'warning' },\n 3: { label: '已结清', variant: 'success' },\n };\n const s = map[status] || { label: tc('unknown'), variant: 'default' };\n return <Badge variant={s.variant}>{s.label}</Badge>;\n };\n\n const handleReceipt = async () => {\n if (!selectedRec || !receiptAmount || !receiptDate) {\n toast.error(tc('fillCompleteInfo'));\n return;\n }\n try {\n const result = await ApiClient.post(`/api/finance/receivables/${selectedRec.id}/receipt`, {\n amount: Number(receiptAmount),\n receiptMethod,\n receiptDate,\n });\n if (result.success) {\n toast.success(result.message);\n setShowReceipt(false);\n setSelectedRec(null);\n setReceiptAmount('');\n loadReceivables();\n } else {\n toast.error(result.message);\n }\n } catch {\n toast.error(tc('receiptFailed'));\n }\n };\n\n return (\n <div className=\"container mx-auto py-6 space-y-6\">\n <div className=\"flex justify-between items-center\">\n <h1 className=\"text-2xl font-bold\">{tc('tabReceivable')}</h1>\n <Button onClick={loadReceivables} disabled={loading}>\n <RefreshCw className={`w-4 h-4 mr-2 ${loading ? 'animate-spin' : ''}`} />\n {tc('refresh')}\n </Button>\n </div>\n\n <Card>\n <CardHeader>\n <CardTitle>{tc('receivableListTitle')}</CardTitle>\n </CardHeader>\n <CardContent>\n <Table>\n <TableHeader>\n <TableRow>\n <TableHead>{tc('receivableNoLabel')}</TableHead>\n <TableHead>{tc('sourceNo')}</TableHead>\n <TableHead>{tc('customer')}</TableHead>\n <TableHead>{tc('amount')}</TableHead>\n <TableHead>{tc('receivedAmount')}</TableHead>\n <TableHead>{tc('balance')}</TableHead>\n <TableHead>{tc('dueDate')}</TableHead>\n <TableHead>{tc('status')}</TableHead>\n <TableHead>{tc('actions')}</TableHead>\n </TableRow>\n </TableHeader>\n <TableBody>\n {receivables.map((rec) => (\n <TableRow key={rec.id}>\n <TableCell className=\"font-medium\">{rec.receivable_no}</TableCell>\n <TableCell>{rec.source_no}</TableCell>\n <TableCell>{rec.customer_name}</TableCell>\n <TableCell>{formatAmount(rec.amount)}</TableCell>\n <TableCell>{formatAmount(rec.received_amount)}</TableCell>\n <TableCell className={rec.balance > 0 ? 'text-orange-600 font-medium' : ''}>\n {formatAmount(rec.balance)}\n </TableCell>\n <TableCell>{formatDate(rec.due_date)}</TableCell>\n <TableCell>{getStatusBadge(rec.status)}</TableCell>\n <TableCell>\n {rec.status !== 3 && (\n <Button\n size=\"sm\"\n variant=\"outline\"\n onClick={() => {\n <TableCell>{getStatusBadge(rec.status)}</TableCell>;\n setShowReceipt(true);\n }}\n >\n <DollarSign className=\"w-3 h-3 mr-1\" />\n {tc('receiptButton')}\n </Button>\n )}\n </TableCell>\n </TableRow>\n ))}\n {receivables.length === 0 && (\n <TableRow>\n <TableCell colSpan={9} className=\"text-center text-muted-foreground py-8\">\n {tc('noData')}\n </TableCell>\n </TableRow>\n )}\n </TableBody>\n </Table>\n </CardContent>\n </Card>\n\n {/* 回款弹窗 */}\n <Dialog open={showReceipt} onOpenChange={setShowReceipt}>\n <DialogContent>\n <DialogHeader>\n <DialogTitle>\n {tc('receiptTitle')}\n {selectedRec?.receivable_no}\n </DialogTitle>\n </DialogHeader>\n <div className=\"space-y-4 py-4\">\n <div>\n <Label>{tc('amount')}</Label>\n <Input value={selectedRec ? formatAmount(selectedRec.amount) : ''} disabled />\n </div>\n <div>\n <Label>{tc('balance')}</Label>\n <Input value={selectedRec ? formatAmount(selectedRec.balance) : ''} disabled />\n </div>\n <div>\n <Label>{tc('receiptAmount')}</Label>\n <Input\n type=\"number\"\n value={receiptAmount}\n onChange={(e) => setReceiptAmount(e.target.value)}\n />\n </div>\n <div>\n <Label>{tc('paymentMethod')}</Label>\n <select\n className=\"w-full border rounded-md p-2\"\n value={receiptMethod}\n onChange={(e) => setReceiptMethod(e.target.value)}\n >\n <option value=\"bank_transfer\">{tc('bankTransfer')}</option>\n <option value=\"cash\">{tc('cash')}</option>\n <option value=\"check\">{tc('check')}</option>\n <option value=\"wechat\">{tc('wechat')}</option>\n <option value=\"alipay\">{tc('alipay')}</option>\n </select>\n </div>\n <div>\n <Label>{tc('receiptDate')}</Label>\n <Input\n type=\"date\"\n value={receiptDate}\n onChange={(e) => setReceiptDate(e.target.value)}\n />\n </div>\n </div>\n <DialogFooter>\n <Button variant=\"outline\" onClick={() => setShowReceipt(false)}>\n {tc('cancel')}\n </Button>\n <Button onClick={handleReceipt}>{tc('confirmReceipt')}</Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n </div>\n );\n}\n","usedDeprecatedRules":[]},{"filePath":"D:\\dcprint\\erp-project\\src\\app\\[locale]\\finance\\report\\page.tsx","messages":[{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":64,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":64,"endColumn":38},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":65,"column":18,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":65,"endColumn":25},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction<ReportItem[]>`.","line":66,"column":17,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":66,"endColumn":40},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":66,"column":24,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":66,"endColumn":28},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction<number>`.","line":67,"column":18,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":67,"endColumn":41},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":67,"column":25,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":67,"endColumn":29},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":75,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":75,"endColumn":38},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":76,"column":18,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":76,"endColumn":25},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":76,"column":36,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":76,"endColumn":40},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":77,"column":15,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":77,"endColumn":30},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":77,"column":26,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":77,"endColumn":30},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":79,"column":11,"nodeType":"Property","messageId":"anyAssignment","endLine":79,"endColumn":46},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .total_revenue on an `any` value.","line":79,"column":28,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":79,"endColumn":41},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":80,"column":11,"nodeType":"Property","messageId":"anyAssignment","endLine":80,"endColumn":40},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .total_cost on an `any` value.","line":80,"column":25,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":80,"endColumn":35},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":81,"column":11,"nodeType":"Property","messageId":"anyAssignment","endLine":81,"endColumn":44},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .total_profit on an `any` value.","line":81,"column":27,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":81,"endColumn":39},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":82,"column":11,"nodeType":"Property","messageId":"anyAssignment","endLine":82,"endColumn":42},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .profit_rate on an `any` value.","line":82,"column":26,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":82,"endColumn":37},{"ruleId":"react-hooks/set-state-in-effect","severity":1,"message":"Error: Calling setState synchronously within an effect can trigger cascading renders\n\nEffects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following:\n* Update external systems with the latest state from React.\n* Subscribe for updates from some external system, calling setState in a callback function when external state changes.\n\nCalling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect).\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\finance\\report\\page.tsx:89:5\n 87 |\n 88 | useEffect(() => {\n> 89 | fetchData();\n | ^^^^^^^^^ Avoid calling setState() directly within an effect\n 90 | fetchSummary();\n 91 | }, [fetchData, fetchSummary]);\n 92 |","line":89,"column":5,"nodeType":null,"endLine":89,"endColumn":14}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":20,"fixableErrorCount":0,"fixableWarningCount":0,"source":"'use client';\n\nimport { authFetch } from '@/lib/auth-fetch';\nimport { useState, useEffect, useCallback } from 'react';\nimport { MainLayout } from '@/components/layout';\nimport { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';\nimport { Button } from '@/components/ui/button';\nimport { Badge } from '@/components/ui/badge';\nimport {\n Table,\n TableBody,\n TableCell,\n TableHead,\n TableHeader,\n TableRow,\n} from '@/components/ui/table';\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@/components/ui/select';\nimport { RefreshCw, BarChart3, TrendingUp, TrendingDown, DollarSign } from 'lucide-react';\nimport { useTranslations } from 'next-intl';\nimport { GlobalExportToolbar } from '@/components/ui/global-export-toolbar';\nimport type { ExportColumn } from '@/lib/global-export-service';\n\ninterface ReportItem {\n id: number;\n period: string;\n type: string;\n category: string;\n revenue: number;\n cost: number;\n profit: number;\n profit_rate: number;\n}\n\nexport default function FinanceReportPage() {\n // 翻译钩子\n const t = useTranslations('Finance');\n const tc = useTranslations('Common');\n\n const [list, setList] = useState<ReportItem[]>([]);\n const [total, setTotal] = useState(0);\n const [page, setPage] = useState(1);\n const [periodType, setPeriodType] = useState('month');\n const [summary, setSummary] = useState({\n total_revenue: 0,\n total_cost: 0,\n total_profit: 0,\n profit_rate: 0,\n });\n\n const fetchData = useCallback(async () => {\n try {\n const params = new URLSearchParams({\n page: String(page),\n pageSize: '20',\n period_type: periodType,\n });\n const res = await authFetch('/api/finance/report?' + params);\n const result = await res.json();\n if (result.success) {\n setList(result.data?.list || []);\n setTotal(result.data?.total || 0);\n }\n } catch {}\n }, [page, periodType]);\n\n const fetchSummary = useCallback(async () => {\n try {\n const res = await authFetch('/api/finance/stats');\n const result = await res.json();\n if (result.success && result.data) {\n const d = result.data;\n setSummary({\n total_revenue: d.total_revenue || 0,\n total_cost: d.total_cost || 0,\n total_profit: d.total_profit || 0,\n profit_rate: d.profit_rate || 0,\n });\n }\n } catch {}\n }, []);\n\n useEffect(() => {\n fetchData();\n fetchSummary();\n }, [fetchData, fetchSummary]);\n\n const formatAmount = (amount: number) => ((amount || 0) / 100).toFixed(2);\n\n return (\n <MainLayout>\n <div className=\"space-y-4\">\n <div className=\"flex items-center justify-between\">\n <h2 className=\"text-2xl font-bold\">{t('financialReport')}</h2>\n <div className=\"flex items-center gap-2\">\n <Select\n value={periodType}\n onValueChange={(v) => {\n setPeriodType(v);\n setPage(1);\n }}\n >\n <SelectTrigger className=\"w-32 h-9\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"day\">{t('byDay')}</SelectItem>\n <SelectItem value=\"week\">{t('byWeek')}</SelectItem>\n <SelectItem value=\"month\">{t('byMonth')}</SelectItem>\n <SelectItem value=\"quarter\">{t('byQuarter')}</SelectItem>\n <SelectItem value=\"year\">{t('byYear')}</SelectItem>\n </SelectContent>\n </Select>\n <Button variant=\"outline\" size=\"sm\" onClick={fetchData}>\n <RefreshCw className=\"h-4 w-4\" />\n {tc('refresh')}\n </Button>\n </div>\n </div>\n\n <div className=\"grid grid-cols-4 gap-4\">\n <Card>\n <CardContent className=\"pt-6\">\n <div className=\"flex items-center gap-2\">\n <TrendingUp className=\"h-5 w-5 text-green-500\" />\n <span className=\"text-sm text-muted-foreground\">{t('totalRevenue')}</span>\n </div>\n <div className=\"text-2xl font-bold text-green-600\">\n ¥{formatAmount(summary.total_revenue)}\n </div>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"pt-6\">\n <div className=\"flex items-center gap-2\">\n <TrendingDown className=\"h-5 w-5 text-red-500\" />\n <span className=\"text-sm text-muted-foreground\">{t('totalCost')}</span>\n </div>\n <div className=\"text-2xl font-bold text-red-600\">\n ¥{formatAmount(summary.total_cost)}\n </div>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"pt-6\">\n <div className=\"flex items-center gap-2\">\n <DollarSign className=\"h-5 w-5 text-blue-500\" />\n <span className=\"text-sm text-muted-foreground\">{t('totalProfit')}</span>\n </div>\n <div className=\"text-2xl font-bold\">¥{formatAmount(summary.total_profit)}</div>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"pt-6\">\n <div className=\"flex items-center gap-2\">\n <BarChart3 className=\"h-5 w-5 text-purple-500\" />\n <span className=\"text-sm text-muted-foreground\">{t('profitRate')}</span>\n </div>\n <div className=\"text-2xl font-bold\">{(summary.profit_rate || 0).toFixed(1)}%</div>\n </CardContent>\n </Card>\n </div>\n\n <Card>\n <CardHeader className=\"flex flex-row items-center justify-between\">\n <CardTitle>{t('incomeExpenseDetail')}</CardTitle>\n <GlobalExportToolbar\n filename={tc('financeReportFileName')}\n title={tc('financeReportTitle')}\n subtitle={periodType || ''}\n columns={\n [\n { key: 'period', label: tc('period'), width: 18 },\n { key: 'type', label: tc('type'), width: 12 },\n { key: 'category', label: tc('category'), width: 15 },\n {\n key: 'revenue',\n label: tc('revenue'),\n width: 12,\n formatter: (v) => Number(v || 0).toFixed(2),\n },\n {\n key: 'cost',\n label: tc('cost'),\n width: 12,\n formatter: (v) => Number(v || 0).toFixed(2),\n },\n {\n key: 'profit',\n label: tc('profit'),\n width: 12,\n formatter: (v) => Number(v || 0).toFixed(2),\n },\n {\n key: 'profit_rate',\n label: tc('profitRate'),\n width: 10,\n formatter: (v) => `${Number(v || 0).toFixed(1)}%`,\n },\n ] as ExportColumn[]\n }\n data={list}\n footer={tc('reportFooter')}\n />\n </CardHeader>\n <CardContent className=\"p-0\">\n <Table>\n <TableHeader>\n <TableRow>\n <TableHead>{t('period')}</TableHead>\n <TableHead>{tc('type')}</TableHead>\n <TableHead>{tc('category')}</TableHead>\n <TableHead className=\"text-right\">{tc('revenue')}</TableHead>\n <TableHead className=\"text-right\">{tc('cost')}</TableHead>\n <TableHead className=\"text-right\">{tc('profit')}</TableHead>\n <TableHead className=\"text-right\">{t('profitRate')}</TableHead>\n </TableRow>\n </TableHeader>\n <TableBody>\n {list.length === 0 ? (\n <TableRow key=\"empty\">\n <TableCell colSpan={7} className=\"text-center py-8 text-muted-foreground\">\n {tc('noData')}\n </TableCell>\n </TableRow>\n ) : (\n list.map((r) => (\n <TableRow key={r.id}>\n <TableCell>{r.period}</TableCell>\n <TableCell>\n <Badge variant=\"outline\">{r.type}</Badge>\n </TableCell>\n <TableCell>{r.category}</TableCell>\n <TableCell className=\"text-right text-green-600\">\n ¥{formatAmount(r.revenue)}\n </TableCell>\n <TableCell className=\"text-right text-red-600\">\n ¥{formatAmount(r.cost)}\n </TableCell>\n <TableCell\n className={`text-right ${r.profit >= 0 ? 'text-green-600' : 'text-red-600'}`}\n >\n ¥{formatAmount(r.profit)}\n </TableCell>\n <TableCell className=\"text-right\">\n {(r.profit_rate || 0).toFixed(1)}%\n </TableCell>\n </TableRow>\n ))\n )}\n </TableBody>\n </Table>\n </CardContent>\n </Card>\n\n <div className=\"flex items-center justify-between text-sm text-muted-foreground\">\n <span>{tc('totalRecords', { total })}</span>\n <div className=\"flex gap-2\">\n <Button\n variant=\"outline\"\n size=\"sm\"\n disabled={page <= 1}\n onClick={() => setPage((p) => p - 1)}\n >\n {t('previousPage')}\n </Button>\n <Button\n variant=\"outline\"\n size=\"sm\"\n disabled={page * 20 >= total}\n onClick={() => setPage((p) => p + 1)}\n >\n {t('nextPage')}\n </Button>\n </div>\n </div>\n </div>\n </MainLayout>\n );\n}\n","usedDeprecatedRules":[]},{"filePath":"D:\\dcprint\\erp-project\\src\\app\\[locale]\\hr\\attendance\\page.tsx","messages":[{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"管理部\"。建议使用: tc('text_iof7n')","line":67,"column":12,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":67,"endColumn":17},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"业务部\"。建议使用: tc('text_buoe9')","line":68,"column":12,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":68,"endColumn":17},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"生产部\"。建议使用: tc('text_hjr0g')","line":69,"column":12,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":69,"endColumn":17},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"打样中心\"。建议使用: tc('text_cu3n96')","line":70,"column":12,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":70,"endColumn":18},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"采购部\"。建议使用: tc('text_m1hlu')","line":71,"column":12,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":71,"endColumn":17},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"品质部\"。建议使用: tc('text_d3pkx')","line":72,"column":12,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":72,"endColumn":17},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"模切\"。建议使用: tc('text_ii2u')","line":73,"column":12,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":73,"endColumn":16},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"商标\"。建议使用: tc('text_f2pt')","line":74,"column":12,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":74,"endColumn":16},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"其他\"。建议使用: tc('text_eae8')","line":75,"column":12,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":75,"endColumn":16},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"采购\"。建议使用: tc('text_pkjq')","line":76,"column":12,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":76,"endColumn":16},{"ruleId":"react-hooks/immutability","severity":1,"message":"Error: Cannot access variable before it is declared\n\n`fetchAttendanceRecords` is accessed before it is declared, which prevents the earlier access from updating when this value changes over time.\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\hr\\attendance\\page.tsx:187:5\n 185 |\n 186 | useEffect(() => {\n> 187 | fetchAttendanceRecords();\n | ^^^^^^^^^^^^^^^^^^^^^^ `fetchAttendanceRecords` accessed before it is declared\n 188 | }, []);\n 189 |\n 190 | const fetchAttendanceRecords = async () => {\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\hr\\attendance\\page.tsx:190:3\n 188 | }, []);\n 189 |\n> 190 | const fetchAttendanceRecords = async () => {\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n> 191 | try {\n | ^^^^^^^^^\n> 192 | setIsLoading(true);\n …\n | ^^^^^^^^^\n> 230 | }\n | ^^^^^^^^^\n> 231 | };\n | ^^^^^ `fetchAttendanceRecords` is declared here\n 232 |\n 233 | // 刷新数据\n 234 | const handleRefresh = useCallback(async () => {","line":187,"column":5,"nodeType":null,"endLine":187,"endColumn":27},{"ruleId":"react-hooks/exhaustive-deps","severity":1,"message":"React Hook useEffect has a missing dependency: 'fetchAttendanceRecords'. Either include it or remove the dependency array.","line":188,"column":6,"nodeType":"ArrayExpression","endLine":188,"endColumn":8,"suggestions":[{"desc":"Update the dependencies array to be: [fetchAttendanceRecords]","fix":{"range":[5288,5290],"text":"[fetchAttendanceRecords]"}}]},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":194,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":194,"endColumn":36},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":195,"column":16,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":195,"endColumn":23},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .code on an `any` value.","line":195,"column":32,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":195,"endColumn":36},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":197,"column":15,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":197,"endColumn":34},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":197,"column":30,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":197,"endColumn":34},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":198,"column":15,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":198,"endColumn":79},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .list on an `any` value.","line":198,"column":69,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":198,"endColumn":73},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction<AttendanceRecord[]>`.","line":200,"column":11,"nodeType":"CallExpression","messageId":"unsafeArgument","endLine":224,"endColumn":13},{"ruleId":"@typescript-eslint/no-unsafe-call","severity":1,"message":"Unsafe call of an `any` typed value.","line":200,"column":11,"nodeType":"MemberExpression","messageId":"unsafeCall","endLine":200,"endColumn":22},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .map on an `any` value.","line":200,"column":19,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":200,"endColumn":22},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":201,"column":17,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":201,"endColumn":80},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .attendanceDate on an `any` value.","line":201,"column":29,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":201,"endColumn":43},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .attendance_date on an `any` value.","line":201,"column":49,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":201,"endColumn":64},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .date on an `any` value.","line":201,"column":70,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":201,"endColumn":74},{"ruleId":"@typescript-eslint/no-unsafe-call","severity":1,"message":"Unsafe call of an `any` typed value.","line":202,"column":28,"nodeType":"MemberExpression","messageId":"unsafeCall","endLine":202,"endColumn":44},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .includes on an `any` value.","line":202,"column":36,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":202,"endColumn":44},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":203,"column":15,"nodeType":"AssignmentExpression","messageId":"anyAssignment","endLine":203,"endColumn":45},{"ruleId":"@typescript-eslint/no-unsafe-call","severity":1,"message":"Unsafe call of an `any` typed value.","line":203,"column":25,"nodeType":"MemberExpression","messageId":"unsafeCall","endLine":203,"endColumn":38},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .slice on an `any` value.","line":203,"column":33,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":203,"endColumn":38},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":206,"column":15,"nodeType":"Property","messageId":"anyAssignment","endLine":206,"endColumn":34},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .id on an `any` value.","line":206,"column":21,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":206,"endColumn":23},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":207,"column":15,"nodeType":"Property","messageId":"anyAssignment","endLine":207,"endColumn":28},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":208,"column":15,"nodeType":"Property","messageId":"anyAssignment","endLine":208,"endColumn":56},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .employeeId on an `any` value.","line":208,"column":29,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":208,"endColumn":39},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .employee_id on an `any` value.","line":208,"column":45,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":208,"endColumn":56},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":209,"column":15,"nodeType":"Property","messageId":"anyAssignment","endLine":209,"endColumn":62},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .employeeName on an `any` value.","line":209,"column":31,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":209,"endColumn":43},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .employee_name on an `any` value.","line":209,"column":49,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":209,"endColumn":62},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":210,"column":15,"nodeType":"Property","messageId":"anyAssignment","endLine":210,"endColumn":80},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .departmentName on an `any` value.","line":210,"column":29,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":210,"endColumn":43},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .department_name on an `any` value.","line":210,"column":49,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":210,"endColumn":64},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .department on an `any` value.","line":210,"column":70,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":210,"endColumn":80},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":211,"column":15,"nodeType":"Property","messageId":"anyAssignment","endLine":211,"endColumn":70},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .checkInTime on an `any` value.","line":211,"column":26,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":211,"endColumn":37},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .check_in_time on an `any` value.","line":211,"column":43,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":211,"endColumn":56},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .check_in on an `any` value.","line":211,"column":62,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":211,"endColumn":70},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":212,"column":15,"nodeType":"Property","messageId":"anyAssignment","endLine":212,"endColumn":74},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .checkOutTime on an `any` value.","line":212,"column":27,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":212,"endColumn":39},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .check_out_time on an `any` value.","line":212,"column":45,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":212,"endColumn":59},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .check_out on an `any` value.","line":212,"column":65,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":212,"endColumn":74},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":213,"column":15,"nodeType":"Property","messageId":"anyAssignment","endLine":213,"endColumn":43},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .status on an `any` value.","line":213,"column":25,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":213,"endColumn":31},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":214,"column":15,"nodeType":"Property","messageId":"anyAssignment","endLine":220,"endColumn":18},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .workingHours on an `any` value.","line":215,"column":19,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":215,"endColumn":31},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .working_hours on an `any` value.","line":216,"column":19,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":216,"endColumn":32},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `string`.","line":218,"column":19,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":218,"endColumn":65},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .checkInTime on an `any` value.","line":218,"column":21,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":218,"endColumn":32},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .check_in_time on an `any` value.","line":218,"column":38,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":218,"endColumn":51},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .check_in on an `any` value.","line":218,"column":57,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":218,"endColumn":65},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `string`.","line":219,"column":19,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":219,"endColumn":68},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .checkOutTime on an `any` value.","line":219,"column":21,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":219,"endColumn":33},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .check_out_time on an `any` value.","line":219,"column":39,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":219,"endColumn":53},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .check_out on an `any` value.","line":219,"column":59,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":219,"endColumn":68},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":221,"column":15,"nodeType":"Property","messageId":"anyAssignment","endLine":221,"endColumn":70},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .overtimeHours on an `any` value.","line":221,"column":32,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":221,"endColumn":45},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .overtime_hours on an `any` value.","line":221,"column":51,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":221,"endColumn":65},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":222,"column":15,"nodeType":"Property","messageId":"anyAssignment","endLine":222,"endColumn":37},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .remark on an `any` value.","line":222,"column":25,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":222,"endColumn":31},{"ruleId":"react-hooks/exhaustive-deps","severity":1,"message":"React Hook useCallback has missing dependencies: 'fetchAttendanceRecords' and 't'. Either include them or remove the dependency array.","line":237,"column":6,"nodeType":"ArrayExpression","endLine":237,"endColumn":8,"suggestions":[{"desc":"Update the dependencies array to be: [fetchAttendanceRecords, t]","fix":{"range":[7127,7129],"text":"[fetchAttendanceRecords, t]"}}]},{"ruleId":"react-hooks/exhaustive-deps","severity":1,"message":"React Hook useCallback has a missing dependency: 't'. Either include it or remove the dependency array.","line":247,"column":6,"nodeType":"ArrayExpression","endLine":247,"endColumn":8,"suggestions":[{"desc":"Update the dependencies array to be: [t]","fix":{"range":[7375,7377],"text":"[t]"}}]},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction<AttendanceRecord | null>`.","line":388,"column":22,"nodeType":"Identifier","messageId":"unsafeArgument","endLine":388,"endColumn":28},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":390,"column":7,"nodeType":"Property","messageId":"anyAssignment","endLine":390,"endColumn":34},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .date on an `any` value.","line":390,"column":30,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":390,"endColumn":34},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":391,"column":7,"nodeType":"Property","messageId":"anyAssignment","endLine":391,"endColumn":36},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .employeeId on an `any` value.","line":391,"column":26,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":391,"endColumn":36},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":392,"column":7,"nodeType":"Property","messageId":"anyAssignment","endLine":392,"endColumn":40},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .employeeName on an `any` value.","line":392,"column":28,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":392,"endColumn":40},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":393,"column":7,"nodeType":"Property","messageId":"anyAssignment","endLine":393,"endColumn":40},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .department on an `any` value.","line":393,"column":30,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":393,"endColumn":40},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":394,"column":7,"nodeType":"Property","messageId":"anyAssignment","endLine":394,"endColumn":34},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .checkIn on an `any` value.","line":394,"column":27,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":394,"endColumn":34},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":395,"column":7,"nodeType":"Property","messageId":"anyAssignment","endLine":395,"endColumn":36},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .checkOut on an `any` value.","line":395,"column":28,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":395,"endColumn":36},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":396,"column":7,"nodeType":"Property","messageId":"anyAssignment","endLine":396,"endColumn":28},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .status on an `any` value.","line":396,"column":22,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":396,"endColumn":28},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":397,"column":7,"nodeType":"Property","messageId":"anyAssignment","endLine":397,"endColumn":58},{"ruleId":"@typescript-eslint/no-unsafe-call","severity":1,"message":"Unsafe call of an `any` typed value.","line":397,"column":21,"nodeType":"MemberExpression","messageId":"unsafeCall","endLine":397,"endColumn":50},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .workingHours on an `any` value.","line":397,"column":28,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":397,"endColumn":40},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":398,"column":7,"nodeType":"Property","messageId":"anyAssignment","endLine":398,"endColumn":60},{"ruleId":"@typescript-eslint/no-unsafe-call","severity":1,"message":"Unsafe call of an `any` typed value.","line":398,"column":22,"nodeType":"MemberExpression","messageId":"unsafeCall","endLine":398,"endColumn":52},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .overtimeHours on an `any` value.","line":398,"column":29,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":398,"endColumn":42},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":399,"column":7,"nodeType":"Property","messageId":"anyAssignment","endLine":399,"endColumn":34},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .remark on an `any` value.","line":399,"column":22,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":399,"endColumn":28},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":422,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":422,"endColumn":36},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":423,"column":16,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":423,"endColumn":23},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .code on an `any` value.","line":423,"column":32,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":423,"endColumn":36},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `string | number | bigint | boolean | ReactElement<unknown, string | JSXElementConstructor<any>> | Iterable<ReactNode> | ... 4 more ... | undefined`.","line":428,"column":21,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":428,"endColumn":52},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .message on an `any` value.","line":428,"column":26,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":428,"endColumn":33},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":455,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":455,"endColumn":36},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":456,"column":16,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":456,"endColumn":23},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .code on an `any` value.","line":456,"column":32,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":456,"endColumn":36},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `string | number | bigint | boolean | ReactElement<unknown, string | JSXElementConstructor<any>> | Iterable<ReactNode> | ... 4 more ... | undefined`.","line":461,"column":21,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":461,"endColumn":54},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .message on an `any` value.","line":461,"column":26,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":461,"endColumn":33},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction<AttendanceRecord | null>`.","line":470,"column":22,"nodeType":"Identifier","messageId":"unsafeArgument","endLine":470,"endColumn":28},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":478,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":478,"endColumn":36},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":479,"column":16,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":479,"endColumn":23},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .code on an `any` value.","line":479,"column":32,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":479,"endColumn":36},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `string | number | bigint | boolean | ReactElement<unknown, string | JSXElementConstructor<any>> | Iterable<ReactNode> | ... 4 more ... | undefined`.","line":484,"column":21,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":484,"endColumn":54},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .message on an `any` value.","line":484,"column":26,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":484,"endColumn":33},{"ruleId":"react-hooks/static-components","severity":1,"message":"Error: Cannot create components during render\n\nComponents created during render will reset their state each time they are created. Declare components outside of render.\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\hr\\attendance\\page.tsx:767:22\n 765 | />\n 766 | </TableHead>\n> 767 | <SortableHeader field=\"date\">{tc('date')}</SortableHeader>\n | ^^^^^^^^^^^^^^ This component is created during render\n 768 | <SortableHeader field=\"employeeId\">{tc('employeeNo')}</SortableHeader>\n 769 | <SortableHeader field=\"employeeName\">{tc('name')}</SortableHeader>\n 770 | <SortableHeader field=\"department\">{tc('department')}</SortableHeader>\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\hr\\attendance\\page.tsx:266:26\n 264 | };\n 265 |\n> 266 | const SortableHeader = ({ field, children }: { field: string; children: React.ReactNode }) => (\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n> 267 | <TableHead\n | ^^^^^^^^^^^^^^\n> 268 | className=\"cursor-pointer select-none border border-border bg-muted text-center whitespace-nowrap hover:bg-muted/80 transition-colors\"\n …\n | ^^^^^^^^^^^^^^\n> 283 | </TableHead>\n | ^^^^^^^^^^^^^^\n> 284 | );\n | ^^^^ The component is created during render here\n 285 |\n 286 | // 筛选考勤记录\n 287 | const filteredRecords = useMemo(() => {","line":767,"column":22,"nodeType":null,"endLine":767,"endColumn":36},{"ruleId":"react-hooks/static-components","severity":1,"message":"Error: Cannot create components during render\n\nComponents created during render will reset their state each time they are created. Declare components outside of render.\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\hr\\attendance\\page.tsx:768:22\n 766 | </TableHead>\n 767 | <SortableHeader field=\"date\">{tc('date')}</SortableHeader>\n> 768 | <SortableHeader field=\"employeeId\">{tc('employeeNo')}</SortableHeader>\n | ^^^^^^^^^^^^^^ This component is created during render\n 769 | <SortableHeader field=\"employeeName\">{tc('name')}</SortableHeader>\n 770 | <SortableHeader field=\"department\">{tc('department')}</SortableHeader>\n 771 | <SortableHeader field=\"checkIn\">{tc('checkIn')}</SortableHeader>\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\hr\\attendance\\page.tsx:266:26\n 264 | };\n 265 |\n> 266 | const SortableHeader = ({ field, children }: { field: string; children: React.ReactNode }) => (\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n> 267 | <TableHead\n | ^^^^^^^^^^^^^^\n> 268 | className=\"cursor-pointer select-none border border-border bg-muted text-center whitespace-nowrap hover:bg-muted/80 transition-colors\"\n …\n | ^^^^^^^^^^^^^^\n> 283 | </TableHead>\n | ^^^^^^^^^^^^^^\n> 284 | );\n | ^^^^ The component is created during render here\n 285 |\n 286 | // 筛选考勤记录\n 287 | const filteredRecords = useMemo(() => {","line":768,"column":22,"nodeType":null,"endLine":768,"endColumn":36},{"ruleId":"react-hooks/static-components","severity":1,"message":"Error: Cannot create components during render\n\nComponents created during render will reset their state each time they are created. Declare components outside of render.\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\hr\\attendance\\page.tsx:769:22\n 767 | <SortableHeader field=\"date\">{tc('date')}</SortableHeader>\n 768 | <SortableHeader field=\"employeeId\">{tc('employeeNo')}</SortableHeader>\n> 769 | <SortableHeader field=\"employeeName\">{tc('name')}</SortableHeader>\n | ^^^^^^^^^^^^^^ This component is created during render\n 770 | <SortableHeader field=\"department\">{tc('department')}</SortableHeader>\n 771 | <SortableHeader field=\"checkIn\">{tc('checkIn')}</SortableHeader>\n 772 | <SortableHeader field=\"checkOut\">{tc('checkOut')}</SortableHeader>\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\hr\\attendance\\page.tsx:266:26\n 264 | };\n 265 |\n> 266 | const SortableHeader = ({ field, children }: { field: string; children: React.ReactNode }) => (\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n> 267 | <TableHead\n | ^^^^^^^^^^^^^^\n> 268 | className=\"cursor-pointer select-none border border-border bg-muted text-center whitespace-nowrap hover:bg-muted/80 transition-colors\"\n …\n | ^^^^^^^^^^^^^^\n> 283 | </TableHead>\n | ^^^^^^^^^^^^^^\n> 284 | );\n | ^^^^ The component is created during render here\n 285 |\n 286 | // 筛选考勤记录\n 287 | const filteredRecords = useMemo(() => {","line":769,"column":22,"nodeType":null,"endLine":769,"endColumn":36},{"ruleId":"react-hooks/static-components","severity":1,"message":"Error: Cannot create components during render\n\nComponents created during render will reset their state each time they are created. Declare components outside of render.\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\hr\\attendance\\page.tsx:770:22\n 768 | <SortableHeader field=\"employeeId\">{tc('employeeNo')}</SortableHeader>\n 769 | <SortableHeader field=\"employeeName\">{tc('name')}</SortableHeader>\n> 770 | <SortableHeader field=\"department\">{tc('department')}</SortableHeader>\n | ^^^^^^^^^^^^^^ This component is created during render\n 771 | <SortableHeader field=\"checkIn\">{tc('checkIn')}</SortableHeader>\n 772 | <SortableHeader field=\"checkOut\">{tc('checkOut')}</SortableHeader>\n 773 | <SortableHeader field=\"workingHours\">{tc('workingHours')}</SortableHeader>\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\hr\\attendance\\page.tsx:266:26\n 264 | };\n 265 |\n> 266 | const SortableHeader = ({ field, children }: { field: string; children: React.ReactNode }) => (\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n> 267 | <TableHead\n | ^^^^^^^^^^^^^^\n> 268 | className=\"cursor-pointer select-none border border-border bg-muted text-center whitespace-nowrap hover:bg-muted/80 transition-colors\"\n …\n | ^^^^^^^^^^^^^^\n> 283 | </TableHead>\n | ^^^^^^^^^^^^^^\n> 284 | );\n | ^^^^ The component is created during render here\n 285 |\n 286 | // 筛选考勤记录\n 287 | const filteredRecords = useMemo(() => {","line":770,"column":22,"nodeType":null,"endLine":770,"endColumn":36},{"ruleId":"react-hooks/static-components","severity":1,"message":"Error: Cannot create components during render\n\nComponents created during render will reset their state each time they are created. Declare components outside of render.\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\hr\\attendance\\page.tsx:771:22\n 769 | <SortableHeader field=\"employeeName\">{tc('name')}</SortableHeader>\n 770 | <SortableHeader field=\"department\">{tc('department')}</SortableHeader>\n> 771 | <SortableHeader field=\"checkIn\">{tc('checkIn')}</SortableHeader>\n | ^^^^^^^^^^^^^^ This component is created during render\n 772 | <SortableHeader field=\"checkOut\">{tc('checkOut')}</SortableHeader>\n 773 | <SortableHeader field=\"workingHours\">{tc('workingHours')}</SortableHeader>\n 774 | <SortableHeader field=\"overtimeHours\">{tc('overtimeHours')}</SortableHeader>\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\hr\\attendance\\page.tsx:266:26\n 264 | };\n 265 |\n> 266 | const SortableHeader = ({ field, children }: { field: string; children: React.ReactNode }) => (\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n> 267 | <TableHead\n | ^^^^^^^^^^^^^^\n> 268 | className=\"cursor-pointer select-none border border-border bg-muted text-center whitespace-nowrap hover:bg-muted/80 transition-colors\"\n …\n | ^^^^^^^^^^^^^^\n> 283 | </TableHead>\n | ^^^^^^^^^^^^^^\n> 284 | );\n | ^^^^ The component is created during render here\n 285 |\n 286 | // 筛选考勤记录\n 287 | const filteredRecords = useMemo(() => {","line":771,"column":22,"nodeType":null,"endLine":771,"endColumn":36},{"ruleId":"react-hooks/static-components","severity":1,"message":"Error: Cannot create components during render\n\nComponents created during render will reset their state each time they are created. Declare components outside of render.\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\hr\\attendance\\page.tsx:772:22\n 770 | <SortableHeader field=\"department\">{tc('department')}</SortableHeader>\n 771 | <SortableHeader field=\"checkIn\">{tc('checkIn')}</SortableHeader>\n> 772 | <SortableHeader field=\"checkOut\">{tc('checkOut')}</SortableHeader>\n | ^^^^^^^^^^^^^^ This component is created during render\n 773 | <SortableHeader field=\"workingHours\">{tc('workingHours')}</SortableHeader>\n 774 | <SortableHeader field=\"overtimeHours\">{tc('overtimeHours')}</SortableHeader>\n 775 | <SortableHeader field=\"status\">{tc('status')}</SortableHeader>\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\hr\\attendance\\page.tsx:266:26\n 264 | };\n 265 |\n> 266 | const SortableHeader = ({ field, children }: { field: string; children: React.ReactNode }) => (\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n> 267 | <TableHead\n | ^^^^^^^^^^^^^^\n> 268 | className=\"cursor-pointer select-none border border-border bg-muted text-center whitespace-nowrap hover:bg-muted/80 transition-colors\"\n …\n | ^^^^^^^^^^^^^^\n> 283 | </TableHead>\n | ^^^^^^^^^^^^^^\n> 284 | );\n | ^^^^ The component is created during render here\n 285 |\n 286 | // 筛选考勤记录\n 287 | const filteredRecords = useMemo(() => {","line":772,"column":22,"nodeType":null,"endLine":772,"endColumn":36},{"ruleId":"react-hooks/static-components","severity":1,"message":"Error: Cannot create components during render\n\nComponents created during render will reset their state each time they are created. Declare components outside of render.\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\hr\\attendance\\page.tsx:773:22\n 771 | <SortableHeader field=\"checkIn\">{tc('checkIn')}</SortableHeader>\n 772 | <SortableHeader field=\"checkOut\">{tc('checkOut')}</SortableHeader>\n> 773 | <SortableHeader field=\"workingHours\">{tc('workingHours')}</SortableHeader>\n | ^^^^^^^^^^^^^^ This component is created during render\n 774 | <SortableHeader field=\"overtimeHours\">{tc('overtimeHours')}</SortableHeader>\n 775 | <SortableHeader field=\"status\">{tc('status')}</SortableHeader>\n 776 | <TableHead className=\"text-center\">{tc('remark')}</TableHead>\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\hr\\attendance\\page.tsx:266:26\n 264 | };\n 265 |\n> 266 | const SortableHeader = ({ field, children }: { field: string; children: React.ReactNode }) => (\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n> 267 | <TableHead\n | ^^^^^^^^^^^^^^\n> 268 | className=\"cursor-pointer select-none border border-border bg-muted text-center whitespace-nowrap hover:bg-muted/80 transition-colors\"\n …\n | ^^^^^^^^^^^^^^\n> 283 | </TableHead>\n | ^^^^^^^^^^^^^^\n> 284 | );\n | ^^^^ The component is created during render here\n 285 |\n 286 | // 筛选考勤记录\n 287 | const filteredRecords = useMemo(() => {","line":773,"column":22,"nodeType":null,"endLine":773,"endColumn":36},{"ruleId":"react-hooks/static-components","severity":1,"message":"Error: Cannot create components during render\n\nComponents created during render will reset their state each time they are created. Declare components outside of render.\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\hr\\attendance\\page.tsx:774:22\n 772 | <SortableHeader field=\"checkOut\">{tc('checkOut')}</SortableHeader>\n 773 | <SortableHeader field=\"workingHours\">{tc('workingHours')}</SortableHeader>\n> 774 | <SortableHeader field=\"overtimeHours\">{tc('overtimeHours')}</SortableHeader>\n | ^^^^^^^^^^^^^^ This component is created during render\n 775 | <SortableHeader field=\"status\">{tc('status')}</SortableHeader>\n 776 | <TableHead className=\"text-center\">{tc('remark')}</TableHead>\n 777 | <TableHead className=\"text-center\">{tc('actions')}</TableHead>\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\hr\\attendance\\page.tsx:266:26\n 264 | };\n 265 |\n> 266 | const SortableHeader = ({ field, children }: { field: string; children: React.ReactNode }) => (\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n> 267 | <TableHead\n | ^^^^^^^^^^^^^^\n> 268 | className=\"cursor-pointer select-none border border-border bg-muted text-center whitespace-nowrap hover:bg-muted/80 transition-colors\"\n …\n | ^^^^^^^^^^^^^^\n> 283 | </TableHead>\n | ^^^^^^^^^^^^^^\n> 284 | );\n | ^^^^ The component is created during render here\n 285 |\n 286 | // 筛选考勤记录\n 287 | const filteredRecords = useMemo(() => {","line":774,"column":22,"nodeType":null,"endLine":774,"endColumn":36},{"ruleId":"react-hooks/static-components","severity":1,"message":"Error: Cannot create components during render\n\nComponents created during render will reset their state each time they are created. Declare components outside of render.\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\hr\\attendance\\page.tsx:775:22\n 773 | <SortableHeader field=\"workingHours\">{tc('workingHours')}</SortableHeader>\n 774 | <SortableHeader field=\"overtimeHours\">{tc('overtimeHours')}</SortableHeader>\n> 775 | <SortableHeader field=\"status\">{tc('status')}</SortableHeader>\n | ^^^^^^^^^^^^^^ This component is created during render\n 776 | <TableHead className=\"text-center\">{tc('remark')}</TableHead>\n 777 | <TableHead className=\"text-center\">{tc('actions')}</TableHead>\n 778 | </TableRow>\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\hr\\attendance\\page.tsx:266:26\n 264 | };\n 265 |\n> 266 | const SortableHeader = ({ field, children }: { field: string; children: React.ReactNode }) => (\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n> 267 | <TableHead\n | ^^^^^^^^^^^^^^\n> 268 | className=\"cursor-pointer select-none border border-border bg-muted text-center whitespace-nowrap hover:bg-muted/80 transition-colors\"\n …\n | ^^^^^^^^^^^^^^\n> 283 | </TableHead>\n | ^^^^^^^^^^^^^^\n> 284 | );\n | ^^^^ The component is created during render here\n 285 |\n 286 | // 筛选考勤记录\n 287 | const filteredRecords = useMemo(() => {","line":775,"column":22,"nodeType":null,"endLine":775,"endColumn":36},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"在\"。建议使用: {tc('text_h7s')}","line":1130,"column":44,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":1130,"endColumn":45},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"的考勤记录吗?此操作不可撤销。\"。建议使用: {tc('text_ybol37')}","line":1130,"column":66,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":1131,"endColumn":13}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":122,"fixableErrorCount":0,"fixableWarningCount":0,"source":"'use client';\r\n\r\nimport { authFetch } from '@/lib/auth-fetch';\r\nimport { useState, useEffect, useMemo, useCallback } from 'react';\r\nimport {\r\n Calendar,\r\n Clock,\r\n Search,\r\n Plus,\r\n Filter,\r\n Building2,\r\n CheckCircle2,\r\n AlertCircle,\r\n Clock3,\r\n MoreHorizontal,\r\n Printer,\r\n RefreshCw,\r\n RotateCcw,\r\n Edit,\r\n Trash2,\r\n ArrowUp,\r\n ArrowDown,\r\n ArrowUpDown,\r\n} from 'lucide-react';\r\nimport { useCompanyName } from '@/hooks/useCompanyName';\r\nimport { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';\r\nimport { Button } from '@/components/ui/button';\r\nimport { Input } from '@/components/ui/input';\r\nimport { Badge } from '@/components/ui/badge';\r\nimport {\r\n Table,\r\n TableBody,\r\n TableCell,\r\n TableHead,\r\n TableHeader,\r\n TableRow,\r\n} from '@/components/ui/table';\r\nimport {\r\n Dialog,\r\n DialogContent,\r\n DialogHeader,\r\n DialogTitle,\r\n DialogDescription,\r\n DialogFooter,\r\n} from '@/components/ui/dialog';\r\nimport { Label } from '@/components/ui/label';\r\nimport {\r\n Select,\r\n SelectContent,\r\n SelectItem,\r\n SelectTrigger,\r\n SelectValue,\r\n} from '@/components/ui/select';\r\nimport { MainLayout } from '@/components/layout';\r\nimport {\r\n DropdownMenu,\r\n DropdownMenuContent,\r\n DropdownMenuItem,\r\n DropdownMenuTrigger,\r\n} from '@/components/ui/dropdown-menu';\r\nimport { Checkbox } from '@/components/ui/checkbox';\r\nimport { toast } from 'sonner';\r\nimport { useTranslations, useLocale } from 'next-intl';\r\n\r\nconst departmentOptions = [\r\n { value: 'all', label: 'allDepartments' },\r\n { value: '管理部', label: 'adminDept' },\r\n { value: '业务部', label: 'businessDept' },\r\n { value: '生产部', label: 'productionDept' },\r\n { value: '打样中心', label: 'samplingDept' },\r\n { value: '采购部', label: 'purchaseDept' },\r\n { value: '品质部', label: 'qualityDept' },\r\n { value: '模切', label: 'dieCutDept' },\r\n { value: '商标', label: 'trademarkDept' },\r\n { value: '其他', label: 'otherDept' },\r\n { value: '采购', label: 'procurementDept' },\r\n];\r\n\r\n// 考勤记录接口\r\ninterface AttendanceRecord {\r\n id: number;\r\n date: string;\r\n employeeId: string;\r\n employeeName: string;\r\n department: string;\r\n checkIn?: string;\r\n checkOut?: string;\r\n status: 'normal' | 'late' | 'absent' | 'leave';\r\n workingHours?: number;\r\n overtimeHours?: number;\r\n remark?: string;\r\n}\r\n\r\nexport default function AttendancePage() {\r\n // 翻译钩子\r\n const t = useTranslations('Hr');\r\n const tc = useTranslations('Common');\r\n const locale = useLocale();\r\n const localeTag =\r\n locale === 'zh-CN' ? 'zh-CN' : locale === 'zh-TW' ? 'zh-TW' : locale === 'vi' ? 'vi' : 'en-US';\r\n\r\n const statusOptions = [\r\n {\r\n value: 'all',\r\n label: tc('all'),\r\n color: 'bg-gray-100 text-gray-700 dark:bg-gray-700 dark:text-gray-200',\r\n },\r\n {\r\n value: 'normal',\r\n label: tc('normal'),\r\n color: 'bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-300',\r\n },\r\n {\r\n value: 'late',\r\n label: tc('late'),\r\n color: 'bg-yellow-100 text-yellow-700 dark:bg-yellow-900/30 dark:text-yellow-300',\r\n },\r\n {\r\n value: 'absent',\r\n label: tc('absent'),\r\n color: 'bg-red-100 text-red-700 dark:bg-red-900/30 dark:text-red-300',\r\n },\r\n {\r\n value: 'leave',\r\n label: tc('leave'),\r\n color: 'bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-300',\r\n },\r\n ];\r\n\r\n const statusConfig: Record<\r\n string,\r\n { label: string; color: string; icon: React.ComponentType<Loose> }\r\n > = {\r\n normal: {\r\n label: tc('normal'),\r\n color: 'bg-green-100 text-green-700 border-green-200',\r\n icon: CheckCircle2,\r\n },\r\n late: {\r\n label: tc('late'),\r\n color: 'bg-yellow-100 text-yellow-700 border-yellow-200',\r\n icon: Clock3,\r\n },\r\n absent: {\r\n label: tc('absent'),\r\n color: 'bg-red-100 text-red-700 border-red-200',\r\n icon: AlertCircle,\r\n },\r\n leave: {\r\n label: tc('leave'),\r\n color: 'bg-blue-100 text-blue-700 border-blue-200',\r\n icon: Calendar,\r\n },\r\n };\r\n\r\n const { companyName } = useCompanyName();\r\n const [searchQuery, setSearchQuery] = useState('');\r\n const [statusFilter, setStatusFilter] = useState('all');\r\n const [departmentFilter, setDepartmentFilter] = useState('all');\r\n const [dateRange, setDateRange] = useState('all');\r\n const [isLoading, setIsLoading] = useState(false);\r\n const [sortField, setSortField] = useState<string>('');\r\n const [sortDirection, setSortDirection] = useState<'asc' | 'desc'>('asc');\r\n\r\n const [attendanceRecords, setAttendanceRecords] = useState<AttendanceRecord[]>([]);\r\n const [selectedRecords, setSelectedRecords] = useState<number[]>([]);\r\n\r\n const [isAddDialogOpen, setIsAddDialogOpen] = useState(false);\r\n const [isEditDialogOpen, setIsEditDialogOpen] = useState(false);\r\n const [isDeleteDialogOpen, setIsDeleteDialogOpen] = useState(false);\r\n const [currentRecord, setCurrentRecord] = useState<AttendanceRecord | null>(null);\r\n\r\n const [formData, setFormData] = useState({\r\n attendanceDate: '',\r\n employeeId: '',\r\n employeeName: '',\r\n departmentName: '',\r\n checkInTime: '',\r\n checkOutTime: '',\r\n status: 'normal',\r\n workingHours: '',\r\n overtimeHours: '',\r\n remark: '',\r\n });\r\n\r\n useEffect(() => {\r\n fetchAttendanceRecords();\r\n }, []);\r\n\r\n const fetchAttendanceRecords = async () => {\r\n try {\r\n setIsLoading(true);\r\n const res = await authFetch('/api/hr/attendance?pageSize=9999');\r\n const data = await res.json();\r\n if (data.success || data.code === 200) {\r\n // 统一处理API返回的数据结构\r\n const rawData = data.data;\r\n const rawList = Array.isArray(rawData) ? rawData : rawData?.list || [];\r\n setAttendanceRecords(\r\n rawList.map((r: Loose, idx: number) => {\r\n let dateStr = r.attendanceDate || r.attendance_date || r.date || '';\r\n if (dateStr && dateStr.includes('T')) {\r\n dateStr = dateStr.slice(0, 10);\r\n }\r\n return {\r\n id: r.id || idx + 1,\r\n date: dateStr,\r\n employeeId: r.employeeId || r.employee_id,\r\n employeeName: r.employeeName || r.employee_name,\r\n department: r.departmentName || r.department_name || r.department,\r\n checkIn: r.checkInTime || r.check_in_time || r.check_in,\r\n checkOut: r.checkOutTime || r.check_out_time || r.check_out,\r\n status: r.status || 'normal',\r\n workingHours:\r\n r.workingHours ||\r\n r.working_hours ||\r\n calculateWorkingHours(\r\n r.checkInTime || r.check_in_time || r.check_in,\r\n r.checkOutTime || r.check_out_time || r.check_out\r\n ),\r\n overtimeHours: r.overtimeHours || r.overtime_hours || 0,\r\n remark: r.remark || '',\r\n };\r\n })\r\n );\r\n }\r\n } catch {\r\n } finally {\r\n setIsLoading(false);\r\n }\r\n };\r\n\r\n // 刷新数据\r\n const handleRefresh = useCallback(async () => {\r\n await fetchAttendanceRecords();\r\n toast.success(t('refreshSuccess'));\r\n }, []);\r\n\r\n // 重置筛选\r\n const handleReset = useCallback(() => {\r\n setSearchQuery('');\r\n setStatusFilter('all');\r\n setDepartmentFilter('all');\r\n setDateRange('all');\r\n setSelectedRecords([]);\r\n toast.success(t('resetSuccess'));\r\n }, []);\r\n\r\n const calculateWorkingHours = (checkIn: string, checkOut: string): number => {\r\n if (!checkIn || !checkOut) return 0;\r\n const [inH, inM] = checkIn.split(':').map(Number);\r\n const [outH, outM] = checkOut.split(':').map(Number);\r\n const diffMinutes = outH * 60 + outM - (inH * 60 + inM);\r\n return diffMinutes > 0 ? Math.round((diffMinutes / 60) * 100) / 100 : 0;\r\n };\r\n\r\n const handleSort = (field: string) => {\r\n if (sortField === field) {\r\n setSortDirection((prev) => (prev === 'asc' ? 'desc' : 'asc'));\r\n } else {\r\n setSortField(field);\r\n setSortDirection('asc');\r\n }\r\n };\r\n\r\n const SortableHeader = ({ field, children }: { field: string; children: React.ReactNode }) => (\r\n <TableHead\r\n className=\"cursor-pointer select-none border border-border bg-muted text-center whitespace-nowrap hover:bg-muted/80 transition-colors\"\r\n onClick={() => handleSort(field)}\r\n >\r\n <div className=\"flex items-center justify-center gap-1\">\r\n {children}\r\n {sortField === field ? (\r\n sortDirection === 'asc' ? (\r\n <ArrowUp className=\"w-3 h-3\" />\r\n ) : (\r\n <ArrowDown className=\"w-3 h-3\" />\r\n )\r\n ) : (\r\n <ArrowUpDown className=\"w-3 h-3 opacity-30\" />\r\n )}\r\n </div>\r\n </TableHead>\r\n );\r\n\r\n // 筛选考勤记录\r\n const filteredRecords = useMemo(() => {\r\n let records = attendanceRecords.filter((record) => {\r\n const matchesSearch =\r\n !searchQuery ||\r\n record.employeeName.toLowerCase().includes(searchQuery.toLowerCase()) ||\r\n record.employeeId.toLowerCase().includes(searchQuery.toLowerCase()) ||\r\n record.department.toLowerCase().includes(searchQuery.toLowerCase());\r\n\r\n const matchesStatus = statusFilter === 'all' || record.status === statusFilter;\r\n\r\n const matchesDepartment =\r\n departmentFilter === 'all' || record.department === departmentFilter;\r\n\r\n return matchesSearch && matchesStatus && matchesDepartment;\r\n });\r\n\r\n if (sortField) {\r\n records = [...records].sort((a, b) => {\r\n let aVal: Loose, bVal: Loose;\r\n switch (sortField) {\r\n case 'date':\r\n aVal = a.date;\r\n bVal = b.date;\r\n break;\r\n case 'employeeId':\r\n aVal = a.employeeId;\r\n bVal = b.employeeId;\r\n break;\r\n case 'employeeName':\r\n aVal = a.employeeName;\r\n bVal = b.employeeName;\r\n break;\r\n case 'department':\r\n aVal = a.department;\r\n bVal = b.department;\r\n break;\r\n case 'checkIn':\r\n aVal = a.checkIn;\r\n bVal = b.checkIn;\r\n break;\r\n case 'checkOut':\r\n aVal = a.checkOut;\r\n bVal = b.checkOut;\r\n break;\r\n case 'workingHours':\r\n aVal = a.workingHours;\r\n bVal = b.workingHours;\r\n break;\r\n case 'overtimeHours':\r\n aVal = a.overtimeHours;\r\n bVal = b.overtimeHours;\r\n break;\r\n case 'status':\r\n aVal = a.status;\r\n bVal = b.status;\r\n break;\r\n default:\r\n return 0;\r\n }\r\n if (typeof aVal === 'number' && typeof bVal === 'number') {\r\n return sortDirection === 'asc' ? aVal - bVal : bVal - aVal;\r\n }\r\n const aStr = String(aVal || '');\r\n const bStr = String(bVal || '');\r\n return sortDirection === 'asc' ? aStr.localeCompare(bStr) : bStr.localeCompare(aStr);\r\n });\r\n }\r\n\r\n return records;\r\n }, [attendanceRecords, searchQuery, statusFilter, departmentFilter, sortField, sortDirection]);\r\n\r\n const handleFormTimeChange = (field: 'checkInTime' | 'checkOutTime', value: string) => {\r\n const updated = { ...formData, [field]: value };\r\n if (updated.checkInTime && updated.checkOutTime) {\r\n updated.workingHours = calculateWorkingHours(\r\n updated.checkInTime,\r\n updated.checkOutTime\r\n ).toString();\r\n }\r\n setFormData(updated);\r\n };\r\n\r\n // 新增考勤记录\r\n const handleAdd = () => {\r\n setFormData({\r\n attendanceDate: new Date().toISOString().slice(0, 10),\r\n employeeId: '',\r\n employeeName: '',\r\n departmentName: '',\r\n checkInTime: '08:00',\r\n checkOutTime: '17:30',\r\n status: 'normal',\r\n workingHours: '8.5',\r\n overtimeHours: '0',\r\n remark: '',\r\n });\r\n setIsAddDialogOpen(true);\r\n };\r\n\r\n // 编辑考勤记录\r\n const handleEdit = (record: Loose) => {\r\n setCurrentRecord(record);\r\n setFormData({\r\n attendanceDate: record.date,\r\n employeeId: record.employeeId,\r\n employeeName: record.employeeName,\r\n departmentName: record.department,\r\n checkInTime: record.checkIn,\r\n checkOutTime: record.checkOut,\r\n status: record.status,\r\n workingHours: record.workingHours?.toString() || '',\r\n overtimeHours: record.overtimeHours?.toString() || '',\r\n remark: record.remark || '',\r\n });\r\n setIsEditDialogOpen(true);\r\n };\r\n\r\n // 保存考勤记录\r\n const handleSave = async () => {\r\n try {\r\n const res = await authFetch('/api/hr/attendance', {\r\n method: 'POST',\r\n body: JSON.stringify({\r\n attendanceDate: formData.attendanceDate,\r\n employeeId: formData.employeeId,\r\n employeeName: formData.employeeName,\r\n departmentName: formData.departmentName,\r\n checkInTime: formData.checkInTime,\r\n checkOutTime: formData.checkOutTime,\r\n status: formData.status,\r\n workingHours: parseFloat(formData.workingHours) || undefined,\r\n overtimeHours: parseFloat(formData.overtimeHours) || 0,\r\n remark: formData.remark,\r\n }),\r\n });\r\n const data = await res.json();\r\n if (data.success || data.code === 200) {\r\n setIsAddDialogOpen(false);\r\n await fetchAttendanceRecords();\r\n toast.success(t('saveSuccess'));\r\n } else {\r\n toast.error(data.message || t('saveFailed'));\r\n }\r\n } catch {\r\n toast.error(t('saveRetry'));\r\n }\r\n };\r\n\r\n // 更新考勤记录\r\n const handleUpdate = async () => {\r\n if (!currentRecord) return;\r\n try {\r\n const res = await authFetch('/api/hr/attendance', {\r\n method: 'PUT',\r\n body: JSON.stringify({\r\n id: currentRecord.id,\r\n attendanceDate: formData.attendanceDate,\r\n employeeId: formData.employeeId,\r\n employeeName: formData.employeeName,\r\n departmentName: formData.departmentName,\r\n checkInTime: formData.checkInTime,\r\n checkOutTime: formData.checkOutTime,\r\n status: formData.status,\r\n workingHours: parseFloat(formData.workingHours) || undefined,\r\n overtimeHours: parseFloat(formData.overtimeHours) || 0,\r\n remark: formData.remark,\r\n }),\r\n });\r\n const data = await res.json();\r\n if (data.success || data.code === 200) {\r\n setIsEditDialogOpen(false);\r\n await fetchAttendanceRecords();\r\n toast.success(t('updateSuccess'));\r\n } else {\r\n toast.error(data.message || t('updateFailed'));\r\n }\r\n } catch {\r\n toast.error(t('updateRetry'));\r\n }\r\n };\r\n\r\n // 删除考勤记录\r\n const handleDelete = (record: Loose) => {\r\n setCurrentRecord(record);\r\n setIsDeleteDialogOpen(true);\r\n };\r\n\r\n const confirmDelete = async () => {\r\n if (!currentRecord) return;\r\n try {\r\n const res = await fetch(`/api/hr/attendance?id=${currentRecord.id}`, { method: 'DELETE' });\r\n const data = await res.json();\r\n if (data.success || data.code === 200) {\r\n setIsDeleteDialogOpen(false);\r\n await fetchAttendanceRecords();\r\n toast.success(t('deleteSuccess'));\r\n } else {\r\n toast.error(data.message || t('deleteFailed'));\r\n }\r\n } catch {\r\n toast.error(t('deleteRetry'));\r\n }\r\n };\r\n\r\n // 打印\r\n const handlePrint = () => {\r\n if (selectedRecords.length === 0) {\r\n toast.error(t('selectToPrint'));\r\n return;\r\n }\r\n const recordsToPrint = filteredRecords.filter((r) => selectedRecords.includes(r.id));\r\n const statusLabels: Record<string, string> = {\r\n normal: tc('normal'),\r\n late: t('late'),\r\n absent: t('absent'),\r\n leave: t('leave'),\r\n };\r\n const printWindow = window.open('', '_blank');\r\n if (!printWindow) {\r\n toast.error(t('cannotOpenPrint'));\r\n return;\r\n }\r\n const rows = recordsToPrint\r\n .map(\r\n (r) => `\r\n <tr>\r\n <td>${r.date}</td>\r\n <td>${r.employeeId}</td>\r\n <td>${r.employeeName}</td>\r\n <td>${r.department}</td>\r\n <td>${r.checkIn || '-'}</td>\r\n <td>${r.checkOut || '-'}</td>\r\n <td>${r.workingHours} ${t('workingHoursUnit')}</td>\r\n <td>${r.overtimeHours} ${t('workingHoursUnit')}</td>\r\n <td>${statusLabels[r.status] || r.status}</td>\r\n <td>${r.remark || '-'}</td>\r\n </tr>`\r\n )\r\n .join('');\r\n const html = `<!DOCTYPE html><html><head><meta charset=\"UTF-8\"><title>${t('printTitle')}\r\n \r\n \r\n

${t('attendanceRecords')}

\r\n
${t('printTime')}:${new Date().toLocaleString(localeTag)} | ${t('totalRecords', { count: recordsToPrint.length })}
\r\n \r\n \r\n ${rows}\r\n
${tc('date')}${tc('employeeNo')}${tc('name')}${tc('department')}${t('checkIn')}${t('checkOut')}${t('workingHours')}${t('overtimeHours')}${tc('status')}${tc('remark')}
\r\n
${companyName}
\r\n \r\n `;\r\n printWindow.document.write(html);\r\n printWindow.document.close();\r\n toast.success(t('printingRecords', { count: recordsToPrint.length }));\r\n };\r\n\r\n // 选择记录\r\n const toggleSelectRecord = (recordId: number) => {\r\n setSelectedRecords((prev) =>\r\n prev.includes(recordId) ? prev.filter((id) => id !== recordId) : [...prev, recordId]\r\n );\r\n };\r\n\r\n // 全选\r\n const toggleSelectAll = () => {\r\n if (selectedRecords.length === filteredRecords.length) {\r\n setSelectedRecords([]);\r\n } else {\r\n setSelectedRecords(filteredRecords.map((r) => r.id));\r\n }\r\n };\r\n\r\n // 计算统计数据\r\n const totalRecords = attendanceRecords.length;\r\n const normalRecords = attendanceRecords.filter((r) => r.status === 'normal').length;\r\n const lateRecords = attendanceRecords.filter((r) => r.status === 'late').length;\r\n const absentRecords = attendanceRecords.filter((r) => r.status === 'absent').length;\r\n const _leaveRecords = attendanceRecords.filter((r) => r.status === 'leave').length;\r\n const attendanceRate =\r\n totalRecords > 0 ? Math.round(((totalRecords - absentRecords) / totalRecords) * 100) : 0;\r\n\r\n return (\r\n \r\n
\r\n
\r\n
\r\n
\r\n \r\n
\r\n
\r\n

{t('attendance')}

\r\n

{t('manageAttendanceDesc')}

\r\n
\r\n
\r\n
\r\n\r\n
\r\n \r\n \r\n
\r\n \r\n \r\n
\r\n\r\n
\r\n
\r\n \r\n {tc('status')}:\r\n \r\n
\r\n\r\n
\r\n \r\n {tc('department')}:\r\n \r\n
\r\n\r\n
\r\n \r\n {tc('keyword')}:\r\n setSearchQuery(e.target.value)}\r\n className=\"w-64\"\r\n />\r\n
\r\n\r\n
\r\n \r\n {t('dateRange')}:\r\n \r\n
\r\n\r\n {selectedRecords.length > 0 && (\r\n \r\n {t('selectedRecords', { count: selectedRecords.length })}\r\n \r\n )}\r\n
\r\n\r\n
\r\n \r\n \r\n
\r\n
\r\n

{t('attendanceRate')}

\r\n

{attendanceRate}%

\r\n

{t('overallAttendanceRate')}

\r\n
\r\n
\r\n \r\n
\r\n
\r\n
\r\n
\r\n\r\n \r\n \r\n
\r\n
\r\n

{t('normalAttendance')}

\r\n

{normalRecords}

\r\n

{t('normalRecords')}

\r\n
\r\n
\r\n \r\n
\r\n
\r\n
\r\n
\r\n\r\n \r\n \r\n
\r\n
\r\n

{t('lateRecord')}

\r\n

{lateRecords}

\r\n

{t('lateRecords')}

\r\n
\r\n
\r\n \r\n
\r\n
\r\n
\r\n
\r\n\r\n \r\n \r\n
\r\n
\r\n

{t('absentRecord')}

\r\n

{absentRecords}

\r\n

{t('absentRecords')}

\r\n
\r\n
\r\n \r\n
\r\n
\r\n
\r\n
\r\n
\r\n\r\n \r\n \r\n {t('attendanceRecords')}\r\n
\r\n \r\n {t('totalRecords', { count: filteredRecords.length })}\r\n \r\n
\r\n
\r\n \r\n
\r\n \r\n \r\n \r\n \r\n 0\r\n }\r\n onCheckedChange={toggleSelectAll}\r\n />\r\n \r\n {tc('date')}\r\n {tc('employeeNo')}\r\n {tc('name')}\r\n {tc('department')}\r\n {tc('checkIn')}\r\n {tc('checkOut')}\r\n {tc('workingHours')}\r\n {tc('overtimeHours')}\r\n {tc('status')}\r\n {tc('remark')}\r\n {tc('actions')}\r\n \r\n \r\n \r\n {filteredRecords.map((record) => {\r\n const StatusIcon = statusConfig[record.status]?.icon || Clock;\r\n return (\r\n \r\n \r\n toggleSelectRecord(record.id)}\r\n />\r\n \r\n {record.date}\r\n {record.employeeId}\r\n {record.employeeName}\r\n {record.department}\r\n {record.checkIn || '-'}\r\n {record.checkOut || '-'}\r\n \r\n {record.workingHours} {tc('hours')}\r\n \r\n \r\n {record.overtimeHours} {tc('hours')}\r\n \r\n \r\n
\r\n \r\n {statusConfig[record.status]?.label || record.status}\r\n
\r\n
\r\n {record.remark || '-'}\r\n \r\n \r\n \r\n \r\n \r\n \r\n handleEdit(record)}>\r\n \r\n {tc('edit')}\r\n \r\n handleDelete(record)}>\r\n \r\n {tc('delete')}\r\n \r\n \r\n \r\n \r\n
\r\n );\r\n })}\r\n
\r\n
\r\n
\r\n {filteredRecords.length === 0 && (\r\n
\r\n
\r\n \r\n
\r\n

{t('noAttendanceRecords')}

\r\n
\r\n )}\r\n
\r\n
\r\n
\r\n\r\n {/* 新增考勤记录对话框 */}\r\n \r\n \r\n \r\n {t('addAttendance')}\r\n {t('fillAttendanceInfo')}\r\n \r\n
\r\n
\r\n
\r\n \r\n setFormData({ ...formData, attendanceDate: e.target.value })}\r\n />\r\n
\r\n
\r\n \r\n setFormData({ ...formData, employeeId: e.target.value })}\r\n placeholder={t('enterEmployeeNo')}\r\n />\r\n
\r\n
\r\n \r\n setFormData({ ...formData, employeeName: e.target.value })}\r\n placeholder={t('enterEmployeeName')}\r\n />\r\n
\r\n
\r\n \r\n setFormData({ ...formData, departmentName: value })}\r\n >\r\n \r\n \r\n \r\n \r\n {departmentOptions\r\n .filter((opt) => opt.value !== 'all')\r\n .map((option) => (\r\n \r\n {tc(option.label)}\r\n \r\n ))}\r\n \r\n \r\n
\r\n
\r\n \r\n handleFormTimeChange('checkInTime', e.target.value)}\r\n />\r\n
\r\n
\r\n \r\n handleFormTimeChange('checkOutTime', e.target.value)}\r\n />\r\n
\r\n
\r\n \r\n setFormData({ ...formData, status: value })}\r\n >\r\n \r\n \r\n \r\n \r\n {statusOptions\r\n .filter((opt) => opt.value !== 'all')\r\n .map((option) => (\r\n \r\n {option.label}\r\n \r\n ))}\r\n \r\n \r\n
\r\n
\r\n \r\n \r\n
\r\n
\r\n \r\n setFormData({ ...formData, overtimeHours: e.target.value })}\r\n placeholder={t('overtimeHoursPlaceholder')}\r\n />\r\n
\r\n
\r\n \r\n setFormData({ ...formData, remark: e.target.value })}\r\n placeholder={tc('enterRemark')}\r\n />\r\n
\r\n
\r\n
\r\n \r\n \r\n \r\n \r\n
\r\n
\r\n\r\n {/* 编辑考勤记录对话框 */}\r\n \r\n \r\n \r\n {t('editAttendance')}\r\n {tc('fillAttendanceInfoShort')}\r\n \r\n
\r\n
\r\n
\r\n \r\n setFormData({ ...formData, attendanceDate: e.target.value })}\r\n />\r\n
\r\n
\r\n \r\n setFormData({ ...formData, employeeId: e.target.value })}\r\n placeholder={t('enterEmployeeNo')}\r\n />\r\n
\r\n
\r\n \r\n setFormData({ ...formData, employeeName: e.target.value })}\r\n placeholder={t('enterEmployeeName')}\r\n />\r\n
\r\n
\r\n \r\n setFormData({ ...formData, departmentName: value })}\r\n >\r\n \r\n \r\n \r\n \r\n {departmentOptions\r\n .filter((opt) => opt.value !== 'all')\r\n .map((option) => (\r\n \r\n {tc(option.label)}\r\n \r\n ))}\r\n \r\n \r\n
\r\n
\r\n \r\n handleFormTimeChange('checkInTime', e.target.value)}\r\n />\r\n
\r\n
\r\n \r\n handleFormTimeChange('checkOutTime', e.target.value)}\r\n />\r\n
\r\n
\r\n \r\n setFormData({ ...formData, status: value })}\r\n >\r\n \r\n \r\n \r\n \r\n {statusOptions\r\n .filter((opt) => opt.value !== 'all')\r\n .map((option) => (\r\n \r\n {option.label}\r\n \r\n ))}\r\n \r\n \r\n
\r\n
\r\n \r\n \r\n
\r\n
\r\n \r\n setFormData({ ...formData, overtimeHours: e.target.value })}\r\n placeholder={t('overtimeHoursPlaceholder')}\r\n />\r\n
\r\n
\r\n \r\n setFormData({ ...formData, remark: e.target.value })}\r\n placeholder={tc('enterRemark')}\r\n />\r\n
\r\n
\r\n
\r\n \r\n \r\n \r\n \r\n
\r\n
\r\n\r\n {/* 删除考勤记录对话框 */}\r\n \r\n \r\n \r\n {t('deleteAttendance')}\r\n \r\n {tc('confirmDeleteAttendanceDesc')}\r\n {currentRecord?.employeeName}在{currentRecord?.date} 的考勤记录吗?此操作不可撤销。\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n );\r\n}\r\n","usedDeprecatedRules":[]},{"filePath":"D:\\dcprint\\erp-project\\src\\app\\[locale]\\hr\\certificates\\expiring\\page.tsx","messages":[{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":75,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":75,"endColumn":36},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .code on an `any` value.","line":76,"column":16,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":76,"endColumn":20},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction`.","line":77,"column":17,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":77,"endColumn":37},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":77,"column":22,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":77,"endColumn":26},{"ruleId":"react-hooks/set-state-in-effect","severity":1,"message":"Error: Calling setState synchronously within an effect can trigger cascading renders\n\nEffects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following:\n* Update external systems with the latest state from React.\n* Subscribe for updates from some external system, calling setState in a callback function when external state changes.\n\nCalling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect).\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\hr\\certificates\\expiring\\page.tsx:87:5\n 85 |\n 86 | useEffect(() => {\n> 87 | fetchData();\n | ^^^^^^^^^ Avoid calling setState() directly within an effect\n 88 | }, []);\n 89 |\n 90 | const handleRenew = async () => {","line":87,"column":5,"nodeType":null,"endLine":87,"endColumn":14},{"ruleId":"react-hooks/exhaustive-deps","severity":1,"message":"React Hook useEffect has a missing dependency: 'fetchData'. Either include it or remove the dependency array.","line":88,"column":6,"nodeType":"ArrayExpression","endLine":88,"endColumn":8,"suggestions":[{"desc":"Update the dependencies array to be: [fetchData]","fix":{"range":[2524,2526],"text":"[fetchData]"}}]},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":100,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":100,"endColumn":36},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .code on an `any` value.","line":101,"column":16,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":101,"endColumn":20},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `string | number | bigint | boolean | ReactElement> | Iterable | ... 4 more ... | undefined`.","line":108,"column":21,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":108,"endColumn":48},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .message on an `any` value.","line":108,"column":26,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":108,"endColumn":33}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":10,"fixableErrorCount":0,"fixableWarningCount":0,"source":"'use client';\r\n\r\nimport { authFetch } from '@/lib/auth-fetch';\r\nimport { useEffect, useState } from 'react';\r\nimport { MainLayout } from '@/components/layout';\r\nimport { Card, CardContent } from '@/components/ui/card';\r\nimport { Button } from '@/components/ui/button';\r\nimport { Badge } from '@/components/ui/badge';\r\nimport { Input } from '@/components/ui/input';\r\nimport { Label } from '@/components/ui/label';\r\nimport {\r\n Table,\r\n TableBody,\r\n TableCell,\r\n TableHead,\r\n TableHeader,\r\n TableRow,\r\n} from '@/components/ui/table';\r\nimport {\r\n Dialog,\r\n DialogContent,\r\n DialogHeader,\r\n DialogTitle,\r\n DialogFooter,\r\n} from '@/components/ui/dialog';\r\nimport { AlertTriangle, RefreshCw } from 'lucide-react';\r\nimport { toast } from 'sonner';\r\nimport { useTranslations } from 'next-intl';\r\n\r\ninterface ExpiringCert {\r\n id: number;\r\n employee_id: number;\r\n employee_name: string;\r\n cert_name: string;\r\n cert_code: string;\r\n expiry_date: string;\r\n days_remaining: number;\r\n}\r\n\r\nconst formatDate = (dateStr: string) => {\r\n if (!dateStr) return '-';\r\n return dateStr.slice(0, 10);\r\n};\r\n\r\nconst getExpiryBadge = (days: number) => {\r\n if (days < 15) {\r\n return {days};\r\n }\r\n if (days < 30) {\r\n return {days};\r\n }\r\n return {days};\r\n};\r\n\r\nconst getRowClass = (days: number) => {\r\n if (days < 15) return 'bg-red-50/50';\r\n if (days < 30) return 'bg-orange-50/30';\r\n return '';\r\n};\r\n\r\nexport default function ExpiringCertificatesPage() {\r\n const [list, setList] = useState([]);\r\n const [loading, setLoading] = useState(true);\r\n const [showRenewDialog, setShowRenewDialog] = useState(false);\r\n const [renewItem, setRenewItem] = useState(null);\r\n const [newExpiryDate, setNewExpiryDate] = useState('');\r\n\r\n const t = useTranslations('Hr');\r\n const tc = useTranslations('Common');\r\n\r\n const fetchData = async () => {\r\n try {\r\n setLoading(true);\r\n const res = await authFetch('/api/hr/certificates/expiring?days=90');\r\n const json = await res.json();\r\n if (json.code === 200) {\r\n setList(json.data.list || []);\r\n }\r\n } catch {\r\n toast.error(tc('fetchFailed'));\r\n } finally {\r\n setLoading(false);\r\n }\r\n };\r\n\r\n useEffect(() => {\r\n fetchData();\r\n }, []);\r\n\r\n const handleRenew = async () => {\r\n if (!renewItem || !newExpiryDate) {\r\n toast.error(tc('select'));\r\n return;\r\n }\r\n try {\r\n const res = await authFetch('/api/hr/certificates', {\r\n method: 'PUT',\r\n body: JSON.stringify({ id: renewItem.id, expiry_date: newExpiryDate }),\r\n });\r\n const json = await res.json();\r\n if (json.code === 200) {\r\n toast.success(tc('success'));\r\n setShowRenewDialog(false);\r\n setRenewItem(null);\r\n setNewExpiryDate('');\r\n fetchData();\r\n } else {\r\n toast.error(json.message || tc('error'));\r\n }\r\n } catch {\r\n toast.error(tc('error'));\r\n }\r\n };\r\n\r\n const openRenewDialog = (item: ExpiringCert) => {\r\n setRenewItem(item);\r\n setNewExpiryDate('');\r\n setShowRenewDialog(true);\r\n };\r\n\r\n const expiringCount = list.length;\r\n const criticalCount = list.filter((item) => item.days_remaining < 15).length;\r\n const warningCount = list.filter(\r\n (item) => item.days_remaining >= 15 && item.days_remaining < 30\r\n ).length;\r\n\r\n return (\r\n \r\n
\r\n
\r\n

{t('certExpiryAlert')}

\r\n \r\n
\r\n\r\n
\r\n \r\n
\r\n

\r\n {t('expiringCert')}:{expiringCount}\r\n

\r\n

\r\n {criticalCount} {tc('critical')},\r\n {warningCount} {tc('warning')}\r\n

\r\n
\r\n
\r\n\r\n \r\n \r\n {loading ? (\r\n
\r\n {tc('loading')}\r\n
\r\n ) : (\r\n \r\n \r\n \r\n {t('employeeName')}\r\n {t('certName')}\r\n {t('certCode')}\r\n {t('expiryDate')}\r\n {t('daysUntilExpiry')}\r\n {tc('operation')}\r\n \r\n \r\n \r\n {list.map((item) => (\r\n \r\n \r\n {item.employee_name}\r\n \r\n {item.cert_name}\r\n {item.cert_code}\r\n \r\n {formatDate(item.expiry_date)}\r\n \r\n \r\n {getExpiryBadge(item.days_remaining)}\r\n \r\n \r\n openRenewDialog(item)}\r\n >\r\n {t('renew')}\r\n \r\n \r\n \r\n ))}\r\n {list.length === 0 && (\r\n \r\n \r\n {tc('noData')}\r\n \r\n \r\n )}\r\n \r\n
\r\n )}\r\n
\r\n
\r\n\r\n \r\n \r\n \r\n {t('renew')}\r\n \r\n {renewItem && (\r\n
\r\n
\r\n
\r\n {t('certName')}:\r\n {renewItem.cert_name}\r\n
\r\n
\r\n {t('certCode')}:\r\n {renewItem.cert_code}\r\n
\r\n
\r\n {t('employeeName')}:\r\n {renewItem.employee_name}\r\n
\r\n
\r\n {t('expiryDate')}:\r\n \r\n {formatDate(renewItem.expiry_date)}\r\n \r\n
\r\n
\r\n
\r\n \r\n setNewExpiryDate(e.target.value)}\r\n />\r\n
\r\n
\r\n )}\r\n \r\n \r\n \r\n \r\n
\r\n
\r\n
\r\n
\r\n );\r\n}\r\n","usedDeprecatedRules":[]},{"filePath":"D:\\dcprint\\erp-project\\src\\app\\[locale]\\hr\\certificates\\page.tsx","messages":[{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":117,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":117,"endColumn":36},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .code on an `any` value.","line":118,"column":16,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":118,"endColumn":20},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction`.","line":119,"column":17,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":119,"endColumn":37},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":119,"column":22,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":119,"endColumn":26},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction`.","line":120,"column":18,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":120,"endColumn":38},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":120,"column":23,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":120,"endColumn":27},{"ruleId":"react-hooks/set-state-in-effect","severity":1,"message":"Error: Calling setState synchronously within an effect can trigger cascading renders\n\nEffects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following:\n* Update external systems with the latest state from React.\n* Subscribe for updates from some external system, calling setState in a callback function when external state changes.\n\nCalling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect).\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\hr\\certificates\\page.tsx:128:5\n 126 |\n 127 | useEffect(() => {\n> 128 | fetchData();\n | ^^^^^^^^^ Avoid calling setState() directly within an effect\n 129 | }, [page]);\n 130 |\n 131 | const handleSave = async () => {","line":128,"column":5,"nodeType":null,"endLine":128,"endColumn":14},{"ruleId":"react-hooks/exhaustive-deps","severity":1,"message":"React Hook useEffect has a missing dependency: 'fetchData'. Either include it or remove the dependency array.","line":129,"column":6,"nodeType":"ArrayExpression","endLine":129,"endColumn":12,"suggestions":[{"desc":"Update the dependencies array to be: [fetchData, page]","fix":{"range":[3753,3759],"text":"[fetchData, page]"}}]},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":138,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":138,"endColumn":36},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .code on an `any` value.","line":139,"column":16,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":139,"endColumn":20},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `string | number | bigint | boolean | ReactElement> | Iterable | ... 4 more ... | undefined`.","line":144,"column":21,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":144,"endColumn":48},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .message on an `any` value.","line":144,"column":26,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":144,"endColumn":33},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":155,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":155,"endColumn":36},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .code on an `any` value.","line":156,"column":16,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":156,"endColumn":20},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `string | number | bigint | boolean | ReactElement> | Iterable | ... 4 more ... | undefined`.","line":160,"column":21,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":160,"endColumn":55},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .message on an `any` value.","line":160,"column":26,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":160,"endColumn":33},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"附件:\"。建议使用: {tc('text_mf6eg')}","line":502,"column":61,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":502,"endColumn":64}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":17,"fixableErrorCount":0,"fixableWarningCount":0,"source":"'use client';\r\n\r\nimport { authFetch } from '@/lib/auth-fetch';\r\nimport { useEffect, useState } from 'react';\r\nimport { MainLayout } from '@/components/layout';\r\nimport { Card, CardContent } from '@/components/ui/card';\r\nimport { Button } from '@/components/ui/button';\r\nimport { Input } from '@/components/ui/input';\r\nimport { Badge } from '@/components/ui/badge';\r\nimport { Textarea } from '@/components/ui/textarea';\r\nimport {\r\n Table,\r\n TableBody,\r\n TableCell,\r\n TableHead,\r\n TableHeader,\r\n TableRow,\r\n} from '@/components/ui/table';\r\nimport {\r\n Dialog,\r\n DialogContent,\r\n DialogHeader,\r\n DialogTitle,\r\n DialogDescription,\r\n DialogFooter,\r\n} from '@/components/ui/dialog';\r\nimport { Label } from '@/components/ui/label';\r\nimport {\r\n Select,\r\n SelectContent,\r\n SelectItem,\r\n SelectTrigger,\r\n SelectValue,\r\n} from '@/components/ui/select';\r\nimport { Plus, Search, Edit, Trash2, AlertTriangle } from 'lucide-react';\r\nimport { toast } from 'sonner';\r\nimport { useTranslations } from 'next-intl';\r\n\r\ninterface Certificate {\r\n id: number;\r\n employee_id: number;\r\n employee_name: string;\r\n cert_name: string;\r\n cert_code: string;\r\n cert_type: string;\r\n issue_authority: string;\r\n issue_date: string;\r\n expiry_date: string;\r\n status: string;\r\n remind_days: number;\r\n file_url: string;\r\n remark: string;\r\n}\r\n\r\nconst certTypeMap: Record = {\r\n operation: 'certTypeOperation',\r\n safety: 'certTypeSafety',\r\n quality: 'certTypeQuality',\r\n skill: 'certTypeSkill',\r\n};\r\n\r\nconst certTypeOptions = [\r\n { value: 'all', label: 'allTypes' },\r\n { value: 'operation', label: 'certTypeOperation' },\r\n { value: 'safety', label: 'certTypeSafety' },\r\n { value: 'quality', label: 'certTypeQuality' },\r\n { value: 'skill', label: 'certTypeSkill' },\r\n];\r\n\r\nconst statusOptions = [\r\n { value: 'all', label: 'allStatus' },\r\n { value: 'active', label: 'valid' },\r\n { value: 'expired', label: 'expired' },\r\n];\r\n\r\nconst formatDate = (dateStr: string) => {\r\n if (!dateStr) return '-';\r\n return dateStr.slice(0, 10);\r\n};\r\n\r\nconst getDaysUntilExpiry = (expiryDate: string) => {\r\n if (!expiryDate) return Infinity;\r\n const now = new Date();\r\n now.setHours(0, 0, 0, 0);\r\n const expiry = new Date(expiryDate);\r\n expiry.setHours(0, 0, 0, 0);\r\n return Math.ceil((expiry.getTime() - now.getTime()) / (1000 * 60 * 60 * 24));\r\n};\r\n\r\nexport default function CertificatesPage() {\r\n const [list, setList] = useState([]);\r\n const [total, setTotal] = useState(0);\r\n const [page, setPage] = useState(1);\r\n const [pageSize] = useState(20);\r\n const [employeeId, setEmployeeId] = useState('');\r\n const [certType, setCertType] = useState('all');\r\n const [status, setStatus] = useState('all');\r\n const [showDialog, setShowDialog] = useState(false);\r\n const [showDetail, setShowDetail] = useState(false);\r\n const [editItem, setEditItem] = useState>({});\r\n const [detailItem, setDetailItem] = useState(null);\r\n\r\n const t = useTranslations('Hr');\r\n const tc = useTranslations('Common');\r\n\r\n const fetchData = async () => {\r\n try {\r\n const params = new URLSearchParams({\r\n page: String(page),\r\n pageSize: String(pageSize),\r\n });\r\n if (employeeId) params.append('employeeId', employeeId);\r\n if (certType) params.append('certType', certType);\r\n if (status) params.append('status', status);\r\n\r\n const res = await authFetch(`/api/hr/certificates?${params}`);\r\n const json = await res.json();\r\n if (json.code === 200) {\r\n setList(json.data.list || []);\r\n setTotal(json.data.total || 0);\r\n }\r\n } catch {\r\n toast.error(tc('fetchFailed'));\r\n }\r\n };\r\n\r\n useEffect(() => {\r\n fetchData();\r\n }, [page]);\r\n\r\n const handleSave = async () => {\r\n try {\r\n const isEdit = !!editItem.id;\r\n const res = await authFetch('/api/hr/certificates', {\r\n method: isEdit ? 'PUT' : 'POST',\r\n body: JSON.stringify(editItem),\r\n });\r\n const json = await res.json();\r\n if (json.code === 200) {\r\n toast.success(isEdit ? tc('updateSuccess') : tc('createSuccess'));\r\n setShowDialog(false);\r\n fetchData();\r\n } else {\r\n toast.error(json.message || tc('error'));\r\n }\r\n } catch {\r\n toast.error(tc('error'));\r\n }\r\n };\r\n\r\n const handleDelete = async (id: number) => {\r\n if (!confirm(tc('confirmDelete'))) return;\r\n try {\r\n const res = await authFetch(`/api/hr/certificates?id=${id}`, { method: 'DELETE' });\r\n const json = await res.json();\r\n if (json.code === 200) {\r\n toast.success(tc('deleteSuccess'));\r\n fetchData();\r\n } else {\r\n toast.error(json.message || tc('deleteFailed'));\r\n }\r\n } catch {\r\n toast.error(tc('deleteFailed'));\r\n }\r\n };\r\n\r\n const handleRowClick = (item: Certificate) => {\r\n setDetailItem(item);\r\n setShowDetail(true);\r\n };\r\n\r\n const totalPages = Math.ceil(total / pageSize);\r\n\r\n return (\r\n \r\n
\r\n
\r\n

{t('certificateManage')}

\r\n
\r\n
\r\n setEmployeeId(e.target.value)}\r\n className=\"w-28 h-8 text-sm\"\r\n />\r\n \r\n \r\n \r\n
\r\n {\r\n setEditItem({});\r\n setShowDialog(true);\r\n }}\r\n >\r\n \r\n {tc('add')}\r\n \r\n
\r\n
\r\n\r\n \r\n \r\n \r\n \r\n \r\n {t('certName')}\r\n {t('certCode')}\r\n {t('certType')}\r\n {t('issueAuthority')}\r\n {t('issueDate')}\r\n {t('expiryDate')}\r\n {tc('status')}\r\n {t('remindDays')}\r\n {tc('operation')}\r\n \r\n \r\n \r\n {list.map((item) => {\r\n const daysLeft = getDaysUntilExpiry(item.expiry_date);\r\n const isExpiring = daysLeft <= 30 && daysLeft > 0;\r\n return (\r\n handleRowClick(item)}\r\n >\r\n {item.cert_name}\r\n {item.cert_code}\r\n \r\n {t(certTypeMap[item.cert_type] || item.cert_type)}\r\n \r\n {item.issue_authority || '-'}\r\n {formatDate(item.issue_date)}\r\n {formatDate(item.expiry_date)}\r\n \r\n {item.status === 'active' ? (\r\n {tc('active')}\r\n ) : (\r\n {tc('expired')}\r\n )}\r\n \r\n \r\n {isExpiring ? (\r\n \r\n \r\n {daysLeft}\r\n \r\n ) : (\r\n -\r\n )}\r\n \r\n e.stopPropagation()}>\r\n
\r\n {\r\n setEditItem(item);\r\n setShowDialog(true);\r\n }}\r\n >\r\n \r\n \r\n handleDelete(item.id)}\r\n >\r\n \r\n \r\n
\r\n
\r\n \r\n );\r\n })}\r\n {list.length === 0 && (\r\n \r\n \r\n {tc('noData')}\r\n \r\n \r\n )}\r\n
\r\n
\r\n
\r\n
\r\n\r\n
\r\n {t('totalRecords', { count: total })}\r\n
\r\n setPage((p) => p - 1)}\r\n >\r\n {tc('prevPage')}\r\n \r\n \r\n {page} / {totalPages || 1}\r\n \r\n = totalPages}\r\n onClick={() => setPage((p) => p + 1)}\r\n >\r\n {tc('nextPage')}\r\n \r\n
\r\n
\r\n\r\n \r\n \r\n \r\n {editItem.id ? tc('edit') : tc('add')}\r\n \r\n
\r\n
\r\n \r\n setEditItem({ ...editItem, employee_id: Number(e.target.value) })}\r\n />\r\n
\r\n
\r\n \r\n setEditItem({ ...editItem, cert_name: e.target.value })}\r\n />\r\n
\r\n
\r\n \r\n setEditItem({ ...editItem, cert_code: e.target.value })}\r\n />\r\n
\r\n
\r\n \r\n setEditItem({ ...editItem, cert_type: v })}\r\n >\r\n \r\n \r\n \r\n \r\n {t('certTypeOperation')}\r\n {t('certTypeSafety')}\r\n {t('certTypeQuality')}\r\n {t('certTypeSkill')}\r\n \r\n \r\n
\r\n
\r\n \r\n setEditItem({ ...editItem, issue_authority: e.target.value })}\r\n />\r\n
\r\n
\r\n \r\n setEditItem({ ...editItem, remind_days: Number(e.target.value) })}\r\n />\r\n
\r\n
\r\n \r\n setEditItem({ ...editItem, issue_date: e.target.value })}\r\n />\r\n
\r\n
\r\n \r\n setEditItem({ ...editItem, expiry_date: e.target.value })}\r\n />\r\n
\r\n
\r\n \r\n setEditItem({ ...editItem, file_url: e.target.value })}\r\n placeholder={t('urlPlaceholder')}\r\n />\r\n
\r\n
\r\n \r\n setEditItem({ ...editItem, remark: e.target.value })}\r\n rows={3}\r\n />\r\n
\r\n
\r\n \r\n \r\n \r\n \r\n
\r\n
\r\n\r\n \r\n \r\n \r\n {tc('detail')}\r\n {tc('view')}\r\n \r\n {detailItem && (\r\n
\r\n
\r\n
\r\n {t('certName')}:\r\n {detailItem.cert_name}\r\n
\r\n
\r\n {t('certCode')}:\r\n {detailItem.cert_code}\r\n
\r\n
\r\n {t('employeeName')}:\r\n {detailItem.employee_name}\r\n
\r\n
\r\n {t('certType')}:\r\n {certTypeMap[detailItem.cert_type] || detailItem.cert_type}\r\n
\r\n
\r\n {t('issueAuthority')}:\r\n {detailItem.issue_authority || '-'}\r\n
\r\n
\r\n {t('issueDate')}:\r\n {formatDate(detailItem.issue_date)}\r\n
\r\n
\r\n {t('expiryDate')}:\r\n {formatDate(detailItem.expiry_date)}\r\n
\r\n
\r\n {t('remindDays')}:\r\n {detailItem.remind_days || 30}\r\n
\r\n
\r\n {tc('status')}:\r\n {detailItem.status === 'active' ? (\r\n {tc('active')}\r\n ) : (\r\n {tc('expired')}\r\n )}\r\n
\r\n
\r\n {detailItem.remark && (\r\n
\r\n {tc('remark')}:\r\n

{detailItem.remark}

\r\n
\r\n )}\r\n {detailItem.file_url && (\r\n
\r\n 附件:\r\n \r\n {tc('view')}\r\n \r\n
\r\n )}\r\n
\r\n )}\r\n \r\n \r\n \r\n
\r\n
\r\n
\r\n
\r\n );\r\n}\r\n","usedDeprecatedRules":[]},{"filePath":"D:\\dcprint\\erp-project\\src\\app\\[locale]\\hr\\employee\\components\\dialogs\\BatchPrintDialog.tsx","messages":[{"ruleId":"@next/next/no-img-element","severity":1,"message":"Using `` could result in slower LCP and higher bandwidth. Consider using `` from `next/image` or a custom image loader to automatically optimize images. This may incur additional usage or cost from your provider. See: https://nextjs.org/docs/messages/no-img-element","line":43,"column":13,"nodeType":"JSXOpeningElement","endLine":43,"endColumn":100}],"suppressedMessages":[{"ruleId":"@next/next/no-img-element","severity":1,"message":"Using `` could result in slower LCP and higher bandwidth. Consider using `` from `next/image` or a custom image loader to automatically optimize images. This may incur additional usage or cost from your provider. See: https://nextjs.org/docs/messages/no-img-element","line":62,"column":13,"nodeType":"JSXOpeningElement","endLine":62,"endColumn":77,"suppressions":[{"kind":"directive","justification":""}]}],"errorCount":0,"fatalErrorCount":0,"warningCount":1,"fixableErrorCount":0,"fixableWarningCount":0,"source":"'use client';\n\nimport { useState, useEffect } from 'react';\nimport {\n Dialog,\n DialogContent,\n DialogHeader,\n DialogTitle,\n DialogDescription,\n DialogFooter,\n} from '@/components/ui/dialog';\nimport { Button } from '@/components/ui/button';\nimport { Printer, UserCircle } from 'lucide-react';\nimport { useTranslations } from 'next-intl';\nimport QRCode from 'qrcode';\nimport type { Employee } from '../../types';\n\n// 批量打印卡片组件\nfunction BatchPrintCard({ employee, index }: { employee: Employee; index: number }) {\n const tc = useTranslations('Common');\n const [qrUrl, setQrUrl] = useState('');\n\n useEffect(() => {\n const generateQR = async () => {\n const queryUrl = `${window.location.origin}/hr/employee/query?id=${employee.id}`;\n const url = await QRCode.toDataURL(queryUrl, { width: 100, margin: 1 });\n setQrUrl(url);\n };\n generateQR();\n }, [employee.id]);\n\n return (\n
\n
\n \n {tc('serialNo')} {index + 1}\n \n {employee.name}\n
\n
\n
\n {employee.photo ? (\n {employee.name}\n ) : (\n \n )}\n
\n
\n
\n {tc('employeeNo')}: {employee.employee_no}\n
\n
\n {tc('department')}: {employee.dept_name}\n
\n
\n {tc('position')}: {employee.position || '-'}\n
\n
\n {qrUrl && (\n
\n {/* eslint-disable-next-line @next/next/no-img-element */}\n {tc('qrCode')}\n
\n )}\n
\n
\n );\n}\n\ninterface BatchPrintDialogProps {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n employees: Employee[];\n selectedEmployees: number[];\n onPrintAll: () => void;\n}\n\nexport function BatchPrintDialog({\n open,\n onOpenChange,\n employees,\n selectedEmployees,\n onPrintAll,\n}: BatchPrintDialogProps) {\n const tc = useTranslations('Common');\n\n return (\n \n \n \n {tc('batchPrintTitle')}\n \n {tc('batchPrintDesc', { count: selectedEmployees.length })}\n \n \n
\n
\n {employees\n .filter((emp) => selectedEmployees.includes(emp.id))\n .map((emp, index) => (\n \n ))}\n
\n
\n \n \n \n \n
\n
\n );\n}\n","usedDeprecatedRules":[]},{"filePath":"D:\\dcprint\\erp-project\\src\\app\\[locale]\\hr\\employee\\components\\dialogs\\EmployeeFormDialog.tsx","messages":[{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"初中\"。建议使用: {tc('text_ee9c')}","line":319,"column":35,"nodeType":"Literal","messageId":"noChineseInJSX","endLine":319,"endColumn":39},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"中专\"。建议使用: {tc('text_dq4m')}","line":320,"column":35,"nodeType":"Literal","messageId":"noChineseInJSX","endLine":320,"endColumn":39},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"高中\"。建议使用: {tc('text_qrmd')}","line":321,"column":35,"nodeType":"Literal","messageId":"noChineseInJSX","endLine":321,"endColumn":39},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"大专\"。建议使用: {tc('text_flcc')}","line":322,"column":35,"nodeType":"Literal","messageId":"noChineseInJSX","endLine":322,"endColumn":39},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"本科\"。建议使用: {tc('text_i7tx')}","line":323,"column":35,"nodeType":"Literal","messageId":"noChineseInJSX","endLine":323,"endColumn":39},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"硕士\"。建议使用: {tc('text_kyeu')}","line":324,"column":35,"nodeType":"Literal","messageId":"noChineseInJSX","endLine":324,"endColumn":39},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"博士\"。建议使用: {tc('text_enyp')}","line":325,"column":35,"nodeType":"Literal","messageId":"noChineseInJSX","endLine":325,"endColumn":39}],"suppressedMessages":[{"ruleId":"@next/next/no-img-element","severity":1,"message":"Using `` could result in slower LCP and higher bandwidth. Consider using `` from `next/image` or a custom image loader to automatically optimize images. This may incur additional usage or cost from your provider. See: https://nextjs.org/docs/messages/no-img-element","line":81,"column":19,"nodeType":"JSXOpeningElement","endLine":85,"endColumn":21,"suppressions":[{"kind":"directive","justification":""}]}],"errorCount":0,"fatalErrorCount":0,"warningCount":7,"fixableErrorCount":0,"fixableWarningCount":0,"source":"'use client';\n\nimport {\n Dialog,\n DialogContent,\n DialogHeader,\n DialogTitle,\n DialogDescription,\n DialogFooter,\n} from '@/components/ui/dialog';\nimport { Button } from '@/components/ui/button';\nimport { Input } from '@/components/ui/input';\nimport { Label } from '@/components/ui/label';\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@/components/ui/select';\nimport { Upload, X, RefreshCw } from 'lucide-react';\nimport { useTranslations } from 'next-intl';\nimport type { Employee, Department, Role } from '../../types';\n\ninterface EmployeeFormDialogProps {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n editing: boolean;\n form: Partial;\n setForm: React.Dispatch>>;\n departments: Department[];\n roles: Role[];\n uploadingPhoto: boolean;\n fileInputRef: React.RefObject;\n onPhotoUpload: (event: React.ChangeEvent) => Promise;\n onRemovePhoto: () => void;\n onSave: () => Promise;\n}\n\nexport function EmployeeFormDialog({\n open,\n onOpenChange,\n editing,\n form,\n setForm,\n departments,\n roles,\n uploadingPhoto,\n fileInputRef,\n onPhotoUpload,\n onRemovePhoto,\n onSave,\n}: EmployeeFormDialogProps) {\n const t = useTranslations('Hr');\n const tc = useTranslations('Common');\n\n return (\n \n \n \n {editing ? t('editEmployee') : t('addEmployeeTitle')}\n \n {editing ? t('editEmployeeDesc') : t('addEmployeeDesc')}\n \n \n
\n {/* 照片上传区域 */}\n
\n \n
\n \n {form.photo ? (\n
\n {/* eslint-disable-next-line @next/next/no-img-element */}\n \n \n \n \n
\n ) : (\n fileInputRef.current?.click()}\n disabled={uploadingPhoto}\n className=\"w-full aspect-[3/4] border-2 border-dashed border-gray-300 dark:border-gray-600 rounded-lg flex flex-col items-center justify-center gap-2 hover:border-blue-400 dark:hover:border-blue-300 hover:bg-blue-50 dark:hover:bg-blue-950 transition-colors disabled:opacity-50\"\n >\n {uploadingPhoto ? (\n \n ) : (\n <>\n \n {tc('uploadPhoto')}\n {tc('supportJpgPng')}\n \n )}\n \n )}\n
\n
\n
\n \n setForm({ ...form, employee_no: e.target.value })}\n placeholder={tc('autoGenerate')}\n readOnly={!editing}\n />\n
\n
\n \n setForm({ ...form, name: e.target.value })}\n placeholder={tc('enterName')}\n />\n
\n
\n \n setForm({ ...form, gender: parseInt(v) })}\n >\n \n \n \n \n {tc('maleShort')}\n {tc('femaleShort')}\n \n \n
\n
\n \n setForm({ ...form, phone: e.target.value })}\n placeholder={tc('enterPhone')}\n />\n
\n
\n \n setForm({ ...form, email: e.target.value })}\n placeholder={tc('enterEmail')}\n />\n
\n
\n \n setForm({ ...form, dept_id: parseInt(v) })}\n >\n \n \n \n \n {departments.map((dept) => (\n \n {dept.dept_name}\n \n ))}\n \n \n
\n
\n \n setForm({ ...form, position: e.target.value })}\n placeholder={tc('enterPosition')}\n />\n
\n
\n \n setForm({ ...form, role_id: parseInt(v) })}\n >\n \n \n \n \n {roles.map((role) => (\n \n {role.role_name}\n \n ))}\n \n \n
\n
\n \n setForm({ ...form, entry_date: e.target.value })}\n />\n
\n
\n \n setForm({ ...form, status: parseInt(v) })}\n >\n \n \n \n \n {tc('statusActive')}\n {tc('statusProbation')}\n {tc('statusResigned')}\n {tc('statusInactive')}\n \n \n
\n
\n \n \n
\n
\n \n setForm({ ...form, section: e.target.value })}\n placeholder={tc('enterSection')}\n />\n
\n
\n \n \n
\n
\n \n {\n const idCard = e.target.value.replace(/[^0-9Xx]/g, '');\n let birthDate = '';\n let age = undefined;\n let gender = form.gender;\n if (idCard.length === 18) {\n const year = idCard.substring(6, 10);\n const month = idCard.substring(10, 12);\n const day = idCard.substring(12, 14);\n birthDate = `${year}-${month}-${day}`;\n const birthYear = parseInt(year);\n const currentYear = new Date().getFullYear();\n age = currentYear - birthYear;\n const genderDigit = parseInt(idCard.substring(16, 17));\n gender = genderDigit % 2 === 1 ? 1 : 2;\n } else if (idCard.length === 15) {\n const year = '19' + idCard.substring(6, 8);\n const month = idCard.substring(8, 10);\n const day = idCard.substring(10, 12);\n birthDate = `${year}-${month}-${day}`;\n const birthYear = parseInt(year);\n const currentYear = new Date().getFullYear();\n age = currentYear - birthYear;\n const genderDigit = parseInt(idCard.substring(14, 15));\n gender = genderDigit % 2 === 1 ? 1 : 2;\n }\n setForm({\n ...form,\n id_card: idCard,\n birth_date: birthDate || form.birth_date,\n age: age || form.age,\n gender: gender,\n });\n }}\n placeholder={tc('enterIdCard')}\n />\n
\n
\n \n setForm({ ...form, native_place: e.target.value })}\n placeholder={tc('enterNativePlace')}\n />\n
\n
\n \n setForm({ ...form, education: v })}\n >\n \n \n \n \n {tc('juniorHigh')}\n {tc('technical')}\n {tc('highSchool')}\n {tc('associate')}\n {tc('bachelor')}\n {tc('master')}\n {tc('doctor')}\n \n \n
\n
\n \n setForm({ ...form, home_address: e.target.value })}\n placeholder={tc('enterHomeAddress')}\n />\n
\n
\n \n setForm({ ...form, current_address: e.target.value })}\n placeholder={tc('enterCurrentAddress')}\n />\n
\n
\n \n \n \n \n
\n
\n );\n}\n","usedDeprecatedRules":[]},{"filePath":"D:\\dcprint\\erp-project\\src\\app\\[locale]\\hr\\employee\\components\\dialogs\\PrintDialog.tsx","messages":[{"ruleId":"@next/next/no-img-element","severity":1,"message":"Using `` could result in slower LCP and higher bandwidth. Consider using `` from `next/image` or a custom image loader to automatically optimize images. This may incur additional usage or cost from your provider. See: https://nextjs.org/docs/messages/no-img-element","line":55,"column":19,"nodeType":"JSXOpeningElement","endLine":59,"endColumn":21}],"suppressedMessages":[{"ruleId":"@next/next/no-img-element","severity":1,"message":"Using `` could result in slower LCP and higher bandwidth. Consider using `` from `next/image` or a custom image loader to automatically optimize images. This may incur additional usage or cost from your provider. See: https://nextjs.org/docs/messages/no-img-element","line":70,"column":25,"nodeType":"JSXOpeningElement","endLine":70,"endColumn":87,"suppressions":[{"kind":"directive","justification":""}]}],"errorCount":0,"fatalErrorCount":0,"warningCount":1,"fixableErrorCount":0,"fixableWarningCount":0,"source":"'use client';\n\nimport {\n Dialog,\n DialogContent,\n DialogHeader,\n DialogTitle,\n DialogDescription,\n DialogFooter,\n} from '@/components/ui/dialog';\nimport { Button } from '@/components/ui/button';\nimport { Printer } from 'lucide-react';\nimport { useTranslations } from 'next-intl';\nimport type { Employee } from '../../types';\n\ninterface PrintDialogProps {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n selectedEmployee: Employee | null;\n qrCodeUrl: string;\n companyName: string;\n printRef: React.RefObject;\n onPrint: () => void;\n}\n\nexport function PrintDialog({\n open,\n onOpenChange,\n selectedEmployee,\n qrCodeUrl,\n companyName,\n printRef,\n onPrint,\n}: PrintDialogProps) {\n const t = useTranslations('Hr');\n const tc = useTranslations('Common');\n\n return (\n <>\n \n \n \n {tc('cardPreview')}\n {tc('clickToPrint')}\n \n
\n {/* 上岗证卡片 - 打印内容 */}\n
\n
\n
{companyName}
\n
{tc('employeeCard')}
\n
\n
\n {selectedEmployee?.photo ? (\n \n ) : (\n {tc('photo')}\n )}\n
\n
\n
\n
\n {qrCodeUrl && (\n <>\n {/* eslint-disable-next-line @next/next/no-img-element */}\n {tc('qrCode')}\n \n )}\n
\n
\n
\n
\n
\n {tc('name')}\n {selectedEmployee?.name}\n
\n
\n {tc('gender')}\n \n {selectedEmployee?.gender === 1 ? t('maleShort') : t('femaleShort')}\n \n
\n
\n {tc('department')}\n {selectedEmployee?.dept_name}\n
\n
\n {tc('position')}\n {selectedEmployee?.position || '-'}\n
\n
\n
\n
\n
NO: {selectedEmployee?.employee_no}
\n
\n
\n \n \n \n \n
\n
\n\n {/* 打印样式 */}\n \n \n );\n}\n","usedDeprecatedRules":[]},{"filePath":"D:\\dcprint\\erp-project\\src\\app\\[locale]\\hr\\employee\\page.tsx","messages":[{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"开始计算员工统计数据\"。建议使用: tc('text_pldi1a')","line":97,"column":61,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":97,"endColumn":73},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"员工统计数据计算完成\"。建议使用: tc('text_1cb9o9')","line":113,"column":61,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":113,"endColumn":73},{"ruleId":"react-hooks/exhaustive-deps","severity":1,"message":"React Hook useCallback has a missing dependency: 't'. Either include it or remove the dependency array.","line":120,"column":6,"nodeType":"ArrayExpression","endLine":120,"endColumn":8,"suggestions":[{"desc":"Update the dependencies array to be: [t]","fix":{"range":[4044,4046],"text":"[t]"}}]},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"开始获取员工列表\"。建议使用: tc('text_5us70o')","line":158,"column":61,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":158,"endColumn":71},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"使用 mock 数据\"。建议使用: tc('text_us9wlf')","line":167,"column":65,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":167,"endColumn":77},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":175,"column":15,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":175,"endColumn":45},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":176,"column":20,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":176,"endColumn":27},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":177,"column":11,"nodeType":"AssignmentExpression","messageId":"anyAssignment","endLine":177,"endColumn":92},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":177,"column":47,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":177,"endColumn":51},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":177,"column":62,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":177,"endColumn":66},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":177,"column":76,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":177,"endColumn":80},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"员工列表获取成功\"。建议使用: tc('text_r2w30s')","line":185,"column":63,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":185,"endColumn":73},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"获取员工列表失败\"。建议使用: tc('text_shdpgv')","line":189,"column":64,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":189,"endColumn":74},{"ruleId":"react-hooks/exhaustive-deps","severity":1,"message":"React Hook useCallback has a missing dependency: 't'. Either include it or remove the dependency array.","line":196,"column":6,"nodeType":"ArrayExpression","endLine":196,"endColumn":39,"suggestions":[{"desc":"Update the dependencies array to be: [debouncedSearch, calculateStats, t]","fix":{"range":[6512,6545],"text":"[debouncedSearch, calculateStats, t]"}}]},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"开始获取部门列表\"。建议使用: tc('text_dpz86z')","line":200,"column":63,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":200,"endColumn":73},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"使用 mock 数据\"。建议使用: tc('text_us9wlf')","line":205,"column":67,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":205,"endColumn":79},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":209,"column":15,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":209,"endColumn":45},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":210,"column":20,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":210,"endColumn":27},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":211,"column":11,"nodeType":"AssignmentExpression","messageId":"anyAssignment","endLine":211,"endColumn":88},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":211,"column":43,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":211,"endColumn":47},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":211,"column":58,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":211,"endColumn":62},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":211,"column":72,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":211,"endColumn":76},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"部门列表获取成功\"。建议使用: tc('text_uee9vl')","line":216,"column":65,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":216,"endColumn":75},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"获取部门列表失败\"。建议使用: tc('text_1zeuh0')","line":220,"column":66,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":220,"endColumn":76},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"开始获取角色列表\"。建议使用: tc('text_cqybrv')","line":228,"column":57,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":228,"endColumn":67},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"使用 mock 数据\"。建议使用: tc('text_us9wlf')","line":233,"column":61,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":233,"endColumn":73},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":237,"column":15,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":237,"endColumn":45},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":238,"column":20,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":238,"endColumn":27},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":239,"column":11,"nodeType":"AssignmentExpression","messageId":"anyAssignment","endLine":239,"endColumn":88},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":239,"column":43,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":239,"endColumn":47},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":239,"column":58,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":239,"endColumn":62},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":239,"column":72,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":239,"endColumn":76},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"角色列表获取成功\"。建议使用: tc('text_17c6zj')","line":244,"column":59,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":244,"endColumn":69},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"获取角色列表失败\"。建议使用: tc('text_9l5v8c')","line":248,"column":60,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":248,"endColumn":70},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"开始上传员工照片\"。建议使用: tc('text_ga1sku')","line":288,"column":61,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":288,"endColumn":71},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":298,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":298,"endColumn":43},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":299,"column":18,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":299,"endColumn":25},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":300,"column":15,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":300,"endColumn":42},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":300,"column":33,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":300,"endColumn":37},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":302,"column":39,"nodeType":"Property","messageId":"anyAssignment","endLine":302,"endColumn":54},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"照片上传成功\"。建议使用: tc('text_3rgyqj')","line":303,"column":63,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":303,"endColumn":71},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":304,"column":11,"nodeType":"Property","messageId":"anyAssignment","endLine":304,"endColumn":24},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":309,"column":11,"nodeType":"Property","messageId":"anyAssignment","endLine":309,"endColumn":34},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .message on an `any` value.","line":309,"column":27,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":309,"endColumn":34},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `string | number | bigint | boolean | ReactElement> | Iterable | ... 4 more ... | undefined`.","line":311,"column":21,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":311,"endColumn":56},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .message on an `any` value.","line":311,"column":28,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":311,"endColumn":35},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"上传照片异常\"。建议使用: tc('text_j1p6r8')","line":314,"column":62,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":314,"endColumn":70},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"删除员工照片\"。建议使用: tc('text_u8cur3')","line":329,"column":64,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":329,"endColumn":72},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":361,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":361,"endColumn":43},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":362,"column":18,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":362,"endColumn":25},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":375,"column":13,"nodeType":"Property","messageId":"anyAssignment","endLine":375,"endColumn":36},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .message on an `any` value.","line":375,"column":29,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":375,"endColumn":36},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `string | number | bigint | boolean | ReactElement> | Iterable | ... 4 more ... | undefined`.","line":377,"column":21,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":377,"endColumn":50},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .message on an `any` value.","line":377,"column":28,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":377,"endColumn":35},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"保存员工异常\"。建议使用: tc('text_wzz4v2')","line":380,"column":62,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":380,"endColumn":70},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"请求删除员工\"。建议使用: tc('text_39iy6c')","line":390,"column":61,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":390,"endColumn":69},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"用户取消删除\"。建议使用: tc('text_y514n9')","line":395,"column":63,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":395,"endColumn":71},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":402,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":402,"endColumn":43},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":403,"column":18,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":403,"endColumn":25},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"员工删除成功\"。建议使用: tc('text_hr0j4w')","line":404,"column":65,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":404,"endColumn":73},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":413,"column":11,"nodeType":"Property","messageId":"anyAssignment","endLine":413,"endColumn":34},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .message on an `any` value.","line":413,"column":27,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":413,"endColumn":34},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `string | number | bigint | boolean | ReactElement> | Iterable | ... 4 more ... | undefined`.","line":415,"column":21,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":415,"endColumn":57},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .message on an `any` value.","line":415,"column":28,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":415,"endColumn":35},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"删除员工异常\"。建议使用: tc('text_u8g3cp')","line":418,"column":64,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":418,"endColumn":72},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"开始打印员工列表\"。建议使用: tc('text_5sywp2')","line":448,"column":62,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":448,"endColumn":72},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"无法打开打印窗口\"。建议使用: tc('text_4vacdn')","line":467,"column":65,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":467,"endColumn":75},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"打印列表窗口已打开\"。建议使用: tc('text_7rej8b')","line":517,"column":62,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":517,"endColumn":73},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"打开批量打印对话框\"。建议使用: tc('text_olqn5u')","line":525,"column":63,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":525,"endColumn":74},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"开始导出Excel\"。建议使用: tc('text_9di4pe')","line":543,"column":60,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":543,"endColumn":71},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"Excel导出成功\"。建议使用: tc('text_1k1sv0')","line":608,"column":60,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":608,"endColumn":71},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"开始导出PDF\"。建议使用: tc('text_93muzb')","line":621,"column":58,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":621,"endColumn":67},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"无法打开PDF弹出窗口\"。建议使用: tc('text_hpgsir')","line":634,"column":61,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":634,"endColumn":74},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"PDF导出成功\"。建议使用: tc('text_bwj1wf')","line":716,"column":58,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":716,"endColumn":67},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"开始批量打印员工上岗证\"。建议使用: tc('text_sy7j6x')","line":726,"column":66,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":726,"endColumn":79},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"无法打开批量打印窗口\"。建议使用: tc('text_y5edi7')","line":732,"column":69,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":732,"endColumn":81},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"批量打印窗口已打开\"。建议使用: tc('text_r7pvsw')","line":842,"column":66,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":842,"endColumn":77},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"开始生成员工二维码\"。建议使用: tc('text_bxm2nk')","line":850,"column":65,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":850,"endColumn":76},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"员工二维码生成成功\"。建议使用: tc('text_mmv2b8')","line":867,"column":67,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":867,"endColumn":78},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"生成二维码失败\"。建议使用: tc('text_xyx1dw')","line":872,"column":68,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":872,"endColumn":77},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"开始打印单张上岗证\"。建议使用: tc('text_y28f5t')","line":882,"column":58,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":882,"endColumn":69},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"无法打开打印窗口\"。建议使用: tc('text_4vacdn')","line":890,"column":61,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":890,"endColumn":71},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在函数调用中硬编码中文参数: \"单张上岗证打印窗口已打开\"。建议使用: tc('text_i1cgkz')","line":1073,"column":58,"nodeType":"Literal","messageId":"noChineseInFunction","endLine":1073,"endColumn":72},{"ruleId":"react-hooks/set-state-in-effect","severity":1,"message":"Error: Calling setState synchronously within an effect can trigger cascading renders\n\nEffects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following:\n* Update external systems with the latest state from React.\n* Subscribe for updates from some external system, calling setState in a callback function when external state changes.\n\nCalling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect).\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\hr\\employee\\page.tsx:1078:5\n 1076 | // 初始化加载\n 1077 | useEffect(() => {\n> 1078 | fetchEmployees();\n | ^^^^^^^^^^^^^^ Avoid calling setState() directly within an effect\n 1079 | fetchDepartments();\n 1080 | fetchRoles();\n 1081 | }, [fetchEmployees, fetchDepartments, fetchRoles]);","line":1078,"column":5,"nodeType":null,"endLine":1078,"endColumn":19},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"员工列表\"。建议使用: {tc('text_b14u4e')}","line":1145,"column":26,"nodeType":"Literal","messageId":"noChineseInJSX","endLine":1145,"endColumn":32},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Computed name [v] resolves to an `any` value.","line":1176,"column":32,"nodeType":"Identifier","messageId":"unsafeComputedMemberAccess","endLine":1176,"endColumn":33}],"suppressedMessages":[{"ruleId":"@next/next/no-img-element","severity":1,"message":"Using `` could result in slower LCP and higher bandwidth. Consider using `` from `next/image` or a custom image loader to automatically optimize images. This may incur additional usage or cost from your provider. See: https://nextjs.org/docs/messages/no-img-element","line":1512,"column":29,"nodeType":"JSXOpeningElement","endLine":1516,"endColumn":31,"suppressions":[{"kind":"directive","justification":""}]}],"errorCount":0,"fatalErrorCount":0,"warningCount":86,"fixableErrorCount":0,"fixableWarningCount":0,"source":"'use client';\n\nimport { useState, useEffect, useCallback, useRef } from 'react';\nimport { MainLayout } from '@/components/layout';\nimport { Card, CardContent, CardHeader, CardTitle, CardDescription } from '@/components/ui/card';\nimport { Button } from '@/components/ui/button';\nimport { Input } from '@/components/ui/input';\nimport { Badge } from '@/components/ui/badge';\nimport {\n Table,\n TableBody,\n TableCell,\n TableHead,\n TableHeader,\n TableRow,\n} from '@/components/ui/table';\nimport {\n Search,\n Plus,\n Edit,\n Trash2,\n UserCircle,\n RefreshCw,\n Printer,\n FileSpreadsheet,\n FileText,\n Users,\n GraduationCap,\n Calendar,\n ArrowUp,\n ArrowDown,\n} from 'lucide-react';\nimport { Checkbox } from '@/components/ui/checkbox';\nimport { toast } from 'sonner';\nimport QRCode from 'qrcode';\nimport { usePermission } from '@/hooks/usePermission';\nimport { useDebounce } from '@/hooks/use-debounce';\nimport { useCompanyName } from '@/hooks/useCompanyName';\nimport { useTranslations } from 'next-intl';\nimport { logger } from '@/lib/logger';\nimport { authFetch } from '@/lib/auth-fetch';\nimport {\n mockEmployees,\n mockDepartments,\n mockRoles,\n USE_MOCK_HR_DATA,\n mockApiListResponse,\n} from '@/lib/mock-hr-data';\nimport type { Employee, Department, Role } from './types';\nimport { EmployeeFormDialog } from './components/dialogs/EmployeeFormDialog';\nimport { PrintDialog } from './components/dialogs/PrintDialog';\nimport { BatchPrintDialog } from './components/dialogs/BatchPrintDialog';\nimport { GlobalExportToolbar } from '@/components/ui/global-export-toolbar';\n\nexport default function EmployeePage() {\n // 翻译钩子\n const t = useTranslations('Hr');\n const tc = useTranslations('Common');\n\n const { companyName } = useCompanyName();\n const { hasPermission: _hasPermission } = usePermission();\n const [employees, setEmployees] = useState([]);\n const [departments, setDepartments] = useState([]);\n const [roles, setRoles] = useState([]);\n const [loading, setLoading] = useState(false);\n const [dialogOpen, setDialogOpen] = useState(false);\n const [form, setForm] = useState>({});\n const [editing, setEditing] = useState(false);\n const [search, setSearch] = useState('');\n const debouncedSearch = useDebounce(search, 300);\n const [printDialogOpen, setPrintDialogOpen] = useState(false);\n const [selectedEmployee, setSelectedEmployee] = useState(null);\n const [qrCodeUrl, setQrCodeUrl] = useState('');\n const printRef = useRef(null);\n const [uploadingPhoto, setUploadingPhoto] = useState(false);\n const fileInputRef = useRef(null);\n const [selectedEmployees, setSelectedEmployees] = useState([]);\n const [batchPrintDialogOpen, setBatchPrintDialogOpen] = useState(false);\n\n // 统计数据\n const [stats, setStats] = useState({\n total: 0,\n male: 0,\n female: 0,\n avgAge: 0,\n education: {} as Record,\n });\n\n // 排序状态\n const [sortConfig, setSortConfig] = useState<{\n key: keyof Employee | null;\n direction: 'asc' | 'desc';\n }>({ key: null, direction: 'asc' });\n\n // 计算统计数据\n const calculateStats = useCallback((data: Employee[]) => {\n logger.info({ module: 'Hr', action: 'calculateStats' }, '开始计算员工统计数据', {\n employeeCount: data.length,\n });\n const total = data.length;\n const male = data.filter((e) => e.gender === 1).length;\n const female = data.filter((e) => e.gender === 2).length;\n const ages = data.filter((e) => e.age).map((e) => e.age!);\n const avgAge = ages.length > 0 ? Math.round(ages.reduce((a, b) => a + b, 0) / ages.length) : 0;\n\n const education: Record = {};\n data.forEach((e) => {\n const edu = e.education || t('notFilled');\n education[edu] = (education[edu] || 0) + 1;\n });\n\n setStats({ total, male, female, avgAge, education });\n logger.info({ module: 'Hr', action: 'calculateStats' }, '员工统计数据计算完成', {\n total,\n male,\n female,\n avgAge,\n education,\n });\n }, []);\n\n // 排序函数\n const handleSort = (key: keyof Employee) => {\n let direction: 'asc' | 'desc' = 'asc';\n if (sortConfig.key === key && sortConfig.direction === 'asc') {\n direction = 'desc';\n }\n setSortConfig({ key, direction });\n };\n\n // 获取排序后的员工列表\n const sortedEmployees = useCallback(() => {\n if (!sortConfig.key) return employees;\n\n return [...employees].sort((a, b) => {\n const aValue = a[sortConfig.key!];\n const bValue = b[sortConfig.key!];\n\n if (aValue === null || aValue === undefined) return 1;\n if (bValue === null || bValue === undefined) return -1;\n\n if (typeof aValue === 'string' && typeof bValue === 'string') {\n return sortConfig.direction === 'asc'\n ? aValue.localeCompare(bValue, 'zh-CN')\n : bValue.localeCompare(aValue, 'zh-CN');\n }\n\n if (typeof aValue === 'number' && typeof bValue === 'number') {\n return sortConfig.direction === 'asc' ? aValue - bValue : bValue - aValue;\n }\n\n return 0;\n });\n }, [employees, sortConfig]);\n\n // 获取员工列表\n const fetchEmployees = useCallback(async () => {\n logger.info({ module: 'Hr', action: 'fetchEmployees' }, '开始获取员工列表', {\n keyword: debouncedSearch || '(全部)',\n });\n setLoading(true);\n try {\n let employeeList: Employee[];\n\n if (USE_MOCK_HR_DATA) {\n // 使用模拟数据\n logger.info({ module: 'Hr', action: 'fetchEmployees' }, '使用 mock 数据');\n const _mockResponse = mockApiListResponse(mockEmployees);\n employeeList = mockEmployees;\n } else {\n const url = debouncedSearch\n ? `/api/organization/employee?keyword=${encodeURIComponent(debouncedSearch)}`\n : '/api/organization/employee';\n const response = await authFetch(url);\n const result = await response.json();\n if (result.success) {\n employeeList = Array.isArray(result.data) ? result.data : result.data?.list || [];\n } else {\n throw new Error('API returned unsuccessful');\n }\n }\n\n setEmployees(employeeList);\n calculateStats(employeeList);\n logger.info({ module: 'Hr', action: 'fetchEmployees' }, '员工列表获取成功', {\n count: employeeList.length,\n });\n } catch (error) {\n logger.error({ module: 'Hr', action: 'fetchEmployees' }, '获取员工列表失败', {\n error: (error as Error).message,\n });\n toast.error(t('fetchEmployeesFailed'));\n } finally {\n setLoading(false);\n }\n }, [debouncedSearch, calculateStats]);\n\n // 获取部门列表\n const fetchDepartments = useCallback(async () => {\n logger.info({ module: 'Hr', action: 'fetchDepartments' }, '开始获取部门列表');\n try {\n let deptList: Department[] = [];\n\n if (USE_MOCK_HR_DATA) {\n logger.info({ module: 'Hr', action: 'fetchDepartments' }, '使用 mock 数据');\n deptList = mockDepartments as unknown as Department[];\n } else {\n const response = await fetch('/api/organization/department');\n const result = await response.json();\n if (result.success) {\n deptList = Array.isArray(result.data) ? result.data : result.data?.list || [];\n }\n }\n\n setDepartments(deptList);\n logger.info({ module: 'Hr', action: 'fetchDepartments' }, '部门列表获取成功', {\n count: deptList.length,\n });\n } catch (error) {\n logger.error({ module: 'Hr', action: 'fetchDepartments' }, '获取部门列表失败', {\n error: (error as Error).message,\n });\n }\n }, []);\n\n // 获取角色列表\n const fetchRoles = useCallback(async () => {\n logger.info({ module: 'Hr', action: 'fetchRoles' }, '开始获取角色列表');\n try {\n let roleList: Role[] = [];\n\n if (USE_MOCK_HR_DATA) {\n logger.info({ module: 'Hr', action: 'fetchRoles' }, '使用 mock 数据');\n roleList = mockRoles as unknown as Role[];\n } else {\n const response = await authFetch('/api/organization/role');\n const result = await response.json();\n if (result.success) {\n roleList = Array.isArray(result.data) ? result.data : result.data?.list || [];\n }\n }\n\n setRoles(roleList);\n logger.info({ module: 'Hr', action: 'fetchRoles' }, '角色列表获取成功', {\n count: roleList.length,\n });\n } catch (error) {\n logger.error({ module: 'Hr', action: 'fetchRoles' }, '获取角色列表失败', {\n error: (error as Error).message,\n });\n }\n }, []);\n\n // 生成员工编号\n const generateEmployeeNo = () => {\n const now = new Date();\n const year = now.getFullYear();\n const random = Math.floor(1000 + Math.random() * 9000);\n return `E${year}${random}`;\n };\n\n // 处理照片上传\n const handlePhotoUpload = async (event: React.ChangeEvent) => {\n const file = event.target.files?.[0];\n if (!file) return;\n\n // 验证文件类型\n if (!file.type.startsWith('image/')) {\n toast.error(t('uploadImageOnly'));\n return;\n }\n\n // 验证文件大小 (最大 2MB)\n if (file.size > 2 * 1024 * 1024) {\n logger.warn({ module: 'Hr', action: 'handleUpload' }, '图片大小超出限制', {\n fileName: file.name,\n fileSize: file.size,\n });\n toast.error(t('imageSizeLimit'));\n return;\n }\n\n setUploadingPhoto(true);\n try {\n const uploadFormData = new FormData();\n uploadFormData.append('file', file);\n\n logger.info({ module: 'Hr', action: 'handleUpload' }, '开始上传员工照片', {\n fileName: file.name,\n fileSize: file.size,\n });\n\n const response = await authFetch('/api/upload', {\n method: 'POST',\n body: uploadFormData,\n });\n\n const result = await response.json();\n if (result.success) {\n const photoUrl = result.data?.url;\n // 使用函数式更新避免闭包陈旧问题:用最新 form state 而非闭包中的 form\n setForm((prev) => ({ ...prev, photo: photoUrl }));\n logger.info({ module: 'Hr', action: 'handleUpload' }, '照片上传成功', {\n url: photoUrl,\n });\n toast.success(t('uploadSuccess'));\n } else {\n logger.warn({ module: 'Hr', action: 'handleUpload' }, '照片上传失败', {\n message: result.message,\n });\n toast.error(result.message || t('uploadFailed'));\n }\n } catch (error) {\n logger.error({ module: 'Hr', action: 'handleUpload' }, '上传照片异常', {\n error: (error as Error).message,\n });\n toast.error(t('uploadPhotoFailed'));\n } finally {\n setUploadingPhoto(false);\n // 清空 input 以便可以重复选择同一文件\n if (fileInputRef.current) {\n fileInputRef.current.value = '';\n }\n }\n };\n\n // 删除照片\n const handleRemovePhoto = () => {\n logger.info({ module: 'Hr', action: 'handleRemovePhoto' }, '删除员工照片', {\n employeeName: form.name,\n });\n setForm((prev) => ({ ...prev, photo: undefined }));\n };\n\n // 保存员工\n const saveEmployee = async () => {\n logger.info({ module: 'Hr', action: 'saveEmployee' }, `开始${editing ? '编辑' : '新增'}员工`, {\n employeeName: form.name,\n employeeNo: form.employee_no,\n editing,\n });\n try {\n // 验证必填字段\n if (!form.name) {\n logger.warn({ module: 'Hr', action: 'saveEmployee' }, '员工姓名未填写');\n toast.error(t('enterEmployeeName'));\n return;\n }\n\n // 确保 employee_no 有值\n const submitData = {\n ...form,\n employee_no: form.employee_no || generateEmployeeNo(),\n };\n\n const method = editing ? 'PUT' : 'POST';\n const response = await authFetch('/api/organization/employee', {\n method,\n body: JSON.stringify(submitData),\n });\n const result = await response.json();\n if (result.success) {\n logger.info(\n { module: 'Hr', action: 'saveEmployee' },\n `员工${editing ? '更新' : '创建'}成功`,\n { employeeName: submitData.name, employeeNo: submitData.employee_no }\n );\n toast.success(editing ? t('updateSuccess') : t('createSuccess'));\n setDialogOpen(false);\n fetchEmployees();\n } else {\n logger.warn(\n { module: 'Hr', action: 'saveEmployee' },\n `员工${editing ? '更新' : '创建'}失败`,\n { message: result.message }\n );\n toast.error(result.message || tc('error'));\n }\n } catch (error) {\n logger.error({ module: 'Hr', action: 'saveEmployee' }, '保存员工异常', {\n error: (error as Error).message,\n });\n toast.error(t('saveFailed'));\n }\n };\n\n // 删除员工\n const deleteEmployee = async (id: number) => {\n const employee = employees.find((e) => e.id === id);\n logger.info({ module: 'Hr', action: 'deleteEmployee' }, '请求删除员工', {\n employeeId: id,\n employeeName: employee?.name,\n });\n if (!confirm(t('deleteConfirm'))) {\n logger.info({ module: 'Hr', action: 'deleteEmployee' }, '用户取消删除', { employeeId: id });\n return;\n }\n try {\n const response = await authFetch(`/api/organization/employee?id=${id}`, {\n method: 'DELETE',\n });\n const result = await response.json();\n if (result.success) {\n logger.info({ module: 'Hr', action: 'deleteEmployee' }, '员工删除成功', {\n employeeId: id,\n employeeName: employee?.name,\n });\n toast.success(t('deleteSuccess'));\n fetchEmployees();\n } else {\n logger.warn({ module: 'Hr', action: 'deleteEmployee' }, '员工删除失败', {\n employeeId: id,\n message: result.message,\n });\n toast.error(result.message || tc('deleteFailed'));\n }\n } catch (error) {\n logger.error({ module: 'Hr', action: 'deleteEmployee' }, '删除员工异常', {\n employeeId: id,\n error: (error as Error).message,\n });\n toast.error(t('deleteFailed'));\n }\n };\n\n // 选择/取消选择员工\n const toggleSelectEmployee = (id: number) => {\n setSelectedEmployees((prev) =>\n prev.includes(id) ? prev.filter((empId) => empId !== id) : [...prev, id]\n );\n };\n\n // 全选/取消全选\n const toggleSelectAll = () => {\n if (selectedEmployees.length === employees.length) {\n setSelectedEmployees([]);\n } else {\n setSelectedEmployees(employees.map((emp) => emp.id));\n }\n };\n\n const handlePrintList = () => {\n const dataToPrint =\n selectedEmployees.length > 0\n ? employees.filter((emp) => selectedEmployees.includes(emp.id))\n : employees;\n\n logger.info({ module: 'Hr', action: 'handlePrintList' }, '开始打印员工列表', {\n totalCount: dataToPrint.length,\n selectedCount: selectedEmployees.length,\n });\n\n if (dataToPrint.length === 0) {\n logger.warn({ module: 'Hr', action: 'handlePrintList' }, '没有数据可打印');\n toast.error(t('noDataToPrint'));\n return;\n }\n\n const statusLabels: Record = {\n 1: t('statusActive'),\n 0: t('statusInactive'),\n 2: t('statusProbation'),\n 3: t('statusResigned'),\n };\n const printWindow = window.open('', '_blank');\n if (!printWindow) {\n logger.error({ module: 'Hr', action: 'handlePrintList' }, '无法打开打印窗口');\n toast.error(t('cannotOpenPrintWindow'));\n return;\n }\n\n const rows = dataToPrint\n .map(\n (emp, index) => `\n \n ${index + 1}\n ${emp.employee_no}\n ${emp.name}\n ${emp.gender === 1 ? t('maleShort') : t('femaleShort')}\n ${emp.age || '-'}\n ${emp.dept_name}\n ${emp.section || '-'}\n ${emp.position}\n ${emp.entry_date}\n ${emp.education || '-'}\n ${emp.native_place || '-'}\n ${emp.phone}\n ${statusLabels[emp.status] || tc('unknown')}\n `\n )\n .join('');\n\n const html = `{tc(\"employeeList\")}\n \n \n

{tc(\"employeeList\")}

\n
{tc(\"printTime\")}: ${new Date().toLocaleString()} | {tc(\"totalEmployees\", { count: dataToPrint.length })}
\n \n \n ${rows}\n
{tc(\"serialNo\")}{tc(\"employeeNo\")}{tc(\"name\")}{tc(\"gender\")}{tc(\"age\")}{tc(\"department\")}{tc(\"section\")}{tc(\"position\")}{tc(\"entryDate\")}{tc(\"education\")}{tc(\"nativePlace\")}{tc(\"contact\")}{tc(\"status\")}
\n
${companyName}
\n \n `;\n printWindow.document.write(html);\n printWindow.document.close();\n logger.info({ module: 'Hr', action: 'handlePrintList' }, '打印列表窗口已打开', {\n count: dataToPrint.length,\n });\n toast.success(t('printingRecords', { count: dataToPrint.length }));\n };\n\n // 批量打印\n const handleBatchPrint = () => {\n logger.info({ module: 'Hr', action: 'handleBatchPrint' }, '打开批量打印对话框', {\n selectedCount: selectedEmployees.length,\n });\n if (selectedEmployees.length === 0) {\n logger.warn({ module: 'Hr', action: 'handleBatchPrint' }, '未选择员工');\n toast.error(t('selectEmployeesFirst'));\n return;\n }\n setBatchPrintDialogOpen(true);\n };\n\n // 导出Excel\n const exportToExcel = () => {\n const dataToExport =\n selectedEmployees.length > 0\n ? employees.filter((emp) => selectedEmployees.includes(emp.id))\n : employees;\n\n logger.info({ module: 'Hr', action: 'exportToExcel' }, '开始导出Excel', {\n totalCount: dataToExport.length,\n selectedCount: selectedEmployees.length,\n });\n\n if (dataToExport.length === 0) {\n logger.warn({ module: 'Hr', action: 'exportToExcel' }, '没有数据可导出');\n toast.error(t('noDataToExport'));\n return;\n }\n\n // 创建CSV内容\n const headers = [\n t('employeeNo'),\n t('name'),\n t('gender'),\n t('age'),\n t('department'),\n t('section'),\n t('position'),\n t('education'),\n t('entryDate'),\n t('nativePlace'),\n t('contact'),\n tc('status'),\n ];\n const rows = dataToExport.map((emp) => [\n emp.employee_no,\n emp.name,\n emp.gender === 1 ? tc('maleShort') : tc('femaleShort'),\n emp.age || '',\n emp.dept_name,\n emp.section || '',\n emp.position,\n emp.education || '',\n emp.entry_date,\n emp.native_place || '',\n emp.phone,\n emp.status === 1\n ? t('statusActive')\n : emp.status === 2\n ? t('statusProbation')\n : emp.status === 3\n ? t('statusResigned')\n : t('statusInactive'),\n ]);\n\n const csvContent = [\n headers.join(','),\n ...rows.map((row) => row.map((cell) => `\"${cell}\"`).join(',')),\n ].join('\\n');\n\n // 添加BOM以支持中文\n const BOM = '\\uFEFF';\n const blob = new Blob([BOM + csvContent], { type: 'text/csv;charset=utf-8;' });\n const link = document.createElement('a');\n const url = URL.createObjectURL(blob);\n link.setAttribute('href', url);\n link.setAttribute(\n 'download',\n `${tc('employeeList')}_${new Date().toISOString().split('T')[0]}.csv`\n );\n document.body.appendChild(link);\n link.click();\n document.body.removeChild(link);\n logger.info({ module: 'Hr', action: 'exportToExcel' }, 'Excel导出成功', {\n count: dataToExport.length,\n });\n toast.success(t('exportSuccess', { count: dataToExport.length }));\n };\n\n // 导出PDF\n const exportToPDF = () => {\n const dataToExport =\n selectedEmployees.length > 0\n ? employees.filter((emp) => selectedEmployees.includes(emp.id))\n : employees;\n\n logger.info({ module: 'Hr', action: 'exportToPDF' }, '开始导出PDF', {\n totalCount: dataToExport.length,\n selectedCount: selectedEmployees.length,\n });\n\n if (dataToExport.length === 0) {\n logger.warn({ module: 'Hr', action: 'exportToPDF' }, '没有数据可导出');\n toast.error(t('noDataToExport'));\n return;\n }\n\n const printWindow = window.open('', '_blank');\n if (!printWindow) {\n logger.error({ module: 'Hr', action: 'exportToPDF' }, '无法打开PDF弹出窗口');\n toast.error(t('allowPopupPdf'));\n return;\n }\n\n const html = `\n \n \n \n \n {tc(\"employeeList\")}\n \n \n \n
\n

{tc(\"employeeList\")} - ${new Date().toLocaleDateString()}

\n

{tc(\"totalCount\")}: ${dataToExport.length}{tc(\"personUnit\")}

\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n ${dataToExport\n .map(\n (emp, index) => `\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n `\n )\n .join('')}\n \n
{tc(\"serialNo\")}{tc(\"employeeNo\")}{tc(\"name\")}{tc(\"gender\")}{tc(\"age\")}{tc(\"department\")}{tc(\"section\")}{tc(\"position\")}{tc(\"education\")}{tc(\"entryDate\")}{tc(\"contact\")}{tc(\"status\")}
${index + 1}${emp.employee_no}${emp.name}${emp.gender === 1 ? t('maleShort') : t('femaleShort')}${emp.age || '-'}${emp.dept_name}${emp.section || '-'}${emp.position}${emp.education || '-'}${emp.entry_date}${emp.phone}${emp.status === 1 ? t('statusActive') : emp.status === 2 ? t('statusProbation') : emp.status === 3 ? t('statusResigned') : t('statusInactive')}
\n \n \n \n `;\n\n printWindow.document.write(html);\n printWindow.document.close();\n logger.info({ module: 'Hr', action: 'exportToPDF' }, 'PDF导出成功', {\n count: dataToExport.length,\n });\n toast.success(t('preparingPdf', { count: dataToExport.length }));\n };\n\n // 批量打印全部\n const handleBatchPrintAll = () => {\n const selectedEmps = employees.filter((emp) => selectedEmployees.includes(emp.id));\n\n logger.info({ module: 'Hr', action: 'handleBatchPrintAll' }, '开始批量打印员工上岗证', {\n count: selectedEmps.length,\n });\n\n const printWindow = window.open('', '_blank');\n if (!printWindow) {\n logger.error({ module: 'Hr', action: 'handleBatchPrintAll' }, '无法打开批量打印窗口');\n toast.error(t('allowPopup'));\n return;\n }\n\n const cardsHtml = selectedEmps\n .map(\n (emp, index) => `\n
0 ? 'page-break-before: always;' : ''}\">\n
\n
${companyName}
\n
{tc(\"employeeCard\")}
\n
\n
\n ${emp.photo ? `\"${emp.name}\"` : `${tc('photo')}`}\n
\n
\n
\n
\n \"${tc('qrCode')}\"\n
\n
\n
\n
\n
\n ${tc('name')}\n ${emp.name}\n
\n
\n ${tc('gender')}\n ${emp.gender === 1 ? t('maleShort') : t('femaleShort')}\n
\n
\n ${tc('department')}\n ${emp.dept_name}\n
\n
\n ${tc('position')}\n ${emp.position || '-'}\n
\n
\n
\n
\n
NO: ${emp.employee_no}
\n
\n `\n )\n .join('');\n\n printWindow.document.write(`\n \n \n \n {tc(\"batchPrintTitle\")}\n \n \n \n ${cardsHtml}\n \n \n `);\n printWindow.document.close();\n\n setTimeout(() => {\n printWindow.print();\n }, 500);\n\n logger.info({ module: 'Hr', action: 'handleBatchPrintAll' }, '批量打印窗口已打开', {\n count: selectedEmps.length,\n });\n toast.success(t('printWindowOpened'));\n };\n\n // 生成员工查询二维码\n const generateEmployeeQR = async (employee: Employee) => {\n logger.info({ module: 'Hr', action: 'generateEmployeeQR' }, '开始生成员工二维码', {\n employeeId: employee.id,\n employeeName: employee.name,\n });\n try {\n const queryUrl = `${window.location.origin}/hr/employee/query?id=${employee.id}`;\n const url = await QRCode.toDataURL(queryUrl, {\n width: 200,\n margin: 2,\n color: {\n dark: '#000000',\n light: '#ffffff',\n },\n });\n setQrCodeUrl(url);\n setSelectedEmployee(employee);\n setPrintDialogOpen(true);\n logger.info({ module: 'Hr', action: 'generateEmployeeQR' }, '员工二维码生成成功', {\n employeeId: employee.id,\n queryUrl,\n });\n } catch (error) {\n logger.error({ module: 'Hr', action: 'generateEmployeeQR' }, '生成二维码失败', {\n employeeId: employee.id,\n error: (error as Error).message,\n });\n toast.error(t('generateQRFailed'));\n }\n };\n\n // 打印上岗证\n const handlePrint = () => {\n logger.info({ module: 'Hr', action: 'handlePrint' }, '开始打印单张上岗证');\n if (!printRef.current) {\n logger.warn({ module: 'Hr', action: 'handlePrint' }, '打印引用不存在');\n return;\n }\n\n const printWindow = window.open('', '_blank');\n if (!printWindow) {\n logger.error({ module: 'Hr', action: 'handlePrint' }, '无法打开打印窗口');\n toast.error(t('allowPopup'));\n return;\n }\n\n const printContent = printRef.current.innerHTML;\n printWindow.document.write(`\n \n \n \n {tc(\"employeeCard\")}\n \n \n \n ${printContent}\n \n \n \n `);\n printWindow.document.close();\n logger.info({ module: 'Hr', action: 'handlePrint' }, '单张上岗证打印窗口已打开');\n };\n\n // 初始化加载\n useEffect(() => {\n fetchEmployees();\n fetchDepartments();\n fetchRoles();\n }, [fetchEmployees, fetchDepartments, fetchRoles]);\n\n // 状态标签\n const getStatusBadge = (status: number) => {\n const styles: Record = {\n 1: 'bg-green-100 text-green-800',\n 0: 'bg-muted text-muted-foreground',\n 2: 'bg-yellow-100 text-yellow-800',\n 3: 'bg-red-100 text-red-800',\n };\n const labels: Record = {\n 1: t('statusActive'),\n 0: t('statusInactive'),\n 2: t('statusProbation'),\n 3: t('statusResigned'),\n };\n return {labels[status] || tc('unknown')};\n };\n\n return (\n \n
\n \n \n
\n \n \n {tc('employeeProfile')}\n \n {tc('manageEmployeeInfo')}\n
\n {\n setForm({ employee_no: generateEmployeeNo() });\n setEditing(false);\n setDialogOpen(true);\n }}\n className=\"bg-blue-600 hover:bg-blue-700\"\n >\n \n {tc('addEmployee')}\n \n
\n \n
\n
\n \n setSearch(e.target.value)}\n onKeyDown={(e) => e.key === 'Enter' && fetchEmployees()}\n className=\"pl-10\"\n />\n
\n \n \n (v === 1 ? tc('maleShort') : tc('femaleShort')),\n },\n { key: 'age', label: tc('age'), width: 6 },\n { key: 'dept_name', label: tc('department'), width: 15 },\n { key: 'section', label: tc('section'), width: 12 },\n { key: 'position', label: tc('position'), width: 12 },\n { key: 'entry_date', label: tc('entryDate'), width: 12 },\n { key: 'education', label: tc('education'), width: 10 },\n { key: 'native_place', label: tc('nativePlace'), width: 12 },\n { key: 'phone', label: tc('contact'), width: 15 },\n {\n key: 'status',\n label: tc('status'),\n width: 10,\n formatter: (v) => {\n const m: Record = {\n 1: t('statusActive'),\n 0: t('statusInactive'),\n 2: t('statusProbation'),\n 3: t('statusResigned'),\n };\n return m[v] || tc('unknown');\n },\n },\n ]}\n data={\n selectedEmployees.length > 0\n ? employees.filter((emp) => selectedEmployees.includes(emp.id))\n : sortedEmployees()\n }\n />\n
\n\n {/* 统计面板 */}\n
\n \n \n
\n
\n

{tc('totalCount')}

\n

{stats.total}

\n
\n \n
\n
\n
\n \n \n
\n
\n

{tc('male')}

\n

{stats.male}

\n

\n {stats.total > 0 ? ((stats.male / stats.total) * 100).toFixed(1) : 0}%\n

\n
\n
\n {tc('maleShort')}\n
\n
\n
\n
\n \n \n
\n
\n

{tc('female')}

\n

{stats.female}

\n

\n {stats.total > 0 ? ((stats.female / stats.total) * 100).toFixed(1) : 0}%\n

\n
\n
\n {tc('femaleShort')}\n
\n
\n
\n
\n \n \n
\n
\n

{tc('avgAge')}

\n

{stats.avgAge}

\n

{tc('ageUnit')}

\n
\n \n
\n
\n
\n
\n\n {/* 学历分布 */}\n \n \n \n \n {tc('educationDistribution')}\n \n \n \n
\n {Object.entries(stats.education).map(([edu, count]) => (\n \n {edu}: {count}\n {tc('personUnit')}\n \n ))}\n
\n
\n
\n\n {loading ? (\n
\n \n
\n ) : (\n <>\n {selectedEmployees.length > 0 && (\n
\n \n {tc('selectedCount', { count: selectedEmployees.length })}\n \n \n \n \n setSelectedEmployees([])}\n className=\"ml-auto text-muted-foreground\"\n >\n {tc('clearSelection')}\n \n
\n )}\n \n \n \n \n 0\n }\n onCheckedChange={toggleSelectAll}\n />\n \n handleSort('id')}\n >\n
\n {tc('serialNo')}\n {sortConfig.key === 'id' &&\n (sortConfig.direction === 'asc' ? (\n \n ) : (\n \n ))}\n
\n \n {tc('photo')}\n handleSort('employee_no')}\n >\n
\n {tc('employeeNo')}\n {sortConfig.key === 'employee_no' &&\n (sortConfig.direction === 'asc' ? (\n \n ) : (\n \n ))}\n
\n \n handleSort('name')}\n >\n
\n {tc('name')}\n {sortConfig.key === 'name' &&\n (sortConfig.direction === 'asc' ? (\n \n ) : (\n \n ))}\n
\n \n handleSort('gender')}\n >\n
\n {tc('gender')}\n {sortConfig.key === 'gender' &&\n (sortConfig.direction === 'asc' ? (\n \n ) : (\n \n ))}\n
\n \n handleSort('age')}\n >\n
\n {tc('age')}\n {sortConfig.key === 'age' &&\n (sortConfig.direction === 'asc' ? (\n \n ) : (\n \n ))}\n
\n \n handleSort('dept_name')}\n >\n
\n {tc('department')}\n {sortConfig.key === 'dept_name' &&\n (sortConfig.direction === 'asc' ? (\n \n ) : (\n \n ))}\n
\n \n handleSort('section')}\n >\n
\n {tc('section')}\n {sortConfig.key === 'section' &&\n (sortConfig.direction === 'asc' ? (\n \n ) : (\n \n ))}\n
\n \n handleSort('position')}\n >\n
\n {tc('position')}\n {sortConfig.key === 'position' &&\n (sortConfig.direction === 'asc' ? (\n \n ) : (\n \n ))}\n
\n \n handleSort('entry_date')}\n >\n
\n {tc('entryDate')}\n {sortConfig.key === 'entry_date' &&\n (sortConfig.direction === 'asc' ? (\n \n ) : (\n \n ))}\n
\n \n handleSort('education')}\n >\n
\n {tc('education')}\n {sortConfig.key === 'education' &&\n (sortConfig.direction === 'asc' ? (\n \n ) : (\n \n ))}\n
\n \n handleSort('native_place')}\n >\n
\n {tc('nativePlace')}\n {sortConfig.key === 'native_place' &&\n (sortConfig.direction === 'asc' ? (\n \n ) : (\n \n ))}\n
\n \n handleSort('phone')}\n >\n
\n {tc('contact')}\n {sortConfig.key === 'phone' &&\n (sortConfig.direction === 'asc' ? (\n \n ) : (\n \n ))}\n
\n \n handleSort('status')}\n >\n
\n {tc('status')}\n {sortConfig.key === 'status' &&\n (sortConfig.direction === 'asc' ? (\n \n ) : (\n \n ))}\n
\n \n {tc('operation')}\n
\n
\n \n {sortedEmployees().map((emp, index) => (\n \n \n toggleSelectEmployee(emp.id)}\n />\n \n \n {index + 1}\n \n \n {emp.photo ? (\n // eslint-disable-next-line @next/next/no-img-element\n \n ) : (\n
\n \n
\n )}\n
\n {emp.employee_no}\n {emp.name}\n \n {emp.gender === 1 ? t('maleShort') : t('femaleShort')}\n \n {emp.age || '-'}\n {emp.dept_name}\n {emp.section || '-'}\n {emp.position}\n {emp.entry_date}\n {emp.education || '-'}\n {emp.native_place || '-'}\n {emp.phone}\n {getStatusBadge(emp.status)}\n \n
\n generateEmployeeQR(emp)}\n title={tc('printCard')}\n >\n \n \n {\n setForm(emp);\n setEditing(true);\n setDialogOpen(true);\n }}\n >\n \n \n deleteEmployee(emp.id)}\n >\n \n \n
\n
\n
\n ))}\n
\n
\n \n )}\n
\n
\n
\n\n {/* 员工表单对话框 */}\n \n\n {/* 打印上岗证对话框 */}\n \n\n {/* 批量打印对话框 */}\n \n
\n );\n}\n","usedDeprecatedRules":[]},{"filePath":"D:\\dcprint\\erp-project\\src\\app\\[locale]\\hr\\employee\\query\\page.tsx","messages":[{"ruleId":"react-hooks/immutability","severity":1,"message":"Error: Cannot access variable before it is declared\n\n`fetchEmployee` is accessed before it is declared, which prevents the earlier access from updating when this value changes over time.\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\hr\\employee\\query\\page.tsx:52:7\n 50 | useEffect(() => {\n 51 | if (employeeId) {\n> 52 | fetchEmployee(parseInt(employeeId));\n | ^^^^^^^^^^^^^ `fetchEmployee` accessed before it is declared\n 53 | } else {\n 54 | setError(tc('invalidEmployeeId'));\n 55 | setLoading(false);\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\hr\\employee\\query\\page.tsx:59:3\n 57 | }, [employeeId]);\n 58 |\n> 59 | const fetchEmployee = async (id: number) => {\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n> 60 | try {\n | ^^^^^^^^^\n> 61 | const response = await authFetch(`/api/organization/employee?id=${id}`);\n …\n | ^^^^^^^^^\n> 72 | }\n | ^^^^^^^^^\n> 73 | };\n | ^^^^^ `fetchEmployee` is declared here\n 74 |\n 75 | const getStatusBadge = (status: number) => {\n 76 | const styles: Record = {","line":52,"column":7,"nodeType":null,"endLine":52,"endColumn":20},{"ruleId":"react-hooks/set-state-in-effect","severity":1,"message":"Error: Calling setState synchronously within an effect can trigger cascading renders\n\nEffects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following:\n* Update external systems with the latest state from React.\n* Subscribe for updates from some external system, calling setState in a callback function when external state changes.\n\nCalling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect).\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\hr\\employee\\query\\page.tsx:54:7\n 52 | fetchEmployee(parseInt(employeeId));\n 53 | } else {\n> 54 | setError(tc('invalidEmployeeId'));\n | ^^^^^^^^ Avoid calling setState() directly within an effect\n 55 | setLoading(false);\n 56 | }\n 57 | }, [employeeId]);","line":54,"column":7,"nodeType":null,"endLine":54,"endColumn":15},{"ruleId":"react-hooks/exhaustive-deps","severity":1,"message":"React Hook useEffect has missing dependencies: 'fetchEmployee' and 'tc'. Either include them or remove the dependency array.","line":57,"column":6,"nodeType":"ArrayExpression","endLine":57,"endColumn":18,"suggestions":[{"desc":"Update the dependencies array to be: [employeeId, fetchEmployee, tc]","fix":{"range":[1314,1326],"text":"[employeeId, fetchEmployee, tc]"}}]},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":62,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":62,"endColumn":43},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":63,"column":18,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":63,"endColumn":25},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":63,"column":36,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":63,"endColumn":40},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction`.","line":64,"column":21,"nodeType":"MemberExpression","messageId":"unsafeArgument","endLine":64,"endColumn":32},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":64,"column":28,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":64,"endColumn":32}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":8,"fixableErrorCount":0,"fixableWarningCount":0,"source":"'use client';\n\nimport { authFetch } from '@/lib/auth-fetch';\nimport { useState, useEffect, Suspense } from 'react';\nimport { useSearchParams } from 'next/navigation';\nimport { Card, CardContent } from '@/components/ui/card';\nimport { Badge } from '@/components/ui/badge';\nimport { Button } from '@/components/ui/button';\nimport {\n UserCircle,\n Building2,\n Briefcase,\n Phone,\n Mail,\n Calendar,\n ArrowLeft,\n Loader2,\n} from 'lucide-react';\nimport Link from 'next/link';\nimport { useTranslations } from 'next-intl';\n\n// 员工接口\ninterface Employee {\n id: number;\n employee_no: string;\n name: string;\n gender: number;\n phone: string;\n email: string;\n dept_id: number;\n dept_name: string;\n role_id: number;\n role_name: string;\n position: string;\n entry_date: string;\n status: number;\n remark?: string;\n}\n\nfunction EmployeeQueryContent() {\n const tc = useTranslations('Common');\n\n const searchParams = useSearchParams();\n const employeeId = searchParams.get('id');\n\n const [employee, setEmployee] = useState(null);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState('');\n\n useEffect(() => {\n if (employeeId) {\n fetchEmployee(parseInt(employeeId));\n } else {\n setError(tc('invalidEmployeeId'));\n setLoading(false);\n }\n }, [employeeId]);\n\n const fetchEmployee = async (id: number) => {\n try {\n const response = await authFetch(`/api/organization/employee?id=${id}`);\n const result = await response.json();\n if (result.success && result.data) {\n setEmployee(result.data);\n } else {\n setError(tc('employeeNotExist'));\n }\n } catch {\n setError(tc('fetchEmployeeFailed'));\n } finally {\n setLoading(false);\n }\n };\n\n const getStatusBadge = (status: number) => {\n const styles: Record = {\n 1: 'bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-300',\n 0: 'bg-gray-100 text-gray-800 dark:bg-gray-700 dark:text-gray-200',\n 2: 'bg-yellow-100 text-yellow-800 dark:bg-yellow-900/30 dark:text-yellow-300',\n 3: 'bg-red-100 text-red-800 dark:bg-red-900/30 dark:text-red-300',\n };\n const labels: Record = {\n 1: tc('statusActive'),\n 0: tc('statusInactive'),\n 2: tc('statusProbation'),\n 3: tc('statusResigned'),\n };\n return (\n {labels[status] || tc('unknown')}\n );\n };\n\n if (loading) {\n return (\n
\n
\n \n

{tc('loading')}

\n
\n
\n );\n }\n\n if (error) {\n return (\n
\n \n \n
\n \n
\n

{tc('queryFailed')}

\n

{error}

\n \n \n \n
\n
\n
\n );\n }\n\n if (!employee) {\n return (\n
\n \n \n
\n \n
\n

{tc('queryFailed')}

\n

{tc('employeeNotExistDesc')}

\n \n \n \n
\n
\n
\n );\n }\n\n return (\n
\n
\n {/* 头部 */}\n
\n \n \n \n
\n {tc('employeeNoLabel')}\n {employee.employee_no}\n
\n
\n\n {/* 员工信息卡片 */}\n \n {/* 卡片头部 - 渐变色背景 */}\n
\n
\n {/* 头像区域 */}\n
\n \n
\n {/* 基本信息 */}\n
\n

{employee.name}

\n
\n \n {employee.gender === 1 ? tc('maleShort') : tc('femaleShort')}\n \n {getStatusBadge(employee.status)}\n
\n
\n
\n
\n\n \n
\n {/* 部门信息 */}\n
\n
\n \n
\n
\n

{tc('department')}

\n

{employee.dept_name || '-'}

\n
\n
\n\n {/* 职位信息 */}\n
\n
\n \n
\n
\n

{tc('position')}

\n

{employee.position || '-'}

\n
\n
\n\n {/* 角色信息 */}\n
\n
\n \n
\n
\n

{tc('role')}

\n

{employee.role_name || '-'}

\n
\n
\n\n {/* 入职日期 */}\n
\n
\n \n
\n
\n

{tc('entryDate')}

\n

{employee.entry_date || '-'}

\n
\n
\n\n {/* 联系电话 */}\n
\n
\n \n
\n
\n

{tc('phone')}

\n

{employee.phone || '-'}

\n
\n
\n\n {/* 邮箱 */}\n
\n
\n \n
\n
\n

{tc('email')}

\n

{employee.email || '-'}

\n
\n
\n
\n\n {/* 备注 */}\n {employee.remark && (\n
\n

{tc('remark')}

\n

{employee.remark}

\n
\n )}\n
\n
\n\n {/* 底部版权 */}\n
\n

{tc('employeeInfoFooter')}

\n
\n
\n
\n );\n}\n\nfunction Loading() {\n const tc = useTranslations('Common');\n return (\n
\n
\n \n

{tc('loading')}

\n
\n
\n );\n}\n\nexport default function EmployeeQueryPage() {\n return (\n }>\n \n \n );\n}\n","usedDeprecatedRules":[]},{"filePath":"D:\\dcprint\\erp-project\\src\\app\\[locale]\\hr\\employee\\types.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\dcprint\\erp-project\\src\\app\\[locale]\\hr\\error.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\dcprint\\erp-project\\src\\app\\[locale]\\hr\\mes-sync\\page.tsx","messages":[{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"连接超时\"。建议使用: tc('text_ikiwuw')","line":29,"column":116,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":29,"endColumn":122},{"ruleId":"@typescript-eslint/no-explicit-any","severity":1,"message":"Unexpected any. Specify a different type.","line":38,"column":53,"nodeType":"TSAnyKeyword","messageId":"unexpectedAny","endLine":38,"endColumn":56,"suggestions":[{"messageId":"suggestUnknown","fix":{"range":[1550,1553],"text":"unknown"},"desc":"Use `unknown` instead, this will force you to explicitly, and safely assert the type is correct."},{"messageId":"suggestNever","fix":{"range":[1550,1553],"text":"never"},"desc":"Use `never` instead, this is useful when instantiating generic type parameters that you don't need to know the type of."}]},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":64,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":64,"endColumn":36},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .code on an `any` value.","line":65,"column":16,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":65,"endColumn":20},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":66,"column":15,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":66,"endColumn":82},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":66,"column":41,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":66,"endColumn":45},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":66,"column":54,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":66,"endColumn":58},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":66,"column":66,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":66,"endColumn":70},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction`.","line":67,"column":20,"nodeType":"Identifier","messageId":"unsafeArgument","endLine":67,"endColumn":24},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":68,"column":15,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":68,"endColumn":77},{"ruleId":"@typescript-eslint/no-unsafe-call","severity":1,"message":"Unsafe call of an `any` typed value.","line":68,"column":24,"nodeType":"MemberExpression","messageId":"unsafeCall","endLine":68,"endColumn":35},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .filter on an `any` value.","line":68,"column":29,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":68,"endColumn":35},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .length on an `any` value.","line":69,"column":20,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":69,"endColumn":26},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction`.","line":70,"column":27,"nodeType":"MemberExpression","messageId":"unsafeArgument","endLine":70,"endColumn":45},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access [0] on an `any` value.","line":70,"column":34,"nodeType":"Literal","messageId":"unsafeMemberExpression","endLine":70,"endColumn":35},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction`.","line":72,"column":27,"nodeType":"MemberExpression","messageId":"unsafeArgument","endLine":72,"endColumn":92},{"ruleId":"@typescript-eslint/no-unsafe-call","severity":1,"message":"Unsafe call of an `any` typed value.","line":72,"column":27,"nodeType":"MemberExpression","messageId":"unsafeCall","endLine":72,"endColumn":38},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .filter on an `any` value.","line":72,"column":32,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":72,"endColumn":38},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .length on an `any` value.","line":72,"column":86,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":72,"endColumn":92},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction`.","line":73,"column":25,"nodeType":"MemberExpression","messageId":"unsafeArgument","endLine":73,"endColumn":88},{"ruleId":"@typescript-eslint/no-unsafe-call","severity":1,"message":"Unsafe call of an `any` typed value.","line":73,"column":25,"nodeType":"MemberExpression","messageId":"unsafeCall","endLine":73,"endColumn":36},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .filter on an `any` value.","line":73,"column":30,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":73,"endColumn":36},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .length on an `any` value.","line":73,"column":82,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":73,"endColumn":88},{"ruleId":"react-hooks/set-state-in-effect","severity":1,"message":"Error: Calling setState synchronously within an effect can trigger cascading renders\n\nEffects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following:\n* Update external systems with the latest state from React.\n* Subscribe for updates from some external system, calling setState in a callback function when external state changes.\n\nCalling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect).\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\hr\\mes-sync\\page.tsx:84:21\n 82 | };\n 83 |\n> 84 | useEffect(() => { fetchSyncData(); }, []);\n | ^^^^^^^^^^^^^ Avoid calling setState() directly within an effect\n 85 |\n 86 | const handleManualSync = async () => {\n 87 | setSyncing(true);","line":84,"column":21,"nodeType":null,"endLine":84,"endColumn":34},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":90,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":90,"endColumn":36},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .code on an `any` value.","line":91,"column":16,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":91,"endColumn":20},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `string | number | bigint | boolean | ReactElement> | Iterable | ... 4 more ... | undefined`.","line":95,"column":21,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":95,"endColumn":62},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .message on an `any` value.","line":95,"column":26,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":95,"endColumn":33}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":28,"fixableErrorCount":0,"fixableWarningCount":0,"source":"'use client';\r\n\r\nimport { authFetch } from '@/lib/auth-fetch';\r\nimport { useState, useEffect } from 'react';\r\nimport { toast } from 'sonner';\r\nimport { MainLayout } from '@/components/layout';\r\nimport { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';\r\nimport {\r\n Table, TableBody, TableCell, TableHead, TableHeader, TableRow,\r\n} from '@/components/ui/table';\r\nimport { Button } from '@/components/ui/button';\r\nimport { Badge } from '@/components/ui/badge';\r\nimport { RefreshCw, Database, CheckCircle2, Clock } from 'lucide-react';\r\nimport { useTranslations } from 'next-intl';\r\n\r\ninterface SyncRecord {\r\n id: number;\r\n syncType: string;\r\n status: 'synced' | 'pending' | 'failed';\r\n syncTime: string;\r\n recordCount: number;\r\n errorMessage?: string;\r\n}\r\n\r\nconst mockSyncHistory: SyncRecord[] = [\r\n { id: 1, syncType: 'pieceWork', status: 'synced', syncTime: '2024-03-15 10:30:00', recordCount: 156 },\r\n { id: 2, syncType: 'pieceWork', status: 'synced', syncTime: '2024-03-15 09:00:00', recordCount: 142 },\r\n { id: 3, syncType: 'quality', status: 'synced', syncTime: '2024-03-15 08:30:00', recordCount: 89 },\r\n { id: 4, syncType: 'pieceWork', status: 'failed', syncTime: '2024-03-14 17:00:00', recordCount: 0, errorMessage: '连接超时' },\r\n { id: 5, syncType: 'quality', status: 'pending', syncTime: '2024-03-14 16:00:00', recordCount: 0 },\r\n];\r\n\r\nconst syncTypeLabels: Record = {\r\n pieceWork: 'syncTypePieceWork',\r\n quality: 'syncTypeQuality',\r\n};\r\n\r\nconst typeIcons: Record> = {\r\n pieceWork: Database,\r\n quality: CheckCircle2,\r\n};\r\n\r\nconst statusConfig: Record = {\r\n synced: { label: 'syncStatusSynced', className: 'bg-green-100 text-green-700' },\r\n pending: { label: 'syncStatusPending', className: 'bg-yellow-100 text-yellow-700' },\r\n failed: { label: 'syncStatusFailed', className: 'bg-red-100 text-red-700' },\r\n};\r\n\r\nexport default function MesSyncPage() {\r\n const t = useTranslations('Hr');\r\n const tc = useTranslations('Common');\r\n\r\n const [records, setRecords] = useState([]);\r\n const [loading, setLoading] = useState(false);\r\n const [syncing, setSyncing] = useState(false);\r\n const [lastSyncTime, setLastSyncTime] = useState('-');\r\n const [pieceWorkCount, setPieceWorkCount] = useState(0);\r\n const [qualityCount, setQualityCount] = useState(0);\r\n\r\n const fetchSyncData = async () => {\r\n setLoading(true);\r\n try {\r\n const res = await authFetch('/api/hr/mes-sync/piece-work');\r\n const json = await res.json();\r\n if (json.code === 200) {\r\n const list = Array.isArray(json.data) ? json.data : json.data?.list || [];\r\n setRecords(list);\r\n const synced = list.filter((r: SyncRecord) => r.status === 'synced');\r\n if (synced.length > 0) {\r\n setLastSyncTime(synced[0].syncTime);\r\n }\r\n setPieceWorkCount(list.filter((r: SyncRecord) => r.syncType === 'pieceWork').length);\r\n setQualityCount(list.filter((r: SyncRecord) => r.syncType === 'quality').length);\r\n } else {\r\n setRecords(mockSyncHistory);\r\n }\r\n } catch {\r\n setRecords(mockSyncHistory);\r\n } finally {\r\n setLoading(false);\r\n }\r\n };\r\n\r\n useEffect(() => { fetchSyncData(); }, []);\r\n\r\n const handleManualSync = async () => {\r\n setSyncing(true);\r\n try {\r\n const res = await authFetch('/api/hr/mes-sync/sync', { method: 'POST' });\r\n const json = await res.json();\r\n if (json.code === 200) {\r\n toast.success(t('syncSuccess') || '同步成功');\r\n fetchSyncData();\r\n } else {\r\n toast.error(json.message || t('syncFailed') || '同步失败');\r\n }\r\n } catch {\r\n toast.error(t('syncFailed') || '同步失败');\r\n } finally {\r\n setSyncing(false);\r\n }\r\n };\r\n\r\n const recentRecords = records.slice(0, 50);\r\n\r\n return (\r\n \r\n
\r\n
\r\n
\r\n \r\n

{t('mesSync')}

\r\n
\r\n \r\n
\r\n\r\n
\r\n \r\n \r\n
\r\n
\r\n

{t('pieceWorkSync')}

\r\n

{pieceWorkCount}

\r\n

{t('syncRecords')}

\r\n
\r\n \r\n
\r\n
\r\n
\r\n\r\n \r\n \r\n
\r\n
\r\n

{t('qualitySync')}

\r\n

{qualityCount}

\r\n

{t('syncRecords')}

\r\n
\r\n \r\n
\r\n
\r\n
\r\n\r\n \r\n \r\n
\r\n
\r\n

{t('lastSyncTime')}

\r\n

{lastSyncTime}

\r\n

{t('lastSyncDesc')}

\r\n
\r\n \r\n
\r\n
\r\n
\r\n
\r\n\r\n \r\n \r\n \r\n \r\n {t('syncHistory')}\r\n {t('latest50')}\r\n \r\n \r\n \r\n \r\n \r\n \r\n {t('syncType')}\r\n {t('syncTime')}\r\n {t('recordCount')}\r\n {tc('status')}\r\n {t('errorMessage')}\r\n \r\n \r\n \r\n {recentRecords.map((r) => {\r\n const TypeIcon = typeIcons[r.syncType] || Database;\r\n const statusConf = statusConfig[r.status] || statusConfig.pending;\r\n return (\r\n \r\n \r\n
\r\n \r\n {t(syncTypeLabels[r.syncType] || r.syncType)}\r\n
\r\n
\r\n {r.syncTime}\r\n {r.recordCount}\r\n \r\n {t(statusConf.label)}\r\n \r\n {r.errorMessage || '-'}\r\n
\r\n );\r\n })}\r\n {recentRecords.length === 0 && (\r\n \r\n \r\n {loading ? tc('loading') : t('noData')}\r\n \r\n \r\n )}\r\n
\r\n
\r\n
\r\n
\r\n
\r\n
\r\n );\r\n}\r\n","usedDeprecatedRules":[]},{"filePath":"D:\\dcprint\\erp-project\\src\\app\\[locale]\\hr\\organization\\page.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\dcprint\\erp-project\\src\\app\\[locale]\\hr\\performance\\page.tsx","messages":[{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":61,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":61,"endColumn":36},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .code on an `any` value.","line":62,"column":16,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":62,"endColumn":20},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":63,"column":15,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":63,"endColumn":82},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":63,"column":41,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":63,"endColumn":45},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":63,"column":54,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":63,"endColumn":58},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":63,"column":66,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":63,"endColumn":70},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction`.","line":65,"column":11,"nodeType":"CallExpression","messageId":"unsafeArgument","endLine":79,"endColumn":77},{"ruleId":"@typescript-eslint/no-unsafe-call","severity":1,"message":"Unsafe call of an `any` typed value.","line":65,"column":11,"nodeType":"MemberExpression","messageId":"unsafeCall","endLine":79,"endColumn":17},{"ruleId":"@typescript-eslint/no-unsafe-call","severity":1,"message":"Unsafe call of an `any` typed value.","line":65,"column":11,"nodeType":"MemberExpression","messageId":"unsafeCall","endLine":66,"endColumn":17},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .map on an `any` value.","line":66,"column":14,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":66,"endColumn":17},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .map on an `any` value.","line":79,"column":14,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":79,"endColumn":17},{"ruleId":"react-hooks/set-state-in-effect","severity":1,"message":"Error: Calling setState synchronously within an effect can trigger cascading renders\n\nEffects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following:\n* Update external systems with the latest state from React.\n* Subscribe for updates from some external system, calling setState in a callback function when external state changes.\n\nCalling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect).\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\hr\\performance\\page.tsx:92:5\n 90 |\n 91 | useEffect(() => {\n> 92 | fetchScores();\n | ^^^^^^^^^^^ Avoid calling setState() directly within an effect\n 93 | }, []);\n 94 |\n 95 | const updateScore = (id: number, field: keyof ScoreRow, value: number) => {","line":92,"column":5,"nodeType":null,"endLine":92,"endColumn":16},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":112,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":112,"endColumn":36},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .code on an `any` value.","line":113,"column":16,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":113,"endColumn":20},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `string | number | bigint | boolean | ReactElement> | Iterable | ... 4 more ... | undefined`.","line":116,"column":21,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":116,"endColumn":48},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .message on an `any` value.","line":116,"column":26,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":116,"endColumn":33},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"张三\"。建议使用: tc('text_glwp')","line":229,"column":19,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":229,"endColumn":23},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"李四\"。建议使用: tc('text_i1ql')","line":239,"column":19,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":239,"endColumn":23},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"王五\"。建议使用: tc('text_k31l')","line":249,"column":19,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":249,"endColumn":23},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"赵六\"。建议使用: tc('text_oiag')","line":259,"column":19,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":259,"endColumn":23},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"孙七\"。建议使用: tc('text_fyru')","line":269,"column":19,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":269,"endColumn":23}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":21,"fixableErrorCount":0,"fixableWarningCount":0,"source":"'use client';\n\nimport { authFetch } from '@/lib/auth-fetch';\nimport { useState, useEffect } from 'react';\nimport { toast } from 'sonner';\nimport { MainLayout } from '@/components/layout';\nimport { Card, CardContent, CardHeader } from '@/components/ui/card';\nimport {\n Table,\n TableBody,\n TableCell,\n TableHead,\n TableHeader,\n TableRow,\n} from '@/components/ui/table';\nimport { Button } from '@/components/ui/button';\nimport { Input } from '@/components/ui/input';\nimport { Search, Save, TrendingUp } from 'lucide-react';\nimport { useTranslations } from 'next-intl';\n\ninterface ScoreRow {\n id: number;\n employeeName: string;\n employeeNo: string;\n outputRate: number;\n qualityRate: number;\n equipmentRate: number;\n siteManagement: number;\n totalScore: number;\n}\n\nconst calculateTotal = (row: {\n outputRate: number;\n qualityRate: number;\n equipmentRate: number;\n siteManagement: number;\n}) => {\n return (\n Math.round(\n (row.outputRate * 0.4 +\n row.qualityRate * 0.3 +\n row.equipmentRate * 0.15 +\n row.siteManagement * 0.15) *\n 100\n ) / 100\n );\n};\n\nexport default function PerformancePage() {\n const t = useTranslations('Hr');\n const tc = useTranslations('Common');\n\n const [scores, setScores] = useState([]);\n const [_loading, setLoading] = useState(false);\n const [search, setSearch] = useState('');\n\n const fetchScores = async () => {\n setLoading(true);\n try {\n const res = await authFetch('/api/hr/performance');\n const json = await res.json();\n if (json.code === 200) {\n const list = Array.isArray(json.data) ? json.data : json.data?.list || [];\n setScores(\n list\n .map((item: unknown) => {\n const i = item as Record;\n return {\n id: i.id,\n employeeName: i.employeeName || i.employee_name,\n employeeNo: i.employeeNo || i.employee_no,\n outputRate: i.outputRate ?? i.output_rate ?? 0,\n qualityRate: i.qualityRate ?? i.quality_rate ?? 0,\n equipmentRate: i.equipmentRate ?? i.equipment_rate ?? 0,\n siteManagement: i.siteManagement ?? i.site_management ?? 0,\n totalScore: 0,\n };\n })\n .map((r: ScoreRow) => ({ ...r, totalScore: calculateTotal(r) }))\n );\n } else {\n setScores(mockData);\n }\n } catch {\n setScores(mockData);\n } finally {\n setLoading(false);\n }\n };\n\n useEffect(() => {\n fetchScores();\n }, []);\n\n const updateScore = (id: number, field: keyof ScoreRow, value: number) => {\n setScores((prev) =>\n prev.map((r) => {\n if (r.id !== id) return r;\n const updated = { ...r, [field]: value };\n updated.totalScore = calculateTotal(updated);\n return updated;\n })\n );\n };\n\n const handleSave = async () => {\n try {\n const res = await authFetch('/api/hr/performance', {\n method: 'POST',\n body: JSON.stringify({ scores }),\n });\n const json = await res.json();\n if (json.code === 200) {\n toast.success(t('saveSuccess') || '保存成功');\n } else {\n toast.error(json.message || tc('error'));\n }\n } catch {\n toast.error(t('saveFailed') || '保存失败');\n }\n };\n\n const filtered = scores.filter(\n (r) => !search || r.employeeName?.toLowerCase().includes(search.toLowerCase())\n );\n\n return (\n \n
\n
\n
\n \n

{t('performance') || '绩效评分'}

\n
\n \n
\n\n \n \n
\n
\n \n setSearch(e.target.value)}\n />\n
\n
\n
\n \n \n \n \n {t('employeeName') || '姓名'}\n \n {t('outputRate40') || '产量达成率(40%)'}\n \n \n {t('qualityRate30') || '质量合格率(30%)'}\n \n \n {t('equipmentRate15') || '设备稼动率(15%)'}\n \n \n {t('siteManagement15') || '5S现场管理(15%)'}\n \n \n {t('totalScore') || '总分'}\n \n \n \n \n {filtered.map((r) => (\n \n \n
\n {r.employeeName}\n {r.employeeNo}\n
\n
\n {(\n ['outputRate', 'qualityRate', 'equipmentRate', 'siteManagement'] as const\n ).map((field) => (\n \n \n updateScore(r.id, field, parseFloat(e.target.value) || 0)\n }\n />\n \n ))}\n \n \n {r.totalScore.toFixed(2)}\n \n \n
\n ))}\n {filtered.length === 0 && (\n \n \n {t('noData') || '暂无数据'}\n \n \n )}\n
\n
\n
\n
\n
\n
\n );\n}\n\nconst mockData: ScoreRow[] = [\n {\n id: 1,\n employeeName: '张三',\n employeeNo: 'EMP001',\n outputRate: 95,\n qualityRate: 98,\n equipmentRate: 88,\n siteManagement: 90,\n totalScore: 0,\n },\n {\n id: 2,\n employeeName: '李四',\n employeeNo: 'EMP002',\n outputRate: 88,\n qualityRate: 92,\n equipmentRate: 85,\n siteManagement: 82,\n totalScore: 0,\n },\n {\n id: 3,\n employeeName: '王五',\n employeeNo: 'EMP003',\n outputRate: 78,\n qualityRate: 85,\n equipmentRate: 80,\n siteManagement: 75,\n totalScore: 0,\n },\n {\n id: 4,\n employeeName: '赵六',\n employeeNo: 'EMP004',\n outputRate: 92,\n qualityRate: 90,\n equipmentRate: 95,\n siteManagement: 88,\n totalScore: 0,\n },\n {\n id: 5,\n employeeName: '孙七',\n employeeNo: 'EMP005',\n outputRate: 85,\n qualityRate: 88,\n equipmentRate: 82,\n siteManagement: 80,\n totalScore: 0,\n },\n].map((r) => ({ ...r, totalScore: calculateTotal(r) }));\n","usedDeprecatedRules":[]},{"filePath":"D:\\dcprint\\erp-project\\src\\app\\[locale]\\hr\\reports\\labor-cost\\page.tsx","messages":[{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"页面加载失败\"。建议使用: {tc('text_p6lute')}","line":54,"column":63,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":54,"endColumn":69},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"发生未知错误\"。建议使用: tc('text_nuijq9')","line":56,"column":47,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":56,"endColumn":55},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"重试\"。建议使用: {tc('text_pkfc')}","line":62,"column":16,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":64,"endColumn":15},{"ruleId":"react-hooks/immutability","severity":1,"message":"Error: Cannot access variable before it is declared\n\n`fetchData` is accessed before it is declared, which prevents the earlier access from updating when this value changes over time.\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\hr\\reports\\labor-cost\\page.tsx:100:5\n 98 |\n 99 | useEffect(() => {\n> 100 | fetchData();\n | ^^^^^^^^^ `fetchData` accessed before it is declared\n 101 | }, [month]);\n 102 |\n 103 | const fetchData = async () => {\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\hr\\reports\\labor-cost\\page.tsx:103:3\n 101 | }, [month]);\n 102 |\n> 103 | const fetchData = async () => {\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n> 104 | setLoading(true);\n | ^^^^^^^^^^^^^^^^^^^^^\n> 105 | try {\n …\n | ^^^^^^^^^^^^^^^^^^^^^\n> 116 | }\n | ^^^^^^^^^^^^^^^^^^^^^\n> 117 | };\n | ^^^^^ `fetchData` is declared here\n 118 |\n 119 | const changeMonth = (offset: number) => {\n 120 | const date = new Date(month + '-01');","line":100,"column":5,"nodeType":null,"endLine":100,"endColumn":14},{"ruleId":"react-hooks/exhaustive-deps","severity":1,"message":"React Hook useEffect has a missing dependency: 'fetchData'. Either include it or remove the dependency array.","line":101,"column":6,"nodeType":"ArrayExpression","endLine":101,"endColumn":13,"suggestions":[{"desc":"Update the dependencies array to be: [fetchData, month]","fix":{"range":[3108,3115],"text":"[fetchData, month]"}}]},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":108,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":108,"endColumn":36},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":109,"column":16,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":109,"endColumn":23},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction`.","line":110,"column":17,"nodeType":"MemberExpression","messageId":"unsafeArgument","endLine":110,"endColumn":26},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":110,"column":22,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":110,"endColumn":26},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe spread of an `any` value in an array.","line":139,"column":15,"nodeType":"SpreadElement","messageId":"unsafeArraySpread","endLine":139,"endColumn":26}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":10,"fixableErrorCount":0,"fixableWarningCount":0,"source":"'use client';\r\n\r\nimport { useState, useEffect, ErrorInfo, Component } from 'react';\r\nimport { authFetch } from '@/lib/auth-fetch';\r\nimport { MainLayout } from '@/components/layout';\r\nimport { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';\r\nimport {\r\n Table,\r\n TableBody,\r\n TableCell,\r\n TableHead,\r\n TableHeader,\r\n TableRow,\r\n} from '@/components/ui/table';\r\nimport { Button } from '@/components/ui/button';\r\nimport { Skeleton } from '@/components/ui/skeleton';\r\nimport { LaborCostChart } from '@/components/hr/charts/LaborCostChart';\r\nimport { ChevronLeft, ChevronRight, DollarSign, Users, TrendingUp, Building2, AlertCircle } from 'lucide-react';\r\nimport { format } from 'date-fns';\r\nimport { useTranslations } from 'next-intl';\r\n\r\ninterface ErrorBoundaryProps {\r\n children: React.ReactNode;\r\n}\r\n\r\ninterface ErrorBoundaryState {\r\n hasError: boolean;\r\n error?: Error;\r\n errorInfo?: ErrorInfo;\r\n}\r\n\r\nclass ErrorBoundary extends Component {\r\n constructor(props: ErrorBoundaryProps) {\r\n super(props);\r\n this.state = { hasError: false };\r\n }\r\n\r\n static getDerivedStateFromError(_error: Error): ErrorBoundaryState {\r\n return { hasError: true };\r\n }\r\n\r\n componentDidCatch(error: Error, errorInfo: ErrorInfo) {\r\n console.error('[LaborCostPage Error]', error, errorInfo);\r\n this.setState({ error, errorInfo });\r\n }\r\n\r\n render() {\r\n if (this.state.hasError) {\r\n return (\r\n \r\n
\r\n
\r\n \r\n

页面加载失败

\r\n

\r\n {this.state.error?.message || '发生未知错误'}\r\n

\r\n this.setState({ hasError: false })}\r\n >\r\n 重试\r\n \r\n
\r\n
\r\n
\r\n );\r\n }\r\n return this.props.children;\r\n }\r\n}\r\n\r\ninterface LaborCostData {\r\n totalCost: number;\r\n avgCost: number;\r\n headcount: number;\r\n costTrend: number;\r\n byDepartment: { dept_name: string; cost: number; percentage: number }[];\r\n byType: { type: string; cost: number; percentage: number }[];\r\n monthlyTrend: {\r\n month: string;\r\n base: number;\r\n piece: number;\r\n overtime: number;\r\n performance: number;\r\n insurance: number;\r\n }[];\r\n}\r\n\r\nexport default function LaborCostReportPage() {\r\n const t = useTranslations('Hr');\r\n const tc = useTranslations('Common');\r\n const [data, setData] = useState(null);\r\n const [loading, setLoading] = useState(true);\r\n const [error, setError] = useState(null);\r\n const [month, setMonth] = useState(format(new Date(), 'yyyy-MM'));\r\n\r\n useEffect(() => {\r\n fetchData();\r\n }, [month]);\r\n\r\n const fetchData = async () => {\r\n setLoading(true);\r\n try {\r\n setError(null);\r\n const res = await authFetch(`/api/hr/reports/labor-cost?month=${month}`);\r\n const json = await res.json();\r\n if (json.success) {\r\n setData(json.data);\r\n }\r\n } catch (err) {\r\n setError(err instanceof Error ? err.message : 'Failed to fetch data');\r\n } finally {\r\n setLoading(false);\r\n }\r\n };\r\n\r\n const changeMonth = (offset: number) => {\r\n const date = new Date(month + '-01');\r\n date.setMonth(date.getMonth() + offset);\r\n setMonth(format(date, 'yyyy-MM'));\r\n };\r\n\r\n const typeLabels: Record = {\r\n base: t('baseSalary') || '基本工资',\r\n piece: t('pieceWage') || '计件工资',\r\n overtime: t('overtimePay') || '加班费',\r\n performance: t('performanceBonus') || '绩效奖金',\r\n insurance: t('socialInsurance') || '社保/公积金',\r\n };\r\n\r\n if (loading) {\r\n return (\r\n \r\n
\r\n \r\n
\r\n {[...Array(4)].map((_, i) => )}\r\n
\r\n \r\n
\r\n
\r\n );\r\n }\r\n\r\n if (error) {\r\n return (\r\n \r\n
\r\n
\r\n \r\n

{tc('error') || '操作失败'}

\r\n

{error}

\r\n \r\n
\r\n
\r\n
\r\n );\r\n }\r\n\r\n if (!data) {\r\n return (\r\n \r\n
\r\n
\r\n \r\n

{tc('noData') || '暂无数据'}

\r\n

{tc('pleaseSelectMonth') || '请选择月份或进行薪资核算'}

\r\n
\r\n
\r\n
\r\n );\r\n }\r\n\r\n return (\r\n \r\n \r\n
\r\n
\r\n

{t('laborCost') || '人力成本分析'}

\r\n
\r\n \r\n {month}\r\n \r\n
\r\n
\r\n\r\n
\r\n \r\n \r\n {t('totalCost') || '总成本'}\r\n \r\n \r\n \r\n
\r\n ¥{(data?.totalCost ?? 0).toLocaleString()}\r\n
\r\n
\r\n
\r\n \r\n \r\n {t('avgCostPerHead') || '人均成本'}\r\n \r\n \r\n \r\n
\r\n ¥{(data?.avgCost ?? 0).toLocaleString()}\r\n
\r\n
\r\n
\r\n \r\n \r\n {t('headcount') || '人数'}\r\n \r\n \r\n \r\n
{data?.headcount || 0}
\r\n
\r\n
\r\n \r\n \r\n {t('trend') || '成本趋势'}\r\n \r\n \r\n \r\n
= 0 ? 'text-red-500' : 'text-green-500'}`}>\r\n {(data?.costTrend || 0) >= 0 ? '+' : ''}{data?.costTrend || 0}%\r\n
\r\n
\r\n
\r\n
\r\n\r\n
\r\n \r\n \r\n {t('departmentCostDistribution') || '部门成本分布'}\r\n \r\n \r\n \r\n \r\n \r\n {t('departmentName') || '部门'}\r\n {t('totalCost') || '成本'}\r\n {t('percentage') || '占比'}\r\n \r\n \r\n \r\n {(data?.byDepartment || []).map((dept, i) => (\r\n \r\n {dept.dept_name}\r\n ¥{(dept.cost ?? 0).toLocaleString()}\r\n {dept.percentage || 0}%\r\n \r\n ))}\r\n \r\n
\r\n
\r\n
\r\n\r\n \r\n \r\n {t('costTypeBreakdown') || '成本类型构成'}\r\n \r\n \r\n \r\n \r\n \r\n {t('type') || '类型'}\r\n {t('amount') || '金额'}\r\n {t('percentage') || '占比'}\r\n \r\n \r\n \r\n {(data?.byType || []).map((t, i) => (\r\n \r\n {typeLabels[t.type] || t.type}\r\n ¥{(t.cost ?? 0).toLocaleString()}\r\n {t.percentage || 0}%\r\n \r\n ))}\r\n \r\n
\r\n
\r\n
\r\n
\r\n\r\n \r\n \r\n {t('monthlyTrend') || '月度成本趋势'}\r\n \r\n \r\n \r\n \r\n \r\n
\r\n
\r\n
\r\n );\r\n}\r\n","usedDeprecatedRules":[]},{"filePath":"D:\\dcprint\\erp-project\\src\\app\\[locale]\\hr\\reports\\page.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\dcprint\\erp-project\\src\\app\\[locale]\\hr\\reports\\salary-structure\\page.tsx","messages":[{"ruleId":"react-hooks/immutability","severity":1,"message":"Error: Cannot access variable before it is declared\n\n`fetchData` is accessed before it is declared, which prevents the earlier access from updating when this value changes over time.\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\hr\\reports\\salary-structure\\page.tsx:37:5\n 35 |\n 36 | useEffect(() => {\n> 37 | fetchData();\n | ^^^^^^^^^ `fetchData` accessed before it is declared\n 38 | }, [month]);\n 39 |\n 40 | const fetchData = async () => {\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\hr\\reports\\salary-structure\\page.tsx:40:3\n 38 | }, [month]);\n 39 |\n> 40 | const fetchData = async () => {\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n> 41 | setLoading(true);\n | ^^^^^^^^^^^^^^^^^^^^^\n> 42 | try {\n …\n | ^^^^^^^^^^^^^^^^^^^^^\n> 54 | }\n | ^^^^^^^^^^^^^^^^^^^^^\n> 55 | };\n | ^^^^^ `fetchData` is declared here\n 56 |\n 57 | const changeMonth = (offset: number) => {\n 58 | const date = new Date(month + '-01');","line":37,"column":5,"nodeType":null,"endLine":37,"endColumn":14},{"ruleId":"react-hooks/exhaustive-deps","severity":1,"message":"React Hook useEffect has a missing dependency: 'fetchData'. Either include it or remove the dependency array.","line":38,"column":6,"nodeType":"ArrayExpression","endLine":38,"endColumn":13,"suggestions":[{"desc":"Update the dependencies array to be: [fetchData, month]","fix":{"range":[1297,1304],"text":"[fetchData, month]"}}]},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":44,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":44,"endColumn":36},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .code on an `any` value.","line":45,"column":16,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":45,"endColumn":20},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction`.","line":46,"column":17,"nodeType":"MemberExpression","messageId":"unsafeArgument","endLine":46,"endColumn":26},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":46,"column":22,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":46,"endColumn":26},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe spread of an `any` value in an array.","line":69,"column":15,"nodeType":"SpreadElement","messageId":"unsafeArraySpread","endLine":69,"endColumn":26}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":7,"fixableErrorCount":0,"fixableWarningCount":0,"source":"'use client';\r\n\r\nimport { useState, useEffect } from 'react';\r\nimport { authFetch } from '@/lib/auth-fetch';\r\nimport { MainLayout } from '@/components/layout';\r\nimport { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';\r\nimport {\r\n Table,\r\n TableBody,\r\n TableCell,\r\n TableHead,\r\n TableHeader,\r\n TableRow,\r\n} from '@/components/ui/table';\r\nimport { Button } from '@/components/ui/button';\r\nimport { Skeleton } from '@/components/ui/skeleton';\r\nimport { SalaryStructureChart } from '@/components/hr/charts/SalaryStructureChart';\r\nimport { ChevronLeft, ChevronRight, DollarSign, TrendingUp } from 'lucide-react';\r\nimport { format } from 'date-fns';\r\nimport { useTranslations } from 'next-intl';\r\n\r\ninterface SalaryStructureData {\r\n avgSalary: number;\r\n medianSalary: number;\r\n componentBreakdown: { name: string; value: number; color: string }[];\r\n distribution: { range: string; count: number }[];\r\n}\r\n\r\nexport default function SalaryStructurePage() {\r\n const t = useTranslations('Hr');\r\n const _tc = useTranslations('Common');\r\n const [data, setData] = useState(null);\r\n const [loading, setLoading] = useState(true);\r\n const [month, setMonth] = useState(format(new Date(), 'yyyy-MM'));\r\n\r\n useEffect(() => {\r\n fetchData();\r\n }, [month]);\r\n\r\n const fetchData = async () => {\r\n setLoading(true);\r\n try {\r\n const res = await authFetch(`/api/hr/reports/salary-structure?month=${month}`);\r\n const json = await res.json();\r\n if (json.code === 200) {\r\n setData(json.data);\r\n } else {\r\n setData({ avgSalary: 0, medianSalary: 0, componentBreakdown: [], distribution: [] });\r\n }\r\n } catch {\r\n setData({ avgSalary: 0, medianSalary: 0, componentBreakdown: [], distribution: [] });\r\n } finally {\r\n setLoading(false);\r\n }\r\n };\r\n\r\n const changeMonth = (offset: number) => {\r\n const date = new Date(month + '-01');\r\n date.setMonth(date.getMonth() + offset);\r\n setMonth(format(date, 'yyyy-MM'));\r\n };\r\n\r\n if (loading) {\r\n return (\r\n \r\n
\r\n \r\n
\r\n {[...Array(2)].map((_, i) => )}\r\n
\r\n \r\n
\r\n
\r\n );\r\n }\r\n\r\n return (\r\n \r\n
\r\n
\r\n

{t('salaryStructure') || '薪资结构分析'}

\r\n
\r\n \r\n {month}\r\n \r\n
\r\n
\r\n\r\n
\r\n \r\n \r\n {t('avgSalary') || '平均薪资'}\r\n \r\n \r\n \r\n
\r\n ¥{data?.avgSalary?.toLocaleString() ?? '0'}\r\n
\r\n
\r\n
\r\n \r\n \r\n {t('medianSalary') || '中位数薪资'}\r\n \r\n \r\n \r\n
\r\n ¥{data?.medianSalary?.toLocaleString() ?? '0'}\r\n
\r\n
\r\n
\r\n
\r\n\r\n
\r\n \r\n \r\n {t('salaryStructure') || '薪资构成'}\r\n \r\n \r\n \r\n \r\n \r\n\r\n \r\n \r\n {t('salaryDistribution') || '薪资分布'}\r\n \r\n \r\n \r\n \r\n \r\n {t('salaryRange') || '薪资区间'}\r\n {t('headcount') || '人数'}\r\n \r\n \r\n \r\n {data?.distribution.map((d, i) => (\r\n \r\n {d.range}\r\n {d.count}\r\n \r\n ))}\r\n \r\n
\r\n
\r\n
\r\n
\r\n
\r\n
\r\n );\r\n}\r\n","usedDeprecatedRules":[]},{"filePath":"D:\\dcprint\\erp-project\\src\\app\\[locale]\\hr\\reports\\turnover\\page.tsx","messages":[{"ruleId":"react-hooks/immutability","severity":1,"message":"Error: Cannot access variable before it is declared\n\n`fetchData` is accessed before it is declared, which prevents the earlier access from updating when this value changes over time.\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\hr\\reports\\turnover\\page.tsx:45:5\n 43 |\n 44 | useEffect(() => {\n> 45 | fetchData();\n | ^^^^^^^^^ `fetchData` accessed before it is declared\n 46 | }, []);\n 47 |\n 48 | const fetchData = async () => {\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\hr\\reports\\turnover\\page.tsx:48:3\n 46 | }, []);\n 47 |\n> 48 | const fetchData = async () => {\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n> 49 | setLoading(true);\n | ^^^^^^^^^^^^^^^^^^^^^\n> 50 | try {\n …\n | ^^^^^^^^^^^^^^^^^^^^^\n> 59 | }\n | ^^^^^^^^^^^^^^^^^^^^^\n> 60 | };\n | ^^^^^ `fetchData` is declared here\n 61 |\n 62 | if (loading) {\n 63 | return (","line":45,"column":5,"nodeType":null,"endLine":45,"endColumn":14},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":52,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":52,"endColumn":36},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":53,"column":16,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":53,"endColumn":23},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction`.","line":54,"column":17,"nodeType":"MemberExpression","messageId":"unsafeArgument","endLine":54,"endColumn":26},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":54,"column":22,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":54,"endColumn":26},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe spread of an `any` value in an array.","line":68,"column":15,"nodeType":"SpreadElement","messageId":"unsafeArraySpread","endLine":68,"endColumn":26}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":6,"fixableErrorCount":0,"fixableWarningCount":0,"source":"'use client';\r\n\r\nimport { useState, useEffect } from 'react';\r\nimport { authFetch } from '@/lib/auth-fetch';\r\nimport { MainLayout } from '@/components/layout';\r\nimport { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';\r\nimport {\r\n Table,\r\n TableBody,\r\n TableCell,\r\n TableHead,\r\n TableHeader,\r\n TableRow,\r\n} from '@/components/ui/table';\r\nimport { Skeleton } from '@/components/ui/skeleton';\r\nimport { Users, UserMinus, CalendarDays, Activity } from 'lucide-react';\r\nimport { useTranslations } from 'next-intl';\r\n\r\ninterface TurnoverData {\r\n totalEmployees: number;\r\n resignedCount: number;\r\n avgTenureDays: number;\r\n turnoverRate: number;\r\n monthlyTrend: {\r\n month: string;\r\n newHires: number;\r\n resignations: number;\r\n netChange: number;\r\n }[];\r\n byDepartment: {\r\n dept_name: string;\r\n total: number;\r\n resigned: number;\r\n rate: number;\r\n }[];\r\n}\r\n\r\nexport default function TurnoverPage() {\r\n const t = useTranslations('Hr');\r\n const _tc = useTranslations('Common');\r\n const [data, setData] = useState(null);\r\n const [loading, setLoading] = useState(true);\r\n\r\n useEffect(() => {\r\n fetchData();\r\n }, []);\r\n\r\n const fetchData = async () => {\r\n setLoading(true);\r\n try {\r\n const res = await authFetch('/api/hr/reports/turnover');\r\n const json = await res.json();\r\n if (json.success) {\r\n setData(json.data);\r\n }\r\n } catch {\r\n } finally {\r\n setLoading(false);\r\n }\r\n };\r\n\r\n if (loading) {\r\n return (\r\n \r\n
\r\n \r\n
\r\n {[...Array(4)].map((_, i) => )}\r\n
\r\n \r\n
\r\n
\r\n );\r\n }\r\n\r\n return (\r\n \r\n
\r\n
\r\n

{t('turnover') || '人员流动分析'}

\r\n
\r\n\r\n
\r\n \r\n \r\n {t('headcount') || '在职人数'}\r\n \r\n \r\n \r\n
{data?.totalEmployees || 0}
\r\n
\r\n
\r\n \r\n \r\n {t('resignations') || '离职人数'}\r\n \r\n \r\n \r\n
{data?.resignedCount || 0}
\r\n
\r\n
\r\n \r\n \r\n {t('avgTenure') || '平均在职天数'}\r\n \r\n \r\n \r\n
{data?.avgTenureDays || 0}
\r\n
\r\n
\r\n \r\n \r\n {t('turnoverRate') || '综合流动率'}\r\n \r\n \r\n \r\n
{data?.turnoverRate || 0}%
\r\n
\r\n
\r\n
\r\n\r\n
\r\n \r\n \r\n {t('monthlyTrend') || '月度流动趋势'}\r\n \r\n \r\n \r\n \r\n \r\n {t('month') || '月份'}\r\n {t('newHires') || '新入职'}\r\n {t('resignations') || '离职'}\r\n {t('netChange') || '净变化'}\r\n \r\n \r\n \r\n {data?.monthlyTrend.map((row, i) => (\r\n \r\n {row.month}\r\n {row.newHires}\r\n {row.resignations}\r\n = 0 ? 'text-green-600' : 'text-red-500'}`}>\r\n {row.netChange >= 0 ? '+' : ''}{row.netChange}\r\n \r\n \r\n ))}\r\n \r\n
\r\n
\r\n
\r\n\r\n \r\n \r\n {t('departmentTurnoverRate') || '各部门流动率'}\r\n \r\n \r\n \r\n \r\n \r\n {t('departmentName') || '部门'}\r\n {t('totalEmployees') || '总人数'}\r\n {t('resigned') || '已离职'}\r\n {t('turnoverRate') || '流动率'}\r\n \r\n \r\n \r\n {data?.byDepartment.map((dept, i) => (\r\n \r\n {dept.dept_name}\r\n {dept.total}\r\n {dept.resigned}\r\n {dept.rate}%\r\n \r\n ))}\r\n \r\n
\r\n
\r\n
\r\n
\r\n
\r\n
\r\n );\r\n}\r\n","usedDeprecatedRules":[]},{"filePath":"D:\\dcprint\\erp-project\\src\\app\\[locale]\\hr\\salary\\bank-report\\page.tsx","messages":[{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"张三\"。建议使用: tc('text_glwp')","line":36,"column":19,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":36,"endColumn":23},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"李四\"。建议使用: tc('text_i1ql')","line":37,"column":19,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":37,"endColumn":23},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"王五\"。建议使用: tc('text_k31l')","line":38,"column":19,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":38,"endColumn":23},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"赵六\"。建议使用: tc('text_oiag')","line":39,"column":19,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":39,"endColumn":23},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"孙七\"。建议使用: tc('text_fyru')","line":40,"column":19,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":40,"endColumn":23},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"周八\"。建议使用: tc('text_esxv')","line":41,"column":19,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":41,"endColumn":23},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":57,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":57,"endColumn":36},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .code on an `any` value.","line":58,"column":16,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":58,"endColumn":20},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":59,"column":15,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":59,"endColumn":82},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":59,"column":41,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":59,"endColumn":45},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":59,"column":54,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":59,"endColumn":58},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":59,"column":66,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":59,"endColumn":70},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":60,"column":15,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":64,"endColumn":12},{"ruleId":"@typescript-eslint/no-unsafe-call","severity":1,"message":"Unsafe call of an `any` typed value.","line":60,"column":24,"nodeType":"MemberExpression","messageId":"unsafeCall","endLine":60,"endColumn":32},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .map on an `any` value.","line":60,"column":29,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":60,"endColumn":32},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction`.","line":65,"column":17,"nodeType":"Identifier","messageId":"unsafeArgument","endLine":65,"endColumn":23},{"ruleId":"react-hooks/set-state-in-effect","severity":1,"message":"Error: Calling setState synchronously within an effect can trigger cascading renders\n\nEffects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following:\n* Update external systems with the latest state from React.\n* Subscribe for updates from some external system, calling setState in a callback function when external state changes.\n\nCalling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect).\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\hr\\salary\\bank-report\\page.tsx:77:5\n 75 |\n 76 | useEffect(() => {\n> 77 | fetchData();\n | ^^^^^^^^^ Avoid calling setState() directly within an effect\n 78 | }, []);\n 79 |\n 80 | const totalAmount = data.reduce((s, item) => s + item.netPay, 0);","line":77,"column":5,"nodeType":null,"endLine":77,"endColumn":14},{"ruleId":"react-hooks/exhaustive-deps","severity":1,"message":"React Hook useEffect has a missing dependency: 'fetchData'. Either include it or remove the dependency array.","line":78,"column":6,"nodeType":"ArrayExpression","endLine":78,"endColumn":8,"suggestions":[{"desc":"Update the dependencies array to be: [fetchData]","fix":{"range":[2503,2505],"text":"[fetchData]"}}]}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":18,"fixableErrorCount":0,"fixableWarningCount":0,"source":"'use client';\n\nimport { authFetch } from '@/lib/auth-fetch';\nimport { useState, useEffect } from 'react';\nimport { toast } from 'sonner';\nimport { MainLayout } from '@/components/layout';\nimport { Card, CardContent, CardHeader } from '@/components/ui/card';\nimport {\n Table,\n TableBody,\n TableCell,\n TableHead,\n TableHeader,\n TableRow,\n} from '@/components/ui/table';\nimport { Button } from '@/components/ui/button';\nimport { Input } from '@/components/ui/input';\nimport { Label } from '@/components/ui/label';\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@/components/ui/select';\nimport { Download, FileText } from 'lucide-react';\nimport { useTranslations } from 'next-intl';\n\ninterface BankItem {\n employeeName: string;\n bankCardNo: string;\n netPay: number;\n}\n\nconst mockBankData: BankItem[] = [\n { employeeName: '张三', bankCardNo: '6222****1234', netPay: 10800 },\n { employeeName: '李四', bankCardNo: '6222****5678', netPay: 6700 },\n { employeeName: '王五', bankCardNo: '6222****9012', netPay: 6890 },\n { employeeName: '赵六', bankCardNo: '6222****3456', netPay: 13300 },\n { employeeName: '孙七', bankCardNo: '6222****7890', netPay: 13420 },\n { employeeName: '周八', bankCardNo: '6222****2345', netPay: 7470 },\n];\n\nexport default function BankReportPage() {\n const t = useTranslations('Hr');\n const tc = useTranslations('Common');\n\n const [data, setData] = useState([]);\n const [loading, setLoading] = useState(false);\n const [month, setMonth] = useState(new Date().toISOString().slice(0, 7));\n const [statusFilter, setStatusFilter] = useState('confirmed');\n\n const fetchData = async () => {\n setLoading(true);\n try {\n const res = await authFetch(`/api/hr/salary/calculate?status=${statusFilter}&month=${month}`);\n const json = await res.json();\n if (json.code === 200) {\n const list = Array.isArray(json.data) ? json.data : json.data?.list || [];\n const mapped = list.map((item: Record) => ({\n employeeName: item.employeeName || item.employee_name || item.name,\n bankCardNo: item.bankCardNo || item.bank_card_no || item.bankCard || '-',\n netPay: item.netPay ?? item.actualSalary ?? item.actual_salary ?? item.netPay ?? 0,\n }));\n setData(mapped);\n } else {\n setData(mockBankData);\n }\n } catch {\n setData(mockBankData);\n } finally {\n setLoading(false);\n }\n };\n\n useEffect(() => {\n fetchData();\n }, []);\n\n const totalAmount = data.reduce((s, item) => s + item.netPay, 0);\n\n const generateBankFile = () => {\n const header = [t('employeeName'), t('bankCardNo'), t('netPay')];\n const rows = data.map((item) => [item.employeeName, item.bankCardNo, item.netPay.toFixed(2)]);\n const csv = [header.join(','), ...rows.map((r) => r.join(','))].join('\\n');\n const blob = new Blob(['\\ufeff' + csv], { type: 'text/csv;charset=utf-8;' });\n const link = document.createElement('a');\n link.href = URL.createObjectURL(blob);\n link.download = `${t('bankReport')}_${month}.csv`;\n document.body.appendChild(link);\n link.click();\n document.body.removeChild(link);\n toast.success(t('bankFileGenerated'));\n };\n\n return (\n \n
\n
\n
\n \n

{t('bankReport')}

\n
\n \n
\n\n \n \n
\n
\n \n setMonth(e.target.value)}\n />\n
\n
\n \n \n
\n \n
\n
\n
\n\n \n \n \n \n \n {t('employeeName')}\n {t('bankCardNo')}\n {t('netPay')}\n \n \n \n {data.map((item, idx) => (\n \n {item.employeeName}\n {item.bankCardNo}\n \n ¥{item.netPay.toLocaleString()}\n \n \n ))}\n {data.length > 0 && (\n \n \n {tc('total')}\n \n \n ¥{totalAmount.toLocaleString()}\n \n \n )}\n {data.length === 0 && (\n \n \n {loading ? tc('loading') : t('noData')}\n \n \n )}\n \n
\n
\n
\n
\n
\n );\n}\n","usedDeprecatedRules":[]},{"filePath":"D:\\dcprint\\erp-project\\src\\app\\[locale]\\hr\\salary\\calculate\\page.tsx","messages":[{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":61,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":61,"endColumn":36},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .code on an `any` value.","line":62,"column":16,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":62,"endColumn":20},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction`.","line":63,"column":19,"nodeType":"MemberExpression","messageId":"unsafeArgument","endLine":63,"endColumn":28},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":63,"column":24,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":63,"endColumn":28},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `string | number | bigint | boolean | ReactElement> | Iterable | ... 4 more ... | undefined`.","line":66,"column":21,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":66,"endColumn":59},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .message on an `any` value.","line":66,"column":26,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":66,"endColumn":33},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":82,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":82,"endColumn":36},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .code on an `any` value.","line":83,"column":16,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":83,"endColumn":20},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction`.","line":84,"column":25,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":84,"endColumn":48},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":84,"column":30,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":84,"endColumn":34},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `string | number | bigint | boolean | ReactElement> | Iterable | ... 4 more ... | undefined`.","line":87,"column":21,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":87,"endColumn":64},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .message on an `any` value.","line":87,"column":26,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":87,"endColumn":33},{"ruleId":"react-hooks/static-components","severity":1,"message":"Error: Cannot create components during render\n\nComponents created during render will reset their state each time they are created. Declare components outside of render.\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\hr\\salary\\calculate\\page.tsx:185:35\n 183 |\n 184 | {/* 单员工结果 */}\n> 185 | {result && !batchMode && }\n | ^^^^^^^^^^^^^^^^ This component is created during render\n 186 |\n 187 | {/* 批量结果 */}\n 188 | {batchResults.length > 0 && batchMode && (\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\hr\\salary\\calculate\\page.tsx:95:28\n 93 | };\n 94 |\n> 95 | const SalaryDetailCard = ({ data }: { data: SalaryResult }) => (\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n> 96 | \n | ^^^^^^^^^^\n> 97 | \n …\n | ^^^^^^^^^^\n> 138 | \n | ^^^^^^^^^^\n> 139 | );\n | ^^^^ The component is created during render here\n 140 |\n 141 | return (\n 142 | ","line":185,"column":35,"nodeType":null,"endLine":185,"endColumn":51}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":13,"fixableErrorCount":0,"fixableWarningCount":0,"source":"'use client';\r\n\r\nimport { useState, useEffect } from 'react';\r\nimport { MainLayout } from '@/components/layout';\r\nimport { Button } from '@/components/ui/button';\r\nimport { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';\r\nimport { Input } from '@/components/ui/input';\r\nimport { Label } from '@/components/ui/label';\r\nimport { Badge } from '@/components/ui/badge';\r\nimport {\r\n Table, TableBody, TableCell, TableHead, TableHeader, TableRow,\r\n} from '@/components/ui/table';\r\nimport { Separator } from '@/components/ui/separator';\r\nimport { Calculator, Loader2 } from 'lucide-react';\r\nimport { useTranslations } from 'next-intl';\r\nimport { toast } from 'sonner';\r\nimport { authFetch } from '@/lib/auth-fetch';\r\n\r\ninterface SalaryResult {\r\n employeeId: number;\r\n employeeName: string;\r\n month: string;\r\n baseSalary: number;\r\n pieceSalary: number;\r\n overtimeSalary: number;\r\n performanceSalary: number;\r\n allowances: number;\r\n socialInsurancePersonal: number;\r\n housingFundPersonal: number;\r\n individualTax: number;\r\n attendanceDeduction: number;\r\n otherDeduction: number;\r\n grossPay: number;\r\n totalDeduction: number;\r\n netPay: number;\r\n status: string;\r\n}\r\n\r\nexport default function SalaryCalculatePage() {\r\n const t = useTranslations('Hr');\r\n const _tc = useTranslations('Common');\r\n const [month, setMonth] = useState(new Date().toISOString().slice(0, 7));\r\n const [employeeId, setEmployeeId] = useState('');\r\n const [result, setResult] = useState(null);\r\n const [loading, setLoading] = useState(false);\r\n const [batchMode, setBatchMode] = useState(false);\r\n const [batchResults, setBatchResults] = useState([]);\r\n\r\n useEffect(() => {\r\n // initialized - month defaults\r\n }, []);\r\n\r\n const handleCalculate = async () => {\r\n if (!employeeId || !month) { toast.error(t('fillEmployeeIdAndMonth')); return; }\r\n setLoading(true);\r\n try {\r\n const res = await authFetch('/api/hr/salary/calculate', {\r\n method: 'POST',\r\n body: JSON.stringify({ employeeId: parseInt(employeeId), month }),\r\n });\r\n const json = await res.json();\r\n if (json.code === 200) {\r\n setResult(json.data);\r\n toast.success(t('calculationCompleted'));\r\n } else {\r\n toast.error(json.message || t('calculationFailed'));\r\n }\r\n } catch (_error) {\r\n toast.error(t('calculationRequestFailed'));\r\n }\r\n setLoading(false);\r\n };\r\n\r\n const handleBatchCalculate = async () => {\r\n if (!month) { toast.error(t('fillMonth')); return; }\r\n setLoading(true);\r\n try {\r\n const res = await authFetch('/api/hr/salary/calculate', {\r\n method: 'POST',\r\n body: JSON.stringify({ month, batch: true }),\r\n });\r\n const json = await res.json();\r\n if (json.code === 200) {\r\n setBatchResults(json.data.results || []);\r\n toast.success(t('batchCalculationCompleted'));\r\n } else {\r\n toast.error(json.message || t('batchCalculationFailed'));\r\n }\r\n } catch (_error) {\r\n toast.error(t('batchCalculationRequestFailed'));\r\n }\r\n setLoading(false);\r\n };\r\n\r\n const SalaryDetailCard = ({ data }: { data: SalaryResult }) => (\r\n \r\n \r\n \r\n {data.employeeName} - {data.month}\r\n \r\n {data.status === 'confirmed' ? t('confirmed') : t('draft')}\r\n \r\n \r\n \r\n \r\n
\r\n

{t('grossPay')}

\r\n
\r\n
{t('baseSalary')}{data.baseSalary.toFixed(2)}
\r\n
{t('pieceSalary')}{data.pieceSalary.toFixed(2)}
\r\n
{t('overtimeSalary')}{data.overtimeSalary.toFixed(2)}
\r\n
{t('performanceBonus')}{data.performanceSalary.toFixed(2)}
\r\n
{t('allowances')}{data.allowances.toFixed(2)}
\r\n
\r\n \r\n
\r\n {t('grossPayTotal')}{data.grossPay.toFixed(2)}\r\n
\r\n
\r\n
\r\n

{t('deductions')}

\r\n
\r\n
{t('socialInsurancePersonal')}{data.socialInsurancePersonal.toFixed(2)}
\r\n
{t('housingFundPersonal')}{data.housingFundPersonal.toFixed(2)}
\r\n
{t('individualTax')}{data.individualTax.toFixed(2)}
\r\n
{t('attendanceDeduction')}{data.attendanceDeduction.toFixed(2)}
\r\n
\r\n \r\n
\r\n {t('deductionTotal')}{data.totalDeduction.toFixed(2)}\r\n
\r\n
\r\n \r\n
\r\n {t('netPay')}{data.netPay.toFixed(2)}\r\n
\r\n
\r\n
\r\n );\r\n\r\n return (\r\n \r\n
\r\n
\r\n \r\n

{t('salaryCalculation')}

\r\n
\r\n\r\n {/* 计算控件 */}\r\n \r\n \r\n \r\n setBatchMode(!batchMode)}>\r\n {batchMode ? t('batchMode') : t('singleMode')}\r\n \r\n \r\n \r\n \r\n
\r\n \r\n setMonth(e.target.value)} />\r\n
\r\n {!batchMode && (\r\n
\r\n \r\n setEmployeeId(e.target.value)}\r\n />\r\n
\r\n )}\r\n \r\n {loading ? : }\r\n {loading ? t('calculating') : t('executeCalculation')}\r\n \r\n
\r\n
\r\n\r\n {/* 单员工结果 */}\r\n {result && !batchMode && }\r\n\r\n {/* 批量结果 */}\r\n {batchResults.length > 0 && batchMode && (\r\n \r\n \r\n \r\n {t('batchCalculationResults', { count: batchResults.length })}\r\n \r\n \r\n \r\n \r\n \r\n \r\n {t('employee')}\r\n {t('baseSalary')}\r\n {t('pieceSalary')}\r\n {t('grossPayTotal')}\r\n {t('deductionTotal')}\r\n {t('netPay')}\r\n {t('status')}\r\n \r\n \r\n \r\n {batchResults.map((r, i) => (\r\n \r\n {r.employeeName}\r\n {r.baseSalary.toFixed(2)}\r\n {r.pieceSalary.toFixed(2)}\r\n {r.grossPay.toFixed(2)}\r\n {r.totalDeduction.toFixed(2)}\r\n {r.netPay.toFixed(2)}\r\n \r\n \r\n {r.status === 'confirmed' ? t('confirmed') : t('draft')}\r\n \r\n \r\n \r\n ))}\r\n \r\n
\r\n
\r\n
\r\n )}\r\n
\r\n
\r\n );\r\n}\r\n","usedDeprecatedRules":[]},{"filePath":"D:\\dcprint\\erp-project\\src\\app\\[locale]\\hr\\salary\\page.tsx","messages":[{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":157,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":157,"endColumn":48},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":158,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":158,"endColumn":44},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":160,"column":22,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":160,"endColumn":29},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":162,"column":15,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":162,"endColumn":40},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":162,"column":36,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":162,"endColumn":40},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":163,"column":15,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":163,"endColumn":79},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .list on an `any` value.","line":163,"column":69,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":163,"endColumn":73},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":164,"column":15,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":187,"endColumn":12},{"ruleId":"@typescript-eslint/no-unsafe-call","severity":1,"message":"Unsafe call of an `any` typed value.","line":164,"column":22,"nodeType":"MemberExpression","messageId":"unsafeCall","endLine":164,"endColumn":33},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .map on an `any` value.","line":164,"column":30,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":164,"endColumn":33},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":165,"column":11,"nodeType":"Property","messageId":"anyAssignment","endLine":165,"endColumn":22},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .id on an `any` value.","line":165,"column":20,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":165,"endColumn":22},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":166,"column":11,"nodeType":"Property","messageId":"anyAssignment","endLine":166,"endColumn":59},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .employeeNo on an `any` value.","line":166,"column":29,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":166,"endColumn":39},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .employee_no on an `any` value.","line":166,"column":48,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":166,"endColumn":59},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":167,"column":11,"nodeType":"Property","messageId":"anyAssignment","endLine":167,"endColumn":26},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .name on an `any` value.","line":167,"column":22,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":167,"endColumn":26},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":168,"column":11,"nodeType":"Property","messageId":"anyAssignment","endLine":168,"endColumn":30},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .gender on an `any` value.","line":168,"column":24,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":168,"endColumn":30},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":169,"column":11,"nodeType":"Property","messageId":"anyAssignment","endLine":169,"endColumn":47},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .deptId on an `any` value.","line":169,"column":25,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":169,"endColumn":31},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .dept_id on an `any` value.","line":169,"column":40,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":169,"endColumn":47},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":170,"column":11,"nodeType":"Property","messageId":"anyAssignment","endLine":170,"endColumn":53},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .deptName on an `any` value.","line":170,"column":27,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":170,"endColumn":35},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .dept_name on an `any` value.","line":170,"column":44,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":170,"endColumn":53},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":171,"column":11,"nodeType":"Property","messageId":"anyAssignment","endLine":171,"endColumn":34},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .position on an `any` value.","line":171,"column":26,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":171,"endColumn":34},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":172,"column":11,"nodeType":"Property","messageId":"anyAssignment","endLine":172,"endColumn":56},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .entryDate on an `any` value.","line":172,"column":28,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":172,"endColumn":37},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .entry_date on an `any` value.","line":172,"column":46,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":172,"endColumn":56},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":173,"column":11,"nodeType":"Property","messageId":"anyAssignment","endLine":173,"endColumn":30},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .status on an `any` value.","line":173,"column":24,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":173,"endColumn":30},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":174,"column":11,"nodeType":"Property","messageId":"anyAssignment","endLine":174,"endColumn":53},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .salaryId on an `any` value.","line":174,"column":27,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":174,"endColumn":35},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .salary_id on an `any` value.","line":174,"column":44,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":174,"endColumn":53},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":175,"column":11,"nodeType":"Property","messageId":"anyAssignment","endLine":175,"endColumn":28},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .month on an `any` value.","line":175,"column":23,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":175,"endColumn":28},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":176,"column":11,"nodeType":"Property","messageId":"anyAssignment","endLine":176,"endColumn":62},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .basicSalary on an `any` value.","line":176,"column":30,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":176,"endColumn":41},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .basic_salary on an `any` value.","line":176,"column":50,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":176,"endColumn":62},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":177,"column":11,"nodeType":"Property","messageId":"anyAssignment","endLine":177,"endColumn":80},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .positionAllowance on an `any` value.","line":177,"column":36,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":177,"endColumn":53},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .position_allowance on an `any` value.","line":177,"column":62,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":177,"endColumn":80},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":178,"column":11,"nodeType":"Property","messageId":"anyAssignment","endLine":178,"endColumn":77},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .performanceBonus on an `any` value.","line":178,"column":35,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":178,"endColumn":51},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .performance_bonus on an `any` value.","line":178,"column":60,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":178,"endColumn":77},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":179,"column":11,"nodeType":"Property","messageId":"anyAssignment","endLine":179,"endColumn":62},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .overtimePay on an `any` value.","line":179,"column":30,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":179,"endColumn":41},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .overtime_pay on an `any` value.","line":179,"column":50,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":179,"endColumn":62},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":180,"column":11,"nodeType":"Property","messageId":"anyAssignment","endLine":180,"endColumn":59},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .otherBonus on an `any` value.","line":180,"column":29,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":180,"endColumn":39},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .other_bonus on an `any` value.","line":180,"column":48,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":180,"endColumn":59},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":181,"column":11,"nodeType":"Property","messageId":"anyAssignment","endLine":181,"endColumn":71},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .socialSecurity on an `any` value.","line":181,"column":33,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":181,"endColumn":47},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .social_security on an `any` value.","line":181,"column":56,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":181,"endColumn":71},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":182,"column":11,"nodeType":"Property","messageId":"anyAssignment","endLine":182,"endColumn":62},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .housingFund on an `any` value.","line":182,"column":30,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":182,"endColumn":41},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .housing_fund on an `any` value.","line":182,"column":50,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":182,"endColumn":62},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":183,"column":11,"nodeType":"Property","messageId":"anyAssignment","endLine":183,"endColumn":62},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .personalTax on an `any` value.","line":183,"column":30,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":183,"endColumn":41},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .personal_tax on an `any` value.","line":183,"column":50,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":183,"endColumn":62},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":184,"column":11,"nodeType":"Property","messageId":"anyAssignment","endLine":184,"endColumn":71},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .otherDeduction on an `any` value.","line":184,"column":33,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":184,"endColumn":47},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .other_deduction on an `any` value.","line":184,"column":56,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":184,"endColumn":71},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":185,"column":11,"nodeType":"Property","messageId":"anyAssignment","endLine":185,"endColumn":65},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .actualSalary on an `any` value.","line":185,"column":31,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":185,"endColumn":43},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .actual_salary on an `any` value.","line":185,"column":52,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":185,"endColumn":65},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":186,"column":11,"nodeType":"Property","messageId":"anyAssignment","endLine":186,"endColumn":30},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .remark on an `any` value.","line":186,"column":24,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":186,"endColumn":30},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction`.","line":188,"column":21,"nodeType":"Identifier","messageId":"unsafeArgument","endLine":188,"endColumn":25},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":189,"column":15,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":192,"endColumn":10},{"ruleId":"@typescript-eslint/no-unsafe-call","severity":1,"message":"Unsafe call of an `any` typed value.","line":189,"column":29,"nodeType":"MemberExpression","messageId":"unsafeCall","endLine":189,"endColumn":40},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .reduce on an `any` value.","line":189,"column":34,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":189,"endColumn":40},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":194,"column":11,"nodeType":"Property","messageId":"anyAssignment","endLine":194,"endColumn":38},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .length on an `any` value.","line":194,"column":32,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":194,"endColumn":38},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":195,"column":11,"nodeType":"Property","messageId":"anyAssignment","endLine":195,"endColumn":37},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .length on an `any` value.","line":195,"column":31,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":195,"endColumn":37},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":196,"column":11,"nodeType":"Property","messageId":"anyAssignment","endLine":196,"endColumn":22},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .length on an `any` value.","line":197,"column":27,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":197,"endColumn":33},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .length on an `any` value.","line":197,"column":70,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":197,"endColumn":76},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .length on an `any` value.","line":199,"column":18,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":199,"endColumn":24},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe spread of an `any` type.","line":200,"column":26,"nodeType":"SpreadElement","messageId":"unsafeSpread","endLine":200,"endColumn":94},{"ruleId":"@typescript-eslint/no-unsafe-call","severity":1,"message":"Unsafe call of an `any` typed value.","line":200,"column":29,"nodeType":"MemberExpression","messageId":"unsafeCall","endLine":200,"endColumn":37},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .map on an `any` value.","line":200,"column":34,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":200,"endColumn":37},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .length on an `any` value.","line":203,"column":18,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":203,"endColumn":24},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe spread of an `any` type.","line":204,"column":26,"nodeType":"SpreadElement","messageId":"unsafeSpread","endLine":204,"endColumn":94},{"ruleId":"@typescript-eslint/no-unsafe-call","severity":1,"message":"Unsafe call of an `any` typed value.","line":204,"column":29,"nodeType":"MemberExpression","messageId":"unsafeCall","endLine":204,"endColumn":37},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .map on an `any` value.","line":204,"column":34,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":204,"endColumn":37},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":210,"column":20,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":210,"endColumn":27},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":211,"column":15,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":211,"endColumn":42},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":211,"column":38,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":211,"endColumn":42},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":212,"column":15,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":212,"endColumn":92},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .list on an `any` value.","line":212,"column":82,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":212,"endColumn":86},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction`.","line":213,"column":24,"nodeType":"Identifier","messageId":"unsafeArgument","endLine":213,"endColumn":32},{"ruleId":"react-hooks/set-state-in-effect","severity":1,"message":"Error: Calling setState synchronously within an effect can trigger cascading renders\n\nEffects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following:\n* Update external systems with the latest state from React.\n* Subscribe for updates from some external system, calling setState in a callback function when external state changes.\n\nCalling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect).\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\hr\\salary\\page.tsx:222:5\n 220 |\n 221 | useEffect(() => {\n> 222 | fetchSalaryData();\n | ^^^^^^^^^^^^^^^ Avoid calling setState() directly within an effect\n 223 | }, []);\n 224 |\n 225 | // 薪资表单","line":222,"column":5,"nodeType":null,"endLine":222,"endColumn":20},{"ruleId":"react-hooks/static-components","severity":1,"message":"Error: Cannot create components during render\n\nComponents created during render will reset their state each time they are created. Declare components outside of render.\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\hr\\salary\\page.tsx:683:20\n 681 | \n 682 | {tc('serialNo')}\n> 683 | {tc('employeeInfo')}\n | ^^^^^^^^^^^^^^ This component is created during render\n 684 | {tc('deptPosition')}\n 685 | \n 686 | {tc('baseSalary')}\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\hr\\salary\\page.tsx:326:26\n 324 | };\n 325 |\n> 326 | const SortableHeader = ({\n | ^^\n> 327 | field,\n | ^^^^^^^^^^\n> 328 | children,\n …\n | ^^^^^^^^^^\n> 351 | \n | ^^^^^^^^^^\n> 352 | );\n | ^^^^ The component is created during render here\n 353 |\n 354 | // 查看详情\n 355 | const handleViewDetail = (salary: Salary) => {","line":683,"column":20,"nodeType":null,"endLine":683,"endColumn":34},{"ruleId":"react-hooks/static-components","severity":1,"message":"Error: Cannot create components during render\n\nComponents created during render will reset their state each time they are created. Declare components outside of render.\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\hr\\salary\\page.tsx:684:20\n 682 | {tc('serialNo')}\n 683 | {tc('employeeInfo')}\n> 684 | {tc('deptPosition')}\n | ^^^^^^^^^^^^^^ This component is created during render\n 685 | \n 686 | {tc('baseSalary')}\n 687 | \n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\hr\\salary\\page.tsx:326:26\n 324 | };\n 325 |\n> 326 | const SortableHeader = ({\n | ^^\n> 327 | field,\n | ^^^^^^^^^^\n> 328 | children,\n …\n | ^^^^^^^^^^\n> 351 | \n | ^^^^^^^^^^\n> 352 | );\n | ^^^^ The component is created during render here\n 353 |\n 354 | // 查看详情\n 355 | const handleViewDetail = (salary: Salary) => {","line":684,"column":20,"nodeType":null,"endLine":684,"endColumn":34},{"ruleId":"react-hooks/static-components","severity":1,"message":"Error: Cannot create components during render\n\nComponents created during render will reset their state each time they are created. Declare components outside of render.\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\hr\\salary\\page.tsx:685:20\n 683 | {tc('employeeInfo')}\n 684 | {tc('deptPosition')}\n> 685 | \n | ^^^^^^^^^^^^^^ This component is created during render\n 686 | {tc('baseSalary')}\n 687 | \n 688 | {tc('allowanceBonus')}\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\hr\\salary\\page.tsx:326:26\n 324 | };\n 325 |\n> 326 | const SortableHeader = ({\n | ^^\n> 327 | field,\n | ^^^^^^^^^^\n> 328 | children,\n …\n | ^^^^^^^^^^\n> 351 | \n | ^^^^^^^^^^\n> 352 | );\n | ^^^^ The component is created during render here\n 353 |\n 354 | // 查看详情\n 355 | const handleViewDetail = (salary: Salary) => {","line":685,"column":20,"nodeType":null,"endLine":685,"endColumn":34},{"ruleId":"react-hooks/static-components","severity":1,"message":"Error: Cannot create components during render\n\nComponents created during render will reset their state each time they are created. Declare components outside of render.\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\hr\\salary\\page.tsx:690:20\n 688 | {tc('allowanceBonus')}\n 689 | {tc('deductionItems')}\n> 690 | \n | ^^^^^^^^^^^^^^ This component is created during render\n 691 | {tc('netSalary')}\n 692 | \n 693 | {tc('status')}\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\hr\\salary\\page.tsx:326:26\n 324 | };\n 325 |\n> 326 | const SortableHeader = ({\n | ^^\n> 327 | field,\n | ^^^^^^^^^^\n> 328 | children,\n …\n | ^^^^^^^^^^\n> 351 | \n | ^^^^^^^^^^\n> 352 | );\n | ^^^^ The component is created during render here\n 353 |\n 354 | // 查看详情\n 355 | const handleViewDetail = (salary: Salary) => {","line":690,"column":20,"nodeType":null,"endLine":690,"endColumn":34},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"薪资明细\"。建议使用: {tc('text_hg3z6a')}","line":830,"column":35,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":832,"endColumn":19},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止在JSX中硬编码中文文本: \"薪资明细\"。建议使用: {tc('text_hg3z6a')}","line":1041,"column":35,"nodeType":"JSXText","messageId":"noChineseInJSX","endLine":1043,"endColumn":19}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":101,"fixableErrorCount":0,"fixableWarningCount":0,"source":"'use client';\n\nimport { authFetch } from '@/lib/auth-fetch';\nimport { useState, useEffect, useRef } from 'react';\nimport { toast } from 'sonner';\nimport { MainLayout } from '@/components/layout';\nimport { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';\nimport {\n Table,\n TableBody,\n TableCell,\n TableHead,\n TableHeader,\n TableRow,\n} from '@/components/ui/table';\nimport {\n Dialog,\n DialogContent,\n DialogDescription,\n DialogHeader,\n DialogTitle,\n} from '@/components/ui/dialog';\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuTrigger,\n} from '@/components/ui/dropdown-menu';\nimport { Button } from '@/components/ui/button';\nimport { Input } from '@/components/ui/input';\nimport { Label } from '@/components/ui/label';\nimport { Badge } from '@/components/ui/badge';\nimport { Textarea } from '@/components/ui/textarea';\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@/components/ui/select';\nimport {\n Search,\n MoreHorizontal,\n Eye,\n Edit,\n Trash2,\n Calculator,\n TrendingUp,\n Calendar,\n FileText,\n Printer,\n Users,\n DollarSign,\n CreditCard,\n Wallet,\n PieChart,\n ChevronLeft,\n ChevronRight,\n ArrowUpDown,\n ArrowUp,\n ArrowDown,\n} from 'lucide-react';\nimport { Checkbox } from '@/components/ui/checkbox';\nimport { format } from 'date-fns';\nimport { useTranslations } from 'next-intl';\nimport { GlobalExportToolbar } from '@/components/ui/global-export-toolbar';\n\n// 薪资数据类型\ninterface Salary {\n id: number;\n employee_no: string;\n name: string;\n gender: number;\n dept_id: number;\n dept_name: string;\n position: string;\n entry_date: string;\n status: number;\n salary_id?: number;\n month?: string;\n basic_salary?: number;\n position_allowance?: number;\n performance_bonus?: number;\n overtime_pay?: number;\n other_bonus?: number;\n social_security?: number;\n housing_fund?: number;\n personal_tax?: number;\n other_deduction?: number;\n actual_salary?: number;\n remark?: string;\n}\n\n// 统计数据类型\ninterface SalaryStats {\n totalEmployees: number;\n paidEmployees: number;\n totalSalary: number;\n avgSalary: number;\n maxSalary: number;\n minSalary: number;\n deptStats: {\n dept_name: string;\n count: number;\n total: number;\n }[];\n}\n\n// 部门类型\ninterface Department {\n id: number;\n dept_name: string;\n dept_code: string;\n parent_id: number;\n}\n\nexport default function HRSalaryPage() {\n // 翻译钩子\n const t = useTranslations('Hr');\n const tc = useTranslations('Common');\n\n const getGenderText = (gender: number) => {\n return gender === 1 ? tc('maleShort') : gender === 2 ? tc('femaleShort') : tc('unknown');\n };\n\n const [salaries, setSalaries] = useState([]);\n const [departments, setDepartments] = useState([]);\n const [stats, setStats] = useState({\n totalEmployees: 0,\n paidEmployees: 0,\n totalSalary: 0,\n avgSalary: 0,\n maxSalary: 0,\n minSalary: 0,\n deptStats: [],\n });\n const [currentMonth, setCurrentMonth] = useState(format(new Date(), 'yyyy-MM'));\n const [selectedDept, setSelectedDept] = useState('all');\n const [searchQuery, setSearchQuery] = useState('');\n const [isEditOpen, setIsEditOpen] = useState(false);\n const [isDetailOpen, setIsDetailOpen] = useState(false);\n const [isReportOpen, setIsReportOpen] = useState(false);\n const [selectedSalary, setSelectedSalary] = useState(null);\n const [loading, setLoading] = useState(false);\n const [selectedIds, setSelectedIds] = useState([]);\n const [sortField, setSortField] = useState('');\n const [sortDirection, setSortDirection] = useState<'asc' | 'desc'>('asc');\n const printRef = useRef(null);\n\n const fetchSalaryData = async () => {\n try {\n setLoading(true);\n const [salaryRes, deptRes] = await Promise.all([\n authFetch('/api/hr/salary'),\n authFetch('/api/organization/department'),\n ]);\n const salaryData = await salaryRes.json();\n const deptData = await deptRes.json();\n\n if (salaryData.success) {\n // 统一处理API返回的数据结构\n const rawData = salaryData.data;\n const rawList = Array.isArray(rawData) ? rawData : rawData?.list || [];\n const list = rawList.map((item: Loose) => ({\n id: item.id,\n employee_no: item.employeeNo || item.employee_no,\n name: item.name,\n gender: item.gender,\n dept_id: item.deptId || item.dept_id,\n dept_name: item.deptName || item.dept_name,\n position: item.position,\n entry_date: item.entryDate || item.entry_date,\n status: item.status,\n salary_id: item.salaryId || item.salary_id,\n month: item.month,\n basic_salary: item.basicSalary || item.basic_salary,\n position_allowance: item.positionAllowance || item.position_allowance,\n performance_bonus: item.performanceBonus || item.performance_bonus,\n overtime_pay: item.overtimePay || item.overtime_pay,\n other_bonus: item.otherBonus || item.other_bonus,\n social_security: item.socialSecurity || item.social_security,\n housing_fund: item.housingFund || item.housing_fund,\n personal_tax: item.personalTax || item.personal_tax,\n other_deduction: item.otherDeduction || item.other_deduction,\n actual_salary: item.actualSalary || item.actual_salary,\n remark: item.remark,\n }));\n setSalaries(list);\n const totalSalary = list.reduce(\n (sum: number, s: Salary) => sum + (parseFloat(String(s.actual_salary)) || 0),\n 0\n );\n setStats({\n totalEmployees: list.length,\n paidEmployees: list.length,\n totalSalary,\n avgSalary: list.length > 0 ? Math.round(totalSalary / list.length) : 0,\n maxSalary:\n list.length > 0\n ? Math.max(...list.map((s: Salary) => parseFloat(String(s.actual_salary)) || 0))\n : 0,\n minSalary:\n list.length > 0\n ? Math.min(...list.map((s: Salary) => parseFloat(String(s.actual_salary)) || 0))\n : 0,\n deptStats: [],\n });\n }\n\n if (deptData.success) {\n const rawDeptData = deptData.data;\n const deptList = Array.isArray(rawDeptData) ? rawDeptData : rawDeptData?.list || [];\n setDepartments(deptList);\n }\n } catch {\n } finally {\n setLoading(false);\n }\n };\n\n useEffect(() => {\n fetchSalaryData();\n }, []);\n\n // 薪资表单\n const [salaryForm, setSalaryForm] = useState({\n basicSalary: 0,\n positionAllowance: 0,\n performanceBonus: 0,\n overtimePay: 0,\n otherBonus: 0,\n socialSecurity: 0,\n housingFund: 0,\n personalTax: 0,\n otherDeduction: 0,\n remark: '',\n });\n\n // 计算实发工资\n const calculateActualSalary = (form: typeof salaryForm) => {\n const income =\n form.basicSalary +\n form.positionAllowance +\n form.performanceBonus +\n form.overtimePay +\n form.otherBonus;\n const deduction =\n form.socialSecurity + form.housingFund + form.personalTax + form.otherDeduction;\n return income - deduction;\n };\n\n // 筛选薪资\n const filteredSalaries = salaries.filter((salary) => {\n if (selectedDept !== 'all' && salary.dept_id !== parseInt(selectedDept)) {\n return false;\n }\n if (searchQuery) {\n const query = searchQuery.toLowerCase();\n return (\n salary.name.toLowerCase().includes(query) ||\n salary.employee_no.toLowerCase().includes(query) ||\n salary.position.toLowerCase().includes(query)\n );\n }\n return true;\n });\n\n const sortedSalaries = (() => {\n if (!sortField) return filteredSalaries;\n return [...filteredSalaries].sort((a, b) => {\n let aVal: Loose, bVal: Loose;\n switch (sortField) {\n case 'name':\n aVal = a.name;\n bVal = b.name;\n break;\n case 'employee_no':\n aVal = a.employee_no;\n bVal = b.employee_no;\n break;\n case 'dept_name':\n aVal = a.dept_name;\n bVal = b.dept_name;\n break;\n case 'basic_salary':\n aVal = a.basic_salary || 0;\n bVal = b.basic_salary || 0;\n break;\n case 'actual_salary':\n aVal = a.actual_salary || 0;\n bVal = b.actual_salary || 0;\n break;\n default:\n return 0;\n }\n if (typeof aVal === 'number' && typeof bVal === 'number') {\n return sortDirection === 'asc' ? aVal - bVal : bVal - aVal;\n }\n return sortDirection === 'asc'\n ? String(aVal).localeCompare(String(bVal))\n : String(bVal).localeCompare(String(aVal));\n });\n })();\n\n const handleSort = (field: string) => {\n if (sortField === field) {\n setSortDirection((prev) => (prev === 'asc' ? 'desc' : 'asc'));\n } else {\n setSortField(field);\n setSortDirection('asc');\n }\n };\n\n const toggleSelectAll = () => {\n if (selectedIds.length === filteredSalaries.length) {\n setSelectedIds([]);\n } else {\n setSelectedIds(filteredSalaries.map((s) => s.id));\n }\n };\n\n const toggleSelect = (id: number) => {\n setSelectedIds((prev) => (prev.includes(id) ? prev.filter((i) => i !== id) : [...prev, id]));\n };\n\n const SortableHeader = ({\n field,\n children,\n className = '',\n }: {\n field: string;\n children: React.ReactNode;\n className?: string;\n }) => (\n handleSort(field)}\n >\n
\n {children}\n {sortField === field ? (\n sortDirection === 'asc' ? (\n \n ) : (\n \n )\n ) : (\n \n )}\n
\n \n );\n\n // 查看详情\n const handleViewDetail = (salary: Salary) => {\n setSelectedSalary(salary);\n setIsDetailOpen(true);\n };\n\n // 编辑薪资\n const handleEdit = (salary: Salary) => {\n setSelectedSalary(salary);\n setSalaryForm({\n basicSalary: salary.basic_salary || 0,\n positionAllowance: salary.position_allowance || 0,\n performanceBonus: salary.performance_bonus || 0,\n overtimePay: salary.overtime_pay || 0,\n otherBonus: salary.other_bonus || 0,\n socialSecurity: salary.social_security || 0,\n housingFund: salary.housing_fund || 0,\n personalTax: salary.personal_tax || 0,\n otherDeduction: salary.other_deduction || 0,\n remark: salary.remark || '',\n });\n setIsEditOpen(true);\n };\n\n // 保存薪资\n const handleSave = async () => {\n if (!selectedSalary) return;\n setLoading(true);\n try {\n const actualSalary = calculateActualSalary(salaryForm);\n\n // 更新本地数据\n setSalaries(\n salaries.map((s) =>\n s.id === selectedSalary.id\n ? {\n ...s,\n basic_salary: salaryForm.basicSalary,\n position_allowance: salaryForm.positionAllowance,\n performance_bonus: salaryForm.performanceBonus,\n overtime_pay: salaryForm.overtimePay,\n other_bonus: salaryForm.otherBonus,\n social_security: salaryForm.socialSecurity,\n housing_fund: salaryForm.housingFund,\n personal_tax: salaryForm.personalTax,\n other_deduction: salaryForm.otherDeduction,\n actual_salary: actualSalary,\n remark: salaryForm.remark,\n }\n : s\n )\n );\n\n setIsEditOpen(false);\n toast.success(t('salarySaveSuccess'));\n } catch {\n toast.error(t('salarySaveFailed'));\n } finally {\n setLoading(false);\n }\n };\n\n // 删除薪资\n const handleDelete = (salary: Salary) => {\n if (confirm(t('confirmDeleteSalary', { name: salary.name }))) {\n setSalaries(salaries.filter((s) => s.id !== salary.id));\n toast.success(t('salaryDeleteSuccess'));\n }\n };\n\n // 生成报告\n const handleGenerateReport = () => {\n setIsReportOpen(true);\n };\n\n // 打印\n const handlePrint = () => {\n const printWindow = window.open('', '_blank');\n if (printWindow && printRef.current) {\n const printContent = printRef.current.innerHTML;\n printWindow.document.write(`\n \n \n ${t('salaryReport')}\n \n \n \n ${printContent}\n \n \n `);\n printWindow.document.close();\n printWindow.print();\n }\n };\n\n // 切换月份\n const changeMonth = (offset: number) => {\n const date = new Date(currentMonth + '-01');\n date.setMonth(date.getMonth() + offset);\n setCurrentMonth(format(date, 'yyyy-MM'));\n };\n\n return (\n \n
\n {/* 统计卡片 */}\n
\n \n \n {tc('totalEmployees')}\n \n \n \n
{stats.totalEmployees}
\n
\n
\n\n \n \n {tc('paidEmployees')}\n \n \n \n
{stats.paidEmployees}
\n
\n
\n\n \n \n {tc('totalSalary')}\n \n \n \n
¥{stats.totalSalary.toLocaleString()}
\n
\n
\n\n \n \n {tc('avgSalary')}\n \n \n \n
¥{stats.avgSalary.toLocaleString()}
\n
\n
\n\n \n \n {tc('maxSalary')}\n \n \n \n
¥{stats.maxSalary.toLocaleString()}
\n
\n
\n\n \n \n {tc('minSalary')}\n \n \n \n
¥{stats.minSalary.toLocaleString()}
\n
\n
\n
\n\n {/* 工具栏 */}\n \n \n
\n
\n {/* 月份选择 */}\n
\n \n
\n \n {currentMonth}\n
\n \n
\n\n {/* 部门筛选 */}\n \n\n {/* 搜索 */}\n
\n \n setSearchQuery(e.target.value)}\n />\n
\n
\n\n
\n \n \n Number(v || 0),\n },\n {\n key: 'position_allowance',\n label: t('positionAllowance'),\n width: 10,\n formatter: (v) => Number(v || 0),\n },\n {\n key: 'performance_bonus',\n label: t('performanceBonus'),\n width: 10,\n formatter: (v) => Number(v || 0),\n },\n {\n key: 'overtime_pay',\n label: t('overtimePay'),\n width: 10,\n formatter: (v) => Number(v || 0),\n },\n {\n key: 'other_bonus',\n label: t('otherBonus'),\n width: 10,\n formatter: (v) => Number(v || 0),\n },\n {\n key: 'social_security',\n label: t('socialSecurity'),\n width: 10,\n formatter: (v) => Number(v || 0),\n },\n {\n key: 'housing_fund',\n label: t('housingFund'),\n width: 10,\n formatter: (v) => Number(v || 0),\n },\n {\n key: 'personal_tax',\n label: t('personalTax'),\n width: 10,\n formatter: (v) => Number(v || 0),\n },\n {\n key: 'other_deduction',\n label: t('otherDeduction'),\n width: 10,\n formatter: (v) => Number(v || 0),\n },\n {\n key: 'actual_salary',\n label: t('actualSalary'),\n width: 12,\n formatter: (v) => Number(v || 0),\n },\n { key: 'remark', label: tc('remark'), width: 15 },\n ]}\n data={\n selectedIds.length > 0\n ? filteredSalaries.filter((s) => selectedIds.includes(s.id))\n : filteredSalaries\n }\n />\n
\n
\n
\n
\n\n {/* 薪资列表 */}\n \n \n \n \n \n \n 0\n }\n onCheckedChange={toggleSelectAll}\n />\n \n {tc('serialNo')}\n {tc('employeeInfo')}\n {tc('deptPosition')}\n \n {tc('baseSalary')}\n \n {tc('allowanceBonus')}\n {tc('deductionItems')}\n \n {tc('netSalary')}\n \n {tc('status')}\n {tc('actions')}\n \n \n \n {sortedSalaries.map((salary, index) => (\n \n \n toggleSelect(salary.id)}\n />\n \n {index + 1}\n \n
\n {salary.name}\n {salary.employee_no}\n \n {getGenderText(salary.gender)}\n \n
\n
\n \n
\n {salary.dept_name}\n {salary.position}\n
\n
\n \n ¥{(salary.basic_salary || 0).toLocaleString()}\n \n \n
\n \n {tc('positionAllowanceShort')}\n {(salary.position_allowance || 0).toLocaleString()}\n \n \n {tc('performanceBonusShort')}\n {(salary.performance_bonus || 0).toLocaleString()}\n \n \n {tc('overtimePayShort')}\n {(salary.overtime_pay || 0).toLocaleString()}\n \n
\n
\n \n
\n \n {tc('socialSecurityShort')}\n {(salary.social_security || 0).toLocaleString()}\n \n \n {tc('housingFundShort')}\n {(salary.housing_fund || 0).toLocaleString()}\n \n \n {tc('personalTaxShort')}\n {(salary.personal_tax || 0).toLocaleString()}\n \n
\n
\n \n \n ¥{(salary.actual_salary || 0).toLocaleString()}\n \n \n \n {salary.actual_salary ? (\n \n {tc('salaryStatusPaid')}\n \n ) : (\n \n {tc('salaryStatusPending')}\n \n )}\n \n \n
\n handleViewDetail(salary)}\n >\n \n \n \n \n \n \n \n \n handleViewDetail(salary)}>\n \n {tc('view')}\n \n handleEdit(salary)}>\n \n {tc('edit')}\n \n handleDelete(salary)}\n className=\"text-red-600\"\n >\n \n {tc('delete')}\n \n \n \n
\n
\n
\n ))}\n
\n
\n
\n
\n\n {/* 编辑对话框 */}\n \n \n {selectedSalary && (\n <>\n \n \n \n {tc('editSalaryTitle')}\n {selectedSalary.name}\n \n \n {currentMonth}\n 薪资明细\n \n \n\n
\n {/* 员工信息 */}\n
\n
\n
\n {tc('employeeNoLabelShort')}\n {selectedSalary.employee_no}\n
\n
\n {tc('employeeNameLabel')}\n {selectedSalary.name}\n
\n
\n {tc('deptLabel')}\n {selectedSalary.dept_name}\n
\n
\n {tc('positionLabel')}\n {selectedSalary.position}\n
\n
\n
\n\n {/* 收入项目 */}\n
\n

\n {tc('incomeItems')}\n

\n
\n
\n \n \n setSalaryForm({\n ...salaryForm,\n basicSalary: parseFloat(e.target.value) || 0,\n })\n }\n />\n
\n
\n \n \n setSalaryForm({\n ...salaryForm,\n positionAllowance: parseFloat(e.target.value) || 0,\n })\n }\n />\n
\n
\n \n \n setSalaryForm({\n ...salaryForm,\n performanceBonus: parseFloat(e.target.value) || 0,\n })\n }\n />\n
\n
\n \n \n setSalaryForm({\n ...salaryForm,\n overtimePay: parseFloat(e.target.value) || 0,\n })\n }\n />\n
\n
\n \n \n setSalaryForm({\n ...salaryForm,\n otherBonus: parseFloat(e.target.value) || 0,\n })\n }\n />\n
\n
\n
\n\n {/* 扣款项目 */}\n
\n

\n {tc('deductionItems')}\n

\n
\n
\n \n \n setSalaryForm({\n ...salaryForm,\n socialSecurity: parseFloat(e.target.value) || 0,\n })\n }\n />\n
\n
\n \n \n setSalaryForm({\n ...salaryForm,\n housingFund: parseFloat(e.target.value) || 0,\n })\n }\n />\n
\n
\n \n \n setSalaryForm({\n ...salaryForm,\n personalTax: parseFloat(e.target.value) || 0,\n })\n }\n />\n
\n
\n \n \n setSalaryForm({\n ...salaryForm,\n otherDeduction: parseFloat(e.target.value) || 0,\n })\n }\n />\n
\n
\n
\n\n {/* 实发工资 */}\n
\n
\n {tc('netSalaryLabel')}\n \n ¥{calculateActualSalary(salaryForm).toLocaleString()}\n \n
\n
\n\n {/* 备注 */}\n
\n \n setSalaryForm({ ...salaryForm, remark: e.target.value })}\n rows={3}\n />\n
\n\n {/* 操作按钮 */}\n
\n \n \n
\n
\n \n )}\n
\n
\n\n {/* 详情对话框 */}\n \n \n {selectedSalary && (\n <>\n \n \n \n {tc('viewSalaryDetailTitle')}\n {selectedSalary.name}\n \n \n {currentMonth}\n 薪资明细\n \n \n\n
\n {/* 员工信息 */}\n
\n
\n

\n {tc('employeeInfo')}\n

\n
\n {tc('employeeNoLabelShort')}\n {selectedSalary.employee_no}\n {tc('employeeNameLabel')}\n {selectedSalary.name}\n {tc('genderLabel')}\n {getGenderText(selectedSalary.gender)}\n {tc('entryDateLabel')}\n {selectedSalary.entry_date}\n
\n
\n\n
\n

\n {tc('positionInfo')}\n

\n
\n {tc('deptLabel')}\n {selectedSalary.dept_name}\n {tc('positionLabel')}\n {selectedSalary.position}\n {tc('salaryMonthLabel')}\n {selectedSalary.month || currentMonth}\n
\n
\n
\n\n {/* 薪资明细 */}\n
\n

\n {tc('salaryDetail')}\n

\n \n \n \n {t('basicSalary')}\n \n ¥{(selectedSalary.basic_salary || 0).toLocaleString()}\n \n \n \n {t('positionAllowance')}\n \n ¥{(selectedSalary.position_allowance || 0).toLocaleString()}\n \n \n \n {t('performanceBonus')}\n \n ¥{(selectedSalary.performance_bonus || 0).toLocaleString()}\n \n \n \n {t('overtimePay')}\n \n ¥{(selectedSalary.overtime_pay || 0).toLocaleString()}\n \n \n \n {t('otherBonus')}\n \n ¥{(selectedSalary.other_bonus || 0).toLocaleString()}\n \n \n \n {t('incomeTotal')}\n \n ¥\n {(\n (selectedSalary.basic_salary || 0) +\n (selectedSalary.position_allowance || 0) +\n (selectedSalary.performance_bonus || 0) +\n (selectedSalary.overtime_pay || 0) +\n (selectedSalary.other_bonus || 0)\n ).toLocaleString()}\n \n \n \n \n {t('socialSecurity')}\n \n \n -¥{(selectedSalary.social_security || 0).toLocaleString()}\n \n \n \n \n {t('housingFund')}\n \n \n -¥{(selectedSalary.housing_fund || 0).toLocaleString()}\n \n \n \n \n {t('personalTax')}\n \n \n -¥{(selectedSalary.personal_tax || 0).toLocaleString()}\n \n \n \n \n {t('otherDeduction')}\n \n \n -¥{(selectedSalary.other_deduction || 0).toLocaleString()}\n \n \n \n {t('actualSalary')}\n \n ¥{(selectedSalary.actual_salary || 0).toLocaleString()}\n \n \n \n
\n
\n\n {selectedSalary.remark && (\n
\n

\n {tc('remark')}\n

\n

{selectedSalary.remark}

\n
\n )}\n\n
\n \n {\n setIsDetailOpen(false);\n handleEdit(selectedSalary);\n }}\n >\n \n {tc('edit')}\n \n
\n
\n \n )}\n
\n
\n\n {/* 报表对话框 */}\n \n \n \n \n \n {t('salaryReport')}\n \n \n {currentMonth}\n {tc('reportSubtitle')}\n \n \n\n
\n
\n

{t('salaryReport')}

\n

\n {tc('salaryMonthLabel')}\n {currentMonth}\n

\n

\n {tc('reportGeneratedAt')}\n {format(new Date(), 'yyyy-MM-dd HH:mm:ss')}\n

\n
\n\n
\n \n \n
{stats.totalEmployees}
\n
{tc('totalEmployees')}
\n
\n
\n \n \n
{stats.paidEmployees}
\n
{tc('paidEmployees')}
\n
\n
\n \n \n
\n ¥{stats.totalSalary.toLocaleString()}\n
\n
{tc('totalSalary')}
\n
\n
\n \n \n
\n ¥{stats.avgSalary.toLocaleString()}\n
\n
{tc('avgSalary')}
\n
\n
\n \n \n
\n ¥{stats.maxSalary.toLocaleString()}\n
\n
{tc('maxSalary')}
\n
\n
\n \n \n
\n ¥{stats.minSalary.toLocaleString()}\n
\n
{tc('minSalary')}
\n
\n
\n
\n\n
\n

{tc('salaryDetail')}

\n \n \n \n {tc('employeeNo')}\n {tc('name')}\n {tc('department')}\n {tc('position')}\n {t('basicSalary')}\n {t('actualSalary')}\n \n \n \n {filteredSalaries.map((salary) => (\n \n {salary.employee_no}\n {salary.name}\n {salary.dept_name}\n {salary.position}\n \n ¥{(salary.basic_salary || 0).toLocaleString()}\n \n \n ¥{(salary.actual_salary || 0).toLocaleString()}\n \n \n ))}\n \n
\n
\n\n
\n
\n
\n
{tc('signaturePrepared')}
\n
\n
\n
\n
{tc('signatureReviewed')}
\n
\n
\n
\n
{tc('signatureApproved')}
\n
\n
\n
\n\n
\n \n \n
\n
\n
\n
\n
\n );\n}\n","usedDeprecatedRules":[]},{"filePath":"D:\\dcprint\\erp-project\\src\\app\\[locale]\\hr\\salary\\payslips\\page.tsx","messages":[{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"张三\"。建议使用: tc('text_glwp')","line":37,"column":17,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":37,"endColumn":21},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"生产部\"。建议使用: tc('text_hjr0g')","line":39,"column":15,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":39,"endColumn":20},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"生产主管\"。建议使用: tc('text_f3plim')","line":40,"column":13,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":40,"endColumn":19},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":74,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":74,"endColumn":36},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .code on an `any` value.","line":75,"column":16,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":75,"endColumn":20},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction`.","line":76,"column":17,"nodeType":"MemberExpression","messageId":"unsafeArgument","endLine":76,"endColumn":26},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":76,"column":22,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":76,"endColumn":26}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":7,"fixableErrorCount":0,"fixableWarningCount":0,"source":"'use client';\n\nimport { useState } from 'react';\nimport { toast } from 'sonner';\nimport { authFetch } from '@/lib/auth-fetch';\nimport { MainLayout } from '@/components/layout';\nimport { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';\nimport { Button } from '@/components/ui/button';\nimport { Input } from '@/components/ui/input';\nimport { Label } from '@/components/ui/label';\nimport { Separator } from '@/components/ui/separator';\nimport { FileText, Printer, Send, DollarSign } from 'lucide-react';\nimport { useTranslations } from 'next-intl';\n\ninterface PayslipData {\n employeeName: string;\n employeeNo: string;\n department: string;\n position: string;\n month: string;\n basicSalary: number;\n pieceSalary: number;\n overtimeSalary: number;\n performanceSalary: number;\n allowances: number;\n grossPay: number;\n socialInsurance: number;\n housingFund: number;\n individualTax: number;\n attendanceDeduction: number;\n otherDeduction: number;\n totalDeduction: number;\n netPay: number;\n}\n\nconst mockPayslip: PayslipData = {\n employeeName: '张三',\n employeeNo: 'EMP2024001',\n department: '生产部',\n position: '生产主管',\n month: '2024-03',\n basicSalary: 8000,\n pieceSalary: 0,\n overtimeSalary: 800,\n performanceSalary: 1500,\n allowances: 2000,\n grossPay: 12300,\n socialInsurance: 800,\n housingFund: 1000,\n individualTax: 200,\n attendanceDeduction: 0,\n otherDeduction: 0,\n totalDeduction: 2000,\n netPay: 10300,\n};\n\nexport default function PayslipsPage() {\n const t = useTranslations('Hr');\n const tc = useTranslations('Common');\n\n const [data, setData] = useState(null);\n const [loading, setLoading] = useState(false);\n const [employeeId, setEmployeeId] = useState('');\n const [month, setMonth] = useState(new Date().toISOString().slice(0, 7));\n\n const fetchPayslip = async () => {\n if (!employeeId) {\n toast.error(t('enterEmployeeId') || '请输入员工ID');\n return;\n }\n setLoading(true);\n try {\n const res = await authFetch('/api/salary/payslips/' + employeeId);\n const json = await res.json();\n if (json.code === 200) {\n setData(json.data);\n } else {\n setData({ ...mockPayslip, month, employeeName: `员工#${employeeId}` });\n }\n } catch (_error) {\n setData({ ...mockPayslip, month, employeeName: `员工#${employeeId}` });\n } finally {\n setLoading(false);\n }\n };\n\n const handlePrint = () => {\n if (!data) return;\n const printWindow = window.open('', '_blank');\n if (!printWindow) {\n toast.error(t('allowPopup') || '请允许弹窗');\n return;\n }\n\n const rows = [\n { label: t('basicSalary') || '基本工资', value: data.basicSalary },\n { label: t('pieceSalary') || '计件工资', value: data.pieceSalary },\n { label: t('overtimeSalary') || '加班工资', value: data.overtimeSalary },\n { label: t('performanceSalary') || '绩效奖金', value: data.performanceSalary },\n { label: t('allowances') || '津贴补贴', value: data.allowances },\n ];\n\n const deductions = [\n { label: t('socialInsurance') || '社保', value: data.socialInsurance },\n { label: t('housingFund') || '公积金', value: data.housingFund },\n { label: t('individualTax') || '个税', value: data.individualTax },\n { label: t('attendanceDeduction') || '考勤扣款', value: data.attendanceDeduction },\n { label: t('otherDeduction') || '其他扣款', value: data.otherDeduction },\n ];\n\n printWindow.document.write(`\n ${t('payslip') || '工资条'}\n \n

${t('payslip') || '工资条'}

\n
\n
${tc('name')}: ${data.employeeName}
\n
${t('employeeNo') || '工号'}: ${data.employeeNo}
\n
${tc('department')}: ${data.department}
\n
${t('month') || '月份'}: ${data.month}
\n
\n

${t('incomeItems') || '收入项'}

\n \n \n ${rows.map((r) => ``).join('')}\n \n
${t('item') || '项目'}${t('amount') || '金额'}
${r.label}¥${r.value.toLocaleString()}
${t('grossPay') || '应发合计'}¥${data.grossPay.toLocaleString()}
\n

${t('deductionItems') || '扣款项'}

\n \n \n ${deductions.map((r) => ``).join('')}\n \n
${t('item') || '项目'}${t('amount') || '金额'}
${r.label}¥${r.value.toLocaleString()}
${t('totalDeduction') || '扣款合计'}¥${data.totalDeduction.toLocaleString()}
\n
${t('netPay') || '实发工资'}: ¥${data.netPay.toLocaleString()}
\n `);\n printWindow.document.close();\n printWindow.print();\n };\n\n const handleSend = () => {\n toast.success(t('payslipSent') || '工资条已发送');\n };\n\n return (\n \n
\n
\n \n

{t('payslip') || '工资条'}

\n
\n\n \n \n
\n
\n \n setEmployeeId(e.target.value)}\n placeholder={t('enterEmployeeId') || '输入员工ID'}\n />\n
\n
\n \n setMonth(e.target.value)}\n />\n
\n \n {data && (\n <>\n \n \n \n )}\n
\n
\n
\n\n {data && (\n \n \n \n \n {data.employeeName} - {data.month} {t('payslip') || '工资条'}\n \n \n \n
\n
\n {t('employeeNo') || '工号'}:{' '}\n {data.employeeNo}\n
\n
\n {tc('department')}:{' '}\n {data.department}\n
\n
\n {tc('position')}: {data.position}\n
\n
\n {t('month') || '月份'}:{' '}\n {data.month}\n
\n
\n\n \n\n
\n

\n {t('incomeItems') || '收入项'}\n

\n
\n {[\n { label: t('basicSalary') || '基本工资', value: data.basicSalary },\n { label: t('pieceSalary') || '计件工资', value: data.pieceSalary },\n { label: t('overtimeSalary') || '加班工资', value: data.overtimeSalary },\n { label: t('performanceSalary') || '绩效奖金', value: data.performanceSalary },\n { label: t('allowances') || '津贴补贴', value: data.allowances },\n ].map((item) => (\n
\n {item.label}\n ¥{item.value.toLocaleString()}\n
\n ))}\n
\n {t('grossPay') || '应发合计'}\n ¥{data.grossPay.toLocaleString()}\n
\n
\n
\n\n \n\n
\n

\n {t('deductionItems') || '扣款项'}\n

\n
\n {[\n { label: t('socialInsurance') || '社保', value: data.socialInsurance },\n { label: t('housingFund') || '公积金', value: data.housingFund },\n { label: t('individualTax') || '个税', value: data.individualTax },\n {\n label: t('attendanceDeduction') || '考勤扣款',\n value: data.attendanceDeduction,\n },\n { label: t('otherDeduction') || '其他扣款', value: data.otherDeduction },\n ].map((item) => (\n
\n {item.label}\n ¥{item.value.toLocaleString()}\n
\n ))}\n
\n {t('totalDeduction') || '扣款合计'}\n ¥{data.totalDeduction.toLocaleString()}\n
\n
\n
\n\n \n\n
\n {t('netPay') || '实发工资'}\n \n ¥{data.netPay.toLocaleString()}\n \n
\n
\n
\n )}\n
\n
\n );\n}\n","usedDeprecatedRules":[]},{"filePath":"D:\\dcprint\\erp-project\\src\\app\\[locale]\\hr\\salary\\piece-rate\\page.tsx","messages":[{"ruleId":"@typescript-eslint/no-explicit-any","severity":1,"message":"Unexpected any. Specify a different type.","line":20,"column":38,"nodeType":"TSAnyKeyword","messageId":"unexpectedAny","endLine":20,"endColumn":41,"suggestions":[{"messageId":"suggestUnknown","fix":{"range":[792,795],"text":"unknown"},"desc":"Use `unknown` instead, this will force you to explicitly, and safely assert the type is correct."},{"messageId":"suggestNever","fix":{"range":[792,795],"text":"never"},"desc":"Use `never` instead, this is useful when instantiating generic type parameters that you don't need to know the type of."}]},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":28,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":28,"endColumn":36},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .code on an `any` value.","line":29,"column":16,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":29,"endColumn":20},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction`.","line":29,"column":39,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":29,"endColumn":59},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":29,"column":44,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":29,"endColumn":48},{"ruleId":"react-hooks/set-state-in-effect","severity":1,"message":"Error: Calling setState synchronously within an effect can trigger cascading renders\n\nEffects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following:\n* Update external systems with the latest state from React.\n* Subscribe for updates from some external system, calling setState in a callback function when external state changes.\n\nCalling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect).\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\hr\\salary\\piece-rate\\page.tsx:36:21\n 34 | };\n 35 |\n> 36 | useEffect(() => { fetchRates(); }, []);\n | ^^^^^^^^^^ Avoid calling setState() directly within an effect\n 37 |\n 38 | return (\n 39 | ","line":36,"column":21,"nodeType":null,"endLine":36,"endColumn":31},{"ruleId":"react-hooks/exhaustive-deps","severity":1,"message":"React Hook useEffect has a missing dependency: 'fetchRates'. Either include it or remove the dependency array.","line":36,"column":38,"nodeType":"ArrayExpression","endLine":36,"endColumn":40,"suggestions":[{"desc":"Update the dependencies array to be: [fetchRates]","fix":{"range":[1295,1297],"text":"[fetchRates]"}}]},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":80,"column":34,"nodeType":"MemberExpression","messageId":"anyAssignment","endLine":80,"endColumn":38},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .id on an `any` value.","line":80,"column":36,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":80,"endColumn":38},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .processCode on an `any` value.","line":81,"column":57,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":81,"endColumn":68},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .processName on an `any` value.","line":82,"column":35,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":82,"endColumn":46},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .productType on an `any` value.","line":83,"column":35,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":83,"endColumn":46},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .unitPrice on an `any` value.","line":84,"column":64,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":84,"endColumn":73},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .effectiveDate on an `any` value.","line":85,"column":35,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":85,"endColumn":48},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .status on an `any` value.","line":86,"column":50,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":86,"endColumn":56},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .status on an `any` value.","line":86,"column":93,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":86,"endColumn":99}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":16,"fixableErrorCount":0,"fixableWarningCount":0,"source":"'use client';\r\n\r\nimport { useState, useEffect } from 'react';\r\nimport { MainLayout } from '@/components/layout';\r\nimport { Button } from '@/components/ui/button';\r\nimport { Card, CardContent, CardHeader } from '@/components/ui/card';\r\nimport { Input } from '@/components/ui/input';\r\nimport { Badge } from '@/components/ui/badge';\r\nimport {\r\n Table, TableBody, TableCell, TableHead, TableHeader, TableRow,\r\n} from '@/components/ui/table';\r\nimport { Plus, Search, Pencil, Trash2, Tag } from 'lucide-react';\r\nimport { toast } from 'sonner';\r\nimport { authFetch } from '@/lib/auth-fetch';\r\nimport { useTranslations } from 'next-intl';\r\n\r\nexport default function PieceRatePage() {\r\n const t = useTranslations('Hr');\r\n const tc = useTranslations('Common');\r\n const [rates, setRates] = useState([]);\r\n const [loading, setLoading] = useState(true);\r\n const [keyword, setKeyword] = useState('');\r\n\r\n const fetchRates = async () => {\r\n setLoading(true);\r\n try {\r\n const res = await authFetch(`/api/hr/piece-rate?keyword=${keyword}`);\r\n const json = await res.json();\r\n if (json.code === 200) setRates(json.data.list || []);\r\n } catch {\r\n toast.error(t('fetchFailed') || '获取工序单价列表失败');\r\n }\r\n setLoading(false);\r\n };\r\n\r\n useEffect(() => { fetchRates(); }, []);\r\n\r\n return (\r\n \r\n
\r\n
\r\n
\r\n \r\n

{t('pieceRate') || '工序单价管理'}

\r\n
\r\n \r\n
\r\n\r\n \r\n \r\n
\r\n setKeyword(e.target.value)}\r\n className=\"max-w-sm\"\r\n />\r\n \r\n
\r\n
\r\n \r\n \r\n \r\n \r\n {t('processCode') || '工序编号'}\r\n {t('processName') || '工序名称'}\r\n {t('productType') || '产品类型'}\r\n {t('unitPrice') || '单价'}\r\n {t('effectiveDate') || '生效日期'}\r\n {t('status') || '状态'}\r\n {tc('actions') || '操作'}\r\n \r\n \r\n \r\n {loading ? (\r\n {tc('loading') || '加载中...'}\r\n ) : rates.length === 0 ? (\r\n {tc('noData') || '暂无数据'}\r\n ) : rates.map(r => (\r\n \r\n {r.processCode}\r\n {r.processName}\r\n {r.productType || '-'}\r\n {Number(r.unitPrice).toFixed(4)}\r\n {r.effectiveDate}\r\n {r.status === 1 ? (t('active') || '启用') : (t('inactive') || '停用')}\r\n \r\n
\r\n \r\n \r\n
\r\n
\r\n
\r\n ))}\r\n
\r\n
\r\n
\r\n
\r\n
\r\n
\r\n );\r\n}\r\n","usedDeprecatedRules":[]},{"filePath":"D:\\dcprint\\erp-project\\src\\app\\[locale]\\hr\\salary\\piece-work\\page.tsx","messages":[{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"张三\"。建议使用: tc('text_glwp')","line":31,"column":46,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":31,"endColumn":50},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"李四\"。建议使用: tc('text_i1ql')","line":32,"column":46,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":32,"endColumn":50},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"王五\"。建议使用: tc('text_k31l')","line":33,"column":46,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":33,"endColumn":50},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"张三\"。建议使用: tc('text_glwp')","line":34,"column":46,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":34,"endColumn":50},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":57,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":57,"endColumn":36},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .code on an `any` value.","line":58,"column":16,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":58,"endColumn":20},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":59,"column":15,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":59,"endColumn":82},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":59,"column":41,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":59,"endColumn":45},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":59,"column":54,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":59,"endColumn":58},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":59,"column":66,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":59,"endColumn":70},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction`.","line":60,"column":20,"nodeType":"Identifier","messageId":"unsafeArgument","endLine":60,"endColumn":24},{"ruleId":"react-hooks/set-state-in-effect","severity":1,"message":"Error: Calling setState synchronously within an effect can trigger cascading renders\n\nEffects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following:\n* Update external systems with the latest state from React.\n* Subscribe for updates from some external system, calling setState in a callback function when external state changes.\n\nCalling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect).\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\hr\\salary\\piece-work\\page.tsx:71:21\n 69 | };\n 70 |\n> 71 | useEffect(() => { fetchRecords(); }, []);\n | ^^^^^^^^^^^^ Avoid calling setState() directly within an effect\n 72 |\n 73 | const totalQuantity = records.reduce((s, r) => s + r.quantity, 0);\n 74 | const totalAmount = records.reduce((s, r) => s + r.amount, 0);","line":71,"column":21,"nodeType":null,"endLine":71,"endColumn":33},{"ruleId":"react-hooks/exhaustive-deps","severity":1,"message":"React Hook useEffect has a missing dependency: 'fetchRecords'. Either include it or remove the dependency array.","line":71,"column":40,"nodeType":"ArrayExpression","endLine":71,"endColumn":42,"suggestions":[{"desc":"Update the dependencies array to be: [fetchRecords]","fix":{"range":[2884,2886],"text":"[fetchRecords]"}}]}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":13,"fixableErrorCount":0,"fixableWarningCount":0,"source":"'use client';\r\n\r\nimport { authFetch } from '@/lib/auth-fetch';\r\nimport { useState, useEffect } from 'react';\r\nimport { MainLayout } from '@/components/layout';\r\nimport { Card, CardContent, CardHeader } from '@/components/ui/card';\r\nimport {\r\n Table, TableBody, TableCell, TableHead, TableHeader, TableRow,\r\n} from '@/components/ui/table';\r\nimport { Button } from '@/components/ui/button';\r\nimport { Input } from '@/components/ui/input';\r\nimport { Label } from '@/components/ui/label';\r\nimport { Badge } from '@/components/ui/badge';\r\nimport { Search, Upload, Package } from 'lucide-react';\r\nimport { useTranslations } from 'next-intl';\r\n\r\ninterface PieceWorkRecord {\r\n id: number;\r\n date: string;\r\n employeeName: string;\r\n processCode: string;\r\n productCode: string;\r\n quantity: number;\r\n defectCount: number;\r\n passRate: number;\r\n unitPrice: number;\r\n amount: number;\r\n}\r\n\r\nconst mockRecords: PieceWorkRecord[] = [\r\n { id: 1, date: '2024-03-01', employeeName: '张三', processCode: 'P001', productCode: 'PRD-001', quantity: 100, defectCount: 2, passRate: 98, unitPrice: 5, amount: 490 },\r\n { id: 2, date: '2024-03-01', employeeName: '李四', processCode: 'P002', productCode: 'PRD-002', quantity: 80, defectCount: 1, passRate: 98.75, unitPrice: 8, amount: 632 },\r\n { id: 3, date: '2024-03-02', employeeName: '王五', processCode: 'P001', productCode: 'PRD-001', quantity: 120, defectCount: 3, passRate: 97.5, unitPrice: 5, amount: 585 },\r\n { id: 4, date: '2024-03-02', employeeName: '张三', processCode: 'P003', productCode: 'PRD-003', quantity: 50, defectCount: 0, passRate: 100, unitPrice: 15, amount: 750 },\r\n];\r\n\r\nexport default function PieceWorkPage() {\r\n const t = useTranslations('Hr');\r\n const tc = useTranslations('Common');\r\n\r\n const [records, setRecords] = useState([]);\r\n const [loading, setLoading] = useState(false);\r\n const [employeeId, setEmployeeId] = useState('');\r\n const [processCode, setProcessCode] = useState('');\r\n const [startDate, setStartDate] = useState('');\r\n const [endDate, setEndDate] = useState('');\r\n\r\n const fetchRecords = async () => {\r\n setLoading(true);\r\n try {\r\n const params = new URLSearchParams();\r\n if (employeeId) params.set('employeeId', employeeId);\r\n if (processCode) params.set('processCode', processCode);\r\n if (startDate) params.set('startDate', startDate);\r\n if (endDate) params.set('endDate', endDate);\r\n const res = await authFetch(`/api/hr/piece-work?${params.toString()}`);\r\n const json = await res.json();\r\n if (json.code === 200) {\r\n const list = Array.isArray(json.data) ? json.data : json.data?.list || [];\r\n setRecords(list);\r\n } else {\r\n setRecords(mockRecords);\r\n }\r\n } catch {\r\n setRecords(mockRecords);\r\n } finally {\r\n setLoading(false);\r\n }\r\n };\r\n\r\n useEffect(() => { fetchRecords(); }, []);\r\n\r\n const totalQuantity = records.reduce((s, r) => s + r.quantity, 0);\r\n const totalAmount = records.reduce((s, r) => s + r.amount, 0);\r\n\r\n return (\r\n \r\n
\r\n
\r\n
\r\n \r\n

{t('pieceWork') || '计件产量'}

\r\n
\r\n \r\n
\r\n\r\n \r\n \r\n
\r\n
\r\n \r\n setEmployeeId(e.target.value)} />\r\n
\r\n
\r\n \r\n setProcessCode(e.target.value)} />\r\n
\r\n
\r\n \r\n setStartDate(e.target.value)} />\r\n
\r\n
\r\n \r\n setEndDate(e.target.value)} />\r\n
\r\n \r\n
\r\n
\r\n
\r\n\r\n \r\n \r\n \r\n \r\n \r\n {t('date') || '日期'}\r\n {t('employeeName') || '员工'}\r\n {t('processCode') || '工序编号'}\r\n {t('productCode') || '产品编号'}\r\n {t('quantity') || '数量'}\r\n {t('defectCount') || '次品'}\r\n {t('passRate') || '合格率'}\r\n {t('unitPrice') || '单价'}\r\n {t('amount') || '金额'}\r\n \r\n \r\n \r\n {records.map((r) => (\r\n \r\n {r.date}\r\n {r.employeeName}\r\n {r.processCode}\r\n {r.productCode}\r\n {r.quantity}\r\n {r.defectCount}\r\n {r.passRate}%\r\n ¥{r.unitPrice}\r\n ¥{r.amount.toFixed(2)}\r\n \r\n ))}\r\n {records.length > 0 && (\r\n \r\n {tc('total') || '合计'}\r\n {totalQuantity}\r\n \r\n ¥{totalAmount.toFixed(2)}\r\n \r\n )}\r\n {records.length === 0 && (\r\n \r\n \r\n {loading ? (tc('loading') || '加载中...') : (t('noData') || '暂无数据')}\r\n \r\n \r\n )}\r\n \r\n
\r\n
\r\n
\r\n
\r\n
\r\n );\r\n}\r\n","usedDeprecatedRules":[]},{"filePath":"D:\\dcprint\\erp-project\\src\\app\\[locale]\\hr\\schedules\\page.tsx","messages":[{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":68,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":68,"endColumn":36},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .code on an `any` value.","line":69,"column":16,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":69,"endColumn":20},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":70,"column":15,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":70,"endColumn":82},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":70,"column":41,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":70,"endColumn":45},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":70,"column":54,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":70,"endColumn":58},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":70,"column":66,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":70,"endColumn":70},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction`.","line":71,"column":22,"nodeType":"Identifier","messageId":"unsafeArgument","endLine":71,"endColumn":26},{"ruleId":"react-hooks/set-state-in-effect","severity":1,"message":"Error: Calling setState synchronously within an effect can trigger cascading renders\n\nEffects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following:\n* Update external systems with the latest state from React.\n* Subscribe for updates from some external system, calling setState in a callback function when external state changes.\n\nCalling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect).\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\hr\\schedules\\page.tsx:82:21\n 80 | };\n 81 |\n> 82 | useEffect(() => { fetchSchedules(); }, []);\n | ^^^^^^^^^^^^^^ Avoid calling setState() directly within an effect\n 83 |\n 84 | const changeMonth = (offset: number) => {\n 85 | const d = new Date(currentMonth + '-01');","line":82,"column":21,"nodeType":null,"endLine":82,"endColumn":35},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":100,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":100,"endColumn":36},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .code on an `any` value.","line":101,"column":16,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":101,"endColumn":20},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `string | number | bigint | boolean | ReactElement> | Iterable | ... 4 more ... | undefined`.","line":106,"column":21,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":106,"endColumn":48},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .message on an `any` value.","line":106,"column":26,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":106,"endColumn":33}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":12,"fixableErrorCount":0,"fixableWarningCount":0,"source":"'use client';\r\n\r\nimport { authFetch } from '@/lib/auth-fetch';\r\nimport { useState, useEffect } from 'react';\r\nimport { toast } from 'sonner';\r\nimport { MainLayout } from '@/components/layout';\r\nimport { Card, CardContent, CardHeader } from '@/components/ui/card';\r\nimport {\r\n Table, TableBody, TableCell, TableHead, TableHeader, TableRow,\r\n} from '@/components/ui/table';\r\nimport {\r\n Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter,\r\n} from '@/components/ui/dialog';\r\nimport { Button } from '@/components/ui/button';\r\nimport { Input } from '@/components/ui/input';\r\nimport { Label } from '@/components/ui/label';\r\nimport { Badge } from '@/components/ui/badge';\r\nimport {\r\n Select, SelectContent, SelectItem, SelectTrigger, SelectValue,\r\n} from '@/components/ui/select';\r\nimport { Calendar, Plus, Search, ChevronLeft, ChevronRight } from 'lucide-react';\r\nimport { useTranslations } from 'next-intl';\r\n\r\ninterface Schedule {\r\n id: number;\r\n employeeId: number;\r\n employeeName: string;\r\n shiftId: number;\r\n shiftName: string;\r\n startDate: string;\r\n endDate: string;\r\n status: string;\r\n}\r\n\r\ninterface Employee {\r\n id: number;\r\n name: string;\r\n employee_no: string;\r\n}\r\n\r\nconst mockShifts = [\r\n { id: 1, shiftName: 'shiftMorning' },\r\n { id: 2, shiftName: 'shiftAfternoon' },\r\n { id: 3, shiftName: 'shiftNight' },\r\n];\r\n\r\nexport default function SchedulesPage() {\r\n const t = useTranslations('Hr');\r\n const tc = useTranslations('Common');\r\n\r\n const [schedules, setSchedules] = useState([]);\r\n const [_employees, _setEmployees] = useState([]);\r\n const [loading, setLoading] = useState(false);\r\n const [dialogOpen, setDialogOpen] = useState(false);\r\n const [search, setSearch] = useState('');\r\n const [currentMonth, setCurrentMonth] = useState(new Date().toISOString().slice(0, 7));\r\n const [form, setForm] = useState({\r\n employeeId: 0,\r\n shiftId: 0,\r\n startDate: '',\r\n endDate: '',\r\n });\r\n\r\n const fetchSchedules = async () => {\r\n setLoading(true);\r\n try {\r\n const res = await authFetch('/api/hr/schedules');\r\n const json = await res.json();\r\n if (json.code === 200) {\r\n const list = Array.isArray(json.data) ? json.data : json.data?.list || [];\r\n setSchedules(list);\r\n } else {\r\n setSchedules([]);\r\n }\r\n } catch {\r\n setSchedules([]);\r\n } finally {\r\n setLoading(false);\r\n }\r\n };\r\n\r\n useEffect(() => { fetchSchedules(); }, []);\r\n\r\n const changeMonth = (offset: number) => {\r\n const d = new Date(currentMonth + '-01');\r\n d.setMonth(d.getMonth() + offset);\r\n setCurrentMonth(d.toISOString().slice(0, 7));\r\n };\r\n\r\n const handleSave = async () => {\r\n if (!form.employeeId || !form.shiftId || !form.startDate || !form.endDate) {\r\n toast.error(t('fillRequired') || '请填写完整信息');\r\n return;\r\n }\r\n try {\r\n const res = await authFetch('/api/hr/schedules', {\r\n method: 'POST',\r\n body: JSON.stringify(form),\r\n });\r\n const json = await res.json();\r\n if (json.code === 200) {\r\n toast.success(t('createSuccess') || '创建成功');\r\n setDialogOpen(false);\r\n fetchSchedules();\r\n } else {\r\n toast.error(json.message || tc('error'));\r\n }\r\n } catch {\r\n toast.error(t('saveFailed') || '保存失败');\r\n }\r\n };\r\n\r\n const filtered = schedules.filter((s) =>\r\n !search || s.employeeName?.toLowerCase().includes(search.toLowerCase())\r\n );\r\n\r\n const monthLabel = currentMonth;\r\n\r\n return (\r\n \r\n
\r\n
\r\n
\r\n \r\n

{t('schedule')}

\r\n
\r\n \r\n
\r\n\r\n \r\n \r\n
\r\n
\r\n \r\n
\r\n \r\n {monthLabel}\r\n
\r\n \r\n
\r\n \r\n setSearch(e.target.value)}\r\n />\r\n
\r\n
\r\n {t('totalCount')} {filtered.length} {t('records')}\r\n
\r\n
\r\n \r\n \r\n \r\n \r\n {t('employeeName')}\r\n {t('shiftName')}\r\n {t('startDate')}\r\n {t('endDate')}\r\n {tc('status')}\r\n \r\n \r\n \r\n {filtered.map((s) => (\r\n \r\n {s.employeeName}\r\n {s.shiftName}\r\n {s.startDate}\r\n {s.endDate}\r\n \r\n \r\n {s.status === 'active' ? tc('active') : tc('inactive')}\r\n \r\n \r\n \r\n ))}\r\n {filtered.length === 0 && (\r\n \r\n \r\n {loading ? tc('loading') : t('noData')}\r\n \r\n \r\n )}\r\n \r\n
\r\n
\r\n
\r\n\r\n \r\n \r\n \r\n {t('addSchedule')}\r\n \r\n
\r\n
\r\n \r\n setForm({ ...form, employeeId: parseInt(e.target.value) || 0 })}\r\n />\r\n
\r\n
\r\n \r\n \r\n
\r\n
\r\n
\r\n \r\n setForm({ ...form, startDate: e.target.value })} />\r\n
\r\n
\r\n \r\n setForm({ ...form, endDate: e.target.value })} />\r\n
\r\n
\r\n
\r\n \r\n \r\n \r\n \r\n
\r\n
\r\n
\r\n
\r\n );\r\n}\r\n","usedDeprecatedRules":[]},{"filePath":"D:\\dcprint\\erp-project\\src\\app\\[locale]\\hr\\shifts\\page.tsx","messages":[{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":68,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":68,"endColumn":36},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .code on an `any` value.","line":69,"column":16,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":69,"endColumn":20},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":70,"column":15,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":70,"endColumn":82},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":70,"column":41,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":70,"endColumn":45},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":70,"column":54,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":70,"endColumn":58},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":70,"column":66,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":70,"endColumn":70},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction`.","line":71,"column":19,"nodeType":"Identifier","messageId":"unsafeArgument","endLine":71,"endColumn":23},{"ruleId":"react-hooks/set-state-in-effect","severity":1,"message":"Error: Calling setState synchronously within an effect can trigger cascading renders\n\nEffects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following:\n* Update external systems with the latest state from React.\n* Subscribe for updates from some external system, calling setState in a callback function when external state changes.\n\nCalling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect).\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\hr\\shifts\\page.tsx:80:21\n 78 | };\n 79 |\n> 80 | useEffect(() => { fetchShifts(); }, []);\n | ^^^^^^^^^^^ Avoid calling setState() directly within an effect\n 81 |\n 82 | const handleSave = async () => {\n 83 | if (!form.shiftName) {","line":80,"column":21,"nodeType":null,"endLine":80,"endColumn":32},{"ruleId":"react-hooks/exhaustive-deps","severity":1,"message":"React Hook useEffect has a missing dependency: 'fetchShifts'. Either include it or remove the dependency array.","line":80,"column":39,"nodeType":"ArrayExpression","endLine":80,"endColumn":41,"suggestions":[{"desc":"Update the dependencies array to be: [fetchShifts]","fix":{"range":[2454,2456],"text":"[fetchShifts]"}}]},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":93,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":93,"endColumn":36},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .code on an `any` value.","line":94,"column":16,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":94,"endColumn":20},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `string | number | bigint | boolean | ReactElement> | Iterable | ... 4 more ... | undefined`.","line":99,"column":21,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":99,"endColumn":48},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .message on an `any` value.","line":99,"column":26,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":99,"endColumn":33},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":110,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":110,"endColumn":36},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .code on an `any` value.","line":111,"column":16,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":111,"endColumn":20}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":15,"fixableErrorCount":0,"fixableWarningCount":0,"source":"'use client';\r\n\r\nimport { authFetch } from '@/lib/auth-fetch';\r\nimport { useState, useEffect } from 'react';\r\nimport { toast } from 'sonner';\r\nimport { MainLayout } from '@/components/layout';\r\nimport { Card, CardContent, CardHeader } from '@/components/ui/card';\r\nimport {\r\n Table, TableBody, TableCell, TableHead, TableHeader, TableRow,\r\n} from '@/components/ui/table';\r\nimport {\r\n Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter,\r\n} from '@/components/ui/dialog';\r\nimport { Button } from '@/components/ui/button';\r\nimport { Input } from '@/components/ui/input';\r\nimport { Label } from '@/components/ui/label';\r\nimport { Badge } from '@/components/ui/badge';\r\nimport {\r\n Select, SelectContent, SelectItem, SelectTrigger, SelectValue,\r\n} from '@/components/ui/select';\r\nimport { Switch } from '@/components/ui/switch';\r\nimport { Checkbox } from '@/components/ui/checkbox';\r\nimport { Clock, Plus, Pencil, Trash2, Search } from 'lucide-react';\r\nimport { useTranslations } from 'next-intl';\r\n\r\ninterface Shift {\r\n id: number;\r\n shiftName: string;\r\n startTime: string;\r\n endTime: string;\r\n allowOvertime: boolean;\r\n overtimeRate: number;\r\n nightAllowance: number;\r\n lateThreshold: number;\r\n earlyLeaveThreshold: number;\r\n workingHours: number;\r\n status: number;\r\n}\r\n\r\nconst defaultForm = {\r\n shiftName: '',\r\n startTime: '08:00',\r\n endTime: '17:00',\r\n allowOvertime: false,\r\n overtimeRate: 1.5,\r\n nightAllowance: 0,\r\n lateThreshold: 30,\r\n earlyLeaveThreshold: 30,\r\n workingHours: 8,\r\n status: 1,\r\n};\r\n\r\nexport default function ShiftsPage() {\r\n const t = useTranslations('Hr');\r\n const tc = useTranslations('Common');\r\n\r\n const [shifts, setShifts] = useState([]);\r\n const [_loading, setLoading] = useState(false);\r\n const [dialogOpen, setDialogOpen] = useState(false);\r\n const [editing, setEditing] = useState(false);\r\n const [form, setForm] = useState>(defaultForm);\r\n const [search, setSearch] = useState('');\r\n\r\n const fetchShifts = async () => {\r\n setLoading(true);\r\n try {\r\n const res = await authFetch('/api/hr/shifts');\r\n const json = await res.json();\r\n if (json.code === 200) {\r\n const list = Array.isArray(json.data) ? json.data : json.data?.list || [];\r\n setShifts(list);\r\n }\r\n } catch {\r\n toast.error(t('fetchFailed') || '获取失败');\r\n } finally {\r\n setLoading(false);\r\n }\r\n };\r\n\r\n useEffect(() => { fetchShifts(); }, []);\r\n\r\n const handleSave = async () => {\r\n if (!form.shiftName) {\r\n toast.error(t('enterShiftName') || '请输入班次名称');\r\n return;\r\n }\r\n try {\r\n const method = editing ? 'PUT' : 'POST';\r\n const res = await authFetch('/api/hr/shifts', {\r\n method,\r\n body: JSON.stringify(editing ? { id: form.id, ...form } : form),\r\n });\r\n const json = await res.json();\r\n if (json.code === 200) {\r\n toast.success(editing ? (t('updateSuccess') || '更新成功') : (t('createSuccess') || '创建成功'));\r\n setDialogOpen(false);\r\n fetchShifts();\r\n } else {\r\n toast.error(json.message || tc('error'));\r\n }\r\n } catch {\r\n toast.error(t('saveFailed') || '保存失败');\r\n }\r\n };\r\n\r\n const handleDelete = async (shift: Shift) => {\r\n if (!confirm(t('deleteConfirm') || '确认删除?')) return;\r\n try {\r\n const res = await authFetch(`/api/hr/shifts?id=${shift.id}`, { method: 'DELETE' });\r\n const json = await res.json();\r\n if (json.code === 200) {\r\n toast.success(t('deleteSuccess') || '删除成功');\r\n fetchShifts();\r\n }\r\n } catch {\r\n toast.error(t('deleteFailed') || '删除失败');\r\n }\r\n };\r\n\r\n const openAdd = () => {\r\n setForm(defaultForm);\r\n setEditing(false);\r\n setDialogOpen(true);\r\n };\r\n\r\n const openEdit = (shift: Shift) => {\r\n setForm(shift);\r\n setEditing(true);\r\n setDialogOpen(true);\r\n };\r\n\r\n const filtered = shifts.filter((s) =>\r\n !search || s.shiftName.toLowerCase().includes(search.toLowerCase())\r\n );\r\n\r\n return (\r\n \r\n
\r\n
\r\n
\r\n \r\n

{t('shift') || '班次管理'}

\r\n
\r\n \r\n
\r\n\r\n \r\n \r\n
\r\n
\r\n \r\n setSearch(e.target.value)}\r\n />\r\n
\r\n {t('totalCount') || '共'} {filtered.length} {t('records') || '条'}\r\n
\r\n
\r\n \r\n \r\n \r\n \r\n {t('shiftName') || '班次名称'}\r\n {t('startTime') || '开始时间'}\r\n {t('endTime') || '结束时间'}\r\n {t('allowOvertime') || '允许加班'}\r\n {t('overtimeRate') || '加班倍率'}\r\n {t('nightAllowance') || '夜班津贴'}\r\n {tc('status')}\r\n {tc('actions')}\r\n \r\n \r\n \r\n {filtered.map((shift) => (\r\n \r\n {shift.shiftName}\r\n {shift.startTime}\r\n {shift.endTime}\r\n \r\n \r\n \r\n {shift.overtimeRate}x\r\n ¥{shift.nightAllowance}\r\n \r\n \r\n {shift.status === 1 ? (tc('active') || '启用') : (tc('inactive') || '停用')}\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n ))}\r\n {filtered.length === 0 && (\r\n \r\n \r\n {t('noData') || '暂无数据'}\r\n \r\n \r\n )}\r\n \r\n
\r\n
\r\n
\r\n\r\n \r\n \r\n \r\n {editing ? (t('editShift') || '编辑班次') : (t('addShift') || '新增班次')}\r\n \r\n
\r\n
\r\n \r\n setForm({ ...form, shiftName: e.target.value })} placeholder=\"例如:白班\" />\r\n
\r\n
\r\n \r\n setForm({ ...form, startTime: e.target.value })} />\r\n
\r\n
\r\n \r\n setForm({ ...form, endTime: e.target.value })} />\r\n
\r\n
\r\n \r\n setForm({ ...form, overtimeRate: parseFloat(e.target.value) || 1 })} />\r\n
\r\n
\r\n \r\n setForm({ ...form, nightAllowance: parseFloat(e.target.value) || 0 })} />\r\n
\r\n
\r\n \r\n setForm({ ...form, lateThreshold: parseInt(e.target.value) || 0 })} />\r\n
\r\n
\r\n \r\n setForm({ ...form, earlyLeaveThreshold: parseInt(e.target.value) || 0 })} />\r\n
\r\n
\r\n \r\n setForm({ ...form, workingHours: parseFloat(e.target.value) || 8 })} />\r\n
\r\n
\r\n setForm({ ...form, allowOvertime: !!v })}\r\n />\r\n \r\n
\r\n
\r\n \r\n
\r\n
\r\n \r\n \r\n \r\n \r\n
\r\n
\r\n
\r\n
\r\n );\r\n}\r\n","usedDeprecatedRules":[]},{"filePath":"D:\\dcprint\\erp-project\\src\\app\\[locale]\\hr\\skills\\page.tsx","messages":[{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":98,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":98,"endColumn":36},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .code on an `any` value.","line":99,"column":16,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":99,"endColumn":20},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction`.","line":100,"column":17,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":100,"endColumn":37},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":100,"column":22,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":100,"endColumn":26},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction`.","line":101,"column":18,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":101,"endColumn":38},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":101,"column":23,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":101,"endColumn":27},{"ruleId":"react-hooks/set-state-in-effect","severity":1,"message":"Error: Calling setState synchronously within an effect can trigger cascading renders\n\nEffects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following:\n* Update external systems with the latest state from React.\n* Subscribe for updates from some external system, calling setState in a callback function when external state changes.\n\nCalling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect).\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\hr\\skills\\page.tsx:109:5\n 107 |\n 108 | useEffect(() => {\n> 109 | fetchData();\n | ^^^^^^^^^ Avoid calling setState() directly within an effect\n 110 | }, [page]);\n 111 |\n 112 | const handleSave = async () => {","line":109,"column":5,"nodeType":null,"endLine":109,"endColumn":14},{"ruleId":"react-hooks/exhaustive-deps","severity":1,"message":"React Hook useEffect has a missing dependency: 'fetchData'. Either include it or remove the dependency array.","line":110,"column":6,"nodeType":"ArrayExpression","endLine":110,"endColumn":12,"suggestions":[{"desc":"Update the dependencies array to be: [fetchData, page]","fix":{"range":[3237,3243],"text":"[fetchData, page]"}}]},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":119,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":119,"endColumn":36},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .code on an `any` value.","line":120,"column":16,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":120,"endColumn":20},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `string | number | bigint | boolean | ReactElement> | Iterable | ... 4 more ... | undefined`.","line":125,"column":21,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":125,"endColumn":48},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .message on an `any` value.","line":125,"column":26,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":125,"endColumn":33},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":136,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":136,"endColumn":36},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .code on an `any` value.","line":137,"column":16,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":137,"endColumn":20},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `string | number | bigint | boolean | ReactElement> | Iterable | ... 4 more ... | undefined`.","line":141,"column":21,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":141,"endColumn":55},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .message on an `any` value.","line":141,"column":26,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":141,"endColumn":33}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":16,"fixableErrorCount":0,"fixableWarningCount":0,"source":"'use client';\r\n\r\nimport { authFetch } from '@/lib/auth-fetch';\r\nimport { useEffect, useState } from 'react';\r\nimport { MainLayout } from '@/components/layout';\r\nimport { Card, CardContent } from '@/components/ui/card';\r\nimport { Button } from '@/components/ui/button';\r\nimport { Input } from '@/components/ui/input';\r\nimport { Badge } from '@/components/ui/badge';\r\nimport { Textarea } from '@/components/ui/textarea';\r\nimport {\r\n Table,\r\n TableBody,\r\n TableCell,\r\n TableHead,\r\n TableHeader,\r\n TableRow,\r\n} from '@/components/ui/table';\r\nimport {\r\n Dialog,\r\n DialogContent,\r\n DialogHeader,\r\n DialogTitle,\r\n DialogFooter,\r\n} from '@/components/ui/dialog';\r\nimport { Label } from '@/components/ui/label';\r\nimport {\r\n Select,\r\n SelectContent,\r\n SelectItem,\r\n SelectTrigger,\r\n SelectValue,\r\n} from '@/components/ui/select';\r\nimport { Plus, Search, Edit, Trash2, Star, CheckCircle2, XCircle } from 'lucide-react';\r\nimport { toast } from 'sonner';\r\nimport { useTranslations } from 'next-intl';\r\n\r\ninterface Skill {\r\n id: number;\r\n employee_id: number;\r\n employee_name: string;\r\n skill_code: string;\r\n skill_name: string;\r\n skill_category: string;\r\n skill_level: number;\r\n certified: number;\r\n assessor: string;\r\n assess_date: string;\r\n next_assess_date: string;\r\n remark: string;\r\n}\r\n\r\nconst categoryMap: Record = {\r\n printing: 'skillCategoryPrinting',\r\n binding: 'skillCategoryBinding',\r\n finishing: 'skillCategoryFinishing',\r\n maintenance: 'skillCategoryMaintenance',\r\n};\r\n\r\nconst categoryOptions = [\r\n { value: '_all', label: 'all' },\r\n { value: 'printing', label: 'skillCategoryPrinting' },\r\n { value: 'binding', label: 'skillCategoryBinding' },\r\n { value: 'finishing', label: 'skillCategoryFinishing' },\r\n { value: 'maintenance', label: 'skillCategoryMaintenance' },\r\n];\r\n\r\nconst levelLabels = ['', 'skillLevel1', 'skillLevel2', 'skillLevel3', 'skillLevel4', 'skillLevel5'];\r\n\r\nconst formatDate = (dateStr: string) => {\r\n if (!dateStr) return '-';\r\n return dateStr.slice(0, 10);\r\n};\r\n\r\nexport default function SkillsPage() {\r\n const [list, setList] = useState([]);\r\n const [total, setTotal] = useState(0);\r\n const [page, setPage] = useState(1);\r\n const [pageSize] = useState(20);\r\n const [employeeId, setEmployeeId] = useState('');\r\n const [skillCategory, setSkillCategory] = useState('_all');\r\n const [showDialog, setShowDialog] = useState(false);\r\n const [editItem, setEditItem] = useState>({});\r\n\r\n const t = useTranslations('Hr');\r\n const tc = useTranslations('Common');\r\n\r\n const fetchData = async () => {\r\n try {\r\n const params = new URLSearchParams({\r\n page: String(page),\r\n pageSize: String(pageSize),\r\n });\r\n if (employeeId) params.append('employeeId', employeeId);\r\n if (skillCategory && skillCategory !== '_all') params.append('skillCategory', skillCategory);\r\n\r\n const res = await authFetch(`/api/hr/skills?${params}`);\r\n const json = await res.json();\r\n if (json.code === 200) {\r\n setList(json.data.list || []);\r\n setTotal(json.data.total || 0);\r\n }\r\n } catch {\r\n toast.error(tc('fetchFailed'));\r\n }\r\n };\r\n\r\n useEffect(() => {\r\n fetchData();\r\n }, [page]);\r\n\r\n const handleSave = async () => {\r\n try {\r\n const isEdit = !!editItem.id;\r\n const res = await authFetch('/api/hr/skills', {\r\n method: isEdit ? 'PUT' : 'POST',\r\n body: JSON.stringify(editItem),\r\n });\r\n const json = await res.json();\r\n if (json.code === 200) {\r\n toast.success(isEdit ? tc('updateSuccess') : tc('createSuccess'));\r\n setShowDialog(false);\r\n fetchData();\r\n } else {\r\n toast.error(json.message || tc('error'));\r\n }\r\n } catch {\r\n toast.error(tc('error'));\r\n }\r\n };\r\n\r\n const handleDelete = async (id: number) => {\r\n if (!confirm(tc('confirmDelete'))) return;\r\n try {\r\n const res = await authFetch(`/api/hr/skills?id=${id}`, { method: 'DELETE' });\r\n const json = await res.json();\r\n if (json.code === 200) {\r\n toast.success(tc('deleteSuccess'));\r\n fetchData();\r\n } else {\r\n toast.error(json.message || tc('deleteFailed'));\r\n }\r\n } catch {\r\n toast.error(tc('deleteFailed'));\r\n }\r\n };\r\n\r\n const renderStars = (level: number) => {\r\n return (\r\n \r\n {Array.from({ length: 5 }, (_, i) => (\r\n \r\n ))}\r\n \r\n );\r\n };\r\n\r\n const totalPages = Math.ceil(total / pageSize);\r\n\r\n return (\r\n \r\n
\r\n
\r\n

{t('skillMatrix')}

\r\n
\r\n
\r\n setEmployeeId(e.target.value)}\r\n className=\"w-28 h-8 text-sm\"\r\n />\r\n \r\n \r\n
\r\n {\r\n setEditItem({ skill_level: 1, certified: 0 });\r\n setShowDialog(true);\r\n }}\r\n >\r\n \r\n {tc('add')}\r\n \r\n
\r\n
\r\n\r\n \r\n \r\n \r\n \r\n \r\n {t('employeeName')}\r\n {t('skillName')}\r\n {t('skillCategory')}\r\n {t('skillLevel')}\r\n {t('certified')}\r\n {t('assessor')}\r\n {t('nextAssessDate')}\r\n {tc('operation')}\r\n \r\n \r\n \r\n {list.map((item) => (\r\n \r\n {item.employee_name}\r\n {item.skill_name}\r\n \r\n {t(categoryMap[item.skill_category] || item.skill_category)}\r\n \r\n \r\n
\r\n {renderStars(item.skill_level)}\r\n \r\n {t(levelLabels[item.skill_level])}\r\n \r\n
\r\n
\r\n \r\n {item.certified ? (\r\n \r\n \r\n {t('certified')}\r\n \r\n ) : (\r\n \r\n \r\n {tc('no')}\r\n \r\n )}\r\n \r\n {item.assessor || '-'}\r\n \r\n {formatDate(item.next_assess_date)}\r\n \r\n \r\n
\r\n {\r\n setEditItem(item);\r\n setShowDialog(true);\r\n }}\r\n >\r\n \r\n \r\n handleDelete(item.id)}\r\n >\r\n \r\n \r\n
\r\n
\r\n
\r\n ))}\r\n {list.length === 0 && (\r\n \r\n \r\n {tc('noData')}\r\n \r\n \r\n )}\r\n
\r\n
\r\n
\r\n
\r\n\r\n
\r\n {t('totalRecords', { count: total })}\r\n
\r\n setPage((p) => p - 1)}\r\n >\r\n {tc('prevPage')}\r\n \r\n \r\n {page} / {totalPages || 1}\r\n \r\n = totalPages}\r\n onClick={() => setPage((p) => p + 1)}\r\n >\r\n {tc('nextPage')}\r\n \r\n
\r\n
\r\n\r\n \r\n \r\n \r\n {editItem.id ? tc('edit') : tc('add')}\r\n \r\n
\r\n
\r\n \r\n setEditItem({ ...editItem, employee_id: Number(e.target.value) })}\r\n />\r\n
\r\n
\r\n \r\n setEditItem({ ...editItem, skill_code: e.target.value })}\r\n />\r\n
\r\n
\r\n \r\n setEditItem({ ...editItem, skill_name: e.target.value })}\r\n />\r\n
\r\n
\r\n \r\n setEditItem({ ...editItem, skill_category: v })}\r\n >\r\n \r\n \r\n \r\n \r\n {t('skillCategoryPrinting')}\r\n {t('skillCategoryBinding')}\r\n {t('skillCategoryFinishing')}\r\n {t('skillCategoryMaintenance')}\r\n \r\n \r\n
\r\n
\r\n \r\n setEditItem({ ...editItem, skill_level: Number(v) })}\r\n >\r\n \r\n \r\n \r\n \r\n {[1, 2, 3, 4, 5].map((lv) => (\r\n \r\n {lv} - {t(levelLabels[lv])}\r\n \r\n ))}\r\n \r\n \r\n
\r\n
\r\n \r\n
\r\n setEditItem({ ...editItem, certified: e.target.checked ? 1 : 0 })}\r\n />\r\n {editItem.certified ? t('certified') : tc('no')}\r\n
\r\n
\r\n
\r\n \r\n setEditItem({ ...editItem, assessor: e.target.value })}\r\n />\r\n
\r\n
\r\n \r\n setEditItem({ ...editItem, assess_date: e.target.value })}\r\n />\r\n
\r\n
\r\n \r\n setEditItem({ ...editItem, next_assess_date: e.target.value })}\r\n />\r\n
\r\n
\r\n \r\n setEditItem({ ...editItem, remark: e.target.value })}\r\n rows={3}\r\n />\r\n
\r\n
\r\n \r\n \r\n \r\n \r\n
\r\n
\r\n
\r\n
\r\n );\r\n}\r\n","usedDeprecatedRules":[]},{"filePath":"D:\\dcprint\\erp-project\\src\\app\\[locale]\\hr\\training\\page.tsx","messages":[{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":91,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":91,"endColumn":38},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":92,"column":18,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":92,"endColumn":25},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction`.","line":93,"column":17,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":93,"endColumn":39},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":93,"column":24,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":93,"endColumn":28},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction`.","line":94,"column":18,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":94,"endColumn":40},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":94,"column":25,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":94,"endColumn":29},{"ruleId":"react-hooks/set-state-in-effect","severity":1,"message":"Error: Calling setState synchronously within an effect can trigger cascading renders\n\nEffects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following:\n* Update external systems with the latest state from React.\n* Subscribe for updates from some external system, calling setState in a callback function when external state changes.\n\nCalling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect).\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\hr\\training\\page.tsx:99:5\n 97 | };\n 98 | useEffect(() => {\n> 99 | fetchData();\n | ^^^^^^^^^ Avoid calling setState() directly within an effect\n 100 | }, [page]);\n 101 |\n 102 | const handleSave = async () => {","line":99,"column":5,"nodeType":null,"endLine":99,"endColumn":14},{"ruleId":"react-hooks/exhaustive-deps","severity":1,"message":"React Hook useEffect has a missing dependency: 'fetchData'. Either include it or remove the dependency array.","line":100,"column":6,"nodeType":"ArrayExpression","endLine":100,"endColumn":12,"suggestions":[{"desc":"Update the dependencies array to be: [fetchData, page]","fix":{"range":[2749,2755],"text":"[fetchData, page]"}}]},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":109,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":109,"endColumn":38},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":110,"column":18,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":110,"endColumn":25},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":115,"column":46,"nodeType":"Property","messageId":"anyAssignment","endLine":115,"endColumn":73},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .message on an `any` value.","line":115,"column":66,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":115,"endColumn":73},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":127,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":127,"endColumn":38},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":128,"column":18,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":128,"endColumn":25},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":140,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":140,"endColumn":38},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":141,"column":18,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":141,"endColumn":25}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":16,"fixableErrorCount":0,"fixableWarningCount":0,"source":"'use client';\r\n\r\nimport { authFetch } from '@/lib/auth-fetch';\r\nimport { useEffect, useState } from 'react';\r\nimport { MainLayout } from '@/components/layout';\r\nimport { Card, CardContent } from '@/components/ui/card';\r\nimport { Button } from '@/components/ui/button';\r\nimport { Input } from '@/components/ui/input';\r\nimport { Badge } from '@/components/ui/badge';\r\nimport {\r\n Table,\r\n TableBody,\r\n TableCell,\r\n TableHead,\r\n TableHeader,\r\n TableRow,\r\n} from '@/components/ui/table';\r\nimport {\r\n Dialog,\r\n DialogContent,\r\n DialogHeader,\r\n DialogTitle,\r\n DialogFooter,\r\n} from '@/components/ui/dialog';\r\nimport { Label } from '@/components/ui/label';\r\nimport {\r\n Select,\r\n SelectContent,\r\n SelectItem,\r\n SelectTrigger,\r\n SelectValue,\r\n} from '@/components/ui/select';\r\nimport { Plus, Search, Edit, Trash2 } from 'lucide-react';\r\nimport { useToast } from '@/hooks/use-toast';\r\nimport { useTranslations } from 'next-intl';\r\n\r\ninterface Item {\r\n id: number;\r\n training_no: string;\r\n training_name: string;\r\n training_type: number;\r\n training_date: string;\r\n training_hours: number;\r\n trainer: string;\r\n training_place: string;\r\n status: number;\r\n}\r\nconst typeMap: Record = {\r\n 1: 'safetyTraining',\r\n 2: 'skillTraining',\r\n 3: 'qualityTraining',\r\n 4: 'managementTraining',\r\n 5: 'newEmployeeTraining',\r\n};\r\nconst statusMap: Record<\r\n number,\r\n { label: string; variant: 'default' | 'secondary' | 'destructive' | 'outline' }\r\n> = {\r\n 0: { label: 'pending', variant: 'outline' },\r\n 1: { label: 'inProgress', variant: 'default' },\r\n 2: { label: 'completed', variant: 'secondary' },\r\n 3: { label: 'cancelled', variant: 'destructive' },\r\n};\r\n\r\nconst formatDate = (dateStr: string) => {\r\n if (!dateStr) return '-';\r\n return dateStr.slice(0, 10);\r\n};\r\n\r\nexport default function TrainingPage() {\r\n // 翻译钩子\r\n const t = useTranslations('Hr');\r\n const tc = useTranslations('Common');\r\n\r\n const { toast } = useToast();\r\n const [list, setList] = useState([]);\r\n const [total, setTotal] = useState(0);\r\n const [page, setPage] = useState(1);\r\n const [searchName, setSearchName] = useState('');\r\n const [showDialog, setShowDialog] = useState(false);\r\n const [editItem, setEditItem] = useState>({});\r\n\r\n const fetchData = async () => {\r\n try {\r\n const params = new URLSearchParams({\r\n page: String(page),\r\n pageSize: '20',\r\n trainingName: searchName,\r\n });\r\n const res = await authFetch('/api/hr/training?' + params);\r\n const result = await res.json();\r\n if (result.success) {\r\n setList(result.data.list || []);\r\n setTotal(result.data.total || 0);\r\n }\r\n } catch {}\r\n };\r\n useEffect(() => {\r\n fetchData();\r\n }, [page]);\r\n\r\n const handleSave = async () => {\r\n try {\r\n const res = await fetch('/api/hr/training', {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify(editItem),\r\n });\r\n const result = await res.json();\r\n if (result.success) {\r\n toast({ title: t('createSuccess') });\r\n setShowDialog(false);\r\n fetchData();\r\n } else {\r\n toast({ title: t('operationFailed'), description: result.message, variant: 'destructive' });\r\n }\r\n } catch {\r\n toast({ title: t('operationFailed'), variant: 'destructive' });\r\n }\r\n };\r\n const handleStatusChange = async (id: number, status: number) => {\r\n try {\r\n const res = await authFetch('/api/hr/training', {\r\n method: 'PUT',\r\n body: JSON.stringify({ id, status }),\r\n });\r\n const result = await res.json();\r\n if (result.success) {\r\n toast({ title: t('updateSuccess') });\r\n fetchData();\r\n }\r\n } catch {\r\n toast({ title: t('operationFailed'), variant: 'destructive' });\r\n }\r\n };\r\n const handleDelete = async (id: number) => {\r\n if (!confirm(t('confirmDelete'))) return;\r\n try {\r\n const res = await fetch('/api/hr/training?id=' + id, { method: 'DELETE' });\r\n const result = await res.json();\r\n if (result.success) {\r\n toast({ title: t('deleteSuccess') });\r\n fetchData();\r\n }\r\n } catch {\r\n toast({ title: t('operationFailed'), variant: 'destructive' });\r\n }\r\n };\r\n\r\n return (\r\n \r\n
\r\n
\r\n

{t('trainingManagement')}

\r\n
\r\n
\r\n setSearchName(e.target.value)}\r\n className=\"w-36 h-8 text-sm\"\r\n />\r\n \r\n
\r\n {\r\n setEditItem({});\r\n setShowDialog(true);\r\n }}\r\n >\r\n \r\n {t('newTraining')}\r\n \r\n
\r\n
\r\n \r\n \r\n \r\n \r\n \r\n {tc('serialNo')}\r\n {tc('trainingNo')}\r\n {tc('trainingName')}\r\n {tc('trainingType')}\r\n {tc('trainingDate')}\r\n {tc('hours')}\r\n {tc('trainer')}\r\n {tc('location')}\r\n {tc('status')}\r\n {tc('actions')}\r\n \r\n \r\n \r\n {list.map((item, index) => {\r\n const st = statusMap[item.status] || statusMap[1];\r\n return (\r\n \r\n \r\n {(page - 1) * 20 + index + 1}\r\n \r\n {item.training_no}\r\n {item.training_name}\r\n \r\n {t(typeMap[item.training_type] || 'unknown')}\r\n \r\n {formatDate(item.training_date)}\r\n {item.training_hours || '-'}h\r\n {item.trainer || '-'}\r\n {item.training_place || '-'}\r\n \r\n \r\n {st.label}\r\n \r\n \r\n \r\n
\r\n {item.status === 1 && (\r\n handleStatusChange(item.id, 2)}\r\n >\r\n {tc('start')}\r\n \r\n )}\r\n {item.status === 2 && (\r\n handleStatusChange(item.id, 3)}\r\n >\r\n {tc('complete')}\r\n \r\n )}\r\n {\r\n setEditItem(item);\r\n setShowDialog(true);\r\n }}\r\n >\r\n \r\n \r\n handleDelete(item.id)}\r\n >\r\n \r\n \r\n
\r\n
\r\n
\r\n );\r\n })}\r\n {list.length === 0 && (\r\n \r\n \r\n {tc('noRecords')}\r\n \r\n \r\n )}\r\n
\r\n
\r\n
\r\n
\r\n
\r\n \r\n {tc('totalRecords', { count: total })}\r\n \r\n
\r\n setPage((p) => p - 1)}\r\n >\r\n {tc('prevPage')}\r\n \r\n = total}\r\n onClick={() => setPage((p) => p + 1)}\r\n >\r\n {tc('nextPage')}\r\n \r\n
\r\n
\r\n \r\n \r\n \r\n {t('addTrainingTitle')}\r\n \r\n
\r\n
\r\n \r\n setEditItem({ ...editItem, training_name: e.target.value })}\r\n />\r\n
\r\n
\r\n \r\n setEditItem({ ...editItem, training_type: Number(v) })}\r\n >\r\n \r\n \r\n \r\n \r\n {tc('safetyTraining')}\r\n {tc('skillTraining')}\r\n {tc('qualityTraining')}\r\n {tc('managementTraining')}\r\n {tc('newEmployeeTraining')}\r\n \r\n \r\n
\r\n
\r\n \r\n setEditItem({ ...editItem, training_date: e.target.value })}\r\n />\r\n
\r\n
\r\n \r\n \r\n setEditItem({ ...editItem, training_hours: Number(e.target.value) })\r\n }\r\n />\r\n
\r\n
\r\n \r\n setEditItem({ ...editItem, trainer: e.target.value })}\r\n />\r\n
\r\n
\r\n \r\n setEditItem({ ...editItem, training_place: e.target.value })}\r\n />\r\n
\r\n
\r\n \r\n \r\n \r\n \r\n
\r\n
\r\n
\r\n
\r\n );\r\n}\r\n","usedDeprecatedRules":[]},{"filePath":"D:\\dcprint\\erp-project\\src\\app\\[locale]\\layout.tsx","messages":[{"ruleId":"@typescript-eslint/no-unsafe-return","severity":1,"message":"Unsafe return of a value of type `any`.","line":21,"column":5,"nodeType":"ReturnStatement","messageId":"unsafeReturn","endLine":21,"endColumn":71},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .default on an `any` value.","line":21,"column":63,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":21,"endColumn":70},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":44,"column":9,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":44,"endColumn":53},{"ruleId":"@typescript-eslint/no-unsafe-return","severity":1,"message":"Unsafe return of a value of type `any`.","line":45,"column":3,"nodeType":"ReturnStatement","messageId":"unsafeReturn","endLine":45,"endColumn":50},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .Common on an `any` value.","line":45,"column":20,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":45,"endColumn":26},{"ruleId":"i18n/no-chinese-hardcode","severity":1,"message":"禁止硬编码中文文本: \"公司名称\"。建议使用: tc('text_amf4wf')","line":45,"column":43,"nodeType":"Literal","messageId":"noChineseHardcode","endLine":45,"endColumn":49},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":90,"column":9,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":90,"endColumn":53},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":92,"column":9,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":92,"endColumn":54},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .Common on an `any` value.","line":92,"column":27,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":92,"endColumn":33},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":93,"column":9,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":93,"endColumn":72},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .Common on an `any` value.","line":93,"column":33,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":93,"endColumn":39},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":96,"column":5,"nodeType":"Property","messageId":"anyAssignment","endLine":96,"endColumn":10},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":97,"column":5,"nodeType":"Property","messageId":"anyAssignment","endLine":97,"endColumn":16}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":13,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import type { Metadata } from 'next';\r\nimport { getMessages } from 'next-intl/server';\r\nimport { cookies } from 'next/headers';\r\nimport { IntlProvider } from '@/components/IntlProvider';\r\nimport { notFound } from 'next/navigation';\r\nimport { locales, type Locale } from '@/i18n/locales';\r\nimport { AuthProvider, type InitialAuthData } from '@/contexts/AuthContext';\r\nimport { ToastProviderComponent } from '@/components/ui/toast';\r\nimport { SnowAdminThemeProvider } from '@/hooks/useSnowAdminTheme';\r\nimport SystemConfigInitializer from '@/components/SystemConfigInitializer';\r\nimport { HtmlLangSetter } from '@/components/HtmlLangSetter';\r\nimport { query } from '@/lib/db';\r\nimport { getMenusByToken } from '@/lib/menu-service';\r\n\r\nlet cachedCompanyName: string | null = null;\r\nlet cacheTimestamp: number = 0;\r\nconst CACHE_TTL = 5 * 60 * 1000;\r\n\r\nasync function getMessagesByLocale(locale: string) {\r\n try {\r\n return (await import(`../../../messages/${locale}.json`)).default;\r\n } catch {\r\n return (await import(`../../../messages/zh-CN.json`)).default;\r\n }\r\n}\r\n\r\nasync function getCompanyName(locale: string): Promise {\r\n const now = Date.now();\r\n if (cachedCompanyName && now - cacheTimestamp < CACHE_TTL) {\r\n return cachedCompanyName;\r\n }\r\n try {\r\n const rows = await query<{ config_value: string }>(\r\n `SELECT config_value FROM sys_config WHERE config_key IN ('company_name', 'company_short_name') ORDER BY FIELD(config_key, 'company_name', 'company_short_name') LIMIT 1`\r\n );\r\n if (Array.isArray(rows) && rows.length > 0 && rows[0]?.config_value) {\r\n cachedCompanyName = rows[0].config_value;\r\n cacheTimestamp = now;\r\n return cachedCompanyName!;\r\n }\r\n } catch {\r\n if (cachedCompanyName) return cachedCompanyName;\r\n }\r\n const messages = await getMessagesByLocale(locale);\r\n return messages?.Common?.companyName || '公司名称';\r\n}\r\n\r\n/**\r\n * 服务端预取菜单数据。\r\n *\r\n * 读取 access_token cookie,若存在则轻量级校验 JWT 并查询菜单。\r\n * 任何失败(无 cookie / token 过期 / DB 异常)均返回 null,\r\n * AuthProvider 会降级到客户端 fetch + localStorage 缓存。\r\n *\r\n * 注意:服务端不能 HTTP 自调 /api/auth/menus,必须直接调用菜单服务函数。\r\n */\r\nasync function prefetchMenus(): Promise {\r\n try {\r\n const cookieStore = await cookies();\r\n const token = cookieStore.get('access_token')?.value;\r\n if (!token) return null;\r\n\r\n const result = await getMenusByToken(token);\r\n if (!result) return null;\r\n\r\n return {\r\n menus: result.menus,\r\n permissions: result.permissions,\r\n };\r\n } catch {\r\n // SSR 预取失败时静默降级,不影响页面渲染\r\n return null;\r\n }\r\n}\r\n\r\nexport function generateStaticParams() {\r\n return locales.map((locale) => ({ locale }));\r\n}\r\n\r\nexport async function generateMetadata({\r\n params,\r\n}: {\r\n params: Promise<{ locale: string }>;\r\n}): Promise {\r\n const { locale: rawLocale } = await params;\r\n const locale = (locales as readonly string[]).includes(rawLocale)\r\n ? (rawLocale as Locale)\r\n : 'zh-CN';\r\n\r\n const messages = await getMessagesByLocale(locale);\r\n\r\n const title = messages?.Common?.appTitle || 'VNERP';\r\n const description = messages?.Common?.appDescription || 'VNERP ERP系统';\r\n\r\n return {\r\n title,\r\n description,\r\n };\r\n}\r\n\r\nexport default async function LocaleLayout({\r\n children,\r\n params,\r\n}: {\r\n children: React.ReactNode;\r\n params: Promise<{ locale: string }>;\r\n}) {\r\n const { locale: rawLocale } = await params;\r\n const locale = (locales as readonly string[]).includes(rawLocale)\r\n ? (rawLocale as Locale)\r\n : 'zh-CN';\r\n\r\n if (!locales.includes(locale)) {\r\n notFound();\r\n }\r\n\r\n const messages = await getMessages();\r\n const _companyName = await getCompanyName(locale);\r\n const initialAuth = await prefetchMenus();\r\n\r\n return (\r\n <>\r\n \r\n \r\n \r\n \r\n \r\n {children}\r\n \r\n \r\n \r\n \r\n );\r\n}\r\n","usedDeprecatedRules":[]},{"filePath":"D:\\dcprint\\erp-project\\src\\app\\[locale]\\login\\page.tsx","messages":[{"ruleId":"react-hooks/refs","severity":1,"message":"Error: Cannot access refs during render\n\nReact refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef).\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\login\\page.tsx:65:25\n 63 | };\n 64 |\n> 65 | const pupilPosition = calculatePupilPosition();\n | ^^^^^^^^^^^^^^^^^^^^^^ This function accesses a ref value\n 66 |\n 67 | return (\n 68 | 131 | const pupilPosition = calculatePupilPosition();\n | ^^^^^^^^^^^^^^^^^^^^^^ This function accesses a ref value\n 132 |\n 133 | return (\n 134 | {\n> 169 | setMounted(true);\n | ^^^^^^^^^^ Avoid calling setState() directly within an effect\n 170 | if (!isLoading && isAuthenticated) {\n 171 | router.replace('/dashboard');\n 172 | }","line":169,"column":5,"nodeType":null,"endLine":169,"endColumn":15},{"ruleId":"react-hooks/set-state-in-effect","severity":1,"message":"Error: Calling setState synchronously within an effect can trigger cascading renders\n\nEffects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following:\n* Update external systems with the latest state from React.\n* Subscribe for updates from some external system, calling setState in a callback function when external state changes.\n\nCalling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect).\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\login\\page.tsx:253:7\n 251 | useEffect(() => {\n 252 | if (isTyping) {\n> 253 | setIsLookingAtEachOther(true);\n | ^^^^^^^^^^^^^^^^^^^^^^^ Avoid calling setState() directly within an effect\n 254 | const timer = setTimeout(() => {\n 255 | setIsLookingAtEachOther(false);\n 256 | }, 800);","line":253,"column":7,"nodeType":null,"endLine":253,"endColumn":30},{"ruleId":"react-hooks/set-state-in-effect","severity":1,"message":"Error: Calling setState synchronously within an effect can trigger cascading renders\n\nEffects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following:\n* Update external systems with the latest state from React.\n* Subscribe for updates from some external system, calling setState in a callback function when external state changes.\n\nCalling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect).\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\login\\page.tsx:280:7\n 278 | return () => clearTimeout(firstPeek);\n 279 | } else {\n> 280 | setIsPurplePeeking(false);\n | ^^^^^^^^^^^^^^^^^^ Avoid calling setState() directly within an effect\n 281 | }\n 282 | }, [loginForm.password, showPassword, isPurplePeeking]);\n 283 |","line":280,"column":7,"nodeType":null,"endLine":280,"endColumn":25},{"ruleId":"react-hooks/refs","severity":1,"message":"Error: Cannot access refs during render\n\nReact refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef).\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\login\\page.tsx:297:21\n 295 | };\n 296 |\n> 297 | const purplePos = calculatePosition(purpleRef);\n | ^^^^^^^^^^^^^^^^^ This function accesses a ref value\n 298 | const blackPos = calculatePosition(blackRef);\n 299 | const yellowPos = calculatePosition(yellowRef);\n 300 | const orangePos = calculatePosition(orangeRef);","line":297,"column":21,"nodeType":null,"endLine":297,"endColumn":38},{"ruleId":"react-hooks/refs","severity":1,"message":"Error: Cannot access refs during render\n\nReact refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef).\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\login\\page.tsx:298:20\n 296 |\n 297 | const purplePos = calculatePosition(purpleRef);\n> 298 | const blackPos = calculatePosition(blackRef);\n | ^^^^^^^^^^^^^^^^^ This function accesses a ref value\n 299 | const yellowPos = calculatePosition(yellowRef);\n 300 | const orangePos = calculatePosition(orangeRef);\n 301 |","line":298,"column":20,"nodeType":null,"endLine":298,"endColumn":37},{"ruleId":"react-hooks/refs","severity":1,"message":"Error: Cannot access refs during render\n\nReact refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef).\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\login\\page.tsx:299:21\n 297 | const purplePos = calculatePosition(purpleRef);\n 298 | const blackPos = calculatePosition(blackRef);\n> 299 | const yellowPos = calculatePosition(yellowRef);\n | ^^^^^^^^^^^^^^^^^ This function accesses a ref value\n 300 | const orangePos = calculatePosition(orangeRef);\n 301 |\n 302 | const handleLogin = async (e: React.FormEvent) => {","line":299,"column":21,"nodeType":null,"endLine":299,"endColumn":38},{"ruleId":"react-hooks/refs","severity":1,"message":"Error: Cannot access refs during render\n\nReact refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef).\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\login\\page.tsx:300:21\n 298 | const blackPos = calculatePosition(blackRef);\n 299 | const yellowPos = calculatePosition(yellowRef);\n> 300 | const orangePos = calculatePosition(orangeRef);\n | ^^^^^^^^^^^^^^^^^ This function accesses a ref value\n 301 |\n 302 | const handleLogin = async (e: React.FormEvent) => {\n 303 | e.preventDefault();","line":300,"column":21,"nodeType":null,"endLine":300,"endColumn":38},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":313,"column":17,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":313,"endColumn":43},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .firstLogin on an `any` value.","line":314,"column":20,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":314,"endColumn":30},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction`.","line":315,"column":31,"nodeType":"MemberExpression","messageId":"unsafeArgument","endLine":315,"endColumn":38},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .id on an `any` value.","line":315,"column":36,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":315,"endColumn":38},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":353,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":353,"endColumn":38},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":354,"column":18,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":354,"endColumn":25},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":359,"column":17,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":359,"endColumn":43},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .firstLogin on an `any` value.","line":360,"column":16,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":360,"endColumn":26},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `string | number | bigint | boolean | ReactElement> | Iterable | ... 4 more ... | undefined`.","line":368,"column":21,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":368,"endColumn":45},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .message on an `any` value.","line":368,"column":28,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":368,"endColumn":35}],"suppressedMessages":[{"ruleId":"@next/next/no-img-element","severity":1,"message":"Using `` could result in slower LCP and higher bandwidth. Consider using `` from `next/image` or a custom image loader to automatically optimize images. This may incur additional usage or cost from your provider. See: https://nextjs.org/docs/messages/no-img-element","line":391,"column":13,"nodeType":"JSXOpeningElement","endLine":391,"endColumn":83,"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"@next/next/no-img-element","severity":1,"message":"Using `` could result in slower LCP and higher bandwidth. Consider using `` from `next/image` or a custom image loader to automatically optimize images. This may incur additional usage or cost from your provider. See: https://nextjs.org/docs/messages/no-img-element","line":698,"column":13,"nodeType":"JSXOpeningElement","endLine":698,"endColumn":83,"suppressions":[{"kind":"directive","justification":""}]}],"errorCount":0,"fatalErrorCount":0,"warningCount":19,"fixableErrorCount":0,"fixableWarningCount":0,"source":"'use client';\r\n\r\nimport { useState, useEffect, useRef } from 'react';\r\nimport { useRouter } from '@/i18n/navigation';\r\nimport { Input } from '@/components/ui/input';\r\nimport { Button } from '@/components/ui/button';\r\nimport { Label } from '@/components/ui/label';\r\nimport { Checkbox } from '@/components/ui/checkbox';\r\nimport {\r\n Dialog,\r\n DialogContent,\r\n DialogHeader,\r\n DialogTitle,\r\n DialogFooter,\r\n} from '@/components/ui/dialog';\r\nimport { Loader2, Eye, EyeOff, KeyRound } from 'lucide-react';\r\nimport { toast } from 'sonner';\r\nimport { useAuth } from '@/contexts/AuthContext';\r\nimport { useCompanyName } from '@/hooks/useCompanyName';\r\nimport { useTranslations } from 'next-intl';\r\n\r\ninterface PupilProps {\r\n size?: number;\r\n maxDistance?: number;\r\n pupilColor?: string;\r\n forceLookX?: number;\r\n forceLookY?: number;\r\n}\r\n\r\nconst Pupil = ({\r\n size = 12,\r\n maxDistance = 5,\r\n pupilColor = 'black',\r\n forceLookX,\r\n forceLookY,\r\n}: PupilProps) => {\r\n const [mouseX, setMouseX] = useState(0);\r\n const [mouseY, setMouseY] = useState(0);\r\n const pupilRef = useRef(null);\r\n\r\n useEffect(() => {\r\n const handleMouseMove = (e: MouseEvent) => {\r\n setMouseX(e.clientX);\r\n setMouseY(e.clientY);\r\n };\r\n window.addEventListener('mousemove', handleMouseMove);\r\n return () => window.removeEventListener('mousemove', handleMouseMove);\r\n }, []);\r\n\r\n const calculatePupilPosition = () => {\r\n if (!pupilRef.current) return { x: 0, y: 0 };\r\n if (forceLookX !== undefined && forceLookY !== undefined) {\r\n return { x: forceLookX, y: forceLookY };\r\n }\r\n const pupil = pupilRef.current.getBoundingClientRect();\r\n const pupilCenterX = pupil.left + pupil.width / 2;\r\n const pupilCenterY = pupil.top + pupil.height / 2;\r\n const deltaX = mouseX - pupilCenterX;\r\n const deltaY = mouseY - pupilCenterY;\r\n const distance = Math.min(Math.sqrt(deltaX ** 2 + deltaY ** 2), maxDistance);\r\n const angle = Math.atan2(deltaY, deltaX);\r\n return { x: Math.cos(angle) * distance, y: Math.sin(angle) * distance };\r\n };\r\n\r\n const pupilPosition = calculatePupilPosition();\r\n\r\n return (\r\n \r\n );\r\n};\r\n\r\ninterface EyeBallProps {\r\n size?: number;\r\n pupilSize?: number;\r\n maxDistance?: number;\r\n eyeColor?: string;\r\n pupilColor?: string;\r\n isBlinking?: boolean;\r\n forceLookX?: number;\r\n forceLookY?: number;\r\n}\r\n\r\nconst EyeBall = ({\r\n size = 48,\r\n pupilSize = 16,\r\n maxDistance = 10,\r\n eyeColor = 'white',\r\n pupilColor = 'black',\r\n isBlinking = false,\r\n forceLookX,\r\n forceLookY,\r\n}: EyeBallProps) => {\r\n const [mouseX, setMouseX] = useState(0);\r\n const [mouseY, setMouseY] = useState(0);\r\n const eyeRef = useRef(null);\r\n\r\n useEffect(() => {\r\n const handleMouseMove = (e: MouseEvent) => {\r\n setMouseX(e.clientX);\r\n setMouseY(e.clientY);\r\n };\r\n window.addEventListener('mousemove', handleMouseMove);\r\n return () => window.removeEventListener('mousemove', handleMouseMove);\r\n }, []);\r\n\r\n const calculatePupilPosition = () => {\r\n if (!eyeRef.current) return { x: 0, y: 0 };\r\n if (forceLookX !== undefined && forceLookY !== undefined) {\r\n return { x: forceLookX, y: forceLookY };\r\n }\r\n const eye = eyeRef.current.getBoundingClientRect();\r\n const eyeCenterX = eye.left + eye.width / 2;\r\n const eyeCenterY = eye.top + eye.height / 2;\r\n const deltaX = mouseX - eyeCenterX;\r\n const deltaY = mouseY - eyeCenterY;\r\n const distance = Math.min(Math.sqrt(deltaX ** 2 + deltaY ** 2), maxDistance);\r\n const angle = Math.atan2(deltaY, deltaX);\r\n return { x: Math.cos(angle) * distance, y: Math.sin(angle) * distance };\r\n };\r\n\r\n const pupilPosition = calculatePupilPosition();\r\n\r\n return (\r\n \r\n {!isBlinking && (\r\n \r\n )}\r\n
\r\n );\r\n};\r\n\r\nexport default function LoginPage() {\r\n const router = useRouter();\r\n const { login, isAuthenticated, isLoading } = useAuth();\r\n const { companyName } = useCompanyName();\r\n const t = useTranslations('Auth');\r\n const tc = useTranslations('Common');\r\n const [mounted, setMounted] = useState(false);\r\n\r\n useEffect(() => {\r\n setMounted(true);\r\n if (!isLoading && isAuthenticated) {\r\n router.replace('/dashboard');\r\n }\r\n }, [isLoading, isAuthenticated, router]);\r\n\r\n const [loginForm, setLoginForm] = useState({\r\n username: '',\r\n password: '',\r\n rememberMe: false,\r\n });\r\n\r\n const [showPassword, setShowPassword] = useState(false);\r\n const [loading, setLoading] = useState(false);\r\n const [error, setError] = useState('');\r\n\r\n const [showChangePwd, setShowChangePwd] = useState(false);\r\n const [changePwdForm, setChangePwdForm] = useState({\r\n oldPassword: '',\r\n newPassword: '',\r\n confirmPassword: '',\r\n });\r\n const [changePwdLoading, setChangePwdLoading] = useState(false);\r\n const [loggedInUserId, setLoggedInUserId] = useState(null);\r\n\r\n const [mouseX, setMouseX] = useState(0);\r\n const [mouseY, setMouseY] = useState(0);\r\n const [isPurpleBlinking, setIsPurpleBlinking] = useState(false);\r\n const [isBlackBlinking, setIsBlackBlinking] = useState(false);\r\n const [isTyping, setIsTyping] = useState(false);\r\n const [isLookingAtEachOther, setIsLookingAtEachOther] = useState(false);\r\n const [isPurplePeeking, setIsPurplePeeking] = useState(false);\r\n const purpleRef = useRef(null);\r\n const blackRef = useRef(null);\r\n const yellowRef = useRef(null);\r\n const orangeRef = useRef(null);\r\n\r\n useEffect(() => {\r\n const handleMouseMove = (e: MouseEvent) => {\r\n setMouseX(e.clientX);\r\n setMouseY(e.clientY);\r\n };\r\n window.addEventListener('mousemove', handleMouseMove);\r\n return () => window.removeEventListener('mousemove', handleMouseMove);\r\n }, []);\r\n\r\n useEffect(() => {\r\n const scheduleBlink = () => {\r\n const timeout = setTimeout(\r\n () => {\r\n setIsPurpleBlinking(true);\r\n setTimeout(() => {\r\n setIsPurpleBlinking(false);\r\n scheduleBlink();\r\n }, 150);\r\n },\r\n Math.random() * 4000 + 3000\r\n );\r\n return timeout;\r\n };\r\n const timeout = scheduleBlink();\r\n return () => clearTimeout(timeout);\r\n }, []);\r\n\r\n useEffect(() => {\r\n const scheduleBlink = () => {\r\n const timeout = setTimeout(\r\n () => {\r\n setIsBlackBlinking(true);\r\n setTimeout(() => {\r\n setIsBlackBlinking(false);\r\n scheduleBlink();\r\n }, 150);\r\n },\r\n Math.random() * 4000 + 3000\r\n );\r\n return timeout;\r\n };\r\n const timeout = scheduleBlink();\r\n return () => clearTimeout(timeout);\r\n }, []);\r\n\r\n useEffect(() => {\r\n if (isTyping) {\r\n setIsLookingAtEachOther(true);\r\n const timer = setTimeout(() => {\r\n setIsLookingAtEachOther(false);\r\n }, 800);\r\n return () => clearTimeout(timer);\r\n } else {\r\n setIsLookingAtEachOther(false);\r\n }\r\n }, [isTyping]);\r\n\r\n useEffect(() => {\r\n if (loginForm.password.length > 0 && showPassword) {\r\n const schedulePeek = () => {\r\n const peekInterval = setTimeout(\r\n () => {\r\n setIsPurplePeeking(true);\r\n setTimeout(() => {\r\n setIsPurplePeeking(false);\r\n }, 800);\r\n },\r\n Math.random() * 3000 + 2000\r\n );\r\n return peekInterval;\r\n };\r\n const firstPeek = schedulePeek();\r\n return () => clearTimeout(firstPeek);\r\n } else {\r\n setIsPurplePeeking(false);\r\n }\r\n }, [loginForm.password, showPassword, isPurplePeeking]);\r\n\r\n const calculatePosition = (ref: React.RefObject) => {\r\n if (!ref.current) return { faceX: 0, faceY: 0, bodySkew: 0 };\r\n const rect = ref.current.getBoundingClientRect();\r\n const centerX = rect.left + rect.width / 2;\r\n const centerY = rect.top + rect.height / 3;\r\n const deltaX = mouseX - centerX;\r\n const deltaY = mouseY - centerY;\r\n const faceX = Math.max(-15, Math.min(15, deltaX / 20));\r\n const faceY = Math.max(-10, Math.min(10, deltaY / 30));\r\n const bodySkew = Math.max(-6, Math.min(6, -deltaX / 120));\r\n return { faceX, faceY, bodySkew };\r\n };\r\n\r\n const purplePos = calculatePosition(purpleRef);\r\n const blackPos = calculatePosition(blackRef);\r\n const yellowPos = calculatePosition(yellowRef);\r\n const orangePos = calculatePosition(orangeRef);\r\n\r\n const handleLogin = async (e: React.FormEvent) => {\r\n e.preventDefault();\r\n setError('');\r\n setLoading(true);\r\n\r\n try {\r\n const result = await login(loginForm.username, loginForm.password, loginForm.rememberMe);\r\n\r\n if (result.success) {\r\n const userStr = localStorage.getItem('user') || sessionStorage.getItem('user');\r\n if (userStr) {\r\n const user = JSON.parse(userStr);\r\n if (user.firstLogin) {\r\n setLoggedInUserId(user.id);\r\n setShowChangePwd(true);\r\n setLoading(false);\r\n return;\r\n }\r\n }\r\n toast.success(t('loginSuccess'));\r\n router.replace('/dashboard');\r\n } else {\r\n setError(result.message || t('loginFailed'));\r\n }\r\n } catch {\r\n setError(t('loginFailed'));\r\n } finally {\r\n setLoading(false);\r\n }\r\n };\r\n\r\n const handleChangePwd = async () => {\r\n if (!changePwdForm.newPassword || changePwdForm.newPassword.length < 6) {\r\n toast.error(t('passwordMismatch'));\r\n return;\r\n }\r\n if (changePwdForm.newPassword !== changePwdForm.confirmPassword) {\r\n toast.error(t('passwordMismatch'));\r\n return;\r\n }\r\n setChangePwdLoading(true);\r\n try {\r\n const res = await fetch('/api/auth/change-password', {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify({\r\n userId: loggedInUserId,\r\n oldPassword: changePwdForm.oldPassword,\r\n newPassword: changePwdForm.newPassword,\r\n }),\r\n });\r\n const result = await res.json();\r\n if (result.success) {\r\n toast.success(t('changePassword') + ' - OK');\r\n setShowChangePwd(false);\r\n const userStr = localStorage.getItem('user') || sessionStorage.getItem('user');\r\n if (userStr) {\r\n const user = JSON.parse(userStr);\r\n user.firstLogin = false;\r\n (localStorage.getItem('user') ? localStorage : sessionStorage).setItem(\r\n 'user',\r\n JSON.stringify(user)\r\n );\r\n }\r\n router.replace('/dashboard');\r\n } else {\r\n toast.error(result.message || '修改失败');\r\n }\r\n } catch {\r\n toast.error(t('changePassword') + ' - ' + tc('error'));\r\n } finally {\r\n setChangePwdLoading(false);\r\n }\r\n };\r\n\r\n if (!mounted) {\r\n return (\r\n
\r\n \r\n
\r\n );\r\n }\r\n\r\n return (\r\n
\r\n \r\n
\r\n\r\n 0 && showPassword\r\n ? 'skewX(0deg)'\r\n : `skewX(${orangePos.bodySkew || 0}deg)`,\r\n transformOrigin: 'bottom center',\r\n }}\r\n >\r\n 0 && showPassword\r\n ? '50px'\r\n : `${82 + (orangePos.faceX || 0)}px`,\r\n top:\r\n loginForm.password.length > 0 && showPassword\r\n ? '85px'\r\n : `${90 + (orangePos.faceY || 0)}px`,\r\n }}\r\n >\r\n 0 && showPassword ? -5 : undefined}\r\n forceLookY={loginForm.password.length > 0 && showPassword ? -4 : undefined}\r\n />\r\n 0 && showPassword ? -5 : undefined}\r\n forceLookY={loginForm.password.length > 0 && showPassword ? -4 : undefined}\r\n />\r\n
\r\n
\r\n\r\n 0 && showPassword\r\n ? 'skewX(0deg)'\r\n : `skewX(${yellowPos.bodySkew || 0}deg)`,\r\n transformOrigin: 'bottom center',\r\n }}\r\n >\r\n 0 && showPassword\r\n ? '20px'\r\n : `${52 + (yellowPos.faceX || 0)}px`,\r\n top:\r\n loginForm.password.length > 0 && showPassword\r\n ? '35px'\r\n : `${40 + (yellowPos.faceY || 0)}px`,\r\n }}\r\n >\r\n 0 && showPassword ? -5 : undefined}\r\n forceLookY={loginForm.password.length > 0 && showPassword ? -4 : undefined}\r\n />\r\n 0 && showPassword ? -5 : undefined}\r\n forceLookY={loginForm.password.length > 0 && showPassword ? -4 : undefined}\r\n />\r\n
\r\n 0 && showPassword\r\n ? '10px'\r\n : `${40 + (yellowPos.faceX || 0)}px`,\r\n top:\r\n loginForm.password.length > 0 && showPassword\r\n ? '88px'\r\n : `${88 + (yellowPos.faceY || 0)}px`,\r\n }}\r\n />\r\n
\r\n
\r\n
\r\n\r\n
\r\n {companyName}\r\n {tc('footerCopyright')}\r\n
\r\n\r\n
\r\n
\r\n
\r\n
\r\n\r\n
\r\n
\r\n
\r\n {/* eslint-disable-next-line @next/next/no-img-element */}\r\n {companyName}\r\n {companyName}\r\n
\r\n\r\n
\r\n

{t('welcome')}

\r\n

{t('login')}

\r\n
\r\n\r\n
\r\n
\r\n \r\n setLoginForm({ ...loginForm, username: e.target.value })}\r\n onFocus={() => setIsTyping(true)}\r\n onBlur={() => setIsTyping(false)}\r\n required\r\n className=\"h-12 bg-background border-border/60 focus:border-primary\"\r\n data-testid=\"username-input\"\r\n />\r\n
\r\n\r\n
\r\n \r\n
\r\n setLoginForm({ ...loginForm, password: e.target.value })}\r\n required\r\n className=\"h-12 pr-10 bg-background border-border/60 focus:border-primary\"\r\n data-testid=\"password-input\"\r\n />\r\n setShowPassword(!showPassword)}\r\n className=\"absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-foreground transition-colors\"\r\n >\r\n {showPassword ? : }\r\n \r\n
\r\n
\r\n\r\n
\r\n
\r\n \r\n setLoginForm({ ...loginForm, rememberMe: checked as boolean })\r\n }\r\n />\r\n \r\n
\r\n
\r\n\r\n {error && (\r\n \r\n {error}\r\n
\r\n )}\r\n\r\n \r\n {loading ? (\r\n
\r\n \r\n {t('login')}...\r\n
\r\n ) : (\r\n t('login')\r\n )}\r\n \r\n \r\n
\r\n
\r\n\r\n {\r\n if (!open && !changePwdLoading) setShowChangePwd(false);\r\n }}\r\n >\r\n e.preventDefault()}\r\n onInteractOutside={(e) => e.preventDefault()}\r\n >\r\n \r\n \r\n \r\n {t('firstLogin')}\r\n \r\n \r\n
\r\n
\r\n \r\n \r\n setChangePwdForm({ ...changePwdForm, oldPassword: e.target.value })\r\n }\r\n placeholder={`${tc('pleaseInput')} ${t('oldPassword')}`}\r\n />\r\n
\r\n
\r\n \r\n \r\n setChangePwdForm({ ...changePwdForm, newPassword: e.target.value })\r\n }\r\n placeholder=\"6+\"\r\n />\r\n
\r\n
\r\n \r\n \r\n setChangePwdForm({ ...changePwdForm, confirmPassword: e.target.value })\r\n }\r\n placeholder={`${tc('pleaseInput')} ${t('confirmPassword')}`}\r\n />\r\n
\r\n
\r\n \r\n \r\n \r\n \r\n \r\n
\r\n );\r\n}\r\n","usedDeprecatedRules":[]},{"filePath":"D:\\dcprint\\erp-project\\src\\app\\[locale]\\material-requisitions\\error.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\dcprint\\erp-project\\src\\app\\[locale]\\material-requisitions\\page.tsx","messages":[{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .type on an `any` value.","line":80,"column":39,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":80,"endColumn":43},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":82,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":82,"endColumn":79},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `Record | undefined`.","line":82,"column":72,"nodeType":"Identifier","messageId":"unsafeArgument","endLine":82,"endColumn":78},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":83,"column":18,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":83,"endColumn":25},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction`.","line":84,"column":25,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":84,"endColumn":47},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":84,"column":32,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":84,"endColumn":36},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `SetStateAction`.","line":85,"column":18,"nodeType":"LogicalExpression","messageId":"unsafeArgument","endLine":85,"endColumn":40},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .data on an `any` value.","line":85,"column":25,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":85,"endColumn":29},{"ruleId":"react-hooks/set-state-in-effect","severity":1,"message":"Error: Calling setState synchronously within an effect can trigger cascading renders\n\nEffects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following:\n* Update external systems with the latest state from React.\n* Subscribe for updates from some external system, calling setState in a callback function when external state changes.\n\nCalling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect).\n\nD:\\dcprint\\erp-project\\src\\app\\[locale]\\material-requisitions\\page.tsx:95:5\n 93 |\n 94 | useEffect(() => {\n> 95 | loadRequisitions();\n | ^^^^^^^^^^^^^^^^ Avoid calling setState() directly within an effect\n 96 | }, [page, activeTab]);\n 97 |\n 98 | const getStatusBadge = (status: number) => {","line":95,"column":5,"nodeType":null,"endLine":95,"endColumn":21},{"ruleId":"react-hooks/exhaustive-deps","severity":1,"message":"React Hook useEffect has a missing dependency: 'loadRequisitions'. Either include it or remove the dependency array.","line":96,"column":6,"nodeType":"ArrayExpression","endLine":96,"endColumn":23,"suggestions":[{"desc":"Update the dependencies array to be: [page, activeTab, loadRequisitions]","fix":{"range":[3001,3018],"text":"[page, activeTab, loadRequisitions]"}}]},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":106,"column":28,"nodeType":"MemberExpression","messageId":"anyAssignment","endLine":106,"endColumn":37},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":124,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":127,"endColumn":9},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":128,"column":18,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":128,"endColumn":25},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `string | number | bigint | boolean | ReactElement> | Iterable | ... 4 more ... | undefined`.","line":129,"column":23,"nodeType":"MemberExpression","messageId":"unsafeArgument","endLine":129,"endColumn":37},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .message on an `any` value.","line":129,"column":30,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":129,"endColumn":37},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `string | number | bigint | boolean | ReactElement> | Iterable | ... 4 more ... | undefined`.","line":134,"column":21,"nodeType":"MemberExpression","messageId":"unsafeArgument","endLine":134,"endColumn":35},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .message on an `any` value.","line":134,"column":28,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":134,"endColumn":35},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":147,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":153,"endColumn":9},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":154,"column":18,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":154,"endColumn":25},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `string | number | bigint | boolean | ReactElement> | Iterable | ... 4 more ... | undefined`.","line":155,"column":23,"nodeType":"MemberExpression","messageId":"unsafeArgument","endLine":155,"endColumn":37},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .message on an `any` value.","line":155,"column":30,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":155,"endColumn":37},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `string | number | bigint | boolean | ReactElement> | Iterable | ... 4 more ... | undefined`.","line":159,"column":21,"nodeType":"MemberExpression","messageId":"unsafeArgument","endLine":159,"endColumn":35},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .message on an `any` value.","line":159,"column":28,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":159,"endColumn":35},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":172,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":178,"endColumn":9},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":179,"column":18,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":179,"endColumn":25},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `string | number | bigint | boolean | ReactElement> | Iterable | ... 4 more ... | undefined`.","line":180,"column":23,"nodeType":"MemberExpression","messageId":"unsafeArgument","endLine":180,"endColumn":37},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .message on an `any` value.","line":180,"column":30,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":180,"endColumn":37},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `string | number | bigint | boolean | ReactElement> | Iterable | ... 4 more ... | undefined`.","line":184,"column":21,"nodeType":"MemberExpression","messageId":"unsafeArgument","endLine":184,"endColumn":35},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .message on an `any` value.","line":184,"column":28,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":184,"endColumn":35},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":197,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":197,"endColumn":43},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":198,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":200,"endColumn":9},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":199,"column":9,"nodeType":"Property","messageId":"anyAssignment","endLine":199,"endColumn":14},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":201,"column":18,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":201,"endColumn":25},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `string | number | bigint | boolean | ReactElement> | Iterable | ... 4 more ... | undefined`.","line":202,"column":23,"nodeType":"MemberExpression","messageId":"unsafeArgument","endLine":202,"endColumn":37},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .message on an `any` value.","line":202,"column":30,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":202,"endColumn":37},{"ruleId":"@typescript-eslint/no-unsafe-argument","severity":1,"message":"Unsafe argument of type `any` assigned to a parameter of type `string | number | bigint | boolean | ReactElement> | Iterable | ... 4 more ... | undefined`.","line":208,"column":21,"nodeType":"MemberExpression","messageId":"unsafeArgument","endLine":208,"endColumn":35},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .message on an `any` value.","line":208,"column":28,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":208,"endColumn":35},{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":1,"message":"Unsafe assignment of an `any` value.","line":217,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":220,"endColumn":9},{"ruleId":"@typescript-eslint/no-unsafe-member-access","severity":1,"message":"Unsafe member access .success on an `any` value.","line":221,"column":18,"nodeType":"Identifier","messageId":"unsafeMemberExpression","endLine":221,"endColumn":25}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":39,"fixableErrorCount":0,"fixableWarningCount":0,"source":"'use client';\n\nimport React, { useState, useEffect } from 'react';\nimport { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';\nimport { useTranslations } from 'next-intl';\nimport { Button } from '@/components/ui/button';\nimport { Badge } from '@/components/ui/badge';\nimport {\n Table,\n TableBody,\n TableCell,\n TableHead,\n TableHeader,\n TableRow,\n} from '@/components/ui/table';\nimport {\n Dialog,\n DialogContent,\n DialogHeader,\n DialogTitle,\n DialogFooter,\n} from '@/components/ui/dialog';\nimport { Input } from '@/components/ui/input';\nimport { Label } from '@/components/ui/label';\nimport { Textarea } from '@/components/ui/textarea';\nimport { Tabs, TabsList, TabsTrigger } from '@/components/ui/tabs';\nimport { ApiClient } from '@/lib/api-client';\nimport { formatDate } from '@/lib/utils';\nimport { toast } from 'sonner';\nimport { RefreshCw, Plus, ScanLine, FileCheck, RotateCcw } from 'lucide-react';\n\ninterface Requisition {\n id: number;\n requisition_no: string;\n work_order_no: string;\n type: string;\n status: number;\n total_quantity: number;\n applicant_name: string;\n approver_name: string;\n create_time: string;\n reason: string;\n}\n\nexport default function MaterialRequisitionsPage() {\n const t = useTranslations('Common');\n const tc = useTranslations('Common');\n\n const [requisitions, setRequisitions] = useState([]);\n const [loading, setLoading] = useState(false);\n const [activeTab, setActiveTab] = useState('all');\n const [page, _setPage] = useState(1);\n const [_total, setTotal] = useState(0);\n const [_searchNo, _setSearchNo] = useState('');\n\n // 弹窗状态\n const [showAutoGen, setShowAutoGen] = useState(false);\n const [showOverIssue, setShowOverIssue] = useState(false);\n const [showSupplementary, setShowSupplementary] = useState(false);\n const [showIssue, setShowIssue] = useState(false);\n const [selectedReq, setSelectedReq] = useState(null);\n\n // 表单数据\n const [workOrderId, setWorkOrderId] = useState('');\n const [overMaterialId, setOverMaterialId] = useState('');\n const [overQuantity, setOverQuantity] = useState('');\n const [overReason, setOverReason] = useState('');\n const [supReqId, setSupReqId] = useState('');\n const [supMaterialId, setSupMaterialId] = useState('');\n const [supQuantity, setSupQuantity] = useState('');\n const [supReason, setSupReason] = useState('');\n const [issueItems, setIssueItems] = useState('');\n\n const pageSize = 20;\n\n const loadRequisitions = async () => {\n setLoading(true);\n try {\n const params: Loose = { page, pageSize };\n if (activeTab !== 'all') params.type = activeTab;\n\n const result = await ApiClient.get('/api/material-requisitions', params);\n if (result.success) {\n setRequisitions(result.data.list || []);\n setTotal(result.data.total || 0);\n }\n } catch {\n toast.error(t('loadFail'));\n } finally {\n setLoading(false);\n }\n };\n\n useEffect(() => {\n loadRequisitions();\n }, [page, activeTab]);\n\n const getStatusBadge = (status: number) => {\n const map: Record = {\n 0: { label: tc('pending'), variant: 'secondary' },\n 1: { label: t('pendingIssue'), variant: 'warning' },\n 2: { label: t('issued'), variant: 'success' },\n 3: { label: t('cancelled'), variant: 'destructive' },\n };\n const s = map[status] || { label: tc('unknown'), variant: 'default' };\n return {s.label};\n };\n\n const getTypeLabel = (type: string) => {\n const map: Record = {\n normal: t('normalIssue'),\n over: t('overIssue'),\n supplementary: t('supplementary'),\n };\n return map[type] || type;\n };\n\n const handleAutoGenerate = async () => {\n if (!workOrderId) {\n toast.error(t('enterWorkOrderId'));\n return;\n }\n try {\n const result = await ApiClient.post('/api/material-requisitions', {\n action: 'auto-generate',\n workOrderId: Number(workOrderId),\n });\n if (result.success) {\n toast.success(result.message);\n setShowAutoGen(false);\n setWorkOrderId('');\n loadRequisitions();\n } else {\n toast.error(result.message);\n }\n } catch {\n toast.error(t('generateFail'));\n }\n };\n\n const handleOverIssue = async () => {\n if (!workOrderId || !overMaterialId || !overQuantity || !overReason) {\n toast.error(t('fillAllInfo'));\n return;\n }\n try {\n const result = await ApiClient.post('/api/material-requisitions', {\n action: 'over-issue',\n workOrderId: Number(workOrderId),\n materialId: Number(overMaterialId),\n quantity: Number(overQuantity),\n reason: overReason,\n });\n if (result.success) {\n toast.success(result.message);\n setShowOverIssue(false);\n loadRequisitions();\n } else {\n toast.error(result.message);\n }\n } catch {\n toast.error(t('submitFail'));\n }\n };\n\n const handleSupplementary = async () => {\n if (!supReqId || !supMaterialId || !supQuantity || !supReason) {\n toast.error(t('fillAllInfo'));\n return;\n }\n try {\n const result = await ApiClient.post('/api/material-requisitions', {\n action: 'supplementary',\n originalRequisitionId: Number(supReqId),\n materialId: Number(supMaterialId),\n quantity: Number(supQuantity),\n reason: supReason,\n });\n if (result.success) {\n toast.success(result.message);\n setShowSupplementary(false);\n loadRequisitions();\n } else {\n toast.error(result.message);\n }\n } catch {\n toast.error(t('submitFail'));\n }\n };\n\n const handleIssue = async () => {\n if (!selectedReq || !issueItems) {\n toast.error(t('fillIssueInfo'));\n return;\n }\n try {\n const items = JSON.parse(issueItems);\n const result = await ApiClient.post(`/api/material-requisitions/${selectedReq.id}/issue`, {\n items,\n });\n if (result.success) {\n toast.success(result.message);\n setShowIssue(false);\n setSelectedReq(null);\n setIssueItems('');\n loadRequisitions();\n } else {\n toast.error(result.message);\n }\n } catch {\n toast.error(t('issueFail'));\n }\n };\n\n const handleApprove = async (id: number, approved: boolean) => {\n try {\n const result = await ApiClient.put('/api/material-requisitions', {\n id,\n status: approved ? 1 : 3,\n });\n if (result.success) {\n toast.success(approved ? t('approveSuccess') : t('rejectSuccess'));\n loadRequisitions();\n }\n } catch {\n toast.error(t('approveFail'));\n }\n };\n\n return (\n
\n
\n

{t('materialRequisitionManagement')}

\n
\n \n \n \n \n
\n
\n\n \n \n {t('all')}\n {t('normalIssue')}\n {t('overIssue')}\n {t('supplementary')}\n \n \n\n \n \n {t('requisitionList')}\n \n \n \n \n \n {t('requisitionNo')}\n {t('workOrderNo')}\n {t('type')}\n {t('status')}\n {t('quantity')}\n {t('applicant')}\n {t('applyTime')}\n {t('actions')}\n \n \n \n {requisitions.map((req) => (\n \n {req.requisition_no}\n {req.work_order_no}\n {getTypeLabel(req.type)}\n {getStatusBadge(req.status)}\n {req.total_quantity}\n {req.applicant_name}\n {formatDate(req.create_time)}\n \n
\n {req.status === 0 && (\n <>\n handleApprove(req.id, true)}\n >\n {t('approve')}\n \n handleApprove(req.id, false)}\n >\n {t('reject')}\n \n \n )}\n {req.status === 1 && (\n {\n setSelectedReq(req);\n setShowIssue(true);\n }}\n >\n {t('issue')}\n \n )}\n
\n
\n
\n ))}\n {requisitions.length === 0 && (\n \n \n {t('noData')}\n \n \n )}\n
\n
\n
\n
\n\n \n \n \n {t('autoGenerateRequisition')}\n \n
\n
\n \n setWorkOrderId(e.target.value)}\n placeholder={t('enterWorkOrderId')}\n />\n
\n
\n \n \n \n \n
\n
\n\n \n \n \n {t('overIssueRequest')}\n \n
\n
\n \n setWorkOrderId(e.target.value)} />\n
\n
\n \n setOverMaterialId(e.target.value)} />\n
\n
\n \n setOverQuantity(e.target.value)}\n />\n
\n
\n \n