From 804a4cf85fb9c6b32ee0e436fc9db71dd92ae0fe Mon Sep 17 00:00:00 2001 From: waruneeauy Date: Tue, 23 Dec 2025 20:21:25 +0700 Subject: [PATCH 001/178] add: build leave --- .onedev/build-leave.yml | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 .onedev/build-leave.yml diff --git a/.onedev/build-leave.yml b/.onedev/build-leave.yml new file mode 100644 index 00000000..c89f0c51 --- /dev/null +++ b/.onedev/build-leave.yml @@ -0,0 +1,31 @@ +version: 37 +jobs: + - name: CI for Leave PROD + steps: + - !CheckoutStep + name: checkout code + cloneCredential: !DefaultCredential {} + withLfs: false + withSubmodules: false + condition: ALL_PREVIOUS_STEPS_WERE_SUCCESSFUL + + - !SetBuildVersionStep + name: set build version + buildVersion: "@tag@" + condition: ALL_PREVIOUS_STEPS_WERE_SUCCESSFUL + + - !BuildImageStep + name: build docker image + dockerfile: ./BMA.EHR.Leave/Dockerfile + output: !RegistryOutput + tags: "hrms-git.bangkok.go.th/bma-hrms/hrms-api-leave:@build_version@ hrms-git.bangkok.go.th/bma-hrms/hrms-api-leave:latest" + condition: ALL_PREVIOUS_STEPS_WERE_SUCCESSFUL + + triggers: + - !TagCreateTrigger + tags: leave-prod-* + branches: main + retryCondition: never + maxRetries: 3 + retryDelay: 30 + timeout: 14400 From 8bb31b4e73708195a5d40602502d336ccf7481d7 Mon Sep 17 00:00:00 2001 From: waruneeauy Date: Tue, 23 Dec 2025 20:30:37 +0700 Subject: [PATCH 002/178] updated deploy --- .onedev/build-leave.yml | 117 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 112 insertions(+), 5 deletions(-) diff --git a/.onedev/build-leave.yml b/.onedev/build-leave.yml index c89f0c51..21b0b4a6 100644 --- a/.onedev/build-leave.yml +++ b/.onedev/build-leave.yml @@ -1,5 +1,73 @@ -version: 37 +version: 38 jobs: + - name: CI for Leave UAT + steps: + - !CheckoutStep + name: checkout code + cloneCredential: !DefaultCredential {} + withLfs: false + withSubmodules: false + condition: ALL_PREVIOUS_STEPS_WERE_SUCCESSFUL + - !GenerateChecksumStep + name: generate project checksum + files: "**/*.csproj" + targetFile: checksum + condition: ALL_PREVIOUS_STEPS_WERE_SUCCESSFUL + - !SetupCacheStep + name: set up nuget cache + key: nuget_packages_@file:checksum@ + loadKeys: + - nuget_packages + paths: + - /root/.nuget/packages + uploadStrategy: UPLOAD_IF_NOT_HIT + condition: ALL_PREVIOUS_STEPS_WERE_SUCCESSFUL + - !CommandStep + name: test and analyze + runInContainer: true + image: mcr.microsoft.com/dotnet/sdk + interpreter: !DefaultInterpreter + commands: | + set -e + dotnet tool install -g roslynator.dotnet.cli + dotnet test -l trx --collect:"XPlat Code Coverage" + #/root/.dotnet/tools/roslynator analyze -o roslynator-analysis.xml + useTTY: true + condition: NEVER + - !PublishTRXReportStep + name: publish unit test report + reportName: Unit Test + filePatterns: "**/*.trx" + condition: NEVER + - !PublishCoberturaReportStep + name: publish code coverage report + reportName: Code Coverage + filePatterns: "**/coverage.cobertura.xml" + condition: NEVER + - !PublishRoslynatorReportStep + name: publish code problem report + reportName: Code Problems + filePatterns: roslynator-analysis.xml + failThreshold: HIGH + condition: NEVER + - !SetBuildVersionStep + name: set build version + buildVersion: "@tag@" + condition: ALL_PREVIOUS_STEPS_WERE_SUCCESSFUL + - !BuildImageStep + name: build docker image + dockerfile: ./BMA.EHR.Leave/Dockerfile + output: !RegistryOutput + tags: "@server@/bma-hrms/hrms-api-leave:@build_version@ @server@/bma-hrms/hrms-api-leave:latest" + condition: ALL_PREVIOUS_STEPS_WERE_SUCCESSFUL + triggers: + - !TagCreateTrigger + tags: leave-uat-* + branches: main + retryCondition: never + maxRetries: 3 + retryDelay: 30 + timeout: 14400 - name: CI for Leave PROD steps: - !CheckoutStep @@ -8,19 +76,58 @@ jobs: withLfs: false withSubmodules: false condition: ALL_PREVIOUS_STEPS_WERE_SUCCESSFUL - + - !GenerateChecksumStep + name: generate project checksum + files: "**/*.csproj" + targetFile: checksum + condition: ALL_PREVIOUS_STEPS_WERE_SUCCESSFUL + - !SetupCacheStep + name: set up nuget cache + key: nuget_packages_@file:checksum@ + loadKeys: + - nuget_packages + paths: + - /root/.nuget/packages + uploadStrategy: UPLOAD_IF_NOT_HIT + condition: ALL_PREVIOUS_STEPS_WERE_SUCCESSFUL + - !CommandStep + name: test and analyze + runInContainer: true + image: mcr.microsoft.com/dotnet/sdk + interpreter: !DefaultInterpreter + commands: | + set -e + dotnet tool install -g roslynator.dotnet.cli + dotnet test -l trx --collect:"XPlat Code Coverage" + #/root/.dotnet/tools/roslynator analyze -o roslynator-analysis.xml + useTTY: true + condition: NEVER + - !PublishTRXReportStep + name: publish unit test report + reportName: Unit Test + filePatterns: "**/*.trx" + condition: NEVER + - !PublishCoberturaReportStep + name: publish code coverage report + reportName: Code Coverage + filePatterns: "**/coverage.cobertura.xml" + condition: NEVER + - !PublishRoslynatorReportStep + name: publish code problem report + reportName: Code Problems + filePatterns: roslynator-analysis.xml + failThreshold: HIGH + condition: NEVER - !SetBuildVersionStep name: set build version buildVersion: "@tag@" condition: ALL_PREVIOUS_STEPS_WERE_SUCCESSFUL - - !BuildImageStep name: build docker image dockerfile: ./BMA.EHR.Leave/Dockerfile output: !RegistryOutput - tags: "hrms-git.bangkok.go.th/bma-hrms/hrms-api-leave:@build_version@ hrms-git.bangkok.go.th/bma-hrms/hrms-api-leave:latest" + tags: "@server@/bma-hrms/hrms-api-leave:@build_version@ @server@/bma-hrms/hrms-api-leave:latest" condition: ALL_PREVIOUS_STEPS_WERE_SUCCESSFUL - triggers: - !TagCreateTrigger tags: leave-prod-* From bde1aa21c94ff9d3901fc204242e05922bae88b5 Mon Sep 17 00:00:00 2001 From: waruneeauy Date: Tue, 23 Dec 2025 21:09:15 +0700 Subject: [PATCH 003/178] test build leave --- .onedev/build-leave.yml => .onedev-build-leave.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename .onedev/build-leave.yml => .onedev-build-leave.yml (98%) diff --git a/.onedev/build-leave.yml b/.onedev-build-leave.yml similarity index 98% rename from .onedev/build-leave.yml rename to .onedev-build-leave.yml index 21b0b4a6..6b94263e 100644 --- a/.onedev/build-leave.yml +++ b/.onedev-build-leave.yml @@ -1,6 +1,6 @@ version: 38 jobs: - - name: CI for Leave UAT + - name: CI for UAT steps: - !CheckoutStep name: checkout code @@ -68,7 +68,7 @@ jobs: maxRetries: 3 retryDelay: 30 timeout: 14400 - - name: CI for Leave PROD + - name: CI for PROD steps: - !CheckoutStep name: checkout code From 2b1d6852c9a6ffbec4cf9fdfa2dcb2bfe613f323 Mon Sep 17 00:00:00 2001 From: waruneeauy Date: Tue, 23 Dec 2025 21:18:04 +0700 Subject: [PATCH 004/178] add build placement --- .onedev-build-placement.yml | 138 ++++++++++++++++++++++++++++++++++++ 1 file changed, 138 insertions(+) create mode 100644 .onedev-build-placement.yml diff --git a/.onedev-build-placement.yml b/.onedev-build-placement.yml new file mode 100644 index 00000000..dbadf72a --- /dev/null +++ b/.onedev-build-placement.yml @@ -0,0 +1,138 @@ +version: 38 +jobs: + - name: CI for UAT Placement + steps: + - !CheckoutStep + name: checkout code + cloneCredential: !DefaultCredential {} + withLfs: false + withSubmodules: false + condition: ALL_PREVIOUS_STEPS_WERE_SUCCESSFUL + - !GenerateChecksumStep + name: generate project checksum + files: "**/*.csproj" + targetFile: checksum + condition: ALL_PREVIOUS_STEPS_WERE_SUCCESSFUL + - !SetupCacheStep + name: set up nuget cache + key: nuget_packages_@file:checksum@ + loadKeys: + - nuget_packages + paths: + - /root/.nuget/packages + uploadStrategy: UPLOAD_IF_NOT_HIT + condition: ALL_PREVIOUS_STEPS_WERE_SUCCESSFUL + - !CommandStep + name: test and analyze + runInContainer: true + image: mcr.microsoft.com/dotnet/sdk + interpreter: !DefaultInterpreter + commands: | + set -e + dotnet tool install -g roslynator.dotnet.cli + dotnet test -l trx --collect:"XPlat Code Coverage" + #/root/.dotnet/tools/roslynator analyze -o roslynator-analysis.xml + useTTY: true + condition: NEVER + - !PublishTRXReportStep + name: publish unit test report + reportName: Unit Test + filePatterns: "**/*.trx" + condition: NEVER + - !PublishCoberturaReportStep + name: publish code coverage report + reportName: Code Coverage + filePatterns: "**/coverage.cobertura.xml" + condition: NEVER + - !PublishRoslynatorReportStep + name: publish code problem report + reportName: Code Problems + filePatterns: roslynator-analysis.xml + failThreshold: HIGH + condition: NEVER + - !SetBuildVersionStep + name: set build version + buildVersion: "@tag@" + condition: ALL_PREVIOUS_STEPS_WERE_SUCCESSFUL + - !BuildImageStep + name: build docker image + dockerfile: ./BMA.EHR.Placement.Service/Dockerfile + output: !RegistryOutput + tags: "@server@/bma-hrms/hrms-api-placement:@build_version@ @server@/bma-hrms/hrms-api-placement:latest" + condition: ALL_PREVIOUS_STEPS_WERE_SUCCESSFUL + triggers: + - !TagCreateTrigger + tags: placement-uat-* + branches: main + retryCondition: never + maxRetries: 3 + retryDelay: 30 + timeout: 14400 + - name: CI for PROD Placement + steps: + - !CheckoutStep + name: checkout code + cloneCredential: !DefaultCredential {} + withLfs: false + withSubmodules: false + condition: ALL_PREVIOUS_STEPS_WERE_SUCCESSFUL + - !GenerateChecksumStep + name: generate project checksum + files: "**/*.csproj" + targetFile: checksum + condition: ALL_PREVIOUS_STEPS_WERE_SUCCESSFUL + - !SetupCacheStep + name: set up nuget cache + key: nuget_packages_@file:checksum@ + loadKeys: + - nuget_packages + paths: + - /root/.nuget/packages + uploadStrategy: UPLOAD_IF_NOT_HIT + condition: ALL_PREVIOUS_STEPS_WERE_SUCCESSFUL + - !CommandStep + name: test and analyze + runInContainer: true + image: mcr.microsoft.com/dotnet/sdk + interpreter: !DefaultInterpreter + commands: | + set -e + dotnet tool install -g roslynator.dotnet.cli + dotnet test -l trx --collect:"XPlat Code Coverage" + #/root/.dotnet/tools/roslynator analyze -o roslynator-analysis.xml + useTTY: true + condition: NEVER + - !PublishTRXReportStep + name: publish unit test report + reportName: Unit Test + filePatterns: "**/*.trx" + condition: NEVER + - !PublishCoberturaReportStep + name: publish code coverage report + reportName: Code Coverage + filePatterns: "**/coverage.cobertura.xml" + condition: NEVER + - !PublishRoslynatorReportStep + name: publish code problem report + reportName: Code Problems + filePatterns: roslynator-analysis.xml + failThreshold: HIGH + condition: NEVER + - !SetBuildVersionStep + name: set build version + buildVersion: "@tag@" + condition: ALL_PREVIOUS_STEPS_WERE_SUCCESSFUL + - !BuildImageStep + name: build docker image + dockerfile: ./BMA.EHR.Placement.Service/Dockerfile + output: !RegistryOutput + tags: "@server@/bma-hrms/hrms-api-placement:@build_version@ @server@/bma-hrms/hrms-api-placement:latest" + condition: ALL_PREVIOUS_STEPS_WERE_SUCCESSFUL + triggers: + - !TagCreateTrigger + tags: placement-prod-* + branches: main + retryCondition: never + maxRetries: 3 + retryDelay: 30 + timeout: 14400 From d10c86e0cbe2e0d9949d52b64d7d06aae79fbedd Mon Sep 17 00:00:00 2001 From: waruneeauy Date: Tue, 23 Dec 2025 21:50:04 +0700 Subject: [PATCH 005/178] add: build docker hub --- .../workflows/dockerhub-release-leave.yaml | 90 ++++++++++++ .onedev-build-leave.yml | 138 ------------------ .onedev-build-placement.yml | 138 ------------------ 3 files changed, 90 insertions(+), 276 deletions(-) create mode 100644 .github/workflows/dockerhub-release-leave.yaml delete mode 100644 .onedev-build-leave.yml delete mode 100644 .onedev-build-placement.yml diff --git a/.github/workflows/dockerhub-release-leave.yaml b/.github/workflows/dockerhub-release-leave.yaml new file mode 100644 index 00000000..e78ce73d --- /dev/null +++ b/.github/workflows/dockerhub-release-leave.yaml @@ -0,0 +1,90 @@ +name: DockerHub Release - Leave Service +run-name: DockerHub Release - Leave Service by ${{ github.actor }} +on: + push: + tags: + - "leave-[0-9]+.[0-9]+.[0-9]+" + branches: + - main + - develop + workflow_dispatch: + inputs: + IMAGE_VER: + description: 'Image version (e.g., latest, v1.0.0)' + required: false + default: 'latest' + +env: + DOCKERHUB_REGISTRY: docker.io + IMAGE_NAME: hrms-api-leave + +jobs: + release-to-dockerhub: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set output tags + id: vars + run: echo "tag=${GITHUB_REF#refs/*/}" >> $GITHUB_OUTPUT + + - name: Generate version + id: gen_ver + run: | + if [[ $GITHUB_REF == 'refs/tags/'* ]]; then + IMAGE_VER=${{ steps.vars.outputs.tag }} + elif [[ $GITHUB_REF == 'refs/heads/'* ]]; then + BRANCH_NAME=${GITHUB_REF#refs/heads/} + IMAGE_VER="${BRANCH_NAME}-latest" + else + IMAGE_VER=${{ github.event.inputs.IMAGE_VER }} + fi + if [[ $IMAGE_VER == '' ]]; then + IMAGE_VER='test-vBeta' + fi + echo "image_ver=${IMAGE_VER}" >> $GITHUB_OUTPUT + echo "Generated version: ${IMAGE_VER}" + + - name: Display version + run: | + echo "Git Ref: $GITHUB_REF" + echo "Image Version: ${{ steps.gen_ver.outputs.image_ver }}" + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Login to Docker Hub + uses: docker/login-action@v3 + with: + registry: ${{env.DOCKERHUB_REGISTRY}} + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Extract metadata for Docker + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ secrets.DOCKERHUB_USERNAME }}/${{ env.IMAGE_NAME }} + tags: | + type=ref,event=tag + type=ref,event=branch + type=raw,value=${{ steps.gen_ver.outputs.image_ver }} + type=raw,value=latest,enable={{is_default_branch}} + + - name: Build and push Docker image + uses: docker/build-push-action@v5 + with: + context: . + file: BMA.EHR.Leave/Dockerfile + platforms: linux/amd64 + push: true + tags: | + ${{ secrets.DOCKERHUB_USERNAME }}/${{ env.IMAGE_NAME }}:${{ steps.gen_ver.outputs.image_ver }} + ${{ secrets.DOCKERHUB_USERNAME }}/${{ env.IMAGE_NAME }}:latest + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max + + - name: Image digest + run: echo "Image pushed with digest ${{ steps.build.outputs.digest }}" diff --git a/.onedev-build-leave.yml b/.onedev-build-leave.yml deleted file mode 100644 index 6b94263e..00000000 --- a/.onedev-build-leave.yml +++ /dev/null @@ -1,138 +0,0 @@ -version: 38 -jobs: - - name: CI for UAT - steps: - - !CheckoutStep - name: checkout code - cloneCredential: !DefaultCredential {} - withLfs: false - withSubmodules: false - condition: ALL_PREVIOUS_STEPS_WERE_SUCCESSFUL - - !GenerateChecksumStep - name: generate project checksum - files: "**/*.csproj" - targetFile: checksum - condition: ALL_PREVIOUS_STEPS_WERE_SUCCESSFUL - - !SetupCacheStep - name: set up nuget cache - key: nuget_packages_@file:checksum@ - loadKeys: - - nuget_packages - paths: - - /root/.nuget/packages - uploadStrategy: UPLOAD_IF_NOT_HIT - condition: ALL_PREVIOUS_STEPS_WERE_SUCCESSFUL - - !CommandStep - name: test and analyze - runInContainer: true - image: mcr.microsoft.com/dotnet/sdk - interpreter: !DefaultInterpreter - commands: | - set -e - dotnet tool install -g roslynator.dotnet.cli - dotnet test -l trx --collect:"XPlat Code Coverage" - #/root/.dotnet/tools/roslynator analyze -o roslynator-analysis.xml - useTTY: true - condition: NEVER - - !PublishTRXReportStep - name: publish unit test report - reportName: Unit Test - filePatterns: "**/*.trx" - condition: NEVER - - !PublishCoberturaReportStep - name: publish code coverage report - reportName: Code Coverage - filePatterns: "**/coverage.cobertura.xml" - condition: NEVER - - !PublishRoslynatorReportStep - name: publish code problem report - reportName: Code Problems - filePatterns: roslynator-analysis.xml - failThreshold: HIGH - condition: NEVER - - !SetBuildVersionStep - name: set build version - buildVersion: "@tag@" - condition: ALL_PREVIOUS_STEPS_WERE_SUCCESSFUL - - !BuildImageStep - name: build docker image - dockerfile: ./BMA.EHR.Leave/Dockerfile - output: !RegistryOutput - tags: "@server@/bma-hrms/hrms-api-leave:@build_version@ @server@/bma-hrms/hrms-api-leave:latest" - condition: ALL_PREVIOUS_STEPS_WERE_SUCCESSFUL - triggers: - - !TagCreateTrigger - tags: leave-uat-* - branches: main - retryCondition: never - maxRetries: 3 - retryDelay: 30 - timeout: 14400 - - name: CI for PROD - steps: - - !CheckoutStep - name: checkout code - cloneCredential: !DefaultCredential {} - withLfs: false - withSubmodules: false - condition: ALL_PREVIOUS_STEPS_WERE_SUCCESSFUL - - !GenerateChecksumStep - name: generate project checksum - files: "**/*.csproj" - targetFile: checksum - condition: ALL_PREVIOUS_STEPS_WERE_SUCCESSFUL - - !SetupCacheStep - name: set up nuget cache - key: nuget_packages_@file:checksum@ - loadKeys: - - nuget_packages - paths: - - /root/.nuget/packages - uploadStrategy: UPLOAD_IF_NOT_HIT - condition: ALL_PREVIOUS_STEPS_WERE_SUCCESSFUL - - !CommandStep - name: test and analyze - runInContainer: true - image: mcr.microsoft.com/dotnet/sdk - interpreter: !DefaultInterpreter - commands: | - set -e - dotnet tool install -g roslynator.dotnet.cli - dotnet test -l trx --collect:"XPlat Code Coverage" - #/root/.dotnet/tools/roslynator analyze -o roslynator-analysis.xml - useTTY: true - condition: NEVER - - !PublishTRXReportStep - name: publish unit test report - reportName: Unit Test - filePatterns: "**/*.trx" - condition: NEVER - - !PublishCoberturaReportStep - name: publish code coverage report - reportName: Code Coverage - filePatterns: "**/coverage.cobertura.xml" - condition: NEVER - - !PublishRoslynatorReportStep - name: publish code problem report - reportName: Code Problems - filePatterns: roslynator-analysis.xml - failThreshold: HIGH - condition: NEVER - - !SetBuildVersionStep - name: set build version - buildVersion: "@tag@" - condition: ALL_PREVIOUS_STEPS_WERE_SUCCESSFUL - - !BuildImageStep - name: build docker image - dockerfile: ./BMA.EHR.Leave/Dockerfile - output: !RegistryOutput - tags: "@server@/bma-hrms/hrms-api-leave:@build_version@ @server@/bma-hrms/hrms-api-leave:latest" - condition: ALL_PREVIOUS_STEPS_WERE_SUCCESSFUL - triggers: - - !TagCreateTrigger - tags: leave-prod-* - branches: main - retryCondition: never - maxRetries: 3 - retryDelay: 30 - timeout: 14400 diff --git a/.onedev-build-placement.yml b/.onedev-build-placement.yml deleted file mode 100644 index dbadf72a..00000000 --- a/.onedev-build-placement.yml +++ /dev/null @@ -1,138 +0,0 @@ -version: 38 -jobs: - - name: CI for UAT Placement - steps: - - !CheckoutStep - name: checkout code - cloneCredential: !DefaultCredential {} - withLfs: false - withSubmodules: false - condition: ALL_PREVIOUS_STEPS_WERE_SUCCESSFUL - - !GenerateChecksumStep - name: generate project checksum - files: "**/*.csproj" - targetFile: checksum - condition: ALL_PREVIOUS_STEPS_WERE_SUCCESSFUL - - !SetupCacheStep - name: set up nuget cache - key: nuget_packages_@file:checksum@ - loadKeys: - - nuget_packages - paths: - - /root/.nuget/packages - uploadStrategy: UPLOAD_IF_NOT_HIT - condition: ALL_PREVIOUS_STEPS_WERE_SUCCESSFUL - - !CommandStep - name: test and analyze - runInContainer: true - image: mcr.microsoft.com/dotnet/sdk - interpreter: !DefaultInterpreter - commands: | - set -e - dotnet tool install -g roslynator.dotnet.cli - dotnet test -l trx --collect:"XPlat Code Coverage" - #/root/.dotnet/tools/roslynator analyze -o roslynator-analysis.xml - useTTY: true - condition: NEVER - - !PublishTRXReportStep - name: publish unit test report - reportName: Unit Test - filePatterns: "**/*.trx" - condition: NEVER - - !PublishCoberturaReportStep - name: publish code coverage report - reportName: Code Coverage - filePatterns: "**/coverage.cobertura.xml" - condition: NEVER - - !PublishRoslynatorReportStep - name: publish code problem report - reportName: Code Problems - filePatterns: roslynator-analysis.xml - failThreshold: HIGH - condition: NEVER - - !SetBuildVersionStep - name: set build version - buildVersion: "@tag@" - condition: ALL_PREVIOUS_STEPS_WERE_SUCCESSFUL - - !BuildImageStep - name: build docker image - dockerfile: ./BMA.EHR.Placement.Service/Dockerfile - output: !RegistryOutput - tags: "@server@/bma-hrms/hrms-api-placement:@build_version@ @server@/bma-hrms/hrms-api-placement:latest" - condition: ALL_PREVIOUS_STEPS_WERE_SUCCESSFUL - triggers: - - !TagCreateTrigger - tags: placement-uat-* - branches: main - retryCondition: never - maxRetries: 3 - retryDelay: 30 - timeout: 14400 - - name: CI for PROD Placement - steps: - - !CheckoutStep - name: checkout code - cloneCredential: !DefaultCredential {} - withLfs: false - withSubmodules: false - condition: ALL_PREVIOUS_STEPS_WERE_SUCCESSFUL - - !GenerateChecksumStep - name: generate project checksum - files: "**/*.csproj" - targetFile: checksum - condition: ALL_PREVIOUS_STEPS_WERE_SUCCESSFUL - - !SetupCacheStep - name: set up nuget cache - key: nuget_packages_@file:checksum@ - loadKeys: - - nuget_packages - paths: - - /root/.nuget/packages - uploadStrategy: UPLOAD_IF_NOT_HIT - condition: ALL_PREVIOUS_STEPS_WERE_SUCCESSFUL - - !CommandStep - name: test and analyze - runInContainer: true - image: mcr.microsoft.com/dotnet/sdk - interpreter: !DefaultInterpreter - commands: | - set -e - dotnet tool install -g roslynator.dotnet.cli - dotnet test -l trx --collect:"XPlat Code Coverage" - #/root/.dotnet/tools/roslynator analyze -o roslynator-analysis.xml - useTTY: true - condition: NEVER - - !PublishTRXReportStep - name: publish unit test report - reportName: Unit Test - filePatterns: "**/*.trx" - condition: NEVER - - !PublishCoberturaReportStep - name: publish code coverage report - reportName: Code Coverage - filePatterns: "**/coverage.cobertura.xml" - condition: NEVER - - !PublishRoslynatorReportStep - name: publish code problem report - reportName: Code Problems - filePatterns: roslynator-analysis.xml - failThreshold: HIGH - condition: NEVER - - !SetBuildVersionStep - name: set build version - buildVersion: "@tag@" - condition: ALL_PREVIOUS_STEPS_WERE_SUCCESSFUL - - !BuildImageStep - name: build docker image - dockerfile: ./BMA.EHR.Placement.Service/Dockerfile - output: !RegistryOutput - tags: "@server@/bma-hrms/hrms-api-placement:@build_version@ @server@/bma-hrms/hrms-api-placement:latest" - condition: ALL_PREVIOUS_STEPS_WERE_SUCCESSFUL - triggers: - - !TagCreateTrigger - tags: placement-prod-* - branches: main - retryCondition: never - maxRetries: 3 - retryDelay: 30 - timeout: 14400 From c87e467a6ef3bc24e1f5efd3662d9e1ad17dcab1 Mon Sep 17 00:00:00 2001 From: waruneeauy Date: Tue, 23 Dec 2025 23:32:02 +0700 Subject: [PATCH 006/178] change build to docker hub --- .../workflows/dockerhub-release-checkin.yaml | 72 +++++++ .../workflows/dockerhub-release-command.yaml | 72 +++++++ .../dockerhub-release-discipline.yaml | 72 +++++++ .../workflows/dockerhub-release-insignia.yaml | 72 +++++++ .../workflows/dockerhub-release-leave.yaml | 10 +- .../dockerhub-release-placement.yaml | 72 +++++++ .../workflows/dockerhub-release-report.yaml | 72 +++++++ .../dockerhub-release-retirement.yaml | 72 +++++++ .github/workflows/release_Retirement.yaml | 6 +- .../workflows/release_checkin_consumer.yaml | 6 +- .github/workflows/release_command.yaml | 6 +- .github/workflows/release_discipline.yaml | 6 +- .github/workflows/release_insignia.yaml | 6 +- .github/workflows/release_leave.yaml | 6 +- .github/workflows/release_placement.yaml | 6 +- .github/workflows/release_report.yaml | 196 +++++++++--------- 16 files changed, 628 insertions(+), 124 deletions(-) create mode 100644 .github/workflows/dockerhub-release-checkin.yaml create mode 100644 .github/workflows/dockerhub-release-command.yaml create mode 100644 .github/workflows/dockerhub-release-discipline.yaml create mode 100644 .github/workflows/dockerhub-release-insignia.yaml create mode 100644 .github/workflows/dockerhub-release-placement.yaml create mode 100644 .github/workflows/dockerhub-release-report.yaml create mode 100644 .github/workflows/dockerhub-release-retirement.yaml diff --git a/.github/workflows/dockerhub-release-checkin.yaml b/.github/workflows/dockerhub-release-checkin.yaml new file mode 100644 index 00000000..9e7c4e23 --- /dev/null +++ b/.github/workflows/dockerhub-release-checkin.yaml @@ -0,0 +1,72 @@ +name: DockerHub Release - CheckIn Consumer +run-name: DockerHub Release - CheckIn Consumer by ${{ github.actor }} +on: + push: + tags: + - "checkin-[0-9]+.[0-9]+.[0-9]+" + workflow_dispatch: + inputs: + IMAGE_VER: + description: "Image version (e.g., latest, v1.0.0)" + required: false + default: "latest" + +env: + DOCKERHUB_REGISTRY: docker.io + IMAGE_NAME: hrms-api-checkin + +jobs: + release-to-dockerhub: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set output tags + id: vars + run: echo "tag=${GITHUB_REF#refs/*/}" >> $GITHUB_OUTPUT + + - name: Generate version + id: gen_ver + run: | + if [[ $GITHUB_REF == 'refs/tags/'* ]]; then + IMAGE_VER=${{ steps.vars.outputs.tag }} + else + IMAGE_VER=${{ github.event.inputs.IMAGE_VER }} + fi + if [[ $IMAGE_VER == '' ]]; then + IMAGE_VER='test-vBeta' + fi + echo "image_ver=${IMAGE_VER}" >> $GITHUB_OUTPUT + echo "Generated version: ${IMAGE_VER}" + + - name: Display version + run: | + echo "Git Ref: $GITHUB_REF" + echo "Image Version: ${{ steps.gen_ver.outputs.image_ver }}" + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Login to Docker Hub + uses: docker/login-action@v3 + with: + registry: ${{env.DOCKERHUB_REGISTRY}} + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Build and push Docker image + uses: docker/build-push-action@v5 + with: + context: . + file: BMA.EHR.CheckInConsumer/Dockerfile + platforms: linux/amd64 + push: true + tags: | + ${{ secrets.DOCKERHUB_USERNAME }}/${{ env.IMAGE_NAME }}:${{ steps.gen_ver.outputs.image_ver }} + ${{ secrets.DOCKERHUB_USERNAME }}/${{ env.IMAGE_NAME }}:latest + labels: | + org.opencontainers.image.title=BMA EHR CheckIn Consumer + org.opencontainers.image.description=HRMS CheckIn Consumer Service + cache-from: type=gha + cache-to: type=gha,mode=max diff --git a/.github/workflows/dockerhub-release-command.yaml b/.github/workflows/dockerhub-release-command.yaml new file mode 100644 index 00000000..33d6b330 --- /dev/null +++ b/.github/workflows/dockerhub-release-command.yaml @@ -0,0 +1,72 @@ +name: DockerHub Release - Command Service +run-name: DockerHub Release - Command Service by ${{ github.actor }} +on: + push: + tags: + - "command-[0-9]+.[0-9]+.[0-9]+" + workflow_dispatch: + inputs: + IMAGE_VER: + description: "Image version (e.g., latest, v1.0.0)" + required: false + default: "latest" + +env: + DOCKERHUB_REGISTRY: docker.io + IMAGE_NAME: hrms-api-command + +jobs: + release-to-dockerhub: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set output tags + id: vars + run: echo "tag=${GITHUB_REF#refs/*/}" >> $GITHUB_OUTPUT + + - name: Generate version + id: gen_ver + run: | + if [[ $GITHUB_REF == 'refs/tags/'* ]]; then + IMAGE_VER=${{ steps.vars.outputs.tag }} + else + IMAGE_VER=${{ github.event.inputs.IMAGE_VER }} + fi + if [[ $IMAGE_VER == '' ]]; then + IMAGE_VER='test-vBeta' + fi + echo "image_ver=${IMAGE_VER}" >> $GITHUB_OUTPUT + echo "Generated version: ${IMAGE_VER}" + + - name: Display version + run: | + echo "Git Ref: $GITHUB_REF" + echo "Image Version: ${{ steps.gen_ver.outputs.image_ver }}" + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Login to Docker Hub + uses: docker/login-action@v3 + with: + registry: ${{env.DOCKERHUB_REGISTRY}} + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Build and push Docker image + uses: docker/build-push-action@v5 + with: + context: . + file: BMA.EHR.Command.Service/Dockerfile + platforms: linux/amd64 + push: true + tags: | + ${{ secrets.DOCKERHUB_USERNAME }}/${{ env.IMAGE_NAME }}:${{ steps.gen_ver.outputs.image_ver }} + ${{ secrets.DOCKERHUB_USERNAME }}/${{ env.IMAGE_NAME }}:latest + labels: | + org.opencontainers.image.title=BMA EHR Command Service + org.opencontainers.image.description=HRMS Command API Service + cache-from: type=gha + cache-to: type=gha,mode=max diff --git a/.github/workflows/dockerhub-release-discipline.yaml b/.github/workflows/dockerhub-release-discipline.yaml new file mode 100644 index 00000000..96096a54 --- /dev/null +++ b/.github/workflows/dockerhub-release-discipline.yaml @@ -0,0 +1,72 @@ +name: DockerHub Release - Discipline Service +run-name: DockerHub Release - Discipline Service by ${{ github.actor }} +on: + push: + tags: + - "discipline-[0-9]+.[0-9]+.[0-9]+" + workflow_dispatch: + inputs: + IMAGE_VER: + description: "Image version (e.g., latest, v1.0.0)" + required: false + default: "latest" + +env: + DOCKERHUB_REGISTRY: docker.io + IMAGE_NAME: hrms-api-discipline + +jobs: + release-to-dockerhub: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set output tags + id: vars + run: echo "tag=${GITHUB_REF#refs/*/}" >> $GITHUB_OUTPUT + + - name: Generate version + id: gen_ver + run: | + if [[ $GITHUB_REF == 'refs/tags/'* ]]; then + IMAGE_VER=${{ steps.vars.outputs.tag }} + else + IMAGE_VER=${{ github.event.inputs.IMAGE_VER }} + fi + if [[ $IMAGE_VER == '' ]]; then + IMAGE_VER='test-vBeta' + fi + echo "image_ver=${IMAGE_VER}" >> $GITHUB_OUTPUT + echo "Generated version: ${IMAGE_VER}" + + - name: Display version + run: | + echo "Git Ref: $GITHUB_REF" + echo "Image Version: ${{ steps.gen_ver.outputs.image_ver }}" + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Login to Docker Hub + uses: docker/login-action@v3 + with: + registry: ${{env.DOCKERHUB_REGISTRY}} + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Build and push Docker image + uses: docker/build-push-action@v5 + with: + context: . + file: BMA.EHR.Discipline.Service/Dockerfile + platforms: linux/amd64 + push: true + tags: | + ${{ secrets.DOCKERHUB_USERNAME }}/${{ env.IMAGE_NAME }}:${{ steps.gen_ver.outputs.image_ver }} + ${{ secrets.DOCKERHUB_USERNAME }}/${{ env.IMAGE_NAME }}:latest + labels: | + org.opencontainers.image.title=BMA EHR Discipline Service + org.opencontainers.image.description=HRMS Discipline API Service + cache-from: type=gha + cache-to: type=gha,mode=max diff --git a/.github/workflows/dockerhub-release-insignia.yaml b/.github/workflows/dockerhub-release-insignia.yaml new file mode 100644 index 00000000..81dae954 --- /dev/null +++ b/.github/workflows/dockerhub-release-insignia.yaml @@ -0,0 +1,72 @@ +name: DockerHub Release - Insignia Service +run-name: DockerHub Release - Insignia Service by ${{ github.actor }} +on: + push: + tags: + - "insignia-[0-9]+.[0-9]+.[0-9]+" + workflow_dispatch: + inputs: + IMAGE_VER: + description: "Image version (e.g., latest, v1.0.0)" + required: false + default: "latest" + +env: + DOCKERHUB_REGISTRY: docker.io + IMAGE_NAME: hrms-api-insignia + +jobs: + release-to-dockerhub: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set output tags + id: vars + run: echo "tag=${GITHUB_REF#refs/*/}" >> $GITHUB_OUTPUT + + - name: Generate version + id: gen_ver + run: | + if [[ $GITHUB_REF == 'refs/tags/'* ]]; then + IMAGE_VER=${{ steps.vars.outputs.tag }} + else + IMAGE_VER=${{ github.event.inputs.IMAGE_VER }} + fi + if [[ $IMAGE_VER == '' ]]; then + IMAGE_VER='test-vBeta' + fi + echo "image_ver=${IMAGE_VER}" >> $GITHUB_OUTPUT + echo "Generated version: ${IMAGE_VER}" + + - name: Display version + run: | + echo "Git Ref: $GITHUB_REF" + echo "Image Version: ${{ steps.gen_ver.outputs.image_ver }}" + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Login to Docker Hub + uses: docker/login-action@v3 + with: + registry: ${{env.DOCKERHUB_REGISTRY}} + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Build and push Docker image + uses: docker/build-push-action@v5 + with: + context: . + file: BMA.EHR.Insignia/Dockerfile + platforms: linux/amd64 + push: true + tags: | + ${{ secrets.DOCKERHUB_USERNAME }}/${{ env.IMAGE_NAME }}:${{ steps.gen_ver.outputs.image_ver }} + ${{ secrets.DOCKERHUB_USERNAME }}/${{ env.IMAGE_NAME }}:latest + labels: | + org.opencontainers.image.title=BMA EHR Insignia Service + org.opencontainers.image.description=HRMS Insignia API Service + cache-from: type=gha + cache-to: type=gha,mode=max diff --git a/.github/workflows/dockerhub-release-leave.yaml b/.github/workflows/dockerhub-release-leave.yaml index e78ce73d..b5a3afe2 100644 --- a/.github/workflows/dockerhub-release-leave.yaml +++ b/.github/workflows/dockerhub-release-leave.yaml @@ -4,15 +4,15 @@ on: push: tags: - "leave-[0-9]+.[0-9]+.[0-9]+" - branches: - - main - - develop + # branches: + # - main + # - develop workflow_dispatch: inputs: IMAGE_VER: - description: 'Image version (e.g., latest, v1.0.0)' + description: "Image version (e.g., latest, v1.0.0)" required: false - default: 'latest' + default: "latest" env: DOCKERHUB_REGISTRY: docker.io diff --git a/.github/workflows/dockerhub-release-placement.yaml b/.github/workflows/dockerhub-release-placement.yaml new file mode 100644 index 00000000..74fa4471 --- /dev/null +++ b/.github/workflows/dockerhub-release-placement.yaml @@ -0,0 +1,72 @@ +name: DockerHub Release - Placement Service +run-name: DockerHub Release - Placement Service by ${{ github.actor }} +on: + push: + tags: + - "placement-[0-9]+.[0-9]+.[0-9]+" + workflow_dispatch: + inputs: + IMAGE_VER: + description: "Image version (e.g., latest, v1.0.0)" + required: false + default: "latest" + +env: + DOCKERHUB_REGISTRY: docker.io + IMAGE_NAME: hrms-api-placement + +jobs: + release-to-dockerhub: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set output tags + id: vars + run: echo "tag=${GITHUB_REF#refs/*/}" >> $GITHUB_OUTPUT + + - name: Generate version + id: gen_ver + run: | + if [[ $GITHUB_REF == 'refs/tags/'* ]]; then + IMAGE_VER=${{ steps.vars.outputs.tag }} + else + IMAGE_VER=${{ github.event.inputs.IMAGE_VER }} + fi + if [[ $IMAGE_VER == '' ]]; then + IMAGE_VER='test-vBeta' + fi + echo "image_ver=${IMAGE_VER}" >> $GITHUB_OUTPUT + echo "Generated version: ${IMAGE_VER}" + + - name: Display version + run: | + echo "Git Ref: $GITHUB_REF" + echo "Image Version: ${{ steps.gen_ver.outputs.image_ver }}" + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Login to Docker Hub + uses: docker/login-action@v3 + with: + registry: ${{env.DOCKERHUB_REGISTRY}} + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Build and push Docker image + uses: docker/build-push-action@v5 + with: + context: . + file: BMA.EHR.Placement.Service/Dockerfile + platforms: linux/amd64 + push: true + tags: | + ${{ secrets.DOCKERHUB_USERNAME }}/${{ env.IMAGE_NAME }}:${{ steps.gen_ver.outputs.image_ver }} + ${{ secrets.DOCKERHUB_USERNAME }}/${{ env.IMAGE_NAME }}:latest + labels: | + org.opencontainers.image.title=BMA EHR Placement Service + org.opencontainers.image.description=HRMS Placement API Service + cache-from: type=gha + cache-to: type=gha,mode=max diff --git a/.github/workflows/dockerhub-release-report.yaml b/.github/workflows/dockerhub-release-report.yaml new file mode 100644 index 00000000..132b218b --- /dev/null +++ b/.github/workflows/dockerhub-release-report.yaml @@ -0,0 +1,72 @@ +name: DockerHub Release - Report Service +run-name: DockerHub Release - Report Service by ${{ github.actor }} +on: + push: + tags: + - "report-[0-9]+.[0-9]+.[0-9]+" + workflow_dispatch: + inputs: + IMAGE_VER: + description: "Image version (e.g., latest, v1.0.0)" + required: false + default: "latest" + +env: + DOCKERHUB_REGISTRY: docker.io + IMAGE_NAME: hrms-api-report + +jobs: + release-to-dockerhub: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set output tags + id: vars + run: echo "tag=${GITHUB_REF#refs/*/}" >> $GITHUB_OUTPUT + + - name: Generate version + id: gen_ver + run: | + if [[ $GITHUB_REF == 'refs/tags/'* ]]; then + IMAGE_VER=${{ steps.vars.outputs.tag }} + else + IMAGE_VER=${{ github.event.inputs.IMAGE_VER }} + fi + if [[ $IMAGE_VER == '' ]]; then + IMAGE_VER='test-vBeta' + fi + echo "image_ver=${IMAGE_VER}" >> $GITHUB_OUTPUT + echo "Generated version: ${IMAGE_VER}" + + - name: Display version + run: | + echo "Git Ref: $GITHUB_REF" + echo "Image Version: ${{ steps.gen_ver.outputs.image_ver }}" + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Login to Docker Hub + uses: docker/login-action@v3 + with: + registry: ${{env.DOCKERHUB_REGISTRY}} + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Build and push Docker image + uses: docker/build-push-action@v5 + with: + context: . + file: BMA.EHR.Report.Service/Dockerfile + platforms: linux/amd64 + push: true + tags: | + ${{ secrets.DOCKERHUB_USERNAME }}/${{ env.IMAGE_NAME }}:${{ steps.gen_ver.outputs.image_ver }} + ${{ secrets.DOCKERHUB_USERNAME }}/${{ env.IMAGE_NAME }}:latest + labels: | + org.opencontainers.image.title=BMA EHR Report Service + org.opencontainers.image.description=HRMS Report API Service + cache-from: type=gha + cache-to: type=gha,mode=max diff --git a/.github/workflows/dockerhub-release-retirement.yaml b/.github/workflows/dockerhub-release-retirement.yaml new file mode 100644 index 00000000..38e107e6 --- /dev/null +++ b/.github/workflows/dockerhub-release-retirement.yaml @@ -0,0 +1,72 @@ +name: DockerHub Release - Retirement Service +run-name: DockerHub Release - Retirement Service by ${{ github.actor }} +on: + push: + tags: + - "retirement-[0-9]+.[0-9]+.[0-9]+" + workflow_dispatch: + inputs: + IMAGE_VER: + description: "Image version (e.g., latest, v1.0.0)" + required: false + default: "latest" + +env: + DOCKERHUB_REGISTRY: docker.io + IMAGE_NAME: hrms-api-retirement + +jobs: + release-to-dockerhub: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set output tags + id: vars + run: echo "tag=${GITHUB_REF#refs/*/}" >> $GITHUB_OUTPUT + + - name: Generate version + id: gen_ver + run: | + if [[ $GITHUB_REF == 'refs/tags/'* ]]; then + IMAGE_VER=${{ steps.vars.outputs.tag }} + else + IMAGE_VER=${{ github.event.inputs.IMAGE_VER }} + fi + if [[ $IMAGE_VER == '' ]]; then + IMAGE_VER='test-vBeta' + fi + echo "image_ver=${IMAGE_VER}" >> $GITHUB_OUTPUT + echo "Generated version: ${IMAGE_VER}" + + - name: Display version + run: | + echo "Git Ref: $GITHUB_REF" + echo "Image Version: ${{ steps.gen_ver.outputs.image_ver }}" + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Login to Docker Hub + uses: docker/login-action@v3 + with: + registry: ${{env.DOCKERHUB_REGISTRY}} + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Build and push Docker image + uses: docker/build-push-action@v5 + with: + context: . + file: BMA.EHR.Retirement.Service/Dockerfile + platforms: linux/amd64 + push: true + tags: | + ${{ secrets.DOCKERHUB_USERNAME }}/${{ env.IMAGE_NAME }}:${{ steps.gen_ver.outputs.image_ver }} + ${{ secrets.DOCKERHUB_USERNAME }}/${{ env.IMAGE_NAME }}:latest + labels: | + org.opencontainers.image.title=BMA EHR Retirement Service + org.opencontainers.image.description=HRMS Retirement API Service + cache-from: type=gha + cache-to: type=gha,mode=max diff --git a/.github/workflows/release_Retirement.yaml b/.github/workflows/release_Retirement.yaml index 6cdceae0..3fd5bedf 100644 --- a/.github/workflows/release_Retirement.yaml +++ b/.github/workflows/release_Retirement.yaml @@ -1,9 +1,9 @@ name: release-dev run-name: release-dev ${{ github.actor }} on: - push: - tags: - - "retirement-[0-9]+.[0-9]+.[0-9]+" + # push: + # tags: + # - "retirement-[0-9]+.[0-9]+.[0-9]+" workflow_dispatch: env: REGISTRY: docker.frappet.com diff --git a/.github/workflows/release_checkin_consumer.yaml b/.github/workflows/release_checkin_consumer.yaml index 31b015e6..f9081264 100644 --- a/.github/workflows/release_checkin_consumer.yaml +++ b/.github/workflows/release_checkin_consumer.yaml @@ -1,9 +1,9 @@ name: release-dev run-name: release-dev ${{ github.actor }} on: - push: - tags: - - "consumer-[0-9]+.[0-9]+.[0-9]+" + # push: + # tags: + # - "consumer-[0-9]+.[0-9]+.[0-9]+" workflow_dispatch: env: REGISTRY: docker.frappet.com diff --git a/.github/workflows/release_command.yaml b/.github/workflows/release_command.yaml index 462ed688..dba3012e 100644 --- a/.github/workflows/release_command.yaml +++ b/.github/workflows/release_command.yaml @@ -1,9 +1,9 @@ name: release-dev run-name: release-dev ${{ github.actor }} on: - push: - tags: - - "command-[0-9]+.[0-9]+.[0-9]+" + # push: + # tags: + # - "command-[0-9]+.[0-9]+.[0-9]+" workflow_dispatch: env: REGISTRY: docker.frappet.com diff --git a/.github/workflows/release_discipline.yaml b/.github/workflows/release_discipline.yaml index 0e8737f4..f4cb42da 100644 --- a/.github/workflows/release_discipline.yaml +++ b/.github/workflows/release_discipline.yaml @@ -1,9 +1,9 @@ name: release-dev run-name: release-dev ${{ github.actor }} on: - push: - tags: - - "discipline-[0-9]+.[0-9]+.[0-9]+" + # push: + # tags: + # - "discipline-[0-9]+.[0-9]+.[0-9]+" workflow_dispatch: env: REGISTRY: docker.frappet.com diff --git a/.github/workflows/release_insignia.yaml b/.github/workflows/release_insignia.yaml index 124d1e9c..af497b37 100644 --- a/.github/workflows/release_insignia.yaml +++ b/.github/workflows/release_insignia.yaml @@ -1,9 +1,9 @@ name: release-dev run-name: release-dev ${{ github.actor }} on: - push: - tags: - - "insignia-[0-9]+.[0-9]+.[0-9]+" + # push: + # tags: + # - "insignia-[0-9]+.[0-9]+.[0-9]+" workflow_dispatch: env: REGISTRY: docker.frappet.com diff --git a/.github/workflows/release_leave.yaml b/.github/workflows/release_leave.yaml index 4b361f5f..9b5e0014 100644 --- a/.github/workflows/release_leave.yaml +++ b/.github/workflows/release_leave.yaml @@ -1,9 +1,9 @@ name: release-dev run-name: release-dev ${{ github.actor }} on: - push: - tags: - - "leave-[0-9]+.[0-9]+.[0-9]+" + # push: + # tags: + # - "leave-[0-9]+.[0-9]+.[0-9]+" workflow_dispatch: env: REGISTRY: docker.frappet.com diff --git a/.github/workflows/release_placement.yaml b/.github/workflows/release_placement.yaml index d9c2f266..377cbaf4 100644 --- a/.github/workflows/release_placement.yaml +++ b/.github/workflows/release_placement.yaml @@ -1,9 +1,9 @@ name: release-dev run-name: release-dev ${{ github.actor }} on: - push: - tags: - - "placement-[0-9]+.[0-9]+.[0-9]+" + # push: + # tags: + # - "placement-[0-9]+.[0-9]+.[0-9]+" workflow_dispatch: env: REGISTRY: docker.frappet.com diff --git a/.github/workflows/release_report.yaml b/.github/workflows/release_report.yaml index 9ba5a7c4..e529729b 100644 --- a/.github/workflows/release_report.yaml +++ b/.github/workflows/release_report.yaml @@ -1,107 +1,107 @@ name: release-dev run-name: release-dev ${{ github.actor }} on: - push: - tags: - - "reportv2-[0-9]+.[0-9]+.[0-9]+" + # push: + # tags: + # - "reportv2-[0-9]+.[0-9]+.[0-9]+" workflow_dispatch: env: - REGISTRY: docker.frappet.com - IMAGE_NAME: ehr/bma-ehr-report-v2-service - DEPLOY_HOST: frappet.com - DEPLOY_PORT: 10102 - # COMPOSE_PATH: /home/frappet/docker/bma-ehr - COMPOSE_PATH: /home/frappet/docker/bma/bma-ehr-report-v2 - TOKEN_LINE: uxuK5hDzS2DsoC5piJBrWRLiz8GgY7iMZZldOWsDDF0 + REGISTRY: docker.frappet.com + IMAGE_NAME: ehr/bma-ehr-report-v2-service + DEPLOY_HOST: frappet.com + DEPLOY_PORT: 10102 + # COMPOSE_PATH: /home/frappet/docker/bma-ehr + COMPOSE_PATH: /home/frappet/docker/bma/bma-ehr-report-v2 + TOKEN_LINE: uxuK5hDzS2DsoC5piJBrWRLiz8GgY7iMZZldOWsDDF0 jobs: - # act workflow_dispatch -W .github/workflows/release_report.yaml --input IMAGE_VER=latest -s DOCKER_USER=admin -s DOCKER_PASS=FPTadmin2357 -s SSH_PASSWORD=FPTadmin2357 - release-dev: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - name: Set output tags - id: vars - run: echo "tag=${GITHUB_REF#refs/*/}" >> $GITHUB_OUTPUT - - name: Gen Version - id: gen_ver - run: | - if [[ $GITHUB_REF == 'refs/tags/'* ]]; then - IMAGE_VER=${{ steps.vars.outputs.tag }} - else - IMAGE_VER=${{ github.event.inputs.IMAGE_VER }} - fi - if [[ $IMAGE_VER == '' ]]; then - IMAGE_VER='test-vBeta' - fi - echo '::set-output name=image_ver::'$IMAGE_VER - - name: Check Version - run: | - echo $GITHUB_REF - echo ${{ steps.gen_ver.outputs.image_ver }} - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2 - - name: Login in to registry - uses: docker/login-action@v2 - with: - registry: ${{env.REGISTRY}} - username: ${{secrets.DOCKER_USER}} - password: ${{secrets.DOCKER_PASS}} - - name: Build and load local docker image - uses: docker/build-push-action@v3 - with: - context: . - platforms: linux/amd64 - file: BMA.EHR.Report.Service/Dockerfile - push: true - tags: ${{env.REGISTRY}}/${{env.IMAGE_NAME}}:${{ steps.gen_ver.outputs.image_ver }},${{env.REGISTRY}}/${{env.IMAGE_NAME}}:latest + # act workflow_dispatch -W .github/workflows/release_report.yaml --input IMAGE_VER=latest -s DOCKER_USER=admin -s DOCKER_PASS=FPTadmin2357 -s SSH_PASSWORD=FPTadmin2357 + release-dev: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Set output tags + id: vars + run: echo "tag=${GITHUB_REF#refs/*/}" >> $GITHUB_OUTPUT + - name: Gen Version + id: gen_ver + run: | + if [[ $GITHUB_REF == 'refs/tags/'* ]]; then + IMAGE_VER=${{ steps.vars.outputs.tag }} + else + IMAGE_VER=${{ github.event.inputs.IMAGE_VER }} + fi + if [[ $IMAGE_VER == '' ]]; then + IMAGE_VER='test-vBeta' + fi + echo '::set-output name=image_ver::'$IMAGE_VER + - name: Check Version + run: | + echo $GITHUB_REF + echo ${{ steps.gen_ver.outputs.image_ver }} + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 + - name: Login in to registry + uses: docker/login-action@v2 + with: + registry: ${{env.REGISTRY}} + username: ${{secrets.DOCKER_USER}} + password: ${{secrets.DOCKER_PASS}} + - name: Build and load local docker image + uses: docker/build-push-action@v3 + with: + context: . + platforms: linux/amd64 + file: BMA.EHR.Report.Service/Dockerfile + push: true + tags: ${{env.REGISTRY}}/${{env.IMAGE_NAME}}:${{ steps.gen_ver.outputs.image_ver }},${{env.REGISTRY}}/${{env.IMAGE_NAME}}:latest - - name: Reload docker compose - uses: appleboy/ssh-action@v0.1.8 - with: - host: ${{env.DEPLOY_HOST}} - username: frappet - password: ${{ secrets.SSH_PASSWORD }} - port: ${{env.DEPLOY_PORT}} - script: | - cd "${{env.COMPOSE_PATH}}" - docker compose pull - docker compose up -d - echo "${{ steps.gen_ver.outputs.image_ver }}"> success - - name: Notify Discord Success - if: success() - run: | - curl -H "Content-Type: application/json" \ - -X POST \ - -d '{ - "embeds": [{ - "title": "✅ Deployment Success!", - "description": "**Details:**\n- Image: `${{env.IMAGE_NAME}}`\n- Version: `${{ steps.gen_ver.outputs.image_ver }}`\n- Deployed by: `${{github.actor}}`", - "color": 3066993, - "footer": { - "text": "Release Notification", - "icon_url": "https://example.com/success-icon.png" - }, - "timestamp": "'$(date -u +%Y-%m-%dT%H:%M:%SZ)'" - }] - }' \ - ${{ secrets.DISCORD_WEBHOOK }} + - name: Reload docker compose + uses: appleboy/ssh-action@v0.1.8 + with: + host: ${{env.DEPLOY_HOST}} + username: frappet + password: ${{ secrets.SSH_PASSWORD }} + port: ${{env.DEPLOY_PORT}} + script: | + cd "${{env.COMPOSE_PATH}}" + docker compose pull + docker compose up -d + echo "${{ steps.gen_ver.outputs.image_ver }}"> success + - name: Notify Discord Success + if: success() + run: | + curl -H "Content-Type: application/json" \ + -X POST \ + -d '{ + "embeds": [{ + "title": "✅ Deployment Success!", + "description": "**Details:**\n- Image: `${{env.IMAGE_NAME}}`\n- Version: `${{ steps.gen_ver.outputs.image_ver }}`\n- Deployed by: `${{github.actor}}`", + "color": 3066993, + "footer": { + "text": "Release Notification", + "icon_url": "https://example.com/success-icon.png" + }, + "timestamp": "'$(date -u +%Y-%m-%dT%H:%M:%SZ)'" + }] + }' \ + ${{ secrets.DISCORD_WEBHOOK }} - - name: Notify Discord Failure - if: failure() - run: | - curl -H "Content-Type: application/json" \ - -X POST \ - -d '{ - "embeds": [{ - "title": "❌ Deployment Failed!", - "description": "**Details:**\n- Image: `${{env.IMAGE_NAME}}`\n- Version: `${{ steps.gen_ver.outputs.image_ver }}`\n- Attempted by: `${{github.actor}}`", - "color": 15158332, - "footer": { - "text": "Release Notification", - "icon_url": "https://example.com/failure-icon.png" - }, - "timestamp": "'$(date -u +%Y-%m-%dT%H:%M:%SZ)'" - }] - }' \ - ${{ secrets.DISCORD_WEBHOOK }} + - name: Notify Discord Failure + if: failure() + run: | + curl -H "Content-Type: application/json" \ + -X POST \ + -d '{ + "embeds": [{ + "title": "❌ Deployment Failed!", + "description": "**Details:**\n- Image: `${{env.IMAGE_NAME}}`\n- Version: `${{ steps.gen_ver.outputs.image_ver }}`\n- Attempted by: `${{github.actor}}`", + "color": 15158332, + "footer": { + "text": "Release Notification", + "icon_url": "https://example.com/failure-icon.png" + }, + "timestamp": "'$(date -u +%Y-%m-%dT%H:%M:%SZ)'" + }] + }' \ + ${{ secrets.DISCORD_WEBHOOK }} From 7caf1cc8d639008afa21ad93b4535e7571741c6e Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Fri, 26 Dec 2025 09:51:51 +0700 Subject: [PATCH 007/178] add date range parameters to GetProfileByAdminRole and GetEmployeeByAdminRole methods --- .../Repositories/UserProfileRepository.cs | 12 ++++++++---- BMA.EHR.Leave/Controllers/LeaveReportController.cs | 12 ++++++------ 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/BMA.EHR.Application/Repositories/UserProfileRepository.cs b/BMA.EHR.Application/Repositories/UserProfileRepository.cs index 15e06ca9..344e6116 100644 --- a/BMA.EHR.Application/Repositories/UserProfileRepository.cs +++ b/BMA.EHR.Application/Repositories/UserProfileRepository.cs @@ -520,7 +520,7 @@ namespace BMA.EHR.Application.Repositories } } - public async Task> GetProfileByAdminRole(string? accessToken, int? node, string? nodeId, string role, string? revisionId, int? reqNode, string? reqNodeId) + public async Task> GetProfileByAdminRole(string? accessToken, int? node, string? nodeId, string role, string? revisionId, int? reqNode, string? reqNodeId,DateTime? startDate, DateTime? endDate) { try { @@ -533,7 +533,9 @@ namespace BMA.EHR.Application.Repositories role = role, revisionId = revisionId, reqNode = reqNode, - reqNodeId = reqNodeId + reqNodeId = reqNodeId, + startDate = startDate, + endDate = endDate }; var profiles = new List(); @@ -686,7 +688,7 @@ namespace BMA.EHR.Application.Repositories } } - public async Task> GetEmployeeByAdminRole(string? accessToken, int? node, string? nodeId, string role, string? revisionId, int? reqNode, string? reqNodeId) + public async Task> GetEmployeeByAdminRole(string? accessToken, int? node, string? nodeId, string role, string? revisionId, int? reqNode, string? reqNodeId,DateTime? startDate, DateTime? endDate) { try { @@ -699,7 +701,9 @@ namespace BMA.EHR.Application.Repositories role = role, revisionId = revisionId, reqNode = reqNode, - reqNodeId = reqNodeId + reqNodeId = reqNodeId, + startDate = startDate, + endDate = endDate }; var profiles = new List(); diff --git a/BMA.EHR.Leave/Controllers/LeaveReportController.cs b/BMA.EHR.Leave/Controllers/LeaveReportController.cs index 2c001072..bb9538f6 100644 --- a/BMA.EHR.Leave/Controllers/LeaveReportController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveReportController.cs @@ -1029,11 +1029,11 @@ namespace BMA.EHR.Leave.Service.Controllers if (type.Trim().ToUpper() == "OFFICER") { - profile = await _userProfileRepository.GetProfileByAdminRole(AccessToken, profileAdmin?.Node, nodeId, role, req.revisionId, req.node, req.nodeId); + profile = await _userProfileRepository.GetProfileByAdminRole(AccessToken, profileAdmin?.Node, nodeId, role, req.revisionId, req.node, req.nodeId, req.StartDate.Date, req.EndDate.Date); } else { - profile = await _userProfileRepository.GetEmployeeByAdminRole(AccessToken, profileAdmin?.Node, nodeId, role, req.revisionId, req.node, req.nodeId); + profile = await _userProfileRepository.GetEmployeeByAdminRole(AccessToken, profileAdmin?.Node, nodeId, role, req.revisionId, req.node, req.nodeId, req.StartDate.Date, req.EndDate.Date); } // get leave day var leaveDays = await _leaveRequestRepository.GetSumApproveLeaveByTypeAndRange(req.StartDate, req.EndDate); @@ -1658,11 +1658,11 @@ namespace BMA.EHR.Leave.Service.Controllers if (type.Trim().ToUpper() == "OFFICER") { - profile = await _userProfileRepository.GetProfileByAdminRole(AccessToken, profileAdmin?.Node, nodeId, role, req.revisionId, req.node, req.nodeId); + profile = await _userProfileRepository.GetProfileByAdminRole(AccessToken, profileAdmin?.Node, nodeId, role, req.revisionId, req.node, req.nodeId, req.StartDate.Date, req.EndDate.Date); } else { - profile = await _userProfileRepository.GetEmployeeByAdminRole(AccessToken, profileAdmin?.Node, nodeId, role, req.revisionId, req.node, req.nodeId); + profile = await _userProfileRepository.GetEmployeeByAdminRole(AccessToken, profileAdmin?.Node, nodeId, role, req.revisionId, req.node, req.nodeId, req.StartDate.Date, req.EndDate.Date); } var date = req.StartDate.Date; @@ -1999,11 +1999,11 @@ namespace BMA.EHR.Leave.Service.Controllers if (type.Trim().ToUpper() == "OFFICER") { - profile = await _userProfileRepository.GetProfileByAdminRole(AccessToken, profileAdmin?.Node, nodeId, role, req.revisionId, req.node, req.nodeId); + profile = await _userProfileRepository.GetProfileByAdminRole(AccessToken, profileAdmin?.Node, nodeId, role, req.revisionId, req.node, req.nodeId, req.StartDate.Date, req.EndDate.Date); } else { - profile = await _userProfileRepository.GetEmployeeByAdminRole(AccessToken, profileAdmin?.Node, nodeId, role, req.revisionId, req.node, req.nodeId); + profile = await _userProfileRepository.GetEmployeeByAdminRole(AccessToken, profileAdmin?.Node, nodeId, role, req.revisionId, req.node, req.nodeId, req.StartDate.Date, req.EndDate.Date); } // Child กรองตามที่ fe ส่งมาอีกชั้น if ((role == "ROOT" || role == "OWNER" || role == "CHILD" || role == "PARENT" || role == "BROTHER") /*&& req.node > profileAdmin?.Node*/) From 8973e1b78f51aba0e2d08984eb692c83bf5c11a4 Mon Sep 17 00:00:00 2001 From: kittapath <> Date: Tue, 30 Dec 2025 23:55:03 +0700 Subject: [PATCH 008/178] leave v2 --- .../Repositories/UserProfileRepository.cs | 43 +++++++++++++++++-- .../Controllers/LeaveReportController.cs | 12 +++--- 2 files changed, 46 insertions(+), 9 deletions(-) diff --git a/BMA.EHR.Application/Repositories/UserProfileRepository.cs b/BMA.EHR.Application/Repositories/UserProfileRepository.cs index 344e6116..150aeded 100644 --- a/BMA.EHR.Application/Repositories/UserProfileRepository.cs +++ b/BMA.EHR.Application/Repositories/UserProfileRepository.cs @@ -157,7 +157,7 @@ namespace BMA.EHR.Application.Repositories return null; } - catch(Exception ex) + catch (Exception ex) { throw; } @@ -520,7 +520,7 @@ namespace BMA.EHR.Application.Repositories } } - public async Task> GetProfileByAdminRole(string? accessToken, int? node, string? nodeId, string role, string? revisionId, int? reqNode, string? reqNodeId,DateTime? startDate, DateTime? endDate) + public async Task> GetProfileByAdminRole(string? accessToken, int? node, string? nodeId, string role, string? revisionId, int? reqNode, string? reqNodeId, DateTime? startDate, DateTime? endDate) { try { @@ -556,6 +556,43 @@ namespace BMA.EHR.Application.Repositories } } + public async Task> GetProfileByAdminRolev2(string? accessToken, int? node, string? nodeId, string role, string? revisionId, int? reqNode, string? reqNodeId, DateTime? startDate, DateTime? endDate) + { + try + { + var apiPath = $"{_configuration["API"]}/org/dotnet/officer-by-admin-rolev2"; + var apiKey = _configuration["API_KEY"]; + var body = new + { + node = node, + nodeId = nodeId, + role = role, + // revisionId = revisionId, + reqNode = reqNode, + reqNodeId = reqNodeId, + // startDate = startDate, + // endDate = endDate + date = endDate + }; + + var profiles = new List(); + + var apiResult = await PostExternalAPIAsync(apiPath, accessToken, body, apiKey); + if (apiResult != null) + { + var raw = JsonConvert.DeserializeObject(apiResult); + if (raw != null) + return raw.Result; + } + + return null; + } + catch + { + throw; + } + } + public async Task> GetProfileWithKeycloakAllOfficerRetireFilterAndRevision(string? accessToken, int? node, string? nodeId, bool isAll, bool? isRetirement, string? revisionId) { try @@ -688,7 +725,7 @@ namespace BMA.EHR.Application.Repositories } } - public async Task> GetEmployeeByAdminRole(string? accessToken, int? node, string? nodeId, string role, string? revisionId, int? reqNode, string? reqNodeId,DateTime? startDate, DateTime? endDate) + public async Task> GetEmployeeByAdminRole(string? accessToken, int? node, string? nodeId, string role, string? revisionId, int? reqNode, string? reqNodeId, DateTime? startDate, DateTime? endDate) { try { diff --git a/BMA.EHR.Leave/Controllers/LeaveReportController.cs b/BMA.EHR.Leave/Controllers/LeaveReportController.cs index bb9538f6..3b4e81fd 100644 --- a/BMA.EHR.Leave/Controllers/LeaveReportController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveReportController.cs @@ -238,7 +238,7 @@ namespace BMA.EHR.Leave.Service.Controllers LeaveTotal = data.LeaveTotal.ToString().ToThaiNumber(), //data.LeaveStartDate.DiffDay(data.LeaveEndDate).ToString().ToThaiNumber(), leaveAddress = data.LeaveAddress.ToThaiNumber(), leaveNumber = data.LeaveNumber.ToThaiNumber(), - + approve = approveResult, leaveStatus = data.LeaveStatus != null && data.LeaveStatus!.ToUpper() == "APPROVE" ? "🗹 อนุญาต ☐ ไม่อนุญาต" @@ -1658,7 +1658,7 @@ namespace BMA.EHR.Leave.Service.Controllers if (type.Trim().ToUpper() == "OFFICER") { - profile = await _userProfileRepository.GetProfileByAdminRole(AccessToken, profileAdmin?.Node, nodeId, role, req.revisionId, req.node, req.nodeId, req.StartDate.Date, req.EndDate.Date); + profile = await _userProfileRepository.GetProfileByAdminRolev2(AccessToken, profileAdmin?.Node, nodeId, role, req.revisionId, req.node, req.nodeId, req.StartDate.Date, req.EndDate.Date); } else { @@ -1778,13 +1778,13 @@ namespace BMA.EHR.Leave.Service.Controllers var leaveRangeEnd = leaveReq.LeaveRangeEnd == null ? "" : leaveReq.LeaveRangeEnd.ToUpper(); - if(leaveRange != leaveRangeEnd) + if (leaveRange != leaveRangeEnd) { if (leaveRangeEnd == "MORNING") remarkStr += " - ครึ่งวันเช้า"; else if (leaveRangeEnd == "AFTERNOON") remarkStr += " - ครึ่งวันบ่าย"; - } + } break; default: remarkStr += leaveReq.Type.Name; @@ -2114,13 +2114,13 @@ namespace BMA.EHR.Leave.Service.Controllers // remarkStr += "ครึ่งวันบ่าย"; var leaveRangeEnd = leaveReq.LeaveRangeEnd == null ? "" : leaveReq.LeaveRangeEnd.ToUpper(); - if(leaveRange != leaveRangeEnd) + if (leaveRange != leaveRangeEnd) { if (leaveRangeEnd == "MORNING") remarkStr += " - ครึ่งวันเช้า"; else if (leaveRangeEnd == "AFTERNOON") remarkStr += " - ครึ่งวันบ่าย"; - } + } break; default: remarkStr += leaveReq.Type.Name; From 72b9c6e31eb5c3409fe77b7d6c40b1ff97e09690 Mon Sep 17 00:00:00 2001 From: waruneeauy Date: Wed, 31 Dec 2025 08:02:16 +0700 Subject: [PATCH 009/178] remove build forgejo, move to dev branch --- .forgejo/workflows/build-checkin.yml | 83 -------------------- .forgejo/workflows/build-command_backup.yml | 61 --------------- .forgejo/workflows/build-discipline.yml | 83 -------------------- .forgejo/workflows/build-insignia.yml | 83 -------------------- .forgejo/workflows/build-leave.yml | 83 -------------------- .forgejo/workflows/build-placement.yml | 83 -------------------- .forgejo/workflows/build-report_backup.yml | 84 --------------------- .forgejo/workflows/build-retirement.yml | 83 -------------------- .forgejo/workflows/build.yml | 49 ------------ .forgejo/workflows/deploy.yml | 29 ------- 10 files changed, 721 deletions(-) delete mode 100644 .forgejo/workflows/build-checkin.yml delete mode 100644 .forgejo/workflows/build-command_backup.yml delete mode 100644 .forgejo/workflows/build-discipline.yml delete mode 100644 .forgejo/workflows/build-insignia.yml delete mode 100644 .forgejo/workflows/build-leave.yml delete mode 100644 .forgejo/workflows/build-placement.yml delete mode 100644 .forgejo/workflows/build-report_backup.yml delete mode 100644 .forgejo/workflows/build-retirement.yml delete mode 100644 .forgejo/workflows/build.yml delete mode 100644 .forgejo/workflows/deploy.yml diff --git a/.forgejo/workflows/build-checkin.yml b/.forgejo/workflows/build-checkin.yml deleted file mode 100644 index 3971e9df..00000000 --- a/.forgejo/workflows/build-checkin.yml +++ /dev/null @@ -1,83 +0,0 @@ -name: Build & Deploy Checkin Service - -on: - push: - tags: - - "checkin-dev[0-9]+.[0-9]+.[0-9]+" - - "checkin-dev[0-9]+.[0-9]+.[0-9]+*" - workflow_dispatch: - -env: - REGISTRY: ${{ vars.CONTAINER_REGISTRY }} - REGISTRY_USERNAME: ${{ vars.CONTAINER_REGISTRY_USERNAME }} - REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }} - CONTAINER_IMAGE_NAME: ${{ vars.CONTAINER_REGISTRY }}/${{ vars.CONTAINER_IMAGE_OWNER }} - IMAGE_VERSION: latest - SERVICE_NAME: hrms-api-checkin - DISCORD_WEBHOOK: ${{ vars.DISCORD_WEBHOOK }} - -jobs: - build: - runs-on: ubuntu-latest - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Tag Version - shell: bash - run: | - if [[ "${{ github.event_name }}" == "push" ]]; then - VERSION=$(echo "${{ github.ref_name }}" | sed 's/checkin-dev//g') - echo "IMAGE_VERSION=${VERSION}" >> $GITHUB_ENV - else - echo "IMAGE_VERSION=${{ env.IMAGE_VERSION }}-${{ github.run_number }}" >> $GITHUB_ENV - fi - - name: Login in to registry - uses: docker/login-action@v3 - with: - registry: ${{ env.REGISTRY }} - username: ${{ env.REGISTRY_USERNAME }} - password: ${{ env.REGISTRY_PASSWORD }} - - - name: Build and push docker image - uses: docker/build-push-action@v5 - with: - platforms: linux/amd64 - context: . - file: ./BMA.EHR.CheckInConsumer/Dockerfile - tags: ${{ env.CONTAINER_IMAGE_NAME }}/${{ env.SERVICE_NAME }}:latest,${{ env.CONTAINER_IMAGE_NAME }}/${{ env.SERVICE_NAME }}:${{ env.IMAGE_VERSION }} - push: true - - - name: Remote Deploy - uses: appleboy/ssh-action@v1.2.1 - with: - host: ${{ vars.SSH_DEPLOY_HOST }} - port: ${{ vars.SSH_DEPLOY_PORT }} - username: ${{ secrets.SSH_DEPLOY_USER }} - password: ${{ secrets.SSH_DEPLOY_PASSWORD }} - script: | - cd ~/repo - ./replace-env.sh API_CHECKIN "${{ env.IMAGE_VERSION }}" - ./deploy.sh ${{ env.SERVICE_NAME }} - - - name: Discord Notification - if: always() - run: | - STATUS="${{ job.status == 'success' && '✅ Success' || '❌ Failed' }}" - COLOR="${{ job.status == 'success' && '3066993' || '15158332' }}" - TIMESTAMP=$(date -u +%Y-%m-%dT%H:%M:%SZ) - curl -H "Content-Type: application/json" \ - -X POST \ - -d "{ - \"embeds\": [{ - \"title\": \"$STATUS\", - \"description\": \"**Build & Deploy**\\n- Image: \`${{ env.CONTAINER_IMAGE_NAME }}/${{ env.SERVICE_NAME }}\`\\n- Version: \`${{ env.IMAGE_VERSION }}\`\\n- By: \`${{ gitea.actor }}\`\", - \"color\": $COLOR, - \"footer\": { - \"text\": \"Release Notification\", - \"icon_url\": \"https://example.com/success-icon.png\" - }, - \"timestamp\": \"$TIMESTAMP\" - }] - }" \ - ${{ env.DISCORD_WEBHOOK }} diff --git a/.forgejo/workflows/build-command_backup.yml b/.forgejo/workflows/build-command_backup.yml deleted file mode 100644 index 0884c70b..00000000 --- a/.forgejo/workflows/build-command_backup.yml +++ /dev/null @@ -1,61 +0,0 @@ -# name: Build - -# on: -# push: -# tags: -# - "command-dev[0-9]+.[0-9]+.[0-9]+" -# - "command-dev[0-9]+.[0-9]+.[0-9]+*" -# workflow_dispatch: - -# env: -# REGISTRY: ${{ vars.CONTAINER_REGISTRY }} -# REGISTRY_USERNAME: ${{ vars.CONTAINER_REGISTRY_USERNAME }} -# REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }} -# CONTAINER_IMAGE_NAME: ${{ vars.CONTAINER_REGISTRY }}/${{ vars.CONTAINER_IMAGE_OWNER }} -# IMAGE_VERSION: build -# DISCORD_WEBHOOK: ${{ vars.DISCORD_WEBHOOK }} - -# jobs: -# build: -# runs-on: ubuntu-latest -# steps: -# - name: Checkout repository -# uses: actions/checkout@v4 -# - name: Set up Docker Buildx -# uses: docker/setup-buildx-action@v2 -# with: -# config-inline: | -# [registry."${{ env.REGISTRY }}"] -# ca=["/etc/ssl/certs/ca-certificates.crt"] -# - name: Tag Version -# run: | -# if [[ "${{ github.event_name }}" == "push" ]]; then -# echo "IMAGE_VERSION=${{ github.ref_name }}" | sed 's/command-dev//g' >> $GITHUB_ENV -# else -# echo "IMAGE_VERSION=${{ env.IMAGE_VERSION }}-${{ github.run_number }}" >> $GITHUB_ENV -# fi -# - name: Login in to registry -# uses: docker/login-action@v2 -# with: -# registry: ${{ env.REGISTRY }} -# username: ${{ env.REGISTRY_USERNAME }} -# password: ${{ env.REGISTRY_PASSWORD }} -# - name: Build and push docker image -# uses: docker/build-push-action@v3 -# with: -# platforms: linux/amd64 -# context: . -# file: ./BMA.EHR.Command.Service/Dockerfile -# tags: ${{ env.CONTAINER_IMAGE_NAME }}/hrms-api-command:latest,${{ env.CONTAINER_IMAGE_NAME }}/hrms-api-command:${{ env.IMAGE_VERSION }} -# push: true -# - name: Remote Deploy -# uses: appleboy/ssh-action@v1.2.1 -# with: -# host: ${{ vars.SSH_DEPLOY_HOST }} -# port: ${{ vars.SSH_DEPLOY_PORT }} -# username: ${{ secrets.SSH_DEPLOY_USER }} -# password: ${{ secrets.SSH_DEPLOY_PASSWORD }} -# script: | -# cd ~/repo -# ./replace-env.sh API_COMMAND "${{ env.IMAGE_VERSION }}" -# ./deploy.sh hrms-api-command diff --git a/.forgejo/workflows/build-discipline.yml b/.forgejo/workflows/build-discipline.yml deleted file mode 100644 index 767d775b..00000000 --- a/.forgejo/workflows/build-discipline.yml +++ /dev/null @@ -1,83 +0,0 @@ -name: Build & Deploy Discipline Service - -on: - push: - tags: - - "discipline-dev[0-9]+.[0-9]+.[0-9]+" - - "discipline-dev[0-9]+.[0-9]+.[0-9]+*" - workflow_dispatch: - -env: - REGISTRY: ${{ vars.CONTAINER_REGISTRY }} - REGISTRY_USERNAME: ${{ vars.CONTAINER_REGISTRY_USERNAME }} - REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }} - CONTAINER_IMAGE_NAME: ${{ vars.CONTAINER_REGISTRY }}/${{ vars.CONTAINER_IMAGE_OWNER }} - IMAGE_VERSION: latest - SERVICE_NAME: hrms-api-discipline - DISCORD_WEBHOOK: ${{ vars.DISCORD_WEBHOOK }} - -jobs: - build: - runs-on: ubuntu-latest - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Tag Version - shell: bash - run: | - if [[ "${{ github.event_name }}" == "push" ]]; then - VERSION=$(echo "${{ github.ref_name }}" | sed 's/discipline-dev//g') - echo "IMAGE_VERSION=${VERSION}" >> $GITHUB_ENV - else - echo "IMAGE_VERSION=${{ env.IMAGE_VERSION }}-${{ github.run_number }}" >> $GITHUB_ENV - fi - - name: Login in to registry - uses: docker/login-action@v3 - with: - registry: ${{ env.REGISTRY }} - username: ${{ env.REGISTRY_USERNAME }} - password: ${{ env.REGISTRY_PASSWORD }} - - - name: Build and push docker image - uses: docker/build-push-action@v5 - with: - platforms: linux/amd64 - context: . - file: ./BMA.EHR.Discipline.Service/Dockerfile - tags: ${{ env.CONTAINER_IMAGE_NAME }}/${{ env.SERVICE_NAME }}:latest,${{ env.CONTAINER_IMAGE_NAME }}/${{ env.SERVICE_NAME }}:${{ env.IMAGE_VERSION }} - push: true - - - name: Remote Deploy - uses: appleboy/ssh-action@v1.2.1 - with: - host: ${{ vars.SSH_DEPLOY_HOST }} - port: ${{ vars.SSH_DEPLOY_PORT }} - username: ${{ secrets.SSH_DEPLOY_USER }} - password: ${{ secrets.SSH_DEPLOY_PASSWORD }} - script: | - cd ~/repo - ./replace-env.sh API_DISCIPLINE "${{ env.IMAGE_VERSION }}" - ./deploy.sh ${{ env.SERVICE_NAME }} - - - name: Discord Notification - if: always() - run: | - STATUS="${{ job.status == 'success' && '✅ Success' || '❌ Failed' }}" - COLOR="${{ job.status == 'success' && '3066993' || '15158332' }}" - TIMESTAMP=$(date -u +%Y-%m-%dT%H:%M:%SZ) - curl -H "Content-Type: application/json" \ - -X POST \ - -d "{ - \"embeds\": [{ - \"title\": \"$STATUS\", - \"description\": \"**Build & Deploy**\\n- Image: \`${{ env.CONTAINER_IMAGE_NAME }}/${{ env.SERVICE_NAME }}\`\\n- Version: \`${{ env.IMAGE_VERSION }}\`\\n- By: \`${{ gitea.actor }}\`\", - \"color\": $COLOR, - \"footer\": { - \"text\": \"Release Notification\", - \"icon_url\": \"https://example.com/success-icon.png\" - }, - \"timestamp\": \"$TIMESTAMP\" - }] - }" \ - ${{ env.DISCORD_WEBHOOK }} diff --git a/.forgejo/workflows/build-insignia.yml b/.forgejo/workflows/build-insignia.yml deleted file mode 100644 index 7ddcbdbd..00000000 --- a/.forgejo/workflows/build-insignia.yml +++ /dev/null @@ -1,83 +0,0 @@ -name: Build & Deploy Insignia Service - -on: - push: - tags: - - "insignia-dev[0-9]+.[0-9]+.[0-9]+" - - "insignia-dev[0-9]+.[0-9]+.[0-9]+*" - workflow_dispatch: - -env: - REGISTRY: ${{ vars.CONTAINER_REGISTRY }} - REGISTRY_USERNAME: ${{ vars.CONTAINER_REGISTRY_USERNAME }} - REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }} - CONTAINER_IMAGE_NAME: ${{ vars.CONTAINER_REGISTRY }}/${{ vars.CONTAINER_IMAGE_OWNER }} - IMAGE_VERSION: latest - SERVICE_NAME: hrms-api-insignia - DISCORD_WEBHOOK: ${{ vars.DISCORD_WEBHOOK }} - -jobs: - build: - runs-on: ubuntu-latest - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Tag Version - shell: bash - run: | - if [[ "${{ github.event_name }}" == "push" ]]; then - VERSION=$(echo "${{ github.ref_name }}" | sed 's/insignia-dev//g') - echo "IMAGE_VERSION=${VERSION}" >> $GITHUB_ENV - else - echo "IMAGE_VERSION=${{ env.IMAGE_VERSION }}-${{ github.run_number }}" >> $GITHUB_ENV - fi - - name: Login in to registry - uses: docker/login-action@v3 - with: - registry: ${{ env.REGISTRY }} - username: ${{ env.REGISTRY_USERNAME }} - password: ${{ env.REGISTRY_PASSWORD }} - - - name: Build and push docker image - uses: docker/build-push-action@v5 - with: - platforms: linux/amd64 - context: . - file: ./BMA.EHR.Insignia/Dockerfile - tags: ${{ env.CONTAINER_IMAGE_NAME }}/${{ env.SERVICE_NAME }}:latest,${{ env.CONTAINER_IMAGE_NAME }}/${{ env.SERVICE_NAME }}:${{ env.IMAGE_VERSION }} - push: true - - - name: Remote Deploy - uses: appleboy/ssh-action@v1.2.1 - with: - host: ${{ vars.SSH_DEPLOY_HOST }} - port: ${{ vars.SSH_DEPLOY_PORT }} - username: ${{ secrets.SSH_DEPLOY_USER }} - password: ${{ secrets.SSH_DEPLOY_PASSWORD }} - script: | - cd ~/repo - ./replace-env.sh API_INSIGNIA "${{ env.IMAGE_VERSION }}" - ./deploy.sh ${{ env.SERVICE_NAME }} - - - name: Discord Notification - if: always() - run: | - STATUS="${{ job.status == 'success' && '✅ Success' || '❌ Failed' }}" - COLOR="${{ job.status == 'success' && '3066993' || '15158332' }}" - TIMESTAMP=$(date -u +%Y-%m-%dT%H:%M:%SZ) - curl -H "Content-Type: application/json" \ - -X POST \ - -d "{ - \"embeds\": [{ - \"title\": \"$STATUS\", - \"description\": \"**Build & Deploy**\\n- Image: \`${{ env.CONTAINER_IMAGE_NAME }}/${{ env.SERVICE_NAME }}\`\\n- Version: \`${{ env.IMAGE_VERSION }}\`\\n- By: \`${{ gitea.actor }}\`\", - \"color\": $COLOR, - \"footer\": { - \"text\": \"Release Notification\", - \"icon_url\": \"https://example.com/success-icon.png\" - }, - \"timestamp\": \"$TIMESTAMP\" - }] - }" \ - ${{ env.DISCORD_WEBHOOK }} diff --git a/.forgejo/workflows/build-leave.yml b/.forgejo/workflows/build-leave.yml deleted file mode 100644 index b1c9c168..00000000 --- a/.forgejo/workflows/build-leave.yml +++ /dev/null @@ -1,83 +0,0 @@ -name: Build & Deploy Leave Service - -on: - push: - tags: - - "leave-dev[0-9]+.[0-9]+.[0-9]+" - - "leave-dev[0-9]+.[0-9]+.[0-9]+*" - workflow_dispatch: - -env: - REGISTRY: ${{ vars.CONTAINER_REGISTRY }} - REGISTRY_USERNAME: ${{ vars.CONTAINER_REGISTRY_USERNAME }} - REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }} - CONTAINER_IMAGE_NAME: ${{ vars.CONTAINER_REGISTRY }}/${{ vars.CONTAINER_IMAGE_OWNER }} - IMAGE_VERSION: latest - SERVICE_NAME: hrms-api-leave - DISCORD_WEBHOOK: ${{ vars.DISCORD_WEBHOOK }} - -jobs: - build: - runs-on: ubuntu-latest - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Tag Version - shell: bash - run: | - if [[ "${{ github.event_name }}" == "push" ]]; then - VERSION=$(echo "${{ github.ref_name }}" | sed 's/leave-dev//g') - echo "IMAGE_VERSION=${VERSION}" >> $GITHUB_ENV - else - echo "IMAGE_VERSION=${{ env.IMAGE_VERSION }}-${{ github.run_number }}" >> $GITHUB_ENV - fi - - name: Login in to registry - uses: docker/login-action@v3 - with: - registry: ${{ env.REGISTRY }} - username: ${{ env.REGISTRY_USERNAME }} - password: ${{ env.REGISTRY_PASSWORD }} - - - name: Build and push docker image - uses: docker/build-push-action@v5 - with: - platforms: linux/amd64 - context: . - file: ./BMA.EHR.Leave/Dockerfile - tags: ${{ env.CONTAINER_IMAGE_NAME }}/${{ env.SERVICE_NAME }}:latest,${{ env.CONTAINER_IMAGE_NAME }}/${{ env.SERVICE_NAME }}:${{ env.IMAGE_VERSION }} - push: true - - - name: Remote Deploy - uses: appleboy/ssh-action@v1.2.1 - with: - host: ${{ vars.SSH_DEPLOY_HOST }} - port: ${{ vars.SSH_DEPLOY_PORT }} - username: ${{ secrets.SSH_DEPLOY_USER }} - password: ${{ secrets.SSH_DEPLOY_PASSWORD }} - script: | - cd ~/repo - ./replace-env.sh API_LEAVE "${{ env.IMAGE_VERSION }}" - ./deploy.sh ${{ env.SERVICE_NAME }} - - - name: Discord Notification - if: always() - run: | - STATUS="${{ job.status == 'success' && '✅ Success' || '❌ Failed' }}" - COLOR="${{ job.status == 'success' && '3066993' || '15158332' }}" - TIMESTAMP=$(date -u +%Y-%m-%dT%H:%M:%SZ) - curl -H "Content-Type: application/json" \ - -X POST \ - -d "{ - \"embeds\": [{ - \"title\": \"$STATUS\", - \"description\": \"**Build & Deploy**\\n- Image: \`${{ env.CONTAINER_IMAGE_NAME }}/${{ env.SERVICE_NAME }}\`\\n- Version: \`${{ env.IMAGE_VERSION }}\`\\n- By: \`${{ gitea.actor }}\`\", - \"color\": $COLOR, - \"footer\": { - \"text\": \"Release Notification\", - \"icon_url\": \"https://example.com/success-icon.png\" - }, - \"timestamp\": \"$TIMESTAMP\" - }] - }" \ - ${{ env.DISCORD_WEBHOOK }} diff --git a/.forgejo/workflows/build-placement.yml b/.forgejo/workflows/build-placement.yml deleted file mode 100644 index 351b1e59..00000000 --- a/.forgejo/workflows/build-placement.yml +++ /dev/null @@ -1,83 +0,0 @@ -name: Build & Deploy Placement Service - -on: - push: - tags: - - "placement-dev[0-9]+.[0-9]+.[0-9]+" - - "placement-dev[0-9]+.[0-9]+.[0-9]+*" - workflow_dispatch: - -env: - REGISTRY: ${{ vars.CONTAINER_REGISTRY }} - REGISTRY_USERNAME: ${{ vars.CONTAINER_REGISTRY_USERNAME }} - REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }} - CONTAINER_IMAGE_NAME: ${{ vars.CONTAINER_REGISTRY }}/${{ vars.CONTAINER_IMAGE_OWNER }} - IMAGE_VERSION: latest - SERVICE_NAME: hrms-api-placement - DISCORD_WEBHOOK: ${{ vars.DISCORD_WEBHOOK }} - -jobs: - build: - runs-on: ubuntu-latest - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Tag Version - shell: bash - run: | - if [[ "${{ github.event_name }}" == "push" ]]; then - VERSION=$(echo "${{ github.ref_name }}" | sed 's/placement-dev//g') - echo "IMAGE_VERSION=${VERSION}" >> $GITHUB_ENV - else - echo "IMAGE_VERSION=${{ env.IMAGE_VERSION }}-${{ github.run_number }}" >> $GITHUB_ENV - fi - - name: Login in to registry - uses: docker/login-action@v3 - with: - registry: ${{ env.REGISTRY }} - username: ${{ env.REGISTRY_USERNAME }} - password: ${{ env.REGISTRY_PASSWORD }} - - - name: Build and push docker image - uses: docker/build-push-action@v5 - with: - platforms: linux/amd64 - context: . - file: ./BMA.EHR.Placement.Service/Dockerfile - tags: ${{ env.CONTAINER_IMAGE_NAME }}/${{ env.SERVICE_NAME }}:latest,${{ env.CONTAINER_IMAGE_NAME }}/${{ env.SERVICE_NAME }}:${{ env.IMAGE_VERSION }} - push: true - - - name: Remote Deploy - uses: appleboy/ssh-action@v1.2.1 - with: - host: ${{ vars.SSH_DEPLOY_HOST }} - port: ${{ vars.SSH_DEPLOY_PORT }} - username: ${{ secrets.SSH_DEPLOY_USER }} - password: ${{ secrets.SSH_DEPLOY_PASSWORD }} - script: | - cd ~/repo - ./replace-env.sh API_PLACEMENT "${{ env.IMAGE_VERSION }}" - ./deploy.sh ${{ env.SERVICE_NAME }} - - - name: Discord Notification - if: always() - run: | - STATUS="${{ job.status == 'success' && '✅ Success' || '❌ Failed' }}" - COLOR="${{ job.status == 'success' && '3066993' || '15158332' }}" - TIMESTAMP=$(date -u +%Y-%m-%dT%H:%M:%SZ) - curl -H "Content-Type: application/json" \ - -X POST \ - -d "{ - \"embeds\": [{ - \"title\": \"$STATUS\", - \"description\": \"**Build & Deploy**\\n- Image: \`${{ env.CONTAINER_IMAGE_NAME }}/${{ env.SERVICE_NAME }}\`\\n- Version: \`${{ env.IMAGE_VERSION }}\`\\n- By: \`${{ gitea.actor }}\`\", - \"color\": $COLOR, - \"footer\": { - \"text\": \"Release Notification\", - \"icon_url\": \"https://example.com/success-icon.png\" - }, - \"timestamp\": \"$TIMESTAMP\" - }] - }" \ - ${{ env.DISCORD_WEBHOOK }} diff --git a/.forgejo/workflows/build-report_backup.yml b/.forgejo/workflows/build-report_backup.yml deleted file mode 100644 index 664de47b..00000000 --- a/.forgejo/workflows/build-report_backup.yml +++ /dev/null @@ -1,84 +0,0 @@ -# name: Build - -# on: -# push: -# tags: -# - "report-dev[0-9]+.[0-9]+.[0-9]+" -# - "report-dev[0-9]+.[0-9]+.[0-9]+*" -# workflow_dispatch: - -# env: -# REGISTRY: ${{ vars.CONTAINER_REGISTRY }} -# REGISTRY_USERNAME: ${{ vars.CONTAINER_REGISTRY_USERNAME }} -# REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }} -# CONTAINER_IMAGE_NAME: ${{ vars.CONTAINER_REGISTRY }}/${{ vars.CONTAINER_IMAGE_OWNER }} -# SERVICE_NAME: hrms-api-report -# IMAGE_VERSION: build -# DISCORD_WEBHOOK: ${{ vars.DISCORD_WEBHOOK }} - -# jobs: -# build: -# runs-on: ubuntu-latest -# steps: -# - name: Checkout repository -# uses: actions/checkout@v4 -# - name: Set up Docker Buildx -# uses: docker/setup-buildx-action@v2 -# with: -# config-inline: | -# [registry."${{ env.REGISTRY }}"] -# ca=["/etc/ssl/certs/ca-certificates.crt"] -# - name: Tag Version -# run: | -# if [[ "${{ github.event_name }}" == "push" ]]; then -# echo "IMAGE_VERSION=${{ github.ref_name }}" | sed 's/report-dev//g' >> $GITHUB_ENV -# else -# echo "IMAGE_VERSION=${{ env.IMAGE_VERSION }}-${{ github.run_number }}" >> $GITHUB_ENV -# fi -# - name: Login in to registry -# uses: docker/login-action@v2 -# with: -# registry: ${{ env.REGISTRY }} -# username: ${{ env.REGISTRY_USERNAME }} -# password: ${{ env.REGISTRY_PASSWORD }} -# - name: Build and push docker image -# uses: docker/build-push-action@v3 -# with: -# platforms: linux/amd64 -# context: . -# file: ./BMA.EHR.Report.Service/Dockerfile -# tags: ${{ env.CONTAINER_IMAGE_NAME }}/${{ env.SERVICE_NAME }}:latest,${{ env.CONTAINER_IMAGE_NAME }}/${{ env.SERVICE_NAME }}:${{ env.IMAGE_VERSION }} -# push: true -# - name: Remote Deploy -# uses: appleboy/ssh-action@v1.2.1 -# with: -# host: ${{ vars.SSH_DEPLOY_HOST }} -# port: ${{ vars.SSH_DEPLOY_PORT }} -# username: ${{ secrets.SSH_DEPLOY_USER }} -# password: ${{ secrets.SSH_DEPLOY_PASSWORD }} -# script: | -# cd ~/repo -# ./replace-env.sh API_REPORT "${{ env.IMAGE_VERSION }}" -# ./deploy.sh ${{ env.SERVICE_NAME }} - -# - name: Discord Notification -# if: always() -# run: | -# STATUS="${{ job.status == 'success' && '✅ Success' || '❌ Failed' }}" -# COLOR="${{ job.status == 'success' && '3066993' || '15158332' }}" -# TIMESTAMP=$(date -u +%Y-%m-%dT%H:%M:%SZ) -# curl -H "Content-Type: application/json" \ -# -X POST \ -# -d "{ -# \"embeds\": [{ -# \"title\": \"$STATUS\", -# \"description\": \"**Build & Deploy**\\n- Image: \`${{ env.CONTAINER_IMAGE_NAME }}/${{ env.SERVICE_NAME }}\`\\n- Version: \`${{ env.IMAGE_VERSION }}\`\\n- By: \`${{ github.actor }}\`\", -# \"color\": $COLOR, -# \"footer\": { -# \"text\": \"Release Notification\", -# \"icon_url\": \"https://example.com/success-icon.png\" -# }, -# \"timestamp\": \"$TIMESTAMP\" -# }] -# }" \ -# ${{ env.DISCORD_WEBHOOK }} diff --git a/.forgejo/workflows/build-retirement.yml b/.forgejo/workflows/build-retirement.yml deleted file mode 100644 index 257f68f2..00000000 --- a/.forgejo/workflows/build-retirement.yml +++ /dev/null @@ -1,83 +0,0 @@ -name: Build & Deploy Retirement Service - -on: - push: - tags: - - "retirement-dev[0-9]+.[0-9]+.[0-9]+" - - "retirement-dev[0-9]+.[0-9]+.[0-9]+*" - workflow_dispatch: - -env: - REGISTRY: ${{ vars.CONTAINER_REGISTRY }} - REGISTRY_USERNAME: ${{ vars.CONTAINER_REGISTRY_USERNAME }} - REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }} - CONTAINER_IMAGE_NAME: ${{ vars.CONTAINER_REGISTRY }}/${{ vars.CONTAINER_IMAGE_OWNER }} - IMAGE_VERSION: latest - SERVICE_NAME: hrms-api-retirement - DISCORD_WEBHOOK: ${{ vars.DISCORD_WEBHOOK }} - -jobs: - build: - runs-on: ubuntu-latest - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Tag Version - shell: bash - run: | - if [[ "${{ github.event_name }}" == "push" ]]; then - VERSION=$(echo "${{ github.ref_name }}" | sed 's/retirement-dev//g') - echo "IMAGE_VERSION=${VERSION}" >> $GITHUB_ENV - else - echo "IMAGE_VERSION=${{ env.IMAGE_VERSION }}-${{ github.run_number }}" >> $GITHUB_ENV - fi - - name: Login in to registry - uses: docker/login-action@v3 - with: - registry: ${{ env.REGISTRY }} - username: ${{ env.REGISTRY_USERNAME }} - password: ${{ env.REGISTRY_PASSWORD }} - - - name: Build and push docker image - uses: docker/build-push-action@v5 - with: - platforms: linux/amd64 - context: . - file: ./BMA.EHR.Retirement.Service/Dockerfile - tags: ${{ env.CONTAINER_IMAGE_NAME }}/${{ env.SERVICE_NAME }}:latest,${{ env.CONTAINER_IMAGE_NAME }}/${{ env.SERVICE_NAME }}:${{ env.IMAGE_VERSION }} - push: true - - - name: Remote Deploy - uses: appleboy/ssh-action@v1.2.1 - with: - host: ${{ vars.SSH_DEPLOY_HOST }} - port: ${{ vars.SSH_DEPLOY_PORT }} - username: ${{ secrets.SSH_DEPLOY_USER }} - password: ${{ secrets.SSH_DEPLOY_PASSWORD }} - script: | - cd ~/repo - ./replace-env.sh API_RETIREMENT "${{ env.IMAGE_VERSION }}" - ./deploy.sh ${{ env.SERVICE_NAME }} - - - name: Discord Notification - if: always() - run: | - STATUS="${{ job.status == 'success' && '✅ Success' || '❌ Failed' }}" - COLOR="${{ job.status == 'success' && '3066993' || '15158332' }}" - TIMESTAMP=$(date -u +%Y-%m-%dT%H:%M:%SZ) - curl -H "Content-Type: application/json" \ - -X POST \ - -d "{ - \"embeds\": [{ - \"title\": \"$STATUS\", - \"description\": \"**Build & Deploy**\\n- Image: \`${{ env.CONTAINER_IMAGE_NAME }}/${{ env.SERVICE_NAME }}\`\\n- Version: \`${{ env.IMAGE_VERSION }}\`\\n- By: \`${{ gitea.actor }}\`\", - \"color\": $COLOR, - \"footer\": { - \"text\": \"Release Notification\", - \"icon_url\": \"https://example.com/success-icon.png\" - }, - \"timestamp\": \"$TIMESTAMP\" - }] - }" \ - ${{ env.DISCORD_WEBHOOK }} diff --git a/.forgejo/workflows/build.yml b/.forgejo/workflows/build.yml deleted file mode 100644 index a4f2d2e5..00000000 --- a/.forgejo/workflows/build.yml +++ /dev/null @@ -1,49 +0,0 @@ -name: Build - -on: - push: - tags: - - "leave[0-9]+.[0-9]+.[0-9]+" - - "leave[0-9]+.[0-9]+.[0-9]+*" - workflow_dispatch: - -env: - REGISTRY: ${{ vars.CONTAINER_REGISTRY }} - REGISTRY_USERNAME: ${{ vars.CONTAINER_REGISTRY_USERNAME }} - REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }} - CONTAINER_IMAGE_NAME: ${{ vars.CONTAINER_REGISTRY }}/${{ vars.CONTAINER_IMAGE_OWNER }} - IMAGE_VERSION: build - -jobs: - build: - runs-on: ubuntu-latest - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2 - with: - config-inline: | - [registry."${{ env.REGISTRY }}"] - ca=["/etc/ssl/certs/ca-certificates.crt"] - - name: Tag Version - run: | - if [[ "${{ github.event_name }}" == "push" ]]; then - echo "IMAGE_VERSION=${{ github.ref_name }}" | sed 's/leave//g' >> $GITHUB_ENV - else - echo "IMAGE_VERSION=${{ env.IMAGE_VERSION }}-${{ github.run_number }}" >> $GITHUB_ENV - fi - - name: Login in to registry - uses: docker/login-action@v2 - with: - registry: ${{ env.REGISTRY }} - username: ${{ env.REGISTRY_USERNAME }} - password: ${{ env.REGISTRY_PASSWORD }} - - name: Build and push docker image - uses: docker/build-push-action@v3 - with: - platforms: linux/amd64 - context: . - file: ./BMA.EHR.Leave/Dockerfile - tags: ${{ env.CONTAINER_IMAGE_NAME }}/hrms-leave:latest,${{ env.CONTAINER_IMAGE_NAME }}/hrms-leave:${{ env.IMAGE_VERSION }} - push: true diff --git a/.forgejo/workflows/deploy.yml b/.forgejo/workflows/deploy.yml deleted file mode 100644 index 7a18d4cc..00000000 --- a/.forgejo/workflows/deploy.yml +++ /dev/null @@ -1,29 +0,0 @@ -name: Build - -on: - workflow_dispatch: - inputs: - version: - description: "Version to deploy" - type: string - required: false - default: "latest" - -env: - IMAGE_VERSION: build - -jobs: - deploy: - runs-on: ubuntu-latest - steps: - - name: Remote Deploy - uses: appleboy/ssh-action@v1.2.1 - with: - host: ${{ vars.SSH_DEPLOY_HOST }} - port: ${{ vars.SSH_DEPLOY_PORT }} - username: ${{ secrets.SSH_DEPLOY_USER }} - password: ${{ secrets.SSH_DEPLOY_PASSWORD }} - script: | - cd ~/repo - ./replace-env.sh API_LEAVE "${{ inputs.version }}" - ./deploy.sh hrms-api-leave From 0b0cc53e0748a1b1b2297c072adeddae53228831 Mon Sep 17 00:00:00 2001 From: kittapath <> Date: Wed, 31 Dec 2025 10:07:01 +0700 Subject: [PATCH 010/178] change to v2 --- BMA.EHR.Leave/Controllers/LeaveReportController.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BMA.EHR.Leave/Controllers/LeaveReportController.cs b/BMA.EHR.Leave/Controllers/LeaveReportController.cs index 3b4e81fd..3f9b9cf1 100644 --- a/BMA.EHR.Leave/Controllers/LeaveReportController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveReportController.cs @@ -1999,7 +1999,7 @@ namespace BMA.EHR.Leave.Service.Controllers if (type.Trim().ToUpper() == "OFFICER") { - profile = await _userProfileRepository.GetProfileByAdminRole(AccessToken, profileAdmin?.Node, nodeId, role, req.revisionId, req.node, req.nodeId, req.StartDate.Date, req.EndDate.Date); + profile = await _userProfileRepository.GetProfileByAdminRolev2(AccessToken, profileAdmin?.Node, nodeId, role, req.revisionId, req.node, req.nodeId, req.StartDate.Date, req.EndDate.Date); } else { From cb074e3e25b7e706caed45b1d8fbe1621bf78c08 Mon Sep 17 00:00:00 2001 From: kittapath <> Date: Fri, 2 Jan 2026 21:46:39 +0700 Subject: [PATCH 011/178] find org v2 --- BMA.EHR.Application/Repositories/UserProfileRepository.cs | 5 +++-- BMA.EHR.Leave/Controllers/LeaveReportController.cs | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/BMA.EHR.Application/Repositories/UserProfileRepository.cs b/BMA.EHR.Application/Repositories/UserProfileRepository.cs index 150aeded..7ffdc024 100644 --- a/BMA.EHR.Application/Repositories/UserProfileRepository.cs +++ b/BMA.EHR.Application/Repositories/UserProfileRepository.cs @@ -574,6 +574,7 @@ namespace BMA.EHR.Application.Repositories // endDate = endDate date = endDate }; + Console.WriteLine(body); var profiles = new List(); @@ -585,7 +586,7 @@ namespace BMA.EHR.Application.Repositories return raw.Result; } - return null; + return new List(); } catch { @@ -983,7 +984,7 @@ namespace BMA.EHR.Application.Repositories { try { - var apiPath = $"{_configuration["API"]}/org/find/all"; + var apiPath = $"{_configuration["API"]}/org/find/allv2"; var apiKey = _configuration["API_KEY"]; var body = new { diff --git a/BMA.EHR.Leave/Controllers/LeaveReportController.cs b/BMA.EHR.Leave/Controllers/LeaveReportController.cs index 3f9b9cf1..b5f620c0 100644 --- a/BMA.EHR.Leave/Controllers/LeaveReportController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveReportController.cs @@ -2241,7 +2241,7 @@ namespace BMA.EHR.Leave.Service.Controllers var enddate = req.EndDate.Date == req.StartDate.Date ? "" : $" - {req.EndDate.Date.ToThaiShortDate()}"; var org = _userProfileRepository.GetOc(Guid.Parse(req.nodeId), req.node, AccessToken); - var organizationName = $"{(!string.IsNullOrEmpty(org.Child4) ? org.Child4 + "/" : "")}{(!string.IsNullOrEmpty(org.Child3) ? org.Child3 + "/" : "")}{(!string.IsNullOrEmpty(org.Child2) ? org.Child2 + "/" : "")}{(!string.IsNullOrEmpty(org.Child1) ? org.Child1 + "/" : "")}{org.Root ?? ""}"; + var organizationName = org == null ? "" : $"{(!string.IsNullOrEmpty(org.Child4) ? org.Child4 + "/" : "")}{(!string.IsNullOrEmpty(org.Child3) ? org.Child3 + "/" : "")}{(!string.IsNullOrEmpty(org.Child2) ? org.Child2 + "/" : "")}{(!string.IsNullOrEmpty(org.Child1) ? org.Child1 + "/" : "")}{org.Root ?? ""}"; var dateTimeStamp = $"ประจำ ณ วัน{req.StartDate.Date.GetThaiDayOfWeek()} ที่ {req.StartDate.Date.ToThaiShortDate()}{enddate}"; var templatePath = Path.Combine(_hostingEnvironment.ContentRootPath, "Reports", "TimeStampRecords.xlsx"); From 909042445c7c52e0093d837bc31a1171299fa7bf Mon Sep 17 00:00:00 2001 From: kittapath <> Date: Fri, 2 Jan 2026 21:47:26 +0700 Subject: [PATCH 012/178] find org v2 --- BMA.EHR.Application/Repositories/UserProfileRepository.cs | 5 +++-- BMA.EHR.Leave/Controllers/LeaveReportController.cs | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/BMA.EHR.Application/Repositories/UserProfileRepository.cs b/BMA.EHR.Application/Repositories/UserProfileRepository.cs index 150aeded..7ffdc024 100644 --- a/BMA.EHR.Application/Repositories/UserProfileRepository.cs +++ b/BMA.EHR.Application/Repositories/UserProfileRepository.cs @@ -574,6 +574,7 @@ namespace BMA.EHR.Application.Repositories // endDate = endDate date = endDate }; + Console.WriteLine(body); var profiles = new List(); @@ -585,7 +586,7 @@ namespace BMA.EHR.Application.Repositories return raw.Result; } - return null; + return new List(); } catch { @@ -983,7 +984,7 @@ namespace BMA.EHR.Application.Repositories { try { - var apiPath = $"{_configuration["API"]}/org/find/all"; + var apiPath = $"{_configuration["API"]}/org/find/allv2"; var apiKey = _configuration["API_KEY"]; var body = new { diff --git a/BMA.EHR.Leave/Controllers/LeaveReportController.cs b/BMA.EHR.Leave/Controllers/LeaveReportController.cs index 3f9b9cf1..b5f620c0 100644 --- a/BMA.EHR.Leave/Controllers/LeaveReportController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveReportController.cs @@ -2241,7 +2241,7 @@ namespace BMA.EHR.Leave.Service.Controllers var enddate = req.EndDate.Date == req.StartDate.Date ? "" : $" - {req.EndDate.Date.ToThaiShortDate()}"; var org = _userProfileRepository.GetOc(Guid.Parse(req.nodeId), req.node, AccessToken); - var organizationName = $"{(!string.IsNullOrEmpty(org.Child4) ? org.Child4 + "/" : "")}{(!string.IsNullOrEmpty(org.Child3) ? org.Child3 + "/" : "")}{(!string.IsNullOrEmpty(org.Child2) ? org.Child2 + "/" : "")}{(!string.IsNullOrEmpty(org.Child1) ? org.Child1 + "/" : "")}{org.Root ?? ""}"; + var organizationName = org == null ? "" : $"{(!string.IsNullOrEmpty(org.Child4) ? org.Child4 + "/" : "")}{(!string.IsNullOrEmpty(org.Child3) ? org.Child3 + "/" : "")}{(!string.IsNullOrEmpty(org.Child2) ? org.Child2 + "/" : "")}{(!string.IsNullOrEmpty(org.Child1) ? org.Child1 + "/" : "")}{org.Root ?? ""}"; var dateTimeStamp = $"ประจำ ณ วัน{req.StartDate.Date.GetThaiDayOfWeek()} ที่ {req.StartDate.Date.ToThaiShortDate()}{enddate}"; var templatePath = Path.Combine(_hostingEnvironment.ContentRootPath, "Reports", "TimeStampRecords.xlsx"); From 05689b5338b8fe0a47c659f5494005e3b2c7d6f0 Mon Sep 17 00:00:00 2001 From: harid Date: Mon, 5 Jan 2026 13:20:00 +0700 Subject: [PATCH 013/178] =?UTF-8?q?=E0=B8=9B=E0=B8=A3=E0=B8=B1=E0=B8=9A=20?= =?UTF-8?q?query=20=E0=B9=81=E0=B8=9A=E0=B8=9A=E0=B9=83=E0=B8=9A=E0=B8=82?= =?UTF-8?q?=E0=B8=AD=E0=B8=A2=E0=B8=81=E0=B9=80=E0=B8=A5=E0=B8=B4=E0=B8=81?= =?UTF-8?q?=E0=B8=A7=E0=B8=B1=E0=B8=99=E0=B8=A5=E0=B8=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Controllers/LeaveReportController.cs | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/BMA.EHR.Leave/Controllers/LeaveReportController.cs b/BMA.EHR.Leave/Controllers/LeaveReportController.cs index b5f620c0..9f88401b 100644 --- a/BMA.EHR.Leave/Controllers/LeaveReportController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveReportController.cs @@ -935,7 +935,7 @@ namespace BMA.EHR.Leave.Service.Controllers // if (list.Count > 0) // approver = list.First().Name; //} - + var approveResult = await GetApproverData(data.Approvers); var result = new { template = "แบบใบขอยกเลิกวันลา", @@ -956,6 +956,22 @@ namespace BMA.EHR.Leave.Service.Controllers profileType = data.ProfileType, leaveReasonDelete = data.LeaveCancelComment == null ? "" : data.LeaveCancelComment!.ToThaiNumber(), leaveDetail = data.LeaveDetail.ToThaiNumber(), + + Type1 = profile.ProfileType == "OFFICER" ? "🗹" : "☐", + Type2 = profile.ProfileType != "OFFICER" ? "🗹" : "☐", + Type3 = "☐", + approve = approveResult, + approverComment = !string.IsNullOrEmpty(data.LeaveDirectorComment) + ? data.LeaveDirectorComment.Replace("\r", "").Replace("\n", "").Trim() + : "......................", + approverUpdatedAt = data.LastUpdatedAt.HasValue + ? data.LastUpdatedAt.Value.ToThaiShortDate().ToThaiNumber() + : "...... /...... /......", + leaveStatus = data.LeaveCancelStatus != null && data.LeaveCancelStatus!.ToUpper() == "APPROVE" + ? "🗹 อนุญาต ☐ ไม่อนุญาต" + : data.LeaveCancelStatus != null && data.LeaveCancelStatus!.ToUpper() == "REJECT" + ? "☐ อนุญาต 🗹 ไม่อนุญาต" + : "☐ อนุญาต ☐ ไม่อนุญาต" } }; From 517755d758272ea6bf93cc6098adcd4becfa4f77 Mon Sep 17 00:00:00 2001 From: harid Date: Mon, 5 Jan 2026 16:12:11 +0700 Subject: [PATCH 014/178] =?UTF-8?q?fix=20=E0=B8=A3=E0=B8=B1=E0=B8=99?= =?UTF-8?q?=E0=B9=80=E0=B8=84=E0=B8=A3=E0=B8=B7=E0=B9=88=E0=B8=AD=E0=B8=87?= =?UTF-8?q?=E0=B8=A3=E0=B8=B2=E0=B8=8A=E0=B9=81=E0=B8=95=E0=B9=88=E0=B8=AB?= =?UTF-8?q?=E0=B8=99=E0=B9=88=E0=B8=A7=E0=B8=A2=E0=B8=87=E0=B8=B2=E0=B8=99?= =?UTF-8?q?=E0=B9=84=E0=B8=A1=E0=B9=88=E0=B8=84=E0=B8=A3=E0=B8=9A=20#1831?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Repositories/InsigniaPeriodsRepository.cs | 1705 +++++++++-------- 1 file changed, 873 insertions(+), 832 deletions(-) diff --git a/BMA.EHR.Application/Repositories/InsigniaPeriodsRepository.cs b/BMA.EHR.Application/Repositories/InsigniaPeriodsRepository.cs index a520f502..ddee97cb 100644 --- a/BMA.EHR.Application/Repositories/InsigniaPeriodsRepository.cs +++ b/BMA.EHR.Application/Repositories/InsigniaPeriodsRepository.cs @@ -297,10 +297,10 @@ namespace BMA.EHR.Application.Repositories : p.ProfileInsignia!.OrderByDescending(x => x.Year).FirstOrDefault()!.InsigniaId.Value, Salary = p.Amount, SalaryCondition = p.ProfileSalary == null || p.ProfileSalary.Count == 0 ? 0 : - p.ProfileSalary.Where(x => x.Date != null).Where(x => x.Date.Value <= new DateTime(period.Year, 4, 29)) + p.ProfileSalary.Where(x => x.Date != null).Where(x => x.Date.HasValue && x.Date.Value <= new DateTime(period.Year, 4, 29)) .OrderByDescending(x => x.Order).FirstOrDefault() != null ? p.ProfileSalary - .Where(x => x.Date != null).Where(x => x.Date.Value <= new DateTime(period.Year, 4, 29)) - .OrderByDescending(x => x.Order).FirstOrDefault().Amount : + .Where(x => x.Date != null).Where(x => x.Date.HasValue && x.Date.Value <= new DateTime(period.Year, 4, 29)) + .OrderByDescending(x => x.Order).FirstOrDefault()?.Amount : p.Amount, PositionSalaryAmount = p.PositionSalaryAmount ?? 0, ProfileInsignia = p.ProfileInsignia, @@ -409,7 +409,7 @@ namespace BMA.EHR.Application.Repositories var s2_a = (from p in allProfilesByRoot where p.ProfileInsignia != null && p.ProfileInsignia.Count > 0 - && (p.ProfileInsignia.Where(x => x.InsigniaId.Value != coinInsignia.Id && + && (p.ProfileInsignia.Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != coinInsignia.Id && x.InsigniaId.Value == bcpRoyal.Id) .ToList() .Count() == 0) @@ -459,22 +459,22 @@ namespace BMA.EHR.Application.Repositories : p.ProfileInsignia!.OrderByDescending(x => x.Year).FirstOrDefault()!.InsigniaId.Value, Salary = p.Amount, SalaryCondition = p.ProfileSalary == null || p.ProfileSalary.Count == 0 ? 0 : - p.ProfileSalary.Where(x => x.Date != null).Where(x => x.Date.Value <= new DateTime(period.Year, 4, 29)) + p.ProfileSalary.Where(x => x.Date != null).Where(x => x.Date.HasValue && x.Date.Value <= new DateTime(period.Year, 4, 29)) .OrderByDescending(x => x.Order).FirstOrDefault() != null ? p.ProfileSalary - .Where(x => x.Date != null).Where(x => x.Date.Value <= new DateTime(period.Year, 4, 29)) - .OrderByDescending(x => x.Order).FirstOrDefault().Amount : + .Where(x => x.Date != null).Where(x => x.Date.HasValue && x.Date.Value <= new DateTime(period.Year, 4, 29)) + .OrderByDescending(x => x.Order).FirstOrDefault()?.Amount : p.Amount, IsHigherLevel = IsHigherLevel(p.ProfileInsignia.ToList() - .Where(x => x.InsigniaId.Value != coinInsignia.Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != coinInsignia.Id) .OrderByDescending(x => x.Year) .FirstOrDefault(), "เบญจมาภรณ์ช้างเผือก"), FirstRecvInsigniaYear = p.ProfileInsignia == null || p.ProfileInsignia.Count == 0 ? 0 : - p.ProfileInsignia.Where(x => x.InsigniaId.Value == bcmRoyal.Id).OrderBy(x => x.Year) + p.ProfileInsignia.Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value == bcmRoyal.Id).OrderBy(x => x.Year) .FirstOrDefault() == null ? 0 : - p.ProfileInsignia.Where(x => x.InsigniaId.Value == bcmRoyal.Id).OrderBy(x => x.Year) - .FirstOrDefault().Year, + p.ProfileInsignia.Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value == bcmRoyal.Id).OrderBy(x => x.Year) + .FirstOrDefault()?.Year, ProfileType = p.ProfileType, MarkDiscipline = p.MarkDiscipline, @@ -555,7 +555,7 @@ namespace BMA.EHR.Application.Repositories var s3 = (from p in allProfilesByRoot where p.ProfileInsignia != null && p.ProfileInsignia.Count > 0 - && (p.ProfileInsignia.Where(x => x.InsigniaId.Value != coinInsignia.Id && + && (p.ProfileInsignia.Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != coinInsignia.Id && x.InsigniaId.Value == jtmRoyal.Id) .ToList() .Count() == 0) @@ -590,21 +590,21 @@ namespace BMA.EHR.Application.Repositories : p.ProfileInsignia!.OrderByDescending(x => x.Year).FirstOrDefault()!.InsigniaId.Value, Salary = p.Amount, SalaryCondition = p.ProfileSalary == null || p.ProfileSalary.Count == 0 ? 0 : - p.ProfileSalary.Where(x => x.Date != null).Where(x => x.Date.Value <= new DateTime(period.Year, 4, 29)) + p.ProfileSalary.Where(x => x.Date != null).Where(x => x.Date.HasValue && x.Date.Value <= new DateTime(period.Year, 4, 29)) .OrderByDescending(x => x.Order).FirstOrDefault() != null ? p.ProfileSalary - .Where(x => x.Date != null).Where(x => x.Date.Value <= new DateTime(period.Year, 4, 29)) - .OrderByDescending(x => x.Order).FirstOrDefault().Amount : + .Where(x => x.Date != null).Where(x => x.Date.HasValue && x.Date.Value <= new DateTime(period.Year, 4, 29)) + .OrderByDescending(x => x.Order).FirstOrDefault()?.Amount : p.Amount, IsHigherLevel = IsHigherLevel(p.ProfileInsignia.ToList() - .Where(x => x.InsigniaId.Value != coinInsignia.Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != coinInsignia.Id) .OrderByDescending(x => x.Year) .FirstOrDefault(), "จัตุรถาภรณ์มงกุฎไทย"), FirstRecvInsigniaYear = p.ProfileInsignia == null ? 0 : - p.ProfileInsignia.Where(x => x.InsigniaId.Value == bcmRoyal.Id).OrderBy(x => x.Year) + p.ProfileInsignia.Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value == bcmRoyal.Id).OrderBy(x => x.Year) .FirstOrDefault() == null ? 0 : - p.ProfileInsignia.Where(x => x.InsigniaId.Value == bcmRoyal.Id).OrderBy(x => x.Year) - .FirstOrDefault().Year, + p.ProfileInsignia.Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value == bcmRoyal.Id).OrderBy(x => x.Year) + .FirstOrDefault()?.Year, PositionSalaryAmount = p.PositionSalaryAmount ?? 0, Amount = p.Amount ?? 0, RootId = p.RootId, @@ -811,10 +811,10 @@ namespace BMA.EHR.Application.Repositories : p.ProfileInsignia!.OrderByDescending(x => x.Year).FirstOrDefault()!.InsigniaId.Value, Salary = p.Amount, SalaryCondition = p.ProfileSalary == null || p.ProfileSalary.Count == 0 ? 0 : - p.ProfileSalary.Where(x => x.Date != null).Where(x => x.Date.Value <= new DateTime(period.Year, 4, 29)) + p.ProfileSalary.Where(x => x.Date != null).Where(x => x.Date.HasValue && x.Date.Value <= new DateTime(period.Year, 4, 29)) .OrderByDescending(x => x.Order).FirstOrDefault() != null ? p.ProfileSalary - .Where(x => x.Date != null).Where(x => x.Date.Value <= new DateTime(period.Year, 4, 29)) - .OrderByDescending(x => x.Order).FirstOrDefault().Amount : + .Where(x => x.Date != null).Where(x => x.Date.HasValue && x.Date.Value <= new DateTime(period.Year, 4, 29)) + .OrderByDescending(x => x.Order).FirstOrDefault()?.Amount : p.Amount, PositionSalaryAmount = p.PositionSalaryAmount ?? 0, ProfileInsignia = p.ProfileInsignia, @@ -923,7 +923,7 @@ namespace BMA.EHR.Application.Repositories var s2 = (from p in allProfilesByRoot where p.ProfileInsignia != null && p.ProfileInsignia.Count > 0 - && (p.ProfileInsignia.Where(x => x.InsigniaId.Value != coinInsignia.Id && + && (p.ProfileInsignia.Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != coinInsignia.Id && x.InsigniaId.Value == jtmRoyal.Id) .ToList() .Count() == 0) @@ -956,21 +956,21 @@ namespace BMA.EHR.Application.Repositories : p.ProfileInsignia!.OrderByDescending(x => x.Year).FirstOrDefault()!.InsigniaId.Value, Salary = p.Amount, SalaryCondition = p.ProfileSalary == null || p.ProfileSalary.Count == 0 ? 0 : - p.ProfileSalary.Where(x => x.Date != null).Where(x => x.Date.Value <= new DateTime(period.Year, 4, 29)) + p.ProfileSalary.Where(x => x.Date != null).Where(x => x.Date.HasValue && x.Date.Value <= new DateTime(period.Year, 4, 29)) .OrderByDescending(x => x.Order).FirstOrDefault() != null ? p.ProfileSalary - .Where(x => x.Date != null).Where(x => x.Date.Value <= new DateTime(period.Year, 4, 29)) - .OrderByDescending(x => x.Order).FirstOrDefault().Amount : + .Where(x => x.Date != null).Where(x => x.Date.HasValue && x.Date.Value <= new DateTime(period.Year, 4, 29)) + .OrderByDescending(x => x.Order).FirstOrDefault()?.Amount : p.Amount, IsHigherLevel = IsHigherLevel(p.ProfileInsignia.ToList() - .Where(x => x.InsigniaId.Value != coinInsignia.Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != coinInsignia.Id) .OrderByDescending(x => x.Year) .FirstOrDefault(), "จัตุรถาภรณ์มงกุฎไทย"), FirstRecvInsigniaYear = p.ProfileInsignia == null ? 0 : - p.ProfileInsignia.Where(x => x.InsigniaId.Value == bcpRoyal.Id).OrderBy(x => x.Year) + p.ProfileInsignia.Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value == bcpRoyal.Id).OrderBy(x => x.Year) .FirstOrDefault() == null ? 0 : - p.ProfileInsignia.Where(x => x.InsigniaId.Value == bcpRoyal.Id).OrderBy(x => x.Year) - .FirstOrDefault().Year, + p.ProfileInsignia.Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value == bcpRoyal.Id).OrderBy(x => x.Year) + .FirstOrDefault()?.Year, PositionSalaryAmount = p.PositionSalaryAmount ?? 0, Amount = p.Amount ?? 0, RootId = p.RootId, @@ -1065,7 +1065,7 @@ namespace BMA.EHR.Application.Repositories var s3 = (from p in allProfilesByRoot where p.ProfileInsignia != null && p.ProfileInsignia.Count > 0 - && (p.ProfileInsignia.Where(x => x.InsigniaId.Value != coinInsignia.Id && + && (p.ProfileInsignia.Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != coinInsignia.Id && x.InsigniaId.Value == jtcRoyal.Id) .ToList() .Count() == 0) @@ -1098,21 +1098,21 @@ namespace BMA.EHR.Application.Repositories : p.ProfileInsignia!.OrderByDescending(x => x.Year).FirstOrDefault()!.InsigniaId.Value, Salary = p.Amount, SalaryCondition = p.ProfileSalary == null || p.ProfileSalary.Count == 0 ? 0 : - p.ProfileSalary.Where(x => x.Date != null).Where(x => x.Date.Value <= new DateTime(period.Year, 4, 29)) + p.ProfileSalary.Where(x => x.Date != null).Where(x => x.Date.HasValue && x.Date.Value <= new DateTime(period.Year, 4, 29)) .OrderByDescending(x => x.Order).FirstOrDefault() != null ? p.ProfileSalary - .Where(x => x.Date != null).Where(x => x.Date.Value <= new DateTime(period.Year, 4, 29)) - .OrderByDescending(x => x.Order).FirstOrDefault().Amount : + .Where(x => x.Date != null).Where(x => x.Date.HasValue && x.Date.Value <= new DateTime(period.Year, 4, 29)) + .OrderByDescending(x => x.Order).FirstOrDefault()?.Amount : p.Amount, IsHigherLevel = IsHigherLevel(p.ProfileInsignia.ToList() - .Where(x => x.InsigniaId.Value != coinInsignia.Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != coinInsignia.Id) .OrderByDescending(x => x.Year) .FirstOrDefault(), "จัตุรถาภรณ์ช้างเผือก"), FirstRecvInsigniaYear = p.ProfileInsignia == null ? 0 : - p.ProfileInsignia.Where(x => x.InsigniaId.Value == jtmRoyal.Id).OrderBy(x => x.Year) + p.ProfileInsignia.Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value == jtmRoyal.Id).OrderBy(x => x.Year) .FirstOrDefault() == null ? 0 : - p.ProfileInsignia.Where(x => x.InsigniaId.Value == jtmRoyal.Id).OrderBy(x => x.Year) - .FirstOrDefault().Year, + p.ProfileInsignia.Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value == jtmRoyal.Id).OrderBy(x => x.Year) + .FirstOrDefault()?.Year, PositionSalaryAmount = p.PositionSalaryAmount, Amount = p.Amount, RootId = p.RootId, @@ -1311,24 +1311,24 @@ namespace BMA.EHR.Application.Repositories Gender = p.Gender ?? "", LastInsignia = p.ProfileInsignia == null || p.ProfileInsignia.Count == 0 ? "" : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .FirstOrDefault() == null ? "" : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) - .OrderByDescending(x => x.Year).FirstOrDefault().Insignia, + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) + .OrderByDescending(x => x.Year).FirstOrDefault()?.Insignia, LastInsigniaId = p.ProfileInsignia == null || p.ProfileInsignia.Count == 0 ? Guid.Empty : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .FirstOrDefault() == null ? Guid.Empty : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) - .OrderByDescending(x => x.Year).FirstOrDefault().InsigniaId, + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) + .OrderByDescending(x => x.Year).FirstOrDefault()?.InsigniaId, Salary = p.Amount, SalaryCondition = p.ProfileSalary == null || p.ProfileSalary.Count == 0 ? 0 : - p.ProfileSalary.Where(x => x.Date != null).Where(x => x.Date.Value <= new DateTime(period.Year, 4, 29)) + p.ProfileSalary.Where(x => x.Date != null).Where(x => x.Date.HasValue && x.Date.Value <= new DateTime(period.Year, 4, 29)) .OrderByDescending(x => x.Order).FirstOrDefault() != null ? p.ProfileSalary - .Where(x => x.Date != null).Where(x => x.Date.Value <= new DateTime(period.Year, 4, 29)) - .OrderByDescending(x => x.Order).FirstOrDefault().Amount : + .Where(x => x.Date != null).Where(x => x.Date.HasValue && x.Date.Value <= new DateTime(period.Year, 4, 29)) + .OrderByDescending(x => x.Order).FirstOrDefault()?.Amount : p.Amount, ProfileInsignia = p.ProfileInsignia, PositionSalaryAmount = p.PositionSalaryAmount, @@ -1426,8 +1426,9 @@ namespace BMA.EHR.Application.Repositories && p.ProfileInsignia != null && p.ProfileInsignia.Count > 0 && (p.ProfileInsignia.Where(x => - x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id && - x.InsigniaId.Value == GetInsigniaByName("เบญจมาภรณ์ช้างเผือก").Id) + x.InsigniaId.HasValue && + x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id && + x.InsigniaId.Value == GetInsigniaByName("เบญจมาภรณ์ช้างเผือก")?.Id) .ToList() .Count() == 0) select new @@ -1446,24 +1447,24 @@ namespace BMA.EHR.Application.Repositories ProfileDateAppoint = p.DateAppoint.Value, LastInsignia = p.ProfileInsignia == null || p.ProfileInsignia.Count == 0 ? "" : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .FirstOrDefault() == null ? "" : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) - .OrderByDescending(x => x.Year).FirstOrDefault().Insignia, + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) + .OrderByDescending(x => x.Year).FirstOrDefault()?.Insignia, LastInsigniaId = p.ProfileInsignia == null || p.ProfileInsignia.Count == 0 ? Guid.Empty : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .FirstOrDefault() == null ? Guid.Empty : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) - .OrderByDescending(x => x.Year).FirstOrDefault().InsigniaId, + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) + .OrderByDescending(x => x.Year).FirstOrDefault()?.InsigniaId, Salary = p.Amount, SalaryCondition = p.ProfileSalary == null || p.ProfileSalary.Count == 0 ? 0 : - p.ProfileSalary.Where(x => x.Date != null).Where(x => x.Date.Value <= new DateTime(period.Year, 4, 29)) + p.ProfileSalary.Where(x => x.Date != null).Where(x => x.Date.HasValue && x.Date.Value <= new DateTime(period.Year, 4, 29)) .OrderByDescending(x => x.Order).FirstOrDefault() != null ? p.ProfileSalary - .Where(x => x.Date != null).Where(x => x.Date.Value <= new DateTime(period.Year, 4, 29)) - .OrderByDescending(x => x.Order).FirstOrDefault().Amount : + .Where(x => x.Date != null).Where(x => x.Date.HasValue && x.Date.Value <= new DateTime(period.Year, 4, 29)) + .OrderByDescending(x => x.Order).FirstOrDefault()?.Amount : p.Amount, PosNo = p.PosNo ?? "", Gender = p.Gender == null ? "" : p.Gender, @@ -1472,7 +1473,7 @@ namespace BMA.EHR.Application.Repositories PositionTypeId = p.PosTypeId, PositionTypeName = p.PosType, IsHigherLevel = IsHigherLevel(p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .OrderByDescending(x => x.Year) .FirstOrDefault(), "เบญจมาภรณ์ช้างเผือก"), @@ -1573,8 +1574,9 @@ namespace BMA.EHR.Application.Repositories && p.ProfileInsignia != null && p.ProfileInsignia.Count > 0 && (p.ProfileInsignia.Where(x => - x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id && - x.InsigniaId.Value == GetInsigniaByName("จัตุรถาภรณ์มงกุฎไทย").Id) + x.InsigniaId.HasValue && + x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id && + x.InsigniaId.Value == GetInsigniaByName("จัตุรถาภรณ์มงกุฎไทย")?.Id) .ToList() .Count() == 0) select new @@ -1593,24 +1595,24 @@ namespace BMA.EHR.Application.Repositories ProfileDateAppoint = p.DateAppoint.Value, LastInsignia = p.ProfileInsignia == null || p.ProfileInsignia.Count == 0 ? "" : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .FirstOrDefault() == null ? "" : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) - .OrderByDescending(x => x.Year).FirstOrDefault().Insignia, + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) + .OrderByDescending(x => x.Year).FirstOrDefault()?.Insignia, LastInsigniaId = p.ProfileInsignia == null || p.ProfileInsignia.Count == 0 ? Guid.Empty : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .FirstOrDefault() == null ? Guid.Empty : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) - .OrderByDescending(x => x.Year).FirstOrDefault().InsigniaId, + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) + .OrderByDescending(x => x.Year).FirstOrDefault()?.InsigniaId, Salary = p.Amount, SalaryCondition = p.ProfileSalary == null || p.ProfileSalary.Count == 0 ? 0 : - p.ProfileSalary.Where(x => x.Date != null).Where(x => x.Date.Value <= new DateTime(period.Year, 4, 29)) + p.ProfileSalary.Where(x => x.Date != null).Where(x => x.Date.HasValue && x.Date.Value <= new DateTime(period.Year, 4, 29)) .OrderByDescending(x => x.Order).FirstOrDefault() != null ? p.ProfileSalary - .Where(x => x.Date != null).Where(x => x.Date.Value <= new DateTime(period.Year, 4, 29)) - .OrderByDescending(x => x.Order).FirstOrDefault().Amount : + .Where(x => x.Date != null).Where(x => x.Date.HasValue && x.Date.Value <= new DateTime(period.Year, 4, 29)) + .OrderByDescending(x => x.Order).FirstOrDefault()?.Amount : p.Amount, PosNo = p.PosNo ?? "", Gender = p.Gender == null ? "" : p.Gender, @@ -1620,12 +1622,12 @@ namespace BMA.EHR.Application.Repositories PositionTypeName = p.PosType, IsHigherLevel = p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .FirstOrDefault() == null ? true : IsHigherLevel(p.ProfileInsignia - .Where(x => x.InsigniaId.Value != - GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != + GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .OrderByDescending(x => x.Year) .FirstOrDefault(), "จัตุรถาภรณ์มงกุฎไทย"), @@ -1719,8 +1721,9 @@ namespace BMA.EHR.Application.Repositories && p.ProfileInsignia != null && p.ProfileInsignia.Count > 0 && (p.ProfileInsignia.Where(x => - x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id && - x.InsigniaId.Value == GetInsigniaByName("จัตุรถาภรณ์ช้างเผือก").Id) + x.InsigniaId.HasValue && + x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id && + x.InsigniaId.Value == GetInsigniaByName("จัตุรถาภรณ์ช้างเผือก")?.Id) .ToList() .Count() == 0) select new @@ -1739,24 +1742,24 @@ namespace BMA.EHR.Application.Repositories ProfileDateAppoint = p.DateAppoint.Value, LastInsignia = p.ProfileInsignia == null || p.ProfileInsignia.Count == 0 ? "" : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .FirstOrDefault() == null ? "" : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) - .OrderByDescending(x => x.Year).FirstOrDefault().Insignia, + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) + .OrderByDescending(x => x.Year).FirstOrDefault()?.Insignia, LastInsigniaId = p.ProfileInsignia == null || p.ProfileInsignia.Count == 0 ? Guid.Empty : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .FirstOrDefault() == null ? Guid.Empty : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) - .OrderByDescending(x => x.Year).FirstOrDefault().InsigniaId, + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) + .OrderByDescending(x => x.Year).FirstOrDefault()?.InsigniaId, Salary = p.Amount, SalaryCondition = p.ProfileSalary == null || p.ProfileSalary.Count == 0 ? 0 : - p.ProfileSalary.Where(x => x.Date != null).Where(x => x.Date.Value <= new DateTime(period.Year, 4, 29)) + p.ProfileSalary.Where(x => x.Date != null).Where(x => x.Date.HasValue && x.Date.Value <= new DateTime(period.Year, 4, 29)) .OrderByDescending(x => x.Order).FirstOrDefault() != null ? p.ProfileSalary - .Where(x => x.Date != null).Where(x => x.Date.Value <= new DateTime(period.Year, 4, 29)) - .OrderByDescending(x => x.Order).FirstOrDefault().Amount : + .Where(x => x.Date != null).Where(x => x.Date.HasValue && x.Date.Value <= new DateTime(period.Year, 4, 29)) + .OrderByDescending(x => x.Order).FirstOrDefault()?.Amount : p.Amount, PosNo = p.PosNo ?? "", Gender = p.Gender == null ? "" : p.Gender, @@ -1766,12 +1769,12 @@ namespace BMA.EHR.Application.Repositories PositionTypeName = p.PosType, IsHigherLevel = p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .FirstOrDefault() == null ? true : IsHigherLevel(p.ProfileInsignia - .Where(x => x.InsigniaId.Value != - GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != + GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .OrderByDescending(x => x.Year) .FirstOrDefault(), "จัตุรถาภรณ์ช้างเผือก"), @@ -1972,8 +1975,9 @@ namespace BMA.EHR.Application.Repositories && p.ProfileInsignia != null && p.ProfileInsignia.Count > 0 && (p.ProfileInsignia.Where(x => - x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id && - x.InsigniaId.Value == GetInsigniaByName("ตริตาภรณ์มงกุฎไทย").Id) + x.InsigniaId.HasValue && + x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id && + x.InsigniaId.Value == GetInsigniaByName("ตริตาภรณ์มงกุฎไทย")?.Id) .ToList() .Count() == 0) select new @@ -1992,24 +1996,24 @@ namespace BMA.EHR.Application.Repositories ProfileDateAppoint = p.DateAppoint.Value, LastInsignia = p.ProfileInsignia == null || p.ProfileInsignia.Count == 0 ? "" : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .FirstOrDefault() == null ? "" : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) - .OrderByDescending(x => x.Year).FirstOrDefault().Insignia, + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) + .OrderByDescending(x => x.Year).FirstOrDefault()?.Insignia, LastInsigniaId = p.ProfileInsignia == null || p.ProfileInsignia.Count == 0 ? Guid.Empty : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .FirstOrDefault() == null ? Guid.Empty : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) - .OrderByDescending(x => x.Year).FirstOrDefault().InsigniaId, + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) + .OrderByDescending(x => x.Year).FirstOrDefault()?.InsigniaId, Salary = p.Amount, SalaryCondition = p.ProfileSalary == null || p.ProfileSalary.Count == 0 ? 0 : - p.ProfileSalary.Where(x => x.Date != null).Where(x => x.Date.Value <= new DateTime(period.Year, 4, 29)) + p.ProfileSalary.Where(x => x.Date != null).Where(x => x.Date.HasValue && x.Date.Value <= new DateTime(period.Year, 4, 29)) .OrderByDescending(x => x.Order).FirstOrDefault() != null ? p.ProfileSalary - .Where(x => x.Date != null).Where(x => x.Date.Value <= new DateTime(period.Year, 4, 29)) - .OrderByDescending(x => x.Order).FirstOrDefault().Amount : + .Where(x => x.Date != null).Where(x => x.Date.HasValue && x.Date.Value <= new DateTime(period.Year, 4, 29)) + .OrderByDescending(x => x.Order).FirstOrDefault()?.Amount : p.Amount, PosNo = p.PosNo, Gender = p.Gender == null ? null : p.Gender, @@ -2019,12 +2023,12 @@ namespace BMA.EHR.Application.Repositories PositionTypeName = p.PosType, IsHigherLevel = p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .FirstOrDefault() == null ? true : IsHigherLevel(p.ProfileInsignia - .Where(x => x.InsigniaId.Value != - GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != + GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .OrderByDescending(x => x.Year) .FirstOrDefault(), "ตริตาภรณ์มงกุฎไทย"), @@ -2118,8 +2122,9 @@ namespace BMA.EHR.Application.Repositories && p.ProfileInsignia != null && p.ProfileInsignia.Count > 0 && (p.ProfileInsignia.Where(x => - x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id && - x.InsigniaId.Value == GetInsigniaByName("ตริตาภรณ์ช้างเผือก").Id) + x.InsigniaId.HasValue && + x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id && + x.InsigniaId.Value == GetInsigniaByName("ตริตาภรณ์ช้างเผือก")?.Id) .ToList() .Count() == 0) select new @@ -2138,24 +2143,24 @@ namespace BMA.EHR.Application.Repositories ProfileDateAppoint = p.DateAppoint.Value, LastInsignia = p.ProfileInsignia == null || p.ProfileInsignia.Count == 0 ? "" : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .FirstOrDefault() == null ? "" : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) - .OrderByDescending(x => x.Year).FirstOrDefault().Insignia, + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) + .OrderByDescending(x => x.Year).FirstOrDefault()?.Insignia, LastInsigniaId = p.ProfileInsignia == null || p.ProfileInsignia.Count == 0 ? Guid.Empty : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .FirstOrDefault() == null ? Guid.Empty : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) - .OrderByDescending(x => x.Year).FirstOrDefault().InsigniaId, + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) + .OrderByDescending(x => x.Year).FirstOrDefault()?.InsigniaId, Salary = p.Amount, SalaryCondition = p.ProfileSalary == null || p.ProfileSalary.Count == 0 ? 0 : - p.ProfileSalary.Where(x => x.Date != null).Where(x => x.Date.Value <= new DateTime(period.Year, 4, 29)) + p.ProfileSalary.Where(x => x.Date != null).Where(x => x.Date.HasValue && x.Date.Value <= new DateTime(period.Year, 4, 29)) .OrderByDescending(x => x.Order).FirstOrDefault() != null ? p.ProfileSalary - .Where(x => x.Date != null).Where(x => x.Date.Value <= new DateTime(period.Year, 4, 29)) - .OrderByDescending(x => x.Order).FirstOrDefault().Amount : + .Where(x => x.Date != null).Where(x => x.Date.HasValue && x.Date.Value <= new DateTime(period.Year, 4, 29)) + .OrderByDescending(x => x.Order).FirstOrDefault()?.Amount : p.Amount, PosNo = p.PosNo, Gender = p.Gender == null ? null : p.Gender, @@ -2165,12 +2170,12 @@ namespace BMA.EHR.Application.Repositories PositionTypeName = p.PosType, IsHigherLevel = p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .FirstOrDefault() == null ? true : IsHigherLevel(p.ProfileInsignia - .Where(x => x.InsigniaId.Value != - GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != + GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .OrderByDescending(x => x.Year) .FirstOrDefault(), "ตริตาภรณ์ช้างเผือก"), @@ -2180,7 +2185,7 @@ namespace BMA.EHR.Application.Repositories p.ProfileSalary.Where(x => x.PositionLevel == "ชำนาญงาน").OrderBy(p => p.Date) .FirstOrDefault() == null ? null : p.ProfileSalary.Where(x => x.PositionLevel == "ชำนาญงาน").OrderBy(p => p.Date) - .FirstOrDefault().Date, + .FirstOrDefault()?.Date, PositionSalaryAmount = p.PositionSalaryAmount, Amount = p.Amount, RootId = p.RootId, @@ -2336,8 +2341,9 @@ namespace BMA.EHR.Application.Repositories && p.ProfileInsignia != null && p.ProfileInsignia.Count > 0 && (p.ProfileInsignia.Where(x => - x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id && - x.InsigniaId.Value == GetInsigniaByName("ทวีติยาภรณ์มงกุฎไทย").Id) + x.InsigniaId.HasValue && + x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id && + x.InsigniaId.Value == GetInsigniaByName("ทวีติยาภรณ์มงกุฎไทย")?.Id) .ToList() .Count() == 0) select new @@ -2356,24 +2362,24 @@ namespace BMA.EHR.Application.Repositories ProfileDateAppoint = p.DateAppoint.Value, LastInsignia = p.ProfileInsignia == null || p.ProfileInsignia.Count == 0 ? "" : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .FirstOrDefault() == null ? "" : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) - .OrderByDescending(x => x.Year).FirstOrDefault().Insignia, + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) + .OrderByDescending(x => x.Year).FirstOrDefault()?.Insignia, LastInsigniaId = p.ProfileInsignia == null || p.ProfileInsignia.Count == 0 ? Guid.Empty : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .FirstOrDefault() == null ? Guid.Empty : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) - .OrderByDescending(x => x.Year).FirstOrDefault().InsigniaId, + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) + .OrderByDescending(x => x.Year).FirstOrDefault()?.InsigniaId, Salary = p.Amount, SalaryCondition = p.ProfileSalary == null || p.ProfileSalary.Count == 0 ? 0 : - p.ProfileSalary.Where(x => x.Date != null).Where(x => x.Date.Value <= new DateTime(period.Year, 4, 29)) + p.ProfileSalary.Where(x => x.Date != null).Where(x => x.Date.HasValue && x.Date.Value <= new DateTime(period.Year, 4, 29)) .OrderByDescending(x => x.Order).FirstOrDefault() != null ? p.ProfileSalary - .Where(x => x.Date != null).Where(x => x.Date.Value <= new DateTime(period.Year, 4, 29)) - .OrderByDescending(x => x.Order).FirstOrDefault().Amount : + .Where(x => x.Date != null).Where(x => x.Date.HasValue && x.Date.Value <= new DateTime(period.Year, 4, 29)) + .OrderByDescending(x => x.Order).FirstOrDefault()?.Amount : p.Amount, PosNo = p.PosNo, Gender = p.Gender == null ? null : p.Gender, @@ -2383,12 +2389,12 @@ namespace BMA.EHR.Application.Repositories PositionTypeName = p.PosType, IsHigherLevel = p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .FirstOrDefault() == null ? true : IsHigherLevel(p.ProfileInsignia - .Where(x => x.InsigniaId.Value != - GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != + GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .OrderByDescending(x => x.Year) .FirstOrDefault(), "ทวีติยาภรณ์มงกุฎไทย"), @@ -2398,7 +2404,7 @@ namespace BMA.EHR.Application.Repositories p.ProfileSalary.Where(x => x.PositionLevel == "อาวุโส").OrderBy(p => p.Date) .FirstOrDefault() == null ? null : p.ProfileSalary.Where(x => x.PositionLevel == "อาวุโส").OrderBy(p => p.Date) - .FirstOrDefault().Date, + .FirstOrDefault()?.Date, PositionSalaryAmount = p.PositionSalaryAmount, Amount = p.Amount, RootId = p.RootId, @@ -2487,8 +2493,9 @@ namespace BMA.EHR.Application.Repositories && p.ProfileInsignia != null && p.ProfileInsignia.Count > 0 && (p.ProfileInsignia.Where(x => - x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id && - x.InsigniaId.Value == GetInsigniaByName("ทวีติยาภรณ์ช้างเผือก").Id) + x.InsigniaId.HasValue && + x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id && + x.InsigniaId.Value == GetInsigniaByName("ทวีติยาภรณ์ช้างเผือก")?.Id) .ToList() .Count() == 0) select new @@ -2507,24 +2514,24 @@ namespace BMA.EHR.Application.Repositories ProfileDateAppoint = p.DateAppoint.Value, LastInsignia = p.ProfileInsignia == null || p.ProfileInsignia.Count == 0 ? "" : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .FirstOrDefault() == null ? "" : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) - .OrderByDescending(x => x.Year).FirstOrDefault().Insignia, + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) + .OrderByDescending(x => x.Year).FirstOrDefault()?.Insignia, LastInsigniaId = p.ProfileInsignia == null || p.ProfileInsignia.Count == 0 ? Guid.Empty : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .FirstOrDefault() == null ? Guid.Empty : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) - .OrderByDescending(x => x.Year).FirstOrDefault().InsigniaId, + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) + .OrderByDescending(x => x.Year).FirstOrDefault()?.InsigniaId, Salary = p.Amount, SalaryCondition = p.ProfileSalary == null || p.ProfileSalary.Count == 0 ? 0 : - p.ProfileSalary.Where(x => x.Date != null).Where(x => x.Date.Value <= new DateTime(period.Year, 4, 29)) + p.ProfileSalary.Where(x => x.Date != null).Where(x => x.Date.HasValue && x.Date.Value <= new DateTime(period.Year, 4, 29)) .OrderByDescending(x => x.Order).FirstOrDefault() != null ? p.ProfileSalary - .Where(x => x.Date != null).Where(x => x.Date.Value <= new DateTime(period.Year, 4, 29)) - .OrderByDescending(x => x.Order).FirstOrDefault().Amount : + .Where(x => x.Date != null).Where(x => x.Date.HasValue && x.Date.Value <= new DateTime(period.Year, 4, 29)) + .OrderByDescending(x => x.Order).FirstOrDefault()?.Amount : p.Amount, PosNo = p.PosNo, Gender = p.Gender == null ? null : p.Gender, @@ -2534,12 +2541,12 @@ namespace BMA.EHR.Application.Repositories PositionTypeName = p.PosType, IsHigherLevel = p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .FirstOrDefault() == null ? true : IsHigherLevel(p.ProfileInsignia - .Where(x => x.InsigniaId.Value != - GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != + GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .OrderByDescending(x => x.Year) .FirstOrDefault(), "ทวีติยาภรณ์ช้างเผือก"), @@ -2549,7 +2556,7 @@ namespace BMA.EHR.Application.Repositories p.ProfileSalary.Where(x => x.PositionLevel == "อาวุโส").OrderBy(p => p.Date) .FirstOrDefault() == null ? null : p.ProfileSalary.Where(x => x.PositionLevel == "อาวุโส").OrderBy(p => p.Date) - .FirstOrDefault().Date, + .FirstOrDefault()?.Date, PositionSalaryAmount = p.PositionSalaryAmount, Amount = p.Amount, RootId = p.RootId, @@ -2706,8 +2713,9 @@ namespace BMA.EHR.Application.Repositories && p.ProfileInsignia != null && p.ProfileInsignia.Count > 0 && (p.ProfileInsignia.Where(x => - x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id && - x.InsigniaId.Value == GetInsigniaByName("ทวีติยาภรณ์ช้างเผือก").Id) + x.InsigniaId.HasValue && + x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id && + x.InsigniaId.Value == GetInsigniaByName("ทวีติยาภรณ์ช้างเผือก")?.Id) .ToList() .Count() == 0) select new @@ -2726,24 +2734,24 @@ namespace BMA.EHR.Application.Repositories ProfileDateAppoint = p.DateAppoint.Value, LastInsignia = p.ProfileInsignia == null || p.ProfileInsignia.Count == 0 ? "" : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .FirstOrDefault() == null ? "" : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) - .OrderByDescending(x => x.Year).FirstOrDefault().Insignia, + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) + .OrderByDescending(x => x.Year).FirstOrDefault()?.Insignia, LastInsigniaId = p.ProfileInsignia == null || p.ProfileInsignia.Count == 0 ? Guid.Empty : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .FirstOrDefault() == null ? Guid.Empty : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) - .OrderByDescending(x => x.Year).FirstOrDefault().InsigniaId, + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) + .OrderByDescending(x => x.Year).FirstOrDefault()?.InsigniaId, Salary = p.Amount, SalaryCondition = p.ProfileSalary == null || p.ProfileSalary.Count == 0 ? 0 : - p.ProfileSalary.Where(x => x.Date != null).Where(x => x.Date.Value <= new DateTime(period.Year, 4, 29)) + p.ProfileSalary.Where(x => x.Date != null).Where(x => x.Date.HasValue && x.Date.Value <= new DateTime(period.Year, 4, 29)) .OrderByDescending(x => x.Order).FirstOrDefault() != null ? p.ProfileSalary - .Where(x => x.Date != null).Where(x => x.Date.Value <= new DateTime(period.Year, 4, 29)) - .OrderByDescending(x => x.Order).FirstOrDefault().Amount : + .Where(x => x.Date != null).Where(x => x.Date.HasValue && x.Date.Value <= new DateTime(period.Year, 4, 29)) + .OrderByDescending(x => x.Order).FirstOrDefault()?.Amount : p.Amount, PosNo = p.PosNo, Gender = p.Gender == null ? null : p.Gender, @@ -2753,12 +2761,12 @@ namespace BMA.EHR.Application.Repositories PositionTypeName = p.PosType, IsHigherLevel = p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .FirstOrDefault() == null ? true : IsHigherLevel(p.ProfileInsignia - .Where(x => x.InsigniaId.Value != - GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != + GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .OrderByDescending(x => x.Year) .FirstOrDefault(), "ทวีติยาภรณ์ช้างเผือก"), @@ -2768,7 +2776,7 @@ namespace BMA.EHR.Application.Repositories p.ProfileSalary.Where(x => x.PositionLevel == "อาวุโส").OrderBy(p => p.Date) .FirstOrDefault() == null ? null : p.ProfileSalary.Where(x => x.PositionLevel == "อาวุโส").OrderBy(p => p.Date) - .FirstOrDefault().Date, + .FirstOrDefault()?.Date, PositionSalaryAmount = p.PositionSalaryAmount, Amount = p.Amount, RootId = p.RootId, @@ -2858,8 +2866,9 @@ namespace BMA.EHR.Application.Repositories && p.ProfileInsignia != null && p.ProfileInsignia.Count > 0 && (p.ProfileInsignia.Where(x => - x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id && - x.InsigniaId.Value == GetInsigniaByName("ประถมาภรณ์มงกุฎไทย").Id) + x.InsigniaId.HasValue && + x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id && + x.InsigniaId.Value == GetInsigniaByName("ประถมาภรณ์มงกุฎไทย")?.Id) .ToList() .Count() == 0) select new @@ -2878,24 +2887,24 @@ namespace BMA.EHR.Application.Repositories ProfileDateAppoint = p.DateAppoint.Value, LastInsignia = p.ProfileInsignia == null || p.ProfileInsignia.Count == 0 ? "" : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .FirstOrDefault() == null ? "" : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) - .OrderByDescending(x => x.Year).FirstOrDefault().Insignia, + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) + .OrderByDescending(x => x.Year).FirstOrDefault()?.Insignia, LastInsigniaId = p.ProfileInsignia == null || p.ProfileInsignia.Count == 0 ? Guid.Empty : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .FirstOrDefault() == null ? Guid.Empty : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) - .OrderByDescending(x => x.Year).FirstOrDefault().InsigniaId, + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) + .OrderByDescending(x => x.Year).FirstOrDefault()?.InsigniaId, Salary = p.Amount, SalaryCondition = p.ProfileSalary == null || p.ProfileSalary.Count == 0 ? 0 : - p.ProfileSalary.Where(x => x.Date != null).Where(x => x.Date.Value <= new DateTime(period.Year, 4, 29)) + p.ProfileSalary.Where(x => x.Date != null).Where(x => x.Date.HasValue && x.Date.Value <= new DateTime(period.Year, 4, 29)) .OrderByDescending(x => x.Order).FirstOrDefault() != null ? p.ProfileSalary - .Where(x => x.Date != null).Where(x => x.Date.Value <= new DateTime(period.Year, 4, 29)) - .OrderByDescending(x => x.Order).FirstOrDefault().Amount : + .Where(x => x.Date != null).Where(x => x.Date.HasValue && x.Date.Value <= new DateTime(period.Year, 4, 29)) + .OrderByDescending(x => x.Order).FirstOrDefault()?.Amount : p.Amount, PosNo = p.PosNo, Gender = p.Gender == null ? null : p.Gender, @@ -2905,12 +2914,12 @@ namespace BMA.EHR.Application.Repositories PositionTypeName = p.PosType, IsHigherLevel = p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .FirstOrDefault() == null ? true : IsHigherLevel(p.ProfileInsignia - .Where(x => x.InsigniaId.Value != - GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != + GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .OrderByDescending(x => x.Year) .FirstOrDefault(), "ประถมาภรณ์มงกุฎไทย"), @@ -2920,14 +2929,14 @@ namespace BMA.EHR.Application.Repositories p.ProfileSalary.Where(x => x.PositionLevel == "อาวุโส").OrderBy(p => p.Date) .FirstOrDefault() == null ? null : p.ProfileSalary.Where(x => x.PositionLevel == "อาวุโส").OrderBy(p => p.Date) - .FirstOrDefault().Date, + .FirstOrDefault()?.Date, FirstRecvInsigniaYear = p.ProfileInsignia.Count == 0 ? 0 : p.ProfileInsignia - .Where(x => x.InsigniaId.Value == GetInsigniaByName("ทวีติยาภรณ์ช้างเผือก").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value == GetInsigniaByName("ทวีติยาภรณ์ช้างเผือก")?.Id) .OrderBy(x => x.Year).FirstOrDefault() == null ? 0 : p.ProfileInsignia - .Where(x => x.InsigniaId.Value == GetInsigniaByName("ทวีติยาภรณ์ช้างเผือก").Id) - .OrderBy(x => x.Year).FirstOrDefault().Year, + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value == GetInsigniaByName("ทวีติยาภรณ์ช้างเผือก")?.Id) + .OrderBy(x => x.Year).FirstOrDefault()?.Year, PositionSalaryAmount = p.PositionSalaryAmount, Amount = p.Amount, RootId = p.RootId, @@ -3019,8 +3028,9 @@ namespace BMA.EHR.Application.Repositories && p.ProfileInsignia != null && p.ProfileInsignia.Count > 0 && (p.ProfileInsignia.Where(x => - x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id && - x.InsigniaId.Value == GetInsigniaByName("ประถมาภรณ์ช้างเผือก").Id) + x.InsigniaId.HasValue && + x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id && + x.InsigniaId.Value == GetInsigniaByName("ประถมาภรณ์ช้างเผือก")?.Id) .ToList() .Count() == 0) select new @@ -3039,24 +3049,24 @@ namespace BMA.EHR.Application.Repositories ProfileDateAppoint = p.DateAppoint.Value, LastInsignia = p.ProfileInsignia == null || p.ProfileInsignia.Count == 0 ? "" : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .FirstOrDefault() == null ? "" : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) - .OrderByDescending(x => x.Year).FirstOrDefault().Insignia, + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) + .OrderByDescending(x => x.Year).FirstOrDefault()?.Insignia, LastInsigniaId = p.ProfileInsignia == null || p.ProfileInsignia.Count == 0 ? Guid.Empty : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .FirstOrDefault() == null ? Guid.Empty : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) - .OrderByDescending(x => x.Year).FirstOrDefault().InsigniaId, + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) + .OrderByDescending(x => x.Year).FirstOrDefault()?.InsigniaId, Salary = p.Amount, SalaryCondition = p.ProfileSalary == null || p.ProfileSalary.Count == 0 ? 0 : - p.ProfileSalary.Where(x => x.Date != null).Where(x => x.Date.Value <= new DateTime(period.Year, 4, 29)) + p.ProfileSalary.Where(x => x.Date != null).Where(x => x.Date.HasValue && x.Date.Value <= new DateTime(period.Year, 4, 29)) .OrderByDescending(x => x.Order).FirstOrDefault() != null ? p.ProfileSalary - .Where(x => x.Date != null).Where(x => x.Date.Value <= new DateTime(period.Year, 4, 29)) - .OrderByDescending(x => x.Order).FirstOrDefault().Amount : + .Where(x => x.Date != null).Where(x => x.Date.HasValue && x.Date.Value <= new DateTime(period.Year, 4, 29)) + .OrderByDescending(x => x.Order).FirstOrDefault()?.Amount : p.Amount, PosNo = p.PosNo, Gender = p.Gender == null ? null : p.Gender, @@ -3066,12 +3076,12 @@ namespace BMA.EHR.Application.Repositories PositionTypeName = p.PosType, IsHigherLevel = p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .FirstOrDefault() == null ? true : IsHigherLevel(p.ProfileInsignia - .Where(x => x.InsigniaId.Value != - GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != + GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .OrderByDescending(x => x.Year) .FirstOrDefault(), "ประถมาภรณ์ช้างเผือก"), @@ -3081,14 +3091,14 @@ namespace BMA.EHR.Application.Repositories p.ProfileSalary.Where(x => x.PositionLevel == "อาวุโส").OrderBy(p => p.Date) .FirstOrDefault() == null ? null : p.ProfileSalary.Where(x => x.PositionLevel == "อาวุโส").OrderBy(p => p.Date) - .FirstOrDefault().Date, + .FirstOrDefault()?.Date, FirstRecvInsigniaYear = p.ProfileInsignia.Count == 0 ? 0 : p.ProfileInsignia - .Where(x => x.InsigniaId.Value == GetInsigniaByName("ประถมาภรณ์มงกุฎไทย").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value == GetInsigniaByName("ประถมาภรณ์มงกุฎไทย")?.Id) .OrderBy(x => x.Year).FirstOrDefault() == null ? 0 : p.ProfileInsignia - .Where(x => x.InsigniaId.Value == GetInsigniaByName("ประถมาภรณ์มงกุฎไทย").Id) - .OrderBy(x => x.Year).FirstOrDefault().Year, + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value == GetInsigniaByName("ประถมาภรณ์มงกุฎไทย")?.Id) + .OrderBy(x => x.Year).FirstOrDefault()?.Year, PositionSalaryAmount = p.PositionSalaryAmount, Amount = p.Amount, RootId = p.RootId, @@ -3265,8 +3275,9 @@ namespace BMA.EHR.Application.Repositories where p.PosType == "วิชาการ" && p.ProfileInsignia.Count > 0 && (p.ProfileInsignia.Where(x => - x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id && - x.InsigniaId.Value == GetInsigniaByName("ตริตาภรณ์มงกุฎไทย").Id) + x.InsigniaId.HasValue && + x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id && + x.InsigniaId.Value == GetInsigniaByName("ตริตาภรณ์มงกุฎไทย")?.Id) .ToList() .Count() == 0) select new @@ -3285,24 +3296,24 @@ namespace BMA.EHR.Application.Repositories ProfileDateAppoint = p.DateAppoint.Value, LastInsignia = p.ProfileInsignia == null || p.ProfileInsignia.Count == 0 ? "" : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .FirstOrDefault() == null ? "" : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) - .OrderByDescending(x => x.Year).FirstOrDefault().Insignia, + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) + .OrderByDescending(x => x.Year).FirstOrDefault()?.Insignia, LastInsigniaId = p.ProfileInsignia == null || p.ProfileInsignia.Count == 0 ? Guid.Empty : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .FirstOrDefault() == null ? Guid.Empty : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) - .OrderByDescending(x => x.Year).FirstOrDefault().InsigniaId, + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) + .OrderByDescending(x => x.Year).FirstOrDefault()?.InsigniaId, Salary = p.Amount, SalaryCondition = p.ProfileSalary == null || p.ProfileSalary.Count == 0 ? 0 : - p.ProfileSalary.Where(x => x.Date != null).Where(x => x.Date.Value <= new DateTime(period.Year, 4, 29)) + p.ProfileSalary.Where(x => x.Date != null).Where(x => x.Date.HasValue && x.Date.Value <= new DateTime(period.Year, 4, 29)) .OrderByDescending(x => x.Order).FirstOrDefault() != null ? p.ProfileSalary - .Where(x => x.Date != null).Where(x => x.Date.Value <= new DateTime(period.Year, 4, 29)) - .OrderByDescending(x => x.Order).FirstOrDefault().Amount : + .Where(x => x.Date != null).Where(x => x.Date.HasValue && x.Date.Value <= new DateTime(period.Year, 4, 29)) + .OrderByDescending(x => x.Order).FirstOrDefault()?.Amount : p.Amount, PosNo = p.PosNo, Gender = p.Gender == null ? null : p.Gender, @@ -3312,12 +3323,12 @@ namespace BMA.EHR.Application.Repositories PositionTypeName = p.PosType, IsHigherLevel = p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .FirstOrDefault() == null ? true : IsHigherLevel(p.ProfileInsignia - .Where(x => x.InsigniaId.Value != - GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != + GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .OrderByDescending(x => x.Year) .FirstOrDefault(), "ตริตาภรณ์มงกุฎไทย"), @@ -3327,14 +3338,14 @@ namespace BMA.EHR.Application.Repositories p.ProfileSalary.Where(x => x.PositionLevel == "อาวุโส").OrderBy(p => p.Date) .FirstOrDefault() == null ? null : p.ProfileSalary.Where(x => x.PositionLevel == "อาวุโส").OrderBy(p => p.Date) - .FirstOrDefault().Date, + .FirstOrDefault()?.Date, FirstRecvInsigniaYear = p.ProfileInsignia.Count == 0 ? 0 : p.ProfileInsignia - .Where(x => x.InsigniaId.Value == GetInsigniaByName("ประถมาภรณ์มงกุฎไทย").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value == GetInsigniaByName("ประถมาภรณ์มงกุฎไทย")?.Id) .OrderBy(x => x.Year).FirstOrDefault() == null ? 0 : p.ProfileInsignia - .Where(x => x.InsigniaId.Value == GetInsigniaByName("ประถมาภรณ์มงกุฎไทย").Id) - .OrderBy(x => x.Year).FirstOrDefault().Year, + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value == GetInsigniaByName("ประถมาภรณ์มงกุฎไทย")?.Id) + .OrderBy(x => x.Year).FirstOrDefault()?.Year, PositionSalaryAmount = p.PositionSalaryAmount, Amount = p.Amount, RootId = p.RootId, @@ -3470,8 +3481,9 @@ namespace BMA.EHR.Application.Repositories && p.ProfileInsignia != null && p.ProfileInsignia.Count > 0 && (p.ProfileInsignia.Where(x => - x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id && - x.InsigniaId.Value == GetInsigniaByName("ตริตาภรณ์ช้างเผือก").Id) + x.InsigniaId.HasValue && + x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id && + x.InsigniaId.Value == GetInsigniaByName("ตริตาภรณ์ช้างเผือก")?.Id) .ToList() .Count() == 0) select new @@ -3490,24 +3502,24 @@ namespace BMA.EHR.Application.Repositories ProfileDateAppoint = p.DateAppoint.Value, LastInsignia = p.ProfileInsignia == null || p.ProfileInsignia.Count == 0 ? "" : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .FirstOrDefault() == null ? "" : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) - .OrderByDescending(x => x.Year).FirstOrDefault().Insignia, + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) + .OrderByDescending(x => x.Year).FirstOrDefault()?.Insignia, LastInsigniaId = p.ProfileInsignia == null || p.ProfileInsignia.Count == 0 ? Guid.Empty : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .FirstOrDefault() == null ? Guid.Empty : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) - .OrderByDescending(x => x.Year).FirstOrDefault().InsigniaId, + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) + .OrderByDescending(x => x.Year).FirstOrDefault()?.InsigniaId, Salary = p.Amount, SalaryCondition = p.ProfileSalary == null || p.ProfileSalary.Count == 0 ? 0 : - p.ProfileSalary.Where(x => x.Date != null).Where(x => x.Date.Value <= new DateTime(period.Year, 4, 29)) + p.ProfileSalary.Where(x => x.Date != null).Where(x => x.Date.HasValue && x.Date.Value <= new DateTime(period.Year, 4, 29)) .OrderByDescending(x => x.Order).FirstOrDefault() != null ? p.ProfileSalary - .Where(x => x.Date != null).Where(x => x.Date.Value <= new DateTime(period.Year, 4, 29)) - .OrderByDescending(x => x.Order).FirstOrDefault().Amount : + .Where(x => x.Date != null).Where(x => x.Date.HasValue && x.Date.Value <= new DateTime(period.Year, 4, 29)) + .OrderByDescending(x => x.Order).FirstOrDefault()?.Amount : p.Amount, PosNo = p.PosNo, Gender = p.Gender == null ? null : p.Gender, @@ -3517,12 +3529,12 @@ namespace BMA.EHR.Application.Repositories PositionTypeName = p.PosType, IsHigherLevel = p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .FirstOrDefault() == null ? true : IsHigherLevel(p.ProfileInsignia - .Where(x => x.InsigniaId.Value != - GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != + GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .OrderByDescending(x => x.Year) .FirstOrDefault(), "ตริตาภรณ์ช้างเผือก"), @@ -3532,14 +3544,14 @@ namespace BMA.EHR.Application.Repositories p.ProfileSalary.Where(x => x.PositionLevel == "ชำนาญการ").OrderBy(p => p.Date) .FirstOrDefault() == null ? null : p.ProfileSalary.Where(x => x.PositionLevel == "ชำนาญการ").OrderBy(p => p.Date) - .FirstOrDefault().Date, + .FirstOrDefault()?.Date, FirstRecvInsigniaYear = p.ProfileInsignia.Count == 0 ? 0 : p.ProfileInsignia - .Where(x => x.InsigniaId.Value == GetInsigniaByName("ประถมาภรณ์มงกุฎไทย").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value == GetInsigniaByName("ประถมาภรณ์มงกุฎไทย")?.Id) .OrderBy(x => x.Year).FirstOrDefault() == null ? 0 : p.ProfileInsignia - .Where(x => x.InsigniaId.Value == GetInsigniaByName("ประถมาภรณ์มงกุฎไทย").Id) - .OrderBy(x => x.Year).FirstOrDefault().Year, + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value == GetInsigniaByName("ประถมาภรณ์มงกุฎไทย")?.Id) + .OrderBy(x => x.Year).FirstOrDefault()?.Year, PositionSalaryAmount = p.PositionSalaryAmount, Amount = p.Amount, RootId = p.RootId, @@ -3628,8 +3640,9 @@ namespace BMA.EHR.Application.Repositories && p.ProfileInsignia != null && p.ProfileInsignia.Count > 0 && (p.ProfileInsignia.Where(x => - x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id && - x.InsigniaId.Value == GetInsigniaByName("ทวีติยาภรณ์มงกุฎไทย").Id) + x.InsigniaId.HasValue && + x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id && + x.InsigniaId.Value == GetInsigniaByName("ทวีติยาภรณ์มงกุฎไทย")?.Id) .ToList() .Count() == 0) select new @@ -3648,24 +3661,24 @@ namespace BMA.EHR.Application.Repositories ProfileDateAppoint = p.DateAppoint.Value, LastInsignia = p.ProfileInsignia == null || p.ProfileInsignia.Count == 0 ? "" : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .FirstOrDefault() == null ? "" : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) - .OrderByDescending(x => x.Year).FirstOrDefault().Insignia, + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) + .OrderByDescending(x => x.Year).FirstOrDefault()?.Insignia, LastInsigniaId = p.ProfileInsignia == null || p.ProfileInsignia.Count == 0 ? Guid.Empty : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .FirstOrDefault() == null ? Guid.Empty : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) - .OrderByDescending(x => x.Year).FirstOrDefault().InsigniaId, + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) + .OrderByDescending(x => x.Year).FirstOrDefault()?.InsigniaId, Salary = p.Amount, SalaryCondition = p.ProfileSalary == null || p.ProfileSalary.Count == 0 ? 0 : - p.ProfileSalary.Where(x => x.Date != null).Where(x => x.Date.Value <= new DateTime(period.Year, 4, 29)) + p.ProfileSalary.Where(x => x.Date != null).Where(x => x.Date.HasValue && x.Date.Value <= new DateTime(period.Year, 4, 29)) .OrderByDescending(x => x.Order).FirstOrDefault() != null ? p.ProfileSalary - .Where(x => x.Date != null).Where(x => x.Date.Value <= new DateTime(period.Year, 4, 29)) - .OrderByDescending(x => x.Order).FirstOrDefault().Amount : + .Where(x => x.Date != null).Where(x => x.Date.HasValue && x.Date.Value <= new DateTime(period.Year, 4, 29)) + .OrderByDescending(x => x.Order).FirstOrDefault()?.Amount : p.Amount, PosNo = p.PosNo, Gender = p.Gender == null ? null : p.Gender, @@ -3675,12 +3688,12 @@ namespace BMA.EHR.Application.Repositories PositionTypeName = p.PosType, IsHigherLevel = p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .FirstOrDefault() == null ? true : IsHigherLevel(p.ProfileInsignia - .Where(x => x.InsigniaId.Value != - GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != + GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .OrderByDescending(x => x.Year) .FirstOrDefault(), "ทวีติยาภรณ์มงกุฎไทย"), @@ -3690,14 +3703,14 @@ namespace BMA.EHR.Application.Repositories p.ProfileSalary.Where(x => x.PositionLevel == "ชำนาญการ").OrderBy(p => p.Date) .FirstOrDefault() == null ? null : p.ProfileSalary.Where(x => x.PositionLevel == "ชำนาญการ").OrderBy(p => p.Date) - .FirstOrDefault().Date, + .FirstOrDefault()?.Date, FirstRecvInsigniaYear = p.ProfileInsignia.Count == 0 ? 0 : p.ProfileInsignia - .Where(x => x.InsigniaId.Value == GetInsigniaByName("ประถมาภรณ์มงกุฎไทย").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value == GetInsigniaByName("ประถมาภรณ์มงกุฎไทย")?.Id) .OrderBy(x => x.Year).FirstOrDefault() == null ? 0 : p.ProfileInsignia - .Where(x => x.InsigniaId.Value == GetInsigniaByName("ประถมาภรณ์มงกุฎไทย").Id) - .OrderBy(x => x.Year).FirstOrDefault().Year, + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value == GetInsigniaByName("ประถมาภรณ์มงกุฎไทย")?.Id) + .OrderBy(x => x.Year).FirstOrDefault()?.Year, PositionSalaryAmount = p.PositionSalaryAmount, Amount = p.Amount, RootId = p.RootId, @@ -3789,8 +3802,9 @@ namespace BMA.EHR.Application.Repositories && p.ProfileInsignia != null && p.ProfileInsignia.Count > 0 && (p.ProfileInsignia.Where(x => - x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id && - x.InsigniaId.Value == GetInsigniaByName("ทวีติยาภรณ์ช้างเผือก").Id) + x.InsigniaId.HasValue && + x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id && + x.InsigniaId.Value == GetInsigniaByName("ทวีติยาภรณ์ช้างเผือก")?.Id) .ToList() .Count() == 0) select new @@ -3809,24 +3823,24 @@ namespace BMA.EHR.Application.Repositories ProfileDateAppoint = p.DateAppoint.Value, LastInsignia = p.ProfileInsignia == null || p.ProfileInsignia.Count == 0 ? "" : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .FirstOrDefault() == null ? "" : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) - .OrderByDescending(x => x.Year).FirstOrDefault().Insignia, + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) + .OrderByDescending(x => x.Year).FirstOrDefault()?.Insignia, LastInsigniaId = p.ProfileInsignia == null || p.ProfileInsignia.Count == 0 ? Guid.Empty : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .FirstOrDefault() == null ? Guid.Empty : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) - .OrderByDescending(x => x.Year).FirstOrDefault().InsigniaId, + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) + .OrderByDescending(x => x.Year).FirstOrDefault()?.InsigniaId, Salary = p.Amount, SalaryCondition = p.ProfileSalary == null || p.ProfileSalary.Count == 0 ? 0 : - p.ProfileSalary.Where(x => x.Date != null).Where(x => x.Date.Value <= new DateTime(period.Year - 5, 4, 29)) + p.ProfileSalary.Where(x => x.Date != null).Where(x => x.Date.HasValue && x.Date.Value <= new DateTime(period.Year - 5, 4, 29)) .OrderByDescending(x => x.Order).FirstOrDefault() != null ? p.ProfileSalary - .Where(x => x.Date != null).Where(x => x.Date.Value <= new DateTime(period.Year - 5, 4, 29)) - .OrderByDescending(x => x.Order).FirstOrDefault().Amount : + .Where(x => x.Date != null).Where(x => x.Date.HasValue && x.Date.Value <= new DateTime(period.Year - 5, 4, 29)) + .OrderByDescending(x => x.Order).FirstOrDefault()?.Amount : p.Amount, PosNo = p.PosNo, Gender = p.Gender == null ? null : p.Gender, @@ -3836,12 +3850,12 @@ namespace BMA.EHR.Application.Repositories PositionTypeName = p.PosType, IsHigherLevel = p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .FirstOrDefault() == null ? true : IsHigherLevel(p.ProfileInsignia - .Where(x => x.InsigniaId.Value != - GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != + GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .OrderByDescending(x => x.Year) .FirstOrDefault(), "ทวีติยาภรณ์ช้างเผือก"), @@ -3851,14 +3865,14 @@ namespace BMA.EHR.Application.Repositories p.ProfileSalary.Where(x => x.PositionLevel == "ชำนาญการ").OrderBy(p => p.Date) .FirstOrDefault() == null ? null : p.ProfileSalary.Where(x => x.PositionLevel == "ชำนาญการ").OrderBy(p => p.Date) - .FirstOrDefault().Date, + .FirstOrDefault()?.Date, FirstRecvInsigniaYear = p.ProfileInsignia.Count == 0 ? 0 : p.ProfileInsignia - .Where(x => x.InsigniaId.Value == GetInsigniaByName("ประถมาภรณ์มงกุฎไทย").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value == GetInsigniaByName("ประถมาภรณ์มงกุฎไทย")?.Id) .OrderBy(x => x.Year).FirstOrDefault() == null ? 0 : p.ProfileInsignia - .Where(x => x.InsigniaId.Value == GetInsigniaByName("ประถมาภรณ์มงกุฎไทย").Id) - .OrderBy(x => x.Year).FirstOrDefault().Year, + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value == GetInsigniaByName("ประถมาภรณ์มงกุฎไทย")?.Id) + .OrderBy(x => x.Year).FirstOrDefault()?.Year, PositionSalaryAmount = p.PositionSalaryAmount, Amount = p.Amount, RootId = p.RootId, @@ -4034,8 +4048,9 @@ namespace BMA.EHR.Application.Repositories && p.ProfileInsignia != null && p.ProfileInsignia.Count > 0 && (p.ProfileInsignia.Where(x => - x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id && - x.InsigniaId.Value == GetInsigniaByName("ทวีติยาภรณ์ช้างเผือก").Id) + x.InsigniaId.HasValue && + x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id && + x.InsigniaId.Value == GetInsigniaByName("ทวีติยาภรณ์ช้างเผือก")?.Id) .ToList() .Count() == 0) select new @@ -4054,24 +4069,24 @@ namespace BMA.EHR.Application.Repositories ProfileDateAppoint = p.DateAppoint.Value, LastInsignia = p.ProfileInsignia == null || p.ProfileInsignia.Count == 0 ? "" : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .FirstOrDefault() == null ? "" : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) - .OrderByDescending(x => x.Year).FirstOrDefault().Insignia, + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) + .OrderByDescending(x => x.Year).FirstOrDefault()?.Insignia, LastInsigniaId = p.ProfileInsignia == null || p.ProfileInsignia.Count == 0 ? Guid.Empty : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .FirstOrDefault() == null ? Guid.Empty : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) - .OrderByDescending(x => x.Year).FirstOrDefault().InsigniaId, + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) + .OrderByDescending(x => x.Year).FirstOrDefault()?.InsigniaId, Salary = p.Amount, SalaryCondition = p.ProfileSalary == null || p.ProfileSalary.Count == 0 ? 0 : - p.ProfileSalary.Where(x => x.Date != null).Where(x => x.Date.Value <= new DateTime(period.Year, 4, 29)) + p.ProfileSalary.Where(x => x.Date != null).Where(x => x.Date.HasValue && x.Date.Value <= new DateTime(period.Year, 4, 29)) .OrderByDescending(x => x.Order).FirstOrDefault() != null ? p.ProfileSalary - .Where(x => x.Date != null).Where(x => x.Date.Value <= new DateTime(period.Year, 4, 29)) - .OrderByDescending(x => x.Order).FirstOrDefault().Amount : + .Where(x => x.Date != null).Where(x => x.Date.HasValue && x.Date.Value <= new DateTime(period.Year, 4, 29)) + .OrderByDescending(x => x.Order).FirstOrDefault()?.Amount : p.Amount, PosNo = p.PosNo, Gender = p.Gender == null ? null : p.Gender, @@ -4081,12 +4096,12 @@ namespace BMA.EHR.Application.Repositories PositionTypeName = p.PosType, IsHigherLevel = p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .FirstOrDefault() == null ? true : IsHigherLevel(p.ProfileInsignia - .Where(x => x.InsigniaId.Value != - GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != + GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .OrderByDescending(x => x.Year) .FirstOrDefault(), "ทวีติยาภรณ์ช้างเผือก"), @@ -4096,14 +4111,14 @@ namespace BMA.EHR.Application.Repositories p.ProfileSalary.Where(x => x.PositionLevel == "ชำนาญการพิเศษ").OrderBy(p => p.Date) .FirstOrDefault() == null ? null : p.ProfileSalary.Where(x => x.PositionLevel == "ชำนาญการพิเศษ").OrderBy(p => p.Date) - .FirstOrDefault().Date, + .FirstOrDefault()?.Date, FirstRecvInsigniaYear = p.ProfileInsignia.Count == 0 ? 0 : p.ProfileInsignia - .Where(x => x.InsigniaId.Value == GetInsigniaByName("ประถมาภรณ์มงกุฎไทย").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value == GetInsigniaByName("ประถมาภรณ์มงกุฎไทย")?.Id) .OrderBy(x => x.Year).FirstOrDefault() == null ? 0 : p.ProfileInsignia - .Where(x => x.InsigniaId.Value == GetInsigniaByName("ประถมาภรณ์มงกุฎไทย").Id) - .OrderBy(x => x.Year).FirstOrDefault().Year, + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value == GetInsigniaByName("ประถมาภรณ์มงกุฎไทย")?.Id) + .OrderBy(x => x.Year).FirstOrDefault()?.Year, PositionSalaryAmount = p.PositionSalaryAmount, Amount = p.Amount, RootId = p.RootId, @@ -4175,8 +4190,9 @@ namespace BMA.EHR.Application.Repositories && p.ProfileInsignia != null && p.ProfileInsignia.Count > 0 && (p.ProfileInsignia.Where(x => - x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id && - x.InsigniaId.Value == GetInsigniaByName("ประถมาภรณ์มงกุฎไทย").Id) + x.InsigniaId.HasValue && + x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id && + x.InsigniaId.Value == GetInsigniaByName("ประถมาภรณ์มงกุฎไทย")?.Id) .ToList() .Count() == 0) select new @@ -4195,24 +4211,24 @@ namespace BMA.EHR.Application.Repositories ProfileDateAppoint = p.DateAppoint.Value, LastInsignia = p.ProfileInsignia == null || p.ProfileInsignia.Count == 0 ? "" : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .FirstOrDefault() == null ? "" : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) - .OrderByDescending(x => x.Year).FirstOrDefault().Insignia, + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) + .OrderByDescending(x => x.Year).FirstOrDefault()?.Insignia, LastInsigniaId = p.ProfileInsignia == null || p.ProfileInsignia.Count == 0 ? Guid.Empty : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .FirstOrDefault() == null ? Guid.Empty : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) - .OrderByDescending(x => x.Year).FirstOrDefault().InsigniaId, + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) + .OrderByDescending(x => x.Year).FirstOrDefault()?.InsigniaId, Salary = p.Amount, SalaryCondition = p.ProfileSalary == null || p.ProfileSalary.Count == 0 ? 0 : - p.ProfileSalary.Where(x => x.Date != null).Where(x => x.Date.Value <= new DateTime(period.Year, 4, 29)) + p.ProfileSalary.Where(x => x.Date != null).Where(x => x.Date.HasValue && x.Date.Value <= new DateTime(period.Year, 4, 29)) .OrderByDescending(x => x.Order).FirstOrDefault() != null ? p.ProfileSalary - .Where(x => x.Date != null).Where(x => x.Date.Value <= new DateTime(period.Year, 4, 29)) - .OrderByDescending(x => x.Order).FirstOrDefault().Amount : + .Where(x => x.Date != null).Where(x => x.Date.HasValue && x.Date.Value <= new DateTime(period.Year, 4, 29)) + .OrderByDescending(x => x.Order).FirstOrDefault()?.Amount : p.Amount, PosNo = p.PosNo, Gender = p.Gender == null ? null : p.Gender, @@ -4222,12 +4238,12 @@ namespace BMA.EHR.Application.Repositories PositionTypeName = p.PosType, IsHigherLevel = p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .FirstOrDefault() == null ? true : IsHigherLevel(p.ProfileInsignia - .Where(x => x.InsigniaId.Value != - GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != + GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .OrderByDescending(x => x.Year) .FirstOrDefault(), "ประถมาภรณ์มงกุฎไทย"), @@ -4238,14 +4254,14 @@ namespace BMA.EHR.Application.Repositories p.ProfileSalary.Where(x => x.PositionLevel == "ชำนาญการพิเศษ").OrderBy(p => p.Date) .FirstOrDefault() == null ? null : p.ProfileSalary.Where(x => x.PositionLevel == "ชำนาญการพิเศษ").OrderBy(p => p.Date) - .FirstOrDefault().Date, + .FirstOrDefault()?.Date, FirstRecvInsigniaYear = p.ProfileInsignia.Count == 0 ? 0 : p.ProfileInsignia - .Where(x => x.InsigniaId.Value == GetInsigniaByName("ทวีติยาภรณ์ช้างเผือก").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value == GetInsigniaByName("ทวีติยาภรณ์ช้างเผือก")?.Id) .OrderBy(x => x.Year).FirstOrDefault() == null ? 0 : p.ProfileInsignia - .Where(x => x.InsigniaId.Value == GetInsigniaByName("ทวีติยาภรณ์ช้างเผือก").Id) - .OrderBy(x => x.Year).FirstOrDefault().Year, + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value == GetInsigniaByName("ทวีติยาภรณ์ช้างเผือก")?.Id) + .OrderBy(x => x.Year).FirstOrDefault()?.Year, PositionSalaryAmount = p.PositionSalaryAmount, Amount = p.Amount, RootId = p.RootId, @@ -4409,8 +4425,9 @@ namespace BMA.EHR.Application.Repositories && p.ProfileInsignia != null && p.ProfileInsignia.Count > 0 && (p.ProfileInsignia.Where(x => - x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id && - x.InsigniaId.Value == GetInsigniaByName("ทวีติยาภรณ์ช้างเผือก").Id) + x.InsigniaId.HasValue && + x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id && + x.InsigniaId.Value == GetInsigniaByName("ทวีติยาภรณ์ช้างเผือก")?.Id) .ToList() .Count() == 0) select new @@ -4429,24 +4446,24 @@ namespace BMA.EHR.Application.Repositories ProfileDateAppoint = p.DateAppoint.Value, LastInsignia = p.ProfileInsignia == null || p.ProfileInsignia.Count == 0 ? "" : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .FirstOrDefault() == null ? "" : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) - .OrderByDescending(x => x.Year).FirstOrDefault().Insignia, + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) + .OrderByDescending(x => x.Year).FirstOrDefault()?.Insignia, LastInsigniaId = p.ProfileInsignia == null || p.ProfileInsignia.Count == 0 ? Guid.Empty : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .FirstOrDefault() == null ? Guid.Empty : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) - .OrderByDescending(x => x.Year).FirstOrDefault().InsigniaId, + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) + .OrderByDescending(x => x.Year).FirstOrDefault()?.InsigniaId, Salary = p.Amount, SalaryCondition = p.ProfileSalary == null || p.ProfileSalary.Count == 0 ? 0 : - p.ProfileSalary.Where(x => x.Date != null).Where(x => x.Date.Value <= new DateTime(period.Year, 4, 29)) + p.ProfileSalary.Where(x => x.Date != null).Where(x => x.Date.HasValue && x.Date.Value <= new DateTime(period.Year, 4, 29)) .OrderByDescending(x => x.Order).FirstOrDefault() != null ? p.ProfileSalary - .Where(x => x.Date != null).Where(x => x.Date.Value <= new DateTime(period.Year, 4, 29)) - .OrderByDescending(x => x.Order).FirstOrDefault().Amount : + .Where(x => x.Date != null).Where(x => x.Date.HasValue && x.Date.Value <= new DateTime(period.Year, 4, 29)) + .OrderByDescending(x => x.Order).FirstOrDefault()?.Amount : p.Amount, PosNo = p.PosNo, Gender = p.Gender == null ? null : p.Gender, @@ -4456,12 +4473,12 @@ namespace BMA.EHR.Application.Repositories PositionTypeName = p.PosType, IsHigherLevel = p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .FirstOrDefault() == null ? true : IsHigherLevel(p.ProfileInsignia - .Where(x => x.InsigniaId.Value != - GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != + GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .OrderByDescending(x => x.Year) .FirstOrDefault(), "ทวีติยาภรณ์ช้างเผือก"), @@ -4471,14 +4488,14 @@ namespace BMA.EHR.Application.Repositories p.ProfileSalary.Where(x => x.PositionLevel == "เชี่ยวชาญ").OrderBy(p => p.Date) .FirstOrDefault() == null ? null : p.ProfileSalary.Where(x => x.PositionLevel == "เชี่ยวชาญ").OrderBy(p => p.Date) - .FirstOrDefault().Date, + .FirstOrDefault()?.Date, FirstRecvInsigniaYear = p.ProfileInsignia.Count == 0 ? 0 : p.ProfileInsignia - .Where(x => x.InsigniaId.Value == GetInsigniaByName("ทวีติยาภรณ์ช้างเผือก").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value == GetInsigniaByName("ทวีติยาภรณ์ช้างเผือก")?.Id) .OrderBy(x => x.Year).FirstOrDefault() == null ? 0 : p.ProfileInsignia - .Where(x => x.InsigniaId.Value == GetInsigniaByName("ทวีติยาภรณ์ช้างเผือก").Id) - .OrderBy(x => x.Year).FirstOrDefault().Year, + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value == GetInsigniaByName("ทวีติยาภรณ์ช้างเผือก")?.Id) + .OrderBy(x => x.Year).FirstOrDefault()?.Year, PositionSalaryAmount = p.PositionSalaryAmount, Amount = p.Amount, RootId = p.RootId, @@ -4568,8 +4585,9 @@ namespace BMA.EHR.Application.Repositories && p.ProfileInsignia != null && p.ProfileInsignia.Count > 0 && (p.ProfileInsignia.Where(x => - x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id && - x.InsigniaId.Value == GetInsigniaByName("ประถมาภรณ์มงกุฎไทย").Id) + x.InsigniaId.HasValue && + x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id && + x.InsigniaId.Value == GetInsigniaByName("ประถมาภรณ์มงกุฎไทย")?.Id) .ToList() .Count() == 0) select new @@ -4588,24 +4606,24 @@ namespace BMA.EHR.Application.Repositories ProfileDateAppoint = p.DateAppoint.Value, LastInsignia = p.ProfileInsignia == null || p.ProfileInsignia.Count == 0 ? "" : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .FirstOrDefault() == null ? "" : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) - .OrderByDescending(x => x.Year).FirstOrDefault().Insignia, + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) + .OrderByDescending(x => x.Year).FirstOrDefault()?.Insignia, LastInsigniaId = p.ProfileInsignia == null || p.ProfileInsignia.Count == 0 ? Guid.Empty : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .FirstOrDefault() == null ? Guid.Empty : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) - .OrderByDescending(x => x.Year).FirstOrDefault().InsigniaId, + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) + .OrderByDescending(x => x.Year).FirstOrDefault()?.InsigniaId, Salary = p.Amount, SalaryCondition = p.ProfileSalary == null || p.ProfileSalary.Count == 0 ? 0 : - p.ProfileSalary.Where(x => x.Date != null).Where(x => x.Date.Value <= new DateTime(period.Year, 4, 29)) + p.ProfileSalary.Where(x => x.Date != null).Where(x => x.Date.HasValue && x.Date.Value <= new DateTime(period.Year, 4, 29)) .OrderByDescending(x => x.Order).FirstOrDefault() != null ? p.ProfileSalary - .Where(x => x.Date != null).Where(x => x.Date.Value <= new DateTime(period.Year, 4, 29)) - .OrderByDescending(x => x.Order).FirstOrDefault().Amount : + .Where(x => x.Date != null).Where(x => x.Date.HasValue && x.Date.Value <= new DateTime(period.Year, 4, 29)) + .OrderByDescending(x => x.Order).FirstOrDefault()?.Amount : p.Amount, PosNo = p.PosNo, Gender = p.Gender == null ? null : p.Gender, @@ -4615,12 +4633,12 @@ namespace BMA.EHR.Application.Repositories PositionTypeName = p.PosType, IsHigherLevel = p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .FirstOrDefault() == null ? true : IsHigherLevel(p.ProfileInsignia - .Where(x => x.InsigniaId.Value != - GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != + GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .OrderByDescending(x => x.Year) .FirstOrDefault(), "ประถมาภรณ์มงกุฎไทย"), @@ -4631,14 +4649,14 @@ namespace BMA.EHR.Application.Repositories p.ProfileSalary.Where(x => x.PositionLevel == "เชี่ยวชาญ").OrderBy(p => p.Date) .FirstOrDefault() == null ? null : p.ProfileSalary.Where(x => x.PositionLevel == "เชี่ยวชาญ").OrderBy(p => p.Date) - .FirstOrDefault().Date, + .FirstOrDefault()?.Date, FirstRecvInsigniaYear = p.ProfileInsignia.Count == 0 ? 0 : p.ProfileInsignia - .Where(x => x.InsigniaId.Value == GetInsigniaByName("ทวีติยาภรณ์ช้างเผือก").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value == GetInsigniaByName("ทวีติยาภรณ์ช้างเผือก")?.Id) .OrderBy(x => x.Year).FirstOrDefault() == null ? 0 : p.ProfileInsignia - .Where(x => x.InsigniaId.Value == GetInsigniaByName("ทวีติยาภรณ์ช้างเผือก").Id) - .OrderBy(x => x.Year).FirstOrDefault().Year, + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value == GetInsigniaByName("ทวีติยาภรณ์ช้างเผือก")?.Id) + .OrderBy(x => x.Year).FirstOrDefault()?.Year, PositionSalaryAmount = p.PositionSalaryAmount, Amount = p.Amount, RootId = p.RootId, @@ -4729,8 +4747,9 @@ namespace BMA.EHR.Application.Repositories && p.ProfileInsignia != null && p.ProfileInsignia.Count > 0 && (p.ProfileInsignia.Where(x => - x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id && - x.InsigniaId.Value == GetInsigniaByName("ประถมาภรณ์ช้างเผือก").Id) + x.InsigniaId.HasValue && + x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id && + x.InsigniaId.Value == GetInsigniaByName("ประถมาภรณ์ช้างเผือก")?.Id) .ToList() .Count() == 0) select new @@ -4749,24 +4768,24 @@ namespace BMA.EHR.Application.Repositories ProfileDateAppoint = p.DateAppoint.Value, LastInsignia = p.ProfileInsignia == null || p.ProfileInsignia.Count == 0 ? "" : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .FirstOrDefault() == null ? "" : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) - .OrderByDescending(x => x.Year).FirstOrDefault().Insignia, + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) + .OrderByDescending(x => x.Year).FirstOrDefault()?.Insignia, LastInsigniaId = p.ProfileInsignia == null || p.ProfileInsignia.Count == 0 ? Guid.Empty : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .FirstOrDefault() == null ? Guid.Empty : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) - .OrderByDescending(x => x.Year).FirstOrDefault().InsigniaId, + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) + .OrderByDescending(x => x.Year).FirstOrDefault()?.InsigniaId, Salary = p.Amount, SalaryCondition = p.ProfileSalary == null || p.ProfileSalary.Count == 0 ? 0 : - p.ProfileSalary.Where(x => x.Date != null).Where(x => x.Date.Value <= new DateTime(period.Year, 4, 29)) + p.ProfileSalary.Where(x => x.Date != null).Where(x => x.Date.HasValue && x.Date.Value <= new DateTime(period.Year, 4, 29)) .OrderByDescending(x => x.Order).FirstOrDefault() != null ? p.ProfileSalary - .Where(x => x.Date != null).Where(x => x.Date.Value <= new DateTime(period.Year, 4, 29)) - .OrderByDescending(x => x.Order).FirstOrDefault().Amount : + .Where(x => x.Date != null).Where(x => x.Date.HasValue && x.Date.Value <= new DateTime(period.Year, 4, 29)) + .OrderByDescending(x => x.Order).FirstOrDefault()?.Amount : p.Amount, PosNo = p.PosNo, Gender = p.Gender == null ? null : p.Gender, @@ -4776,12 +4795,12 @@ namespace BMA.EHR.Application.Repositories PositionTypeName = p.PosType, IsHigherLevel = p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .FirstOrDefault() == null ? true : IsHigherLevel(p.ProfileInsignia - .Where(x => x.InsigniaId.Value != - GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != + GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .OrderByDescending(x => x.Year) .FirstOrDefault(), "ประถมาภรณ์ช้างเผือก"), @@ -4791,14 +4810,14 @@ namespace BMA.EHR.Application.Repositories p.ProfileSalary.Where(x => x.PositionLevel == "เชี่ยวชาญ").OrderBy(p => p.Date) .FirstOrDefault() == null ? null : p.ProfileSalary.Where(x => x.PositionLevel == "เชี่ยวชาญ").OrderBy(p => p.Date) - .FirstOrDefault().Date, + .FirstOrDefault()?.Date, FirstRecvInsigniaYear = p.ProfileInsignia.Count == 0 ? 0 : p.ProfileInsignia - .Where(x => x.InsigniaId.Value == GetInsigniaByName("ประถมาภรณ์ช้างเผือก").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value == GetInsigniaByName("ประถมาภรณ์ช้างเผือก")?.Id) .OrderBy(x => x.Year).FirstOrDefault() == null ? 0 : p.ProfileInsignia - .Where(x => x.InsigniaId.Value == GetInsigniaByName("ประถมาภรณ์ช้างเผือก").Id) - .OrderBy(x => x.Year).FirstOrDefault().Year, + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value == GetInsigniaByName("ประถมาภรณ์ช้างเผือก")?.Id) + .OrderBy(x => x.Year).FirstOrDefault()?.Year, PositionSalaryAmount = p.PositionSalaryAmount, Amount = p.Amount, RootId = p.RootId, @@ -4972,8 +4991,9 @@ namespace BMA.EHR.Application.Repositories && p.ProfileInsignia != null && p.ProfileInsignia.Count > 0 && (p.ProfileInsignia.Where(x => - x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id && - x.InsigniaId.Value == GetInsigniaByName("ประถมาภรณ์ช้างเผือก").Id) + x.InsigniaId.HasValue && + x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id && + x.InsigniaId.Value == GetInsigniaByName("ประถมาภรณ์ช้างเผือก")?.Id) .ToList() .Count() == 0) select new @@ -4992,25 +5012,25 @@ namespace BMA.EHR.Application.Repositories ProfileDateAppoint = p.DateAppoint.Value, LastInsignia = p.ProfileInsignia == null || p.ProfileInsignia.Count == 0 ? "" : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .FirstOrDefault() == null ? "" : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) - .OrderByDescending(x => x.Year).FirstOrDefault().Insignia, + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) + .OrderByDescending(x => x.Year).FirstOrDefault()?.Insignia, LastInsigniaId = p.ProfileInsignia == null || p.ProfileInsignia.Count == 0 ? Guid.Empty : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .FirstOrDefault() == null ? Guid.Empty : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) - .OrderByDescending(x => x.Year).FirstOrDefault().InsigniaId, + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) + .OrderByDescending(x => x.Year).FirstOrDefault()?.InsigniaId, Salary = p.Amount, SalaryPosition = p.PositionSalaryAmount, SalaryCondition = p.ProfileSalary == null || p.ProfileSalary.Count == 0 ? 0 : - p.ProfileSalary.Where(x => x.Date != null).Where(x => x.Date.Value <= new DateTime(period.Year, 4, 29)) + p.ProfileSalary.Where(x => x.Date != null).Where(x => x.Date.HasValue && x.Date.Value <= new DateTime(period.Year, 4, 29)) .OrderByDescending(x => x.Order).FirstOrDefault() != null ? p.ProfileSalary - .Where(x => x.Date != null).Where(x => x.Date.Value <= new DateTime(period.Year, 4, 29)) - .OrderByDescending(x => x.Order).FirstOrDefault().Amount : + .Where(x => x.Date != null).Where(x => x.Date.HasValue && x.Date.Value <= new DateTime(period.Year, 4, 29)) + .OrderByDescending(x => x.Order).FirstOrDefault()?.Amount : p.Amount, PosNo = p.PosNo, Gender = p.Gender == null ? null : p.Gender, @@ -5020,12 +5040,12 @@ namespace BMA.EHR.Application.Repositories PositionTypeName = p.PosType, IsHigherLevel = p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .FirstOrDefault() == null ? true : IsHigherLevel(p.ProfileInsignia - .Where(x => x.InsigniaId.Value != - GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != + GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .OrderByDescending(x => x.Year) .FirstOrDefault(), "ประถมาภรณ์ช้างเผือก"), @@ -5035,14 +5055,14 @@ namespace BMA.EHR.Application.Repositories p.ProfileSalary.Where(x => x.PositionLevel == "ทรงคุณวุฒิ").OrderBy(p => p.Date) .FirstOrDefault() == null ? null : p.ProfileSalary.Where(x => x.PositionLevel == "ทรงคุณวุฒิ").OrderBy(p => p.Date) - .FirstOrDefault().Date, + .FirstOrDefault()?.Date, FirstRecvInsigniaYear = p.ProfileInsignia.Count == 0 ? 0 : p.ProfileInsignia - .Where(x => x.InsigniaId.Value == GetInsigniaByName("ประถมาภรณ์มงกุฎไทย").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value == GetInsigniaByName("ประถมาภรณ์มงกุฎไทย")?.Id) .OrderBy(x => x.Year).FirstOrDefault() == null ? 0 : p.ProfileInsignia - .Where(x => x.InsigniaId.Value == GetInsigniaByName("ประถมาภรณ์มงกุฎไทย").Id) - .OrderBy(x => x.Year).FirstOrDefault().Year, + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value == GetInsigniaByName("ประถมาภรณ์มงกุฎไทย")?.Id) + .OrderBy(x => x.Year).FirstOrDefault()?.Year, PositionSalaryAmount = p.PositionSalaryAmount, Amount = p.Amount, RootId = p.RootId, @@ -5139,8 +5159,9 @@ namespace BMA.EHR.Application.Repositories && p.ProfileInsignia != null && p.ProfileInsignia.Count > 0 && (p.ProfileInsignia.Where(x => - x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id && - x.InsigniaId.Value == GetInsigniaByName("มหาวชิรมงกุฎ").Id) + x.InsigniaId.HasValue && + x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id && + x.InsigniaId.Value == GetInsigniaByName("มหาวชิรมงกุฎ")?.Id) .ToList() .Count() == 0) select new @@ -5159,25 +5180,25 @@ namespace BMA.EHR.Application.Repositories ProfileDateAppoint = p.DateAppoint.Value, LastInsignia = p.ProfileInsignia == null || p.ProfileInsignia.Count == 0 ? "" : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .FirstOrDefault() == null ? "" : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) - .OrderByDescending(x => x.Year).FirstOrDefault().Insignia, + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) + .OrderByDescending(x => x.Year).FirstOrDefault()?.Insignia, LastInsigniaId = p.ProfileInsignia == null || p.ProfileInsignia.Count == 0 ? Guid.Empty : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .FirstOrDefault() == null ? Guid.Empty : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) - .OrderByDescending(x => x.Year).FirstOrDefault().InsigniaId, + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) + .OrderByDescending(x => x.Year).FirstOrDefault()?.InsigniaId, Salary = p.Amount, SalaryPosition = p.PositionSalaryAmount, SalaryCondition = p.ProfileSalary == null || p.ProfileSalary.Count == 0 ? 0 : - p.ProfileSalary.Where(x => x.Date != null).Where(x => x.Date.Value <= new DateTime(period.Year, 4, 29)) + p.ProfileSalary.Where(x => x.Date != null).Where(x => x.Date.HasValue && x.Date.Value <= new DateTime(period.Year, 4, 29)) .OrderByDescending(x => x.Order).FirstOrDefault() != null ? p.ProfileSalary - .Where(x => x.Date != null).Where(x => x.Date.Value <= new DateTime(period.Year, 4, 29)) - .OrderByDescending(x => x.Order).FirstOrDefault().Amount : + .Where(x => x.Date != null).Where(x => x.Date.HasValue && x.Date.Value <= new DateTime(period.Year, 4, 29)) + .OrderByDescending(x => x.Order).FirstOrDefault()?.Amount : p.Amount, PosNo = p.PosNo == null ? "" : p.PosNo, Gender = p.Gender == null ? null : p.Gender, @@ -5187,12 +5208,12 @@ namespace BMA.EHR.Application.Repositories PositionTypeName = p.PosType, IsHigherLevel = p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .FirstOrDefault() == null ? true : IsHigherLevel(p.ProfileInsignia - .Where(x => x.InsigniaId.Value != - GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != + GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .OrderByDescending(x => x.Year) .FirstOrDefault(), "มหาวชิรมงกุฎ"), @@ -5202,14 +5223,14 @@ namespace BMA.EHR.Application.Repositories p.ProfileSalary.Where(x => x.PositionLevel == "ทรงคุณวุฒิ").OrderBy(p => p.Date) .FirstOrDefault() == null ? null : p.ProfileSalary.Where(x => x.PositionLevel == "ทรงคุณวุฒิ").OrderBy(p => p.Date) - .FirstOrDefault().Date, + .FirstOrDefault()?.Date, FirstRecvInsigniaYear = p.ProfileInsignia.Count == 0 ? 0 : p.ProfileInsignia - .Where(x => x.InsigniaId.Value == GetInsigniaByName("ประถมาภรณ์ช้างเผือก").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value == GetInsigniaByName("ประถมาภรณ์ช้างเผือก")?.Id) .OrderBy(x => x.Year).FirstOrDefault() == null ? 0 : p.ProfileInsignia - .Where(x => x.InsigniaId.Value == GetInsigniaByName("ประถมาภรณ์ช้างเผือก").Id) - .OrderBy(x => x.Year).FirstOrDefault().Year, + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value == GetInsigniaByName("ประถมาภรณ์ช้างเผือก")?.Id) + .OrderBy(x => x.Year).FirstOrDefault()?.Year, PositionSalaryAmount = p.PositionSalaryAmount, Amount = p.Amount, RootId = p.RootId, @@ -5306,8 +5327,9 @@ namespace BMA.EHR.Application.Repositories && p.ProfileInsignia != null && p.ProfileInsignia.Count > 0 && (p.ProfileInsignia.Where(x => - x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id && - x.InsigniaId.Value == GetInsigniaByName("มหาปรมาภรณ์ช้างเผือก").Id) + x.InsigniaId.HasValue && + x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id && + x.InsigniaId.Value == GetInsigniaByName("มหาปรมาภรณ์ช้างเผือก")?.Id) .ToList() .Count() == 0) select new @@ -5326,25 +5348,25 @@ namespace BMA.EHR.Application.Repositories ProfileDateAppoint = p.DateAppoint.Value, LastInsignia = p.ProfileInsignia == null || p.ProfileInsignia.Count == 0 ? "" : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .FirstOrDefault() == null ? "" : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) - .OrderByDescending(x => x.Year).FirstOrDefault().Insignia, + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) + .OrderByDescending(x => x.Year).FirstOrDefault()?.Insignia, LastInsigniaId = p.ProfileInsignia == null || p.ProfileInsignia.Count == 0 ? Guid.Empty : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .FirstOrDefault() == null ? Guid.Empty : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) - .OrderByDescending(x => x.Year).FirstOrDefault().InsigniaId, + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) + .OrderByDescending(x => x.Year).FirstOrDefault()?.InsigniaId, Salary = p.Amount, SalaryPosition = p.PositionSalaryAmount, SalaryCondition = p.ProfileSalary == null || p.ProfileSalary.Count == 0 ? 0 : - p.ProfileSalary.Where(x => x.Date != null).Where(x => x.Date.Value <= new DateTime(period.Year, 4, 29)) + p.ProfileSalary.Where(x => x.Date != null).Where(x => x.Date.HasValue && x.Date.Value <= new DateTime(period.Year, 4, 29)) .OrderByDescending(x => x.Order).FirstOrDefault() != null ? p.ProfileSalary - .Where(x => x.Date != null).Where(x => x.Date.Value <= new DateTime(period.Year, 4, 29)) - .OrderByDescending(x => x.Order).FirstOrDefault().Amount : + .Where(x => x.Date != null).Where(x => x.Date.HasValue && x.Date.Value <= new DateTime(period.Year, 4, 29)) + .OrderByDescending(x => x.Order).FirstOrDefault()?.Amount : p.Amount, PosNo = p.PosNo, @@ -5355,12 +5377,12 @@ namespace BMA.EHR.Application.Repositories PositionTypeName = p.PosType, IsHigherLevel = p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .FirstOrDefault() == null ? true : IsHigherLevel(p.ProfileInsignia - .Where(x => x.InsigniaId.Value != - GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != + GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .OrderByDescending(x => x.Year) .FirstOrDefault(), "มหาปรมาภรณ์ช้างเผือก"), @@ -5371,12 +5393,12 @@ namespace BMA.EHR.Application.Repositories p.ProfileSalary.Where(x => x.PositionLevel == "ทรงคุณวุฒิ").OrderBy(p => p.Date) .FirstOrDefault() == null ? null : p.ProfileSalary.Where(x => x.PositionLevel == "ทรงคุณวุฒิ").OrderBy(p => p.Date) - .FirstOrDefault().Date, + .FirstOrDefault()?.Date, FirstRecvInsigniaYear = p.ProfileInsignia.Count == 0 ? 0 : - p.ProfileInsignia.Where(x => x.InsigniaId.Value == GetInsigniaByName("มหาวชิรมงกุฎ").Id) + p.ProfileInsignia.Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value == GetInsigniaByName("มหาวชิรมงกุฎ")?.Id) .OrderBy(x => x.Year).FirstOrDefault() == null ? 0 : - p.ProfileInsignia.Where(x => x.InsigniaId.Value == GetInsigniaByName("มหาวชิรมงกุฎ").Id) - .OrderBy(x => x.Year).FirstOrDefault().Year, + p.ProfileInsignia.Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value == GetInsigniaByName("มหาวชิรมงกุฎ")?.Id) + .OrderBy(x => x.Year).FirstOrDefault()?.Year, PositionSalaryAmount = p.PositionSalaryAmount, Amount = p.Amount, RootId = p.RootId, @@ -5555,8 +5577,9 @@ namespace BMA.EHR.Application.Repositories && p.ProfileInsignia != null && p.ProfileInsignia.Count > 0 && (p.ProfileInsignia.Where(x => - x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id && - x.InsigniaId.Value == GetInsigniaByName("ประถมาภรณ์ช้างเผือก").Id) + x.InsigniaId.HasValue && + x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id && + x.InsigniaId.Value == GetInsigniaByName("ประถมาภรณ์ช้างเผือก")?.Id) .ToList() .Count() == 0) select new @@ -5575,25 +5598,25 @@ namespace BMA.EHR.Application.Repositories ProfileDateAppoint = p.DateAppoint.Value, LastInsignia = p.ProfileInsignia == null || p.ProfileInsignia.Count == 0 ? "" : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .FirstOrDefault() == null ? "" : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) - .OrderByDescending(x => x.Year).FirstOrDefault().Insignia, + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) + .OrderByDescending(x => x.Year).FirstOrDefault()?.Insignia, LastInsigniaId = p.ProfileInsignia == null || p.ProfileInsignia.Count == 0 ? Guid.Empty : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .FirstOrDefault() == null ? Guid.Empty : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) - .OrderByDescending(x => x.Year).FirstOrDefault().InsigniaId, + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) + .OrderByDescending(x => x.Year).FirstOrDefault()?.InsigniaId, Salary = p.Amount, SalaryPosition = p.PositionSalaryAmount, SalaryCondition = p.ProfileSalary == null || p.ProfileSalary.Count == 0 ? 0 : - p.ProfileSalary.Where(x => x.Date != null).Where(x => x.Date.Value <= new DateTime(period.Year, 4, 29)) + p.ProfileSalary.Where(x => x.Date != null).Where(x => x.Date.HasValue && x.Date.Value <= new DateTime(period.Year, 4, 29)) .OrderByDescending(x => x.Order).FirstOrDefault() != null ? p.ProfileSalary - .Where(x => x.Date != null).Where(x => x.Date.Value <= new DateTime(period.Year, 4, 29)) - .OrderByDescending(x => x.Order).FirstOrDefault().Amount : + .Where(x => x.Date != null).Where(x => x.Date.HasValue && x.Date.Value <= new DateTime(period.Year, 4, 29)) + .OrderByDescending(x => x.Order).FirstOrDefault()?.Amount : p.Amount, PosNo = p.PosNo, Gender = p.Gender == null ? null : p.Gender, @@ -5603,12 +5626,12 @@ namespace BMA.EHR.Application.Repositories PositionTypeName = p.PosType, IsHigherLevel = p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .FirstOrDefault() == null ? true : IsHigherLevel(p.ProfileInsignia - .Where(x => x.InsigniaId.Value != - GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != + GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .OrderByDescending(x => x.Year) .FirstOrDefault(), "ประถมาภรณ์ช้างเผือก"), @@ -5618,14 +5641,14 @@ namespace BMA.EHR.Application.Repositories p.ProfileSalary.Where(x => x.PositionLevel == "ทรงคุณวุฒิ").OrderBy(p => p.Date) .FirstOrDefault() == null ? null : p.ProfileSalary.Where(x => x.PositionLevel == "ทรงคุณวุฒิ").OrderBy(p => p.Date) - .FirstOrDefault().Date, + .FirstOrDefault()?.Date, FirstRecvInsigniaYear = p.ProfileInsignia.Count == 0 ? 0 : p.ProfileInsignia - .Where(x => x.InsigniaId.Value == GetInsigniaByName("ประถมาภรณ์มงกุฎไทย").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value == GetInsigniaByName("ประถมาภรณ์มงกุฎไทย")?.Id) .OrderBy(x => x.Year).FirstOrDefault() == null ? 0 : p.ProfileInsignia - .Where(x => x.InsigniaId.Value == GetInsigniaByName("ประถมาภรณ์มงกุฎไทย").Id) - .OrderBy(x => x.Year).FirstOrDefault().Year, + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value == GetInsigniaByName("ประถมาภรณ์มงกุฎไทย")?.Id) + .OrderBy(x => x.Year).FirstOrDefault()?.Year, PositionSalaryAmount = p.PositionSalaryAmount, Amount = p.Amount, RootId = p.RootId, @@ -5722,8 +5745,9 @@ namespace BMA.EHR.Application.Repositories && p.ProfileInsignia != null && p.ProfileInsignia.Count > 0 && (p.ProfileInsignia.Where(x => - x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id && - x.InsigniaId.Value == GetInsigniaByName("มหาวชิรมงกุฎ").Id) + x.InsigniaId.HasValue && + x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id && + x.InsigniaId.Value == GetInsigniaByName("มหาวชิรมงกุฎ")?.Id) .ToList() .Count() == 0) select new @@ -5742,25 +5766,25 @@ namespace BMA.EHR.Application.Repositories ProfileDateAppoint = p.DateAppoint.Value, LastInsignia = p.ProfileInsignia == null || p.ProfileInsignia.Count == 0 ? "" : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .FirstOrDefault() == null ? "" : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) - .OrderByDescending(x => x.Year).FirstOrDefault().Insignia, + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) + .OrderByDescending(x => x.Year).FirstOrDefault()?.Insignia, LastInsigniaId = p.ProfileInsignia == null || p.ProfileInsignia.Count == 0 ? Guid.Empty : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .FirstOrDefault() == null ? Guid.Empty : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) - .OrderByDescending(x => x.Year).FirstOrDefault().InsigniaId, + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) + .OrderByDescending(x => x.Year).FirstOrDefault()?.InsigniaId, Salary = p.Amount, SalaryPosition = p.PositionSalaryAmount, SalaryCondition = p.ProfileSalary == null || p.ProfileSalary.Count == 0 ? 0 : - p.ProfileSalary.Where(x => x.Date != null).Where(x => x.Date.Value <= new DateTime(period.Year, 4, 29)) + p.ProfileSalary.Where(x => x.Date != null).Where(x => x.Date.HasValue && x.Date.Value <= new DateTime(period.Year, 4, 29)) .OrderByDescending(x => x.Order).FirstOrDefault() != null ? p.ProfileSalary - .Where(x => x.Date != null).Where(x => x.Date.Value <= new DateTime(period.Year, 4, 29)) - .OrderByDescending(x => x.Order).FirstOrDefault().Amount : + .Where(x => x.Date != null).Where(x => x.Date.HasValue && x.Date.Value <= new DateTime(period.Year, 4, 29)) + .OrderByDescending(x => x.Order).FirstOrDefault()?.Amount : p.Amount, PosNo = p.PosNo, Gender = p.Gender == null ? null : p.Gender, @@ -5770,12 +5794,12 @@ namespace BMA.EHR.Application.Repositories PositionTypeName = p.PosType, IsHigherLevel = p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .FirstOrDefault() == null ? true : IsHigherLevel(p.ProfileInsignia - .Where(x => x.InsigniaId.Value != - GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != + GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .OrderByDescending(x => x.Year) .FirstOrDefault(), "มหาวชิรมงกุฎ"), @@ -5785,14 +5809,14 @@ namespace BMA.EHR.Application.Repositories p.ProfileSalary.Where(x => x.PositionLevel == "อาวุโส").OrderBy(p => p.Date) .FirstOrDefault() == null ? null : p.ProfileSalary.Where(x => x.PositionLevel == "อาวุโส").OrderBy(p => p.Date) - .FirstOrDefault().Date, + .FirstOrDefault()?.Date, FirstRecvInsigniaYear = p.ProfileInsignia.Count == 0 ? 0 : p.ProfileInsignia - .Where(x => x.InsigniaId.Value == GetInsigniaByName("ประถมาภรณ์ช้างเผือก").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value == GetInsigniaByName("ประถมาภรณ์ช้างเผือก")?.Id) .OrderBy(x => x.Year).FirstOrDefault() == null ? 0 : p.ProfileInsignia - .Where(x => x.InsigniaId.Value == GetInsigniaByName("ประถมาภรณ์ช้างเผือก").Id) - .OrderBy(x => x.Year).FirstOrDefault().Year, + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value == GetInsigniaByName("ประถมาภรณ์ช้างเผือก")?.Id) + .OrderBy(x => x.Year).FirstOrDefault()?.Year, PositionSalaryAmount = p.PositionSalaryAmount, Amount = p.Amount, RootId = p.RootId, @@ -5889,8 +5913,9 @@ namespace BMA.EHR.Application.Repositories && p.ProfileInsignia != null && p.ProfileInsignia.Count > 0 && (p.ProfileInsignia.Where(x => - x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id && - x.InsigniaId.Value == GetInsigniaByName("มหาปรมาภรณ์ช้างเผือก").Id) + x.InsigniaId.HasValue && + x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id && + x.InsigniaId.Value == GetInsigniaByName("มหาปรมาภรณ์ช้างเผือก")?.Id) .ToList() .Count() == 0) select new @@ -5909,25 +5934,25 @@ namespace BMA.EHR.Application.Repositories ProfileDateAppoint = p.DateAppoint.Value, LastInsignia = p.ProfileInsignia == null || p.ProfileInsignia.Count == 0 ? "" : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .FirstOrDefault() == null ? "" : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) - .OrderByDescending(x => x.Year).FirstOrDefault().Insignia, + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) + .OrderByDescending(x => x.Year).FirstOrDefault()?.Insignia, LastInsigniaId = p.ProfileInsignia == null || p.ProfileInsignia.Count == 0 ? Guid.Empty : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .FirstOrDefault() == null ? Guid.Empty : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) - .OrderByDescending(x => x.Year).FirstOrDefault().InsigniaId, + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) + .OrderByDescending(x => x.Year).FirstOrDefault()?.InsigniaId, Salary = p.Amount, SalaryPosition = p.PositionSalaryAmount, SalaryCondition = p.ProfileSalary == null || p.ProfileSalary.Count == 0 ? 0 : - p.ProfileSalary.Where(x => x.Date != null).Where(x => x.Date.Value <= new DateTime(period.Year, 4, 29)) + p.ProfileSalary.Where(x => x.Date != null).Where(x => x.Date.HasValue && x.Date.Value <= new DateTime(period.Year, 4, 29)) .OrderByDescending(x => x.Order).FirstOrDefault() != null ? p.ProfileSalary - .Where(x => x.Date != null).Where(x => x.Date.Value <= new DateTime(period.Year, 4, 29)) - .OrderByDescending(x => x.Order).FirstOrDefault().Amount : + .Where(x => x.Date != null).Where(x => x.Date.HasValue && x.Date.Value <= new DateTime(period.Year, 4, 29)) + .OrderByDescending(x => x.Order).FirstOrDefault()?.Amount : p.Amount, PosNo = p.PosNo, Gender = p.Gender == null ? null : p.Gender, @@ -5937,12 +5962,12 @@ namespace BMA.EHR.Application.Repositories PositionTypeName = p.PosType, IsHigherLevel = p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .FirstOrDefault() == null ? true : IsHigherLevel(p.ProfileInsignia - .Where(x => x.InsigniaId.Value != - GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != + GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .OrderByDescending(x => x.Year) .FirstOrDefault(), "มหาปรมาภรณ์ช้างเผือก"), @@ -5952,12 +5977,12 @@ namespace BMA.EHR.Application.Repositories p.ProfileSalary.Where(x => x.PositionLevel == "ทรงคุณวุฒิ").OrderBy(p => p.Date) .FirstOrDefault() == null ? null : p.ProfileSalary.Where(x => x.PositionLevel == "ทรงคุณวุฒิ").OrderBy(p => p.Date) - .FirstOrDefault().Date, + .FirstOrDefault()?.Date, FirstRecvInsigniaYear = p.ProfileInsignia.Count == 0 ? 0 : - p.ProfileInsignia.Where(x => x.InsigniaId.Value == GetInsigniaByName("มหาวชิรมงกุฎ").Id) + p.ProfileInsignia.Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value == GetInsigniaByName("มหาวชิรมงกุฎ")?.Id) .OrderBy(x => x.Year).FirstOrDefault() == null ? 0 : - p.ProfileInsignia.Where(x => x.InsigniaId.Value == GetInsigniaByName("มหาวชิรมงกุฎ").Id) - .OrderBy(x => x.Year).FirstOrDefault().Year, + p.ProfileInsignia.Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value == GetInsigniaByName("มหาวชิรมงกุฎ")?.Id) + .OrderBy(x => x.Year).FirstOrDefault()?.Year, PositionSalaryAmount = p.PositionSalaryAmount, Amount = p.Amount, RootId = p.RootId, @@ -6134,8 +6159,9 @@ namespace BMA.EHR.Application.Repositories && p.ProfileInsignia != null && p.ProfileInsignia.Count > 0 && (p.ProfileInsignia.Where(x => - x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id && - x.InsigniaId.Value == GetInsigniaByName("ทวีติยาภรณ์ช้างเผือก").Id) + x.InsigniaId.HasValue && + x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id && + x.InsigniaId.Value == GetInsigniaByName("ทวีติยาภรณ์ช้างเผือก")?.Id) .ToList() .Count() == 0) select new @@ -6154,25 +6180,25 @@ namespace BMA.EHR.Application.Repositories ProfileDateAppoint = p.DateAppoint.Value, LastInsignia = p.ProfileInsignia == null || p.ProfileInsignia.Count == 0 ? "" : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .FirstOrDefault() == null ? "" : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) - .OrderByDescending(x => x.Year).FirstOrDefault().Insignia, + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) + .OrderByDescending(x => x.Year).FirstOrDefault()?.Insignia, LastInsigniaId = p.ProfileInsignia == null || p.ProfileInsignia.Count == 0 ? Guid.Empty : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .FirstOrDefault() == null ? Guid.Empty : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) - .OrderByDescending(x => x.Year).FirstOrDefault().InsigniaId, + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) + .OrderByDescending(x => x.Year).FirstOrDefault()?.InsigniaId, Salary = p.Amount, SalaryPosition = p.PositionSalaryAmount, SalaryCondition = p.ProfileSalary == null || p.ProfileSalary.Count == 0 ? 0 : - p.ProfileSalary.Where(x => x.Date != null).Where(x => x.Date.Value <= new DateTime(period.Year, 4, 29)) + p.ProfileSalary.Where(x => x.Date != null).Where(x => x.Date.HasValue && x.Date.Value <= new DateTime(period.Year, 4, 29)) .OrderByDescending(x => x.Order).FirstOrDefault() != null ? p.ProfileSalary - .Where(x => x.Date != null).Where(x => x.Date.Value <= new DateTime(period.Year, 4, 29)) - .OrderByDescending(x => x.Order).FirstOrDefault().Amount : + .Where(x => x.Date != null).Where(x => x.Date.HasValue && x.Date.Value <= new DateTime(period.Year, 4, 29)) + .OrderByDescending(x => x.Order).FirstOrDefault()?.Amount : p.Amount, PosNo = p.PosNo == null ? "" : p.PosNo, Gender = p.Gender == null ? "" : p.Gender, @@ -6182,12 +6208,12 @@ namespace BMA.EHR.Application.Repositories PositionTypeName = p.PosType, IsHigherLevel = p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .FirstOrDefault() == null ? true : IsHigherLevel(p.ProfileInsignia - .Where(x => x.InsigniaId.Value != - GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != + GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .OrderByDescending(x => x.Year) .FirstOrDefault(), "ทวีติยาภรณ์ช้างเผือก"), @@ -6197,12 +6223,12 @@ namespace BMA.EHR.Application.Repositories p.ProfileSalary.Where(x => x.PositionLevel == "ต้น" && x.PositionType == "อำนวยการ") .OrderBy(p => p.Date).FirstOrDefault() == null ? null : p.ProfileSalary.Where(x => x.PositionLevel == "ต้น" && x.PositionType == "อำนวยการ") - .OrderBy(p => p.Date).FirstOrDefault().Date, + .OrderBy(p => p.Date).FirstOrDefault()?.Date, FirstRecvInsigniaYear = p.ProfileInsignia.Count == 0 ? 0 : - p.ProfileInsignia.Where(x => x.InsigniaId.Value == GetInsigniaByName("มหาวชิรมงกุฎ").Id) + p.ProfileInsignia.Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value == GetInsigniaByName("มหาวชิรมงกุฎ")?.Id) .OrderBy(x => x.Year).FirstOrDefault() == null ? 0 : - p.ProfileInsignia.Where(x => x.InsigniaId.Value == GetInsigniaByName("มหาวชิรมงกุฎ").Id) - .OrderBy(x => x.Year).FirstOrDefault().Year, + p.ProfileInsignia.Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value == GetInsigniaByName("มหาวชิรมงกุฎ")?.Id) + .OrderBy(x => x.Year).FirstOrDefault()?.Year, PositionSalaryAmount = p.PositionSalaryAmount, Amount = p.Amount, RootId = p.RootId, @@ -6291,8 +6317,9 @@ namespace BMA.EHR.Application.Repositories && p.ProfileInsignia != null && p.ProfileInsignia.Count > 0 && (p.ProfileInsignia.Where(x => - x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id && - x.InsigniaId.Value == GetInsigniaByName("ประถมาภรณ์มงกุฎไทย").Id) + x.InsigniaId.HasValue && + x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id && + x.InsigniaId.Value == GetInsigniaByName("ประถมาภรณ์มงกุฎไทย")?.Id) .ToList() .Count() == 0) select new @@ -6311,25 +6338,25 @@ namespace BMA.EHR.Application.Repositories ProfileDateAppoint = p.DateAppoint.Value, LastInsignia = p.ProfileInsignia == null || p.ProfileInsignia.Count == 0 ? "" : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .FirstOrDefault() == null ? "" : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) - .OrderByDescending(x => x.Year).FirstOrDefault().Insignia, + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) + .OrderByDescending(x => x.Year).FirstOrDefault()?.Insignia, LastInsigniaId = p.ProfileInsignia == null || p.ProfileInsignia.Count == 0 ? Guid.Empty : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .FirstOrDefault() == null ? Guid.Empty : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) - .OrderByDescending(x => x.Year).FirstOrDefault().InsigniaId, + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) + .OrderByDescending(x => x.Year).FirstOrDefault()?.InsigniaId, Salary = p.Amount, SalaryPosition = p.PositionSalaryAmount, SalaryCondition = p.ProfileSalary == null || p.ProfileSalary.Count == 0 ? 0 : - p.ProfileSalary.Where(x => x.Date != null).Where(x => x.Date.Value <= new DateTime(period.Year, 4, 29)) + p.ProfileSalary.Where(x => x.Date != null).Where(x => x.Date.HasValue && x.Date.Value <= new DateTime(period.Year, 4, 29)) .OrderByDescending(x => x.Order).FirstOrDefault() != null ? p.ProfileSalary - .Where(x => x.Date != null).Where(x => x.Date.Value <= new DateTime(period.Year, 4, 29)) - .OrderByDescending(x => x.Order).FirstOrDefault().Amount : + .Where(x => x.Date != null).Where(x => x.Date.HasValue && x.Date.Value <= new DateTime(period.Year, 4, 29)) + .OrderByDescending(x => x.Order).FirstOrDefault()?.Amount : p.Amount, PosNo = p.PosNo, Gender = p.Gender == null ? "" : p.Gender, @@ -6339,12 +6366,12 @@ namespace BMA.EHR.Application.Repositories PositionTypeName = p.PosType, IsHigherLevel = p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .FirstOrDefault() == null ? true : IsHigherLevel(p.ProfileInsignia - .Where(x => x.InsigniaId.Value != - GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != + GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .OrderByDescending(x => x.Year) .FirstOrDefault(), "ประถมาภรณ์มงกุฎไทย"), @@ -6354,14 +6381,14 @@ namespace BMA.EHR.Application.Repositories p.ProfileSalary.Where(x => x.PositionLevel == "ต้น" && x.PositionType == "อำนวยการ") .OrderBy(p => p.Date).FirstOrDefault() == null ? null : p.ProfileSalary.Where(x => x.PositionLevel == "ต้น" && x.PositionType == "อำนวยการ") - .OrderBy(p => p.Date).FirstOrDefault().Date, + .OrderBy(p => p.Date).FirstOrDefault()?.Date, FirstRecvInsigniaYear = p.ProfileInsignia.Count == 0 ? 0 : p.ProfileInsignia - .Where(x => x.InsigniaId.Value == GetInsigniaByName("ทวีติยาภรณ์ช้างเผือก").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value == GetInsigniaByName("ทวีติยาภรณ์ช้างเผือก")?.Id) .OrderBy(x => x.Year).FirstOrDefault() == null ? 0 : p.ProfileInsignia - .Where(x => x.InsigniaId.Value == GetInsigniaByName("ทวีติยาภรณ์ช้างเผือก").Id) - .OrderBy(x => x.Year).FirstOrDefault().Year, + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value == GetInsigniaByName("ทวีติยาภรณ์ช้างเผือก")?.Id) + .OrderBy(x => x.Year).FirstOrDefault()?.Year, PositionSalaryAmount = p.PositionSalaryAmount, Amount = p.Amount, RootId = p.RootId, @@ -6519,8 +6546,9 @@ namespace BMA.EHR.Application.Repositories && p.ProfileInsignia != null && p.ProfileInsignia.Count > 0 && (p.ProfileInsignia.Where(x => - x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id && - x.InsigniaId.Value == GetInsigniaByName("ประถมาภรณ์มงกุฎไทย").Id) + x.InsigniaId.HasValue && + x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id && + x.InsigniaId.Value == GetInsigniaByName("ประถมาภรณ์มงกุฎไทย")?.Id) .ToList() .Count() == 0) select new @@ -6539,25 +6567,25 @@ namespace BMA.EHR.Application.Repositories ProfileDateAppoint = p.DateAppoint.Value, LastInsignia = p.ProfileInsignia == null || p.ProfileInsignia.Count == 0 ? "" : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .FirstOrDefault() == null ? "" : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) - .OrderByDescending(x => x.Year).FirstOrDefault().Insignia, + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) + .OrderByDescending(x => x.Year).FirstOrDefault()?.Insignia, LastInsigniaId = p.ProfileInsignia == null || p.ProfileInsignia.Count == 0 ? Guid.Empty : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .FirstOrDefault() == null ? Guid.Empty : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) - .OrderByDescending(x => x.Year).FirstOrDefault().InsigniaId, + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) + .OrderByDescending(x => x.Year).FirstOrDefault()?.InsigniaId, Salary = p.Amount, SalaryPosition = p.PositionSalaryAmount, SalaryCondition = p.ProfileSalary == null || p.ProfileSalary.Count == 0 ? 0 : - p.ProfileSalary.Where(x => x.Date != null).Where(x => x.Date.Value <= new DateTime(period.Year, 4, 29)) + p.ProfileSalary.Where(x => x.Date != null).Where(x => x.Date.HasValue && x.Date.Value <= new DateTime(period.Year, 4, 29)) .OrderByDescending(x => x.Order).FirstOrDefault() != null ? p.ProfileSalary - .Where(x => x.Date != null).Where(x => x.Date.Value <= new DateTime(period.Year, 4, 29)) - .OrderByDescending(x => x.Order).FirstOrDefault().Amount : + .Where(x => x.Date != null).Where(x => x.Date.HasValue && x.Date.Value <= new DateTime(period.Year, 4, 29)) + .OrderByDescending(x => x.Order).FirstOrDefault()?.Amount : p.Amount, PosNo = p.PosNo, Gender = p.Gender == null ? "" : p.Gender, @@ -6567,12 +6595,12 @@ namespace BMA.EHR.Application.Repositories PositionTypeName = p.PosType, IsHigherLevel = p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .FirstOrDefault() == null ? true : IsHigherLevel(p.ProfileInsignia - .Where(x => x.InsigniaId.Value != - GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != + GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .OrderByDescending(x => x.Year) .FirstOrDefault(), "ประถมาภรณ์มงกุฎไทย"), @@ -6582,14 +6610,14 @@ namespace BMA.EHR.Application.Repositories p.ProfileSalary.Where(x => x.PositionLevel == "สูง" && x.PositionType == "อำนวยการ") .OrderBy(p => p.Date).FirstOrDefault() == null ? null : p.ProfileSalary.Where(x => x.PositionLevel == "สูง" && x.PositionType == "อำนวยการ") - .OrderBy(p => p.Date).FirstOrDefault().Date, + .OrderBy(p => p.Date).FirstOrDefault()?.Date, FirstRecvInsigniaYear = p.ProfileInsignia.Count == 0 ? 0 : p.ProfileInsignia - .Where(x => x.InsigniaId.Value == GetInsigniaByName("ทวีติยาภรณ์ช้างเผือก").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value == GetInsigniaByName("ทวีติยาภรณ์ช้างเผือก")?.Id) .OrderBy(x => x.Year).FirstOrDefault() == null ? 0 : p.ProfileInsignia - .Where(x => x.InsigniaId.Value == GetInsigniaByName("ทวีติยาภรณ์ช้างเผือก").Id) - .OrderBy(x => x.Year).FirstOrDefault().Year, + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value == GetInsigniaByName("ทวีติยาภรณ์ช้างเผือก")?.Id) + .OrderBy(x => x.Year).FirstOrDefault()?.Year, PositionSalaryAmount = p.PositionSalaryAmount, Amount = p.Amount, RootId = p.RootId, @@ -6680,8 +6708,9 @@ namespace BMA.EHR.Application.Repositories && p.ProfileInsignia != null && p.ProfileInsignia.Count > 0 && (p.ProfileInsignia.Where(x => - x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id && - x.InsigniaId.Value == GetInsigniaByName("ประถมาภรณ์ช้างเผือก").Id) + x.InsigniaId.HasValue && + x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id && + x.InsigniaId.Value == GetInsigniaByName("ประถมาภรณ์ช้างเผือก")?.Id) .ToList() .Count() == 0) select new @@ -6700,25 +6729,25 @@ namespace BMA.EHR.Application.Repositories ProfileDateAppoint = p.DateAppoint.Value, LastInsignia = p.ProfileInsignia == null || p.ProfileInsignia.Count == 0 ? "" : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .FirstOrDefault() == null ? "" : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) - .OrderByDescending(x => x.Year).FirstOrDefault().Insignia, + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) + .OrderByDescending(x => x.Year).FirstOrDefault()?.Insignia, LastInsigniaId = p.ProfileInsignia == null || p.ProfileInsignia.Count == 0 ? Guid.Empty : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .FirstOrDefault() == null ? Guid.Empty : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) - .OrderByDescending(x => x.Year).FirstOrDefault().InsigniaId, + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) + .OrderByDescending(x => x.Year).FirstOrDefault()?.InsigniaId, Salary = p.Amount, SalaryPosition = p.PositionSalaryAmount, SalaryCondition = p.ProfileSalary == null || p.ProfileSalary.Count == 0 ? 0 : - p.ProfileSalary.Where(x => x.Date != null).Where(x => x.Date.Value <= new DateTime(period.Year, 4, 29)) + p.ProfileSalary.Where(x => x.Date != null).Where(x => x.Date.HasValue && x.Date.Value <= new DateTime(period.Year, 4, 29)) .OrderByDescending(x => x.Order).FirstOrDefault() != null ? p.ProfileSalary - .Where(x => x.Date != null).Where(x => x.Date.Value <= new DateTime(period.Year, 4, 29)) - .OrderByDescending(x => x.Order).FirstOrDefault().Amount : + .Where(x => x.Date != null).Where(x => x.Date.HasValue && x.Date.Value <= new DateTime(period.Year, 4, 29)) + .OrderByDescending(x => x.Order).FirstOrDefault()?.Amount : p.Amount, PosNo = p.PosNo, Gender = p.Gender == null ? "" : p.Gender, @@ -6728,12 +6757,12 @@ namespace BMA.EHR.Application.Repositories PositionTypeName = p.PosType, IsHigherLevel = p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .FirstOrDefault() == null ? true : IsHigherLevel(p.ProfileInsignia - .Where(x => x.InsigniaId.Value != - GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != + GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .OrderByDescending(x => x.Year) .FirstOrDefault(), "ประถมาภรณ์ช้างเผือก"), @@ -6743,14 +6772,14 @@ namespace BMA.EHR.Application.Repositories p.ProfileSalary.Where(x => x.PositionLevel == "สูง" && x.PositionType == "อำนวยการ") .OrderBy(p => p.Date).FirstOrDefault() == null ? null : p.ProfileSalary.Where(x => x.PositionLevel == "สูง" && x.PositionType == "อำนวยการ") - .OrderBy(p => p.Date).FirstOrDefault().Date, + .OrderBy(p => p.Date).FirstOrDefault()?.Date, FirstRecvInsigniaYear = p.ProfileInsignia.Count == 0 ? 0 : p.ProfileInsignia - .Where(x => x.InsigniaId.Value == GetInsigniaByName("ประถมาภรณ์มงกุฎไทย").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value == GetInsigniaByName("ประถมาภรณ์มงกุฎไทย")?.Id) .OrderBy(x => x.Year).FirstOrDefault() == null ? 0 : p.ProfileInsignia - .Where(x => x.InsigniaId.Value == GetInsigniaByName("ประถมาภรณ์มงกุฎไทย").Id) - .OrderBy(x => x.Year).FirstOrDefault().Year, + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value == GetInsigniaByName("ประถมาภรณ์มงกุฎไทย")?.Id) + .OrderBy(x => x.Year).FirstOrDefault()?.Year, PositionSalaryAmount = p.PositionSalaryAmount, Amount = p.Amount, RootId = p.RootId, @@ -6842,8 +6871,9 @@ namespace BMA.EHR.Application.Repositories && p.ProfileInsignia != null && p.ProfileInsignia.Count > 0 && (p.ProfileInsignia.Where(x => - x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id && - x.InsigniaId.Value == GetInsigniaByName("มหาวชิรมงกุฎ").Id) + x.InsigniaId.HasValue && + x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id && + x.InsigniaId.Value == GetInsigniaByName("มหาวชิรมงกุฎ")?.Id) .ToList() .Count() == 0) select new @@ -6862,25 +6892,25 @@ namespace BMA.EHR.Application.Repositories ProfileDateAppoint = p.DateAppoint.Value, LastInsignia = p.ProfileInsignia == null || p.ProfileInsignia.Count == 0 ? "" : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .FirstOrDefault() == null ? "" : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) - .OrderByDescending(x => x.Year).FirstOrDefault().Insignia, + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) + .OrderByDescending(x => x.Year).FirstOrDefault()?.Insignia, LastInsigniaId = p.ProfileInsignia == null || p.ProfileInsignia.Count == 0 ? Guid.Empty : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .FirstOrDefault() == null ? Guid.Empty : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) - .OrderByDescending(x => x.Year).FirstOrDefault().InsigniaId, + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) + .OrderByDescending(x => x.Year).FirstOrDefault()?.InsigniaId, Salary = p.Amount, SalaryPosition = p.PositionSalaryAmount, SalaryCondition = p.ProfileSalary == null || p.ProfileSalary.Count == 0 ? 0 : - p.ProfileSalary.Where(x => x.Date != null).Where(x => x.Date.Value <= new DateTime(period.Year, 4, 29)) + p.ProfileSalary.Where(x => x.Date != null).Where(x => x.Date.HasValue && x.Date.Value <= new DateTime(period.Year, 4, 29)) .OrderByDescending(x => x.Order).FirstOrDefault() != null ? p.ProfileSalary - .Where(x => x.Date != null).Where(x => x.Date.Value <= new DateTime(period.Year, 4, 29)) - .OrderByDescending(x => x.Order).FirstOrDefault().Amount : + .Where(x => x.Date != null).Where(x => x.Date.HasValue && x.Date.Value <= new DateTime(period.Year, 4, 29)) + .OrderByDescending(x => x.Order).FirstOrDefault()?.Amount : p.Amount, PosNo = p.PosNo, Gender = p.Gender == null ? "" : p.Gender, @@ -6890,12 +6920,12 @@ namespace BMA.EHR.Application.Repositories PositionTypeName = p.PosType, IsHigherLevel = p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .FirstOrDefault() == null ? true : IsHigherLevel(p.ProfileInsignia - .Where(x => x.InsigniaId.Value != - GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != + GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .OrderByDescending(x => x.Year) .FirstOrDefault(), "มหาวชิรมงกุฎ"), @@ -6905,14 +6935,14 @@ namespace BMA.EHR.Application.Repositories p.ProfileSalary.Where(x => x.PositionLevel == "สูง" && x.PositionType == "อำนวยการ") .OrderBy(p => p.Date).FirstOrDefault() == null ? null : p.ProfileSalary.Where(x => x.PositionLevel == "สูง" && x.PositionType == "อำนวยการ") - .OrderBy(p => p.Date).FirstOrDefault().Date, + .OrderBy(p => p.Date).FirstOrDefault()?.Date, FirstRecvInsigniaYear = p.ProfileInsignia.Count == 0 ? 0 : p.ProfileInsignia - .Where(x => x.InsigniaId.Value == GetInsigniaByName("ประถมาภรณ์ช้างเผือก").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value == GetInsigniaByName("ประถมาภรณ์ช้างเผือก")?.Id) .OrderBy(x => x.Year).FirstOrDefault() == null ? 0 : p.ProfileInsignia - .Where(x => x.InsigniaId.Value == GetInsigniaByName("ประถมาภรณ์ช้างเผือก").Id) - .OrderBy(x => x.Year).FirstOrDefault().Year, + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value == GetInsigniaByName("ประถมาภรณ์ช้างเผือก")?.Id) + .OrderBy(x => x.Year).FirstOrDefault()?.Year, PositionSalaryAmount = p.PositionSalaryAmount, Amount = p.Amount, RootId = p.RootId, @@ -7083,8 +7113,9 @@ namespace BMA.EHR.Application.Repositories && p.ProfileInsignia != null && p.ProfileInsignia.Count > 0 && (p.ProfileInsignia.Where(x => - x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id && - x.InsigniaId.Value == GetInsigniaByName("ประถมาภรณ์มงกุฎไทย").Id) + x.InsigniaId.HasValue && + x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id && + x.InsigniaId.Value == GetInsigniaByName("ประถมาภรณ์มงกุฎไทย")?.Id) .ToList() .Count() == 0) select new @@ -7103,25 +7134,25 @@ namespace BMA.EHR.Application.Repositories ProfileDateAppoint = p.DateAppoint.Value, LastInsignia = p.ProfileInsignia == null || p.ProfileInsignia.Count == 0 ? "" : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .FirstOrDefault() == null ? "" : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) - .OrderByDescending(x => x.Year).FirstOrDefault().Insignia, + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) + .OrderByDescending(x => x.Year).FirstOrDefault()?.Insignia, LastInsigniaId = p.ProfileInsignia == null || p.ProfileInsignia.Count == 0 ? Guid.Empty : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .FirstOrDefault() == null ? Guid.Empty : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) - .OrderByDescending(x => x.Year).FirstOrDefault().InsigniaId, + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) + .OrderByDescending(x => x.Year).FirstOrDefault()?.InsigniaId, Salary = p.Amount, SalaryPosition = p.PositionSalaryAmount, SalaryCondition = p.ProfileSalary == null || p.ProfileSalary.Count == 0 ? 0 : - p.ProfileSalary.Where(x => x.Date != null).Where(x => x.Date.Value <= new DateTime(period.Year, 4, 29)) + p.ProfileSalary.Where(x => x.Date != null).Where(x => x.Date.HasValue && x.Date.Value <= new DateTime(period.Year, 4, 29)) .OrderByDescending(x => x.Order).FirstOrDefault() != null ? p.ProfileSalary - .Where(x => x.Date != null).Where(x => x.Date.Value <= new DateTime(period.Year, 4, 29)) - .OrderByDescending(x => x.Order).FirstOrDefault().Amount : + .Where(x => x.Date != null).Where(x => x.Date.HasValue && x.Date.Value <= new DateTime(period.Year, 4, 29)) + .OrderByDescending(x => x.Order).FirstOrDefault()?.Amount : p.Amount, PosNo = p.PosNo, Gender = p.Gender == null ? null : p.Gender, @@ -7131,12 +7162,12 @@ namespace BMA.EHR.Application.Repositories PositionTypeName = p.PosType, IsHigherLevel = p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .FirstOrDefault() == null ? true : IsHigherLevel(p.ProfileInsignia - .Where(x => x.InsigniaId.Value != - GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != + GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .OrderByDescending(x => x.Year) .FirstOrDefault(), "ประถมาภรณ์มงกุฎไทย"), @@ -7146,14 +7177,14 @@ namespace BMA.EHR.Application.Repositories p.ProfileSalary.Where(x => x.PositionLevel == "ต้น" && x.PositionType == "บริหาร") .OrderBy(p => p.Date).FirstOrDefault() == null ? null : p.ProfileSalary.Where(x => x.PositionLevel == "ต้น" && x.PositionType == "บริหาร") - .OrderBy(p => p.Date).FirstOrDefault().Date, + .OrderBy(p => p.Date).FirstOrDefault()?.Date, FirstRecvInsigniaYear = p.ProfileInsignia.Count == 0 ? 0 : p.ProfileInsignia - .Where(x => x.InsigniaId.Value == GetInsigniaByName("ทวีติยาภรณ์ช้างเผือก").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value == GetInsigniaByName("ทวีติยาภรณ์ช้างเผือก")?.Id) .OrderBy(x => x.Year).FirstOrDefault() == null ? 0 : p.ProfileInsignia - .Where(x => x.InsigniaId.Value == GetInsigniaByName("ทวีติยาภรณ์ช้างเผือก").Id) - .OrderBy(x => x.Year).FirstOrDefault().Year, + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value == GetInsigniaByName("ทวีติยาภรณ์ช้างเผือก")?.Id) + .OrderBy(x => x.Year).FirstOrDefault()?.Year, PositionSalaryAmount = p.PositionSalaryAmount, Amount = p.Amount, RootId = p.RootId, @@ -7244,8 +7275,9 @@ namespace BMA.EHR.Application.Repositories && p.ProfileInsignia != null && p.ProfileInsignia.Count > 0 && (p.ProfileInsignia.Where(x => - x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id && - x.InsigniaId.Value == GetInsigniaByName("ประถมาภรณ์ช้างเผือก").Id) + x.InsigniaId.HasValue && + x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id && + x.InsigniaId.Value == GetInsigniaByName("ประถมาภรณ์ช้างเผือก")?.Id) .ToList() .Count() == 0) select new @@ -7264,25 +7296,25 @@ namespace BMA.EHR.Application.Repositories ProfileDateAppoint = p.DateAppoint.Value, LastInsignia = p.ProfileInsignia == null || p.ProfileInsignia.Count == 0 ? "" : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .FirstOrDefault() == null ? "" : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) - .OrderByDescending(x => x.Year).FirstOrDefault().Insignia, + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) + .OrderByDescending(x => x.Year).FirstOrDefault()?.Insignia, LastInsigniaId = p.ProfileInsignia == null || p.ProfileInsignia.Count == 0 ? Guid.Empty : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .FirstOrDefault() == null ? Guid.Empty : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) - .OrderByDescending(x => x.Year).FirstOrDefault().InsigniaId, + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) + .OrderByDescending(x => x.Year).FirstOrDefault()?.InsigniaId, Salary = p.Amount, SalaryPosition = p.PositionSalaryAmount, SalaryCondition = p.ProfileSalary == null || p.ProfileSalary.Count == 0 ? 0 : - p.ProfileSalary.Where(x => x.Date != null).Where(x => x.Date.Value <= new DateTime(period.Year, 4, 29)) + p.ProfileSalary.Where(x => x.Date != null).Where(x => x.Date.HasValue && x.Date.Value <= new DateTime(period.Year, 4, 29)) .OrderByDescending(x => x.Order).FirstOrDefault() != null ? p.ProfileSalary - .Where(x => x.Date != null).Where(x => x.Date.Value <= new DateTime(period.Year, 4, 29)) - .OrderByDescending(x => x.Order).FirstOrDefault().Amount : + .Where(x => x.Date != null).Where(x => x.Date.HasValue && x.Date.Value <= new DateTime(period.Year, 4, 29)) + .OrderByDescending(x => x.Order).FirstOrDefault()?.Amount : p.Amount, PosNo = p.PosNo, Gender = p.Gender == null ? null : p.Gender, @@ -7292,12 +7324,12 @@ namespace BMA.EHR.Application.Repositories PositionTypeName = p.PosType, IsHigherLevel = p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .FirstOrDefault() == null ? true : IsHigherLevel(p.ProfileInsignia - .Where(x => x.InsigniaId.Value != - GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != + GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .OrderByDescending(x => x.Year) .FirstOrDefault(), "ประถมาภรณ์ช้างเผือก"), @@ -7307,14 +7339,14 @@ namespace BMA.EHR.Application.Repositories p.ProfileSalary.Where(x => x.PositionLevel == "ต้น" && x.PositionType == "บริหาร") .OrderBy(p => p.Date).FirstOrDefault() == null ? null : p.ProfileSalary.Where(x => x.PositionLevel == "ต้น" && x.PositionType == "บริหาร") - .OrderBy(p => p.Date).FirstOrDefault().Date, + .OrderBy(p => p.Date).FirstOrDefault()?.Date, FirstRecvInsigniaYear = p.ProfileInsignia.Count == 0 ? 0 : p.ProfileInsignia - .Where(x => x.InsigniaId.Value == GetInsigniaByName("ประถมาภรณ์มงกุฎไทย").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value == GetInsigniaByName("ประถมาภรณ์มงกุฎไทย")?.Id) .OrderBy(x => x.Year).FirstOrDefault() == null ? 0 : p.ProfileInsignia - .Where(x => x.InsigniaId.Value == GetInsigniaByName("ประถมาภรณ์มงกุฎไทย").Id) - .OrderBy(x => x.Year).FirstOrDefault().Year, + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value == GetInsigniaByName("ประถมาภรณ์มงกุฎไทย")?.Id) + .OrderBy(x => x.Year).FirstOrDefault()?.Year, PositionSalaryAmount = p.PositionSalaryAmount, Amount = p.Amount, RootId = p.RootId, @@ -7405,8 +7437,9 @@ namespace BMA.EHR.Application.Repositories && p.ProfileInsignia != null && p.ProfileInsignia.Count > 0 && (p.ProfileInsignia.Where(x => - x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id && - x.InsigniaId.Value == GetInsigniaByName("มหาวชิรมงกุฎ").Id) + x.InsigniaId.HasValue && + x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id && + x.InsigniaId.Value == GetInsigniaByName("มหาวชิรมงกุฎ")?.Id) .ToList() .Count() == 0) select new @@ -7425,25 +7458,25 @@ namespace BMA.EHR.Application.Repositories ProfileDateAppoint = p.DateAppoint.Value, LastInsignia = p.ProfileInsignia == null || p.ProfileInsignia.Count == 0 ? "" : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .FirstOrDefault() == null ? "" : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) - .OrderByDescending(x => x.Year).FirstOrDefault().Insignia, + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) + .OrderByDescending(x => x.Year).FirstOrDefault()?.Insignia, LastInsigniaId = p.ProfileInsignia == null || p.ProfileInsignia.Count == 0 ? Guid.Empty : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .FirstOrDefault() == null ? Guid.Empty : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) - .OrderByDescending(x => x.Year).FirstOrDefault().InsigniaId, + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) + .OrderByDescending(x => x.Year).FirstOrDefault()?.InsigniaId, Salary = p.Amount, SalaryPosition = p.PositionSalaryAmount, SalaryCondition = p.ProfileSalary == null || p.ProfileSalary.Count == 0 ? 0 : - p.ProfileSalary.Where(x => x.Date != null).Where(x => x.Date.Value <= new DateTime(period.Year, 4, 29)) + p.ProfileSalary.Where(x => x.Date != null).Where(x => x.Date.HasValue && x.Date.Value <= new DateTime(period.Year, 4, 29)) .OrderByDescending(x => x.Order).FirstOrDefault() != null ? p.ProfileSalary - .Where(x => x.Date != null).Where(x => x.Date.Value <= new DateTime(period.Year, 4, 29)) - .OrderByDescending(x => x.Order).FirstOrDefault().Amount : + .Where(x => x.Date != null).Where(x => x.Date.HasValue && x.Date.Value <= new DateTime(period.Year, 4, 29)) + .OrderByDescending(x => x.Order).FirstOrDefault()?.Amount : p.Amount, PosNo = p.PosNo, Gender = p.Gender == null ? null : p.Gender, @@ -7453,12 +7486,12 @@ namespace BMA.EHR.Application.Repositories PositionTypeName = p.PosType, IsHigherLevel = p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .FirstOrDefault() == null ? true : IsHigherLevel(p.ProfileInsignia - .Where(x => x.InsigniaId.Value != - GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != + GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .OrderByDescending(x => x.Year) .FirstOrDefault(), "มหาวชิรมงกุฎ"), @@ -7468,14 +7501,14 @@ namespace BMA.EHR.Application.Repositories p.ProfileSalary.Where(x => x.PositionLevel == "ต้น" && x.PositionType == "บริหาร") .OrderBy(p => p.Date).FirstOrDefault() == null ? null : p.ProfileSalary.Where(x => x.PositionLevel == "ต้น" && x.PositionType == "บริหาร") - .OrderBy(p => p.Date).FirstOrDefault().Date, + .OrderBy(p => p.Date).FirstOrDefault()?.Date, FirstRecvInsigniaYear = p.ProfileInsignia.Count == 0 ? 0 : p.ProfileInsignia - .Where(x => x.InsigniaId.Value == GetInsigniaByName("ประถมาภรณ์ช้างเผือก").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value == GetInsigniaByName("ประถมาภรณ์ช้างเผือก")?.Id) .OrderBy(x => x.Year).FirstOrDefault() == null ? 0 : p.ProfileInsignia - .Where(x => x.InsigniaId.Value == GetInsigniaByName("ประถมาภรณ์ช้างเผือก").Id) - .OrderBy(x => x.Year).FirstOrDefault().Year, + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value == GetInsigniaByName("ประถมาภรณ์ช้างเผือก")?.Id) + .OrderBy(x => x.Year).FirstOrDefault()?.Year, PositionSalaryAmount = p.PositionSalaryAmount, Amount = p.Amount, RootId = p.RootId, @@ -7617,8 +7650,9 @@ namespace BMA.EHR.Application.Repositories && p.ProfileInsignia != null && p.ProfileInsignia.Count > 0 && (p.ProfileInsignia.Where(x => - x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id && - x.InsigniaId.Value == GetInsigniaByName("ประถมาภรณ์ช้างเผือก").Id) + x.InsigniaId.HasValue && + x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id && + x.InsigniaId.Value == GetInsigniaByName("ประถมาภรณ์ช้างเผือก")?.Id) .ToList() .Count() == 0) select new @@ -7637,25 +7671,25 @@ namespace BMA.EHR.Application.Repositories ProfileDateAppoint = p.DateAppoint.Value, LastInsignia = p.ProfileInsignia == null || p.ProfileInsignia.Count == 0 ? "" : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .FirstOrDefault() == null ? "" : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) - .OrderByDescending(x => x.Year).FirstOrDefault().Insignia, + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) + .OrderByDescending(x => x.Year).FirstOrDefault()?.Insignia, LastInsigniaId = p.ProfileInsignia == null || p.ProfileInsignia.Count == 0 ? Guid.Empty : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .FirstOrDefault() == null ? Guid.Empty : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) - .OrderByDescending(x => x.Year).FirstOrDefault().InsigniaId, + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) + .OrderByDescending(x => x.Year).FirstOrDefault()?.InsigniaId, Salary = p.Amount, SalaryPosition = p.PositionSalaryAmount, SalaryCondition = p.ProfileSalary == null || p.ProfileSalary.Count == 0 ? 0 : - p.ProfileSalary.Where(x => x.Date != null).Where(x => x.Date.Value <= new DateTime(period.Year, 4, 29)) + p.ProfileSalary.Where(x => x.Date != null).Where(x => x.Date.HasValue && x.Date.Value <= new DateTime(period.Year, 4, 29)) .OrderByDescending(x => x.Order).FirstOrDefault() != null ? p.ProfileSalary - .Where(x => x.Date != null).Where(x => x.Date.Value <= new DateTime(period.Year, 4, 29)) - .OrderByDescending(x => x.Order).FirstOrDefault().Amount : + .Where(x => x.Date != null).Where(x => x.Date.HasValue && x.Date.Value <= new DateTime(period.Year, 4, 29)) + .OrderByDescending(x => x.Order).FirstOrDefault()?.Amount : p.Amount, PosNo = p.PosNo, Gender = p.Gender == null ? null : p.Gender, @@ -7665,12 +7699,12 @@ namespace BMA.EHR.Application.Repositories PositionTypeName = p.PosType, IsHigherLevel = p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .FirstOrDefault() == null ? true : IsHigherLevel(p.ProfileInsignia - .Where(x => x.InsigniaId.Value != - GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != + GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .OrderByDescending(x => x.Year) .FirstOrDefault(), "ประถมาภรณ์ช้างเผือก"), @@ -7680,14 +7714,14 @@ namespace BMA.EHR.Application.Repositories p.ProfileSalary.Where(x => x.PositionLevel == "สูง" && x.PositionType == "บริหาร") .OrderBy(p => p.Date).FirstOrDefault() == null ? null : p.ProfileSalary.Where(x => x.PositionLevel == "สูง" && x.PositionType == "บริหาร") - .OrderBy(p => p.Date).FirstOrDefault().Date, + .OrderBy(p => p.Date).FirstOrDefault()?.Date, FirstRecvInsigniaYear = p.ProfileInsignia.Count == 0 ? 0 : p.ProfileInsignia - .Where(x => x.InsigniaId.Value == GetInsigniaByName("ประถมาภรณ์มงกุฎไทย").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value == GetInsigniaByName("ประถมาภรณ์มงกุฎไทย")?.Id) .OrderBy(x => x.Year).FirstOrDefault() == null ? 0 : p.ProfileInsignia - .Where(x => x.InsigniaId.Value == GetInsigniaByName("ประถมาภรณ์มงกุฎไทย").Id) - .OrderBy(x => x.Year).FirstOrDefault().Year, + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value == GetInsigniaByName("ประถมาภรณ์มงกุฎไทย")?.Id) + .OrderBy(x => x.Year).FirstOrDefault()?.Year, PositionSalaryAmount = p.PositionSalaryAmount, Amount = p.Amount, RootId = p.RootId, @@ -7784,8 +7818,9 @@ namespace BMA.EHR.Application.Repositories && p.ProfileInsignia != null && p.ProfileInsignia.Count > 0 && (p.ProfileInsignia.Where(x => - x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id && - x.InsigniaId.Value == GetInsigniaByName("มหาวชิรมงกุฎ").Id) + x.InsigniaId.HasValue && + x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id && + x.InsigniaId.Value == GetInsigniaByName("มหาวชิรมงกุฎ")?.Id) .ToList() .Count() == 0) select new @@ -7804,25 +7839,25 @@ namespace BMA.EHR.Application.Repositories ProfileDateAppoint = p.DateAppoint.Value, LastInsignia = p.ProfileInsignia == null || p.ProfileInsignia.Count == 0 ? "" : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .FirstOrDefault() == null ? "" : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) - .OrderByDescending(x => x.Year).FirstOrDefault().Insignia, + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) + .OrderByDescending(x => x.Year).FirstOrDefault()?.Insignia, LastInsigniaId = p.ProfileInsignia == null || p.ProfileInsignia.Count == 0 ? Guid.Empty : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .FirstOrDefault() == null ? Guid.Empty : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) - .OrderByDescending(x => x.Year).FirstOrDefault().InsigniaId, + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) + .OrderByDescending(x => x.Year).FirstOrDefault()?.InsigniaId, Salary = p.Amount, SalaryPosition = p.PositionSalaryAmount, SalaryCondition = p.ProfileSalary == null || p.ProfileSalary.Count == 0 ? 0 : - p.ProfileSalary.Where(x => x.Date != null).Where(x => x.Date.Value <= new DateTime(period.Year, 4, 29)) + p.ProfileSalary.Where(x => x.Date != null).Where(x => x.Date.HasValue && x.Date.Value <= new DateTime(period.Year, 4, 29)) .OrderByDescending(x => x.Order).FirstOrDefault() != null ? p.ProfileSalary - .Where(x => x.Date != null).Where(x => x.Date.Value <= new DateTime(period.Year, 4, 29)) - .OrderByDescending(x => x.Order).FirstOrDefault().Amount : + .Where(x => x.Date != null).Where(x => x.Date.HasValue && x.Date.Value <= new DateTime(period.Year, 4, 29)) + .OrderByDescending(x => x.Order).FirstOrDefault()?.Amount : p.Amount, PosNo = p.PosNo, Gender = p.Gender == null ? null : p.Gender, @@ -7832,12 +7867,12 @@ namespace BMA.EHR.Application.Repositories PositionTypeName = p.PosType, IsHigherLevel = p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .FirstOrDefault() == null ? true : IsHigherLevel(p.ProfileInsignia - .Where(x => x.InsigniaId.Value != - GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != + GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .OrderByDescending(x => x.Year) .FirstOrDefault(), "มหาวชิรมงกุฎ"), @@ -7847,14 +7882,14 @@ namespace BMA.EHR.Application.Repositories p.ProfileSalary.Where(x => x.PositionLevel == "สูง" && x.PositionType == "บริหาร") .OrderBy(p => p.Date).FirstOrDefault() == null ? null : p.ProfileSalary.Where(x => x.PositionLevel == "สูง" && x.PositionType == "บริหาร") - .OrderBy(p => p.Date).FirstOrDefault().Date, + .OrderBy(p => p.Date).FirstOrDefault()?.Date, FirstRecvInsigniaYear = p.ProfileInsignia.Count == 0 ? 0 : p.ProfileInsignia - .Where(x => x.InsigniaId.Value == GetInsigniaByName("ประถมาภรณ์ช้างเผือก").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value == GetInsigniaByName("ประถมาภรณ์ช้างเผือก")?.Id) .OrderBy(x => x.Year).FirstOrDefault() == null ? 0 : p.ProfileInsignia - .Where(x => x.InsigniaId.Value == GetInsigniaByName("ประถมาภรณ์ช้างเผือก").Id) - .OrderBy(x => x.Year).FirstOrDefault().Year, + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value == GetInsigniaByName("ประถมาภรณ์ช้างเผือก")?.Id) + .OrderBy(x => x.Year).FirstOrDefault()?.Year, PositionSalaryAmount = p.PositionSalaryAmount, Amount = p.Amount, RootId = p.RootId, @@ -7951,8 +7986,9 @@ namespace BMA.EHR.Application.Repositories && p.ProfileInsignia != null && p.ProfileInsignia.Count > 0 && (p.ProfileInsignia.Where(x => - x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id && - x.InsigniaId.Value == GetInsigniaByName("มหาปรมาภรณ์ช้างเผือก").Id) + x.InsigniaId.HasValue && + x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id && + x.InsigniaId.Value == GetInsigniaByName("มหาปรมาภรณ์ช้างเผือก")?.Id) .ToList() .Count() == 0) select new @@ -7971,25 +8007,25 @@ namespace BMA.EHR.Application.Repositories ProfileDateAppoint = p.DateAppoint.Value, LastInsignia = p.ProfileInsignia == null || p.ProfileInsignia.Count == 0 ? "" : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .FirstOrDefault() == null ? "" : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) - .OrderByDescending(x => x.Year).FirstOrDefault().Insignia, + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) + .OrderByDescending(x => x.Year).FirstOrDefault()?.Insignia, LastInsigniaId = p.ProfileInsignia == null || p.ProfileInsignia.Count == 0 ? Guid.Empty : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .FirstOrDefault() == null ? Guid.Empty : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) - .OrderByDescending(x => x.Year).FirstOrDefault().InsigniaId, + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) + .OrderByDescending(x => x.Year).FirstOrDefault()?.InsigniaId, Salary = p.Amount, SalaryPosition = p.PositionSalaryAmount, SalaryCondition = p.ProfileSalary == null || p.ProfileSalary.Count == 0 ? 0 : - p.ProfileSalary.Where(x => x.Date != null).Where(x => x.Date.Value <= new DateTime(period.Year, 4, 29)) + p.ProfileSalary.Where(x => x.Date != null).Where(x => x.Date.HasValue && x.Date.Value <= new DateTime(period.Year, 4, 29)) .OrderByDescending(x => x.Order).FirstOrDefault() != null ? p.ProfileSalary - .Where(x => x.Date != null).Where(x => x.Date.Value <= new DateTime(period.Year, 4, 29)) - .OrderByDescending(x => x.Order).FirstOrDefault().Amount : + .Where(x => x.Date != null).Where(x => x.Date.HasValue && x.Date.Value <= new DateTime(period.Year, 4, 29)) + .OrderByDescending(x => x.Order).FirstOrDefault()?.Amount : p.Amount, PosNo = p.PosNo, Gender = p.Gender == null ? null : p.Gender, @@ -7999,12 +8035,12 @@ namespace BMA.EHR.Application.Repositories PositionTypeName = p.PosType, IsHigherLevel = p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .FirstOrDefault() == null ? true : IsHigherLevel(p.ProfileInsignia - .Where(x => x.InsigniaId.Value != - GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != + GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .OrderByDescending(x => x.Year) .FirstOrDefault(), "มหาปรมาภรณ์ช้างเผือก"), @@ -8014,12 +8050,12 @@ namespace BMA.EHR.Application.Repositories p.ProfileSalary.Where(x => x.PositionLevel == "สูง" && x.PositionType == "บริหาร") .OrderBy(p => p.Date).FirstOrDefault() == null ? null : p.ProfileSalary.Where(x => x.PositionLevel == "สูง" && x.PositionType == "บริหาร") - .OrderBy(p => p.Date).FirstOrDefault().Date, + .OrderBy(p => p.Date).FirstOrDefault()?.Date, FirstRecvInsigniaYear = p.ProfileInsignia.Count == 0 ? 0 : - p.ProfileInsignia.Where(x => x.InsigniaId.Value == GetInsigniaByName("มหาวชิรมงกุฎ").Id) + p.ProfileInsignia.Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value == GetInsigniaByName("มหาวชิรมงกุฎ")?.Id) .OrderBy(x => x.Year).FirstOrDefault() == null ? 0 : - p.ProfileInsignia.Where(x => x.InsigniaId.Value == GetInsigniaByName("มหาวชิรมงกุฎ").Id) - .OrderBy(x => x.Year).FirstOrDefault().Year, + p.ProfileInsignia.Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value == GetInsigniaByName("มหาวชิรมงกุฎ")?.Id) + .OrderBy(x => x.Year).FirstOrDefault()?.Year, PositionSalaryAmount = p.PositionSalaryAmount, Amount = p.Amount, RootId = p.RootId, @@ -8179,144 +8215,146 @@ namespace BMA.EHR.Application.Repositories if (type.ToLower().Trim() == "officer") { var allOfficerProfilesByRoot = (await _userProfileRepository.GetOfficerProfileByRootIdAsync(ocId, AccessToken)); - - // calculate ตามแต่ละชั้น - var type_coin = allOfficerProfilesByRoot.Count() > 0 ? await GetCoinCandidate(periodId, ocId, allOfficerProfilesByRoot) : new List(); - var type1_level1 = allOfficerProfilesByRoot.Count() > 0 ? await GetInsigniaCandidate_Type1_Level1(periodId, ocId, allOfficerProfilesByRoot) : new List(); - var type1_level2 = allOfficerProfilesByRoot.Count() > 0 ? await GetInsigniaCandidate_Type1_Level2(periodId, ocId, allOfficerProfilesByRoot) : new List(); - var type1_level3 = allOfficerProfilesByRoot.Count() > 0 ? await GetInsigniaCandidate_Type1_Level3(periodId, ocId, allOfficerProfilesByRoot) : new List(); - var type1_level4 = allOfficerProfilesByRoot.Count() > 0 ? await GetInsigniaCandidate_Type1_Level4(periodId, ocId, allOfficerProfilesByRoot) : new List(); - var type2_level5 = allOfficerProfilesByRoot.Count() > 0 ? await GetInsigniaCandidate_Type2_Level5(periodId, ocId, allOfficerProfilesByRoot) : new List(); - var type2_level6 = allOfficerProfilesByRoot.Count() > 0 ? await GetInsigniaCandidate_Type2_Level6(periodId, ocId, allOfficerProfilesByRoot) : new List(); - var type2_level7 = allOfficerProfilesByRoot.Count() > 0 ? await GetInsigniaCandidate_Type2_Level7(periodId, ocId, allOfficerProfilesByRoot) : new List(); - var type2_level8 = allOfficerProfilesByRoot.Count() > 0 ? await GetInsigniaCandidate_Type2_Level8(periodId, ocId, allOfficerProfilesByRoot) : new List(); - var type2_level9_1 = - allOfficerProfilesByRoot.Count() > 0 ? await GetInsigniaCandidate_Type2_Level9_1(periodId, ocId, allOfficerProfilesByRoot) : new List(); - var type2_level9_2 = - allOfficerProfilesByRoot.Count() > 0 ? await GetInsigniaCandidate_Type2_Level9_2(periodId, ocId, allOfficerProfilesByRoot) : new List(); - var type3_level10 = allOfficerProfilesByRoot.Count() > 0 ? await GetInsigniaCandidate_Type3_Level10(periodId, ocId, allOfficerProfilesByRoot) : new List(); - var type3_level11 = allOfficerProfilesByRoot.Count() > 0 ? await GetInsigniaCandidate_Type3_Level11(periodId, ocId, allOfficerProfilesByRoot) : new List(); - var type4_level10 = allOfficerProfilesByRoot.Count() > 0 ? await GetInsigniaCandidate_Type4_Level10(periodId, ocId, allOfficerProfilesByRoot) : new List(); - var type4_level11 = allOfficerProfilesByRoot.Count() > 0 ? await GetInsigniaCandidate_Type4_Level11(periodId, ocId, allOfficerProfilesByRoot) : new List(); - - - // union result - foreach (var r in type_coin) + if (allOfficerProfilesByRoot != null) { - var old = result_candidate.FirstOrDefault(x => x.ProfileId == r.ProfileId); - if (old == null) - result_candidate.Add(r); - } - - foreach (var r in type4_level11) - { - var old = result_candidate.FirstOrDefault(x => x.ProfileId == r.ProfileId); - if (old == null) - result_candidate.Add(r); - } - - foreach (var r in type4_level10) - { - var old = result_candidate.FirstOrDefault(x => x.ProfileId == r.ProfileId); - if (old == null) - result_candidate.Add(r); - } - - foreach (var r in type3_level11) - { - var old = result_candidate.FirstOrDefault(x => x.ProfileId == r.ProfileId); - if (old == null) - result_candidate.Add(r); - } - - foreach (var r in type3_level10) - { - var old = result_candidate.FirstOrDefault(x => x.ProfileId == r.ProfileId); - if (old == null) - result_candidate.Add(r); - } - - foreach (var r in type2_level9_2) - { - var old = result_candidate.FirstOrDefault(x => x.ProfileId == r.ProfileId); - if (old == null) - result_candidate.Add(r); - } - - foreach (var r in type2_level9_1) - { - var old = result_candidate.FirstOrDefault(x => x.ProfileId == r.ProfileId); - if (old == null) - result_candidate.Add(r); - } - - foreach (var r in type2_level8) - { - var old = result_candidate.FirstOrDefault(x => x.ProfileId == r.ProfileId); - if (old == null) - result_candidate.Add(r); - } - - foreach (var r in type2_level7) - { - var old = result_candidate.FirstOrDefault(x => x.ProfileId == r.ProfileId); - if (old == null) - result_candidate.Add(r); - } - - foreach (var r in type2_level6) - { - var old = result_candidate.FirstOrDefault(x => x.ProfileId == r.ProfileId); - if (old == null) - result_candidate.Add(r); - } - - foreach (var r in type2_level5) - { - var old = result_candidate.FirstOrDefault(x => x.ProfileId == r.ProfileId); - if (old == null) - result_candidate.Add(r); - } - - foreach (var r in type1_level4) - { - var old = result_candidate.FirstOrDefault(x => x.ProfileId == r.ProfileId); - if (old == null) - result_candidate.Add(r); - } - - foreach (var r in type1_level3) - { - var old = result_candidate.FirstOrDefault(x => x.ProfileId == r.ProfileId); - if (old == null) - result_candidate.Add(r); - } - - foreach (var r in type1_level2) - { - var old = result_candidate.FirstOrDefault(x => x.ProfileId == r.ProfileId); - if (old == null) - result_candidate.Add(r); - } - - foreach (var r in type1_level1) - { - var old = result_candidate.FirstOrDefault(x => x.ProfileId == r.ProfileId); - if (old == null) - result_candidate.Add(r); - } - - // ย้ายที่ตามที่ มอสแจ้ง - if (period.Round != 1) - { - var insigniaIdList = await _dbContext.Set() - .Include(x => x.InsigniaType) - .Where(x => x.InsigniaType!.Name == "ชั้นสายสะพาย") - .Select(x => x.Id) - .ToListAsync(); + // calculate ตามแต่ละชั้น + var type_coin = allOfficerProfilesByRoot.Count() > 0 ? await GetCoinCandidate(periodId, ocId, allOfficerProfilesByRoot) : new List(); + var type1_level1 = allOfficerProfilesByRoot.Count() > 0 ? await GetInsigniaCandidate_Type1_Level1(periodId, ocId, allOfficerProfilesByRoot) : new List(); + var type1_level2 = allOfficerProfilesByRoot.Count() > 0 ? await GetInsigniaCandidate_Type1_Level2(periodId, ocId, allOfficerProfilesByRoot) : new List(); + var type1_level3 = allOfficerProfilesByRoot.Count() > 0 ? await GetInsigniaCandidate_Type1_Level3(periodId, ocId, allOfficerProfilesByRoot) : new List(); + var type1_level4 = allOfficerProfilesByRoot.Count() > 0 ? await GetInsigniaCandidate_Type1_Level4(periodId, ocId, allOfficerProfilesByRoot) : new List(); + var type2_level5 = allOfficerProfilesByRoot.Count() > 0 ? await GetInsigniaCandidate_Type2_Level5(periodId, ocId, allOfficerProfilesByRoot) : new List(); + var type2_level6 = allOfficerProfilesByRoot.Count() > 0 ? await GetInsigniaCandidate_Type2_Level6(periodId, ocId, allOfficerProfilesByRoot) : new List(); + var type2_level7 = allOfficerProfilesByRoot.Count() > 0 ? await GetInsigniaCandidate_Type2_Level7(periodId, ocId, allOfficerProfilesByRoot) : new List(); + var type2_level8 = allOfficerProfilesByRoot.Count() > 0 ? await GetInsigniaCandidate_Type2_Level8(periodId, ocId, allOfficerProfilesByRoot) : new List(); + var type2_level9_1 = + allOfficerProfilesByRoot.Count() > 0 ? await GetInsigniaCandidate_Type2_Level9_1(periodId, ocId, allOfficerProfilesByRoot) : new List(); + var type2_level9_2 = + allOfficerProfilesByRoot.Count() > 0 ? await GetInsigniaCandidate_Type2_Level9_2(periodId, ocId, allOfficerProfilesByRoot) : new List(); + var type3_level10 = allOfficerProfilesByRoot.Count() > 0 ? await GetInsigniaCandidate_Type3_Level10(periodId, ocId, allOfficerProfilesByRoot) : new List(); + var type3_level11 = allOfficerProfilesByRoot.Count() > 0 ? await GetInsigniaCandidate_Type3_Level11(periodId, ocId, allOfficerProfilesByRoot) : new List(); + var type4_level10 = allOfficerProfilesByRoot.Count() > 0 ? await GetInsigniaCandidate_Type4_Level10(periodId, ocId, allOfficerProfilesByRoot) : new List(); + var type4_level11 = allOfficerProfilesByRoot.Count() > 0 ? await GetInsigniaCandidate_Type4_Level11(periodId, ocId, allOfficerProfilesByRoot) : new List(); - result_candidate = result_candidate.Where(x => insigniaIdList.Contains(x.RequestInsignia.Id)).ToList(); + // union result + foreach (var r in type_coin) + { + var old = result_candidate.FirstOrDefault(x => x.ProfileId == r.ProfileId); + if (old == null) + result_candidate.Add(r); + } + + foreach (var r in type4_level11) + { + var old = result_candidate.FirstOrDefault(x => x.ProfileId == r.ProfileId); + if (old == null) + result_candidate.Add(r); + } + + foreach (var r in type4_level10) + { + var old = result_candidate.FirstOrDefault(x => x.ProfileId == r.ProfileId); + if (old == null) + result_candidate.Add(r); + } + + foreach (var r in type3_level11) + { + var old = result_candidate.FirstOrDefault(x => x.ProfileId == r.ProfileId); + if (old == null) + result_candidate.Add(r); + } + + foreach (var r in type3_level10) + { + var old = result_candidate.FirstOrDefault(x => x.ProfileId == r.ProfileId); + if (old == null) + result_candidate.Add(r); + } + + foreach (var r in type2_level9_2) + { + var old = result_candidate.FirstOrDefault(x => x.ProfileId == r.ProfileId); + if (old == null) + result_candidate.Add(r); + } + + foreach (var r in type2_level9_1) + { + var old = result_candidate.FirstOrDefault(x => x.ProfileId == r.ProfileId); + if (old == null) + result_candidate.Add(r); + } + + foreach (var r in type2_level8) + { + var old = result_candidate.FirstOrDefault(x => x.ProfileId == r.ProfileId); + if (old == null) + result_candidate.Add(r); + } + + foreach (var r in type2_level7) + { + var old = result_candidate.FirstOrDefault(x => x.ProfileId == r.ProfileId); + if (old == null) + result_candidate.Add(r); + } + + foreach (var r in type2_level6) + { + var old = result_candidate.FirstOrDefault(x => x.ProfileId == r.ProfileId); + if (old == null) + result_candidate.Add(r); + } + + foreach (var r in type2_level5) + { + var old = result_candidate.FirstOrDefault(x => x.ProfileId == r.ProfileId); + if (old == null) + result_candidate.Add(r); + } + + foreach (var r in type1_level4) + { + var old = result_candidate.FirstOrDefault(x => x.ProfileId == r.ProfileId); + if (old == null) + result_candidate.Add(r); + } + + foreach (var r in type1_level3) + { + var old = result_candidate.FirstOrDefault(x => x.ProfileId == r.ProfileId); + if (old == null) + result_candidate.Add(r); + } + + foreach (var r in type1_level2) + { + var old = result_candidate.FirstOrDefault(x => x.ProfileId == r.ProfileId); + if (old == null) + result_candidate.Add(r); + } + + foreach (var r in type1_level1) + { + var old = result_candidate.FirstOrDefault(x => x.ProfileId == r.ProfileId); + if (old == null) + result_candidate.Add(r); + } + + // ย้ายที่ตามที่ มอสแจ้ง + if (period.Round != 1) + { + var insigniaIdList = await _dbContext.Set() + .Include(x => x.InsigniaType) + .Where(x => x.InsigniaType!.Name == "ชั้นสายสะพาย") + .Select(x => x.Id) + .ToListAsync(); + + + result_candidate = result_candidate.Where(x => insigniaIdList.Contains(x.RequestInsignia.Id)).ToList(); + } } } else if (type.ToLower().Trim() == "employee") @@ -8327,31 +8365,34 @@ namespace BMA.EHR.Application.Repositories allEmployeeProfileByRoot = (await _userProfileRepository.GetEmployeeProfileByPositionAsync(ocId, period.InsigniaEmployees.Select(x => x.RefId!.ValueOrBlank()).ToArray(), AccessToken)); } - var type_coin = allEmployeeProfileByRoot.Count() > 0 ? await GetCoinCandidate(periodId, ocId, allEmployeeProfileByRoot) : new List(); - - var employee_type1 = allEmployeeProfileByRoot.Count() > 0 ? await GetEmployeeInsignia_Type1(periodId, ocId, allEmployeeProfileByRoot) : new List(); - var employee_type2 = allEmployeeProfileByRoot.Count() > 0 ? await GetEmployeeInsignia_Type2(periodId, ocId, allEmployeeProfileByRoot) : new List(); - - // union result - foreach (var r in type_coin) + if (allEmployeeProfileByRoot != null) { - var old = result_candidate.FirstOrDefault(x => x.ProfileId == r.ProfileId); - if (old == null) - result_candidate.Add(r); - } + var type_coin = allEmployeeProfileByRoot.Count() > 0 ? await GetCoinCandidate(periodId, ocId, allEmployeeProfileByRoot) : new List(); - foreach (var r in employee_type2) - { - var old = result_candidate.FirstOrDefault(x => x.ProfileId == r.ProfileId); - if (old == null) - result_candidate.Add(r); - } + var employee_type1 = allEmployeeProfileByRoot.Count() > 0 ? await GetEmployeeInsignia_Type1(periodId, ocId, allEmployeeProfileByRoot) : new List(); + var employee_type2 = allEmployeeProfileByRoot.Count() > 0 ? await GetEmployeeInsignia_Type2(periodId, ocId, allEmployeeProfileByRoot) : new List(); - foreach (var r in employee_type1) - { - var old = result_candidate.FirstOrDefault(x => x.ProfileId == r.ProfileId); - if (old == null) - result_candidate.Add(r); + // union result + foreach (var r in type_coin) + { + var old = result_candidate.FirstOrDefault(x => x.ProfileId == r.ProfileId); + if (old == null) + result_candidate.Add(r); + } + + foreach (var r in employee_type2) + { + var old = result_candidate.FirstOrDefault(x => x.ProfileId == r.ProfileId); + if (old == null) + result_candidate.Add(r); + } + + foreach (var r in employee_type1) + { + var old = result_candidate.FirstOrDefault(x => x.ProfileId == r.ProfileId); + if (old == null) + result_candidate.Add(r); + } } } else @@ -8571,7 +8612,7 @@ namespace BMA.EHR.Application.Repositories if (period == null) throw new Exception(GlobalMessages.CoinPeriodNotFound); - var inst_profile = allProfilesByRoot.Where(x => x.DateAppoint != null) + var inst_profile = allProfilesByRoot.Where(x => x.DateAppoint.HasValue && x.DateAppoint != null) .Select(p => new { ProfileId = p.Id, @@ -8594,24 +8635,24 @@ namespace BMA.EHR.Application.Repositories Gender = p.Gender ?? "", LastInsignia = p.ProfileInsignia == null || p.ProfileInsignia.Count == 0 ? "" : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .FirstOrDefault() == null ? "" : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) - .OrderByDescending(x => x.Year).FirstOrDefault().Insignia, + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) + .OrderByDescending(x => x.Year).FirstOrDefault()?.Insignia, LastInsigniaId = p.ProfileInsignia == null || p.ProfileInsignia.Count == 0 ? Guid.Empty : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) .FirstOrDefault() == null ? Guid.Empty : p.ProfileInsignia - .Where(x => x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา").Id) - .OrderByDescending(x => x.Year).FirstOrDefault().InsigniaId, + .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) + .OrderByDescending(x => x.Year).FirstOrDefault()?.InsigniaId, Salary = p.Amount, SalaryCondition = p.ProfileSalary == null || p.ProfileSalary.Count == 0 ? 0 : - p.ProfileSalary.Where(x => x.Date != null).Where(x => x.Date.Value <= new DateTime(period.Year, 4, 29)) + p.ProfileSalary.Where(x => x.Date != null).Where(x => x.Date.HasValue && x.Date.Value <= new DateTime(period.Year, 4, 29)) .OrderByDescending(x => x.Order).FirstOrDefault() != null ? p.ProfileSalary - .Where(x => x.Date != null).Where(x => x.Date.Value <= new DateTime(period.Year, 4, 29)) - .OrderByDescending(x => x.Order).FirstOrDefault().Amount : + .Where(x => x.Date != null).Where(x => x.Date.HasValue && x.Date.Value <= new DateTime(period.Year, 4, 29)) + .OrderByDescending(x => x.Order).FirstOrDefault()?.Amount : p.Amount, PositionSalaryAmount = p.PositionSalaryAmount, ProfileInsignia = p.ProfileInsignia, From f9ca9b52af70ef1eb8fa2d62a7bd7a542574d28d Mon Sep 17 00:00:00 2001 From: Adisak Date: Mon, 5 Jan 2026 16:38:30 +0700 Subject: [PATCH 015/178] #2150 --- .../Controllers/PlacementReceiveController.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/BMA.EHR.Placement.Service/Controllers/PlacementReceiveController.cs b/BMA.EHR.Placement.Service/Controllers/PlacementReceiveController.cs index f18e2b32..db8a28a7 100644 --- a/BMA.EHR.Placement.Service/Controllers/PlacementReceiveController.cs +++ b/BMA.EHR.Placement.Service/Controllers/PlacementReceiveController.cs @@ -553,18 +553,23 @@ namespace BMA.EHR.Placement.Service.Controllers // placementReceive.citizenId = org.result.citizenId; placementReceive.rootOld = org.result.root; placementReceive.rootOldId = org.result.rootId; + placementReceive.rootDnaId = org.result.rootDnaId; placementReceive.rootShortNameOld = org.result.rootShortName; placementReceive.child1Old = org.result.child1; placementReceive.child1OldId = org.result.child1Id; + placementReceive.child1DnaId = org.result.child1DnaId; placementReceive.child1ShortNameOld = org.result.child1ShortName; placementReceive.child2Old = org.result.child2; placementReceive.child2OldId = org.result.child2Id; + placementReceive.child2DnaId = org.result.child2DnaId; placementReceive.child2ShortNameOld = org.result.child2ShortName; placementReceive.child3Old = org.result.child3; placementReceive.child3OldId = org.result.child3Id; + placementReceive.child4DnaId = org.result.child4DnaId; placementReceive.child3ShortNameOld = org.result.child3ShortName; placementReceive.child4Old = org.result.child4; placementReceive.child4OldId = org.result.child4Id; + placementReceive.child4DnaId = org.result.child4DnaId; placementReceive.child4ShortNameOld = org.result.child4ShortName; placementReceive.posMasterNoOld = org.result.posMasterNo; placementReceive.posTypeOldId = org.result.posTypeId; From e8dfe976a2eaf3f407c2db0e9e44bbd3ecadad0f Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Wed, 7 Jan 2026 12:06:49 +0700 Subject: [PATCH 016/178] remove workflow integration from LeaveRequestController #2164 --- .../Controllers/LeaveRequestController.cs | 81 ++++++++++--------- 1 file changed, 41 insertions(+), 40 deletions(-) diff --git a/BMA.EHR.Leave/Controllers/LeaveRequestController.cs b/BMA.EHR.Leave/Controllers/LeaveRequestController.cs index 09addc23..9233b374 100644 --- a/BMA.EHR.Leave/Controllers/LeaveRequestController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveRequestController.cs @@ -2247,46 +2247,47 @@ namespace BMA.EHR.Leave.Service.Controllers { await _leaveRequestRepository.SendToOfficerAsync(id); - var userId = UserId == null ? Guid.Empty : Guid.Parse(UserId); - var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(userId, AccessToken); - if (profile == null) - { - return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound); - } - var baseAPIOrg = _configuration["API"]; - var apiUrlOrg = $"{baseAPIOrg}/org/workflow/add-workflow"; - if (profile.ProfileType == "OFFICER") - { - using (var client = new HttpClient()) - { - client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", AccessToken.Replace("Bearer ", "")); - client.DefaultRequestHeaders.Add("api-key", _configuration["API_KEY"]); - var _res = await client.PostAsJsonAsync(apiUrlOrg, new - { - refId = id, - sysName = "SYS_LEAVE_LIST", - posLevelName = profile.PosLevel ?? "", - posTypeName = profile.PosType ?? "", - fullName = $"{profile.Prefix}{profile.FirstName} {profile.LastName}" - }); - } - } - else - { - using (var client = new HttpClient()) - { - client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", AccessToken.Replace("Bearer ", "")); - client.DefaultRequestHeaders.Add("api-key", _configuration["API_KEY"]); - var _res = await client.PostAsJsonAsync(apiUrlOrg, new - { - refId = id, - sysName = "SYS_LEAVE_LIST_EMP", - posLevelName = profile.PosLevel ?? "", - posTypeName = profile.PosType ?? "", - fullName = $"{profile.Prefix}{profile.FirstName} {profile.LastName}" - }); - } - } + // Remove Workflow Integration + // var userId = UserId == null ? Guid.Empty : Guid.Parse(UserId); + // var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(userId, AccessToken); + // if (profile == null) + // { + // return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound); + // } + // var baseAPIOrg = _configuration["API"]; + // var apiUrlOrg = $"{baseAPIOrg}/org/workflow/add-workflow"; + // if (profile.ProfileType == "OFFICER") + // { + // using (var client = new HttpClient()) + // { + // client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", AccessToken.Replace("Bearer ", "")); + // client.DefaultRequestHeaders.Add("api-key", _configuration["API_KEY"]); + // var _res = await client.PostAsJsonAsync(apiUrlOrg, new + // { + // refId = id, + // sysName = "SYS_LEAVE_LIST", + // posLevelName = profile.PosLevel ?? "", + // posTypeName = profile.PosType ?? "", + // fullName = $"{profile.Prefix}{profile.FirstName} {profile.LastName}" + // }); + // } + // } + // else + // { + // using (var client = new HttpClient()) + // { + // client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", AccessToken.Replace("Bearer ", "")); + // client.DefaultRequestHeaders.Add("api-key", _configuration["API_KEY"]); + // var _res = await client.PostAsJsonAsync(apiUrlOrg, new + // { + // refId = id, + // sysName = "SYS_LEAVE_LIST_EMP", + // posLevelName = profile.PosLevel ?? "", + // posTypeName = profile.PosType ?? "", + // fullName = $"{profile.Prefix}{profile.FirstName} {profile.LastName}" + // }); + // } + // } return Success(); } From 8c1a219084b6f0ad592b77d6a09bc45699e95b16 Mon Sep 17 00:00:00 2001 From: waruneeauy Date: Fri, 9 Jan 2026 16:29:43 +0700 Subject: [PATCH 017/178] fix build report to v2 and add noti --- ...-release-report.yaml => dockerhub-release-reportv2.yaml} | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename .github/workflows/{dockerhub-release-report.yaml => dockerhub-release-reportv2.yaml} (94%) diff --git a/.github/workflows/dockerhub-release-report.yaml b/.github/workflows/dockerhub-release-reportv2.yaml similarity index 94% rename from .github/workflows/dockerhub-release-report.yaml rename to .github/workflows/dockerhub-release-reportv2.yaml index 132b218b..f3ddead5 100644 --- a/.github/workflows/dockerhub-release-report.yaml +++ b/.github/workflows/dockerhub-release-reportv2.yaml @@ -3,7 +3,7 @@ run-name: DockerHub Release - Report Service by ${{ github.actor }} on: push: tags: - - "report-[0-9]+.[0-9]+.[0-9]+" + - "reportv2-[0-9]+.[0-9]+.[0-9]+" workflow_dispatch: inputs: IMAGE_VER: @@ -13,7 +13,7 @@ on: env: DOCKERHUB_REGISTRY: docker.io - IMAGE_NAME: hrms-api-report + IMAGE_NAME: hrms-api-reportv2 jobs: release-to-dockerhub: @@ -59,7 +59,7 @@ jobs: uses: docker/build-push-action@v5 with: context: . - file: BMA.EHR.Report.Service/Dockerfile + file: BMA.EHR.ReportV2.Service/Dockerfile platforms: linux/amd64 push: true tags: | From 0690337422cee2deaac48dc6fe416fcdeb05202a Mon Sep 17 00:00:00 2001 From: waruneeauy Date: Fri, 9 Jan 2026 16:35:33 +0700 Subject: [PATCH 018/178] fix build report --- .../workflows/dockerhub-release-reportv2.yaml | 31 ++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/.github/workflows/dockerhub-release-reportv2.yaml b/.github/workflows/dockerhub-release-reportv2.yaml index f3ddead5..4dc73f3f 100644 --- a/.github/workflows/dockerhub-release-reportv2.yaml +++ b/.github/workflows/dockerhub-release-reportv2.yaml @@ -14,6 +14,7 @@ on: env: DOCKERHUB_REGISTRY: docker.io IMAGE_NAME: hrms-api-reportv2 + DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }} jobs: release-to-dockerhub: @@ -59,7 +60,7 @@ jobs: uses: docker/build-push-action@v5 with: context: . - file: BMA.EHR.ReportV2.Service/Dockerfile + file: BMA.EHR.Report.Service/Dockerfile platforms: linux/amd64 push: true tags: | @@ -70,3 +71,31 @@ jobs: org.opencontainers.image.description=HRMS Report API Service cache-from: type=gha cache-to: type=gha,mode=max + + - name: Notify Discord on success + if: success() + env: + IMAGE_VER: ${{ steps.gen_ver.outputs.image_ver }} + run: | + TAG_INFO="Tag: ${IMAGE_VER:-unknown}" + REF_INFO="Ref: ${GITHUB_REF}" + ACTOR_INFO="Actor: ${GITHUB_ACTOR}" + MSG="✅ DockerHub release succeeded\n${TAG_INFO}\n${REF_INFO}\n${ACTOR_INFO}" + curl -s -H "Content-Type: application/json" \ + -X POST \ + -d "{\"content\":\"${MSG}\"}" \ + "$DISCORD_WEBHOOK" + + - name: Notify Discord on failure + if: failure() + env: + IMAGE_VER: ${{ steps.gen_ver.outputs.image_ver }} + run: | + TAG_INFO="Tag: ${IMAGE_VER:-unknown}" + REF_INFO="Ref: ${GITHUB_REF}" + ACTOR_INFO="Actor: ${GITHUB_ACTOR}" + MSG="❌ DockerHub release failed\n${TAG_INFO}\n${REF_INFO}\n${ACTOR_INFO}" + curl -s -H "Content-Type: application/json" \ + -X POST \ + -d "{\"content\":\"${MSG}\"}" \ + "$DISCORD_WEBHOOK" From a54091220251c92e6632b78cd73ff0b609f566dc Mon Sep 17 00:00:00 2001 From: waruneeauy Date: Fri, 9 Jan 2026 18:43:59 +0700 Subject: [PATCH 019/178] add noti discord --- .../workflows/dockerhub-release-checkin.yaml | 29 +++++++++++++++++++ .../workflows/dockerhub-release-command.yaml | 29 +++++++++++++++++++ .../dockerhub-release-discipline.yaml | 29 +++++++++++++++++++ .../workflows/dockerhub-release-insignia.yaml | 29 +++++++++++++++++++ .../workflows/dockerhub-release-leave.yaml | 29 +++++++++++++++++++ .../dockerhub-release-placement.yaml | 29 +++++++++++++++++++ .../dockerhub-release-retirement.yaml | 29 +++++++++++++++++++ 7 files changed, 203 insertions(+) diff --git a/.github/workflows/dockerhub-release-checkin.yaml b/.github/workflows/dockerhub-release-checkin.yaml index 9e7c4e23..b80c44f7 100644 --- a/.github/workflows/dockerhub-release-checkin.yaml +++ b/.github/workflows/dockerhub-release-checkin.yaml @@ -14,6 +14,7 @@ on: env: DOCKERHUB_REGISTRY: docker.io IMAGE_NAME: hrms-api-checkin + DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }} jobs: release-to-dockerhub: @@ -70,3 +71,31 @@ jobs: org.opencontainers.image.description=HRMS CheckIn Consumer Service cache-from: type=gha cache-to: type=gha,mode=max + + - name: Notify Discord on success + if: success() + env: + IMAGE_VER: ${{ steps.gen_ver.outputs.image_ver }} + run: | + TAG_INFO="Tag: ${IMAGE_VER:-unknown}" + REF_INFO="Ref: ${GITHUB_REF}" + ACTOR_INFO="Actor: ${GITHUB_ACTOR}" + MSG="✅ DockerHub release succeeded\n${TAG_INFO}\n${REF_INFO}\n${ACTOR_INFO}" + curl -s -H "Content-Type: application/json" \ + -X POST \ + -d "{\"content\":\"${MSG}\"}" \ + "$DISCORD_WEBHOOK" + + - name: Notify Discord on failure + if: failure() + env: + IMAGE_VER: ${{ steps.gen_ver.outputs.image_ver }} + run: | + TAG_INFO="Tag: ${IMAGE_VER:-unknown}" + REF_INFO="Ref: ${GITHUB_REF}" + ACTOR_INFO="Actor: ${GITHUB_ACTOR}" + MSG="❌ DockerHub release failed\n${TAG_INFO}\n${REF_INFO}\n${ACTOR_INFO}" + curl -s -H "Content-Type: application/json" \ + -X POST \ + -d "{\"content\":\"${MSG}\"}" \ + "$DISCORD_WEBHOOK" diff --git a/.github/workflows/dockerhub-release-command.yaml b/.github/workflows/dockerhub-release-command.yaml index 33d6b330..571fd6c9 100644 --- a/.github/workflows/dockerhub-release-command.yaml +++ b/.github/workflows/dockerhub-release-command.yaml @@ -14,6 +14,7 @@ on: env: DOCKERHUB_REGISTRY: docker.io IMAGE_NAME: hrms-api-command + DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }} jobs: release-to-dockerhub: @@ -70,3 +71,31 @@ jobs: org.opencontainers.image.description=HRMS Command API Service cache-from: type=gha cache-to: type=gha,mode=max + + - name: Notify Discord on success + if: success() + env: + IMAGE_VER: ${{ steps.gen_ver.outputs.image_ver }} + run: | + TAG_INFO="Tag: ${IMAGE_VER:-unknown}" + REF_INFO="Ref: ${GITHUB_REF}" + ACTOR_INFO="Actor: ${GITHUB_ACTOR}" + MSG="✅ DockerHub release succeeded\n${TAG_INFO}\n${REF_INFO}\n${ACTOR_INFO}" + curl -s -H "Content-Type: application/json" \ + -X POST \ + -d "{\"content\":\"${MSG}\"}" \ + "$DISCORD_WEBHOOK" + + - name: Notify Discord on failure + if: failure() + env: + IMAGE_VER: ${{ steps.gen_ver.outputs.image_ver }} + run: | + TAG_INFO="Tag: ${IMAGE_VER:-unknown}" + REF_INFO="Ref: ${GITHUB_REF}" + ACTOR_INFO="Actor: ${GITHUB_ACTOR}" + MSG="❌ DockerHub release failed\n${TAG_INFO}\n${REF_INFO}\n${ACTOR_INFO}" + curl -s -H "Content-Type: application/json" \ + -X POST \ + -d "{\"content\":\"${MSG}\"}" \ + "$DISCORD_WEBHOOK" diff --git a/.github/workflows/dockerhub-release-discipline.yaml b/.github/workflows/dockerhub-release-discipline.yaml index 96096a54..f4642bd8 100644 --- a/.github/workflows/dockerhub-release-discipline.yaml +++ b/.github/workflows/dockerhub-release-discipline.yaml @@ -14,6 +14,7 @@ on: env: DOCKERHUB_REGISTRY: docker.io IMAGE_NAME: hrms-api-discipline + DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }} jobs: release-to-dockerhub: @@ -70,3 +71,31 @@ jobs: org.opencontainers.image.description=HRMS Discipline API Service cache-from: type=gha cache-to: type=gha,mode=max + + - name: Notify Discord on success + if: success() + env: + IMAGE_VER: ${{ steps.gen_ver.outputs.image_ver }} + run: | + TAG_INFO="Tag: ${IMAGE_VER:-unknown}" + REF_INFO="Ref: ${GITHUB_REF}" + ACTOR_INFO="Actor: ${GITHUB_ACTOR}" + MSG="✅ DockerHub release succeeded\n${TAG_INFO}\n${REF_INFO}\n${ACTOR_INFO}" + curl -s -H "Content-Type: application/json" \ + -X POST \ + -d "{\"content\":\"${MSG}\"}" \ + "$DISCORD_WEBHOOK" + + - name: Notify Discord on failure + if: failure() + env: + IMAGE_VER: ${{ steps.gen_ver.outputs.image_ver }} + run: | + TAG_INFO="Tag: ${IMAGE_VER:-unknown}" + REF_INFO="Ref: ${GITHUB_REF}" + ACTOR_INFO="Actor: ${GITHUB_ACTOR}" + MSG="❌ DockerHub release failed\n${TAG_INFO}\n${REF_INFO}\n${ACTOR_INFO}" + curl -s -H "Content-Type: application/json" \ + -X POST \ + -d "{\"content\":\"${MSG}\"}" \ + "$DISCORD_WEBHOOK" diff --git a/.github/workflows/dockerhub-release-insignia.yaml b/.github/workflows/dockerhub-release-insignia.yaml index 81dae954..79d89963 100644 --- a/.github/workflows/dockerhub-release-insignia.yaml +++ b/.github/workflows/dockerhub-release-insignia.yaml @@ -14,6 +14,7 @@ on: env: DOCKERHUB_REGISTRY: docker.io IMAGE_NAME: hrms-api-insignia + DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }} jobs: release-to-dockerhub: @@ -70,3 +71,31 @@ jobs: org.opencontainers.image.description=HRMS Insignia API Service cache-from: type=gha cache-to: type=gha,mode=max + + - name: Notify Discord on success + if: success() + env: + IMAGE_VER: ${{ steps.gen_ver.outputs.image_ver }} + run: | + TAG_INFO="Tag: ${IMAGE_VER:-unknown}" + REF_INFO="Ref: ${GITHUB_REF}" + ACTOR_INFO="Actor: ${GITHUB_ACTOR}" + MSG="✅ DockerHub release succeeded\n${TAG_INFO}\n${REF_INFO}\n${ACTOR_INFO}" + curl -s -H "Content-Type: application/json" \ + -X POST \ + -d "{\"content\":\"${MSG}\"}" \ + "$DISCORD_WEBHOOK" + + - name: Notify Discord on failure + if: failure() + env: + IMAGE_VER: ${{ steps.gen_ver.outputs.image_ver }} + run: | + TAG_INFO="Tag: ${IMAGE_VER:-unknown}" + REF_INFO="Ref: ${GITHUB_REF}" + ACTOR_INFO="Actor: ${GITHUB_ACTOR}" + MSG="❌ DockerHub release failed\n${TAG_INFO}\n${REF_INFO}\n${ACTOR_INFO}" + curl -s -H "Content-Type: application/json" \ + -X POST \ + -d "{\"content\":\"${MSG}\"}" \ + "$DISCORD_WEBHOOK" diff --git a/.github/workflows/dockerhub-release-leave.yaml b/.github/workflows/dockerhub-release-leave.yaml index b5a3afe2..f9c2e0ba 100644 --- a/.github/workflows/dockerhub-release-leave.yaml +++ b/.github/workflows/dockerhub-release-leave.yaml @@ -17,6 +17,7 @@ on: env: DOCKERHUB_REGISTRY: docker.io IMAGE_NAME: hrms-api-leave + DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }} jobs: release-to-dockerhub: @@ -88,3 +89,31 @@ jobs: - name: Image digest run: echo "Image pushed with digest ${{ steps.build.outputs.digest }}" + + - name: Notify Discord on success + if: success() + env: + IMAGE_VER: ${{ steps.gen_ver.outputs.image_ver }} + run: | + TAG_INFO="Tag: ${IMAGE_VER:-unknown}" + REF_INFO="Ref: ${GITHUB_REF}" + ACTOR_INFO="Actor: ${GITHUB_ACTOR}" + MSG="✅ DockerHub release succeeded\n${TAG_INFO}\n${REF_INFO}\n${ACTOR_INFO}" + curl -s -H "Content-Type: application/json" \ + -X POST \ + -d "{\"content\":\"${MSG}\"}" \ + "$DISCORD_WEBHOOK" + + - name: Notify Discord on failure + if: failure() + env: + IMAGE_VER: ${{ steps.gen_ver.outputs.image_ver }} + run: | + TAG_INFO="Tag: ${IMAGE_VER:-unknown}" + REF_INFO="Ref: ${GITHUB_REF}" + ACTOR_INFO="Actor: ${GITHUB_ACTOR}" + MSG="❌ DockerHub release failed\n${TAG_INFO}\n${REF_INFO}\n${ACTOR_INFO}" + curl -s -H "Content-Type: application/json" \ + -X POST \ + -d "{\"content\":\"${MSG}\"}" \ + "$DISCORD_WEBHOOK" diff --git a/.github/workflows/dockerhub-release-placement.yaml b/.github/workflows/dockerhub-release-placement.yaml index 74fa4471..d4e3dade 100644 --- a/.github/workflows/dockerhub-release-placement.yaml +++ b/.github/workflows/dockerhub-release-placement.yaml @@ -14,6 +14,7 @@ on: env: DOCKERHUB_REGISTRY: docker.io IMAGE_NAME: hrms-api-placement + DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }} jobs: release-to-dockerhub: @@ -70,3 +71,31 @@ jobs: org.opencontainers.image.description=HRMS Placement API Service cache-from: type=gha cache-to: type=gha,mode=max + + - name: Notify Discord on success + if: success() + env: + IMAGE_VER: ${{ steps.gen_ver.outputs.image_ver }} + run: | + TAG_INFO="Tag: ${IMAGE_VER:-unknown}" + REF_INFO="Ref: ${GITHUB_REF}" + ACTOR_INFO="Actor: ${GITHUB_ACTOR}" + MSG="✅ DockerHub release succeeded\n${TAG_INFO}\n${REF_INFO}\n${ACTOR_INFO}" + curl -s -H "Content-Type: application/json" \ + -X POST \ + -d "{\"content\":\"${MSG}\"}" \ + "$DISCORD_WEBHOOK" + + - name: Notify Discord on failure + if: failure() + env: + IMAGE_VER: ${{ steps.gen_ver.outputs.image_ver }} + run: | + TAG_INFO="Tag: ${IMAGE_VER:-unknown}" + REF_INFO="Ref: ${GITHUB_REF}" + ACTOR_INFO="Actor: ${GITHUB_ACTOR}" + MSG="❌ DockerHub release failed\n${TAG_INFO}\n${REF_INFO}\n${ACTOR_INFO}" + curl -s -H "Content-Type: application/json" \ + -X POST \ + -d "{\"content\":\"${MSG}\"}" \ + "$DISCORD_WEBHOOK" diff --git a/.github/workflows/dockerhub-release-retirement.yaml b/.github/workflows/dockerhub-release-retirement.yaml index 38e107e6..50ace5cd 100644 --- a/.github/workflows/dockerhub-release-retirement.yaml +++ b/.github/workflows/dockerhub-release-retirement.yaml @@ -14,6 +14,7 @@ on: env: DOCKERHUB_REGISTRY: docker.io IMAGE_NAME: hrms-api-retirement + DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }} jobs: release-to-dockerhub: @@ -70,3 +71,31 @@ jobs: org.opencontainers.image.description=HRMS Retirement API Service cache-from: type=gha cache-to: type=gha,mode=max + + - name: Notify Discord on success + if: success() + env: + IMAGE_VER: ${{ steps.gen_ver.outputs.image_ver }} + run: | + TAG_INFO="Tag: ${IMAGE_VER:-unknown}" + REF_INFO="Ref: ${GITHUB_REF}" + ACTOR_INFO="Actor: ${GITHUB_ACTOR}" + MSG="✅ DockerHub release succeeded\n${TAG_INFO}\n${REF_INFO}\n${ACTOR_INFO}" + curl -s -H "Content-Type: application/json" \ + -X POST \ + -d "{\"content\":\"${MSG}\"}" \ + "$DISCORD_WEBHOOK" + + - name: Notify Discord on failure + if: failure() + env: + IMAGE_VER: ${{ steps.gen_ver.outputs.image_ver }} + run: | + TAG_INFO="Tag: ${IMAGE_VER:-unknown}" + REF_INFO="Ref: ${GITHUB_REF}" + ACTOR_INFO="Actor: ${GITHUB_ACTOR}" + MSG="❌ DockerHub release failed\n${TAG_INFO}\n${REF_INFO}\n${ACTOR_INFO}" + curl -s -H "Content-Type: application/json" \ + -X POST \ + -d "{\"content\":\"${MSG}\"}" \ + "$DISCORD_WEBHOOK" From 99accd44e365d80478f1cb7ae36d6c79fb5ff0e3 Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Fri, 9 Jan 2026 18:57:24 +0700 Subject: [PATCH 020/178] update GetLastEffectRound method to accept effectiveDate parameter and adjust usage in LeaveReportController --- .../Leaves/TimeAttendants/UserDutyTimeRepository.cs | 6 +++--- BMA.EHR.Leave/Controllers/LeaveReportController.cs | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/BMA.EHR.Application/Repositories/Leaves/TimeAttendants/UserDutyTimeRepository.cs b/BMA.EHR.Application/Repositories/Leaves/TimeAttendants/UserDutyTimeRepository.cs index 3eefefe0..5ca823d9 100644 --- a/BMA.EHR.Application/Repositories/Leaves/TimeAttendants/UserDutyTimeRepository.cs +++ b/BMA.EHR.Application/Repositories/Leaves/TimeAttendants/UserDutyTimeRepository.cs @@ -101,12 +101,12 @@ namespace BMA.EHR.Application.Repositories.Leaves.TimeAttendants return data; } - public async Task GetLastEffectRound(Guid profileId) + public async Task GetLastEffectRound(Guid profileId, DateTime? effectiveDate = null) { + effectiveDate ??= DateTime.Now; var data = await _dbContext.Set() .Where(x => x.ProfileId == profileId) - .Where(x => x.IsProcess) - .Where(x => x.EffectiveDate.Value.Date <= DateTime.Now.Date) + .Where(x => x.EffectiveDate.Value.Date <= effectiveDate.Value.Date) .OrderByDescending(x => x.EffectiveDate) .FirstOrDefaultAsync(); diff --git a/BMA.EHR.Leave/Controllers/LeaveReportController.cs b/BMA.EHR.Leave/Controllers/LeaveReportController.cs index 9f88401b..059da265 100644 --- a/BMA.EHR.Leave/Controllers/LeaveReportController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveReportController.cs @@ -1768,7 +1768,8 @@ namespace BMA.EHR.Leave.Service.Controllers // return Error("ไม่พบรอบการลงเวลา Default", StatusCodes.Status404NotFound); //} - var effectiveDate = await _userDutyTimeRepository.GetLastEffectRound(p.Id); + // ให้ใช้วันที่จาก loop date แทน + var effectiveDate = await _userDutyTimeRepository.GetLastEffectRound(p.Id, dd.date); var roundId = effectiveDate != null ? effectiveDate.DutyTimeId : Guid.Empty; var userRound = await _dutyTimeRepository.GetByIdAsync(roundId); From 6e531e4d16c999107e95d0390e50ac1d559694db Mon Sep 17 00:00:00 2001 From: kittapath <> Date: Fri, 9 Jan 2026 18:59:11 +0700 Subject: [PATCH 021/178] export report leave --- .../Repositories/UserProfileRepository.cs | 36 +++++++++++++++++++ .../Controllers/LeaveReportController.cs | 10 +++--- 2 files changed, 41 insertions(+), 5 deletions(-) diff --git a/BMA.EHR.Application/Repositories/UserProfileRepository.cs b/BMA.EHR.Application/Repositories/UserProfileRepository.cs index 7ffdc024..d20cfb12 100644 --- a/BMA.EHR.Application/Repositories/UserProfileRepository.cs +++ b/BMA.EHR.Application/Repositories/UserProfileRepository.cs @@ -594,6 +594,42 @@ namespace BMA.EHR.Application.Repositories } } + public async Task> GetProfileByAdminRolev3(string? accessToken, int? node, string? nodeId, string role, string? revisionId, int? reqNode, string? reqNodeId, DateTime? startDate, DateTime? endDate) + { + try + { + var apiPath = $"{_configuration["API"]}/org/dotnet/officer-by-admin-rolev3"; + var apiKey = _configuration["API_KEY"]; + var body = new + { + node = node, + nodeId = nodeId, + role = role, + // revisionId = revisionId, + reqNode = reqNode, + reqNodeId = reqNodeId, + startDate = startDate, + endDate = endDate + }; + + var profiles = new List(); + + var apiResult = await PostExternalAPIAsync(apiPath, accessToken, body, apiKey); + if (apiResult != null) + { + var raw = JsonConvert.DeserializeObject(apiResult); + if (raw != null) + return raw.Result; + } + + return null; + } + catch + { + throw; + } + } + public async Task> GetProfileWithKeycloakAllOfficerRetireFilterAndRevision(string? accessToken, int? node, string? nodeId, bool isAll, bool? isRetirement, string? revisionId) { try diff --git a/BMA.EHR.Leave/Controllers/LeaveReportController.cs b/BMA.EHR.Leave/Controllers/LeaveReportController.cs index 9f88401b..ff7d3257 100644 --- a/BMA.EHR.Leave/Controllers/LeaveReportController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveReportController.cs @@ -962,10 +962,10 @@ namespace BMA.EHR.Leave.Service.Controllers Type3 = "☐", approve = approveResult, approverComment = !string.IsNullOrEmpty(data.LeaveDirectorComment) - ? data.LeaveDirectorComment.Replace("\r", "").Replace("\n", "").Trim() + ? data.LeaveDirectorComment.Replace("\r", "").Replace("\n", "").Trim() : "......................", - approverUpdatedAt = data.LastUpdatedAt.HasValue - ? data.LastUpdatedAt.Value.ToThaiShortDate().ToThaiNumber() + approverUpdatedAt = data.LastUpdatedAt.HasValue + ? data.LastUpdatedAt.Value.ToThaiShortDate().ToThaiNumber() : "...... /...... /......", leaveStatus = data.LeaveCancelStatus != null && data.LeaveCancelStatus!.ToUpper() == "APPROVE" ? "🗹 อนุญาต ☐ ไม่อนุญาต" @@ -1674,7 +1674,7 @@ namespace BMA.EHR.Leave.Service.Controllers if (type.Trim().ToUpper() == "OFFICER") { - profile = await _userProfileRepository.GetProfileByAdminRolev2(AccessToken, profileAdmin?.Node, nodeId, role, req.revisionId, req.node, req.nodeId, req.StartDate.Date, req.EndDate.Date); + profile = await _userProfileRepository.GetProfileByAdminRolev3(AccessToken, profileAdmin?.Node, nodeId, role, req.revisionId, req.node, req.nodeId, req.StartDate.Date, req.EndDate.Date); } else { @@ -2015,7 +2015,7 @@ namespace BMA.EHR.Leave.Service.Controllers if (type.Trim().ToUpper() == "OFFICER") { - profile = await _userProfileRepository.GetProfileByAdminRolev2(AccessToken, profileAdmin?.Node, nodeId, role, req.revisionId, req.node, req.nodeId, req.StartDate.Date, req.EndDate.Date); + profile = await _userProfileRepository.GetProfileByAdminRolev3(AccessToken, profileAdmin?.Node, nodeId, role, req.revisionId, req.node, req.nodeId, req.StartDate.Date, req.EndDate.Date); } else { From 95cd49ecbcc647df39d35a373bac7647723cd4c7 Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Fri, 9 Jan 2026 19:10:16 +0700 Subject: [PATCH 022/178] update LeaveReportController to pass date parameter to GetLastEffectRound method --- BMA.EHR.Leave/Controllers/LeaveController.cs | 70 ++++++++++++++++--- .../Controllers/LeaveReportController.cs | 2 +- 2 files changed, 60 insertions(+), 12 deletions(-) diff --git a/BMA.EHR.Leave/Controllers/LeaveController.cs b/BMA.EHR.Leave/Controllers/LeaveController.cs index d4edffb2..2ff01180 100644 --- a/BMA.EHR.Leave/Controllers/LeaveController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveController.cs @@ -877,7 +877,28 @@ namespace BMA.EHR.Leave.Service.Controllers else startTime = duty.StartTimeMorning; - var checkInStatus = DateTime.Parse(currentDate.ToString("yyyy-MM-dd HH:mm")) > + string checkInStatus = "NORMAL"; + var leaveReq = await _leaveRequestRepository.GetLeavePeriodAsync(userId, currentDate.Date); + if (leaveReq != null) + { + var leaveRange = leaveReq.LeaveRange == null ? "" : leaveReq.LeaveRange.ToUpper(); + if (leaveRange == "MORNING") + checkInStatus = "NORMAL"; + else + { + checkInStatus = DateTime.Parse(currentDate.ToString("yyyy-MM-dd HH:mm")) > + DateTime.Parse($"{currentDate.ToString("yyyy-MM-dd")} {startTime}") ? + + DateTime.Parse(currentDate.ToString("yyyy-MM-dd HH:mm")) > + DateTime.Parse($"{currentDate.ToString("yyyy-MM-dd")} {duty.EndTimeMorning}") ? + "ABSENT" : + "LATE" : + "NORMAL"; + } + } + else + { + checkInStatus = DateTime.Parse(currentDate.ToString("yyyy-MM-dd HH:mm")) > DateTime.Parse($"{currentDate.ToString("yyyy-MM-dd")} {startTime}") ? DateTime.Parse(currentDate.ToString("yyyy-MM-dd HH:mm")) > @@ -885,6 +906,8 @@ namespace BMA.EHR.Leave.Service.Controllers "ABSENT" : "LATE" : "NORMAL"; + } + // process - รอทำใน queue var checkin_process = new ProcessUserTimeStamp @@ -959,16 +982,41 @@ namespace BMA.EHR.Leave.Service.Controllers { return Error(new Exception(GlobalMessages.DataNotFound), StatusCodes.Status404NotFound); } - // fix issue : SIT ระบบบันทึกเวลาปฏิบัติงาน>>ลงเวลาเข้า-ออกงาน (กรณีลงเวลาออกอีกวัน) #921 - var checkOutStatus = DateTime.Parse(currentDate.ToString("yyyy-MM-dd HH:mm")) < - DateTime.Parse($"{currentDate.ToString("yyyy-MM-dd")} {duty.EndTimeAfternoon}") ? - // "ABSENT" : - checkout.CheckIn.Date < currentDate.Date ? "NORMAL" : - "ABSENT" : - DateTime.Parse(currentDate.ToString("yyyy-MM-dd HH:mm")) < - DateTime.Parse($"{currentDate.ToString("yyyy-MM-dd")} {duty.EndTimeMorning}") ? - "ABSENT" : - "NORMAL"; + + string checkOutStatus = "NORMAL"; + var leaveReq = await _leaveRequestRepository.GetLeavePeriodAsync(userId, currentDate.Date); + if (leaveReq != null) + { + var leaveRange = leaveReq.LeaveRange == null ? "" : leaveReq.LeaveRange.ToUpper(); + if (leaveRange == "AFTERNOON") + checkOutStatus = "NORMAL"; + else + { + // fix issue : SIT ระบบบันทึกเวลาปฏิบัติงาน>>ลงเวลาเข้า-ออกงาน (กรณีลงเวลาออกอีกวัน) #921 + checkOutStatus = DateTime.Parse(currentDate.ToString("yyyy-MM-dd HH:mm")) < + DateTime.Parse($"{currentDate.ToString("yyyy-MM-dd")} {duty.EndTimeAfternoon}") ? + // "ABSENT" : + checkout.CheckIn.Date < currentDate.Date ? "NORMAL" : + "ABSENT" : + DateTime.Parse(currentDate.ToString("yyyy-MM-dd HH:mm")) < + DateTime.Parse($"{currentDate.ToString("yyyy-MM-dd")} {duty.EndTimeMorning}") ? + "ABSENT" : + "NORMAL"; + } + } + else + { + // fix issue : SIT ระบบบันทึกเวลาปฏิบัติงาน>>ลงเวลาเข้า-ออกงาน (กรณีลงเวลาออกอีกวัน) #921 + checkOutStatus = DateTime.Parse(currentDate.ToString("yyyy-MM-dd HH:mm")) < + DateTime.Parse($"{currentDate.ToString("yyyy-MM-dd")} {duty.EndTimeAfternoon}") ? + // "ABSENT" : + checkout.CheckIn.Date < currentDate.Date ? "NORMAL" : + "ABSENT" : + DateTime.Parse(currentDate.ToString("yyyy-MM-dd HH:mm")) < + DateTime.Parse($"{currentDate.ToString("yyyy-MM-dd")} {duty.EndTimeMorning}") ? + "ABSENT" : + "NORMAL"; + } if (checkout_process != null) { diff --git a/BMA.EHR.Leave/Controllers/LeaveReportController.cs b/BMA.EHR.Leave/Controllers/LeaveReportController.cs index 059da265..ecebb4a0 100644 --- a/BMA.EHR.Leave/Controllers/LeaveReportController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveReportController.cs @@ -2099,7 +2099,7 @@ namespace BMA.EHR.Leave.Service.Controllers var fullName = $"{p.Prefix}{p.FirstName} {p.LastName}"; - var effectiveDate = await _userDutyTimeRepository.GetLastEffectRound(p.Id); + var effectiveDate = await _userDutyTimeRepository.GetLastEffectRound(p.Id, dd.date); var roundId = effectiveDate != null ? effectiveDate.DutyTimeId : Guid.Empty; var userRound = await _dutyTimeRepository.GetByIdAsync(roundId); From 3f13557b31a7cf2f01b2a1489f6badfecf6add04 Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Fri, 9 Jan 2026 19:11:52 +0700 Subject: [PATCH 023/178] update LeaveController to pass currentDate parameter to GetLastEffectRound method --- BMA.EHR.Leave/Controllers/LeaveController.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BMA.EHR.Leave/Controllers/LeaveController.cs b/BMA.EHR.Leave/Controllers/LeaveController.cs index 2ff01180..ebce7bbe 100644 --- a/BMA.EHR.Leave/Controllers/LeaveController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveController.cs @@ -812,7 +812,7 @@ namespace BMA.EHR.Leave.Service.Controllers return Error("ไม่พบรอบการลงเวลาทำงาน Default", StatusCodes.Status404NotFound); } - var effectiveDate = await _userDutyTimeRepository.GetLastEffectRound(profile.Id); + var effectiveDate = await _userDutyTimeRepository.GetLastEffectRound(profile.Id, currentDate); var roundId = effectiveDate != null ? effectiveDate.DutyTimeId : Guid.Empty; var userRound = await _dutyTimeRepository.GetByIdAsync(roundId); From 7b97cd09a347e4380aae017c11e0513b50fe137e Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Fri, 9 Jan 2026 20:24:50 +0700 Subject: [PATCH 024/178] update LeaveController to handle additional leave range options for check-in and check-out statuses --- BMA.EHR.Leave/Controllers/LeaveController.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/BMA.EHR.Leave/Controllers/LeaveController.cs b/BMA.EHR.Leave/Controllers/LeaveController.cs index ebce7bbe..9b1773ee 100644 --- a/BMA.EHR.Leave/Controllers/LeaveController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveController.cs @@ -882,7 +882,7 @@ namespace BMA.EHR.Leave.Service.Controllers if (leaveReq != null) { var leaveRange = leaveReq.LeaveRange == null ? "" : leaveReq.LeaveRange.ToUpper(); - if (leaveRange == "MORNING") + if (leaveRange == "MORNING" || leaveRange == "ALL") checkInStatus = "NORMAL"; else { @@ -987,8 +987,8 @@ namespace BMA.EHR.Leave.Service.Controllers var leaveReq = await _leaveRequestRepository.GetLeavePeriodAsync(userId, currentDate.Date); if (leaveReq != null) { - var leaveRange = leaveReq.LeaveRange == null ? "" : leaveReq.LeaveRange.ToUpper(); - if (leaveRange == "AFTERNOON") + var leaveRange = leaveReq.LeaveRangeEnd == null ? "" : leaveReq.LeaveRangeEnd.ToUpper(); + if (leaveRange == "AFTERNOON" || leaveRange == "ALL") checkOutStatus = "NORMAL"; else { From 5cdef791f3ad14370d9ece1dd9e7b15a5baf4b2c Mon Sep 17 00:00:00 2001 From: kittapath <> Date: Sun, 11 Jan 2026 15:17:02 +0700 Subject: [PATCH 025/178] report leave --- BMA.EHR.Leave/Controllers/LeaveReportController.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/BMA.EHR.Leave/Controllers/LeaveReportController.cs b/BMA.EHR.Leave/Controllers/LeaveReportController.cs index ff7d3257..52e33ac8 100644 --- a/BMA.EHR.Leave/Controllers/LeaveReportController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveReportController.cs @@ -1674,7 +1674,7 @@ namespace BMA.EHR.Leave.Service.Controllers if (type.Trim().ToUpper() == "OFFICER") { - profile = await _userProfileRepository.GetProfileByAdminRolev3(AccessToken, profileAdmin?.Node, nodeId, role, req.revisionId, req.node, req.nodeId, req.StartDate.Date, req.EndDate.Date); + profile = await _userProfileRepository.GetProfileByAdminRolev2(AccessToken, profileAdmin?.Node, nodeId, role, req.revisionId, req.node, req.nodeId, req.StartDate.Date, req.EndDate.Date); } else { @@ -2015,7 +2015,7 @@ namespace BMA.EHR.Leave.Service.Controllers if (type.Trim().ToUpper() == "OFFICER") { - profile = await _userProfileRepository.GetProfileByAdminRolev3(AccessToken, profileAdmin?.Node, nodeId, role, req.revisionId, req.node, req.nodeId, req.StartDate.Date, req.EndDate.Date); + profile = await _userProfileRepository.GetProfileByAdminRolev2(AccessToken, profileAdmin?.Node, nodeId, role, req.revisionId, req.node, req.nodeId, req.StartDate.Date, req.EndDate.Date); } else { From 49cb60dee77f478284f105158412babbe9ec8911 Mon Sep 17 00:00:00 2001 From: kittapath <> Date: Sun, 11 Jan 2026 16:35:02 +0700 Subject: [PATCH 026/178] report leave api4 --- .../Repositories/UserProfileRepository.cs | 38 +++++++++++++++++++ .../Controllers/LeaveReportController.cs | 4 +- 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/BMA.EHR.Application/Repositories/UserProfileRepository.cs b/BMA.EHR.Application/Repositories/UserProfileRepository.cs index d20cfb12..3454ae42 100644 --- a/BMA.EHR.Application/Repositories/UserProfileRepository.cs +++ b/BMA.EHR.Application/Repositories/UserProfileRepository.cs @@ -630,6 +630,44 @@ namespace BMA.EHR.Application.Repositories } } + public async Task> GetProfileByAdminRolev4(string? accessToken, int? node, string? nodeId, string role, string? revisionId, int? reqNode, string? reqNodeId, DateTime? startDate, DateTime? endDate) + { + try + { + var apiPath = $"{_configuration["API"]}/org/dotnet/officer-by-admin-rolev4"; + var apiKey = _configuration["API_KEY"]; + var body = new + { + node = node, + nodeId = nodeId, + role = role, + // revisionId = revisionId, + reqNode = reqNode, + reqNodeId = reqNodeId, + // startDate = startDate, + // endDate = endDate + date = endDate + }; + Console.WriteLine(body); + + var profiles = new List(); + + var apiResult = await PostExternalAPIAsync(apiPath, accessToken, body, apiKey); + if (apiResult != null) + { + var raw = JsonConvert.DeserializeObject(apiResult); + if (raw != null) + return raw.Result; + } + + return new List(); + } + catch + { + throw; + } + } + public async Task> GetProfileWithKeycloakAllOfficerRetireFilterAndRevision(string? accessToken, int? node, string? nodeId, bool isAll, bool? isRetirement, string? revisionId) { try diff --git a/BMA.EHR.Leave/Controllers/LeaveReportController.cs b/BMA.EHR.Leave/Controllers/LeaveReportController.cs index 52e33ac8..b1756bd1 100644 --- a/BMA.EHR.Leave/Controllers/LeaveReportController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveReportController.cs @@ -1674,7 +1674,7 @@ namespace BMA.EHR.Leave.Service.Controllers if (type.Trim().ToUpper() == "OFFICER") { - profile = await _userProfileRepository.GetProfileByAdminRolev2(AccessToken, profileAdmin?.Node, nodeId, role, req.revisionId, req.node, req.nodeId, req.StartDate.Date, req.EndDate.Date); + profile = await _userProfileRepository.GetProfileByAdminRolev4(AccessToken, profileAdmin?.Node, nodeId, role, req.revisionId, req.node, req.nodeId, req.StartDate.Date, req.EndDate.Date); } else { @@ -2015,7 +2015,7 @@ namespace BMA.EHR.Leave.Service.Controllers if (type.Trim().ToUpper() == "OFFICER") { - profile = await _userProfileRepository.GetProfileByAdminRolev2(AccessToken, profileAdmin?.Node, nodeId, role, req.revisionId, req.node, req.nodeId, req.StartDate.Date, req.EndDate.Date); + profile = await _userProfileRepository.GetProfileByAdminRolev4(AccessToken, profileAdmin?.Node, nodeId, role, req.revisionId, req.node, req.nodeId, req.StartDate.Date, req.EndDate.Date); } else { From 398679cbcc9f20d5b0187ffdd4bc00d7565ee889 Mon Sep 17 00:00:00 2001 From: waruneeauy Date: Sun, 11 Jan 2026 21:10:24 +0700 Subject: [PATCH 027/178] rollback --- .forgejo/workflows/build-checkin.yml | 83 +++++++++++++++++++++++++ .forgejo/workflows/build-discipline.yml | 83 +++++++++++++++++++++++++ .forgejo/workflows/build-insignia.yml | 83 +++++++++++++++++++++++++ .forgejo/workflows/build-leave.yml | 83 +++++++++++++++++++++++++ .forgejo/workflows/build-placement.yml | 83 +++++++++++++++++++++++++ .forgejo/workflows/build-retirement.yml | 83 +++++++++++++++++++++++++ 6 files changed, 498 insertions(+) create mode 100644 .forgejo/workflows/build-checkin.yml create mode 100644 .forgejo/workflows/build-discipline.yml create mode 100644 .forgejo/workflows/build-insignia.yml create mode 100644 .forgejo/workflows/build-leave.yml create mode 100644 .forgejo/workflows/build-placement.yml create mode 100644 .forgejo/workflows/build-retirement.yml diff --git a/.forgejo/workflows/build-checkin.yml b/.forgejo/workflows/build-checkin.yml new file mode 100644 index 00000000..3971e9df --- /dev/null +++ b/.forgejo/workflows/build-checkin.yml @@ -0,0 +1,83 @@ +name: Build & Deploy Checkin Service + +on: + push: + tags: + - "checkin-dev[0-9]+.[0-9]+.[0-9]+" + - "checkin-dev[0-9]+.[0-9]+.[0-9]+*" + workflow_dispatch: + +env: + REGISTRY: ${{ vars.CONTAINER_REGISTRY }} + REGISTRY_USERNAME: ${{ vars.CONTAINER_REGISTRY_USERNAME }} + REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }} + CONTAINER_IMAGE_NAME: ${{ vars.CONTAINER_REGISTRY }}/${{ vars.CONTAINER_IMAGE_OWNER }} + IMAGE_VERSION: latest + SERVICE_NAME: hrms-api-checkin + DISCORD_WEBHOOK: ${{ vars.DISCORD_WEBHOOK }} + +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Tag Version + shell: bash + run: | + if [[ "${{ github.event_name }}" == "push" ]]; then + VERSION=$(echo "${{ github.ref_name }}" | sed 's/checkin-dev//g') + echo "IMAGE_VERSION=${VERSION}" >> $GITHUB_ENV + else + echo "IMAGE_VERSION=${{ env.IMAGE_VERSION }}-${{ github.run_number }}" >> $GITHUB_ENV + fi + - name: Login in to registry + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ env.REGISTRY_USERNAME }} + password: ${{ env.REGISTRY_PASSWORD }} + + - name: Build and push docker image + uses: docker/build-push-action@v5 + with: + platforms: linux/amd64 + context: . + file: ./BMA.EHR.CheckInConsumer/Dockerfile + tags: ${{ env.CONTAINER_IMAGE_NAME }}/${{ env.SERVICE_NAME }}:latest,${{ env.CONTAINER_IMAGE_NAME }}/${{ env.SERVICE_NAME }}:${{ env.IMAGE_VERSION }} + push: true + + - name: Remote Deploy + uses: appleboy/ssh-action@v1.2.1 + with: + host: ${{ vars.SSH_DEPLOY_HOST }} + port: ${{ vars.SSH_DEPLOY_PORT }} + username: ${{ secrets.SSH_DEPLOY_USER }} + password: ${{ secrets.SSH_DEPLOY_PASSWORD }} + script: | + cd ~/repo + ./replace-env.sh API_CHECKIN "${{ env.IMAGE_VERSION }}" + ./deploy.sh ${{ env.SERVICE_NAME }} + + - name: Discord Notification + if: always() + run: | + STATUS="${{ job.status == 'success' && '✅ Success' || '❌ Failed' }}" + COLOR="${{ job.status == 'success' && '3066993' || '15158332' }}" + TIMESTAMP=$(date -u +%Y-%m-%dT%H:%M:%SZ) + curl -H "Content-Type: application/json" \ + -X POST \ + -d "{ + \"embeds\": [{ + \"title\": \"$STATUS\", + \"description\": \"**Build & Deploy**\\n- Image: \`${{ env.CONTAINER_IMAGE_NAME }}/${{ env.SERVICE_NAME }}\`\\n- Version: \`${{ env.IMAGE_VERSION }}\`\\n- By: \`${{ gitea.actor }}\`\", + \"color\": $COLOR, + \"footer\": { + \"text\": \"Release Notification\", + \"icon_url\": \"https://example.com/success-icon.png\" + }, + \"timestamp\": \"$TIMESTAMP\" + }] + }" \ + ${{ env.DISCORD_WEBHOOK }} diff --git a/.forgejo/workflows/build-discipline.yml b/.forgejo/workflows/build-discipline.yml new file mode 100644 index 00000000..767d775b --- /dev/null +++ b/.forgejo/workflows/build-discipline.yml @@ -0,0 +1,83 @@ +name: Build & Deploy Discipline Service + +on: + push: + tags: + - "discipline-dev[0-9]+.[0-9]+.[0-9]+" + - "discipline-dev[0-9]+.[0-9]+.[0-9]+*" + workflow_dispatch: + +env: + REGISTRY: ${{ vars.CONTAINER_REGISTRY }} + REGISTRY_USERNAME: ${{ vars.CONTAINER_REGISTRY_USERNAME }} + REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }} + CONTAINER_IMAGE_NAME: ${{ vars.CONTAINER_REGISTRY }}/${{ vars.CONTAINER_IMAGE_OWNER }} + IMAGE_VERSION: latest + SERVICE_NAME: hrms-api-discipline + DISCORD_WEBHOOK: ${{ vars.DISCORD_WEBHOOK }} + +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Tag Version + shell: bash + run: | + if [[ "${{ github.event_name }}" == "push" ]]; then + VERSION=$(echo "${{ github.ref_name }}" | sed 's/discipline-dev//g') + echo "IMAGE_VERSION=${VERSION}" >> $GITHUB_ENV + else + echo "IMAGE_VERSION=${{ env.IMAGE_VERSION }}-${{ github.run_number }}" >> $GITHUB_ENV + fi + - name: Login in to registry + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ env.REGISTRY_USERNAME }} + password: ${{ env.REGISTRY_PASSWORD }} + + - name: Build and push docker image + uses: docker/build-push-action@v5 + with: + platforms: linux/amd64 + context: . + file: ./BMA.EHR.Discipline.Service/Dockerfile + tags: ${{ env.CONTAINER_IMAGE_NAME }}/${{ env.SERVICE_NAME }}:latest,${{ env.CONTAINER_IMAGE_NAME }}/${{ env.SERVICE_NAME }}:${{ env.IMAGE_VERSION }} + push: true + + - name: Remote Deploy + uses: appleboy/ssh-action@v1.2.1 + with: + host: ${{ vars.SSH_DEPLOY_HOST }} + port: ${{ vars.SSH_DEPLOY_PORT }} + username: ${{ secrets.SSH_DEPLOY_USER }} + password: ${{ secrets.SSH_DEPLOY_PASSWORD }} + script: | + cd ~/repo + ./replace-env.sh API_DISCIPLINE "${{ env.IMAGE_VERSION }}" + ./deploy.sh ${{ env.SERVICE_NAME }} + + - name: Discord Notification + if: always() + run: | + STATUS="${{ job.status == 'success' && '✅ Success' || '❌ Failed' }}" + COLOR="${{ job.status == 'success' && '3066993' || '15158332' }}" + TIMESTAMP=$(date -u +%Y-%m-%dT%H:%M:%SZ) + curl -H "Content-Type: application/json" \ + -X POST \ + -d "{ + \"embeds\": [{ + \"title\": \"$STATUS\", + \"description\": \"**Build & Deploy**\\n- Image: \`${{ env.CONTAINER_IMAGE_NAME }}/${{ env.SERVICE_NAME }}\`\\n- Version: \`${{ env.IMAGE_VERSION }}\`\\n- By: \`${{ gitea.actor }}\`\", + \"color\": $COLOR, + \"footer\": { + \"text\": \"Release Notification\", + \"icon_url\": \"https://example.com/success-icon.png\" + }, + \"timestamp\": \"$TIMESTAMP\" + }] + }" \ + ${{ env.DISCORD_WEBHOOK }} diff --git a/.forgejo/workflows/build-insignia.yml b/.forgejo/workflows/build-insignia.yml new file mode 100644 index 00000000..7ddcbdbd --- /dev/null +++ b/.forgejo/workflows/build-insignia.yml @@ -0,0 +1,83 @@ +name: Build & Deploy Insignia Service + +on: + push: + tags: + - "insignia-dev[0-9]+.[0-9]+.[0-9]+" + - "insignia-dev[0-9]+.[0-9]+.[0-9]+*" + workflow_dispatch: + +env: + REGISTRY: ${{ vars.CONTAINER_REGISTRY }} + REGISTRY_USERNAME: ${{ vars.CONTAINER_REGISTRY_USERNAME }} + REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }} + CONTAINER_IMAGE_NAME: ${{ vars.CONTAINER_REGISTRY }}/${{ vars.CONTAINER_IMAGE_OWNER }} + IMAGE_VERSION: latest + SERVICE_NAME: hrms-api-insignia + DISCORD_WEBHOOK: ${{ vars.DISCORD_WEBHOOK }} + +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Tag Version + shell: bash + run: | + if [[ "${{ github.event_name }}" == "push" ]]; then + VERSION=$(echo "${{ github.ref_name }}" | sed 's/insignia-dev//g') + echo "IMAGE_VERSION=${VERSION}" >> $GITHUB_ENV + else + echo "IMAGE_VERSION=${{ env.IMAGE_VERSION }}-${{ github.run_number }}" >> $GITHUB_ENV + fi + - name: Login in to registry + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ env.REGISTRY_USERNAME }} + password: ${{ env.REGISTRY_PASSWORD }} + + - name: Build and push docker image + uses: docker/build-push-action@v5 + with: + platforms: linux/amd64 + context: . + file: ./BMA.EHR.Insignia/Dockerfile + tags: ${{ env.CONTAINER_IMAGE_NAME }}/${{ env.SERVICE_NAME }}:latest,${{ env.CONTAINER_IMAGE_NAME }}/${{ env.SERVICE_NAME }}:${{ env.IMAGE_VERSION }} + push: true + + - name: Remote Deploy + uses: appleboy/ssh-action@v1.2.1 + with: + host: ${{ vars.SSH_DEPLOY_HOST }} + port: ${{ vars.SSH_DEPLOY_PORT }} + username: ${{ secrets.SSH_DEPLOY_USER }} + password: ${{ secrets.SSH_DEPLOY_PASSWORD }} + script: | + cd ~/repo + ./replace-env.sh API_INSIGNIA "${{ env.IMAGE_VERSION }}" + ./deploy.sh ${{ env.SERVICE_NAME }} + + - name: Discord Notification + if: always() + run: | + STATUS="${{ job.status == 'success' && '✅ Success' || '❌ Failed' }}" + COLOR="${{ job.status == 'success' && '3066993' || '15158332' }}" + TIMESTAMP=$(date -u +%Y-%m-%dT%H:%M:%SZ) + curl -H "Content-Type: application/json" \ + -X POST \ + -d "{ + \"embeds\": [{ + \"title\": \"$STATUS\", + \"description\": \"**Build & Deploy**\\n- Image: \`${{ env.CONTAINER_IMAGE_NAME }}/${{ env.SERVICE_NAME }}\`\\n- Version: \`${{ env.IMAGE_VERSION }}\`\\n- By: \`${{ gitea.actor }}\`\", + \"color\": $COLOR, + \"footer\": { + \"text\": \"Release Notification\", + \"icon_url\": \"https://example.com/success-icon.png\" + }, + \"timestamp\": \"$TIMESTAMP\" + }] + }" \ + ${{ env.DISCORD_WEBHOOK }} diff --git a/.forgejo/workflows/build-leave.yml b/.forgejo/workflows/build-leave.yml new file mode 100644 index 00000000..b1c9c168 --- /dev/null +++ b/.forgejo/workflows/build-leave.yml @@ -0,0 +1,83 @@ +name: Build & Deploy Leave Service + +on: + push: + tags: + - "leave-dev[0-9]+.[0-9]+.[0-9]+" + - "leave-dev[0-9]+.[0-9]+.[0-9]+*" + workflow_dispatch: + +env: + REGISTRY: ${{ vars.CONTAINER_REGISTRY }} + REGISTRY_USERNAME: ${{ vars.CONTAINER_REGISTRY_USERNAME }} + REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }} + CONTAINER_IMAGE_NAME: ${{ vars.CONTAINER_REGISTRY }}/${{ vars.CONTAINER_IMAGE_OWNER }} + IMAGE_VERSION: latest + SERVICE_NAME: hrms-api-leave + DISCORD_WEBHOOK: ${{ vars.DISCORD_WEBHOOK }} + +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Tag Version + shell: bash + run: | + if [[ "${{ github.event_name }}" == "push" ]]; then + VERSION=$(echo "${{ github.ref_name }}" | sed 's/leave-dev//g') + echo "IMAGE_VERSION=${VERSION}" >> $GITHUB_ENV + else + echo "IMAGE_VERSION=${{ env.IMAGE_VERSION }}-${{ github.run_number }}" >> $GITHUB_ENV + fi + - name: Login in to registry + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ env.REGISTRY_USERNAME }} + password: ${{ env.REGISTRY_PASSWORD }} + + - name: Build and push docker image + uses: docker/build-push-action@v5 + with: + platforms: linux/amd64 + context: . + file: ./BMA.EHR.Leave/Dockerfile + tags: ${{ env.CONTAINER_IMAGE_NAME }}/${{ env.SERVICE_NAME }}:latest,${{ env.CONTAINER_IMAGE_NAME }}/${{ env.SERVICE_NAME }}:${{ env.IMAGE_VERSION }} + push: true + + - name: Remote Deploy + uses: appleboy/ssh-action@v1.2.1 + with: + host: ${{ vars.SSH_DEPLOY_HOST }} + port: ${{ vars.SSH_DEPLOY_PORT }} + username: ${{ secrets.SSH_DEPLOY_USER }} + password: ${{ secrets.SSH_DEPLOY_PASSWORD }} + script: | + cd ~/repo + ./replace-env.sh API_LEAVE "${{ env.IMAGE_VERSION }}" + ./deploy.sh ${{ env.SERVICE_NAME }} + + - name: Discord Notification + if: always() + run: | + STATUS="${{ job.status == 'success' && '✅ Success' || '❌ Failed' }}" + COLOR="${{ job.status == 'success' && '3066993' || '15158332' }}" + TIMESTAMP=$(date -u +%Y-%m-%dT%H:%M:%SZ) + curl -H "Content-Type: application/json" \ + -X POST \ + -d "{ + \"embeds\": [{ + \"title\": \"$STATUS\", + \"description\": \"**Build & Deploy**\\n- Image: \`${{ env.CONTAINER_IMAGE_NAME }}/${{ env.SERVICE_NAME }}\`\\n- Version: \`${{ env.IMAGE_VERSION }}\`\\n- By: \`${{ gitea.actor }}\`\", + \"color\": $COLOR, + \"footer\": { + \"text\": \"Release Notification\", + \"icon_url\": \"https://example.com/success-icon.png\" + }, + \"timestamp\": \"$TIMESTAMP\" + }] + }" \ + ${{ env.DISCORD_WEBHOOK }} diff --git a/.forgejo/workflows/build-placement.yml b/.forgejo/workflows/build-placement.yml new file mode 100644 index 00000000..351b1e59 --- /dev/null +++ b/.forgejo/workflows/build-placement.yml @@ -0,0 +1,83 @@ +name: Build & Deploy Placement Service + +on: + push: + tags: + - "placement-dev[0-9]+.[0-9]+.[0-9]+" + - "placement-dev[0-9]+.[0-9]+.[0-9]+*" + workflow_dispatch: + +env: + REGISTRY: ${{ vars.CONTAINER_REGISTRY }} + REGISTRY_USERNAME: ${{ vars.CONTAINER_REGISTRY_USERNAME }} + REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }} + CONTAINER_IMAGE_NAME: ${{ vars.CONTAINER_REGISTRY }}/${{ vars.CONTAINER_IMAGE_OWNER }} + IMAGE_VERSION: latest + SERVICE_NAME: hrms-api-placement + DISCORD_WEBHOOK: ${{ vars.DISCORD_WEBHOOK }} + +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Tag Version + shell: bash + run: | + if [[ "${{ github.event_name }}" == "push" ]]; then + VERSION=$(echo "${{ github.ref_name }}" | sed 's/placement-dev//g') + echo "IMAGE_VERSION=${VERSION}" >> $GITHUB_ENV + else + echo "IMAGE_VERSION=${{ env.IMAGE_VERSION }}-${{ github.run_number }}" >> $GITHUB_ENV + fi + - name: Login in to registry + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ env.REGISTRY_USERNAME }} + password: ${{ env.REGISTRY_PASSWORD }} + + - name: Build and push docker image + uses: docker/build-push-action@v5 + with: + platforms: linux/amd64 + context: . + file: ./BMA.EHR.Placement.Service/Dockerfile + tags: ${{ env.CONTAINER_IMAGE_NAME }}/${{ env.SERVICE_NAME }}:latest,${{ env.CONTAINER_IMAGE_NAME }}/${{ env.SERVICE_NAME }}:${{ env.IMAGE_VERSION }} + push: true + + - name: Remote Deploy + uses: appleboy/ssh-action@v1.2.1 + with: + host: ${{ vars.SSH_DEPLOY_HOST }} + port: ${{ vars.SSH_DEPLOY_PORT }} + username: ${{ secrets.SSH_DEPLOY_USER }} + password: ${{ secrets.SSH_DEPLOY_PASSWORD }} + script: | + cd ~/repo + ./replace-env.sh API_PLACEMENT "${{ env.IMAGE_VERSION }}" + ./deploy.sh ${{ env.SERVICE_NAME }} + + - name: Discord Notification + if: always() + run: | + STATUS="${{ job.status == 'success' && '✅ Success' || '❌ Failed' }}" + COLOR="${{ job.status == 'success' && '3066993' || '15158332' }}" + TIMESTAMP=$(date -u +%Y-%m-%dT%H:%M:%SZ) + curl -H "Content-Type: application/json" \ + -X POST \ + -d "{ + \"embeds\": [{ + \"title\": \"$STATUS\", + \"description\": \"**Build & Deploy**\\n- Image: \`${{ env.CONTAINER_IMAGE_NAME }}/${{ env.SERVICE_NAME }}\`\\n- Version: \`${{ env.IMAGE_VERSION }}\`\\n- By: \`${{ gitea.actor }}\`\", + \"color\": $COLOR, + \"footer\": { + \"text\": \"Release Notification\", + \"icon_url\": \"https://example.com/success-icon.png\" + }, + \"timestamp\": \"$TIMESTAMP\" + }] + }" \ + ${{ env.DISCORD_WEBHOOK }} diff --git a/.forgejo/workflows/build-retirement.yml b/.forgejo/workflows/build-retirement.yml new file mode 100644 index 00000000..257f68f2 --- /dev/null +++ b/.forgejo/workflows/build-retirement.yml @@ -0,0 +1,83 @@ +name: Build & Deploy Retirement Service + +on: + push: + tags: + - "retirement-dev[0-9]+.[0-9]+.[0-9]+" + - "retirement-dev[0-9]+.[0-9]+.[0-9]+*" + workflow_dispatch: + +env: + REGISTRY: ${{ vars.CONTAINER_REGISTRY }} + REGISTRY_USERNAME: ${{ vars.CONTAINER_REGISTRY_USERNAME }} + REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }} + CONTAINER_IMAGE_NAME: ${{ vars.CONTAINER_REGISTRY }}/${{ vars.CONTAINER_IMAGE_OWNER }} + IMAGE_VERSION: latest + SERVICE_NAME: hrms-api-retirement + DISCORD_WEBHOOK: ${{ vars.DISCORD_WEBHOOK }} + +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Tag Version + shell: bash + run: | + if [[ "${{ github.event_name }}" == "push" ]]; then + VERSION=$(echo "${{ github.ref_name }}" | sed 's/retirement-dev//g') + echo "IMAGE_VERSION=${VERSION}" >> $GITHUB_ENV + else + echo "IMAGE_VERSION=${{ env.IMAGE_VERSION }}-${{ github.run_number }}" >> $GITHUB_ENV + fi + - name: Login in to registry + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ env.REGISTRY_USERNAME }} + password: ${{ env.REGISTRY_PASSWORD }} + + - name: Build and push docker image + uses: docker/build-push-action@v5 + with: + platforms: linux/amd64 + context: . + file: ./BMA.EHR.Retirement.Service/Dockerfile + tags: ${{ env.CONTAINER_IMAGE_NAME }}/${{ env.SERVICE_NAME }}:latest,${{ env.CONTAINER_IMAGE_NAME }}/${{ env.SERVICE_NAME }}:${{ env.IMAGE_VERSION }} + push: true + + - name: Remote Deploy + uses: appleboy/ssh-action@v1.2.1 + with: + host: ${{ vars.SSH_DEPLOY_HOST }} + port: ${{ vars.SSH_DEPLOY_PORT }} + username: ${{ secrets.SSH_DEPLOY_USER }} + password: ${{ secrets.SSH_DEPLOY_PASSWORD }} + script: | + cd ~/repo + ./replace-env.sh API_RETIREMENT "${{ env.IMAGE_VERSION }}" + ./deploy.sh ${{ env.SERVICE_NAME }} + + - name: Discord Notification + if: always() + run: | + STATUS="${{ job.status == 'success' && '✅ Success' || '❌ Failed' }}" + COLOR="${{ job.status == 'success' && '3066993' || '15158332' }}" + TIMESTAMP=$(date -u +%Y-%m-%dT%H:%M:%SZ) + curl -H "Content-Type: application/json" \ + -X POST \ + -d "{ + \"embeds\": [{ + \"title\": \"$STATUS\", + \"description\": \"**Build & Deploy**\\n- Image: \`${{ env.CONTAINER_IMAGE_NAME }}/${{ env.SERVICE_NAME }}\`\\n- Version: \`${{ env.IMAGE_VERSION }}\`\\n- By: \`${{ gitea.actor }}\`\", + \"color\": $COLOR, + \"footer\": { + \"text\": \"Release Notification\", + \"icon_url\": \"https://example.com/success-icon.png\" + }, + \"timestamp\": \"$TIMESTAMP\" + }] + }" \ + ${{ env.DISCORD_WEBHOOK }} From 1c3ce46bcb843d19a77b6b1612d09769ff0c3d45 Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Mon, 12 Jan 2026 13:41:23 +0700 Subject: [PATCH 028/178] update LeaveController to determine status based on leave request and range #2187 --- BMA.EHR.Leave/Controllers/LeaveController.cs | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/BMA.EHR.Leave/Controllers/LeaveController.cs b/BMA.EHR.Leave/Controllers/LeaveController.cs index 9b1773ee..7dafb605 100644 --- a/BMA.EHR.Leave/Controllers/LeaveController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveController.cs @@ -2437,7 +2437,23 @@ namespace BMA.EHR.Leave.Service.Controllers { if (time < endTime) { - status = "ABSENT"; + + //string checkOutStatus = "NORMAL"; + var leaveReq = await _leaveRequestRepository.GetLeavePeriodAsync(userId, time.Date); + if (leaveReq != null) + { + var leaveRange = leaveReq.LeaveRangeEnd == null ? "" : leaveReq.LeaveRangeEnd.ToUpper(); + if (leaveRange == "AFTERNOON" || leaveRange == "ALL") + status = "NORMAL"; + else + { + status = "ABSENT"; + } + } + else + { + status = "ABSENT"; + } } else { From 90ea98683181036707f48e797a8121f70a427784 Mon Sep 17 00:00:00 2001 From: harid Date: Mon, 12 Jan 2026 17:23:08 +0700 Subject: [PATCH 029/178] =?UTF-8?q?fix=20=E0=B8=A3=E0=B8=B2=E0=B8=A2?= =?UTF-8?q?=E0=B8=87=E0=B8=B2=E0=B8=99=E0=B9=83=E0=B8=9A=E0=B8=A5=E0=B8=B2?= =?UTF-8?q?=20=E0=B8=82=E0=B9=89=E0=B8=AD=E0=B8=A1=E0=B8=B9=E0=B8=A5?= =?UTF-8?q?=E0=B9=81=E0=B8=AA=E0=B8=94=E0=B8=87=E0=B9=83=E0=B8=99=E0=B8=A3?= =?UTF-8?q?=E0=B8=B2=E0=B8=A2=E0=B8=87=E0=B8=B2=E0=B8=99=E0=B9=81=E0=B8=AA?= =?UTF-8?q?=E0=B8=94=E0=B8=87=E0=B9=84=E0=B8=A1=E0=B9=88=E0=B8=84=E0=B8=A3?= =?UTF-8?q?=E0=B8=9A=20#2184?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BMA.EHR.Leave/Controllers/LeaveReportController.cs | 7 +++---- BMA.EHR.Leave/Controllers/LeaveRequestController.cs | 8 ++++---- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/BMA.EHR.Leave/Controllers/LeaveReportController.cs b/BMA.EHR.Leave/Controllers/LeaveReportController.cs index 55cb2378..e839af77 100644 --- a/BMA.EHR.Leave/Controllers/LeaveReportController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveReportController.cs @@ -2618,10 +2618,9 @@ namespace BMA.EHR.Leave.Service.Controllers updatedAt = x.LastUpdatedAt.HasValue ? x.LastUpdatedAt.Value.Date.ToThaiShortDate().ToThaiNumber() : "...... /...... /......", - comment = (x.Comment ?? "") - .Replace("\r", "") - .Replace("\n", "") - .Trim(), + comment = !string.IsNullOrEmpty(x.Comment) + ? x.Comment.Replace("\r", "").Replace("\n", "").Trim() + : "......................", approveType = (x.ApproveType ?? "").Trim().ToUpper() }) .ToList(); diff --git a/BMA.EHR.Leave/Controllers/LeaveRequestController.cs b/BMA.EHR.Leave/Controllers/LeaveRequestController.cs index 9233b374..cf17535f 100644 --- a/BMA.EHR.Leave/Controllers/LeaveRequestController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveRequestController.cs @@ -2027,10 +2027,10 @@ namespace BMA.EHR.Leave.Service.Controllers { new LeaveRequestApprover { - Prefix = profile.Prefix, - FirstName = profile.FirstName, - LastName = profile.LastName, - PositionName = profile.Position, + Prefix = profile.Prefix ?? "", + FirstName = profile.FirstName ?? "", + LastName = profile.LastName ?? "", + PositionName = $"{profile.Position ?? ""}{profile.PositionLeaveName ?? ""}", ProfileId = profile.Id, KeycloakId = Guid.Parse(UserId!), ApproveType = "SENDER", From 0233d929319584388c2401876f49cde4946b7b60 Mon Sep 17 00:00:00 2001 From: harid Date: Tue, 13 Jan 2026 12:05:52 +0700 Subject: [PATCH 030/178] Change function call Org Service --- BMA.EHR.Application/Repositories/UserProfileRepository.cs | 4 ++-- BMA.EHR.Leave/Controllers/LeaveReportController.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/BMA.EHR.Application/Repositories/UserProfileRepository.cs b/BMA.EHR.Application/Repositories/UserProfileRepository.cs index 3454ae42..1c9d5596 100644 --- a/BMA.EHR.Application/Repositories/UserProfileRepository.cs +++ b/BMA.EHR.Application/Repositories/UserProfileRepository.cs @@ -534,8 +534,8 @@ namespace BMA.EHR.Application.Repositories revisionId = revisionId, reqNode = reqNode, reqNodeId = reqNodeId, - startDate = startDate, - endDate = endDate + //startDate = startDate, + //endDate = endDate }; var profiles = new List(); diff --git a/BMA.EHR.Leave/Controllers/LeaveReportController.cs b/BMA.EHR.Leave/Controllers/LeaveReportController.cs index e839af77..50974f31 100644 --- a/BMA.EHR.Leave/Controllers/LeaveReportController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveReportController.cs @@ -1045,7 +1045,7 @@ namespace BMA.EHR.Leave.Service.Controllers if (type.Trim().ToUpper() == "OFFICER") { - profile = await _userProfileRepository.GetProfileByAdminRole(AccessToken, profileAdmin?.Node, nodeId, role, req.revisionId, req.node, req.nodeId, req.StartDate.Date, req.EndDate.Date); + profile = await _userProfileRepository.GetProfileByAdminRolev4(AccessToken, profileAdmin?.Node, nodeId, role, req.revisionId, req.node, req.nodeId, req.StartDate.Date, req.EndDate.Date); } else { From 6907607a06afbd07a425e21a4b497177b57c7a55 Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Tue, 13 Jan 2026 17:01:20 +0700 Subject: [PATCH 031/178] refactor LeaveController to improve sorting and pagination logic #2192 --- .vscode/launch.json | 68 +++---- BMA.EHR.Leave/Controllers/LeaveController.cs | 191 ++++++++++++------- 2 files changed, 158 insertions(+), 101 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index fd9cf53a..205e817c 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -1,35 +1,35 @@ { - "version": "0.2.0", - "configurations": [ - { - // Use IntelliSense to find out which attributes exist for C# debugging - // Use hover for the description of the existing attributes - // For further information visit https://github.com/dotnet/vscode-csharp/blob/main/debugger-launchjson.md. - "name": ".NET Core Launch (web)", - "type": "coreclr", - "request": "launch", - "preLaunchTask": "build", - // If you have changed target frameworks, make sure to update the program path. - "program": "${workspaceFolder}/BMA.EHR.Leave.Service/bin/Debug/net7.0/BMA.EHR.Leave.Service.dll", - "args": [], - "cwd": "${workspaceFolder}/BMA.EHR.Leave.Service", - "stopAtEntry": false, - // Enable launching a web browser when ASP.NET Core starts. For more information: https://aka.ms/VSCode-CS-LaunchJson-WebBrowser - "serverReadyAction": { - "action": "openExternally", - "pattern": "\\bNow listening on:\\s+(https?://\\S+)" - }, - "env": { - "ASPNETCORE_ENVIRONMENT": "Development" - }, - "sourceFileMap": { - "/Views": "${workspaceFolder}/Views" - } - }, - { - "name": ".NET Core Attach", - "type": "coreclr", - "request": "attach" - } - ] -} \ No newline at end of file + "version": "0.2.0", + "configurations": [ + { + // Use IntelliSense to find out which attributes exist for C# debugging + // Use hover for the description of the existing attributes + // For further information visit https://github.com/dotnet/vscode-csharp/blob/main/debugger-launchjson.md. + "name": ".NET Core Launch (web)", + "type": "coreclr", + "request": "launch", + "preLaunchTask": "build", + // If you have changed target frameworks, make sure to update the program path. + "program": "${workspaceFolder}/BMA.EHR.Leave/bin/Debug/net7.0/BMA.EHR.Leave.dll", + "args": [], + "cwd": "${workspaceFolder}/BMA.EHR.Leave", + "stopAtEntry": false, + // Enable launching a web browser when ASP.NET Core starts. For more information: https://aka.ms/VSCode-CS-LaunchJson-WebBrowser + "serverReadyAction": { + "action": "openExternally", + "pattern": "\\bNow listening on:\\s+(https?://\\S+)" + }, + "env": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "sourceFileMap": { + "/Views": "${workspaceFolder}/Views" + } + }, + { + "name": ".NET Core Attach", + "type": "coreclr", + "request": "attach" + } + ] +} diff --git a/BMA.EHR.Leave/Controllers/LeaveController.cs b/BMA.EHR.Leave/Controllers/LeaveController.cs index 7dafb605..3e0c1d54 100644 --- a/BMA.EHR.Leave/Controllers/LeaveController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveController.cs @@ -2591,6 +2591,8 @@ namespace BMA.EHR.Leave.Service.Controllers //var rawData = await _additionalCheckRequestRepository.GetAdditionalCheckRequests(year, month); var rawData = await _additionalCheckRequestRepository.GetAdditionalCheckRequestsByAdminRole(year, month, role, nodeId, profileAdmin?.Node); + var total = rawData.Count; + var getDefaultRound = await _dutyTimeRepository.GetDefaultAsync(); if (getDefaultRound == null) { @@ -2599,7 +2601,65 @@ namespace BMA.EHR.Leave.Service.Controllers var result = new List(); - foreach (var data in rawData) + + if (!string.IsNullOrWhiteSpace(sortBy)) + { + switch (sortBy.ToUpper()) + { + case "FULLNAME": + if (descending == true) + rawData = rawData.OrderByDescending(x => x.Prefix) + .ThenByDescending(x => x.FirstName) + .ThenByDescending(x => x.LastName) + .ToList(); + else + rawData = rawData.OrderBy(x => x.Prefix) + .ThenBy(x => x.FirstName) + .ThenBy(x => x.LastName) + .ToList(); + break; + case "CREATEDAT": + if (descending == true) + rawData = rawData.OrderByDescending(x => x.CreatedAt).ToList(); + else + rawData = rawData.OrderBy(x => x.CreatedAt).ToList(); + break; + case "CHECKDATE": + if (descending == true) + rawData = rawData.OrderByDescending(x => x.CheckDate).ToList(); + else + rawData = rawData.OrderBy(x => x.CheckDate).ToList(); + break; + // case "STARTTIMEMORNING": + // if (descending == true) + // rawData = rawData.OrderByDescending(x => x.StartTimeMorning).ToList(); + // else + // result = result.OrderBy(x => x.StartTimeMorning).ToList(); + // break; + // case "STARTTIMEAFTERNOON": + // if (descending == true) + // result = result.OrderByDescending(x => x.StartTimeAfternoon).ToList(); + // else + // result = result.OrderBy(x => x.StartTimeAfternoon).ToList(); + // break; + case "DESCRIPTION": + if (descending == true) + rawData = rawData.OrderByDescending(x => x.Description).ToList(); + else + rawData = rawData.OrderBy(x => x.Description).ToList(); + break; + default: + rawData = rawData.OrderBy(x => + x.Status.Trim().ToLower() == "pending" ? 1 : + x.Status.Trim().ToLower() == "approve" ? 2 : 3).ToList(); + break; + } + } + + var rawDataPaged = rawData.Skip((page - 1) * pageSize).Take(pageSize) + .ToList(); + + foreach (var data in rawDataPaged) { var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(data.KeycloakUserId, AccessToken); if (profile == null) @@ -2678,69 +2738,69 @@ namespace BMA.EHR.Leave.Service.Controllers result.Add(resObj); } - if (keyword != "") - { - result = result.Where(x => x.FullName.Contains(keyword)).ToList(); - } - if (string.IsNullOrWhiteSpace(sortBy)) - { - sortBy = "default"; - } - if (!string.IsNullOrWhiteSpace(sortBy)) - { - switch (sortBy.ToUpper()) - { - case "FULLNAME": - if (descending == true) - result = result.OrderByDescending(x => x.Prefix) - .ThenByDescending(x => x.FirstName) - .ThenByDescending(x => x.LastName) - .ToList(); - else - result = result.OrderBy(x => x.Prefix) - .ThenBy(x => x.FirstName) - .ThenBy(x => x.LastName) - .ToList(); - break; - case "CREATEDAT": - if (descending == true) - result = result.OrderByDescending(x => x.CreatedAt).ToList(); - else - result = result.OrderBy(x => x.CreatedAt).ToList(); - break; - case "CHECKDATE": - if (descending == true) - result = result.OrderByDescending(x => x.CheckDate).ToList(); - else - result = result.OrderBy(x => x.CheckDate).ToList(); - break; - case "STARTTIMEMORNING": - if (descending == true) - result = result.OrderByDescending(x => x.StartTimeMorning).ToList(); - else - result = result.OrderBy(x => x.StartTimeMorning).ToList(); - break; - case "STARTTIMEAFTERNOON": - if (descending == true) - result = result.OrderByDescending(x => x.StartTimeAfternoon).ToList(); - else - result = result.OrderBy(x => x.StartTimeAfternoon).ToList(); - break; - case "DESCRIPTION": - if (descending == true) - result = result.OrderByDescending(x => x.Description).ToList(); - else - result = result.OrderBy(x => x.Description).ToList(); - break; - default: - result = result.OrderBy(x => x.StatusSort).ToList(); - break; - } - } - var pageResult = result.Skip((page - 1) * pageSize).Take(pageSize) - .ToList(); + // if (keyword != "") + // { + // result = result.Where(x => x.FullName.Contains(keyword)).ToList(); + // } + // if (string.IsNullOrWhiteSpace(sortBy)) + // { + // sortBy = "default"; + // } + // if (!string.IsNullOrWhiteSpace(sortBy)) + // { + // switch (sortBy.ToUpper()) + // { + // case "FULLNAME": + // if (descending == true) + // result = result.OrderByDescending(x => x.Prefix) + // .ThenByDescending(x => x.FirstName) + // .ThenByDescending(x => x.LastName) + // .ToList(); + // else + // result = result.OrderBy(x => x.Prefix) + // .ThenBy(x => x.FirstName) + // .ThenBy(x => x.LastName) + // .ToList(); + // break; + // case "CREATEDAT": + // if (descending == true) + // result = result.OrderByDescending(x => x.CreatedAt).ToList(); + // else + // result = result.OrderBy(x => x.CreatedAt).ToList(); + // break; + // case "CHECKDATE": + // if (descending == true) + // result = result.OrderByDescending(x => x.CheckDate).ToList(); + // else + // result = result.OrderBy(x => x.CheckDate).ToList(); + // break; + // case "STARTTIMEMORNING": + // if (descending == true) + // result = result.OrderByDescending(x => x.StartTimeMorning).ToList(); + // else + // result = result.OrderBy(x => x.StartTimeMorning).ToList(); + // break; + // case "STARTTIMEAFTERNOON": + // if (descending == true) + // result = result.OrderByDescending(x => x.StartTimeAfternoon).ToList(); + // else + // result = result.OrderBy(x => x.StartTimeAfternoon).ToList(); + // break; + // case "DESCRIPTION": + // if (descending == true) + // result = result.OrderByDescending(x => x.Description).ToList(); + // else + // result = result.OrderBy(x => x.Description).ToList(); + // break; + // default: + // result = result.OrderBy(x => x.StatusSort).ToList(); + // break; + // } + // } + // var pageResult = result.Skip((page - 1) * pageSize).Take(pageSize) + // .ToList(); - return Success(new { data = pageResult, total = result.Count }); + return Success(new { data = result, total = total }); } /// @@ -3170,10 +3230,7 @@ namespace BMA.EHR.Leave.Service.Controllers result.Add(resObj); } - if (keyword != "") - { - result = result.Where(x => x.EditReason!.Contains(keyword) || x.CheckInLocation!.Contains(keyword) || x.CheckOutLocation!.Contains(keyword)).ToList(); - } + var pageResult = result.Skip((page - 1) * pageSize).Take(pageSize) .ToList(); From 86790cf9f379d07166beffef59e5924750d8181e Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Wed, 14 Jan 2026 10:16:42 +0700 Subject: [PATCH 032/178] refactor LeaveReportController to enhance employee sorting by remark and check-in/check-out times #2193 --- BMA.EHR.Leave/Controllers/LeaveReportController.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/BMA.EHR.Leave/Controllers/LeaveReportController.cs b/BMA.EHR.Leave/Controllers/LeaveReportController.cs index 50974f31..6a09e2b9 100644 --- a/BMA.EHR.Leave/Controllers/LeaveReportController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveReportController.cs @@ -2250,7 +2250,11 @@ namespace BMA.EHR.Leave.Service.Controllers } } //employees = employees.OrderBy(x => x.checkInDate).ThenBy(x => x.checkInTimeRaw ?? DateTime.MaxValue).ThenBy(x => x.checkOutTimeRaw ?? DateTime.MaxValue).ToList(); - employees = employees.OrderBy(x => x.checkInTimeRaw ?? DateTime.MaxValue).ThenBy(x => x.checkOutTimeRaw ?? DateTime.MaxValue).ToList(); + employees = employees + .OrderBy(x => x.remark.Trim() == "" ? 0 : 1) // ข้อมูลที่ไม่มี remark ให้ขึ้นก่อน + .ThenBy(x => x.checkInTimeRaw ?? DateTime.MaxValue).ThenBy(x => x.checkOutTimeRaw ?? DateTime.MaxValue) + .ThenBy(x => x.remark) // จากนั้นจัดเรียงตาม remark + .ToList(); for (int i = 0; i < employees.Count; i++) { employees[i].no = i + 1; From 5415019b3cd70ded219a4534349aeb739ab26f41 Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Wed, 14 Jan 2026 10:25:31 +0700 Subject: [PATCH 033/178] refactor LeaveController to update image URL handling for check-in and check-out #2188 --- BMA.EHR.Leave/Controllers/LeaveController.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/BMA.EHR.Leave/Controllers/LeaveController.cs b/BMA.EHR.Leave/Controllers/LeaveController.cs index 3e0c1d54..e730a2a9 100644 --- a/BMA.EHR.Leave/Controllers/LeaveController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveController.cs @@ -3069,7 +3069,8 @@ namespace BMA.EHR.Leave.Service.Controllers CheckInPOI = d.CheckInPOI, CheckInLat = d.CheckInLat, CheckInLon = d.CheckInLon, - CheckInImg = $"{imgUrl}/{d.CheckInImageUrl}", + // CheckInImg = $"{imgUrl}/{d.CheckInImageUrl}", + CheckInImg = await _minIOService.ImagesPathByName(d.CheckInImageUrl), CheckInStatus = DateTime.Parse(d.CheckIn.ToString("yyyy-MM-dd HH:mm")) > DateTime.Parse($"{d.CheckIn.Date.ToString("yyyy-MM-dd")} {duty.StartTimeMorning}") ? @@ -3086,7 +3087,7 @@ namespace BMA.EHR.Leave.Service.Controllers CheckOutPOI = d.CheckOut == null ? "" : d.CheckOutPOI, CheckOutLat = d.CheckOut == null ? null : d.CheckOutLat, CheckOutLon = d.CheckOut == null ? null : d.CheckOutLon, - CheckOutImg = d.CheckOut == null ? "" : $"{imgUrl}/{d.CheckOutImageUrl}", + CheckOutImg = d.CheckOut == null ? "" : await _minIOService.ImagesPathByName(d.CheckOutImageUrl), CheckOutStatus = d.CheckOut == null ? null : DateTime.Parse(d.CheckOut.Value.ToString("yyyy-MM-dd HH:mm")) < From 3a6e4501fd08a6e6168ff401be0d5d2a7dd2a043 Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Wed, 14 Jan 2026 16:38:55 +0700 Subject: [PATCH 034/178] refactor LeaveController to update start and end time logic for check-in and check-out #2199 --- BMA.EHR.Leave/Controllers/LeaveController.cs | 22 +++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/BMA.EHR.Leave/Controllers/LeaveController.cs b/BMA.EHR.Leave/Controllers/LeaveController.cs index e730a2a9..ab0e61b9 100644 --- a/BMA.EHR.Leave/Controllers/LeaveController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveController.cs @@ -872,7 +872,8 @@ namespace BMA.EHR.Leave.Service.Controllers var startTime = ""; if (!data.IsLocation && data.LocationName == "ไปประชุม / อบรม / สัมมนา") { - startTime = "09:30"; + //startTime = "09:30"; + startTime = "10:30"; } else startTime = duty.StartTimeMorning; @@ -888,7 +889,6 @@ namespace BMA.EHR.Leave.Service.Controllers { checkInStatus = DateTime.Parse(currentDate.ToString("yyyy-MM-dd HH:mm")) > DateTime.Parse($"{currentDate.ToString("yyyy-MM-dd")} {startTime}") ? - DateTime.Parse(currentDate.ToString("yyyy-MM-dd HH:mm")) > DateTime.Parse($"{currentDate.ToString("yyyy-MM-dd")} {duty.EndTimeMorning}") ? "ABSENT" : @@ -900,7 +900,6 @@ namespace BMA.EHR.Leave.Service.Controllers { checkInStatus = DateTime.Parse(currentDate.ToString("yyyy-MM-dd HH:mm")) > DateTime.Parse($"{currentDate.ToString("yyyy-MM-dd")} {startTime}") ? - DateTime.Parse(currentDate.ToString("yyyy-MM-dd HH:mm")) > DateTime.Parse($"{currentDate.ToString("yyyy-MM-dd")} {duty.EndTimeMorning}") ? "ABSENT" : @@ -956,8 +955,6 @@ namespace BMA.EHR.Leave.Service.Controllers } else { - - var checkout = await _userTimeStampRepository.GetByIdAsync(data.CheckInId.Value); var currentCheckInProcess = await _processUserTimeStampRepository.GetTimestampByDateAsync(userId, checkout.CheckIn.Date); @@ -983,6 +980,15 @@ namespace BMA.EHR.Leave.Service.Controllers return Error(new Exception(GlobalMessages.DataNotFound), StatusCodes.Status404NotFound); } + var endTime = ""; + if (!data.IsLocation && data.LocationName == "ไปประชุม / อบรม / สัมมนา") + { + //startTime = "09:30"; + endTime = "14:30"; + } + else + endTime = duty.EndTimeAfternoon; + string checkOutStatus = "NORMAL"; var leaveReq = await _leaveRequestRepository.GetLeavePeriodAsync(userId, currentDate.Date); if (leaveReq != null) @@ -997,6 +1003,9 @@ namespace BMA.EHR.Leave.Service.Controllers DateTime.Parse($"{currentDate.ToString("yyyy-MM-dd")} {duty.EndTimeAfternoon}") ? // "ABSENT" : checkout.CheckIn.Date < currentDate.Date ? "NORMAL" : + DateTime.Parse(currentDate.ToString("yyyy-MM-dd HH:mm")) >= + DateTime.Parse($"{currentDate.ToString("yyyy-MM-dd")} {endTime}") ? + "NORMAL" : "ABSENT" : DateTime.Parse(currentDate.ToString("yyyy-MM-dd HH:mm")) < DateTime.Parse($"{currentDate.ToString("yyyy-MM-dd")} {duty.EndTimeMorning}") ? @@ -1011,6 +1020,9 @@ namespace BMA.EHR.Leave.Service.Controllers DateTime.Parse($"{currentDate.ToString("yyyy-MM-dd")} {duty.EndTimeAfternoon}") ? // "ABSENT" : checkout.CheckIn.Date < currentDate.Date ? "NORMAL" : + DateTime.Parse(currentDate.ToString("yyyy-MM-dd HH:mm")) >= + DateTime.Parse($"{currentDate.ToString("yyyy-MM-dd")} {endTime}") ? + "NORMAL" : "ABSENT" : DateTime.Parse(currentDate.ToString("yyyy-MM-dd HH:mm")) < DateTime.Parse($"{currentDate.ToString("yyyy-MM-dd")} {duty.EndTimeMorning}") ? From 3e8c3d998e048c7dd21a0ceca1f21b0a27614d22 Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Wed, 14 Jan 2026 16:42:36 +0700 Subject: [PATCH 035/178] refactor LeaveController to modify checkout-check endpoint for seminar handling #2199 --- BMA.EHR.Leave/Controllers/LeaveController.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/BMA.EHR.Leave/Controllers/LeaveController.cs b/BMA.EHR.Leave/Controllers/LeaveController.cs index ab0e61b9..6bc682a2 100644 --- a/BMA.EHR.Leave/Controllers/LeaveController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveController.cs @@ -2397,11 +2397,11 @@ namespace BMA.EHR.Leave.Service.Controllers /// เมื่อทำรายการสำเร็จ /// ไม่ได้ Login เข้าระบบ /// เมื่อเกิดข้อผิดพลาดในการทำงาน - [HttpGet("user/checkout-check")] + [HttpGet("user/checkout-check/{isSeminar:bool}")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] [ProducesResponseType(StatusCodes.Status500InternalServerError)] - public async Task> CheckoutCheckAsync() + public async Task> CheckoutCheckAsync(bool isSeminar = false) { var time = DateTime.Now; @@ -2435,7 +2435,9 @@ namespace BMA.EHR.Leave.Service.Controllers //var endTime = DateTimeOffset.Parse($"{DateTime.Now.Date.ToString("yyyy-MM-dd")}T{duty.EndTimeAfternoon}:00.0000000+07:00").ToLocalTime().DateTime;  //var endTime = DateTime.Parse($"{DateTime.Now.Date.ToString("yyyy-MM-dd")}T{duty.EndTimeAfternoon}:00.0000000+07:00"); - var endTime = DateTime.Parse($"{DateTime.Now.Date.ToString("yyyy-MM-dd")} {duty.EndTimeAfternoon}"); + var endTime = isSeminar + ? DateTime.Parse($"{DateTime.Now.Date.ToString("yyyy-MM-dd")} {14:30}") + : DateTime.Parse($"{DateTime.Now.Date.ToString("yyyy-MM-dd")} {duty.EndTimeAfternoon}"); var status = string.Empty; if(lastCheckIn == null) { From 9e529ed19b2239d1cd3c587c91ca27a26339d82a Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Wed, 14 Jan 2026 16:43:35 +0700 Subject: [PATCH 036/178] refactor LeaveController to update checkout-check endpoint to accept string for seminar handling #2199 --- BMA.EHR.Leave/Controllers/LeaveController.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/BMA.EHR.Leave/Controllers/LeaveController.cs b/BMA.EHR.Leave/Controllers/LeaveController.cs index 6bc682a2..580252e9 100644 --- a/BMA.EHR.Leave/Controllers/LeaveController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveController.cs @@ -2397,11 +2397,11 @@ namespace BMA.EHR.Leave.Service.Controllers /// เมื่อทำรายการสำเร็จ /// ไม่ได้ Login เข้าระบบ /// เมื่อเกิดข้อผิดพลาดในการทำงาน - [HttpGet("user/checkout-check/{isSeminar:bool}")] + [HttpGet("user/checkout-check/{isSeminar}")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] [ProducesResponseType(StatusCodes.Status500InternalServerError)] - public async Task> CheckoutCheckAsync(bool isSeminar = false) + public async Task> CheckoutCheckAsync(string isSeminar = "N") { var time = DateTime.Now; @@ -2435,7 +2435,7 @@ namespace BMA.EHR.Leave.Service.Controllers //var endTime = DateTimeOffset.Parse($"{DateTime.Now.Date.ToString("yyyy-MM-dd")}T{duty.EndTimeAfternoon}:00.0000000+07:00").ToLocalTime().DateTime;  //var endTime = DateTime.Parse($"{DateTime.Now.Date.ToString("yyyy-MM-dd")}T{duty.EndTimeAfternoon}:00.0000000+07:00"); - var endTime = isSeminar + var endTime = isSeminar.Trim().ToUpper() == "Y" ? DateTime.Parse($"{DateTime.Now.Date.ToString("yyyy-MM-dd")} {14:30}") : DateTime.Parse($"{DateTime.Now.Date.ToString("yyyy-MM-dd")} {duty.EndTimeAfternoon}"); var status = string.Empty; From 5ec7933b3c7dfb90a9ac3adb2a8790c3915b81b4 Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Wed, 14 Jan 2026 16:51:21 +0700 Subject: [PATCH 037/178] refactor LeaveController to correct end time parsing for seminar handling #2199 --- BMA.EHR.Leave/Controllers/LeaveController.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BMA.EHR.Leave/Controllers/LeaveController.cs b/BMA.EHR.Leave/Controllers/LeaveController.cs index 580252e9..26e3372d 100644 --- a/BMA.EHR.Leave/Controllers/LeaveController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveController.cs @@ -2436,7 +2436,7 @@ namespace BMA.EHR.Leave.Service.Controllers  //var endTime = DateTime.Parse($"{DateTime.Now.Date.ToString("yyyy-MM-dd")}T{duty.EndTimeAfternoon}:00.0000000+07:00"); var endTime = isSeminar.Trim().ToUpper() == "Y" - ? DateTime.Parse($"{DateTime.Now.Date.ToString("yyyy-MM-dd")} {14:30}") + ? DateTime.Parse($"{DateTime.Now.Date.ToString("yyyy-MM-dd")} 14:30") : DateTime.Parse($"{DateTime.Now.Date.ToString("yyyy-MM-dd")} {duty.EndTimeAfternoon}"); var status = string.Empty; if(lastCheckIn == null) From 64c1021c529958393c9a840c135c0c22c2b58522 Mon Sep 17 00:00:00 2001 From: harid Date: Fri, 16 Jan 2026 09:25:22 +0700 Subject: [PATCH 038/178] =?UTF-8?q?Migrate=20=E0=B9=80=E0=B8=81=E0=B9=87?= =?UTF-8?q?=E0=B8=9A=E0=B8=9F=E0=B8=B4=E0=B8=A5=E0=B8=94=E0=B9=8C=E0=B9=83?= =?UTF-8?q?=E0=B8=8A=E0=B9=89=E0=B9=81=E0=B8=AA=E0=B8=94=E0=B8=87=E0=B9=83?= =?UTF-8?q?=E0=B8=99=E0=B8=A3=E0=B8=B2=E0=B8=A2=E0=B8=87=E0=B8=B2=E0=B8=99?= =?UTF-8?q?=E0=B8=A5=E0=B8=B2=E0=B9=80=E0=B8=9E=E0=B8=B4=E0=B9=88=E0=B8=A1?= =?UTF-8?q?=20#2195?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Profiles/GetProfileByKeycloakIdDto.cs | 2 + .../Leave/Requests/LeaveRequestApprover.cs | 9 + ...fields_table_eaveequestpprover.Designer.cs | 1612 +++++++++++++++++ ...0500_add_fields_table_eaveequestpprover.cs | 54 + .../LeaveDb/LeaveDbContextModelSnapshot.cs | 15 + .../Controllers/LeaveRequestController.cs | 6 + .../LeaveRequest/LeaveRequestApproverDto.cs | 10 + 7 files changed, 1708 insertions(+) create mode 100644 BMA.EHR.Infrastructure/Migrations/LeaveDb/20260115140500_add_fields_table_eaveequestpprover.Designer.cs create mode 100644 BMA.EHR.Infrastructure/Migrations/LeaveDb/20260115140500_add_fields_table_eaveequestpprover.cs diff --git a/BMA.EHR.Application/Responses/Profiles/GetProfileByKeycloakIdDto.cs b/BMA.EHR.Application/Responses/Profiles/GetProfileByKeycloakIdDto.cs index bb377f3e..b2e71129 100644 --- a/BMA.EHR.Application/Responses/Profiles/GetProfileByKeycloakIdDto.cs +++ b/BMA.EHR.Application/Responses/Profiles/GetProfileByKeycloakIdDto.cs @@ -80,6 +80,8 @@ namespace BMA.EHR.Application.Responses.Profiles public string? CurrentZipCode { get; set; } public string? PositionLeaveName { get; set; } + + public string? PosExecutiveName { get; set; } public string? CommanderPositionName { get; set; } = string.Empty; diff --git a/BMA.EHR.Domain/Models/Leave/Requests/LeaveRequestApprover.cs b/BMA.EHR.Domain/Models/Leave/Requests/LeaveRequestApprover.cs index 10e7175d..f974d004 100644 --- a/BMA.EHR.Domain/Models/Leave/Requests/LeaveRequestApprover.cs +++ b/BMA.EHR.Domain/Models/Leave/Requests/LeaveRequestApprover.cs @@ -17,6 +17,15 @@ namespace BMA.EHR.Domain.Models.Leave.Requests public string PositionName { get; set; } = string.Empty; + [Comment("ประเภทระดับตำแหน่ง")] + public string PositionLevelName { get; set; } = string.Empty; + + [Comment("ตำแหน่งทางการบริหาร")] + public string PosExecutiveName { get; set; } = string.Empty; + + [Comment("สังกัด")] + public string OrganizationName { get; set; } = string.Empty; + [Comment("ตำแหน่งใต้ลายเช็นต์")] public string? PositionSign { get; set; } = string.Empty; diff --git a/BMA.EHR.Infrastructure/Migrations/LeaveDb/20260115140500_add_fields_table_eaveequestpprover.Designer.cs b/BMA.EHR.Infrastructure/Migrations/LeaveDb/20260115140500_add_fields_table_eaveequestpprover.Designer.cs new file mode 100644 index 00000000..8a505bde --- /dev/null +++ b/BMA.EHR.Infrastructure/Migrations/LeaveDb/20260115140500_add_fields_table_eaveequestpprover.Designer.cs @@ -0,0 +1,1612 @@ +// +using System; +using BMA.EHR.Infrastructure.Persistence; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace BMA.EHR.Infrastructure.Migrations.LeaveDb +{ + [DbContext(typeof(LeaveDbContext))] + [Migration("20260115140500_add_fields_table_eaveequestpprover")] + partial class add_fields_table_eaveequestpprover + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "7.0.9") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + modelBuilder.Entity("BMA.EHR.Domain.Models.Documents.Document", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedDate") + .HasColumnType("datetime(6)"); + + b.Property("Detail") + .IsRequired() + .HasColumnType("text"); + + b.Property("FileName") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("FileSize") + .HasColumnType("int"); + + b.Property("FileType") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("varchar(128)"); + + b.Property("ObjectRefId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.ToTable("Document"); + }); + + modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.Commons.LeaveType", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)") + .HasColumnOrder(0) + .HasComment("PrimaryKey") + .HasAnnotation("Relational:JsonPropertyName", "id"); + + b.Property("Code") + .IsRequired() + .HasColumnType("longtext") + .HasComment("รหัสประเภทการลา"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(100) + .HasComment("สร้างข้อมูลเมื่อ"); + + b.Property("CreatedFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(104) + .HasComment("ชื่อ User ที่สร้างข้อมูล"); + + b.Property("CreatedUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(101) + .HasComment("User Id ที่สร้างข้อมูล"); + + b.Property("LastUpdateFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(105) + .HasComment("ชื่อ User ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdateUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(103) + .HasComment("User Id ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(102) + .HasComment("แก้ไขข้อมูลล่าสุดเมื่อ"); + + b.Property("Limit") + .HasColumnType("int") + .HasComment("จำนวนวันลาสูงสุดประจำปี"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext") + .HasComment("ชื่อประเภทการลา"); + + b.HasKey("Id"); + + b.ToTable("LeaveTypes"); + }); + + modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.Requests.LeaveBeginning", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)") + .HasColumnOrder(0) + .HasComment("PrimaryKey") + .HasAnnotation("Relational:JsonPropertyName", "id"); + + b.Property("Child1DnaId") + .HasColumnType("char(36)"); + + b.Property("Child2DnaId") + .HasColumnType("char(36)"); + + b.Property("Child3DnaId") + .HasColumnType("char(36)"); + + b.Property("Child4DnaId") + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(100) + .HasComment("สร้างข้อมูลเมื่อ"); + + b.Property("CreatedFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(104) + .HasComment("ชื่อ User ที่สร้างข้อมูล"); + + b.Property("CreatedUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(101) + .HasComment("User Id ที่สร้างข้อมูล"); + + b.Property("FirstName") + .HasColumnType("longtext"); + + b.Property("LastName") + .HasColumnType("longtext"); + + b.Property("LastUpdateFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(105) + .HasComment("ชื่อ User ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdateUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(103) + .HasComment("User Id ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(102) + .HasComment("แก้ไขข้อมูลล่าสุดเมื่อ"); + + b.Property("LeaveDays") + .HasColumnType("double") + .HasComment("จำนวนวันลายกมา"); + + b.Property("LeaveDaysUsed") + .HasColumnType("double") + .HasComment("จำนวนวันลาที่ใช้ไป"); + + b.Property("LeaveTypeId") + .HasColumnType("char(36)") + .HasComment("รหัสประเภทการลา"); + + b.Property("LeaveYear") + .HasColumnType("int") + .HasComment("ปีงบประมาณ"); + + b.Property("Prefix") + .HasColumnType("longtext"); + + b.Property("ProfileId") + .HasColumnType("char(36)") + .HasComment("รหัส Profile ในระบบทะเบียนประวัติ"); + + b.Property("RootDnaId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("LeaveTypeId"); + + b.ToTable("LeaveBeginnings"); + }); + + modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.Requests.LeaveDocument", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)") + .HasColumnOrder(0) + .HasComment("PrimaryKey") + .HasAnnotation("Relational:JsonPropertyName", "id"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(100) + .HasComment("สร้างข้อมูลเมื่อ"); + + b.Property("CreatedFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(104) + .HasComment("ชื่อ User ที่สร้างข้อมูล"); + + b.Property("CreatedUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(101) + .HasComment("User Id ที่สร้างข้อมูล"); + + b.Property("DocumentId") + .HasColumnType("char(36)"); + + b.Property("LastUpdateFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(105) + .HasComment("ชื่อ User ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdateUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(103) + .HasComment("User Id ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(102) + .HasComment("แก้ไขข้อมูลล่าสุดเมื่อ"); + + b.Property("LeaveRequestId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("DocumentId"); + + b.HasIndex("LeaveRequestId"); + + b.ToTable("LeaveDocuments"); + }); + + modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.Requests.LeaveRequest", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)") + .HasColumnOrder(0) + .HasComment("PrimaryKey") + .HasAnnotation("Relational:JsonPropertyName", "id"); + + b.Property("AbsentDayAt") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("AbsentDayGetIn") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("AbsentDayLocation") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("AbsentDayRegistorDate") + .HasColumnType("datetime(6)"); + + b.Property("AbsentDaySummon") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Amount") + .HasColumnType("double"); + + b.Property("ApproveStep") + .HasColumnType("longtext") + .HasComment("step การอนุมัติ st1 = จทน.อนุมัตื,st2 = ผู้บังคับบัญชา อนุมัติ "); + + b.Property("BirthDate") + .HasColumnType("datetime(6)"); + + b.Property("CancelLeaveWrote") + .HasColumnType("longtext") + .HasComment("เขียนที่ (ขอยกเลิก)"); + + b.Property("Child1") + .HasColumnType("longtext"); + + b.Property("Child1DnaId") + .HasColumnType("char(36)"); + + b.Property("Child1Id") + .HasColumnType("char(36)"); + + b.Property("Child2") + .HasColumnType("longtext"); + + b.Property("Child2DnaId") + .HasColumnType("char(36)"); + + b.Property("Child2Id") + .HasColumnType("char(36)"); + + b.Property("Child3") + .HasColumnType("longtext"); + + b.Property("Child3DnaId") + .HasColumnType("char(36)"); + + b.Property("Child3Id") + .HasColumnType("char(36)"); + + b.Property("Child4") + .HasColumnType("longtext"); + + b.Property("Child4DnaId") + .HasColumnType("char(36)"); + + b.Property("Child4Id") + .HasColumnType("char(36)"); + + b.Property("CitizenId") + .HasColumnType("longtext"); + + b.Property("CommanderPosition") + .HasColumnType("longtext"); + + b.Property("CoupleDayCountryHistory") + .HasColumnType("longtext"); + + b.Property("CoupleDayEndDateHistory") + .HasColumnType("datetime(6)"); + + b.Property("CoupleDayLevel") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("CoupleDayLevelCountry") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("CoupleDayName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("CoupleDayPosition") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("CoupleDayStartDateHistory") + .HasColumnType("datetime(6)"); + + b.Property("CoupleDaySumTotalHistory") + .HasColumnType("longtext"); + + b.Property("CoupleDayTotalHistory") + .HasColumnType("longtext"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(100) + .HasComment("สร้างข้อมูลเมื่อ"); + + b.Property("CreatedFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(104) + .HasComment("ชื่อ User ที่สร้างข้อมูล"); + + b.Property("CreatedUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(101) + .HasComment("User Id ที่สร้างข้อมูล"); + + b.Property("DateAppoint") + .HasColumnType("datetime(6)"); + + b.Property("Dear") + .HasColumnType("longtext") + .HasComment("เรียนใคร"); + + b.Property("FirstName") + .HasColumnType("longtext"); + + b.Property("Gender") + .HasColumnType("longtext"); + + b.Property("HajjDayStatus") + .HasColumnType("tinyint(1)"); + + b.Property("KeycloakUserId") + .HasColumnType("char(36)"); + + b.Property("LastName") + .HasColumnType("longtext"); + + b.Property("LastUpdateFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(105) + .HasComment("ชื่อ User ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdateUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(103) + .HasComment("User Id ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(102) + .HasComment("แก้ไขข้อมูลล่าสุดเมื่อ"); + + b.Property("LeaveAddress") + .IsRequired() + .HasColumnType("longtext") + .HasComment("สถานที่ติดต่อขณะลา"); + + b.Property("LeaveBirthDate") + .HasColumnType("datetime(6)"); + + b.Property("LeaveCancelComment") + .HasColumnType("longtext") + .HasComment("เหตุผลในการขอยกเลิก"); + + b.Property("LeaveCancelDocumentId") + .HasColumnType("char(36)"); + + b.Property("LeaveCancelStatus") + .HasColumnType("longtext") + .HasComment("สถานะของคำขอยกเลิก"); + + b.Property("LeaveComment") + .HasColumnType("longtext") + .HasComment("ความเห็นของผู้บังคับบัญชา"); + + b.Property("LeaveDetail") + .IsRequired() + .HasColumnType("longtext") + .HasComment("รายละเอียดการลา"); + + b.Property("LeaveDirectorComment") + .HasColumnType("longtext") + .HasComment("ความเห็นของผู้อำนวยการสำนัก"); + + b.Property("LeaveDraftDocumentId") + .HasColumnType("char(36)"); + + b.Property("LeaveEndDate") + .HasColumnType("datetime(6)") + .HasComment("วัน เดือน ปีสิ้นสุดลา"); + + b.Property("LeaveGovernmentDate") + .HasColumnType("datetime(6)"); + + b.Property("LeaveLast") + .HasColumnType("datetime(6)"); + + b.Property("LeaveNumber") + .IsRequired() + .HasColumnType("longtext") + .HasComment("หมายเลขที่ติดต่อขณะลา"); + + b.Property("LeaveRange") + .HasColumnType("longtext") + .HasComment("ช่วงของการลาของวันเริ่ม เช่น ลาทั้งวัน ครึ่งวันเช้า ครึ่งวันบ่าย"); + + b.Property("LeaveRangeEnd") + .HasColumnType("longtext") + .HasComment("ช่วงของการลาของวันสิ้นสุด เช่น ลาทั้งวัน ครึ่งวันเช้า ครึ่งวันบ่าย"); + + b.Property("LeaveSalary") + .HasColumnType("int"); + + b.Property("LeaveSalaryText") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("LeaveStartDate") + .HasColumnType("datetime(6)") + .HasComment("วัน เดือน ปีเริ่มต้นลา"); + + b.Property("LeaveStatus") + .IsRequired() + .HasColumnType("longtext") + .HasComment("สถานะของคำร้อง"); + + b.Property("LeaveSubTypeName") + .HasColumnType("longtext"); + + b.Property("LeaveTotal") + .HasColumnType("double"); + + b.Property("LeaveTypeCode") + .HasColumnType("longtext") + .HasComment("code ของประเภทการลา"); + + b.Property("LeaveWrote") + .IsRequired() + .HasColumnType("longtext") + .HasComment("เขียนที่"); + + b.Property("OrdainDayBuddhistLentAddress") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("OrdainDayBuddhistLentName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("OrdainDayLocationAddress") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("OrdainDayLocationName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("OrdainDayLocationNumber") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("OrdainDayOrdination") + .HasColumnType("datetime(6)"); + + b.Property("OrdainDayStatus") + .HasColumnType("tinyint(1)"); + + b.Property("OrganizationName") + .HasColumnType("longtext") + .HasComment("สังกัดผู้ยื่นขอ"); + + b.Property("PositionLevelName") + .HasColumnType("longtext") + .HasComment("ระดับผู้ยื่นขอ"); + + b.Property("PositionName") + .HasColumnType("longtext") + .HasComment("ตำแหน่งผู้ยื่นขอ"); + + b.Property("Prefix") + .HasColumnType("longtext"); + + b.Property("ProfileId") + .HasColumnType("char(36)"); + + b.Property("ProfileType") + .HasColumnType("longtext"); + + b.Property("RestDayCurrentTotal") + .HasColumnType("double"); + + b.Property("RestDayOldTotal") + .HasColumnType("double"); + + b.Property("Root") + .HasColumnType("longtext"); + + b.Property("RootDnaId") + .HasColumnType("char(36)"); + + b.Property("RootId") + .HasColumnType("char(36)"); + + b.Property("StudyDayCountry") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("StudyDayDegreeLevel") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("StudyDayScholarship") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("StudyDaySubject") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("StudyDayTrainingName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("StudyDayTrainingSubject") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("StudyDayUniversityName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("TypeId") + .HasColumnType("char(36)"); + + b.Property("WifeDayDateBorn") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("WifeDayName") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("LeaveCancelDocumentId"); + + b.HasIndex("LeaveDraftDocumentId"); + + b.HasIndex("TypeId"); + + b.ToTable("LeaveRequests"); + }); + + modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.Requests.LeaveRequestApprover", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)") + .HasColumnOrder(0) + .HasComment("PrimaryKey") + .HasAnnotation("Relational:JsonPropertyName", "id"); + + b.Property("ApproveStatus") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("ApproveType") + .HasColumnType("longtext"); + + b.Property("Comment") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(100) + .HasComment("สร้างข้อมูลเมื่อ"); + + b.Property("CreatedFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(104) + .HasComment("ชื่อ User ที่สร้างข้อมูล"); + + b.Property("CreatedUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(101) + .HasComment("User Id ที่สร้างข้อมูล"); + + b.Property("FirstName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("KeycloakId") + .HasColumnType("char(36)"); + + b.Property("LastName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("LastUpdateFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(105) + .HasComment("ชื่อ User ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdateUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(103) + .HasComment("User Id ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(102) + .HasComment("แก้ไขข้อมูลล่าสุดเมื่อ"); + + b.Property("LeaveRequestId") + .HasColumnType("char(36)"); + + b.Property("OrganizationName") + .IsRequired() + .HasColumnType("longtext") + .HasComment("สังกัด"); + + b.Property("PosExecutiveName") + .IsRequired() + .HasColumnType("longtext") + .HasComment("ตำแหน่งทางการบริหาร"); + + b.Property("PositionLevelName") + .IsRequired() + .HasColumnType("longtext") + .HasComment("ประเภทระดับตำแหน่ง"); + + b.Property("PositionName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("PositionSign") + .HasColumnType("longtext") + .HasComment("ตำแหน่งใต้ลายเช็นต์"); + + b.Property("Prefix") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("ProfileId") + .HasColumnType("char(36)"); + + b.Property("Seq") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("LeaveRequestId"); + + b.ToTable("LeaveRequestApprovers"); + }); + + modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.TimeAttendants.AdditionalCheckRequest", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)") + .HasColumnOrder(0) + .HasComment("PrimaryKey") + .HasAnnotation("Relational:JsonPropertyName", "id"); + + b.Property("CheckDate") + .HasColumnType("datetime(6)") + .HasComment("*วันที่ลงเวลา"); + + b.Property("CheckInEdit") + .HasColumnType("tinyint(1)") + .HasComment("*ขอลงเวลาช่วงเช้า"); + + b.Property("CheckOutEdit") + .HasColumnType("tinyint(1)") + .HasComment("*ขอลงเวลาช่วงบ่าย"); + + b.Property("Child1DnaId") + .HasColumnType("char(36)"); + + b.Property("Child2DnaId") + .HasColumnType("char(36)"); + + b.Property("Child3DnaId") + .HasColumnType("char(36)"); + + b.Property("Child4DnaId") + .HasColumnType("char(36)"); + + b.Property("Comment") + .HasColumnType("longtext") + .HasComment("หมายเหตุในการการอนุมัติ/ไม่อนุมัติ"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(100) + .HasComment("สร้างข้อมูลเมื่อ"); + + b.Property("CreatedFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(104) + .HasComment("ชื่อ User ที่สร้างข้อมูล"); + + b.Property("CreatedUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(101) + .HasComment("User Id ที่สร้างข้อมูล"); + + b.Property("Description") + .IsRequired() + .HasColumnType("longtext") + .HasComment("*หมายเหตุขอลงเวลาพิเศษ"); + + b.Property("FirstName") + .HasColumnType("longtext"); + + b.Property("KeycloakUserId") + .HasColumnType("char(36)") + .HasComment("รหัส User ของ Keycloak ที่ร้องขอ"); + + b.Property("LastName") + .HasColumnType("longtext"); + + b.Property("LastUpdateFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(105) + .HasComment("ชื่อ User ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdateUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(103) + .HasComment("User Id ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(102) + .HasComment("แก้ไขข้อมูลล่าสุดเมื่อ"); + + b.Property("Latitude") + .HasColumnType("double"); + + b.Property("Longitude") + .HasColumnType("double"); + + b.Property("POI") + .HasColumnType("longtext"); + + b.Property("Prefix") + .HasColumnType("longtext"); + + b.Property("RootDnaId") + .HasColumnType("char(36)"); + + b.Property("Status") + .IsRequired() + .HasColumnType("longtext") + .HasComment("สถานะการอนุมัติ"); + + b.HasKey("Id"); + + b.ToTable("AdditionalCheckRequests"); + }); + + modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.TimeAttendants.DutyTime", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)") + .HasColumnOrder(0) + .HasComment("PrimaryKey") + .HasAnnotation("Relational:JsonPropertyName", "id"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(100) + .HasComment("สร้างข้อมูลเมื่อ"); + + b.Property("CreatedFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(104) + .HasComment("ชื่อ User ที่สร้างข้อมูล"); + + b.Property("CreatedUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(101) + .HasComment("User Id ที่สร้างข้อมูล"); + + b.Property("Description") + .IsRequired() + .HasColumnType("longtext") + .HasComment("คำอธิบาย"); + + b.Property("EndTimeAfternoon") + .IsRequired() + .HasColumnType("longtext") + .HasComment("เวลาออกงานช่วงบ่าย"); + + b.Property("EndTimeMorning") + .IsRequired() + .HasColumnType("longtext") + .HasComment("เวลาออกงานช่วงเช้า"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)") + .HasComment("สถานะการเปิดใช้งาน (เปิด/ปิด)"); + + b.Property("IsDefault") + .HasColumnType("tinyint(1)") + .HasComment("สถานะว่ารอบใดเป็นค่า Default ของข้าราชการ (สำหรับทุกคนที่ยังไม่ได้ทำการเลือกรอบ)"); + + b.Property("LastUpdateFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(105) + .HasComment("ชื่อ User ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdateUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(103) + .HasComment("User Id ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(102) + .HasComment("แก้ไขข้อมูลล่าสุดเมื่อ"); + + b.Property("StartTimeAfternoon") + .IsRequired() + .HasColumnType("longtext") + .HasComment("เวลาเข้างานช่วงบ่าย"); + + b.Property("StartTimeMorning") + .IsRequired() + .HasColumnType("longtext") + .HasComment("เวลาเข้างานช่วงเช้า"); + + b.HasKey("Id"); + + b.ToTable("DutyTimes"); + }); + + modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.TimeAttendants.ProcessUserTimeStamp", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)") + .HasColumnOrder(0) + .HasComment("PrimaryKey") + .HasAnnotation("Relational:JsonPropertyName", "id"); + + b.Property("CheckIn") + .HasColumnType("datetime(6)") + .HasComment("วัน เวลา เข้างาน"); + + b.Property("CheckInImageUrl") + .IsRequired() + .HasColumnType("longtext") + .HasComment("รูปถ่ายสถานที่ Check-In"); + + b.Property("CheckInLat") + .HasColumnType("double") + .HasComment("พิกัดละติจูด Check-In"); + + b.Property("CheckInLocationName") + .HasColumnType("longtext") + .HasComment("กรณีเลือกนอกสถานที่ตั้ง ต้องระบุข้อมูลชื่อสถานะที่ Check-In"); + + b.Property("CheckInLon") + .HasColumnType("double") + .HasComment("พิกัดลองจิจูด Check-In"); + + b.Property("CheckInPOI") + .IsRequired() + .HasColumnType("longtext") + .HasComment("ชื่อสถานที่ ได้มาจากระบบ ArcGis ของกองสารสนเทศภูมิศาสตร์ Check-In"); + + b.Property("CheckInRemark") + .HasColumnType("longtext") + .HasComment("ข้อความหมายเหตุที่ต้องการระบุเพิ่ม(มีเผื่อไว้อาจไม่ได้ใช้) Check-In"); + + b.Property("CheckInStatus") + .IsRequired() + .HasColumnType("longtext") + .HasComment("สถานะ Check-In"); + + b.Property("CheckOut") + .HasColumnType("datetime(6)") + .HasComment("วัน เวลา ออกงาน"); + + b.Property("CheckOutImageUrl") + .IsRequired() + .HasColumnType("longtext") + .HasComment("รูปถ่ายสถานที่ Check-Out"); + + b.Property("CheckOutLat") + .HasColumnType("double") + .HasComment("พิกัดละติจูด Check-Out"); + + b.Property("CheckOutLocationName") + .HasColumnType("longtext") + .HasComment("กรณีเลือกนอกสถานที่ตั้ง ต้องระบุข้อมูลชื่อสถานะที่ Check-Out"); + + b.Property("CheckOutLon") + .HasColumnType("double") + .HasComment("พิกัดลองจิจูด Check-Out"); + + b.Property("CheckOutPOI") + .IsRequired() + .HasColumnType("longtext") + .HasComment("ชื่อสถานที่ ได้มาจากระบบ ArcGis ของกองสารสนเทศภูมิศาสตร์ Check-Out"); + + b.Property("CheckOutRemark") + .HasColumnType("longtext") + .HasComment("ข้อความหมายเหตุที่ต้องการระบุเพิ่ม(มีเผื่อไว้อาจไม่ได้ใช้) Check-Out"); + + b.Property("CheckOutStatus") + .HasColumnType("longtext") + .HasComment("สถานะ Check-Out"); + + b.Property("Child1") + .HasColumnType("longtext"); + + b.Property("Child1DnaId") + .HasColumnType("char(36)"); + + b.Property("Child1Id") + .HasColumnType("char(36)"); + + b.Property("Child2") + .HasColumnType("longtext"); + + b.Property("Child2DnaId") + .HasColumnType("char(36)"); + + b.Property("Child2Id") + .HasColumnType("char(36)"); + + b.Property("Child3") + .HasColumnType("longtext"); + + b.Property("Child3DnaId") + .HasColumnType("char(36)"); + + b.Property("Child3Id") + .HasColumnType("char(36)"); + + b.Property("Child4") + .HasColumnType("longtext"); + + b.Property("Child4DnaId") + .HasColumnType("char(36)"); + + b.Property("Child4Id") + .HasColumnType("char(36)"); + + b.Property("CitizenId") + .HasColumnType("longtext"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(100) + .HasComment("สร้างข้อมูลเมื่อ"); + + b.Property("CreatedFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(104) + .HasComment("ชื่อ User ที่สร้างข้อมูล"); + + b.Property("CreatedUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(101) + .HasComment("User Id ที่สร้างข้อมูล"); + + b.Property("EditReason") + .HasColumnType("longtext") + .HasComment("เหตุผลการอนุมัติ/ไม่อนุมัติขอลงเวลาพิเศษ"); + + b.Property("EditStatus") + .HasColumnType("longtext") + .HasComment("สถานะการของลงเวลาพิเศษ"); + + b.Property("FirstName") + .HasColumnType("longtext"); + + b.Property("Gender") + .HasColumnType("longtext"); + + b.Property("IsLocationCheckIn") + .HasColumnType("tinyint(1)") + .HasComment("true คือ ณ สถานที่ตั้ง, false คือ นอกสถานที่ตั้ง Check-In"); + + b.Property("IsLocationCheckOut") + .HasColumnType("tinyint(1)") + .HasComment("true คือ ณ สถานที่ตั้ง, false คือ นอกสถานที่ตั้ง Check-Out"); + + b.Property("IsProcess") + .HasColumnType("tinyint(1)") + .HasComment("นำไปประมวลผลแล้วหรือยัง"); + + b.Property("KeycloakUserId") + .HasColumnType("char(36)") + .HasComment("รหัส User ของ Keycloak"); + + b.Property("LastName") + .HasColumnType("longtext"); + + b.Property("LastUpdateFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(105) + .HasComment("ชื่อ User ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdateUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(103) + .HasComment("User Id ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(102) + .HasComment("แก้ไขข้อมูลล่าสุดเมื่อ"); + + b.Property("Prefix") + .HasColumnType("longtext"); + + b.Property("ProfileId") + .HasColumnType("char(36)"); + + b.Property("ProfileType") + .HasColumnType("longtext"); + + b.Property("Root") + .HasColumnType("longtext"); + + b.Property("RootDnaId") + .HasColumnType("char(36)"); + + b.Property("RootId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.ToTable("ProcessUserTimeStamps"); + }); + + modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.TimeAttendants.UserCalendar", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)") + .HasColumnOrder(0) + .HasComment("PrimaryKey") + .HasAnnotation("Relational:JsonPropertyName", "id"); + + b.Property("Calendar") + .IsRequired() + .HasColumnType("longtext") + .HasComment("ปฏิทินการทำงานของ ขรก ปกติ หรือ 6 วันต่อสัปดาห์"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(100) + .HasComment("สร้างข้อมูลเมื่อ"); + + b.Property("CreatedFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(104) + .HasComment("ชื่อ User ที่สร้างข้อมูล"); + + b.Property("CreatedUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(101) + .HasComment("User Id ที่สร้างข้อมูล"); + + b.Property("LastUpdateFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(105) + .HasComment("ชื่อ User ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdateUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(103) + .HasComment("User Id ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(102) + .HasComment("แก้ไขข้อมูลล่าสุดเมื่อ"); + + b.Property("ProfileId") + .HasColumnType("char(36)") + .HasComment("รหัส Profile ในระบบทะเบียนประวัติ"); + + b.HasKey("Id"); + + b.ToTable("UserCalendars"); + }); + + modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.TimeAttendants.UserDutyTime", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)") + .HasColumnOrder(0) + .HasComment("PrimaryKey") + .HasAnnotation("Relational:JsonPropertyName", "id"); + + b.Property("Child1DnaId") + .HasColumnType("char(36)"); + + b.Property("Child2DnaId") + .HasColumnType("char(36)"); + + b.Property("Child3DnaId") + .HasColumnType("char(36)"); + + b.Property("Child4DnaId") + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(100) + .HasComment("สร้างข้อมูลเมื่อ"); + + b.Property("CreatedFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(104) + .HasComment("ชื่อ User ที่สร้างข้อมูล"); + + b.Property("CreatedUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(101) + .HasComment("User Id ที่สร้างข้อมูล"); + + b.Property("DutyTimeId") + .HasColumnType("char(36)") + .HasComment("รหัสรอบการลงเวลา"); + + b.Property("EffectiveDate") + .HasColumnType("datetime(6)") + .HasComment("วันที่มีผล"); + + b.Property("IsProcess") + .HasColumnType("tinyint(1)") + .HasComment("ทำการประมวลผลแล้วหรือยัง"); + + b.Property("LastUpdateFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(105) + .HasComment("ชื่อ User ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdateUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(103) + .HasComment("User Id ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(102) + .HasComment("แก้ไขข้อมูลล่าสุดเมื่อ"); + + b.Property("ProfileId") + .HasColumnType("char(36)") + .HasComment("รหัส Profile ในระบบทะเบียนประวัติ"); + + b.Property("Remark") + .HasColumnType("longtext") + .HasComment("หมายเหตุ"); + + b.Property("RootDnaId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("DutyTimeId"); + + b.ToTable("UserDutyTimes"); + }); + + modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.TimeAttendants.UserTimeStamp", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)") + .HasColumnOrder(0) + .HasComment("PrimaryKey") + .HasAnnotation("Relational:JsonPropertyName", "id"); + + b.Property("CheckIn") + .HasColumnType("datetime(6)") + .HasComment("วัน เวลา เข้างาน"); + + b.Property("CheckInImageUrl") + .IsRequired() + .HasColumnType("longtext") + .HasComment("รูปถ่ายสถานที่ Check-In"); + + b.Property("CheckInLat") + .HasColumnType("double") + .HasComment("พิกัดละติจูด Check-In"); + + b.Property("CheckInLocationName") + .HasColumnType("longtext") + .HasComment("กรณีเลือกนอกสถานที่ตั้ง ต้องระบุข้อมูลชื่อสถานะที่ Check-In"); + + b.Property("CheckInLon") + .HasColumnType("double") + .HasComment("พิกัดลองจิจูด Check-In"); + + b.Property("CheckInPOI") + .IsRequired() + .HasColumnType("longtext") + .HasComment("ชื่อสถานที่ ได้มาจากระบบ ArcGis ของกองสารสนเทศภูมิศาสตร์ Check-In"); + + b.Property("CheckInRemark") + .HasColumnType("longtext") + .HasComment("ข้อความหมายเหตุที่ต้องการระบุเพิ่ม(มีเผื่อไว้อาจไม่ได้ใช้) Check-In"); + + b.Property("CheckOut") + .HasColumnType("datetime(6)") + .HasComment("วัน เวลา ออกงาน"); + + b.Property("CheckOutImageUrl") + .IsRequired() + .HasColumnType("longtext") + .HasComment("รูปถ่ายสถานที่ Check-Out"); + + b.Property("CheckOutLat") + .HasColumnType("double") + .HasComment("พิกัดละติจูด Check-Out"); + + b.Property("CheckOutLocationName") + .HasColumnType("longtext") + .HasComment("กรณีเลือกนอกสถานที่ตั้ง ต้องระบุข้อมูลชื่อสถานะที่ Check-Out"); + + b.Property("CheckOutLon") + .HasColumnType("double") + .HasComment("พิกัดลองจิจูด Check-Out"); + + b.Property("CheckOutPOI") + .IsRequired() + .HasColumnType("longtext") + .HasComment("ชื่อสถานที่ ได้มาจากระบบ ArcGis ของกองสารสนเทศภูมิศาสตร์ Check-Out"); + + b.Property("CheckOutRemark") + .HasColumnType("longtext") + .HasComment("ข้อความหมายเหตุที่ต้องการระบุเพิ่ม(มีเผื่อไว้อาจไม่ได้ใช้) Check-Out"); + + b.Property("Child1") + .HasColumnType("longtext"); + + b.Property("Child1DnaId") + .HasColumnType("char(36)"); + + b.Property("Child1Id") + .HasColumnType("char(36)"); + + b.Property("Child2") + .HasColumnType("longtext"); + + b.Property("Child2DnaId") + .HasColumnType("char(36)"); + + b.Property("Child2Id") + .HasColumnType("char(36)"); + + b.Property("Child3") + .HasColumnType("longtext"); + + b.Property("Child3DnaId") + .HasColumnType("char(36)"); + + b.Property("Child3Id") + .HasColumnType("char(36)"); + + b.Property("Child4") + .HasColumnType("longtext"); + + b.Property("Child4DnaId") + .HasColumnType("char(36)"); + + b.Property("Child4Id") + .HasColumnType("char(36)"); + + b.Property("CitizenId") + .HasColumnType("longtext"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(100) + .HasComment("สร้างข้อมูลเมื่อ"); + + b.Property("CreatedFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(104) + .HasComment("ชื่อ User ที่สร้างข้อมูล"); + + b.Property("CreatedUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(101) + .HasComment("User Id ที่สร้างข้อมูล"); + + b.Property("FirstName") + .HasColumnType("longtext"); + + b.Property("Gender") + .HasColumnType("longtext"); + + b.Property("IsLocationCheckIn") + .HasColumnType("tinyint(1)") + .HasComment("true คือ ณ สถานที่ตั้ง, false คือ นอกสถานที่ตั้ง Check-In"); + + b.Property("IsLocationCheckOut") + .HasColumnType("tinyint(1)") + .HasComment("true คือ ณ สถานที่ตั้ง, false คือ นอกสถานที่ตั้ง Check-Out"); + + b.Property("IsProcess") + .HasColumnType("tinyint(1)") + .HasComment("นำไปประมวลผลแล้วหรือยัง"); + + b.Property("KeycloakUserId") + .HasColumnType("char(36)") + .HasComment("รหัส User ของ Keycloak"); + + b.Property("LastName") + .HasColumnType("longtext"); + + b.Property("LastUpdateFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(105) + .HasComment("ชื่อ User ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdateUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(103) + .HasComment("User Id ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(102) + .HasComment("แก้ไขข้อมูลล่าสุดเมื่อ"); + + b.Property("Prefix") + .HasColumnType("longtext"); + + b.Property("ProfileId") + .HasColumnType("char(36)"); + + b.Property("ProfileType") + .HasColumnType("longtext"); + + b.Property("Root") + .HasColumnType("longtext"); + + b.Property("RootDnaId") + .HasColumnType("char(36)"); + + b.Property("RootId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.ToTable("UserTimeStamps"); + }); + + modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.Requests.LeaveBeginning", b => + { + b.HasOne("BMA.EHR.Domain.Models.Leave.Commons.LeaveType", "LeaveType") + .WithMany() + .HasForeignKey("LeaveTypeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("LeaveType"); + }); + + modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.Requests.LeaveDocument", b => + { + b.HasOne("BMA.EHR.Domain.Models.Documents.Document", "Document") + .WithMany() + .HasForeignKey("DocumentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("BMA.EHR.Domain.Models.Leave.Requests.LeaveRequest", "LeaveRequest") + .WithMany("LeaveDocument") + .HasForeignKey("LeaveRequestId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Document"); + + b.Navigation("LeaveRequest"); + }); + + modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.Requests.LeaveRequest", b => + { + b.HasOne("BMA.EHR.Domain.Models.Documents.Document", "LeaveCancelDocument") + .WithMany() + .HasForeignKey("LeaveCancelDocumentId"); + + b.HasOne("BMA.EHR.Domain.Models.Documents.Document", "LeaveDraftDocument") + .WithMany() + .HasForeignKey("LeaveDraftDocumentId"); + + b.HasOne("BMA.EHR.Domain.Models.Leave.Commons.LeaveType", "Type") + .WithMany() + .HasForeignKey("TypeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("LeaveCancelDocument"); + + b.Navigation("LeaveDraftDocument"); + + b.Navigation("Type"); + }); + + modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.Requests.LeaveRequestApprover", b => + { + b.HasOne("BMA.EHR.Domain.Models.Leave.Requests.LeaveRequest", "LeaveRequest") + .WithMany("Approvers") + .HasForeignKey("LeaveRequestId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("LeaveRequest"); + }); + + modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.TimeAttendants.UserDutyTime", b => + { + b.HasOne("BMA.EHR.Domain.Models.Leave.TimeAttendants.DutyTime", "DutyTime") + .WithMany() + .HasForeignKey("DutyTimeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("DutyTime"); + }); + + modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.Requests.LeaveRequest", b => + { + b.Navigation("Approvers"); + + b.Navigation("LeaveDocument"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/BMA.EHR.Infrastructure/Migrations/LeaveDb/20260115140500_add_fields_table_eaveequestpprover.cs b/BMA.EHR.Infrastructure/Migrations/LeaveDb/20260115140500_add_fields_table_eaveequestpprover.cs new file mode 100644 index 00000000..02aee038 --- /dev/null +++ b/BMA.EHR.Infrastructure/Migrations/LeaveDb/20260115140500_add_fields_table_eaveequestpprover.cs @@ -0,0 +1,54 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace BMA.EHR.Infrastructure.Migrations.LeaveDb +{ + /// + public partial class add_fields_table_eaveequestpprover : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "OrganizationName", + table: "LeaveRequestApprovers", + type: "longtext", + nullable: false, + comment: "สังกัด") + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.AddColumn( + name: "PosExecutiveName", + table: "LeaveRequestApprovers", + type: "longtext", + nullable: false, + comment: "ตำแหน่งทางการบริหาร") + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.AddColumn( + name: "PositionLevelName", + table: "LeaveRequestApprovers", + type: "longtext", + nullable: false, + comment: "ประเภทระดับตำแหน่ง") + .Annotation("MySql:CharSet", "utf8mb4"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "OrganizationName", + table: "LeaveRequestApprovers"); + + migrationBuilder.DropColumn( + name: "PosExecutiveName", + table: "LeaveRequestApprovers"); + + migrationBuilder.DropColumn( + name: "PositionLevelName", + table: "LeaveRequestApprovers"); + } + } +} diff --git a/BMA.EHR.Infrastructure/Migrations/LeaveDb/LeaveDbContextModelSnapshot.cs b/BMA.EHR.Infrastructure/Migrations/LeaveDb/LeaveDbContextModelSnapshot.cs index c0a77c10..14e45995 100644 --- a/BMA.EHR.Infrastructure/Migrations/LeaveDb/LeaveDbContextModelSnapshot.cs +++ b/BMA.EHR.Infrastructure/Migrations/LeaveDb/LeaveDbContextModelSnapshot.cs @@ -727,6 +727,21 @@ namespace BMA.EHR.Infrastructure.Migrations.LeaveDb b.Property("LeaveRequestId") .HasColumnType("char(36)"); + b.Property("OrganizationName") + .IsRequired() + .HasColumnType("longtext") + .HasComment("สังกัด"); + + b.Property("PosExecutiveName") + .IsRequired() + .HasColumnType("longtext") + .HasComment("ตำแหน่งทางการบริหาร"); + + b.Property("PositionLevelName") + .IsRequired() + .HasColumnType("longtext") + .HasComment("ประเภทระดับตำแหน่ง"); + b.Property("PositionName") .IsRequired() .HasColumnType("longtext"); diff --git a/BMA.EHR.Leave/Controllers/LeaveRequestController.cs b/BMA.EHR.Leave/Controllers/LeaveRequestController.cs index cf17535f..4db84d53 100644 --- a/BMA.EHR.Leave/Controllers/LeaveRequestController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveRequestController.cs @@ -160,6 +160,9 @@ namespace BMA.EHR.Leave.Service.Controllers LastName = r.LastName, PositionName = r.PositionName, PositionSign = r.PositionSign, + PosExecutiveName = r.PosExecutiveName, + PositionLevelName = r.PositionLeaveName, + OrganizationName = r.OrganizationName, ProfileId = r.ProfileId, KeycloakId = r.KeycloakId, ApproveStatus = "PENDING", @@ -2034,6 +2037,9 @@ namespace BMA.EHR.Leave.Service.Controllers ProfileId = profile.Id, KeycloakId = Guid.Parse(UserId!), ApproveType = "SENDER", + PositionLevelName = profile.PositionLeaveName ?? "", + PosExecutiveName = profile.PosExecutiveName ?? "", + OrganizationName = profile.Oc ?? "", CreatedFullName = FullName ?? "", CreatedUserId = UserId!, diff --git a/BMA.EHR.Leave/DTOs/LeaveRequest/LeaveRequestApproverDto.cs b/BMA.EHR.Leave/DTOs/LeaveRequest/LeaveRequestApproverDto.cs index 21c2a216..b5ff27a5 100644 --- a/BMA.EHR.Leave/DTOs/LeaveRequest/LeaveRequestApproverDto.cs +++ b/BMA.EHR.Leave/DTOs/LeaveRequest/LeaveRequestApproverDto.cs @@ -27,5 +27,15 @@ namespace BMA.EHR.Leave.Service.DTOs.LeaveRequest [JsonProperty("keycloakId")] public Guid KeycloakId { get; set; } = Guid.Empty; + + [JsonProperty("positionLeaveName")] + public string PositionLeaveName { get; set; } = string.Empty; + + [JsonProperty("posExecutiveName")] + public string PosExecutiveName { get; set; } = string.Empty; + + [JsonProperty("organizationName")] + public string OrganizationName { get; set; } = string.Empty; + } } From 094789bfb174db57f20b9a3d9892f5b57cda5938 Mon Sep 17 00:00:00 2001 From: harid Date: Fri, 16 Jan 2026 15:43:55 +0700 Subject: [PATCH 039/178] =?UTF-8?q?API=20=E0=B8=82=E0=B8=AD=E0=B8=A5?= =?UTF-8?q?=E0=B8=B2=E0=B8=AD=E0=B8=AD=E0=B8=81=E0=B9=81=E0=B8=A5=E0=B8=B0?= =?UTF-8?q?=E0=B9=82=E0=B8=AD=E0=B8=99=E0=B8=AD=E0=B8=AD=E0=B8=81=20?= =?UTF-8?q?=E0=B8=9D=E0=B8=B1=E0=B9=88=E0=B8=87=20admin=20#2196?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../PlacementTransferController.cs | 134 ++++++++++++++++ .../Requests/PlacementTransferRequest.cs | 9 ++ .../Controllers/RetirementResignController.cs | 149 ++++++++++++++++++ .../RetirementResignEmployeeController.cs | 142 +++++++++++++++++ .../RetirementResignEmployeeRequest.cs | 17 ++ .../Requests/RetirementResignRequest.cs | 17 ++ 6 files changed, 468 insertions(+) diff --git a/BMA.EHR.Placement.Service/Controllers/PlacementTransferController.cs b/BMA.EHR.Placement.Service/Controllers/PlacementTransferController.cs index 566b799b..e27b4ad3 100644 --- a/BMA.EHR.Placement.Service/Controllers/PlacementTransferController.cs +++ b/BMA.EHR.Placement.Service/Controllers/PlacementTransferController.cs @@ -683,6 +683,140 @@ namespace BMA.EHR.Placement.Service.Controllers return Success(placementTransfer.Id); } + /// + /// สร้างคำขอโอน โดย admin + /// + /// + /// + /// ค่าตัวแปรที่ส่งมาไม่ถูกต้อง + /// ไม่ได้ Login เข้าระบบ + /// เมื่อเกิดข้อผิดพลาดในการทำงาน + [HttpPost("admin")] + public async Task> PostAdmin([FromForm] PlacementTransferAdminRequest req) + { + var placementTransfer = new PlacementTransfer + { + Organization = req.Organization, + Reason = req.Reason, + Status = "APPROVE", + CreatedFullName = FullName ?? "System Administrator", + CreatedUserId = UserId ?? "", + CreatedAt = DateTime.Now, + LastUpdateFullName = FullName ?? "System Administrator", + LastUpdateUserId = UserId ?? "", + LastUpdatedAt = DateTime.Now, + }; + var apiUrl = $"{_configuration["API"]}/org/profile/profileid/position/{req.ProfileId}"; + using (var client = new HttpClient()) + { + client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token.Replace("Bearer ", "")); + client.DefaultRequestHeaders.Add("api-key", _configuration["API_KEY"]); + var _req = new HttpRequestMessage(HttpMethod.Get, apiUrl); + var _res = await client.SendAsync(_req); + var _result = await _res.Content.ReadAsStringAsync(); + + var org = JsonConvert.DeserializeObject(_result); + + if (org == null || org.result == null) + return Error("ไม่พบหน่วยงานของผู้ใช้งานคนนี้", 404); + + placementTransfer.profileId = org.result.profileId; + placementTransfer.prefix = org.result.prefix; + placementTransfer.firstName = org.result.firstName; + placementTransfer.lastName = org.result.lastName; + placementTransfer.citizenId = org.result.citizenId; + placementTransfer.rootOld = org.result.root; + placementTransfer.rootOldId = org.result.rootId; + placementTransfer.rootDnaOldId = org.result.rootDnaId; + placementTransfer.rootShortNameOld = org.result.rootShortName; + placementTransfer.child1Old = org.result.child1; + placementTransfer.child1OldId = org.result.child1Id; + placementTransfer.child1DnaOldId = org.result.child1DnaId; + placementTransfer.child1ShortNameOld = org.result.child1ShortName; + placementTransfer.child2Old = org.result.child2; + placementTransfer.child2OldId = org.result.child2Id; + placementTransfer.child2DnaOldId = org.result.child2DnaId; + placementTransfer.child2ShortNameOld = org.result.child2ShortName; + placementTransfer.child3Old = org.result.child3; + placementTransfer.child3OldId = org.result.child3Id; + placementTransfer.child3DnaOldId = org.result.child3DnaId; + placementTransfer.child3ShortNameOld = org.result.child3ShortName; + placementTransfer.child4Old = org.result.child4; + placementTransfer.child4OldId = org.result.child4Id; + placementTransfer.child4DnaOldId = org.result.child4DnaId; + placementTransfer.child4ShortNameOld = org.result.child4ShortName; + placementTransfer.posMasterNoOld = org.result.posMasterNo; + placementTransfer.posTypeOldId = org.result.posTypeId; + placementTransfer.posTypeNameOld = org.result.posTypeName; + placementTransfer.posLevelOldId = org.result.posLevelId; + placementTransfer.posLevelNameOld = org.result.posLevelName; + placementTransfer.AmountOld = org.result.salary; + placementTransfer.PositionOld = org.result.position; + placementTransfer.PositionExecutiveOld = org.result.posExecutiveName; + placementTransfer.positionExecutiveFieldOld = org.result.positionExecutiveField; + placementTransfer.positionAreaOld = org.result.positionArea; + placementTransfer.PositionLevelOld = org.result.posLevelName; + placementTransfer.PositionTypeOld = org.result.posTypeName; + placementTransfer.PositionNumberOld = org.result.nodeShortName + " " + org.result.posMasterNo; + placementTransfer.OrganizationOld = (org.result.child4 == null ? "" : org.result.child4 + "\n") + + (org.result.child3 == null ? "" : org.result.child3 + "\n") + + (org.result.child2 == null ? "" : org.result.child2 + "\n") + + (org.result.child1 == null ? "" : org.result.child1 + "\n") + + (org.result.root == null ? "" : org.result.root); + placementTransfer.OrganizationPositionOld = org.result.position + "\n" + + (placementTransfer.PositionExecutiveOld == null ? "" : (placementTransfer.positionExecutiveField == null ? placementTransfer.PositionExecutiveOld + "\n" : placementTransfer.PositionExecutiveOld + "(" + placementTransfer.positionExecutiveField + ")" + "\n")) + + placementTransfer.OrganizationOld; + } + await _context.PlacementTransfers.AddAsync(placementTransfer); + await _context.SaveChangesAsync(); + if (Request.Form.Files != null && Request.Form.Files.Count != 0) + { + foreach (var file in Request.Form.Files) + { + var fileExtension = Path.GetExtension(file.FileName); + + var doc = await _documentService.UploadFileAsync(file, file.FileName); + var _doc = await _context.Documents.AsQueryable() + .FirstOrDefaultAsync(x => x.Id == doc.Id); + if (_doc != null) + { + var placementTransferDoc = new PlacementTransferDoc + { + PlacementTransfer = placementTransfer, + Document = _doc, + CreatedFullName = FullName ?? "System Administrator", + CreatedUserId = UserId ?? "", + CreatedAt = DateTime.Now, + LastUpdateFullName = FullName ?? "System Administrator", + LastUpdateUserId = UserId ?? "", + LastUpdatedAt = DateTime.Now, + }; + await _context.PlacementTransferDocs.AddAsync(placementTransferDoc); + + } + } + } + + //var baseAPIOrg = _configuration["API"]; + //var apiUrlOrg = $"{baseAPIOrg}/org/workflow/add-workflow"; + //using (var client = new HttpClient()) + //{ + // client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token.Replace("Bearer ", "")); + // client.DefaultRequestHeaders.Add("api-key", _configuration["API_KEY"]); + // var _res = await client.PostAsJsonAsync(apiUrlOrg, new + // { + // refId = placementTransfer.Id, + // sysName = "SYS_TRANSFER_REQ", + // posLevelName = placementTransfer.posLevelNameOld, + // posTypeName = placementTransfer.posTypeNameOld, + // fullName = $"{placementTransfer.prefix}{placementTransfer.firstName} {placementTransfer.lastName}" + // }); + //} + await _context.SaveChangesAsync(); + + return Success(placementTransfer.Id); + } + /// /// แก้ไขคำขอโอน /// diff --git a/BMA.EHR.Placement.Service/Requests/PlacementTransferRequest.cs b/BMA.EHR.Placement.Service/Requests/PlacementTransferRequest.cs index 7f740043..6611f92c 100644 --- a/BMA.EHR.Placement.Service/Requests/PlacementTransferRequest.cs +++ b/BMA.EHR.Placement.Service/Requests/PlacementTransferRequest.cs @@ -10,4 +10,13 @@ namespace BMA.EHR.Placement.Service.Requests public DateTime? Date { get; set; } public List? File { get; set; } } + + public class PlacementTransferAdminRequest + { + public string ProfileId { get; set; } + public string Organization { get; set; } + public string Reason { get; set; } + public DateTime? Date { get; set; } + public List? File { get; set; } + } } diff --git a/BMA.EHR.Retirement.Service/Controllers/RetirementResignController.cs b/BMA.EHR.Retirement.Service/Controllers/RetirementResignController.cs index d33b945b..2caa44a1 100644 --- a/BMA.EHR.Retirement.Service/Controllers/RetirementResignController.cs +++ b/BMA.EHR.Retirement.Service/Controllers/RetirementResignController.cs @@ -1343,6 +1343,155 @@ namespace BMA.EHR.Retirement.Service.Controllers return Success(retirementResign); } + /// + /// สร้างการลาออก โดย admin + /// + /// + /// + /// ค่าตัวแปรที่ส่งมาไม่ถูกต้อง + /// ไม่ได้ Login เข้าระบบ + /// เมื่อเกิดข้อผิดพลาดในการทำงาน + [HttpPost("admin")] + public async Task> PostAdmin([FromForm] RetirementResignAdminRequest req) + { + var Remark = req.Remark; + if (req.Reason != null) + { + switch (req.Reason.Trim().ToUpper()) + { + case "CAREER": Remark = $"ประกอบอาชีพอื่น" + (req.Remark == null || req.Remark == "" ? $"{req.Remark}" : ""); break; + case "MOVE": Remark = $"รับราชการสังกัดอื่น" + (req.Remark == null || req.Remark == "" ? $"{req.Remark}" : ""); break; + case "FAMILY": Remark = $"ดูแลบิดามารดา" + (req.Remark == null || req.Remark == "" ? $"{req.Remark}" : ""); break; + case "EDUCATION": Remark = $"ศึกษาต่อ" + (req.Remark == null || req.Remark == "" ? $"{req.Remark}" : ""); break; + case "OTHER": Remark = $"อื่น ๆ" + (req.Remark == null || req.Remark == "" ? $"{req.Remark}" : ""); break; + } + } + + var retirementResign = new RetirementResign + { + ApproveStep = "st1", + Location = req.Location, + SendDate = DateTime.Now, + ActiveDate = req.ActiveDate, + Reason = req.Reason, + ReasonResign = Remark, + Remark = req.Remark, + Status = "APPROVE", + IsActive = true, + CreatedFullName = FullName ?? "System Administrator", + CreatedUserId = UserId ?? "", + CreatedAt = DateTime.Now, + LastUpdateFullName = FullName ?? "System Administrator", + LastUpdateUserId = UserId ?? "", + LastUpdatedAt = DateTime.Now, + }; + + var apiUrl = $"{_configuration["API"]}/org/profile/profileid/position/{req.ProfileId}"; + using (var client = new HttpClient()) + { + client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token.Replace("Bearer ", "")); + client.DefaultRequestHeaders.Add("api-key", _configuration["API_KEY"]); + var _req = new HttpRequestMessage(HttpMethod.Get, apiUrl); + var _res = await client.SendAsync(_req); + var _result = await _res.Content.ReadAsStringAsync(); + + var org = JsonConvert.DeserializeObject(_result); + + if (org == null || org.result == null) + return Error("ไม่พบหน่วยงานของผู้ใช้งานคนนี้", 404); + + retirementResign.profileId = org.result.profileId; + retirementResign.prefix = org.result.prefix; + retirementResign.firstName = org.result.firstName; + retirementResign.lastName = org.result.lastName; + retirementResign.citizenId = org.result.citizenId; + retirementResign.rootOld = org.result.root; + retirementResign.rootOldId = org.result.rootId; + retirementResign.rootDnaOldId = org.result.rootDnaId; + retirementResign.rootShortNameOld = org.result.rootShortName; + retirementResign.child1Old = org.result.child1; + retirementResign.child1OldId = org.result.child1Id; + retirementResign.child1DnaOldId = org.result.child1DnaId; + retirementResign.child1ShortNameOld = org.result.child1ShortName; + retirementResign.child2Old = org.result.child2; + retirementResign.child2OldId = org.result.child2Id; + retirementResign.child2DnaOldId = org.result.child2DnaId; + retirementResign.child2ShortNameOld = org.result.child2ShortName; + retirementResign.child3Old = org.result.child3; + retirementResign.child3OldId = org.result.child3Id; + retirementResign.child3DnaOldId = org.result.child3DnaId; + retirementResign.child3ShortNameOld = org.result.child3ShortName; + retirementResign.child4Old = org.result.child4; + retirementResign.child4OldId = org.result.child4Id; + retirementResign.child4DnaOldId = org.result.child4DnaId; + retirementResign.child4ShortNameOld = org.result.child4ShortName; + retirementResign.posMasterNoOld = org.result.posMasterNo; + retirementResign.posTypeOldId = org.result.posTypeId; + retirementResign.posTypeNameOld = org.result.posTypeName; + retirementResign.posLevelOldId = org.result.posLevelId; + retirementResign.posLevelNameOld = org.result.posLevelName; + retirementResign.AmountOld = org.result.salary; + retirementResign.PositionOld = org.result.position; + retirementResign.PositionExecutiveOld = org.result.posExecutiveName; + retirementResign.positionExecutiveFieldOld = org.result.positionExecutiveField; + retirementResign.positionAreaOld = org.result.positionArea; + retirementResign.PositionLevelOld = org.result.posLevelName; + retirementResign.PositionTypeOld = org.result.posTypeName; + retirementResign.PositionNumberOld = org.result.nodeShortName + " " + org.result.posMasterNo; + retirementResign.OrganizationOld = (org.result.child4 == null ? "" : org.result.child4 + "\n") + + (org.result.child3 == null ? "" : org.result.child3 + "\n") + + (org.result.child2 == null ? "" : org.result.child2 + "\n") + + (org.result.child1 == null ? "" : org.result.child1 + "\n") + + (org.result.root == null ? "" : org.result.root); + retirementResign.OrganizationPositionOld = org.result.position + "\n" + + (retirementResign.PositionExecutiveOld == null ? "" : (retirementResign.positionExecutiveField == null ? retirementResign.PositionExecutiveOld + "\n" : retirementResign.PositionExecutiveOld + "(" + retirementResign.positionExecutiveField + ")" + "\n")) + + retirementResign.OrganizationOld; + + if ((retirementResign.posTypeNameOld == "ทั่วไป" && retirementResign.posLevelNameOld == "ชำนาญงาน") || (retirementResign.posTypeNameOld == "ทั่วไป" && retirementResign.posLevelNameOld == "ปฏิบัติงาน") || (retirementResign.posTypeNameOld == "วิชาการ" && retirementResign.posLevelNameOld == "ปฏิบัติการ") || (retirementResign.posTypeNameOld == "วิชาการ" && retirementResign.posLevelNameOld == "ชำนาญการ")) + { + retirementResign.Group = "1.1"; + } + else if ((retirementResign.posTypeNameOld == "ทั่วไป" && retirementResign.posLevelNameOld == "อาวุโส") || (retirementResign.posTypeNameOld == "วิชาการ" && retirementResign.posLevelNameOld == "ชำนาญการพิเศษ") || (retirementResign.posTypeNameOld == "อำนวยการ" && retirementResign.posLevelNameOld == "ต้น")) + { + retirementResign.Group = "1.2"; + } + else if ((retirementResign.posTypeNameOld == "ทั่วไป" && retirementResign.posLevelNameOld == "ทักษะพิเศษ") || (retirementResign.posTypeNameOld == "วิชาการ" && retirementResign.posLevelNameOld == "เชี่ยวชาญ") || (retirementResign.posTypeNameOld == "วิชาการ" && retirementResign.posLevelNameOld == "ทรงคุณวุฒิ") || (retirementResign.posTypeNameOld == "อำนวยการ" && retirementResign.posLevelNameOld == "สูง") || (retirementResign.posTypeNameOld == "บริหาร" && retirementResign.posLevelNameOld == "ต้น") || (retirementResign.posTypeNameOld == "บริหาร" && retirementResign.posLevelNameOld == "สูง")) + { + retirementResign.Group = "2"; + } + } + await _context.RetirementResigns.AddAsync(retirementResign); + await _context.SaveChangesAsync(); + if (Request.Form.Files != null && Request.Form.Files.Count != 0) + { + foreach (var file in Request.Form.Files) + { + var fileExtension = Path.GetExtension(file.FileName); + + var doc = await _documentService.UploadFileAsync(file, file.FileName); + var _doc = await _context.Documents.AsQueryable() + .FirstOrDefaultAsync(x => x.Id == doc.Id); + if (_doc != null) + { + var retirementResignDoc = new RetirementResignDoc + { + RetirementResign = retirementResign, + Document = _doc, + CreatedFullName = FullName ?? "System Administrator", + CreatedUserId = UserId ?? "", + CreatedAt = DateTime.Now, + LastUpdateFullName = FullName ?? "System Administrator", + LastUpdateUserId = UserId ?? "", + LastUpdatedAt = DateTime.Now, + }; + await _context.RetirementResignDocs.AddAsync(retirementResignDoc); + } + } + } + await _context.SaveChangesAsync(); + return Success(retirementResign); + } + /// /// แก้ไขการลาออก /// diff --git a/BMA.EHR.Retirement.Service/Controllers/RetirementResignEmployeeController.cs b/BMA.EHR.Retirement.Service/Controllers/RetirementResignEmployeeController.cs index bfd8ab9f..8bc6c975 100644 --- a/BMA.EHR.Retirement.Service/Controllers/RetirementResignEmployeeController.cs +++ b/BMA.EHR.Retirement.Service/Controllers/RetirementResignEmployeeController.cs @@ -1291,6 +1291,148 @@ namespace BMA.EHR.Retirement.Service.Controllers return Success(retirementResignEmployee); } + /// + /// สร้างการลาออก โดย admin + /// + /// + /// + /// ค่าตัวแปรที่ส่งมาไม่ถูกต้อง + /// ไม่ได้ Login เข้าระบบ + /// เมื่อเกิดข้อผิดพลาดในการทำงาน + [HttpPost("admin")] + public async Task> PostAdmin([FromForm] RetirementResignEmployeeAdminRequest req) + { + var Remark = req.Remark; + if (req.Reason != null) + { + switch (req.Reason.Trim().ToUpper()) + { + case "CAREER": Remark = $"ประกอบอาชีพอื่น" + (req.Remark == null || req.Remark == "" ? $"{req.Remark}" : ""); break; + case "MOVE": Remark = $"รับราชการสังกัดอื่น" + (req.Remark == null || req.Remark == "" ? $"{req.Remark}" : ""); break; + case "FAMILY": Remark = $"ดูแลบิดามารดา" + (req.Remark == null || req.Remark == "" ? $"{req.Remark}" : ""); break; + case "EDUCATION": Remark = $"ศึกษาต่อ" + (req.Remark == null || req.Remark == "" ? $"{req.Remark}" : ""); break; + case "OTHER": Remark = $"อื่น ๆ" + (req.Remark == null || req.Remark == "" ? $"{req.Remark}" : ""); break; + } + } + var retirementResignEmployee = new RetirementResignEmployee + { + ApproveStep = "st1", + Location = req.Location, + SendDate = DateTime.Now, + ActiveDate = req.ActiveDate, + Reason = req.Reason, + ReasonResign = Remark, + Remark = req.Remark, + Status = "APPROVE", + IsActive = true, + CreatedFullName = FullName ?? "System Administrator", + CreatedUserId = UserId ?? "", + CreatedAt = DateTime.Now, + LastUpdateFullName = FullName ?? "System Administrator", + LastUpdateUserId = UserId ?? "", + LastUpdatedAt = DateTime.Now, + }; + + var apiUrl = $"{_configuration["API"]}/org/profile-employee/profileid/position/{req.ProfileId}"; + using (var client = new HttpClient()) + { + client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token.Replace("Bearer ", "")); + client.DefaultRequestHeaders.Add("api-key", _configuration["API_KEY"]); + var _req = new HttpRequestMessage(HttpMethod.Get, apiUrl); + var _res = await client.SendAsync(_req); + var _result = await _res.Content.ReadAsStringAsync(); + + var org = JsonConvert.DeserializeObject(_result); + + if (org == null || org.result == null) + return Error("ไม่พบหน่วยงานของผู้ใช้งานคนนี้", 404); + + retirementResignEmployee.profileId = org.result.profileId; + retirementResignEmployee.prefix = org.result.prefix; + retirementResignEmployee.firstName = org.result.firstName; + retirementResignEmployee.lastName = org.result.lastName; + retirementResignEmployee.citizenId = org.result.citizenId; + retirementResignEmployee.rootOld = org.result.root; + retirementResignEmployee.rootOldId = org.result.rootId; + retirementResignEmployee.rootDnaOldId = org.result.rootDnaId; + retirementResignEmployee.rootShortNameOld = org.result.rootShortName; + retirementResignEmployee.child1Old = org.result.child1; + retirementResignEmployee.child1OldId = org.result.child1Id; + retirementResignEmployee.child1DnaOldId = org.result.child1DnaId; + retirementResignEmployee.child1ShortNameOld = org.result.child1ShortName; + retirementResignEmployee.child2Old = org.result.child2; + retirementResignEmployee.child2OldId = org.result.child2Id; + retirementResignEmployee.child2DnaOldId = org.result.child2DnaId; + retirementResignEmployee.child2ShortNameOld = org.result.child2ShortName; + retirementResignEmployee.child3Old = org.result.child3; + retirementResignEmployee.child3OldId = org.result.child3Id; + retirementResignEmployee.child3DnaOldId = org.result.child3DnaId; + retirementResignEmployee.child3ShortNameOld = org.result.child3ShortName; + retirementResignEmployee.child4Old = org.result.child4; + retirementResignEmployee.child4OldId = org.result.child4Id; + retirementResignEmployee.child4DnaOldId = org.result.child4DnaId; + retirementResignEmployee.child4ShortNameOld = org.result.child4ShortName; + retirementResignEmployee.posMasterNoOld = org.result.posMasterNo; + retirementResignEmployee.posTypeOldId = org.result.posTypeId; + retirementResignEmployee.posTypeNameOld = org.result.posTypeName; + retirementResignEmployee.posLevelOldId = org.result.posLevelId; + retirementResignEmployee.posLevelNameOld = org.result.posLevelName; + retirementResignEmployee.AmountOld = org.result.salary; + retirementResignEmployee.PositionOld = org.result.position; + retirementResignEmployee.PositionLevelOld = org.result.posLevelName; + retirementResignEmployee.PositionTypeOld = org.result.posTypeName; + retirementResignEmployee.PositionNumberOld = org.result.nodeShortName + " " + org.result.posMasterNo; + retirementResignEmployee.OrganizationOld = (org.result.child4 == null ? "" : org.result.child4 + "\n") + + (org.result.child3 == null ? "" : org.result.child3 + "\n") + + (org.result.child2 == null ? "" : org.result.child2 + "\n") + + (org.result.child1 == null ? "" : org.result.child1 + "\n") + + (org.result.root == null ? "" : org.result.root); + retirementResignEmployee.OrganizationPositionOld = org.result.position + "\n" + retirementResignEmployee.OrganizationOld; + if ((retirementResignEmployee.posTypeNameOld == "ทั่วไป" && retirementResignEmployee.posLevelNameOld == "ชำนาญงาน") || (retirementResignEmployee.posTypeNameOld == "ทั่วไป" && retirementResignEmployee.posLevelNameOld == "ปฏิบัติงาน") || (retirementResignEmployee.posTypeNameOld == "วิชาการ" && retirementResignEmployee.posLevelNameOld == "ปฏิบัติการ") || (retirementResignEmployee.posTypeNameOld == "วิชาการ" && retirementResignEmployee.posLevelNameOld == "ชำนาญการ")) + { + retirementResignEmployee.Group = "1.1"; + } + else if ((retirementResignEmployee.posTypeNameOld == "ทั่วไป" && retirementResignEmployee.posLevelNameOld == "อาวุโส") || (retirementResignEmployee.posTypeNameOld == "วิชาการ" && retirementResignEmployee.posLevelNameOld == "ชำนาญการพิเศษ") || (retirementResignEmployee.posTypeNameOld == "อำนวยการ" && retirementResignEmployee.posLevelNameOld == "ต้น")) + { + retirementResignEmployee.Group = "1.2"; + } + else if ((retirementResignEmployee.posTypeNameOld == "ทั่วไป" && retirementResignEmployee.posLevelNameOld == "ทักษะพิเศษ") || (retirementResignEmployee.posTypeNameOld == "วิชาการ" && retirementResignEmployee.posLevelNameOld == "เชี่ยวชาญ") || (retirementResignEmployee.posTypeNameOld == "วิชาการ" && retirementResignEmployee.posLevelNameOld == "ทรงคุณวุฒิ") || (retirementResignEmployee.posTypeNameOld == "อำนวยการ" && retirementResignEmployee.posLevelNameOld == "สูง") || (retirementResignEmployee.posTypeNameOld == "บริหาร" && retirementResignEmployee.posLevelNameOld == "ต้น") || (retirementResignEmployee.posTypeNameOld == "บริหาร" && retirementResignEmployee.posLevelNameOld == "สูง")) + { + retirementResignEmployee.Group = "2"; + } + } + await _context.RetirementResignEmployees.AddAsync(retirementResignEmployee); + await _context.SaveChangesAsync(); + if (Request.Form.Files != null && Request.Form.Files.Count != 0) + { + foreach (var file in Request.Form.Files) + { + var fileExtension = Path.GetExtension(file.FileName); + + var doc = await _documentService.UploadFileAsync(file, file.FileName); + var _doc = await _context.Documents.AsQueryable() + .FirstOrDefaultAsync(x => x.Id == doc.Id); + if (_doc != null) + { + var retirementResignEmployeeDoc = new RetirementResignEmployeeDoc + { + RetirementResignEmployee = retirementResignEmployee, + Document = _doc, + CreatedFullName = FullName ?? "System Administrator", + CreatedUserId = UserId ?? "", + CreatedAt = DateTime.Now, + LastUpdateFullName = FullName ?? "System Administrator", + LastUpdateUserId = UserId ?? "", + LastUpdatedAt = DateTime.Now, + }; + await _context.RetirementResignEmployeeDocs.AddAsync(retirementResignEmployeeDoc); + } + } + } + await _context.SaveChangesAsync(); + return Success(retirementResignEmployee); + } + /// /// แก้ไขการลาออก /// diff --git a/BMA.EHR.Retirement.Service/Requests/RetirementResignEmployeeRequest.cs b/BMA.EHR.Retirement.Service/Requests/RetirementResignEmployeeRequest.cs index 409bc185..dd883d0d 100644 --- a/BMA.EHR.Retirement.Service/Requests/RetirementResignEmployeeRequest.cs +++ b/BMA.EHR.Retirement.Service/Requests/RetirementResignEmployeeRequest.cs @@ -19,4 +19,21 @@ namespace BMA.EHR.Retirement.Service.Requests public double? AmountOld { get; set; } public List? File { get; set; } } + + public class RetirementResignEmployeeAdminRequest + { + public string ProfileId { get; set; } + public string? Location { get; set; } + public DateTime? ActiveDate { get; set; } + public string? Reason { get; set; } + public string? Remark { get; set; } + public string? ReasonResign { get; set; } + public string? OrganizationPositionOld { get; set; } + public string? PositionTypeOld { get; set; } + public string? PositionLevelOld { get; set; } + public string? PositionNumberOld { get; set; } + public string? RemarkHorizontal { get; set; } + public double? AmountOld { get; set; } + public List? File { get; set; } + } } diff --git a/BMA.EHR.Retirement.Service/Requests/RetirementResignRequest.cs b/BMA.EHR.Retirement.Service/Requests/RetirementResignRequest.cs index 7f1bf500..acef05aa 100644 --- a/BMA.EHR.Retirement.Service/Requests/RetirementResignRequest.cs +++ b/BMA.EHR.Retirement.Service/Requests/RetirementResignRequest.cs @@ -20,4 +20,21 @@ namespace BMA.EHR.Retirement.Service.Requests public double? AmountOld { get; set; } public List? File { get; set; } } + public class RetirementResignAdminRequest + { + public string ProfileId { get; set; } + public string? Location { get; set; } + public DateTime? ActiveDate { get; set; } + public string? Reason { get; set; } + public string? Remark { get; set; } + public string? ReasonResign { get; set; } + public string? OrganizationPositionOld { get; set; } + public string? PositionExecutiveOld { get; set; } + public string? PositionTypeOld { get; set; } + public string? PositionLevelOld { get; set; } + public string? PositionNumberOld { get; set; } + public string? RemarkHorizontal { get; set; } + public double? AmountOld { get; set; } + public List? File { get; set; } + } } From 83a915f92c08ea31c49a14eb8be21fa70edc2fa5 Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Fri, 16 Jan 2026 16:01:07 +0700 Subject: [PATCH 040/178] refactor LeaveReportController to sort employees by check-in time and remark #2193 --- BMA.EHR.Leave/Controllers/LeaveReportController.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/BMA.EHR.Leave/Controllers/LeaveReportController.cs b/BMA.EHR.Leave/Controllers/LeaveReportController.cs index 6a09e2b9..a86059fb 100644 --- a/BMA.EHR.Leave/Controllers/LeaveReportController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveReportController.cs @@ -2251,7 +2251,8 @@ namespace BMA.EHR.Leave.Service.Controllers } //employees = employees.OrderBy(x => x.checkInDate).ThenBy(x => x.checkInTimeRaw ?? DateTime.MaxValue).ThenBy(x => x.checkOutTimeRaw ?? DateTime.MaxValue).ToList(); employees = employees - .OrderBy(x => x.remark.Trim() == "" ? 0 : 1) // ข้อมูลที่ไม่มี remark ให้ขึ้นก่อน + .OrderBy(x => x.checkInTime.Trim() == "" ? 1 : 0) // เรียงตามวันที่ลงเวลา + .ThenBy(x => x.remark.Trim() == "" ? 0 : 1) // ข้อมูลที่ไม่มี remark ให้ขึ้นก่อน .ThenBy(x => x.checkInTimeRaw ?? DateTime.MaxValue).ThenBy(x => x.checkOutTimeRaw ?? DateTime.MaxValue) .ThenBy(x => x.remark) // จากนั้นจัดเรียงตาม remark .ToList(); From 510f1cd78ab3cc99696c4aed3fcec5ef916d4c2b Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Fri, 16 Jan 2026 16:09:23 +0700 Subject: [PATCH 041/178] fix sort order condition --- BMA.EHR.Leave/Controllers/LeaveReportController.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BMA.EHR.Leave/Controllers/LeaveReportController.cs b/BMA.EHR.Leave/Controllers/LeaveReportController.cs index a86059fb..8625497b 100644 --- a/BMA.EHR.Leave/Controllers/LeaveReportController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveReportController.cs @@ -2251,7 +2251,7 @@ namespace BMA.EHR.Leave.Service.Controllers } //employees = employees.OrderBy(x => x.checkInDate).ThenBy(x => x.checkInTimeRaw ?? DateTime.MaxValue).ThenBy(x => x.checkOutTimeRaw ?? DateTime.MaxValue).ToList(); employees = employees - .OrderBy(x => x.checkInTime.Trim() == "" ? 1 : 0) // เรียงตามวันที่ลงเวลา + .OrderBy(x => x.checkInTime.Trim() != "" ? 0 : 1) // เรียงตามวันที่ลงเวลา .ThenBy(x => x.remark.Trim() == "" ? 0 : 1) // ข้อมูลที่ไม่มี remark ให้ขึ้นก่อน .ThenBy(x => x.checkInTimeRaw ?? DateTime.MaxValue).ThenBy(x => x.checkOutTimeRaw ?? DateTime.MaxValue) .ThenBy(x => x.remark) // จากนั้นจัดเรียงตาม remark From b0715e3da6a7cf972dd28bb87ed308884a26c28d Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Fri, 16 Jan 2026 16:19:49 +0700 Subject: [PATCH 042/178] refactor LeaveController to update check-out status logic based on morning end time #2187 --- BMA.EHR.Leave/Controllers/LeaveController.cs | 24 ++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/BMA.EHR.Leave/Controllers/LeaveController.cs b/BMA.EHR.Leave/Controllers/LeaveController.cs index 26e3372d..b4206777 100644 --- a/BMA.EHR.Leave/Controllers/LeaveController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveController.cs @@ -989,13 +989,21 @@ namespace BMA.EHR.Leave.Service.Controllers else endTime = duty.EndTimeAfternoon; + var endTimeMorning = duty.EndTimeMorning; + string checkOutStatus = "NORMAL"; var leaveReq = await _leaveRequestRepository.GetLeavePeriodAsync(userId, currentDate.Date); if (leaveReq != null) { var leaveRange = leaveReq.LeaveRangeEnd == null ? "" : leaveReq.LeaveRangeEnd.ToUpper(); if (leaveRange == "AFTERNOON" || leaveRange == "ALL") - checkOutStatus = "NORMAL"; + { + if(DateTime.Parse(currentDate.ToString("yyyy-MM-dd HH:mm")) < + DateTime.Parse($"{currentDate.ToString("yyyy-MM-dd")} {endTimeMorning}")) + checkOutStatus = "ABSENT"; + else + checkOutStatus = "NORMAL"; + } else { // fix issue : SIT ระบบบันทึกเวลาปฏิบัติงาน>>ลงเวลาเข้า-ออกงาน (กรณีลงเวลาออกอีกวัน) #921 @@ -2438,6 +2446,9 @@ namespace BMA.EHR.Leave.Service.Controllers var endTime = isSeminar.Trim().ToUpper() == "Y" ? DateTime.Parse($"{DateTime.Now.Date.ToString("yyyy-MM-dd")} 14:30") : DateTime.Parse($"{DateTime.Now.Date.ToString("yyyy-MM-dd")} {duty.EndTimeAfternoon}"); + + var endTimeMorning = DateTime.Parse($"{DateTime.Now.Date.ToString("yyyy-MM-dd")} {duty.EndTimeMorning}"); + var status = string.Empty; if(lastCheckIn == null) { @@ -2458,7 +2469,16 @@ namespace BMA.EHR.Leave.Service.Controllers { var leaveRange = leaveReq.LeaveRangeEnd == null ? "" : leaveReq.LeaveRangeEnd.ToUpper(); if (leaveRange == "AFTERNOON" || leaveRange == "ALL") - status = "NORMAL"; + { + if(time < endTimeMorning) + { + status = "ABSENT"; + } + else + { + status = "NORMAL"; + } + } else { status = "ABSENT"; From b5c82f42431912fa7facc5cd0e0da78e779e1c34 Mon Sep 17 00:00:00 2001 From: harid Date: Mon, 19 Jan 2026 12:13:45 +0700 Subject: [PATCH 043/178] =?UTF-8?q?=E0=B8=9B=E0=B8=A3=E0=B8=B1=E0=B8=9A?= =?UTF-8?q?=E0=B8=A3=E0=B8=B2=E0=B8=A2=E0=B8=87=E0=B8=B2=E0=B8=99=E0=B8=A5?= =?UTF-8?q?=E0=B8=B2=20#2195?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Controllers/LeaveReportController.cs | 283 +++++++++++++++--- .../Controllers/LeaveRequestController.cs | 11 +- 2 files changed, 258 insertions(+), 36 deletions(-) diff --git a/BMA.EHR.Leave/Controllers/LeaveReportController.cs b/BMA.EHR.Leave/Controllers/LeaveReportController.cs index 8625497b..0dec80c6 100644 --- a/BMA.EHR.Leave/Controllers/LeaveReportController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveReportController.cs @@ -165,9 +165,28 @@ namespace BMA.EHR.Leave.Service.Controllers leaveSubTypeName = data.LeaveSubTypeName != null ? data.LeaveSubTypeName.ToThaiNumber() : "", dear = data.CommanderPosition == null ? data.Dear : data.CommanderPosition.ToThaiNumber(), fullname = fullName, - positionName = profile!.Position == null ? "-" : profile!.Position.ToThaiNumber(), + position = string.IsNullOrEmpty(profile.Position) ? "-" : profile.Position, + positionName = (!string.IsNullOrEmpty(profile.PositionLeaveName) && + (profile.PositionLeaveName.Contains("อำนวยการ") || profile.PositionLeaveName.Contains("บริหาร")) + ? string.IsNullOrEmpty(profile.PosExecutiveName) + ? string.IsNullOrEmpty(profile.Position) + ? "-" + : profile.Position + : profile.PosExecutiveName + : string.IsNullOrEmpty(profile.Position) + ? "-" + : string.IsNullOrEmpty(profile.PositionLeaveName) + ? profile.Position + : $"{profile.Position}{profile.PositionLeaveName}").ToThaiNumber(), positionLeaveName = profile!.PositionLeaveName == null ? "-" : profile!.PositionLeaveName.ToThaiNumber(), + posExecutiveName = profile.PosExecutiveName, organizationName = profile!.Oc!.ToThaiNumber(), + posExOrg = !string.IsNullOrEmpty(profile.PositionLeaveName) && + (profile.PositionLeaveName.Contains("อำนวยการ") || profile.PositionLeaveName.Contains("บริหาร")) + ? new[] { (profile!.Oc!.ToThaiNumber()) } + : !string.IsNullOrEmpty(profile.PosExecutiveName) + ? new[] { (profile.PosExecutiveName.ToThaiNumber()), (profile!.Oc!.ToThaiNumber()) } + : new[] { (profile!.Oc!.ToThaiNumber()) }, leaveDetail = data.LeaveDetail.ToThaiNumber(), leaveDateStart = data.LeaveStartDate.Date.ToThaiShortDate().ToThaiNumber(), leaveDateEnd = data.LeaveEndDate.Date.ToThaiShortDate().ToThaiNumber(), @@ -228,9 +247,28 @@ namespace BMA.EHR.Leave.Service.Controllers leaveSubTypeName = data.LeaveSubTypeName != null ? data.LeaveSubTypeName.ToThaiNumber() : "", dear = data.CommanderPosition == null ? data.Dear : data.CommanderPosition.ToThaiNumber(), fullname = fullName, - positionName = profile!.Position == null ? "-" : profile!.Position.ToThaiNumber(), + position = string.IsNullOrEmpty(profile.Position) ? "-" : profile.Position, + positionName = (!string.IsNullOrEmpty(profile.PositionLeaveName) && + (profile.PositionLeaveName.Contains("อำนวยการ") || profile.PositionLeaveName.Contains("บริหาร")) + ? string.IsNullOrEmpty(profile.PosExecutiveName) + ? string.IsNullOrEmpty(profile.Position) + ? "-" + : profile.Position + : profile.PosExecutiveName + : string.IsNullOrEmpty(profile.Position) + ? "-" + : string.IsNullOrEmpty(profile.PositionLeaveName) + ? profile.Position + : $"{profile.Position}{profile.PositionLeaveName}").ToThaiNumber(), positionLeaveName = profile!.PositionLeaveName == null ? "-" : profile!.PositionLeaveName.ToThaiNumber(), - organizationName = profile!.Oc!.ToThaiNumber() ?? "", + posExecutiveName = profile.PosExecutiveName, + organizationName = profile!.Oc!.ToThaiNumber(), + posExOrg = !string.IsNullOrEmpty(profile.PositionLeaveName) && + (profile.PositionLeaveName.Contains("อำนวยการ") || profile.PositionLeaveName.Contains("บริหาร")) + ? new[] { (profile!.Oc!.ToThaiNumber()) } + : !string.IsNullOrEmpty(profile.PosExecutiveName) + ? new[] { (profile.PosExecutiveName.ToThaiNumber()), (profile!.Oc!.ToThaiNumber()) } + : new[] { (profile!.Oc!.ToThaiNumber()) }, wifeDayName = data.WifeDayName ?? "", wifeDayDateBorn = data.WifeDayDateBorn == null || data.WifeDayDateBorn == "" ? "" : DateTime.Parse(data.WifeDayDateBorn).ToThaiShortDate().ToThaiNumber(), leaveDateStart = data.LeaveStartDate.Date.ToThaiShortDate().ToThaiNumber(), @@ -289,9 +327,28 @@ namespace BMA.EHR.Leave.Service.Controllers leaveSubTypeName = data.LeaveSubTypeName != null ? data.LeaveSubTypeName.ToThaiNumber() : "", dear = data.CommanderPosition == null ? data.Dear : data.CommanderPosition.ToThaiNumber(), fullname = fullName, - positionName = profile!.Position == null ? "-" : profile!.Position.ToThaiNumber(), + position = string.IsNullOrEmpty(profile.Position) ? "-" : profile.Position, + positionName = (!string.IsNullOrEmpty(profile.PositionLeaveName) && + (profile.PositionLeaveName.Contains("อำนวยการ") || profile.PositionLeaveName.Contains("บริหาร")) + ? string.IsNullOrEmpty(profile.PosExecutiveName) + ? string.IsNullOrEmpty(profile.Position) + ? "-" + : profile.Position + : profile.PosExecutiveName + : string.IsNullOrEmpty(profile.Position) + ? "-" + : string.IsNullOrEmpty(profile.PositionLeaveName) + ? profile.Position + : $"{profile.Position}{profile.PositionLeaveName}").ToThaiNumber(), positionLeaveName = profile!.PositionLeaveName == null ? "-" : profile!.PositionLeaveName.ToThaiNumber(), - organizationName = profile!.Oc!.ToThaiNumber() ?? "", + posExecutiveName = profile.PosExecutiveName, + organizationName = profile!.Oc!.ToThaiNumber(), + posExOrg = !string.IsNullOrEmpty(profile.PositionLeaveName) && + (profile.PositionLeaveName.Contains("อำนวยการ") || profile.PositionLeaveName.Contains("บริหาร")) + ? new[] { (profile!.Oc!.ToThaiNumber()) } + : !string.IsNullOrEmpty(profile.PosExecutiveName) + ? new[] { (profile.PosExecutiveName.ToThaiNumber()), (profile!.Oc!.ToThaiNumber()) } + : new[] { (profile!.Oc!.ToThaiNumber()) }, restDayOldTotal = extendLeave.ToString().ToThaiNumber(), restDayCurrentTotal = (10).ToString().ToThaiNumber(), @@ -357,9 +414,28 @@ namespace BMA.EHR.Leave.Service.Controllers leaveSubTypeName = data.LeaveSubTypeName != null ? data.LeaveSubTypeName.ToThaiNumber() : "", dear = data.CommanderPosition == null ? data.Dear : data.CommanderPosition.ToThaiNumber(), fullname = fullName, - positionName = profile!.Position == null ? "-" : profile!.Position.ToThaiNumber(), + position = string.IsNullOrEmpty(profile.Position) ? "-" : profile.Position, + positionName = (!string.IsNullOrEmpty(profile.PositionLeaveName) && + (profile.PositionLeaveName.Contains("อำนวยการ") || profile.PositionLeaveName.Contains("บริหาร")) + ? string.IsNullOrEmpty(profile.PosExecutiveName) + ? string.IsNullOrEmpty(profile.Position) + ? "-" + : profile.Position + : profile.PosExecutiveName + : string.IsNullOrEmpty(profile.Position) + ? "-" + : string.IsNullOrEmpty(profile.PositionLeaveName) + ? profile.Position + : $"{profile.Position}{profile.PositionLeaveName}").ToThaiNumber(), positionLeaveName = profile!.PositionLeaveName == null ? "-" : profile!.PositionLeaveName.ToThaiNumber(), - organizationName = profile!.Oc!.ToThaiNumber() ?? "", + posExecutiveName = profile.PosExecutiveName, + organizationName = profile!.Oc!.ToThaiNumber(), + posExOrg = !string.IsNullOrEmpty(profile.PositionLeaveName) && + (profile.PositionLeaveName.Contains("อำนวยการ") || profile.PositionLeaveName.Contains("บริหาร")) + ? new[] { (profile!.Oc!.ToThaiNumber()) } + : !string.IsNullOrEmpty(profile.PosExecutiveName) + ? new[] { (profile.PosExecutiveName.ToThaiNumber()), (profile!.Oc!.ToThaiNumber()) } + : new[] { (profile!.Oc!.ToThaiNumber()) }, leavegovernmentDate = data.LeaveGovernmentDate == null ? "" : data.LeaveGovernmentDate.Value.Date.ToThaiShortDate().ToThaiNumber(), @@ -393,9 +469,28 @@ namespace BMA.EHR.Leave.Service.Controllers leaveSubTypeName = data.LeaveSubTypeName != null ? data.LeaveSubTypeName.ToThaiNumber() : "", dear = data.CommanderPosition == null ? data.Dear : data.CommanderPosition.ToThaiNumber(), fullname = fullName, - positionName = profile!.Position == null ? "-" : profile!.Position.ToThaiNumber(), + position = string.IsNullOrEmpty(profile.Position) ? "-" : profile.Position, + positionName = (!string.IsNullOrEmpty(profile.PositionLeaveName) && + (profile.PositionLeaveName.Contains("อำนวยการ") || profile.PositionLeaveName.Contains("บริหาร")) + ? string.IsNullOrEmpty(profile.PosExecutiveName) + ? string.IsNullOrEmpty(profile.Position) + ? "-" + : profile.Position + : profile.PosExecutiveName + : string.IsNullOrEmpty(profile.Position) + ? "-" + : string.IsNullOrEmpty(profile.PositionLeaveName) + ? profile.Position + : $"{profile.Position}{profile.PositionLeaveName}").ToThaiNumber(), positionLeaveName = profile!.PositionLeaveName == null ? "-" : profile!.PositionLeaveName.ToThaiNumber(), - organizationName = profile!.Oc!.ToThaiNumber() ?? "", + posExecutiveName = profile.PosExecutiveName, + organizationName = profile!.Oc!.ToThaiNumber(), + posExOrg = !string.IsNullOrEmpty(profile.PositionLeaveName) && + (profile.PositionLeaveName.Contains("อำนวยการ") || profile.PositionLeaveName.Contains("บริหาร")) + ? new[] { (profile!.Oc!.ToThaiNumber()) } + : !string.IsNullOrEmpty(profile.PosExecutiveName) + ? new[] { (profile.PosExecutiveName.ToThaiNumber()), (profile!.Oc!.ToThaiNumber()) } + : new[] { (profile!.Oc!.ToThaiNumber()) }, leavebirthDate = data.LeaveBirthDate == null ? "" : data.LeaveBirthDate.Value.Date.ToThaiShortDate().ToThaiNumber(), leavegovernmentDate = data.LeaveGovernmentDate == null ? "" : data.LeaveGovernmentDate.Value.Date.ToThaiShortDate().ToThaiNumber(), @@ -454,9 +549,28 @@ namespace BMA.EHR.Leave.Service.Controllers leaveSubTypeName = data.LeaveSubTypeName != null ? data.LeaveSubTypeName.ToThaiNumber() : "", dear = data.CommanderPosition == null ? data.Dear : data.CommanderPosition.ToThaiNumber(), fullname = fullName, - positionName = profile!.Position == null ? "-" : profile!.Position.ToThaiNumber(), + position = string.IsNullOrEmpty(profile.Position) ? "-" : profile.Position, + positionName = (!string.IsNullOrEmpty(profile.PositionLeaveName) && + (profile.PositionLeaveName.Contains("อำนวยการ") || profile.PositionLeaveName.Contains("บริหาร")) + ? string.IsNullOrEmpty(profile.PosExecutiveName) + ? string.IsNullOrEmpty(profile.Position) + ? "-" + : profile.Position + : profile.PosExecutiveName + : string.IsNullOrEmpty(profile.Position) + ? "-" + : string.IsNullOrEmpty(profile.PositionLeaveName) + ? profile.Position + : $"{profile.Position}{profile.PositionLeaveName}").ToThaiNumber(), positionLeaveName = profile!.PositionLeaveName == null ? "-" : profile!.PositionLeaveName.ToThaiNumber(), - organizationName = profile!.Oc!.ToThaiNumber() ?? "", + posExecutiveName = profile.PosExecutiveName, + organizationName = profile!.Oc!.ToThaiNumber(), + posExOrg = !string.IsNullOrEmpty(profile.PositionLeaveName) && + (profile.PositionLeaveName.Contains("อำนวยการ") || profile.PositionLeaveName.Contains("บริหาร")) + ? new[] { (profile!.Oc!.ToThaiNumber()) } + : !string.IsNullOrEmpty(profile.PosExecutiveName) + ? new[] { (profile.PosExecutiveName.ToThaiNumber()), (profile!.Oc!.ToThaiNumber()) } + : new[] { (profile!.Oc!.ToThaiNumber()) }, absentDaySummon = data.AbsentDaySummon.ToThaiNumber(), absentDayLocation = data.AbsentDayLocation.ToThaiNumber(), @@ -509,9 +623,28 @@ namespace BMA.EHR.Leave.Service.Controllers leaveSubTypeName = data.LeaveSubTypeName != null ? data.LeaveSubTypeName.ToThaiNumber() : "", dear = data.CommanderPosition == null ? data.Dear : data.CommanderPosition.ToThaiNumber(), fullname = fullName, - positionName = profile!.Position == null ? "-" : profile!.Position.ToThaiNumber(), + position = string.IsNullOrEmpty(profile.Position) ? "-" : profile.Position, + positionName = (!string.IsNullOrEmpty(profile.PositionLeaveName) && + (profile.PositionLeaveName.Contains("อำนวยการ") || profile.PositionLeaveName.Contains("บริหาร")) + ? string.IsNullOrEmpty(profile.PosExecutiveName) + ? string.IsNullOrEmpty(profile.Position) + ? "-" + : profile.Position + : profile.PosExecutiveName + : string.IsNullOrEmpty(profile.Position) + ? "-" + : string.IsNullOrEmpty(profile.PositionLeaveName) + ? profile.Position + : $"{profile.Position}{profile.PositionLeaveName}").ToThaiNumber(), positionLeaveName = profile!.PositionLeaveName == null ? "-" : profile!.PositionLeaveName.ToThaiNumber(), - organizationName = profile!.Oc!.ToThaiNumber() ?? "", + posExecutiveName = profile.PosExecutiveName, + organizationName = profile!.Oc!.ToThaiNumber(), + posExOrg = !string.IsNullOrEmpty(profile.PositionLeaveName) && + (profile.PositionLeaveName.Contains("อำนวยการ") || profile.PositionLeaveName.Contains("บริหาร")) + ? new[] { (profile!.Oc!.ToThaiNumber()) } + : !string.IsNullOrEmpty(profile.PosExecutiveName) + ? new[] { (profile.PosExecutiveName.ToThaiNumber()), (profile!.Oc!.ToThaiNumber()) } + : new[] { (profile!.Oc!.ToThaiNumber()) }, leavebirthDate = data.LeaveBirthDate == null ? "" : data.LeaveBirthDate.Value.Date.ToThaiShortDate().ToThaiNumber(), leavegovernmentDate = data.LeaveGovernmentDate == null ? "" : data.LeaveGovernmentDate.Value.Date.ToThaiShortDate().ToThaiNumber(), @@ -587,9 +720,28 @@ namespace BMA.EHR.Leave.Service.Controllers dear = data.CommanderPosition == null ? data.Dear : data.CommanderPosition.ToThaiNumber(), fullname = fullName, fullnameEng = "", - positionName = profile!.Position == null ? "-" : profile!.Position.ToThaiNumber(), + position = string.IsNullOrEmpty(profile.Position) ? "-" : profile.Position, + positionName = (!string.IsNullOrEmpty(profile.PositionLeaveName) && + (profile.PositionLeaveName.Contains("อำนวยการ") || profile.PositionLeaveName.Contains("บริหาร")) + ? string.IsNullOrEmpty(profile.PosExecutiveName) + ? string.IsNullOrEmpty(profile.Position) + ? "-" + : profile.Position + : profile.PosExecutiveName + : string.IsNullOrEmpty(profile.Position) + ? "-" + : string.IsNullOrEmpty(profile.PositionLeaveName) + ? profile.Position + : $"{profile.Position}{profile.PositionLeaveName}").ToThaiNumber(), positionLeaveName = profile!.PositionLeaveName == null ? "-" : profile!.PositionLeaveName.ToThaiNumber(), - organizationName = profile!.Oc!.ToThaiNumber() ?? "", + posExecutiveName = profile.PosExecutiveName, + organizationName = profile!.Oc!.ToThaiNumber(), + posExOrg = !string.IsNullOrEmpty(profile.PositionLeaveName) && + (profile.PositionLeaveName.Contains("อำนวยการ") || profile.PositionLeaveName.Contains("บริหาร")) + ? new[] { (profile!.Oc!.ToThaiNumber()) } + : !string.IsNullOrEmpty(profile.PosExecutiveName) + ? new[] { (profile.PosExecutiveName.ToThaiNumber()), (profile!.Oc!.ToThaiNumber()) } + : new[] { (profile!.Oc!.ToThaiNumber()) }, leaveDateStart = data.LeaveStartDate.Date.ToThaiShortDate().ToThaiNumber(), leaveDateEnd = data.LeaveEndDate.Date.ToThaiShortDate().ToThaiNumber(), @@ -679,9 +831,28 @@ namespace BMA.EHR.Leave.Service.Controllers leaveSubTypeName = data.LeaveSubTypeName != null ? data.LeaveSubTypeName.ToThaiNumber() : "", dear = data.CommanderPosition == null ? data.Dear : data.CommanderPosition.ToThaiNumber(), fullname = fullName, - positionName = profile!.Position == null ? "-" : profile!.Position.ToThaiNumber(), + position = string.IsNullOrEmpty(profile.Position) ? "-" : profile.Position, + positionName = (!string.IsNullOrEmpty(profile.PositionLeaveName) && + (profile.PositionLeaveName.Contains("อำนวยการ") || profile.PositionLeaveName.Contains("บริหาร")) + ? string.IsNullOrEmpty(profile.PosExecutiveName) + ? string.IsNullOrEmpty(profile.Position) + ? "-" + : profile.Position + : profile.PosExecutiveName + : string.IsNullOrEmpty(profile.Position) + ? "-" + : string.IsNullOrEmpty(profile.PositionLeaveName) + ? profile.Position + : $"{profile.Position}{profile.PositionLeaveName}").ToThaiNumber(), positionLeaveName = profile!.PositionLeaveName == null ? "-" : profile!.PositionLeaveName.ToThaiNumber(), - organizationName = profile!.Oc!.ToThaiNumber() ?? "", + posExecutiveName = profile.PosExecutiveName, + organizationName = profile!.Oc!.ToThaiNumber(), + posExOrg = !string.IsNullOrEmpty(profile.PositionLeaveName) && + (profile.PositionLeaveName.Contains("อำนวยการ") || profile.PositionLeaveName.Contains("บริหาร")) + ? new[] { (profile!.Oc!.ToThaiNumber()) } + : !string.IsNullOrEmpty(profile.PosExecutiveName) + ? new[] { (profile.PosExecutiveName.ToThaiNumber()), (profile!.Oc!.ToThaiNumber()) } + : new[] { (profile!.Oc!.ToThaiNumber()) }, leaveSalary = data.LeaveSalary == null ? "" : data.LeaveSalary.Value.ToNumericText().ToThaiNumber(), leaveSalaryText = data.LeaveSalaryText.ToThaiNumber(), @@ -748,9 +919,28 @@ namespace BMA.EHR.Leave.Service.Controllers leaveSubTypeName = data.LeaveSubTypeName != null ? data.LeaveSubTypeName.ToThaiNumber() : "", dear = data.CommanderPosition == null ? data.Dear : data.CommanderPosition.ToThaiNumber(), fullname = fullName, - positionName = profile!.Position == null ? "-" : profile!.Position.ToThaiNumber(), + position = string.IsNullOrEmpty(profile.Position) ? "-" : profile.Position, + positionName = (!string.IsNullOrEmpty(profile.PositionLeaveName) && + (profile.PositionLeaveName.Contains("อำนวยการ") || profile.PositionLeaveName.Contains("บริหาร")) + ? string.IsNullOrEmpty(profile.PosExecutiveName) + ? string.IsNullOrEmpty(profile.Position) + ? "-" + : profile.Position + : profile.PosExecutiveName + : string.IsNullOrEmpty(profile.Position) + ? "-" + : string.IsNullOrEmpty(profile.PositionLeaveName) + ? profile.Position + : $"{profile.Position}{profile.PositionLeaveName}").ToThaiNumber(), positionLeaveName = profile!.PositionLeaveName == null ? "-" : profile!.PositionLeaveName.ToThaiNumber(), - organizationName = profile!.Oc!.ToThaiNumber() ?? "", + posExecutiveName = profile.PosExecutiveName, + organizationName = profile!.Oc!.ToThaiNumber(), + posExOrg = !string.IsNullOrEmpty(profile.PositionLeaveName) && + (profile.PositionLeaveName.Contains("อำนวยการ") || profile.PositionLeaveName.Contains("บริหาร")) + ? new[] { (profile!.Oc!.ToThaiNumber()) } + : !string.IsNullOrEmpty(profile.PosExecutiveName) + ? new[] { (profile.PosExecutiveName.ToThaiNumber()), (profile!.Oc!.ToThaiNumber()) } + : new[] { (profile!.Oc!.ToThaiNumber()) }, leaveDateStart = data.LeaveStartDate.Date.ToThaiShortDate().ToThaiNumber(), leaveDateEnd = data.LeaveEndDate.Date.ToThaiShortDate().ToThaiNumber(), @@ -2606,11 +2796,12 @@ namespace BMA.EHR.Leave.Service.Controllers { var _default = new { - fullName = "......................", - positionName = "......................", - positionSign = "......................", - updatedAt = "...... /...... /......", - comment = "......................", + fullName = "............................................", + positionName = "............................................", + posExOrg = Array.Empty(), + positionSign = "............................................", + updatedAt = "............/............/............", + comment = "......................................................................................................................................................................", approveType = "" }; @@ -2618,37 +2809,61 @@ namespace BMA.EHR.Leave.Service.Controllers .Select(x => new { fullName = $"{(x.Prefix ?? "")}{(x.FirstName ?? "")} {(x.LastName ?? "")}".Trim(), - positionName = x.PositionName ?? "......................", - positionSign = x.PositionSign ?? "......................", + positionName = (!string.IsNullOrEmpty(x.PositionLevelName) && + (x.PositionLevelName.Contains("อำนวยการ") || x.PositionLevelName.Contains("บริหาร")) + ? string.IsNullOrEmpty(x.PosExecutiveName) + ? string.IsNullOrEmpty(x.PositionName) + ? "............................................" + : x.PositionName + : x.PosExecutiveName + : string.IsNullOrEmpty(x.PositionName) + ? "............................................" + : string.IsNullOrEmpty(x.PositionLevelName) + ? x.PositionName + : $"{x.PositionName}{x.PositionLevelName}").ToThaiNumber(), + posExOrg = !string.IsNullOrEmpty(x.PositionLevelName) && + (x.PositionLevelName.Contains("อำนวยการ") || x.PositionLevelName.Contains("บริหาร")) + ? new[] { (x!.OrganizationName!.ToThaiNumber()) } + : !string.IsNullOrEmpty(x.PosExecutiveName) + ? new[] { (x.PosExecutiveName.ToThaiNumber()), (x!.OrganizationName!.ToThaiNumber()) } + : new[] { (x!.OrganizationName!.ToThaiNumber()) }, + positionSign = !string.IsNullOrEmpty(x.PositionSign) + ? x.PositionSign.Replace("\r", "").Replace("\n", " ") + : "............................................", updatedAt = x.LastUpdatedAt.HasValue ? x.LastUpdatedAt.Value.Date.ToThaiShortDate().ToThaiNumber() - : "...... /...... /......", - comment = !string.IsNullOrEmpty(x.Comment) - ? x.Comment.Replace("\r", "").Replace("\n", "").Trim() - : "......................", + : "............/............/............", + comment = !string.IsNullOrEmpty(x.Comment) + ? x.Comment.Replace("\r", "").Replace("\n", " ").Trim() + : "......................................................................................................................................................................", approveType = (x.ApproveType ?? "").Trim().ToUpper() }) .ToList(); + // การเจ้าหน้าที่ var sender = approvers .FirstOrDefault(x => x.approveType == "SENDER") ?? _default; - var approver = approvers - .FirstOrDefault(x => x.approveType == "APPROVER") - ?? _default; - + // ผู้บังคับบัญชา (มีได้มากกว่า 1 คน) var commanders = approvers .Where(x => x.approveType == "COMMANDER") .DefaultIfEmpty(_default) .ToList(); + // ผู้มีอำนาจ + var approver = approvers + .FirstOrDefault(x => x.approveType == "APPROVER") + ?? _default; + return new { + sign = "............................................", sender = sender, approver = approver, commanders = commanders }; + } } diff --git a/BMA.EHR.Leave/Controllers/LeaveRequestController.cs b/BMA.EHR.Leave/Controllers/LeaveRequestController.cs index 4db84d53..dc3afc62 100644 --- a/BMA.EHR.Leave/Controllers/LeaveRequestController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveRequestController.cs @@ -166,7 +166,14 @@ namespace BMA.EHR.Leave.Service.Controllers ProfileId = r.ProfileId, KeycloakId = r.KeycloakId, ApproveStatus = "PENDING", - ApproveType = type.Trim().ToUpper() + ApproveType = type.Trim().ToUpper(), + + CreatedFullName = FullName ?? "", + CreatedUserId = UserId!, + CreatedAt = DateTime.Now, + LastUpdateFullName = FullName ?? "", + LastUpdateUserId = UserId!, + LastUpdatedAt = DateTime.Now, }); } @@ -2033,7 +2040,7 @@ namespace BMA.EHR.Leave.Service.Controllers Prefix = profile.Prefix ?? "", FirstName = profile.FirstName ?? "", LastName = profile.LastName ?? "", - PositionName = $"{profile.Position ?? ""}{profile.PositionLeaveName ?? ""}", + PositionName = $"{profile.Position ?? ""}", ProfileId = profile.Id, KeycloakId = Guid.Parse(UserId!), ApproveType = "SENDER", From 21f82d69e1f2d99b5f9436a99d1cc046c61a57cf Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Mon, 19 Jan 2026 14:27:43 +0700 Subject: [PATCH 044/178] Add load testing script for simulating 30,000 requests over 10 minutes --- BMA.EHR.Leave/Controllers/LeaveController.cs | 4 +- dotnet_keycloak_test.js | 54 ++++++++++++++++++++ 2 files changed, 57 insertions(+), 1 deletion(-) create mode 100644 dotnet_keycloak_test.js diff --git a/BMA.EHR.Leave/Controllers/LeaveController.cs b/BMA.EHR.Leave/Controllers/LeaveController.cs index b4206777..aa89fc6a 100644 --- a/BMA.EHR.Leave/Controllers/LeaveController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveController.cs @@ -2448,6 +2448,7 @@ namespace BMA.EHR.Leave.Service.Controllers : DateTime.Parse($"{DateTime.Now.Date.ToString("yyyy-MM-dd")} {duty.EndTimeAfternoon}"); var endTimeMorning = DateTime.Parse($"{DateTime.Now.Date.ToString("yyyy-MM-dd")} {duty.EndTimeMorning}"); + var endTimeDisplay = endTime; var status = string.Empty; if(lastCheckIn == null) @@ -2473,6 +2474,7 @@ namespace BMA.EHR.Leave.Service.Controllers if(time < endTimeMorning) { status = "ABSENT"; + endTimeDisplay = endTimeMorning; } else { @@ -2502,7 +2504,7 @@ namespace BMA.EHR.Leave.Service.Controllers Status = status, StatusText = status == "ABSENT" ? "ขาดราชการ" : "ปกติ", ServerTime = time, - EndTime = endTime + EndTime = endTimeDisplay }); } diff --git a/dotnet_keycloak_test.js b/dotnet_keycloak_test.js new file mode 100644 index 00000000..d271087d --- /dev/null +++ b/dotnet_keycloak_test.js @@ -0,0 +1,54 @@ +// ทดสอบการยิง 30,000 requests ในเวลา 10 นาที โดยให้กระจายการยิงในเวลาที่ต่างๆ กัน + +import { check, sleep } from "k6"; +import http from "k6/http"; +import { Rate } from "k6/metrics"; + +export let errorRate = new Rate("errors"); + +// จำนวน request ที่ต้องการยิง + +// ระยะเวลาทดสอบทั้งหมด + +// จำนวน Virtual Users เฉลี่ยที่ต้องการ 300 users +//const averageVus = Math.ceil(totalRequests / totalDuration); +const averageVus = 300; + +export let options = { + stages: [ + { duration: "2m", target: averageVus * 0.5 }, // 20% ของการทดสอบ เพิ่ม VUs เป็น 50% ของค่าเฉลี่ย + { duration: "4m", target: averageVus }, // 40% ของการทดสอบ เพิ่ม VUs เป็น 100% ของค่าเฉลี่ย + { duration: "2m", target: averageVus * 1.5 }, // 20% ของการทดสอบ เพิ่ม VUs เป็น 150% ของค่าเฉลี่ย + { duration: "2m", target: 0 }, // ลด VUs ลงมาเป็น 0 + ], + thresholds: { + errors: ["rate<0.01"], // อัตรา error ต้องน้อยกว่า 1% + http_req_duration: ["p(95)<2000"], // 95% ของ requests ควรใช้เวลาไม่เกิน 2 วินาที + }, +}; + +export default function () { + // ตัวเลือก headers + let headers = { + "Content-Type": "application/json", + Authorization: + "Bearer eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJ4WTJWUi1FRnZ2TlBzTXMzOXU4b29WQldRTDZtUHdyTkpPaDNrb0pGVGdVIn0.eyJleHAiOjE3NzYyMTkxNjgsImlhdCI6MTc2ODQ0MzE2OCwianRpIjoiZDQxMmI5MWEtZmZhMi00N2JiLTliZDUtZDE5NTdmMDFjYzQyIiwiaXNzIjoiaHR0cHM6Ly9ocm1zLWlkLmJhbmdrb2suZ28udGgvcmVhbG1zL2hybXMiLCJhdWQiOiJhY2NvdW50Iiwic3ViIjoiYmFmYzU3OTUtYmVmYy00ZDNmLWE0NjEtMzUzM2MzOGE1ZmMxIiwidHlwIjoiQmVhcmVyIiwiYXpwIjoiZ2V0dG9rZW4tY2hlY2tpbiIsInNpZCI6IjBkNzdiY2Y5LTE4YWQtNGQyMS1hYjBjLTI4Y2ZiZjUyZGZiNCIsImFjciI6IjEiLCJhbGxvd2VkLW9yaWdpbnMiOlsiaHR0cHM6Ly9ocm1zLmJhbmdrb2suZ28udGgiLCJodHRwczovL2hybXMtY2hlY2tpbi5iYW5na29rLmdvLnRoIl0sInJlYWxtX2FjY2VzcyI6eyJyb2xlcyI6WyJTVVBFUl9BRE1JTiIsInN0b3JhZ2VfbWFuYWdlbWVudCIsIm9mZmxpbmVfYWNjZXNzIiwiU1RBRkYiLCJkZWZhdWx0LXJvbGVzLWhybXMiLCJ1bWFfYXV0aG9yaXphdGlvbiIsIkFETUlOIiwiVVNFUiJdfSwicmVzb3VyY2VfYWNjZXNzIjp7ImFjY291bnQiOnsicm9sZXMiOlsibWFuYWdlLWFjY291bnQiLCJtYW5hZ2UtYWNjb3VudC1saW5rcyIsInZpZXctcHJvZmlsZSJdfX0sInNjb3BlIjoiZW1haWwgb3BlbmlkIHByb2ZpbGUiLCJlbWFpbF92ZXJpZmllZCI6ZmFsc2UsInJvbGUiOlsiU1VQRVJfQURNSU4iLCJzdG9yYWdlX21hbmFnZW1lbnQiLCJvZmZsaW5lX2FjY2VzcyIsIlNUQUZGIiwiZGVmYXVsdC1yb2xlcy1ocm1zIiwidW1hX2F1dGhvcml6YXRpb24iLCJBRE1JTiIsIlVTRVIiXSwibmFtZSI6IuC4p-C4seC4meC5gOC4ieC4peC4tOC4oSDguInguLHguJXguKPguJfguK3guIciLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiIzMTIwMjAwNDI0OTc1IiwiZ2l2ZW5fbmFtZSI6IuC4p-C4seC4meC5gOC4ieC4peC4tOC4oSIsImZhbWlseV9uYW1lIjoi4LiJ4Lix4LiV4Lij4LiX4Lit4LiHIn0.UhMn0NEkymPxMAcb4noZedHCSqXotCyD2RziBtLYHn5OhA9yk1915Rrt9iV4wVaebr74iZ2eZMpBwp8YVy8-3cPXSv9T3vzbXwFP7IeICPCDDf4bOPFEHP5FYow2s9v48qG81wnu01AG7_EL2-CQKh1sBVrCVUUlATlf-P4lT_lHeHOCKNXTmw4V0IWm96ec6pk-jFY3KH2JdRSWR7wq8g-KVxhLOxk_pF72kMwOpdvcr_99byg28zzj6QfeNYXLt61koHXnZppUqytt86mQQgfamv2FNVywCEzbRITUceu2rmJnwQE8ubeoCh4UOsYauUuSKd7RPqvvXxL_Vg__8Q", + //"Bearer eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJTT2wwWmFidm9rRzZET3pDZVBtT09Kek5haTdMUldkci1zV3lEYjRELTc0In0.eyJleHAiOjE3Njg4ODAzMjgsImlhdCI6MTc2ODc5MzkyOCwianRpIjoiMDYxODBlMWYtNTQzYy00MjU0LWFmN2QtYWI1NDA5NzFmNWY2IiwiaXNzIjoiaHR0cHM6Ly9ocm1zYmtrLWlkLmNhc2UtY29sbGVjdGlvbi5jb20vcmVhbG1zL2hybXMiLCJhdWQiOlsiYWNjb3VudCIsImdldHRva2VuIl0sInN1YiI6IjQzOWZhMzZkLTZiYzUtNGVmNS05NWFhLWVmMjllNjRkMmU5ZiIsInR5cCI6IkJlYXJlciIsImF6cCI6ImdldHRva2VuIiwic2lkIjoiZGI2YzUxNjItNzZhYS00MmVmLWI0ZDMtYThmOTk2N2NjZWM2IiwiYWNyIjoiMSIsImFsbG93ZWQtb3JpZ2lucyI6WyIqIl0sInJlYWxtX2FjY2VzcyI6eyJyb2xlcyI6WyJTVVBFUl9BRE1JTiIsIm9mZmxpbmVfYWNjZXNzIiwiU1RBRkYiLCJkZWZhdWx0LXJvbGVzLWhybXMiLCJ1bWFfYXV0aG9yaXphdGlvbiIsIkFETUlOIiwiVVNFUiJdfSwicmVzb3VyY2VfYWNjZXNzIjp7ImFjY291bnQiOnsicm9sZXMiOlsibWFuYWdlLWFjY291bnQiLCJtYW5hZ2UtYWNjb3VudC1saW5rcyIsInZpZXctcHJvZmlsZSJdfX0sInNjb3BlIjoiZW1haWwgb3BlbmlkIHByb2ZpbGUiLCJlbWFpbF92ZXJpZmllZCI6ZmFsc2UsInJvbGUiOlsiU1VQRVJfQURNSU4iLCJvZmZsaW5lX2FjY2VzcyIsIlNUQUZGIiwiZGVmYXVsdC1yb2xlcy1ocm1zIiwidW1hX2F1dGhvcml6YXRpb24iLCJBRE1JTiIsIlVTRVIiXSwibmFtZSI6IuC4p-C4seC4meC5gOC4ieC4peC4tOC4oSDguInguLHguJXguKPguJfguK3guIciLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiIzMTIwMjAwNDI0OTc1IiwiZ2l2ZW5fbmFtZSI6IuC4p-C4seC4meC5gOC4ieC4peC4tOC4oSIsImZhbWlseV9uYW1lIjoi4LiJ4Lix4LiV4Lij4LiX4Lit4LiHIn0.fHdMzpHMD4JcbzYnUrfM473FSXka2Z4lz_S3HI2c-dPXfO5ATpijqsi12C6-ExE0RJRXUK671erMuyVXL6u2qj-FvdliBL3ubKy4J3jIT3svkcZxZL2ib16dRg375dITefvqd-J4vw6MR4bq8YAGPbqRIy6BQ2pdEiZgNiwUUihHAFwZlVER1lNbaqlbL6vk_L4k-g25DBVnDr756BFvrw7zEDbawkKZ31EZF5_DYk4RWej0wvWrGHQWLw-RyzYVSBB_AooqHkncHn_CwLBGC5juOEfFO4a2ThuKwoxYCstjtBj-zmjpHFs-Hh3CBTWJCGFcKst1Ey28StlKtNkLiw", + }; + + // ส่ง GET request + let response = http.get( + //"https://bma-hrms.bangkok.go.th/api/v1/leave/fake-check-in", + //"https://hrmsbkk.case-collection.com/api/v1/org/dotnet/keycloak/439fa36d-6bc5-4ef5-95aa-ef29e64d2e9f", + "https://hrms.bangkok.go.th/api/v1/org/dotnet/keycloak/bafc5795-befc-4d3f-a461-3533c38a5fc1", + { headers: headers } + ); + + // ตรวจสอบการตอบสนอง + check(response, { + "is status 200": (r) => r.status === 200, + }); + + // หน่วงเวลา 1 วินาที + sleep(1); +} From 93a83b34e6e0bab690af4270b06cd2a2b9f88c87 Mon Sep 17 00:00:00 2001 From: harid Date: Mon, 19 Jan 2026 17:15:17 +0700 Subject: [PATCH 045/178] =?UTF-8?q?=E0=B8=9B=E0=B8=A3=E0=B8=B1=E0=B8=9A?= =?UTF-8?q?=E0=B9=81=E0=B8=9A=E0=B8=9A=E0=B9=83=E0=B8=9A=E0=B8=82=E0=B8=AD?= =?UTF-8?q?=E0=B8=A2=E0=B8=81=E0=B9=80=E0=B8=A5=E0=B8=B4=E0=B8=81=E0=B8=A7?= =?UTF-8?q?=E0=B8=B1=E0=B8=99=E0=B8=A5=E0=B8=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Controllers/LeaveReportController.cs | 25 ++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/BMA.EHR.Leave/Controllers/LeaveReportController.cs b/BMA.EHR.Leave/Controllers/LeaveReportController.cs index 0dec80c6..5dad15fa 100644 --- a/BMA.EHR.Leave/Controllers/LeaveReportController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveReportController.cs @@ -1136,9 +1136,28 @@ namespace BMA.EHR.Leave.Service.Controllers dateSendLeave = data.CreatedAt.Date.ToThaiShortDate().ToThaiNumber(), leaveTypeName = data.Type.Name, fullname = fullName, - positionName = profile!.Position == null ? "-" : profile!.Position.ToThaiNumber(), + position = string.IsNullOrEmpty(profile.Position) ? "-" : profile.Position, + positionName = (!string.IsNullOrEmpty(profile.PositionLeaveName) && + (profile.PositionLeaveName.Contains("อำนวยการ") || profile.PositionLeaveName.Contains("บริหาร")) + ? string.IsNullOrEmpty(profile.PosExecutiveName) + ? string.IsNullOrEmpty(profile.Position) + ? "-" + : profile.Position + : profile.PosExecutiveName + : string.IsNullOrEmpty(profile.Position) + ? "-" + : string.IsNullOrEmpty(profile.PositionLeaveName) + ? profile.Position + : $"{profile.Position}{profile.PositionLeaveName}").ToThaiNumber(), positionLeaveName = profile!.PositionLeaveName == null ? "-" : profile!.PositionLeaveName.ToThaiNumber(), - organizationName = profile!.Oc ?? "", + posExecutiveName = profile.PosExecutiveName, + organizationName = profile!.Oc!.ToThaiNumber(), + posExOrg = !string.IsNullOrEmpty(profile.PositionLeaveName) && + (profile.PositionLeaveName.Contains("อำนวยการ") || profile.PositionLeaveName.Contains("บริหาร")) + ? new[] { (profile!.Oc!.ToThaiNumber()) } + : !string.IsNullOrEmpty(profile.PosExecutiveName) + ? new[] { (profile.PosExecutiveName.ToThaiNumber()), (profile!.Oc!.ToThaiNumber()) } + : new[] { (profile!.Oc!.ToThaiNumber()) }, leaveDateStart = data.LeaveStartDate.Date.ToThaiShortDate().ToThaiNumber(), leaveDateEnd = data.LeaveEndDate.Date.ToThaiShortDate().ToThaiNumber(), dear = data.CommanderPosition == null ? data.Dear : data.CommanderPosition.ToThaiNumber(), @@ -2834,7 +2853,7 @@ namespace BMA.EHR.Leave.Service.Controllers ? x.LastUpdatedAt.Value.Date.ToThaiShortDate().ToThaiNumber() : "............/............/............", comment = !string.IsNullOrEmpty(x.Comment) - ? x.Comment.Replace("\r", "").Replace("\n", " ").Trim() + ? (x.Comment.Replace("\r", "").Replace("\n", " ").Trim()).ToThaiNumber() : "......................................................................................................................................................................", approveType = (x.ApproveType ?? "").Trim().ToUpper() }) From 1aab307f6a2545ecd21671cb6440b52a3d618ecd Mon Sep 17 00:00:00 2001 From: harid Date: Mon, 19 Jan 2026 18:17:11 +0700 Subject: [PATCH 046/178] =?UTF-8?q?fix=20=E0=B8=A3=E0=B8=B2=E0=B8=A2?= =?UTF-8?q?=E0=B8=81=E0=B8=B2=E0=B8=A3=E0=B9=83=E0=B8=AB=E0=B9=89=E0=B8=AD?= =?UTF-8?q?=E0=B8=AD=E0=B8=81=E0=B8=A5=E0=B8=B9=E0=B8=81=E0=B8=88=E0=B9=89?= =?UTF-8?q?=E0=B8=B2=E0=B8=87=20=E0=B8=A3=E0=B8=B0=E0=B8=9A=E0=B8=9A?= =?UTF-8?q?=E0=B9=83=E0=B8=8A=E0=B9=89=E0=B8=AA=E0=B8=B4=E0=B8=97=E0=B8=98?= =?UTF-8?q?=E0=B8=B4=E0=B9=8C=20API=20=E0=B9=80=E0=B8=AA=E0=B9=89=E0=B8=99?= =?UTF-8?q?=E0=B9=80=E0=B8=94=E0=B8=B5=E0=B8=A2=E0=B8=A7=E0=B8=81=E0=B8=B1?= =?UTF-8?q?=E0=B8=9A=E0=B8=82=E0=B8=AD=E0=B8=87=E0=B8=82=E0=B8=A3=E0=B8=81?= =?UTF-8?q?.=20#2173?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Controllers/RetirementOutController.cs | 44 ++++++++++++------- 1 file changed, 28 insertions(+), 16 deletions(-) diff --git a/BMA.EHR.Retirement.Service/Controllers/RetirementOutController.cs b/BMA.EHR.Retirement.Service/Controllers/RetirementOutController.cs index e996abab..be31a5c9 100644 --- a/BMA.EHR.Retirement.Service/Controllers/RetirementOutController.cs +++ b/BMA.EHR.Retirement.Service/Controllers/RetirementOutController.cs @@ -239,16 +239,6 @@ namespace BMA.EHR.Retirement.Service.Controllers [HttpGet("{id:length(36)}")] public async Task> GetDetailAdmin(Guid id) { - var getWorkflow = await _permission.GetPermissionAPIWorkflowAsync(id.ToString(), "SYS_DISMISS"); - if (getWorkflow == false) - { - var getPermission = await _permission.GetPermissionAPIAsync("GET", "SYS_DISMISS"); - var jsonData = JsonConvert.DeserializeObject(getPermission); - if (jsonData["status"]?.ToString() != "200") - { - return Error(jsonData["message"]?.ToString(), StatusCodes.Status403Forbidden); - } - } var data = await _context.RetirementOuts.AsQueryable() .Where(x => x.Id == id) .Select(p => new @@ -294,6 +284,20 @@ namespace BMA.EHR.Retirement.Service.Controllers p.OrganizationOld, }) .FirstOrDefaultAsync(); + + string _system = data != null && data.profileType?.Trim().ToUpper() == "OFFICER" ? "SYS_DISMISS" : "SYS_DISMISS_EMP"; + + var getWorkflow = await _permission.GetPermissionAPIWorkflowAsync(id.ToString(), _system); + if (getWorkflow == false) + { + var getPermission = await _permission.GetPermissionAPIAsync("GET", "SYS_DISMISS"); + var jsonData = JsonConvert.DeserializeObject(getPermission); + if (jsonData["status"]?.ToString() != "200") + { + return Error(jsonData["message"]?.ToString(), StatusCodes.Status403Forbidden); + } + } + if (data == null) return Error(GlobalMessages.DataNotFound, 404); @@ -462,14 +466,18 @@ namespace BMA.EHR.Retirement.Service.Controllers [HttpPut("{id:length(36)}")] public async Task> Put([FromBody] RetirementOutEditRequest req, Guid id) { - var getPermission = await _permission.GetPermissionAPIAsync("UPDATE", "SYS_DISMISS"); + var uppdated = await _context.RetirementOuts + .FirstOrDefaultAsync(x => x.Id == id); + + string _system = uppdated != null && uppdated.profileType?.Trim().ToUpper() == "OFFICER" ? "SYS_DISMISS" : "SYS_DISMISS_EMP"; + + var getPermission = await _permission.GetPermissionAPIAsync("UPDATE", _system); var jsonData = JsonConvert.DeserializeObject(getPermission); if (jsonData["status"]?.ToString() != "200") { return Error(jsonData["message"]?.ToString(), StatusCodes.Status403Forbidden); } - var uppdated = await _context.RetirementOuts - .FirstOrDefaultAsync(x => x.Id == id); + if (uppdated == null) return Error(GlobalMessages.RetirementOutNotFound, 404); @@ -527,14 +535,18 @@ namespace BMA.EHR.Retirement.Service.Controllers [HttpDelete("{id:length(36)}")] public async Task> Delete(Guid id) { - var getPermission = await _permission.GetPermissionAPIAsync("DELETE", "SYS_DISMISS"); + var deleted = await _context.RetirementOuts.AsQueryable() + .FirstOrDefaultAsync(x => x.Id == id); + + string _system = deleted != null && deleted.profileType?.Trim().ToUpper() == "OFFICER" ? "SYS_DISMISS" : "SYS_DISMISS_EMP"; + + var getPermission = await _permission.GetPermissionAPIAsync("DELETE", _system); var jsonData = JsonConvert.DeserializeObject(getPermission); if (jsonData["status"]?.ToString() != "200") { return Error(jsonData["message"]?.ToString(), StatusCodes.Status403Forbidden); } - var deleted = await _context.RetirementOuts.AsQueryable() - .FirstOrDefaultAsync(x => x.Id == id); + if (deleted == null) return NotFound(); _context.RetirementOuts.Remove(deleted); From 0ab75b2a1935454d130213d25373988d051f47d4 Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Mon, 19 Jan 2026 22:42:39 +0700 Subject: [PATCH 047/178] Add delay to start message consumption until 8:10 AM and implement time calculation --- BMA.EHR.CheckInConsumer/Program.cs | 27 +++++++++++++++++++++++++++ dotnet_keycloak_test.js | 2 +- 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/BMA.EHR.CheckInConsumer/Program.cs b/BMA.EHR.CheckInConsumer/Program.cs index 1a5a68d1..40a9db26 100644 --- a/BMA.EHR.CheckInConsumer/Program.cs +++ b/BMA.EHR.CheckInConsumer/Program.cs @@ -13,6 +13,9 @@ var configuration = new ConfigurationBuilder() WriteToConsole("Consumer Start!"); +// Wait until 8:00 AM before starting to consume messages +await WaitUntil8AM(); + var host = configuration["Rabbit:Host"] ?? ""; var user = configuration["Rabbit:User"] ?? ""; var pass = configuration["Rabbit:Password"] ?? ""; @@ -95,6 +98,30 @@ async Task CallRestApi(string requestData) } } +async Task WaitUntil8AM() +{ + // Get current time in Bangkok timezone + var bangkokTimeZone = TimeZoneInfo.FindSystemTimeZoneById("SE Asia Standard Time"); + var currentTime = TimeZoneInfo.ConvertTimeFromUtc(DateTime.UtcNow, bangkokTimeZone); + + var targetTime = new DateTime(currentTime.Year, currentTime.Month, currentTime.Day, 8, 10, 0); + + // If current time is already past 8:10 AM today, start immediately + if (currentTime >= targetTime) + { + WriteToConsole($"Current time is {currentTime:HH:mm:ss}. Starting consumer immediately."); + return; + } + + // Calculate time to wait + var timeToWait = targetTime - currentTime; + WriteToConsole($"Current time is {currentTime:HH:mm:ss}. Waiting until 08:10:00 to start consuming messages."); + WriteToConsole($"Time to wait: {timeToWait.Hours} hours, {timeToWait.Minutes} minutes, {timeToWait.Seconds} seconds"); + + await Task.Delay(timeToWait); + WriteToConsole("It's now 08:10:00. Starting to consume messages from queue."); +} + public class ResponseObject { diff --git a/dotnet_keycloak_test.js b/dotnet_keycloak_test.js index d271087d..1e623210 100644 --- a/dotnet_keycloak_test.js +++ b/dotnet_keycloak_test.js @@ -41,7 +41,7 @@ export default function () { //"https://bma-hrms.bangkok.go.th/api/v1/leave/fake-check-in", //"https://hrmsbkk.case-collection.com/api/v1/org/dotnet/keycloak/439fa36d-6bc5-4ef5-95aa-ef29e64d2e9f", "https://hrms.bangkok.go.th/api/v1/org/dotnet/keycloak/bafc5795-befc-4d3f-a461-3533c38a5fc1", - { headers: headers } + { headers: headers }, ); // ตรวจสอบการตอบสนอง From 3532df32fdf882971f321f2fb143ac41147a8f75 Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Tue, 20 Jan 2026 05:38:42 +0700 Subject: [PATCH 048/178] Refactor message consumption to start after 8:10 AM and implement time checks for operating hours --- BMA.EHR.CheckInConsumer/Program.cs | 100 +++++++++++++++-------------- 1 file changed, 51 insertions(+), 49 deletions(-) diff --git a/BMA.EHR.CheckInConsumer/Program.cs b/BMA.EHR.CheckInConsumer/Program.cs index 40a9db26..e09c61c6 100644 --- a/BMA.EHR.CheckInConsumer/Program.cs +++ b/BMA.EHR.CheckInConsumer/Program.cs @@ -13,9 +13,6 @@ var configuration = new ConfigurationBuilder() WriteToConsole("Consumer Start!"); -// Wait until 8:00 AM before starting to consume messages -await WaitUntil8AM(); - var host = configuration["Rabbit:Host"] ?? ""; var user = configuration["Rabbit:User"] ?? ""; var pass = configuration["Rabbit:Password"] ?? ""; @@ -24,52 +21,63 @@ var queue = configuration["Rabbit:Queue"] ?? "basic-queue"; // create connection var factory = new ConnectionFactory() { - //Uri = new Uri("amqp://admin:P@ssw0rd@192.168.4.11:5672") - HostName = host,// หรือ hostname ของ RabbitMQ Server ที่คุณใช้ - UserName = user, // ใส่ชื่อผู้ใช้ของคุณ - Password = pass // ใส่รหัสผ่านของคุณ + HostName = host, + UserName = user, + Password = pass }; using var connection = factory.CreateConnection(); using var channel = connection.CreateModel(); -//channel.QueueDeclare(queue: "bma-checkin-queue", durable: true, exclusive: false, autoDelete: false, arguments: null); channel.QueueDeclare(queue: queue, durable: true, exclusive: false, autoDelete: false, arguments: null); var consumer = new EventingBasicConsumer(channel); +string? consumerTag = null; +bool isConsuming = false; consumer.Received += async (model, ea) => { var body = ea.Body.ToArray(); var message = Encoding.UTF8.GetString(body); + + // Double-check time before processing (safety check) + if (!IsWithinOperatingHours()) + { + WriteToConsole($"Message received outside operating hours. Requeuing message."); + channel.BasicNack(ea.DeliveryTag, false, true); // Requeue the message + return; + } + await CallRestApi(message); - - // convert string into object - //var request = JsonConvert.DeserializeObject(message); - //using (var db = new ApplicationDbContext()) - //{ - // var item = new AttendantItem - // { - // Name = request.Name, - // CheckInDateTime = request.CheckInDateTime, - // }; - // db.AttendantItems.Add(item); - // db.SaveChanges(); - - // WriteToConsole($"ได้รับคำขอจาก Queue: {message}"); - // WriteToConsole($"ตอบกลับจาก REST API: {JsonConvert.SerializeObject(item)}"); - //} - WriteToConsole($"ได้รับคำขอจาก Queue: {message}"); - //WriteToConsole($"ตอบกลับจาก REST API: {JsonConvert.SerializeObject(item)}"); }; -//channel.BasicConsume(queue: "bma-checkin-queue", autoAck: true, consumer: consumer); -channel.BasicConsume(queue: queue, autoAck: true, consumer: consumer); +// Monitor and control consumer based on time schedule +using var timer = new PeriodicTimer(TimeSpan.FromMinutes(1)); -//Console.WriteLine("\nPress 'Enter' to exit the process..."); - -await Task.Delay(-1); +while (await timer.WaitForNextTickAsync()) +{ + var shouldBeConsuming = IsWithinOperatingHours(); + + if (shouldBeConsuming && !isConsuming) + { + // Start consuming + consumerTag = channel.BasicConsume(queue: queue, autoAck: true, consumer: consumer); + isConsuming = true; + WriteToConsole($"✅ Started consuming messages at {GetCurrentBangkokTime():yyyy-MM-dd HH:mm:ss}"); + } + else if (!shouldBeConsuming && isConsuming) + { + // Stop consuming + if (consumerTag != null) + { + channel.BasicCancel(consumerTag); + consumerTag = null; + } + isConsuming = false; + WriteToConsole($"⏸️ Stopped consuming messages at {GetCurrentBangkokTime():yyyy-MM-dd HH:mm:ss}. Will resume after 08:10 tomorrow."); + } +} static void WriteToConsole(string message) { @@ -98,28 +106,22 @@ async Task CallRestApi(string requestData) } } -async Task WaitUntil8AM() +DateTime GetCurrentBangkokTime() { - // Get current time in Bangkok timezone var bangkokTimeZone = TimeZoneInfo.FindSystemTimeZoneById("SE Asia Standard Time"); - var currentTime = TimeZoneInfo.ConvertTimeFromUtc(DateTime.UtcNow, bangkokTimeZone); + return TimeZoneInfo.ConvertTimeFromUtc(DateTime.UtcNow, bangkokTimeZone); +} + +bool IsWithinOperatingHours() +{ + var currentTime = GetCurrentBangkokTime(); + var startTime = new TimeSpan(8, 10, 0); // 8:10 AM + var endTime = new TimeSpan(23, 59, 59); // End of day - var targetTime = new DateTime(currentTime.Year, currentTime.Month, currentTime.Day, 8, 10, 0); + var currentTimeOfDay = currentTime.TimeOfDay; - // If current time is already past 8:10 AM today, start immediately - if (currentTime >= targetTime) - { - WriteToConsole($"Current time is {currentTime:HH:mm:ss}. Starting consumer immediately."); - return; - } - - // Calculate time to wait - var timeToWait = targetTime - currentTime; - WriteToConsole($"Current time is {currentTime:HH:mm:ss}. Waiting until 08:10:00 to start consuming messages."); - WriteToConsole($"Time to wait: {timeToWait.Hours} hours, {timeToWait.Minutes} minutes, {timeToWait.Seconds} seconds"); - - await Task.Delay(timeToWait); - WriteToConsole("It's now 08:10:00. Starting to consume messages from queue."); + // Consumer should only work from 8:10 AM to end of day + return currentTimeOfDay >= startTime && currentTimeOfDay <= endTime; } From 9442d3b29f25519137b17b52f56aa92758e1db6a Mon Sep 17 00:00:00 2001 From: harid Date: Tue, 20 Jan 2026 09:32:10 +0700 Subject: [PATCH 049/178] fix #2173 --- .../Controllers/RetirementOutController.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BMA.EHR.Retirement.Service/Controllers/RetirementOutController.cs b/BMA.EHR.Retirement.Service/Controllers/RetirementOutController.cs index be31a5c9..72a7bc91 100644 --- a/BMA.EHR.Retirement.Service/Controllers/RetirementOutController.cs +++ b/BMA.EHR.Retirement.Service/Controllers/RetirementOutController.cs @@ -290,7 +290,7 @@ namespace BMA.EHR.Retirement.Service.Controllers var getWorkflow = await _permission.GetPermissionAPIWorkflowAsync(id.ToString(), _system); if (getWorkflow == false) { - var getPermission = await _permission.GetPermissionAPIAsync("GET", "SYS_DISMISS"); + var getPermission = await _permission.GetPermissionAPIAsync("GET", _system); var jsonData = JsonConvert.DeserializeObject(getPermission); if (jsonData["status"]?.ToString() != "200") { From 60602d99c476ac877fe3e7fdfbeeadecb3a0bd7e Mon Sep 17 00:00:00 2001 From: harid Date: Tue, 20 Jan 2026 09:54:15 +0700 Subject: [PATCH 050/178] =?UTF-8?q?api=20=E0=B8=82=E0=B8=AD=E0=B9=82?= =?UTF-8?q?=E0=B8=AD=E0=B8=99=20(admin)=20=E0=B9=80=E0=B8=9E=E0=B8=B4?= =?UTF-8?q?=E0=B9=88=E0=B8=A1=E0=B8=9A=E0=B8=B1=E0=B8=99=E0=B8=97=E0=B8=B6?= =?UTF-8?q?=E0=B8=81=E0=B8=9F=E0=B8=B4=E0=B8=A5=E0=B8=A5=E0=B9=8C=20"?= =?UTF-8?q?=E0=B8=82=E0=B8=AD=E0=B9=82=E0=B8=AD=E0=B8=99=E0=B8=95=E0=B8=B1?= =?UTF-8?q?=E0=B9=89=E0=B8=87=E0=B9=81=E0=B8=95=E0=B9=88=E0=B8=A7=E0=B8=B1?= =?UTF-8?q?=E0=B8=99=E0=B8=97=E0=B8=B5=E0=B9=88"=20#2196?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Controllers/PlacementTransferController.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/BMA.EHR.Placement.Service/Controllers/PlacementTransferController.cs b/BMA.EHR.Placement.Service/Controllers/PlacementTransferController.cs index e27b4ad3..73115246 100644 --- a/BMA.EHR.Placement.Service/Controllers/PlacementTransferController.cs +++ b/BMA.EHR.Placement.Service/Controllers/PlacementTransferController.cs @@ -699,6 +699,7 @@ namespace BMA.EHR.Placement.Service.Controllers Organization = req.Organization, Reason = req.Reason, Status = "APPROVE", + Date = req.Date, CreatedFullName = FullName ?? "System Administrator", CreatedUserId = UserId ?? "", CreatedAt = DateTime.Now, From a463df571695723a9ba32bf599fe6553dc0399c4 Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Tue, 20 Jan 2026 10:49:13 +0700 Subject: [PATCH 051/178] Add migration to create CheckInJobStatuses table for RMQ task control - Introduced a new migration that creates the CheckInJobStatuses table. - The table includes fields for tracking job statuses, timestamps, user information, and error messages. - Supports various statuses such as PENDING, PROCESSING, COMPLETED, and FAILED. --- .../ApplicationServicesRegistration.cs | 1 + .../CheckInJobStatusRepository.cs | 135 ++ BMA.EHR.CheckInConsumer/Program.cs | 86 +- BMA.EHR.CheckInConsumer/appsettings.json | 16 +- .../Leave/TimeAttendants/CheckInJobStatus.cs | 39 + ...120032158_Add RMQ Task Control.Designer.cs | 1705 +++++++++++++++++ .../20260120032158_Add RMQ Task Control.cs | 58 + .../LeaveDb/LeaveDbContextModelSnapshot.cs | 93 + .../Persistence/LeaveDbContext.cs | 2 + BMA.EHR.Leave/Controllers/LeaveController.cs | 247 ++- BMA.EHR.Leave/DTOs/CheckIn/CheckTimeDto.cs | 1 + BMA.EHR.Leave/appsettings.json | 2 +- 12 files changed, 2259 insertions(+), 126 deletions(-) create mode 100644 BMA.EHR.Application/Repositories/Leaves/TimeAttendants/CheckInJobStatusRepository.cs create mode 100644 BMA.EHR.Domain/Models/Leave/TimeAttendants/CheckInJobStatus.cs create mode 100644 BMA.EHR.Infrastructure/Migrations/LeaveDb/20260120032158_Add RMQ Task Control.Designer.cs create mode 100644 BMA.EHR.Infrastructure/Migrations/LeaveDb/20260120032158_Add RMQ Task Control.cs diff --git a/BMA.EHR.Application/ApplicationServicesRegistration.cs b/BMA.EHR.Application/ApplicationServicesRegistration.cs index 350b7a75..bf6dc6df 100644 --- a/BMA.EHR.Application/ApplicationServicesRegistration.cs +++ b/BMA.EHR.Application/ApplicationServicesRegistration.cs @@ -53,6 +53,7 @@ namespace BMA.EHR.Application services.AddTransient(); services.AddTransient(); services.AddTransient(); + services.AddTransient(); services.AddTransient(); services.AddTransient(); diff --git a/BMA.EHR.Application/Repositories/Leaves/TimeAttendants/CheckInJobStatusRepository.cs b/BMA.EHR.Application/Repositories/Leaves/TimeAttendants/CheckInJobStatusRepository.cs new file mode 100644 index 00000000..302bdd12 --- /dev/null +++ b/BMA.EHR.Application/Repositories/Leaves/TimeAttendants/CheckInJobStatusRepository.cs @@ -0,0 +1,135 @@ +using BMA.EHR.Application.Common.Interfaces; +using BMA.EHR.Domain.Models.Leave.TimeAttendants; +using Microsoft.AspNetCore.Http; +using Microsoft.EntityFrameworkCore; + +namespace BMA.EHR.Application.Repositories.Leaves.TimeAttendants +{ + public class CheckInJobStatusRepository : GenericLeaveRepository + { + #region " Fields " + + private readonly ILeaveDbContext _dbContext; + + #endregion + + #region " Constructor and Destructor " + + public CheckInJobStatusRepository(ILeaveDbContext dbContext, + IHttpContextAccessor httpContextAccessor) : base(dbContext, httpContextAccessor) + { + _dbContext = dbContext; + } + + #endregion + + #region " Methods " + + /// + /// ดึงข้อมูล Job Status จาก TaskId + /// + public async Task GetByTaskIdAsync(Guid taskId) + { + var data = await _dbContext.Set() + .Where(x => x.TaskId == taskId) + .FirstOrDefaultAsync(); + + return data; + } + + /// + /// ดึงข้อมูล Job Status จาก UserId และสถานะ + /// + public async Task> GetByUserIdAndStatusAsync(Guid userId, string status) + { + var data = await _dbContext.Set() + .Where(x => x.KeycloakUserId == userId && x.Status == status) + .OrderByDescending(x => x.CreatedDate) + .ToListAsync(); + + return data; + } + + /// + /// ดึงข้อมูล Job Status ที่ยัง pending หรือ processing + /// + public async Task> GetPendingOrProcessingJobsAsync(Guid userId) + { + var data = await _dbContext.Set() + .Where(x => x.KeycloakUserId == userId && + (x.Status == "PENDING" || x.Status == "PROCESSING")) + //.OrderByDescending(x => x.CreatedDate) + .ToListAsync(); + + return data; + } + + /// + /// อัปเดตสถานะเป็น Processing + /// + public async Task UpdateToProcessingAsync(Guid taskId) + { + var job = await GetByTaskIdAsync(taskId); + if (job != null) + { + job.Status = "PROCESSING"; + job.ProcessingDate = DateTime.Now; + await UpdateAsync(job); + } + return job!; + } + + /// + /// อัปเดตสถานะเป็น Completed + /// + public async Task UpdateToCompletedAsync(Guid taskId, string? additionalData = null) + { + var job = await GetByTaskIdAsync(taskId); + if (job != null) + { + job.Status = "COMPLETED"; + job.CompletedDate = DateTime.Now; + if (!string.IsNullOrEmpty(additionalData)) + { + job.AdditionalData = additionalData; + } + await UpdateAsync(job); + } + return job!; + } + + /// + /// อัปเดตสถานะเป็น Failed + /// + public async Task UpdateToFailedAsync(Guid taskId, string errorMessage) + { + var job = await GetByTaskIdAsync(taskId); + if (job != null) + { + job.Status = "FAILED"; + job.CompletedDate = DateTime.Now; + job.ErrorMessage = errorMessage; + await UpdateAsync(job); + } + return job!; + } + + /// + /// ล้างข้อมูล Job Status ที่เก่าเกิน X วัน + /// + public async Task CleanupOldJobsAsync(int daysOld = 30) + { + var cutoffDate = DateTime.Now.AddDays(-daysOld); + var oldJobs = await _dbContext.Set() + .Where(x => x.CreatedDate < cutoffDate) + .ToListAsync(); + + _dbContext.Set().RemoveRange(oldJobs); + await _dbContext.SaveChangesAsync(); + + return oldJobs.Count; + } + + #endregion + } +} diff --git a/BMA.EHR.CheckInConsumer/Program.cs b/BMA.EHR.CheckInConsumer/Program.cs index e09c61c6..0ae439cf 100644 --- a/BMA.EHR.CheckInConsumer/Program.cs +++ b/BMA.EHR.CheckInConsumer/Program.cs @@ -21,63 +21,52 @@ var queue = configuration["Rabbit:Queue"] ?? "basic-queue"; // create connection var factory = new ConnectionFactory() { - HostName = host, - UserName = user, - Password = pass + //Uri = new Uri("amqp://admin:P@ssw0rd@192.168.4.11:5672") + HostName = host,// หรือ hostname ของ RabbitMQ Server ที่คุณใช้ + UserName = user, // ใส่ชื่อผู้ใช้ของคุณ + Password = pass // ใส่รหัสผ่านของคุณ }; using var connection = factory.CreateConnection(); using var channel = connection.CreateModel(); +//channel.QueueDeclare(queue: "bma-checkin-queue", durable: true, exclusive: false, autoDelete: false, arguments: null); channel.QueueDeclare(queue: queue, durable: true, exclusive: false, autoDelete: false, arguments: null); var consumer = new EventingBasicConsumer(channel); -string? consumerTag = null; -bool isConsuming = false; consumer.Received += async (model, ea) => { var body = ea.Body.ToArray(); var message = Encoding.UTF8.GetString(body); - - // Double-check time before processing (safety check) - if (!IsWithinOperatingHours()) - { - WriteToConsole($"Message received outside operating hours. Requeuing message."); - channel.BasicNack(ea.DeliveryTag, false, true); // Requeue the message - return; - } - await CallRestApi(message); + + // convert string into object + //var request = JsonConvert.DeserializeObject(message); + //using (var db = new ApplicationDbContext()) + //{ + // var item = new AttendantItem + // { + // Name = request.Name, + // CheckInDateTime = request.CheckInDateTime, + // }; + // db.AttendantItems.Add(item); + // db.SaveChanges(); + + // WriteToConsole($"ได้รับคำขอจาก Queue: {message}"); + // WriteToConsole($"ตอบกลับจาก REST API: {JsonConvert.SerializeObject(item)}"); + //} + WriteToConsole($"ได้รับคำขอจาก Queue: {message}"); + //WriteToConsole($"ตอบกลับจาก REST API: {JsonConvert.SerializeObject(item)}"); }; -// Monitor and control consumer based on time schedule -using var timer = new PeriodicTimer(TimeSpan.FromMinutes(1)); +//channel.BasicConsume(queue: "bma-checkin-queue", autoAck: true, consumer: consumer); +channel.BasicConsume(queue: queue, autoAck: true, consumer: consumer); -while (await timer.WaitForNextTickAsync()) -{ - var shouldBeConsuming = IsWithinOperatingHours(); - - if (shouldBeConsuming && !isConsuming) - { - // Start consuming - consumerTag = channel.BasicConsume(queue: queue, autoAck: true, consumer: consumer); - isConsuming = true; - WriteToConsole($"✅ Started consuming messages at {GetCurrentBangkokTime():yyyy-MM-dd HH:mm:ss}"); - } - else if (!shouldBeConsuming && isConsuming) - { - // Stop consuming - if (consumerTag != null) - { - channel.BasicCancel(consumerTag); - consumerTag = null; - } - isConsuming = false; - WriteToConsole($"⏸️ Stopped consuming messages at {GetCurrentBangkokTime():yyyy-MM-dd HH:mm:ss}. Will resume after 08:10 tomorrow."); - } -} +//Console.WriteLine("\nPress 'Enter' to exit the process..."); + +await Task.Delay(-1); static void WriteToConsole(string message) { @@ -106,25 +95,6 @@ async Task CallRestApi(string requestData) } } -DateTime GetCurrentBangkokTime() -{ - var bangkokTimeZone = TimeZoneInfo.FindSystemTimeZoneById("SE Asia Standard Time"); - return TimeZoneInfo.ConvertTimeFromUtc(DateTime.UtcNow, bangkokTimeZone); -} - -bool IsWithinOperatingHours() -{ - var currentTime = GetCurrentBangkokTime(); - var startTime = new TimeSpan(8, 10, 0); // 8:10 AM - var endTime = new TimeSpan(23, 59, 59); // End of day - - var currentTimeOfDay = currentTime.TimeOfDay; - - // Consumer should only work from 8:10 AM to end of day - return currentTimeOfDay >= startTime && currentTimeOfDay <= endTime; -} - - public class ResponseObject { [JsonPropertyName("status")] diff --git a/BMA.EHR.CheckInConsumer/appsettings.json b/BMA.EHR.CheckInConsumer/appsettings.json index b180f90c..76f86c86 100644 --- a/BMA.EHR.CheckInConsumer/appsettings.json +++ b/BMA.EHR.CheckInConsumer/appsettings.json @@ -1,9 +1,9 @@ { - "Rabbit": { - "Host": "192.168.1.40", - "User": "admin", - "Password": "Test123456", - "Queue": "bma-checkin-queue" - }, - "API": "https://localhost:7283/api/v1" -} \ No newline at end of file + "Rabbit": { + "Host": "192.168.1.63", + "User": "admin", + "Password": "12345678", + "Queue": "hrms-checkin-queue-dev" + }, + "API": "https://localhost:7283/api/v1" +} diff --git a/BMA.EHR.Domain/Models/Leave/TimeAttendants/CheckInJobStatus.cs b/BMA.EHR.Domain/Models/Leave/TimeAttendants/CheckInJobStatus.cs new file mode 100644 index 00000000..922f966b --- /dev/null +++ b/BMA.EHR.Domain/Models/Leave/TimeAttendants/CheckInJobStatus.cs @@ -0,0 +1,39 @@ +using BMA.EHR.Domain.Models.Base; +using Microsoft.EntityFrameworkCore; +using System.ComponentModel.DataAnnotations; + +namespace BMA.EHR.Domain.Models.Leave.TimeAttendants +{ + public class CheckInJobStatus : EntityBase + { + [Required, Comment("Task ID สำหรับติดตามสถานะงาน")] + public Guid TaskId { get; set; } = Guid.Empty; + + [Required, Comment("รหัส User ของ Keycloak")] + public Guid KeycloakUserId { get; set; } = Guid.Empty; + + [Comment("วันเวลาที่สร้างงาน")] + public DateTime CreatedDate { get; set; } = DateTime.Now; + + [Comment("วันเวลาที่เริ่มประมวลผล")] + public DateTime? ProcessingDate { get; set; } + + [Comment("วันเวลาที่เสร็จสิ้นการประมวลผล")] + public DateTime? CompletedDate { get; set; } + + [Required, Comment("สถานะงาน: PENDING, PROCESSING, COMPLETED, FAILED")] + public string Status { get; set; } = "PENDING"; + + [Comment("ประเภทการลงเวลา: CHECK_IN, CHECK_OUT")] + public string? CheckType { get; set; } + + [Comment("CheckInId สำหรับ Check-Out")] + public Guid? CheckInId { get; set; } + + [Comment("ข้อความแสดงข้อผิดพลาด")] + public string? ErrorMessage { get; set; } + + [Comment("ข้อมูลเพิ่มเติม (JSON)")] + public string? AdditionalData { get; set; } + } +} diff --git a/BMA.EHR.Infrastructure/Migrations/LeaveDb/20260120032158_Add RMQ Task Control.Designer.cs b/BMA.EHR.Infrastructure/Migrations/LeaveDb/20260120032158_Add RMQ Task Control.Designer.cs new file mode 100644 index 00000000..a7f4e1fb --- /dev/null +++ b/BMA.EHR.Infrastructure/Migrations/LeaveDb/20260120032158_Add RMQ Task Control.Designer.cs @@ -0,0 +1,1705 @@ +// +using System; +using BMA.EHR.Infrastructure.Persistence; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace BMA.EHR.Infrastructure.Migrations.LeaveDb +{ + [DbContext(typeof(LeaveDbContext))] + [Migration("20260120032158_Add RMQ Task Control")] + partial class AddRMQTaskControl + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "7.0.9") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + modelBuilder.Entity("BMA.EHR.Domain.Models.Documents.Document", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedDate") + .HasColumnType("datetime(6)"); + + b.Property("Detail") + .IsRequired() + .HasColumnType("text"); + + b.Property("FileName") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("FileSize") + .HasColumnType("int"); + + b.Property("FileType") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("varchar(128)"); + + b.Property("ObjectRefId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.ToTable("Document"); + }); + + modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.Commons.LeaveType", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)") + .HasColumnOrder(0) + .HasComment("PrimaryKey") + .HasAnnotation("Relational:JsonPropertyName", "id"); + + b.Property("Code") + .IsRequired() + .HasColumnType("longtext") + .HasComment("รหัสประเภทการลา"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(100) + .HasComment("สร้างข้อมูลเมื่อ"); + + b.Property("CreatedFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(104) + .HasComment("ชื่อ User ที่สร้างข้อมูล"); + + b.Property("CreatedUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(101) + .HasComment("User Id ที่สร้างข้อมูล"); + + b.Property("LastUpdateFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(105) + .HasComment("ชื่อ User ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdateUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(103) + .HasComment("User Id ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(102) + .HasComment("แก้ไขข้อมูลล่าสุดเมื่อ"); + + b.Property("Limit") + .HasColumnType("int") + .HasComment("จำนวนวันลาสูงสุดประจำปี"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext") + .HasComment("ชื่อประเภทการลา"); + + b.HasKey("Id"); + + b.ToTable("LeaveTypes"); + }); + + modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.Requests.LeaveBeginning", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)") + .HasColumnOrder(0) + .HasComment("PrimaryKey") + .HasAnnotation("Relational:JsonPropertyName", "id"); + + b.Property("Child1DnaId") + .HasColumnType("char(36)"); + + b.Property("Child2DnaId") + .HasColumnType("char(36)"); + + b.Property("Child3DnaId") + .HasColumnType("char(36)"); + + b.Property("Child4DnaId") + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(100) + .HasComment("สร้างข้อมูลเมื่อ"); + + b.Property("CreatedFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(104) + .HasComment("ชื่อ User ที่สร้างข้อมูล"); + + b.Property("CreatedUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(101) + .HasComment("User Id ที่สร้างข้อมูล"); + + b.Property("FirstName") + .HasColumnType("longtext"); + + b.Property("LastName") + .HasColumnType("longtext"); + + b.Property("LastUpdateFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(105) + .HasComment("ชื่อ User ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdateUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(103) + .HasComment("User Id ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(102) + .HasComment("แก้ไขข้อมูลล่าสุดเมื่อ"); + + b.Property("LeaveDays") + .HasColumnType("double") + .HasComment("จำนวนวันลายกมา"); + + b.Property("LeaveDaysUsed") + .HasColumnType("double") + .HasComment("จำนวนวันลาที่ใช้ไป"); + + b.Property("LeaveTypeId") + .HasColumnType("char(36)") + .HasComment("รหัสประเภทการลา"); + + b.Property("LeaveYear") + .HasColumnType("int") + .HasComment("ปีงบประมาณ"); + + b.Property("Prefix") + .HasColumnType("longtext"); + + b.Property("ProfileId") + .HasColumnType("char(36)") + .HasComment("รหัส Profile ในระบบทะเบียนประวัติ"); + + b.Property("RootDnaId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("LeaveTypeId"); + + b.ToTable("LeaveBeginnings"); + }); + + modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.Requests.LeaveDocument", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)") + .HasColumnOrder(0) + .HasComment("PrimaryKey") + .HasAnnotation("Relational:JsonPropertyName", "id"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(100) + .HasComment("สร้างข้อมูลเมื่อ"); + + b.Property("CreatedFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(104) + .HasComment("ชื่อ User ที่สร้างข้อมูล"); + + b.Property("CreatedUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(101) + .HasComment("User Id ที่สร้างข้อมูล"); + + b.Property("DocumentId") + .HasColumnType("char(36)"); + + b.Property("LastUpdateFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(105) + .HasComment("ชื่อ User ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdateUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(103) + .HasComment("User Id ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(102) + .HasComment("แก้ไขข้อมูลล่าสุดเมื่อ"); + + b.Property("LeaveRequestId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("DocumentId"); + + b.HasIndex("LeaveRequestId"); + + b.ToTable("LeaveDocuments"); + }); + + modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.Requests.LeaveRequest", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)") + .HasColumnOrder(0) + .HasComment("PrimaryKey") + .HasAnnotation("Relational:JsonPropertyName", "id"); + + b.Property("AbsentDayAt") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("AbsentDayGetIn") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("AbsentDayLocation") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("AbsentDayRegistorDate") + .HasColumnType("datetime(6)"); + + b.Property("AbsentDaySummon") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Amount") + .HasColumnType("double"); + + b.Property("ApproveStep") + .HasColumnType("longtext") + .HasComment("step การอนุมัติ st1 = จทน.อนุมัตื,st2 = ผู้บังคับบัญชา อนุมัติ "); + + b.Property("BirthDate") + .HasColumnType("datetime(6)"); + + b.Property("CancelLeaveWrote") + .HasColumnType("longtext") + .HasComment("เขียนที่ (ขอยกเลิก)"); + + b.Property("Child1") + .HasColumnType("longtext"); + + b.Property("Child1DnaId") + .HasColumnType("char(36)"); + + b.Property("Child1Id") + .HasColumnType("char(36)"); + + b.Property("Child2") + .HasColumnType("longtext"); + + b.Property("Child2DnaId") + .HasColumnType("char(36)"); + + b.Property("Child2Id") + .HasColumnType("char(36)"); + + b.Property("Child3") + .HasColumnType("longtext"); + + b.Property("Child3DnaId") + .HasColumnType("char(36)"); + + b.Property("Child3Id") + .HasColumnType("char(36)"); + + b.Property("Child4") + .HasColumnType("longtext"); + + b.Property("Child4DnaId") + .HasColumnType("char(36)"); + + b.Property("Child4Id") + .HasColumnType("char(36)"); + + b.Property("CitizenId") + .HasColumnType("longtext"); + + b.Property("CommanderPosition") + .HasColumnType("longtext"); + + b.Property("CoupleDayCountryHistory") + .HasColumnType("longtext"); + + b.Property("CoupleDayEndDateHistory") + .HasColumnType("datetime(6)"); + + b.Property("CoupleDayLevel") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("CoupleDayLevelCountry") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("CoupleDayName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("CoupleDayPosition") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("CoupleDayStartDateHistory") + .HasColumnType("datetime(6)"); + + b.Property("CoupleDaySumTotalHistory") + .HasColumnType("longtext"); + + b.Property("CoupleDayTotalHistory") + .HasColumnType("longtext"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(100) + .HasComment("สร้างข้อมูลเมื่อ"); + + b.Property("CreatedFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(104) + .HasComment("ชื่อ User ที่สร้างข้อมูล"); + + b.Property("CreatedUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(101) + .HasComment("User Id ที่สร้างข้อมูล"); + + b.Property("DateAppoint") + .HasColumnType("datetime(6)"); + + b.Property("Dear") + .HasColumnType("longtext") + .HasComment("เรียนใคร"); + + b.Property("FirstName") + .HasColumnType("longtext"); + + b.Property("Gender") + .HasColumnType("longtext"); + + b.Property("HajjDayStatus") + .HasColumnType("tinyint(1)"); + + b.Property("KeycloakUserId") + .HasColumnType("char(36)"); + + b.Property("LastName") + .HasColumnType("longtext"); + + b.Property("LastUpdateFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(105) + .HasComment("ชื่อ User ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdateUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(103) + .HasComment("User Id ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(102) + .HasComment("แก้ไขข้อมูลล่าสุดเมื่อ"); + + b.Property("LeaveAddress") + .IsRequired() + .HasColumnType("longtext") + .HasComment("สถานที่ติดต่อขณะลา"); + + b.Property("LeaveBirthDate") + .HasColumnType("datetime(6)"); + + b.Property("LeaveCancelComment") + .HasColumnType("longtext") + .HasComment("เหตุผลในการขอยกเลิก"); + + b.Property("LeaveCancelDocumentId") + .HasColumnType("char(36)"); + + b.Property("LeaveCancelStatus") + .HasColumnType("longtext") + .HasComment("สถานะของคำขอยกเลิก"); + + b.Property("LeaveComment") + .HasColumnType("longtext") + .HasComment("ความเห็นของผู้บังคับบัญชา"); + + b.Property("LeaveDetail") + .IsRequired() + .HasColumnType("longtext") + .HasComment("รายละเอียดการลา"); + + b.Property("LeaveDirectorComment") + .HasColumnType("longtext") + .HasComment("ความเห็นของผู้อำนวยการสำนัก"); + + b.Property("LeaveDraftDocumentId") + .HasColumnType("char(36)"); + + b.Property("LeaveEndDate") + .HasColumnType("datetime(6)") + .HasComment("วัน เดือน ปีสิ้นสุดลา"); + + b.Property("LeaveGovernmentDate") + .HasColumnType("datetime(6)"); + + b.Property("LeaveLast") + .HasColumnType("datetime(6)"); + + b.Property("LeaveNumber") + .IsRequired() + .HasColumnType("longtext") + .HasComment("หมายเลขที่ติดต่อขณะลา"); + + b.Property("LeaveRange") + .HasColumnType("longtext") + .HasComment("ช่วงของการลาของวันเริ่ม เช่น ลาทั้งวัน ครึ่งวันเช้า ครึ่งวันบ่าย"); + + b.Property("LeaveRangeEnd") + .HasColumnType("longtext") + .HasComment("ช่วงของการลาของวันสิ้นสุด เช่น ลาทั้งวัน ครึ่งวันเช้า ครึ่งวันบ่าย"); + + b.Property("LeaveSalary") + .HasColumnType("int"); + + b.Property("LeaveSalaryText") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("LeaveStartDate") + .HasColumnType("datetime(6)") + .HasComment("วัน เดือน ปีเริ่มต้นลา"); + + b.Property("LeaveStatus") + .IsRequired() + .HasColumnType("longtext") + .HasComment("สถานะของคำร้อง"); + + b.Property("LeaveSubTypeName") + .HasColumnType("longtext"); + + b.Property("LeaveTotal") + .HasColumnType("double"); + + b.Property("LeaveTypeCode") + .HasColumnType("longtext") + .HasComment("code ของประเภทการลา"); + + b.Property("LeaveWrote") + .IsRequired() + .HasColumnType("longtext") + .HasComment("เขียนที่"); + + b.Property("OrdainDayBuddhistLentAddress") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("OrdainDayBuddhistLentName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("OrdainDayLocationAddress") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("OrdainDayLocationName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("OrdainDayLocationNumber") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("OrdainDayOrdination") + .HasColumnType("datetime(6)"); + + b.Property("OrdainDayStatus") + .HasColumnType("tinyint(1)"); + + b.Property("OrganizationName") + .HasColumnType("longtext") + .HasComment("สังกัดผู้ยื่นขอ"); + + b.Property("PositionLevelName") + .HasColumnType("longtext") + .HasComment("ระดับผู้ยื่นขอ"); + + b.Property("PositionName") + .HasColumnType("longtext") + .HasComment("ตำแหน่งผู้ยื่นขอ"); + + b.Property("Prefix") + .HasColumnType("longtext"); + + b.Property("ProfileId") + .HasColumnType("char(36)"); + + b.Property("ProfileType") + .HasColumnType("longtext"); + + b.Property("RestDayCurrentTotal") + .HasColumnType("double"); + + b.Property("RestDayOldTotal") + .HasColumnType("double"); + + b.Property("Root") + .HasColumnType("longtext"); + + b.Property("RootDnaId") + .HasColumnType("char(36)"); + + b.Property("RootId") + .HasColumnType("char(36)"); + + b.Property("StudyDayCountry") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("StudyDayDegreeLevel") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("StudyDayScholarship") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("StudyDaySubject") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("StudyDayTrainingName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("StudyDayTrainingSubject") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("StudyDayUniversityName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("TypeId") + .HasColumnType("char(36)"); + + b.Property("WifeDayDateBorn") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("WifeDayName") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("LeaveCancelDocumentId"); + + b.HasIndex("LeaveDraftDocumentId"); + + b.HasIndex("TypeId"); + + b.ToTable("LeaveRequests"); + }); + + modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.Requests.LeaveRequestApprover", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)") + .HasColumnOrder(0) + .HasComment("PrimaryKey") + .HasAnnotation("Relational:JsonPropertyName", "id"); + + b.Property("ApproveStatus") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("ApproveType") + .HasColumnType("longtext"); + + b.Property("Comment") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(100) + .HasComment("สร้างข้อมูลเมื่อ"); + + b.Property("CreatedFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(104) + .HasComment("ชื่อ User ที่สร้างข้อมูล"); + + b.Property("CreatedUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(101) + .HasComment("User Id ที่สร้างข้อมูล"); + + b.Property("FirstName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("KeycloakId") + .HasColumnType("char(36)"); + + b.Property("LastName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("LastUpdateFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(105) + .HasComment("ชื่อ User ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdateUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(103) + .HasComment("User Id ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(102) + .HasComment("แก้ไขข้อมูลล่าสุดเมื่อ"); + + b.Property("LeaveRequestId") + .HasColumnType("char(36)"); + + b.Property("OrganizationName") + .IsRequired() + .HasColumnType("longtext") + .HasComment("สังกัด"); + + b.Property("PosExecutiveName") + .IsRequired() + .HasColumnType("longtext") + .HasComment("ตำแหน่งทางการบริหาร"); + + b.Property("PositionLevelName") + .IsRequired() + .HasColumnType("longtext") + .HasComment("ประเภทระดับตำแหน่ง"); + + b.Property("PositionName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("PositionSign") + .HasColumnType("longtext") + .HasComment("ตำแหน่งใต้ลายเช็นต์"); + + b.Property("Prefix") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("ProfileId") + .HasColumnType("char(36)"); + + b.Property("Seq") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("LeaveRequestId"); + + b.ToTable("LeaveRequestApprovers"); + }); + + modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.TimeAttendants.AdditionalCheckRequest", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)") + .HasColumnOrder(0) + .HasComment("PrimaryKey") + .HasAnnotation("Relational:JsonPropertyName", "id"); + + b.Property("CheckDate") + .HasColumnType("datetime(6)") + .HasComment("*วันที่ลงเวลา"); + + b.Property("CheckInEdit") + .HasColumnType("tinyint(1)") + .HasComment("*ขอลงเวลาช่วงเช้า"); + + b.Property("CheckOutEdit") + .HasColumnType("tinyint(1)") + .HasComment("*ขอลงเวลาช่วงบ่าย"); + + b.Property("Child1DnaId") + .HasColumnType("char(36)"); + + b.Property("Child2DnaId") + .HasColumnType("char(36)"); + + b.Property("Child3DnaId") + .HasColumnType("char(36)"); + + b.Property("Child4DnaId") + .HasColumnType("char(36)"); + + b.Property("Comment") + .HasColumnType("longtext") + .HasComment("หมายเหตุในการการอนุมัติ/ไม่อนุมัติ"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(100) + .HasComment("สร้างข้อมูลเมื่อ"); + + b.Property("CreatedFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(104) + .HasComment("ชื่อ User ที่สร้างข้อมูล"); + + b.Property("CreatedUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(101) + .HasComment("User Id ที่สร้างข้อมูล"); + + b.Property("Description") + .IsRequired() + .HasColumnType("longtext") + .HasComment("*หมายเหตุขอลงเวลาพิเศษ"); + + b.Property("FirstName") + .HasColumnType("longtext"); + + b.Property("KeycloakUserId") + .HasColumnType("char(36)") + .HasComment("รหัส User ของ Keycloak ที่ร้องขอ"); + + b.Property("LastName") + .HasColumnType("longtext"); + + b.Property("LastUpdateFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(105) + .HasComment("ชื่อ User ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdateUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(103) + .HasComment("User Id ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(102) + .HasComment("แก้ไขข้อมูลล่าสุดเมื่อ"); + + b.Property("Latitude") + .HasColumnType("double"); + + b.Property("Longitude") + .HasColumnType("double"); + + b.Property("POI") + .HasColumnType("longtext"); + + b.Property("Prefix") + .HasColumnType("longtext"); + + b.Property("RootDnaId") + .HasColumnType("char(36)"); + + b.Property("Status") + .IsRequired() + .HasColumnType("longtext") + .HasComment("สถานะการอนุมัติ"); + + b.HasKey("Id"); + + b.ToTable("AdditionalCheckRequests"); + }); + + modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.TimeAttendants.CheckInJobStatus", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)") + .HasColumnOrder(0) + .HasComment("PrimaryKey") + .HasAnnotation("Relational:JsonPropertyName", "id"); + + b.Property("AdditionalData") + .HasColumnType("longtext") + .HasComment("ข้อมูลเพิ่มเติม (JSON)"); + + b.Property("CheckInId") + .HasColumnType("char(36)") + .HasComment("CheckInId สำหรับ Check-Out"); + + b.Property("CheckType") + .HasColumnType("longtext") + .HasComment("ประเภทการลงเวลา: CHECK_IN, CHECK_OUT"); + + b.Property("CompletedDate") + .HasColumnType("datetime(6)") + .HasComment("วันเวลาที่เสร็จสิ้นการประมวลผล"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(100) + .HasComment("สร้างข้อมูลเมื่อ"); + + b.Property("CreatedDate") + .HasColumnType("datetime(6)") + .HasComment("วันเวลาที่สร้างงาน"); + + b.Property("CreatedFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(104) + .HasComment("ชื่อ User ที่สร้างข้อมูล"); + + b.Property("CreatedUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(101) + .HasComment("User Id ที่สร้างข้อมูล"); + + b.Property("ErrorMessage") + .HasColumnType("longtext") + .HasComment("ข้อความแสดงข้อผิดพลาด"); + + b.Property("KeycloakUserId") + .HasColumnType("char(36)") + .HasComment("รหัส User ของ Keycloak"); + + b.Property("LastUpdateFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(105) + .HasComment("ชื่อ User ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdateUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(103) + .HasComment("User Id ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(102) + .HasComment("แก้ไขข้อมูลล่าสุดเมื่อ"); + + b.Property("ProcessingDate") + .HasColumnType("datetime(6)") + .HasComment("วันเวลาที่เริ่มประมวลผล"); + + b.Property("Status") + .IsRequired() + .HasColumnType("longtext") + .HasComment("สถานะงาน: PENDING, PROCESSING, COMPLETED, FAILED"); + + b.Property("TaskId") + .HasColumnType("char(36)") + .HasComment("Task ID สำหรับติดตามสถานะงาน"); + + b.HasKey("Id"); + + b.ToTable("CheckInJobStatuses"); + }); + + modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.TimeAttendants.DutyTime", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)") + .HasColumnOrder(0) + .HasComment("PrimaryKey") + .HasAnnotation("Relational:JsonPropertyName", "id"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(100) + .HasComment("สร้างข้อมูลเมื่อ"); + + b.Property("CreatedFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(104) + .HasComment("ชื่อ User ที่สร้างข้อมูล"); + + b.Property("CreatedUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(101) + .HasComment("User Id ที่สร้างข้อมูล"); + + b.Property("Description") + .IsRequired() + .HasColumnType("longtext") + .HasComment("คำอธิบาย"); + + b.Property("EndTimeAfternoon") + .IsRequired() + .HasColumnType("longtext") + .HasComment("เวลาออกงานช่วงบ่าย"); + + b.Property("EndTimeMorning") + .IsRequired() + .HasColumnType("longtext") + .HasComment("เวลาออกงานช่วงเช้า"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)") + .HasComment("สถานะการเปิดใช้งาน (เปิด/ปิด)"); + + b.Property("IsDefault") + .HasColumnType("tinyint(1)") + .HasComment("สถานะว่ารอบใดเป็นค่า Default ของข้าราชการ (สำหรับทุกคนที่ยังไม่ได้ทำการเลือกรอบ)"); + + b.Property("LastUpdateFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(105) + .HasComment("ชื่อ User ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdateUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(103) + .HasComment("User Id ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(102) + .HasComment("แก้ไขข้อมูลล่าสุดเมื่อ"); + + b.Property("StartTimeAfternoon") + .IsRequired() + .HasColumnType("longtext") + .HasComment("เวลาเข้างานช่วงบ่าย"); + + b.Property("StartTimeMorning") + .IsRequired() + .HasColumnType("longtext") + .HasComment("เวลาเข้างานช่วงเช้า"); + + b.HasKey("Id"); + + b.ToTable("DutyTimes"); + }); + + modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.TimeAttendants.ProcessUserTimeStamp", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)") + .HasColumnOrder(0) + .HasComment("PrimaryKey") + .HasAnnotation("Relational:JsonPropertyName", "id"); + + b.Property("CheckIn") + .HasColumnType("datetime(6)") + .HasComment("วัน เวลา เข้างาน"); + + b.Property("CheckInImageUrl") + .IsRequired() + .HasColumnType("longtext") + .HasComment("รูปถ่ายสถานที่ Check-In"); + + b.Property("CheckInLat") + .HasColumnType("double") + .HasComment("พิกัดละติจูด Check-In"); + + b.Property("CheckInLocationName") + .HasColumnType("longtext") + .HasComment("กรณีเลือกนอกสถานที่ตั้ง ต้องระบุข้อมูลชื่อสถานะที่ Check-In"); + + b.Property("CheckInLon") + .HasColumnType("double") + .HasComment("พิกัดลองจิจูด Check-In"); + + b.Property("CheckInPOI") + .IsRequired() + .HasColumnType("longtext") + .HasComment("ชื่อสถานที่ ได้มาจากระบบ ArcGis ของกองสารสนเทศภูมิศาสตร์ Check-In"); + + b.Property("CheckInRemark") + .HasColumnType("longtext") + .HasComment("ข้อความหมายเหตุที่ต้องการระบุเพิ่ม(มีเผื่อไว้อาจไม่ได้ใช้) Check-In"); + + b.Property("CheckInStatus") + .IsRequired() + .HasColumnType("longtext") + .HasComment("สถานะ Check-In"); + + b.Property("CheckOut") + .HasColumnType("datetime(6)") + .HasComment("วัน เวลา ออกงาน"); + + b.Property("CheckOutImageUrl") + .IsRequired() + .HasColumnType("longtext") + .HasComment("รูปถ่ายสถานที่ Check-Out"); + + b.Property("CheckOutLat") + .HasColumnType("double") + .HasComment("พิกัดละติจูด Check-Out"); + + b.Property("CheckOutLocationName") + .HasColumnType("longtext") + .HasComment("กรณีเลือกนอกสถานที่ตั้ง ต้องระบุข้อมูลชื่อสถานะที่ Check-Out"); + + b.Property("CheckOutLon") + .HasColumnType("double") + .HasComment("พิกัดลองจิจูด Check-Out"); + + b.Property("CheckOutPOI") + .IsRequired() + .HasColumnType("longtext") + .HasComment("ชื่อสถานที่ ได้มาจากระบบ ArcGis ของกองสารสนเทศภูมิศาสตร์ Check-Out"); + + b.Property("CheckOutRemark") + .HasColumnType("longtext") + .HasComment("ข้อความหมายเหตุที่ต้องการระบุเพิ่ม(มีเผื่อไว้อาจไม่ได้ใช้) Check-Out"); + + b.Property("CheckOutStatus") + .HasColumnType("longtext") + .HasComment("สถานะ Check-Out"); + + b.Property("Child1") + .HasColumnType("longtext"); + + b.Property("Child1DnaId") + .HasColumnType("char(36)"); + + b.Property("Child1Id") + .HasColumnType("char(36)"); + + b.Property("Child2") + .HasColumnType("longtext"); + + b.Property("Child2DnaId") + .HasColumnType("char(36)"); + + b.Property("Child2Id") + .HasColumnType("char(36)"); + + b.Property("Child3") + .HasColumnType("longtext"); + + b.Property("Child3DnaId") + .HasColumnType("char(36)"); + + b.Property("Child3Id") + .HasColumnType("char(36)"); + + b.Property("Child4") + .HasColumnType("longtext"); + + b.Property("Child4DnaId") + .HasColumnType("char(36)"); + + b.Property("Child4Id") + .HasColumnType("char(36)"); + + b.Property("CitizenId") + .HasColumnType("longtext"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(100) + .HasComment("สร้างข้อมูลเมื่อ"); + + b.Property("CreatedFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(104) + .HasComment("ชื่อ User ที่สร้างข้อมูล"); + + b.Property("CreatedUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(101) + .HasComment("User Id ที่สร้างข้อมูล"); + + b.Property("EditReason") + .HasColumnType("longtext") + .HasComment("เหตุผลการอนุมัติ/ไม่อนุมัติขอลงเวลาพิเศษ"); + + b.Property("EditStatus") + .HasColumnType("longtext") + .HasComment("สถานะการของลงเวลาพิเศษ"); + + b.Property("FirstName") + .HasColumnType("longtext"); + + b.Property("Gender") + .HasColumnType("longtext"); + + b.Property("IsLocationCheckIn") + .HasColumnType("tinyint(1)") + .HasComment("true คือ ณ สถานที่ตั้ง, false คือ นอกสถานที่ตั้ง Check-In"); + + b.Property("IsLocationCheckOut") + .HasColumnType("tinyint(1)") + .HasComment("true คือ ณ สถานที่ตั้ง, false คือ นอกสถานที่ตั้ง Check-Out"); + + b.Property("IsProcess") + .HasColumnType("tinyint(1)") + .HasComment("นำไปประมวลผลแล้วหรือยัง"); + + b.Property("KeycloakUserId") + .HasColumnType("char(36)") + .HasComment("รหัส User ของ Keycloak"); + + b.Property("LastName") + .HasColumnType("longtext"); + + b.Property("LastUpdateFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(105) + .HasComment("ชื่อ User ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdateUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(103) + .HasComment("User Id ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(102) + .HasComment("แก้ไขข้อมูลล่าสุดเมื่อ"); + + b.Property("Prefix") + .HasColumnType("longtext"); + + b.Property("ProfileId") + .HasColumnType("char(36)"); + + b.Property("ProfileType") + .HasColumnType("longtext"); + + b.Property("Root") + .HasColumnType("longtext"); + + b.Property("RootDnaId") + .HasColumnType("char(36)"); + + b.Property("RootId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.ToTable("ProcessUserTimeStamps"); + }); + + modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.TimeAttendants.UserCalendar", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)") + .HasColumnOrder(0) + .HasComment("PrimaryKey") + .HasAnnotation("Relational:JsonPropertyName", "id"); + + b.Property("Calendar") + .IsRequired() + .HasColumnType("longtext") + .HasComment("ปฏิทินการทำงานของ ขรก ปกติ หรือ 6 วันต่อสัปดาห์"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(100) + .HasComment("สร้างข้อมูลเมื่อ"); + + b.Property("CreatedFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(104) + .HasComment("ชื่อ User ที่สร้างข้อมูล"); + + b.Property("CreatedUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(101) + .HasComment("User Id ที่สร้างข้อมูล"); + + b.Property("LastUpdateFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(105) + .HasComment("ชื่อ User ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdateUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(103) + .HasComment("User Id ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(102) + .HasComment("แก้ไขข้อมูลล่าสุดเมื่อ"); + + b.Property("ProfileId") + .HasColumnType("char(36)") + .HasComment("รหัส Profile ในระบบทะเบียนประวัติ"); + + b.HasKey("Id"); + + b.ToTable("UserCalendars"); + }); + + modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.TimeAttendants.UserDutyTime", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)") + .HasColumnOrder(0) + .HasComment("PrimaryKey") + .HasAnnotation("Relational:JsonPropertyName", "id"); + + b.Property("Child1DnaId") + .HasColumnType("char(36)"); + + b.Property("Child2DnaId") + .HasColumnType("char(36)"); + + b.Property("Child3DnaId") + .HasColumnType("char(36)"); + + b.Property("Child4DnaId") + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(100) + .HasComment("สร้างข้อมูลเมื่อ"); + + b.Property("CreatedFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(104) + .HasComment("ชื่อ User ที่สร้างข้อมูล"); + + b.Property("CreatedUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(101) + .HasComment("User Id ที่สร้างข้อมูล"); + + b.Property("DutyTimeId") + .HasColumnType("char(36)") + .HasComment("รหัสรอบการลงเวลา"); + + b.Property("EffectiveDate") + .HasColumnType("datetime(6)") + .HasComment("วันที่มีผล"); + + b.Property("IsProcess") + .HasColumnType("tinyint(1)") + .HasComment("ทำการประมวลผลแล้วหรือยัง"); + + b.Property("LastUpdateFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(105) + .HasComment("ชื่อ User ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdateUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(103) + .HasComment("User Id ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(102) + .HasComment("แก้ไขข้อมูลล่าสุดเมื่อ"); + + b.Property("ProfileId") + .HasColumnType("char(36)") + .HasComment("รหัส Profile ในระบบทะเบียนประวัติ"); + + b.Property("Remark") + .HasColumnType("longtext") + .HasComment("หมายเหตุ"); + + b.Property("RootDnaId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("DutyTimeId"); + + b.ToTable("UserDutyTimes"); + }); + + modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.TimeAttendants.UserTimeStamp", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)") + .HasColumnOrder(0) + .HasComment("PrimaryKey") + .HasAnnotation("Relational:JsonPropertyName", "id"); + + b.Property("CheckIn") + .HasColumnType("datetime(6)") + .HasComment("วัน เวลา เข้างาน"); + + b.Property("CheckInImageUrl") + .IsRequired() + .HasColumnType("longtext") + .HasComment("รูปถ่ายสถานที่ Check-In"); + + b.Property("CheckInLat") + .HasColumnType("double") + .HasComment("พิกัดละติจูด Check-In"); + + b.Property("CheckInLocationName") + .HasColumnType("longtext") + .HasComment("กรณีเลือกนอกสถานที่ตั้ง ต้องระบุข้อมูลชื่อสถานะที่ Check-In"); + + b.Property("CheckInLon") + .HasColumnType("double") + .HasComment("พิกัดลองจิจูด Check-In"); + + b.Property("CheckInPOI") + .IsRequired() + .HasColumnType("longtext") + .HasComment("ชื่อสถานที่ ได้มาจากระบบ ArcGis ของกองสารสนเทศภูมิศาสตร์ Check-In"); + + b.Property("CheckInRemark") + .HasColumnType("longtext") + .HasComment("ข้อความหมายเหตุที่ต้องการระบุเพิ่ม(มีเผื่อไว้อาจไม่ได้ใช้) Check-In"); + + b.Property("CheckOut") + .HasColumnType("datetime(6)") + .HasComment("วัน เวลา ออกงาน"); + + b.Property("CheckOutImageUrl") + .IsRequired() + .HasColumnType("longtext") + .HasComment("รูปถ่ายสถานที่ Check-Out"); + + b.Property("CheckOutLat") + .HasColumnType("double") + .HasComment("พิกัดละติจูด Check-Out"); + + b.Property("CheckOutLocationName") + .HasColumnType("longtext") + .HasComment("กรณีเลือกนอกสถานที่ตั้ง ต้องระบุข้อมูลชื่อสถานะที่ Check-Out"); + + b.Property("CheckOutLon") + .HasColumnType("double") + .HasComment("พิกัดลองจิจูด Check-Out"); + + b.Property("CheckOutPOI") + .IsRequired() + .HasColumnType("longtext") + .HasComment("ชื่อสถานที่ ได้มาจากระบบ ArcGis ของกองสารสนเทศภูมิศาสตร์ Check-Out"); + + b.Property("CheckOutRemark") + .HasColumnType("longtext") + .HasComment("ข้อความหมายเหตุที่ต้องการระบุเพิ่ม(มีเผื่อไว้อาจไม่ได้ใช้) Check-Out"); + + b.Property("Child1") + .HasColumnType("longtext"); + + b.Property("Child1DnaId") + .HasColumnType("char(36)"); + + b.Property("Child1Id") + .HasColumnType("char(36)"); + + b.Property("Child2") + .HasColumnType("longtext"); + + b.Property("Child2DnaId") + .HasColumnType("char(36)"); + + b.Property("Child2Id") + .HasColumnType("char(36)"); + + b.Property("Child3") + .HasColumnType("longtext"); + + b.Property("Child3DnaId") + .HasColumnType("char(36)"); + + b.Property("Child3Id") + .HasColumnType("char(36)"); + + b.Property("Child4") + .HasColumnType("longtext"); + + b.Property("Child4DnaId") + .HasColumnType("char(36)"); + + b.Property("Child4Id") + .HasColumnType("char(36)"); + + b.Property("CitizenId") + .HasColumnType("longtext"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(100) + .HasComment("สร้างข้อมูลเมื่อ"); + + b.Property("CreatedFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(104) + .HasComment("ชื่อ User ที่สร้างข้อมูล"); + + b.Property("CreatedUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(101) + .HasComment("User Id ที่สร้างข้อมูล"); + + b.Property("FirstName") + .HasColumnType("longtext"); + + b.Property("Gender") + .HasColumnType("longtext"); + + b.Property("IsLocationCheckIn") + .HasColumnType("tinyint(1)") + .HasComment("true คือ ณ สถานที่ตั้ง, false คือ นอกสถานที่ตั้ง Check-In"); + + b.Property("IsLocationCheckOut") + .HasColumnType("tinyint(1)") + .HasComment("true คือ ณ สถานที่ตั้ง, false คือ นอกสถานที่ตั้ง Check-Out"); + + b.Property("IsProcess") + .HasColumnType("tinyint(1)") + .HasComment("นำไปประมวลผลแล้วหรือยัง"); + + b.Property("KeycloakUserId") + .HasColumnType("char(36)") + .HasComment("รหัส User ของ Keycloak"); + + b.Property("LastName") + .HasColumnType("longtext"); + + b.Property("LastUpdateFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(105) + .HasComment("ชื่อ User ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdateUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(103) + .HasComment("User Id ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(102) + .HasComment("แก้ไขข้อมูลล่าสุดเมื่อ"); + + b.Property("Prefix") + .HasColumnType("longtext"); + + b.Property("ProfileId") + .HasColumnType("char(36)"); + + b.Property("ProfileType") + .HasColumnType("longtext"); + + b.Property("Root") + .HasColumnType("longtext"); + + b.Property("RootDnaId") + .HasColumnType("char(36)"); + + b.Property("RootId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.ToTable("UserTimeStamps"); + }); + + modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.Requests.LeaveBeginning", b => + { + b.HasOne("BMA.EHR.Domain.Models.Leave.Commons.LeaveType", "LeaveType") + .WithMany() + .HasForeignKey("LeaveTypeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("LeaveType"); + }); + + modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.Requests.LeaveDocument", b => + { + b.HasOne("BMA.EHR.Domain.Models.Documents.Document", "Document") + .WithMany() + .HasForeignKey("DocumentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("BMA.EHR.Domain.Models.Leave.Requests.LeaveRequest", "LeaveRequest") + .WithMany("LeaveDocument") + .HasForeignKey("LeaveRequestId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Document"); + + b.Navigation("LeaveRequest"); + }); + + modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.Requests.LeaveRequest", b => + { + b.HasOne("BMA.EHR.Domain.Models.Documents.Document", "LeaveCancelDocument") + .WithMany() + .HasForeignKey("LeaveCancelDocumentId"); + + b.HasOne("BMA.EHR.Domain.Models.Documents.Document", "LeaveDraftDocument") + .WithMany() + .HasForeignKey("LeaveDraftDocumentId"); + + b.HasOne("BMA.EHR.Domain.Models.Leave.Commons.LeaveType", "Type") + .WithMany() + .HasForeignKey("TypeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("LeaveCancelDocument"); + + b.Navigation("LeaveDraftDocument"); + + b.Navigation("Type"); + }); + + modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.Requests.LeaveRequestApprover", b => + { + b.HasOne("BMA.EHR.Domain.Models.Leave.Requests.LeaveRequest", "LeaveRequest") + .WithMany("Approvers") + .HasForeignKey("LeaveRequestId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("LeaveRequest"); + }); + + modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.TimeAttendants.UserDutyTime", b => + { + b.HasOne("BMA.EHR.Domain.Models.Leave.TimeAttendants.DutyTime", "DutyTime") + .WithMany() + .HasForeignKey("DutyTimeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("DutyTime"); + }); + + modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.Requests.LeaveRequest", b => + { + b.Navigation("Approvers"); + + b.Navigation("LeaveDocument"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/BMA.EHR.Infrastructure/Migrations/LeaveDb/20260120032158_Add RMQ Task Control.cs b/BMA.EHR.Infrastructure/Migrations/LeaveDb/20260120032158_Add RMQ Task Control.cs new file mode 100644 index 00000000..f8d2090b --- /dev/null +++ b/BMA.EHR.Infrastructure/Migrations/LeaveDb/20260120032158_Add RMQ Task Control.cs @@ -0,0 +1,58 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace BMA.EHR.Infrastructure.Migrations.LeaveDb +{ + /// + public partial class AddRMQTaskControl : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "CheckInJobStatuses", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, comment: "PrimaryKey", collation: "ascii_general_ci"), + CreatedAt = table.Column(type: "datetime(6)", nullable: false, comment: "สร้างข้อมูลเมื่อ"), + CreatedUserId = table.Column(type: "varchar(40)", maxLength: 40, nullable: false, comment: "User Id ที่สร้างข้อมูล") + .Annotation("MySql:CharSet", "utf8mb4"), + LastUpdatedAt = table.Column(type: "datetime(6)", nullable: true, comment: "แก้ไขข้อมูลล่าสุดเมื่อ"), + LastUpdateUserId = table.Column(type: "varchar(40)", maxLength: 40, nullable: false, comment: "User Id ที่แก้ไขข้อมูลล่าสุด") + .Annotation("MySql:CharSet", "utf8mb4"), + CreatedFullName = table.Column(type: "varchar(200)", maxLength: 200, nullable: false, comment: "ชื่อ User ที่สร้างข้อมูล") + .Annotation("MySql:CharSet", "utf8mb4"), + LastUpdateFullName = table.Column(type: "varchar(200)", maxLength: 200, nullable: false, comment: "ชื่อ User ที่แก้ไขข้อมูลล่าสุด") + .Annotation("MySql:CharSet", "utf8mb4"), + TaskId = table.Column(type: "char(36)", nullable: false, comment: "Task ID สำหรับติดตามสถานะงาน", collation: "ascii_general_ci"), + KeycloakUserId = table.Column(type: "char(36)", nullable: false, comment: "รหัส User ของ Keycloak", collation: "ascii_general_ci"), + CreatedDate = table.Column(type: "datetime(6)", nullable: false, comment: "วันเวลาที่สร้างงาน"), + ProcessingDate = table.Column(type: "datetime(6)", nullable: true, comment: "วันเวลาที่เริ่มประมวลผล"), + CompletedDate = table.Column(type: "datetime(6)", nullable: true, comment: "วันเวลาที่เสร็จสิ้นการประมวลผล"), + Status = table.Column(type: "longtext", nullable: false, comment: "สถานะงาน: PENDING, PROCESSING, COMPLETED, FAILED") + .Annotation("MySql:CharSet", "utf8mb4"), + CheckType = table.Column(type: "longtext", nullable: true, comment: "ประเภทการลงเวลา: CHECK_IN, CHECK_OUT") + .Annotation("MySql:CharSet", "utf8mb4"), + CheckInId = table.Column(type: "char(36)", nullable: true, comment: "CheckInId สำหรับ Check-Out", collation: "ascii_general_ci"), + ErrorMessage = table.Column(type: "longtext", nullable: true, comment: "ข้อความแสดงข้อผิดพลาด") + .Annotation("MySql:CharSet", "utf8mb4"), + AdditionalData = table.Column(type: "longtext", nullable: true, comment: "ข้อมูลเพิ่มเติม (JSON)") + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK_CheckInJobStatuses", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "CheckInJobStatuses"); + } + } +} diff --git a/BMA.EHR.Infrastructure/Migrations/LeaveDb/LeaveDbContextModelSnapshot.cs b/BMA.EHR.Infrastructure/Migrations/LeaveDb/LeaveDbContextModelSnapshot.cs index 14e45995..d12bf747 100644 --- a/BMA.EHR.Infrastructure/Migrations/LeaveDb/LeaveDbContextModelSnapshot.cs +++ b/BMA.EHR.Infrastructure/Migrations/LeaveDb/LeaveDbContextModelSnapshot.cs @@ -882,6 +882,99 @@ namespace BMA.EHR.Infrastructure.Migrations.LeaveDb b.ToTable("AdditionalCheckRequests"); }); + modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.TimeAttendants.CheckInJobStatus", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)") + .HasColumnOrder(0) + .HasComment("PrimaryKey") + .HasAnnotation("Relational:JsonPropertyName", "id"); + + b.Property("AdditionalData") + .HasColumnType("longtext") + .HasComment("ข้อมูลเพิ่มเติม (JSON)"); + + b.Property("CheckInId") + .HasColumnType("char(36)") + .HasComment("CheckInId สำหรับ Check-Out"); + + b.Property("CheckType") + .HasColumnType("longtext") + .HasComment("ประเภทการลงเวลา: CHECK_IN, CHECK_OUT"); + + b.Property("CompletedDate") + .HasColumnType("datetime(6)") + .HasComment("วันเวลาที่เสร็จสิ้นการประมวลผล"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(100) + .HasComment("สร้างข้อมูลเมื่อ"); + + b.Property("CreatedDate") + .HasColumnType("datetime(6)") + .HasComment("วันเวลาที่สร้างงาน"); + + b.Property("CreatedFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(104) + .HasComment("ชื่อ User ที่สร้างข้อมูล"); + + b.Property("CreatedUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(101) + .HasComment("User Id ที่สร้างข้อมูล"); + + b.Property("ErrorMessage") + .HasColumnType("longtext") + .HasComment("ข้อความแสดงข้อผิดพลาด"); + + b.Property("KeycloakUserId") + .HasColumnType("char(36)") + .HasComment("รหัส User ของ Keycloak"); + + b.Property("LastUpdateFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(105) + .HasComment("ชื่อ User ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdateUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(103) + .HasComment("User Id ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(102) + .HasComment("แก้ไขข้อมูลล่าสุดเมื่อ"); + + b.Property("ProcessingDate") + .HasColumnType("datetime(6)") + .HasComment("วันเวลาที่เริ่มประมวลผล"); + + b.Property("Status") + .IsRequired() + .HasColumnType("longtext") + .HasComment("สถานะงาน: PENDING, PROCESSING, COMPLETED, FAILED"); + + b.Property("TaskId") + .HasColumnType("char(36)") + .HasComment("Task ID สำหรับติดตามสถานะงาน"); + + b.HasKey("Id"); + + b.ToTable("CheckInJobStatuses"); + }); + modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.TimeAttendants.DutyTime", b => { b.Property("Id") diff --git a/BMA.EHR.Infrastructure/Persistence/LeaveDbContext.cs b/BMA.EHR.Infrastructure/Persistence/LeaveDbContext.cs index c6d37b7a..19848c6b 100644 --- a/BMA.EHR.Infrastructure/Persistence/LeaveDbContext.cs +++ b/BMA.EHR.Infrastructure/Persistence/LeaveDbContext.cs @@ -22,6 +22,8 @@ namespace BMA.EHR.Infrastructure.Persistence public DbSet UserCalendars { get; set; } + public DbSet CheckInJobStatuses { get; set; } + #endregion #region " Leave System " diff --git a/BMA.EHR.Leave/Controllers/LeaveController.cs b/BMA.EHR.Leave/Controllers/LeaveController.cs index aa89fc6a..b2544898 100644 --- a/BMA.EHR.Leave/Controllers/LeaveController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveController.cs @@ -58,6 +58,7 @@ namespace BMA.EHR.Leave.Service.Controllers private readonly LeaveRequestRepository _leaveRequestRepository; private readonly UserCalendarRepository _userCalendarRepository; private readonly PermissionRepository _permission; + private readonly CheckInJobStatusRepository _checkInJobStatusRepository; private readonly CommandRepository _commandRepository; @@ -92,6 +93,7 @@ namespace BMA.EHR.Leave.Service.Controllers ObjectPool objectPool, PermissionRepository permission, NotificationRepository notificationRepository, + CheckInJobStatusRepository checkInJobStatusRepository, HttpClient httpClient) { _dutyTimeRepository = dutyTimeRepository; @@ -109,6 +111,7 @@ namespace BMA.EHR.Leave.Service.Controllers _commandRepository = commandRepository; _leaveRequestRepository = leaveRequestRepository; _notificationRepository = notificationRepository; + _checkInJobStatusRepository = checkInJobStatusRepository; _objectPool = objectPool; _permission = permission; @@ -540,11 +543,15 @@ namespace BMA.EHR.Leave.Service.Controllers } } + // add task id for check in queue + string taskId = Guid.NewGuid().ToString(); + var checkData = new CheckTimeDtoRB { UserId = userId, CurrentDate = currentDate, CheckInId = data.CheckInId, + TaskId = Guid.Parse(taskId), Lat = data.Lat, Lon = data.Lon, POI = data.POI, @@ -564,11 +571,27 @@ namespace BMA.EHR.Leave.Service.Controllers var serializedObject = JsonConvert.SerializeObject(checkData); var body = Encoding.UTF8.GetBytes(serializedObject); - // add task id for check in queue - string taskId = Guid.NewGuid().ToString(); var properties = channel.CreateBasicProperties(); properties.Persistent = true; - properties.MessageId = userId.ToString("D");// ระบบลงเวลาต้องมีการเช็คสถานะใน rabbitMQ ด้วยว่ามีการรอรันอยู่ไหม ลงเวลาเข้า/ออกงาน #894 + properties.MessageId = taskId; + + // บันทึกสถานะงานก่อนส่งไป RabbitMQ + var jobStatus = new CheckInJobStatus + { + TaskId = Guid.Parse(taskId), + KeycloakUserId = userId, + CreatedDate = currentDate, + Status = "PENDING", + CheckType = data.CheckInId == null ? "CHECK_IN" : "CHECK_OUT", + CheckInId = data.CheckInId, + AdditionalData = JsonConvert.SerializeObject(new + { + IsLocation = data.IsLocation, + LocationName = data.LocationName, + POI = data.POI + }) + }; + await _checkInJobStatusRepository.AddAsync(jobStatus); channel.BasicPublish(exchange: "", routingKey: queue, @@ -583,6 +606,78 @@ namespace BMA.EHR.Leave.Service.Controllers } } + /// + /// ตรวจสอบสถานะงาน check-in ด้วย Task ID + /// + /// Task ID ที่ได้จากการเรียก CheckInAsync + /// + /// + /// เมื่อทำรายการสำเร็จ + /// ไม่ได้ Login เข้าระบบ + /// ไม่พบข้อมูลงาน + /// เมื่อเกิดข้อผิดพลาดในการทำงาน + [HttpGet("job-status/{taskId:guid}")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status401Unauthorized)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + [ProducesResponseType(StatusCodes.Status500InternalServerError)] + public async Task> GetJobStatusAsync(Guid taskId) + { + var jobStatus = await _checkInJobStatusRepository.GetByTaskIdAsync(taskId); + + if (jobStatus == null) + { + return Error("ไม่พบข้อมูลงาน", StatusCodes.Status404NotFound); + } + + var result = new + { + taskId = jobStatus.TaskId, + keycloakUserId = jobStatus.KeycloakUserId, + status = jobStatus.Status, + checkType = jobStatus.CheckType, + checkInId = jobStatus.CheckInId, + createdDate = jobStatus.CreatedDate, + processingDate = jobStatus.ProcessingDate, + completedDate = jobStatus.CompletedDate, + errorMessage = jobStatus.ErrorMessage, + additionalData = jobStatus.AdditionalData != null ? + JsonConvert.DeserializeObject(jobStatus.AdditionalData) : null + }; + + return Success(result); + } + + /// + /// ดึงรายการงานที่ยัง pending หรือ processing ของผู้ใช้ + /// + /// + /// + /// เมื่อทำรายการสำเร็จ + /// ไม่ได้ Login เข้าระบบ + /// เมื่อเกิดข้อผิดพลาดในการทำงาน + [HttpGet("pending-jobs")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status401Unauthorized)] + [ProducesResponseType(StatusCodes.Status500InternalServerError)] + public async Task> GetPendingJobsAsync() + { + var userId = UserId == null ? Guid.Empty : Guid.Parse(UserId); + var jobs = await _checkInJobStatusRepository.GetPendingOrProcessingJobsAsync(userId); + + var result = jobs.Select(job => new + { + taskId = job.TaskId, + status = job.Status, + checkType = job.CheckType, + checkInId = job.CheckInId, + createdDate = job.CreatedDate, + processingDate = job.ProcessingDate + }).ToList(); + + return Success(new { count = result.Count, jobs = result }); + } + [HttpGet("check-status")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] @@ -590,61 +685,62 @@ namespace BMA.EHR.Leave.Service.Controllers public async Task> CheckInCheckStatus() { var userId = UserId == null ? Guid.Empty : Guid.Parse(UserId); - var currentDate = DateTime.Now; - var channel = _objectPool.Get(); + // var currentDate = DateTime.Now; + // var channel = _objectPool.Get(); try { - var _url = _configuration["Rabbit:URL"] ?? ""; - var _queue = _configuration["Rabbit:Queue"] ?? "basic-queue"; + // var _url = _configuration["Rabbit:URL"] ?? ""; + // var _queue = _configuration["Rabbit:Queue"] ?? "basic-queue"; - // Step 1: ตรวจสอบจำนวน message ทั้งหมดในคิว - string queueUrl = $"{_url}{_queue}"; - var queueResponse = await _httpClient.GetAsync(queueUrl); - if (!queueResponse.IsSuccessStatusCode) - { - return Error("Error accessing RabbitMQ API", (int)queueResponse.StatusCode); - } + // // Step 1: ตรวจสอบจำนวน message ทั้งหมดในคิว + // string queueUrl = $"{_url}{_queue}"; + // var queueResponse = await _httpClient.GetAsync(queueUrl); + // if (!queueResponse.IsSuccessStatusCode) + // { + // return Error("Error accessing RabbitMQ API", (int)queueResponse.StatusCode); + // } - var queueContent = await queueResponse.Content.ReadAsStringAsync(); - var queueData = JObject.Parse(queueContent); - int totalMessages = queueData["messages"]?.Value() ?? 0; + // var queueContent = await queueResponse.Content.ReadAsStringAsync(); + // var queueData = JObject.Parse(queueContent); + // int totalMessages = queueData["messages"]?.Value() ?? 0; - // Step 2: วนลูปดึง message ทีละ 100 งาน - int batchSize = 100; - var allMessages = new List(); - int processedMessages = 0; + // // Step 2: วนลูปดึง message ทีละ 100 งาน + // int batchSize = 100; + // var allMessages = new List(); + // int processedMessages = 0; - while (processedMessages < totalMessages) - { - var requestBody = new StringContent( - $"{{\"count\":{batchSize},\"requeue\":true,\"encoding\":\"auto\",\"ackmode\":\"ack_requeue_true\"}}", - Encoding.UTF8, - "application/json" - ); + // while (processedMessages < totalMessages) + // { + // var requestBody = new StringContent( + // $"{{\"count\":{batchSize},\"requeue\":true,\"encoding\":\"auto\",\"ackmode\":\"ack_requeue_true\"}}", + // Encoding.UTF8, + // "application/json" + // ); - string getMessagesUrl = $"{_url}{_queue}/get"; - var response = await _httpClient.PostAsync(getMessagesUrl, requestBody); - if (!response.IsSuccessStatusCode) - { - return StatusCode((int)response.StatusCode, "Error retrieving messages from RabbitMQ."); - } + // string getMessagesUrl = $"{_url}{_queue}/get"; + // var response = await _httpClient.PostAsync(getMessagesUrl, requestBody); + // if (!response.IsSuccessStatusCode) + // { + // return StatusCode((int)response.StatusCode, "Error retrieving messages from RabbitMQ."); + // } - var content = await response.Content.ReadAsStringAsync(); - var messages = JArray.Parse(content); + // var content = await response.Content.ReadAsStringAsync(); + // var messages = JArray.Parse(content); - if (messages.Count == 0) - { - break; - } + // if (messages.Count == 0) + // { + // break; + // } - processedMessages += messages.Count; - allMessages.AddRange(messages.Select(m => m["properties"].ToString())); - } + // processedMessages += messages.Count; + // allMessages.AddRange(messages.Select(m => m["properties"].ToString())); + // } + var jobs = await _checkInJobStatusRepository.GetPendingOrProcessingJobsAsync(userId); // Step 3: ค้นหา taskIds ที่อยู่ใน messages ทั้งหมด - var foundTasks = allMessages.FirstOrDefault(x => x.Contains(userId.ToString("D"))); + //var foundTasks = allMessages.FirstOrDefault(x => x.Contains(userId.ToString("D"))); - return Success(new { keycloakId = userId, InQueue = foundTasks != null }); + return Success(new { keycloakId = userId, InQueue = (jobs != null && jobs.Count > 0) }); } catch (Exception ex) @@ -653,7 +749,7 @@ namespace BMA.EHR.Leave.Service.Controllers } finally { - _objectPool.Return(channel); + //_objectPool.Return(channel); } } @@ -790,21 +886,31 @@ namespace BMA.EHR.Leave.Service.Controllers public async Task> ProcessCheckInAsync([FromBody] CheckTimeDtoRB data) { var userId = data.UserId ?? Guid.Empty; - var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(userId, data.Token); + var taskId = data.TaskId ?? Guid.Empty; - if (profile == null) - return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound); - - if (data.CheckInFileName == "no-file") throw new Exception(GlobalMessages.NoFileToUpload); - var currentDate = data.CurrentDate ?? DateTime.Now; - - var check_status = data.CheckInId == null ? "check-in-picture" : "check-out-picture"; - - var fileName = $"{_bucketName}/{userId}/{currentDate.ToString("dd-MM-yyyy")}/{check_status}/{data.CheckInFileName}"; - using (var ms = new MemoryStream(data.CheckInFileBytes ?? new byte[0])) + try { - await _minIOService.UploadFileAsync(fileName, ms); - } + // อัปเดตสถานะเป็น PROCESSING + if (taskId != Guid.Empty) + { + await _checkInJobStatusRepository.UpdateToProcessingAsync(taskId); + } + + var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(userId, data.Token); + + if (profile == null) + return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound); + + if (data.CheckInFileName == "no-file") throw new Exception(GlobalMessages.NoFileToUpload); + var currentDate = data.CurrentDate ?? DateTime.Now; + + var check_status = data.CheckInId == null ? "check-in-picture" : "check-out-picture"; + + var fileName = $"{_bucketName}/{userId}/{currentDate.ToString("dd-MM-yyyy")}/{check_status}/{data.CheckInFileName}"; + using (var ms = new MemoryStream(data.CheckInFileBytes ?? new byte[0])) + { + await _minIOService.UploadFileAsync(fileName, ms); + } var defaultRound = await _dutyTimeRepository.GetDefaultAsync(); if (defaultRound == null) @@ -1058,9 +1164,32 @@ namespace BMA.EHR.Leave.Service.Controllers } } + + // อัปเดตสถานะเป็น COMPLETED + if (taskId != Guid.Empty) + { + var additionalData = JsonConvert.SerializeObject(new + { + CheckInType = data.CheckInId == null ? "check-in" : "check-out", + FileName = fileName, + ProcessedDate = currentDate + }); + await _checkInJobStatusRepository.UpdateToCompletedAsync(taskId, additionalData); + } + var checkInType = data.CheckInId == null ? "check-in" : "check-out"; return Success(new { user = $"{profile.FirstName} {profile.LastName}", date = currentDate, type = checkInType }); ; } + catch (Exception ex) + { + // อัปเดตสถานะเป็น FAILED + if (taskId != Guid.Empty) + { + await _checkInJobStatusRepository.UpdateToFailedAsync(taskId, ex.Message); + } + throw; + } + } /// /// LV1_005 - ลงเวลาเข้า-ออกงาน (USER) diff --git a/BMA.EHR.Leave/DTOs/CheckIn/CheckTimeDto.cs b/BMA.EHR.Leave/DTOs/CheckIn/CheckTimeDto.cs index 3a738170..0d49c24f 100644 --- a/BMA.EHR.Leave/DTOs/CheckIn/CheckTimeDto.cs +++ b/BMA.EHR.Leave/DTOs/CheckIn/CheckTimeDto.cs @@ -54,6 +54,7 @@ namespace BMA.EHR.Leave.Service.DTOs.CheckIn public Guid? CheckInId { get; set; } + public Guid? TaskId { get; set; } public double Lat { get; set; } = 0; diff --git a/BMA.EHR.Leave/appsettings.json b/BMA.EHR.Leave/appsettings.json index e1bb5d10..d1f58a39 100644 --- a/BMA.EHR.Leave/appsettings.json +++ b/BMA.EHR.Leave/appsettings.json @@ -53,7 +53,7 @@ "Host": "192.168.1.63", "User": "admin", "Password": "12345678", - "Queue": "hrms-checkin-queue", + "Queue": "hrms-checkin-queue-dev", "URL": "http://192.168.1.63:9122/api/queues/%2F/" }, "Mail": { From 90eea1ac7febedfc33e57258705d81f64991c496 Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Tue, 20 Jan 2026 11:09:13 +0700 Subject: [PATCH 052/178] Enhance CombinedErrorHandlerAndLoggingMiddleware with caching and improved token handling --- ...ombinedErrorHandlerAndLoggingMiddleware.cs | 230 ++++++++++++------ 1 file changed, 162 insertions(+), 68 deletions(-) diff --git a/BMA.EHR.Domain/Middlewares/CombinedErrorHandlerAndLoggingMiddleware.cs b/BMA.EHR.Domain/Middlewares/CombinedErrorHandlerAndLoggingMiddleware.cs index a9de25fc..5f5aa2af 100644 --- a/BMA.EHR.Domain/Middlewares/CombinedErrorHandlerAndLoggingMiddleware.cs +++ b/BMA.EHR.Domain/Middlewares/CombinedErrorHandlerAndLoggingMiddleware.cs @@ -18,6 +18,10 @@ namespace BMA.EHR.Domain.Middlewares { private readonly RequestDelegate _next; private readonly IConfiguration _configuration; + private static ElasticClient? _elasticClient; + private static readonly object _lock = new object(); + private static readonly Dictionary _profileCache = new(); + private static readonly TimeSpan _cacheExpiry = TimeSpan.FromMinutes(10); private string Uri = ""; private string IndexFormat = ""; @@ -31,19 +35,28 @@ namespace BMA.EHR.Domain.Middlewares Uri = _configuration["ElasticConfiguration:Uri"] ?? "http://192.168.1.40:9200"; IndexFormat = _configuration["ElasticConfiguration:IndexFormat"] ?? "bma-ehr-log-index"; SystemName = _configuration["ElasticConfiguration:SystemName"] ?? "Unknown"; + + // สร้าง ElasticClient แค่ครั้งเดียว + if (_elasticClient == null) + { + lock (_lock) + { + if (_elasticClient == null) + { + var settings = new ConnectionSettings(new Uri(Uri)) + .DefaultIndex(IndexFormat) + .DisableDirectStreaming() // เพิ่มประสิทธิภาพ + .RequestTimeout(TimeSpan.FromSeconds(5)); // กำหนด timeout + _elasticClient = new ElasticClient(settings); + } + } + } } public async Task Invoke(HttpContext context) { - Console.WriteLine("=== CombinedErrorHandlerAndLoggingMiddleware Start ==="); - - var settings = new ConnectionSettings(new Uri(Uri)) - .DefaultIndex(IndexFormat); - var client = new ElasticClient(settings); - var startTime = DateTime.UtcNow; var stopwatch = Stopwatch.StartNew(); - string? responseBodyJson = null; string? requestBodyJson = null; Exception? caughtException = null; @@ -64,27 +77,15 @@ namespace BMA.EHR.Domain.Middlewares string keycloakId = Guid.Empty.ToString("D"); var token = context.Request.Headers["Authorization"]; GetProfileByKeycloakIdLocal? pf = null; + var tokenUserInfo = await ExtractTokenUserInfoAsync(token); - // ลองดึง keycloakId จาก JWT token ก่อน (ถ้ามี) - try - { - keycloakId = await ExtractKeycloakIdFromToken(token); - } - catch (Exception ex) - { - Console.WriteLine($"Error extracting keycloakId from token: {ex.Message}"); - } + // ดึง keycloakId จาก JWT token + keycloakId = tokenUserInfo.KeycloakId; - try + // ดึง profile จาก cache หรือ API + if (Guid.TryParse(keycloakId, out var parsedId) && parsedId != Guid.Empty) { - if (Guid.TryParse(keycloakId, out var parsedId) && parsedId != Guid.Empty) - { - pf = await GetProfileByKeycloakIdAsync(parsedId, token); - } - } - catch (Exception ex) - { - Console.WriteLine($"Error getting profile: {ex.Message}"); + pf = await GetProfileWithCacheAsync(parsedId, token); } try @@ -103,17 +104,17 @@ namespace BMA.EHR.Domain.Middlewares Console.WriteLine($"Updated keycloakId from authenticated user: {keycloakId}"); // อัพเดต profile ด้วย keycloakId ที่ถูกต้อง - try - { - if (Guid.TryParse(keycloakId, out var parsedId)) - { - pf = await GetProfileByKeycloakIdAsync(parsedId, token); - } - } - catch (Exception ex) - { - Console.WriteLine($"Error updating profile after authentication: {ex.Message}"); - } + // try + // { + // if (Guid.TryParse(keycloakId, out var parsedId)) + // { + // //pf = await GetProfileByKeycloakIdAsync(parsedId, token); + // } + // } + // catch (Exception ex) + // { + // Console.WriteLine($"Error updating profile after authentication: {ex.Message}"); + // } } } @@ -142,7 +143,19 @@ namespace BMA.EHR.Domain.Middlewares finally { stopwatch.Stop(); - await LogRequest(context, client, startTime, stopwatch, pf, keycloakId, requestBodyJson, memoryStream, caughtException); + + // ทำ logging แบบ fire-and-forget เพื่อไม่ block response + _ = Task.Run(async () => + { + try + { + await LogRequestAsync(context, _elasticClient!, startTime, stopwatch, pf, keycloakId, requestBodyJson, memoryStream, caughtException); + } + catch (Exception ex) + { + Console.WriteLine($"Background logging error: {ex.Message}"); + } + }); // เขียนข้อมูลกลับไปยัง original Response body if (memoryStream.Length > 0) @@ -379,7 +392,7 @@ namespace BMA.EHR.Domain.Middlewares } } - private async Task LogRequest(HttpContext context, ElasticClient client, DateTime startTime, Stopwatch stopwatch, + private async Task LogRequestAsync(HttpContext context, ElasticClient client, DateTime startTime, Stopwatch stopwatch, GetProfileByKeycloakIdLocal? pf, string keycloakId, string? requestBodyJson, MemoryStream memoryStream, Exception? caughtException) { try @@ -399,7 +412,7 @@ namespace BMA.EHR.Domain.Middlewares string? message = null; string? responseBodyJson = null; - // อ่านข้อมูลจาก Response + // อ่านข้อมูลจาก Response (ลด serialization ที่ซ้ำ) if (memoryStream.Length > 0) { memoryStream.Seek(0, SeekOrigin.Begin); @@ -407,7 +420,7 @@ namespace BMA.EHR.Domain.Middlewares if (!string.IsNullOrEmpty(responseBody)) { - var contentType = context.Response.ContentType; + var contentType = context.Response.ContentType ?? ""; var isFileResponse = !contentType.StartsWith("application/json") && !contentType.StartsWith("text/html") && ( contentType.StartsWith("application/") || contentType.StartsWith("image/") || @@ -422,33 +435,23 @@ namespace BMA.EHR.Domain.Middlewares } else { + // ใช้ response body ที่มีอยู่แล้วโดยไม่ serialize ซ้ำ + responseBodyJson = responseBody; + try { - var jsonOptions = new JsonSerializerOptions - { - PropertyNamingPolicy = JsonNamingPolicy.CamelCase, - Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping, - WriteIndented = true, - Converters = { new DateTimeFixConverter() } - }; - responseBodyJson = JsonSerializer.Serialize(JsonSerializer.Deserialize(responseBody), jsonOptions); - var json = JsonSerializer.Deserialize(responseBody); if (json.ValueKind == JsonValueKind.Array) { message = "success"; } - else + else if (json.TryGetProperty("message", out var messageElement)) { - if (json.TryGetProperty("message", out var messageElement)) - { - message = messageElement.GetString(); - } + message = messageElement.GetString(); } } catch { - responseBodyJson = responseBody; message = caughtException?.Message ?? "Unknown error"; } } @@ -536,11 +539,19 @@ namespace BMA.EHR.Domain.Middlewares private async Task ExtractKeycloakIdFromToken(string? authorizationHeader) { + var tokenInfo = await ExtractTokenUserInfoAsync(authorizationHeader); + return tokenInfo.KeycloakId; + } + + private async Task ExtractTokenUserInfoAsync(string? authorizationHeader) + { + var defaultResult = new TokenUserInfo { KeycloakId = Guid.Empty.ToString("D") }; + try { if (string.IsNullOrEmpty(authorizationHeader) || !authorizationHeader.StartsWith("Bearer ")) { - return Guid.Empty.ToString("D"); + return defaultResult; } var token = authorizationHeader.Replace("Bearer ", ""); @@ -549,7 +560,7 @@ namespace BMA.EHR.Domain.Middlewares var parts = token.Split('.'); if (parts.Length != 3) { - return Guid.Empty.ToString("D"); + return defaultResult; } // Decode Base64Url payload (JWT uses Base64Url encoding, not standard Base64) @@ -570,31 +581,55 @@ namespace BMA.EHR.Domain.Middlewares Console.WriteLine($"JWT Payload: {payloadJson}"); - // Parse JSON และดึง sub (subject) claim + // Parse JSON และดึง claims ต่างๆ var jsonDoc = JsonDocument.Parse(payloadJson); + var result = new TokenUserInfo(); - // ลองหา keycloak ID ใน claims ต่างๆ - string? keycloakId = null; - + // ดึง keycloak ID if (jsonDoc.RootElement.TryGetProperty("sub", out var subElement)) { - keycloakId = subElement.GetString(); + result.KeycloakId = subElement.GetString() ?? Guid.Empty.ToString("D"); } else if (jsonDoc.RootElement.TryGetProperty("nameid", out var nameidElement)) { - keycloakId = nameidElement.GetString(); + result.KeycloakId = nameidElement.GetString() ?? Guid.Empty.ToString("D"); } else if (jsonDoc.RootElement.TryGetProperty("user_id", out var userIdElement)) { - keycloakId = userIdElement.GetString(); + result.KeycloakId = userIdElement.GetString() ?? Guid.Empty.ToString("D"); + } + else + { + result.KeycloakId = Guid.Empty.ToString("D"); } - return keycloakId ?? Guid.Empty.ToString("D"); + // ดึง preferred_username + if (jsonDoc.RootElement.TryGetProperty("preferred_username", out var preferredUsernameElement)) + { + result.PreferredUsername = preferredUsernameElement.GetString(); + Console.WriteLine($"Extracted preferred_username: {result.PreferredUsername}"); + } + + // ดึง given_name + if (jsonDoc.RootElement.TryGetProperty("given_name", out var givenNameElement)) + { + result.GivenName = givenNameElement.GetString(); + Console.WriteLine($"Extracted given_name: {result.GivenName}"); + } + + // ดึง family_name + if (jsonDoc.RootElement.TryGetProperty("family_name", out var familyNameElement)) + { + result.FamilyName = familyNameElement.GetString(); + Console.WriteLine($"Extracted family_name: {result.FamilyName}"); + } + + return result; } catch (Exception ex) { - Console.WriteLine($"Error extracting keycloak ID from token: {ex.Message}"); - return Guid.Empty.ToString("D"); + Console.WriteLine($"Error extracting token user info: {ex.Message}"); + return defaultResult; } } @@ -640,7 +675,58 @@ namespace BMA.EHR.Domain.Middlewares } catch { - throw; + return null; + } + } + + private async Task GetProfileWithCacheAsync(Guid keycloakId, string? accessToken) + { + var cacheKey = keycloakId.ToString(); + + // ตรวจสอบ cache + lock (_profileCache) + { + if (_profileCache.TryGetValue(cacheKey, out var cached)) + { + if (cached.ExpiryTime > DateTime.UtcNow) + { + return cached.Profile; + } + // ลบ cache ที่หมดอายุ + _profileCache.Remove(cacheKey); + } + } + + // ดึงข้อมูลจาก API + try + { + var profile = await GetProfileByKeycloakIdAsync(keycloakId, accessToken); + if (profile != null) + { + // เก็บใน cache + lock (_profileCache) + { + _profileCache[cacheKey] = (profile, DateTime.UtcNow.Add(_cacheExpiry)); + + // ลบ cache เก่าที่เกิน 1000 รายการ + if (_profileCache.Count > 1000) + { + var expiredKeys = _profileCache + .Where(x => x.Value.ExpiryTime < DateTime.UtcNow) + .Select(x => x.Key) + .ToList(); + foreach (var key in expiredKeys) + { + _profileCache.Remove(key); + } + } + } + } + return profile; + } + catch + { + return null; } } @@ -655,6 +741,14 @@ namespace BMA.EHR.Domain.Middlewares } // Model classes + public class TokenUserInfo + { + public string KeycloakId { get; set; } = string.Empty; + public string? PreferredUsername { get; set; } + public string? GivenName { get; set; } + public string? FamilyName { get; set; } + } + public class GetProfileByKeycloakIdLocal { public Guid Id { get; set; } From e3a228773e5f55cafd1e315b767ca0e0941b8a04 Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Tue, 20 Jan 2026 11:14:30 +0700 Subject: [PATCH 053/178] Add scheduled job to clean up CheckIn Job Status older than 30 days --- BMA.EHR.Leave/Program.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/BMA.EHR.Leave/Program.cs b/BMA.EHR.Leave/Program.cs index 7ff93507..928c8fc2 100644 --- a/BMA.EHR.Leave/Program.cs +++ b/BMA.EHR.Leave/Program.cs @@ -181,6 +181,8 @@ var manager = new RecurringJobManager(); if (manager != null) { manager.AddOrUpdate("ปรับปรุงรอบการลงเวลาทำงาน", Job.FromExpression(x => x.UpdateUserDutyTime()), "0 1 * * *", bangkokTimeZone); + // ทำความสะอาดข้อมูล CheckIn Job Status ที่เก่ากว่า 30 วัน - รันทุกวันเวลา 02:00 น. + manager.AddOrUpdate("ทำความสะอาดข้อมูล CheckIn Job Status", Job.FromExpression(x => x.CleanupOldJobsAsync(30)), "0 2 * * *", bangkokTimeZone); } // apply migrations From 15f5d7cae76d1e6a2ba92deff06c890509264a4f Mon Sep 17 00:00:00 2001 From: harid Date: Tue, 20 Jan 2026 16:41:22 +0700 Subject: [PATCH 054/178] =?UTF-8?q?Task=20#2207=20=E0=B8=81=E0=B8=A3?= =?UTF-8?q?=E0=B8=93=E0=B8=B5=E0=B8=84=E0=B8=99=E0=B8=82=E0=B8=AD=E0=B9=82?= =?UTF-8?q?=E0=B8=AD=E0=B8=99=E0=B8=AD=E0=B8=A2=E0=B8=B9=E0=B9=88=E0=B9=83?= =?UTF-8?q?=E0=B8=99=E0=B8=AA=E0=B8=B3=E0=B8=99=E0=B8=B1=E0=B8=81=E0=B8=9B?= =?UTF-8?q?=E0=B8=A5=E0=B8=B1=E0=B8=94=E0=B8=81=E0=B8=A3=E0=B8=B8=E0=B8=87?= =?UTF-8?q?=E0=B9=80=E0=B8=97=E0=B8=9E=E0=B8=A1=E0=B8=AB=E0=B8=B2=E0=B8=99?= =?UTF-8?q?=E0=B8=84=E0=B8=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Controllers/PlacementTransferController.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/BMA.EHR.Placement.Service/Controllers/PlacementTransferController.cs b/BMA.EHR.Placement.Service/Controllers/PlacementTransferController.cs index 73115246..f2931e3d 100644 --- a/BMA.EHR.Placement.Service/Controllers/PlacementTransferController.cs +++ b/BMA.EHR.Placement.Service/Controllers/PlacementTransferController.cs @@ -574,6 +574,7 @@ namespace BMA.EHR.Placement.Service.Controllers LastUpdatedAt = DateTime.Now, }; var apiUrl = $"{_configuration["API"]}/org/profile/keycloak/position"; + bool isDeputy = false; using (var client = new HttpClient()) { client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token.Replace("Bearer ", "")); @@ -587,6 +588,8 @@ namespace BMA.EHR.Placement.Service.Controllers if (org == null || org.result == null) return Error("ไม่พบหน่วยงานของผู้ใช้งานคนนี้", 404); + isDeputy = org.result.isDeputy.HasValue ? org.result.isDeputy.Value : false; + placementTransfer.profileId = org.result.profileId; placementTransfer.prefix = org.result.prefix; placementTransfer.firstName = org.result.firstName; @@ -663,6 +666,7 @@ namespace BMA.EHR.Placement.Service.Controllers } } } + var baseAPIOrg = _configuration["API"]; var apiUrlOrg = $"{baseAPIOrg}/org/workflow/add-workflow"; using (var client = new HttpClient()) @@ -675,7 +679,8 @@ namespace BMA.EHR.Placement.Service.Controllers sysName = "SYS_TRANSFER_REQ", posLevelName = placementTransfer.posLevelNameOld, posTypeName = placementTransfer.posTypeNameOld, - fullName = $"{placementTransfer.prefix}{placementTransfer.firstName} {placementTransfer.lastName}" + fullName = $"{placementTransfer.prefix}{placementTransfer.firstName} {placementTransfer.lastName}", + isDeputy = isDeputy }); } await _context.SaveChangesAsync(); From 5219934e052dd868b9b35027ad0c1a018db529a0 Mon Sep 17 00:00:00 2001 From: harid Date: Tue, 20 Jan 2026 17:30:11 +0700 Subject: [PATCH 055/178] fix #2207 --- BMA.EHR.Placement.Service/Requests/OrgRequest.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/BMA.EHR.Placement.Service/Requests/OrgRequest.cs b/BMA.EHR.Placement.Service/Requests/OrgRequest.cs index 817c583f..c943bc53 100644 --- a/BMA.EHR.Placement.Service/Requests/OrgRequest.cs +++ b/BMA.EHR.Placement.Service/Requests/OrgRequest.cs @@ -52,6 +52,7 @@ namespace BMA.EHR.Placement.Service.Requests public string? education { get; set; } public double? Amount { get; set; } public string? avatarUrl { get; set; } + public bool? isDeputy { get; set; } } } \ No newline at end of file From 27b3773c79bc85f3c2b50c78c32034758a292982 Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Tue, 20 Jan 2026 20:54:47 +0700 Subject: [PATCH 056/178] load test script --- dotnet_leave_test.js | 54 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 dotnet_leave_test.js diff --git a/dotnet_leave_test.js b/dotnet_leave_test.js new file mode 100644 index 00000000..5add397d --- /dev/null +++ b/dotnet_leave_test.js @@ -0,0 +1,54 @@ +// ทดสอบการยิง 30,000 requests ในเวลา 10 นาที โดยให้กระจายการยิงในเวลาที่ต่างๆ กัน + +import { check, sleep } from "k6"; +import http from "k6/http"; +import { Rate } from "k6/metrics"; + +export let errorRate = new Rate("errors"); + +// จำนวน request ที่ต้องการยิง + +// ระยะเวลาทดสอบทั้งหมด + +// จำนวน Virtual Users เฉลี่ยที่ต้องการ 300 users +//const averageVus = Math.ceil(totalRequests / totalDuration); +const averageVus = 300; + +export let options = { + stages: [ + { duration: "2m", target: averageVus * 0.5 }, // 20% ของการทดสอบ เพิ่ม VUs เป็น 50% ของค่าเฉลี่ย + { duration: "4m", target: averageVus }, // 40% ของการทดสอบ เพิ่ม VUs เป็น 100% ของค่าเฉลี่ย + { duration: "2m", target: averageVus * 1.5 }, // 20% ของการทดสอบ เพิ่ม VUs เป็น 150% ของค่าเฉลี่ย + { duration: "2m", target: 0 }, // ลด VUs ลงมาเป็น 0 + ], + thresholds: { + errors: ["rate<0.01"], // อัตรา error ต้องน้อยกว่า 1% + http_req_duration: ["p(95)<2000"], // 95% ของ requests ควรใช้เวลาไม่เกิน 2 วินาที + }, +}; + +export default function () { + // ตัวเลือก headers + let headers = { + "Content-Type": "application/json", + Authorization: + "Bearer eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJ4WTJWUi1FRnZ2TlBzTXMzOXU4b29WQldRTDZtUHdyTkpPaDNrb0pGVGdVIn0.eyJleHAiOjE3NzYyMTkxNjgsImlhdCI6MTc2ODQ0MzE2OCwianRpIjoiZDQxMmI5MWEtZmZhMi00N2JiLTliZDUtZDE5NTdmMDFjYzQyIiwiaXNzIjoiaHR0cHM6Ly9ocm1zLWlkLmJhbmdrb2suZ28udGgvcmVhbG1zL2hybXMiLCJhdWQiOiJhY2NvdW50Iiwic3ViIjoiYmFmYzU3OTUtYmVmYy00ZDNmLWE0NjEtMzUzM2MzOGE1ZmMxIiwidHlwIjoiQmVhcmVyIiwiYXpwIjoiZ2V0dG9rZW4tY2hlY2tpbiIsInNpZCI6IjBkNzdiY2Y5LTE4YWQtNGQyMS1hYjBjLTI4Y2ZiZjUyZGZiNCIsImFjciI6IjEiLCJhbGxvd2VkLW9yaWdpbnMiOlsiaHR0cHM6Ly9ocm1zLmJhbmdrb2suZ28udGgiLCJodHRwczovL2hybXMtY2hlY2tpbi5iYW5na29rLmdvLnRoIl0sInJlYWxtX2FjY2VzcyI6eyJyb2xlcyI6WyJTVVBFUl9BRE1JTiIsInN0b3JhZ2VfbWFuYWdlbWVudCIsIm9mZmxpbmVfYWNjZXNzIiwiU1RBRkYiLCJkZWZhdWx0LXJvbGVzLWhybXMiLCJ1bWFfYXV0aG9yaXphdGlvbiIsIkFETUlOIiwiVVNFUiJdfSwicmVzb3VyY2VfYWNjZXNzIjp7ImFjY291bnQiOnsicm9sZXMiOlsibWFuYWdlLWFjY291bnQiLCJtYW5hZ2UtYWNjb3VudC1saW5rcyIsInZpZXctcHJvZmlsZSJdfX0sInNjb3BlIjoiZW1haWwgb3BlbmlkIHByb2ZpbGUiLCJlbWFpbF92ZXJpZmllZCI6ZmFsc2UsInJvbGUiOlsiU1VQRVJfQURNSU4iLCJzdG9yYWdlX21hbmFnZW1lbnQiLCJvZmZsaW5lX2FjY2VzcyIsIlNUQUZGIiwiZGVmYXVsdC1yb2xlcy1ocm1zIiwidW1hX2F1dGhvcml6YXRpb24iLCJBRE1JTiIsIlVTRVIiXSwibmFtZSI6IuC4p-C4seC4meC5gOC4ieC4peC4tOC4oSDguInguLHguJXguKPguJfguK3guIciLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiIzMTIwMjAwNDI0OTc1IiwiZ2l2ZW5fbmFtZSI6IuC4p-C4seC4meC5gOC4ieC4peC4tOC4oSIsImZhbWlseV9uYW1lIjoi4LiJ4Lix4LiV4Lij4LiX4Lit4LiHIn0.UhMn0NEkymPxMAcb4noZedHCSqXotCyD2RziBtLYHn5OhA9yk1915Rrt9iV4wVaebr74iZ2eZMpBwp8YVy8-3cPXSv9T3vzbXwFP7IeICPCDDf4bOPFEHP5FYow2s9v48qG81wnu01AG7_EL2-CQKh1sBVrCVUUlATlf-P4lT_lHeHOCKNXTmw4V0IWm96ec6pk-jFY3KH2JdRSWR7wq8g-KVxhLOxk_pF72kMwOpdvcr_99byg28zzj6QfeNYXLt61koHXnZppUqytt86mQQgfamv2FNVywCEzbRITUceu2rmJnwQE8ubeoCh4UOsYauUuSKd7RPqvvXxL_Vg__8Q", + //"Bearer eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJTT2wwWmFidm9rRzZET3pDZVBtT09Kek5haTdMUldkci1zV3lEYjRELTc0In0.eyJleHAiOjE3Njg4ODAzMjgsImlhdCI6MTc2ODc5MzkyOCwianRpIjoiMDYxODBlMWYtNTQzYy00MjU0LWFmN2QtYWI1NDA5NzFmNWY2IiwiaXNzIjoiaHR0cHM6Ly9ocm1zYmtrLWlkLmNhc2UtY29sbGVjdGlvbi5jb20vcmVhbG1zL2hybXMiLCJhdWQiOlsiYWNjb3VudCIsImdldHRva2VuIl0sInN1YiI6IjQzOWZhMzZkLTZiYzUtNGVmNS05NWFhLWVmMjllNjRkMmU5ZiIsInR5cCI6IkJlYXJlciIsImF6cCI6ImdldHRva2VuIiwic2lkIjoiZGI2YzUxNjItNzZhYS00MmVmLWI0ZDMtYThmOTk2N2NjZWM2IiwiYWNyIjoiMSIsImFsbG93ZWQtb3JpZ2lucyI6WyIqIl0sInJlYWxtX2FjY2VzcyI6eyJyb2xlcyI6WyJTVVBFUl9BRE1JTiIsIm9mZmxpbmVfYWNjZXNzIiwiU1RBRkYiLCJkZWZhdWx0LXJvbGVzLWhybXMiLCJ1bWFfYXV0aG9yaXphdGlvbiIsIkFETUlOIiwiVVNFUiJdfSwicmVzb3VyY2VfYWNjZXNzIjp7ImFjY291bnQiOnsicm9sZXMiOlsibWFuYWdlLWFjY291bnQiLCJtYW5hZ2UtYWNjb3VudC1saW5rcyIsInZpZXctcHJvZmlsZSJdfX0sInNjb3BlIjoiZW1haWwgb3BlbmlkIHByb2ZpbGUiLCJlbWFpbF92ZXJpZmllZCI6ZmFsc2UsInJvbGUiOlsiU1VQRVJfQURNSU4iLCJvZmZsaW5lX2FjY2VzcyIsIlNUQUZGIiwiZGVmYXVsdC1yb2xlcy1ocm1zIiwidW1hX2F1dGhvcml6YXRpb24iLCJBRE1JTiIsIlVTRVIiXSwibmFtZSI6IuC4p-C4seC4meC5gOC4ieC4peC4tOC4oSDguInguLHguJXguKPguJfguK3guIciLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiIzMTIwMjAwNDI0OTc1IiwiZ2l2ZW5fbmFtZSI6IuC4p-C4seC4meC5gOC4ieC4peC4tOC4oSIsImZhbWlseV9uYW1lIjoi4LiJ4Lix4LiV4Lij4LiX4Lit4LiHIn0.fHdMzpHMD4JcbzYnUrfM473FSXka2Z4lz_S3HI2c-dPXfO5ATpijqsi12C6-ExE0RJRXUK671erMuyVXL6u2qj-FvdliBL3ubKy4J3jIT3svkcZxZL2ib16dRg375dITefvqd-J4vw6MR4bq8YAGPbqRIy6BQ2pdEiZgNiwUUihHAFwZlVER1lNbaqlbL6vk_L4k-g25DBVnDr756BFvrw7zEDbawkKZ31EZF5_DYk4RWej0wvWrGHQWLw-RyzYVSBB_AooqHkncHn_CwLBGC5juOEfFO4a2ThuKwoxYCstjtBj-zmjpHFs-Hh3CBTWJCGFcKst1Ey28StlKtNkLiw", + }; + + // ส่ง GET request + let response = http.get( + //"https://bma-hrms.bangkok.go.th/api/v1/leave/fake-check-in", + //"https://hrmsbkk.case-collection.com/api/v1/org/dotnet/keycloak/439fa36d-6bc5-4ef5-95aa-ef29e64d2e9f", + "https://hrms.bangkok.go.th/api/v1/leave/check-time", + { headers: headers }, + ); + + // ตรวจสอบการตอบสนอง + check(response, { + "is status 200": (r) => r.status === 200, + }); + + // หน่วงเวลา 1 วินาที + sleep(1); +} From d3501e831c4e8ebe4fd20c42af50cea2b78830f5 Mon Sep 17 00:00:00 2001 From: harid Date: Wed, 21 Jan 2026 17:00:45 +0700 Subject: [PATCH 057/178] =?UTF-8?q?fix=20=E0=B8=81=E0=B8=88.=20=E0=B9=84?= =?UTF-8?q?=E0=B8=A1=E0=B9=88=E0=B9=80=E0=B8=AB=E0=B9=87=E0=B8=99=E0=B8=A3?= =?UTF-8?q?=E0=B8=B2=E0=B8=A2=E0=B8=8A=E0=B8=B7=E0=B9=88=E0=B8=AD=E0=B8=9C?= =?UTF-8?q?=E0=B8=B9=E0=B9=89=E0=B8=AA=E0=B8=AD=E0=B8=9A=E0=B8=9C=E0=B9=88?= =?UTF-8?q?=E0=B8=B2=E0=B8=99=E0=B8=AB=E0=B8=A5=E0=B8=B1=E0=B8=87=E0=B8=88?= =?UTF-8?q?=E0=B8=B2=E0=B8=81=E0=B9=80=E0=B8=9C=E0=B8=A2=E0=B9=81=E0=B8=9E?= =?UTF-8?q?=E0=B8=A3=E0=B9=88=E0=B9=82=E0=B8=84=E0=B8=A3=E0=B8=87=E0=B8=AA?= =?UTF-8?q?=E0=B8=A3=E0=B9=89=E0=B8=B2=E0=B8=87=20Task=20#2219?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Controllers/PlacementController.cs | 18 +++++++++++------- .../Requests/OrgRequestAct.cs | 1 + 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/BMA.EHR.Placement.Service/Controllers/PlacementController.cs b/BMA.EHR.Placement.Service/Controllers/PlacementController.cs index 62b9bbce..a485a42d 100644 --- a/BMA.EHR.Placement.Service/Controllers/PlacementController.cs +++ b/BMA.EHR.Placement.Service/Controllers/PlacementController.cs @@ -155,6 +155,7 @@ namespace BMA.EHR.Placement.Service.Controllers var child2Id = ""; var child3Id = ""; var child4Id = ""; + var rootDnaId = ""; var apiUrl = $"{_configuration["API"]}/org/profile/keycloak/position-act"; using (var client = new HttpClient()) { @@ -173,11 +174,12 @@ namespace BMA.EHR.Placement.Service.Controllers // child2Id = org.result.child2Id == null ? "" : org.result.child2Id; // child3Id = org.result.child3Id == null ? "" : org.result.child3Id; // child4Id = org.result.child4Id == null ? "" : org.result.child4Id; + rootDnaId = org.result.rootDnaId == null ? "" : org.result.rootDnaId; var data1 = await _context.PlacementProfiles .Where(x => x.Placement.Id == examId) .Where(x => x.Draft == true) .Where(x => x.PlacementStatus != "UN-CONTAIN") - .Where(x => rootId == "" ? true : (child1Id == "" ? x.rootId == rootId : (child2Id == "" ? x.child1Id == child1Id : (child3Id == "" ? x.child2Id == child2Id : (child4Id == "" ? x.child3Id == child3Id : x.child4Id == child4Id))))) + .Where(x => rootDnaId == "" ? true : (child1Id == "" ? x.rootDnaId == rootDnaId : (child2Id == "" ? x.child1Id == child1Id : (child3Id == "" ? x.child2Id == child2Id : (child4Id == "" ? x.child3Id == child3Id : x.child4Id == child4Id))))) .Select(x => new { Id = x.Id, @@ -694,6 +696,7 @@ namespace BMA.EHR.Placement.Service.Controllers var child2Id = ""; var child3Id = ""; var child4Id = ""; + var rootDnaId = ""; var apiUrl = $"{_configuration["API"]}/org/profile/keycloak/position-act"; using (var client = new HttpClient()) { @@ -713,16 +716,17 @@ namespace BMA.EHR.Placement.Service.Controllers // child2Id = org.result.child2Id == null ? "" : org.result.child2Id; // child3Id = org.result.child3Id == null ? "" : org.result.child3Id; // child4Id = org.result.child4Id == null ? "" : org.result.child4Id; + rootDnaId = org.result.rootDnaId == null ? "" : org.result.rootDnaId; var placement = await _context.Placements .Where(x => x.Id == examId) .Select(x => new { - Total = x.PlacementProfiles.Where(x => x.Draft == true).Where(x => rootId == "" ? true : (child1Id == "" ? x.rootId == rootId : (child2Id == "" ? x.child1Id == child1Id : (child3Id == "" ? x.child2Id == child2Id : (child4Id == "" ? x.child3Id == child3Id : x.child4Id == child4Id))))).Count(), - UnContain = x.PlacementProfiles.Where(x => x.Draft == true).Where(x => rootId == "" ? true : (child1Id == "" ? x.rootId == rootId : (child2Id == "" ? x.child1Id == child1Id : (child3Id == "" ? x.child2Id == child2Id : (child4Id == "" ? x.child3Id == child3Id : x.child4Id == child4Id))))).Where(p => p.PlacementStatus.Trim().ToUpper() == "UN-CONTAIN").Count(), - PrepareContain = x.PlacementProfiles.Where(x => x.Draft == true).Where(x => rootId == "" ? true : (child1Id == "" ? x.rootId == rootId : (child2Id == "" ? x.child1Id == child1Id : (child3Id == "" ? x.child2Id == child2Id : (child4Id == "" ? x.child3Id == child3Id : x.child4Id == child4Id))))).Where(p => p.PlacementStatus.Trim().ToUpper() == "PREPARE-CONTAIN").Count(), - Report = x.PlacementProfiles.Where(x => x.Draft == true).Where(x => rootId == "" ? true : (child1Id == "" ? x.rootId == rootId : (child2Id == "" ? x.child1Id == child1Id : (child3Id == "" ? x.child2Id == child2Id : (child4Id == "" ? x.child3Id == child3Id : x.child4Id == child4Id))))).Where(p => p.PlacementStatus.Trim().ToUpper() == "REPORT").Count(), - Done = x.PlacementProfiles.Where(x => x.Draft == true).Where(x => rootId == "" ? true : (child1Id == "" ? x.rootId == rootId : (child2Id == "" ? x.child1Id == child1Id : (child3Id == "" ? x.child2Id == child2Id : (child4Id == "" ? x.child3Id == child3Id : x.child4Id == child4Id))))).Where(p => p.PlacementStatus.Trim().ToUpper() == "DONE").Count(), - Disclaim = x.PlacementProfiles.Where(x => x.Draft == true).Where(x => rootId == "" ? true : (child1Id == "" ? x.rootId == rootId : (child2Id == "" ? x.child1Id == child1Id : (child3Id == "" ? x.child2Id == child2Id : (child4Id == "" ? x.child3Id == child3Id : x.child4Id == child4Id))))).Where(p => p.PlacementStatus.Trim().ToUpper() == "DISCLAIM").Count(), + Total = x.PlacementProfiles.Where(x => x.Draft == true).Where(x => rootDnaId == "" ? true : (child1Id == "" ? x.rootDnaId == rootDnaId : (child2Id == "" ? x.child1Id == child1Id : (child3Id == "" ? x.child2Id == child2Id : (child4Id == "" ? x.child3Id == child3Id : x.child4Id == child4Id))))).Count(), + UnContain = x.PlacementProfiles.Where(x => x.Draft == true).Where(x => rootDnaId == "" ? true : (child1Id == "" ? x.rootDnaId == rootDnaId : (child2Id == "" ? x.child1Id == child1Id : (child3Id == "" ? x.child2Id == child2Id : (child4Id == "" ? x.child3Id == child3Id : x.child4Id == child4Id))))).Where(p => p.PlacementStatus.Trim().ToUpper() == "UN-CONTAIN").Count(), + PrepareContain = x.PlacementProfiles.Where(x => x.Draft == true).Where(x => rootDnaId == "" ? true : (child1Id == "" ? x.rootDnaId == rootDnaId : (child2Id == "" ? x.child1Id == child1Id : (child3Id == "" ? x.child2Id == child2Id : (child4Id == "" ? x.child3Id == child3Id : x.child4Id == child4Id))))).Where(p => p.PlacementStatus.Trim().ToUpper() == "PREPARE-CONTAIN").Count(), + Report = x.PlacementProfiles.Where(x => x.Draft == true).Where(x => rootDnaId == "" ? true : (child1Id == "" ? x.rootDnaId == rootDnaId : (child2Id == "" ? x.child1Id == child1Id : (child3Id == "" ? x.child2Id == child2Id : (child4Id == "" ? x.child3Id == child3Id : x.child4Id == child4Id))))).Where(p => p.PlacementStatus.Trim().ToUpper() == "REPORT").Count(), + Done = x.PlacementProfiles.Where(x => x.Draft == true).Where(x => rootDnaId == "" ? true : (child1Id == "" ? x.rootDnaId == rootDnaId : (child2Id == "" ? x.child1Id == child1Id : (child3Id == "" ? x.child2Id == child2Id : (child4Id == "" ? x.child3Id == child3Id : x.child4Id == child4Id))))).Where(p => p.PlacementStatus.Trim().ToUpper() == "DONE").Count(), + Disclaim = x.PlacementProfiles.Where(x => x.Draft == true).Where(x => rootDnaId == "" ? true : (child1Id == "" ? x.rootDnaId == rootDnaId : (child2Id == "" ? x.child1Id == child1Id : (child3Id == "" ? x.child2Id == child2Id : (child4Id == "" ? x.child3Id == child3Id : x.child4Id == child4Id))))).Where(p => p.PlacementStatus.Trim().ToUpper() == "DISCLAIM").Count(), }).FirstOrDefaultAsync(); if (placement == null) return Error(GlobalMessages.DataNotFound, 404); diff --git a/BMA.EHR.Placement.Service/Requests/OrgRequestAct.cs b/BMA.EHR.Placement.Service/Requests/OrgRequestAct.cs index dace91d3..6189eaf4 100644 --- a/BMA.EHR.Placement.Service/Requests/OrgRequestAct.cs +++ b/BMA.EHR.Placement.Service/Requests/OrgRequestAct.cs @@ -15,5 +15,6 @@ namespace BMA.EHR.Placement.Service.Requests public string? child2Id { get; set; } public string? child3Id { get; set; } public string? child4Id { get; set; } + public string? rootDnaId { get; set; } } } \ No newline at end of file From d945deae4fdaa952966a39ebe055e1e88309907e Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Thu, 22 Jan 2026 11:58:26 +0700 Subject: [PATCH 058/178] Add error handling for permission API calls and enhance logging in middleware --- .../Repositories/PermissionRepository.cs | 4 + .../Repositories/UserProfileRepository.cs | 24 ++++++ ...ombinedErrorHandlerAndLoggingMiddleware.cs | 84 ++++++++++--------- 3 files changed, 72 insertions(+), 40 deletions(-) diff --git a/BMA.EHR.Application/Repositories/PermissionRepository.cs b/BMA.EHR.Application/Repositories/PermissionRepository.cs index d5ac981a..84191f91 100644 --- a/BMA.EHR.Application/Repositories/PermissionRepository.cs +++ b/BMA.EHR.Application/Repositories/PermissionRepository.cs @@ -62,6 +62,10 @@ namespace BMA.EHR.Application.Repositories new AuthenticationHeaderValue("Bearer", AccessToken.Replace("Bearer ", "")); client.DefaultRequestHeaders.Add("api-key", _configuration["API_KEY"]); var req = await client.GetAsync(apiPath); + if (!req.IsSuccessStatusCode) + { + throw new Exception("Error calling permission API"); + } var res = await req.Content.ReadAsStringAsync(); return res; } diff --git a/BMA.EHR.Application/Repositories/UserProfileRepository.cs b/BMA.EHR.Application/Repositories/UserProfileRepository.cs index 1c9d5596..9ef3f76e 100644 --- a/BMA.EHR.Application/Repositories/UserProfileRepository.cs +++ b/BMA.EHR.Application/Repositories/UserProfileRepository.cs @@ -186,6 +186,30 @@ namespace BMA.EHR.Application.Repositories } } + public async Task GetProfileByKeycloakIdNewAsync(Guid keycloakId, string? accessToken) + { + try + { + var apiPath = $"{_configuration["API"]}/org/dotnet/by-keycloak/{keycloakId}"; + var apiKey = _configuration["API_KEY"]; + + var apiResult = await GetExternalAPIAsync(apiPath, accessToken ?? "", apiKey); + if (apiResult != null) + { + var raw = JsonConvert.DeserializeObject(apiResult); + if (raw != null) + return raw.Result; + } + + return null; + } + catch + { + throw; + } + } + + public async Task GetProfileLeaveByKeycloakIdAsync(Guid keycloakId, string? accessToken) { try diff --git a/BMA.EHR.Domain/Middlewares/CombinedErrorHandlerAndLoggingMiddleware.cs b/BMA.EHR.Domain/Middlewares/CombinedErrorHandlerAndLoggingMiddleware.cs index 5f5aa2af..3391e6a3 100644 --- a/BMA.EHR.Domain/Middlewares/CombinedErrorHandlerAndLoggingMiddleware.cs +++ b/BMA.EHR.Domain/Middlewares/CombinedErrorHandlerAndLoggingMiddleware.cs @@ -144,12 +144,22 @@ namespace BMA.EHR.Domain.Middlewares { stopwatch.Stop(); + // อ่านข้อมูล response ก่อนที่ stream จะถูก dispose + string? responseBodyForLogging = null; + if (memoryStream.Length > 0) + { + memoryStream.Seek(0, SeekOrigin.Begin); + using var reader = new StreamReader(memoryStream, leaveOpen: true); + responseBodyForLogging = await reader.ReadToEndAsync(); + memoryStream.Seek(0, SeekOrigin.Begin); + } + // ทำ logging แบบ fire-and-forget เพื่อไม่ block response _ = Task.Run(async () => { try { - await LogRequestAsync(context, _elasticClient!, startTime, stopwatch, pf, keycloakId, requestBodyJson, memoryStream, caughtException); + await LogRequestAsync(context, _elasticClient!, startTime, stopwatch, pf, keycloakId, requestBodyJson, responseBodyForLogging, caughtException); } catch (Exception ex) { @@ -393,7 +403,7 @@ namespace BMA.EHR.Domain.Middlewares } private async Task LogRequestAsync(HttpContext context, ElasticClient client, DateTime startTime, Stopwatch stopwatch, - GetProfileByKeycloakIdLocal? pf, string keycloakId, string? requestBodyJson, MemoryStream memoryStream, Exception? caughtException) + GetProfileByKeycloakIdLocal? pf, string keycloakId, string? requestBodyJson, string? responseBodyForLogging, Exception? caughtException) { try { @@ -412,48 +422,42 @@ namespace BMA.EHR.Domain.Middlewares string? message = null; string? responseBodyJson = null; - // อ่านข้อมูลจาก Response (ลด serialization ที่ซ้ำ) - if (memoryStream.Length > 0) + // ใช้ response body ที่ส่งมาจากการอ่านก่อนหน้า + if (!string.IsNullOrEmpty(responseBodyForLogging)) { - memoryStream.Seek(0, SeekOrigin.Begin); - var responseBody = new StreamReader(memoryStream).ReadToEnd(); + var contentType = context.Response.ContentType ?? ""; + var isFileResponse = !contentType.StartsWith("application/json") && !contentType.StartsWith("text/html") && ( + contentType.StartsWith("application/") || + contentType.StartsWith("image/") || + contentType.StartsWith("audio/") || + context.Response.Headers.ContainsKey("Content-Disposition") + ); - if (!string.IsNullOrEmpty(responseBody)) + if (isFileResponse) { - var contentType = context.Response.ContentType ?? ""; - var isFileResponse = !contentType.StartsWith("application/json") && !contentType.StartsWith("text/html") && ( - contentType.StartsWith("application/") || - contentType.StartsWith("image/") || - contentType.StartsWith("audio/") || - context.Response.Headers.ContainsKey("Content-Disposition") - ); - - if (isFileResponse) + responseBodyJson = ""; + message = "success"; + } + else + { + // ใช้ response body ที่มีอยู่แล้วโดยไม่ serialize ซ้ำ + responseBodyJson = responseBodyForLogging; + + try { - responseBodyJson = ""; - message = "success"; + var json = JsonSerializer.Deserialize(responseBodyForLogging); + if (json.ValueKind == JsonValueKind.Array) + { + message = "success"; + } + else if (json.TryGetProperty("message", out var messageElement)) + { + message = messageElement.GetString(); + } } - else + catch { - // ใช้ response body ที่มีอยู่แล้วโดยไม่ serialize ซ้ำ - responseBodyJson = responseBody; - - try - { - var json = JsonSerializer.Deserialize(responseBody); - if (json.ValueKind == JsonValueKind.Array) - { - message = "success"; - } - else if (json.TryGetProperty("message", out var messageElement)) - { - message = messageElement.GetString(); - } - } - catch - { - message = caughtException?.Message ?? "Unknown error"; - } + message = caughtException?.Message ?? "Unknown error"; } } } @@ -467,7 +471,7 @@ namespace BMA.EHR.Domain.Middlewares { logType = logType, ip = context.Connection.RemoteIpAddress?.ToString(), - rootId = pf?.RootId, + rootId = pf?.RootDnaId, systemName = SystemName, startTimeStamp = startTime.ToString("o"), endTimeStamp = endTime.ToString("o"), @@ -660,7 +664,7 @@ namespace BMA.EHR.Domain.Middlewares { try { - var apiPath = $"{_configuration["API"]}/org/dotnet/keycloak/{keycloakId}"; + var apiPath = $"{_configuration["API"]}/org/dotnet/by-keycloak/{keycloakId}"; var apiKey = _configuration["API_KEY"]; var apiResult = await GetExternalAPIAsync(apiPath, accessToken ?? "", apiKey); From 2b737de23b29766c65e398fed4b598dcdfce4c40 Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Thu, 22 Jan 2026 12:24:27 +0700 Subject: [PATCH 059/178] Update ElasticConfiguration URIs and IndexFormats in appsettings for Leave and Insignia --- ...ombinedErrorHandlerAndLoggingMiddleware.cs | 72 ++++++++++++------- BMA.EHR.Insignia/appsettings.json | 4 +- BMA.EHR.Leave/appsettings.json | 4 +- 3 files changed, 50 insertions(+), 30 deletions(-) diff --git a/BMA.EHR.Domain/Middlewares/CombinedErrorHandlerAndLoggingMiddleware.cs b/BMA.EHR.Domain/Middlewares/CombinedErrorHandlerAndLoggingMiddleware.cs index 3391e6a3..57ff69dd 100644 --- a/BMA.EHR.Domain/Middlewares/CombinedErrorHandlerAndLoggingMiddleware.cs +++ b/BMA.EHR.Domain/Middlewares/CombinedErrorHandlerAndLoggingMiddleware.cs @@ -154,25 +154,37 @@ namespace BMA.EHR.Domain.Middlewares memoryStream.Seek(0, SeekOrigin.Begin); } - // ทำ logging แบบ fire-and-forget เพื่อไม่ block response - _ = Task.Run(async () => + // เก็บข้อมูลที่จำเป็นจาก HttpContext ก่อนที่มันจะถูก dispose + var logData = new { - try - { - await LogRequestAsync(context, _elasticClient!, startTime, stopwatch, pf, keycloakId, requestBodyJson, responseBodyForLogging, caughtException); - } - catch (Exception ex) - { - Console.WriteLine($"Background logging error: {ex.Message}"); - } - }); - - // เขียนข้อมูลกลับไปยัง original Response body + RemoteIpAddress = context.Connection.RemoteIpAddress?.ToString(), + HostValue = context.Request.Host.Value, + Method = context.Request.Method, + Path = context.Request.Path.ToString(), + QueryString = context.Request.QueryString.ToString(), + StatusCode = context.Response.StatusCode, + ContentType = context.Response.ContentType ?? "" + }; + + // เขียนข้อมูลกลับไปยัง original Response body ก่อน if (memoryStream.Length > 0) { memoryStream.Seek(0, SeekOrigin.Begin); await memoryStream.CopyToAsync(originalBodyStream); } + + // ทำ logging แบบ await + Console.WriteLine("[DEBUG] Starting logging..."); + try + { + await LogRequestAsync(_elasticClient!, startTime, stopwatch, pf, keycloakId, requestBodyJson, responseBodyForLogging, caughtException, logData); + Console.WriteLine("[DEBUG] Logging completed successfully"); + } + catch (Exception ex) + { + Console.WriteLine($"[ERROR] Logging error: {ex.Message}"); + Console.WriteLine($"[ERROR] Stack trace: {ex.StackTrace}"); + } } } @@ -402,15 +414,16 @@ namespace BMA.EHR.Domain.Middlewares } } - private async Task LogRequestAsync(HttpContext context, ElasticClient client, DateTime startTime, Stopwatch stopwatch, - GetProfileByKeycloakIdLocal? pf, string keycloakId, string? requestBodyJson, string? responseBodyForLogging, Exception? caughtException) + private async Task LogRequestAsync(ElasticClient client, DateTime startTime, Stopwatch stopwatch, + GetProfileByKeycloakIdLocal? pf, string keycloakId, string? requestBodyJson, string? responseBodyForLogging, Exception? caughtException, dynamic contextData) { + Console.WriteLine("[DEBUG] LogRequestAsync called"); try { var processTime = stopwatch.ElapsedMilliseconds; var endTime = DateTime.UtcNow; - var statusCode = caughtException != null ? (int)HttpStatusCode.InternalServerError : context.Response.StatusCode; + var statusCode = caughtException != null ? (int)HttpStatusCode.InternalServerError : (int)contextData.StatusCode; var logType = caughtException != null ? "error" : statusCode switch { @@ -425,12 +438,11 @@ namespace BMA.EHR.Domain.Middlewares // ใช้ response body ที่ส่งมาจากการอ่านก่อนหน้า if (!string.IsNullOrEmpty(responseBodyForLogging)) { - var contentType = context.Response.ContentType ?? ""; + var contentType = (string)contextData.ContentType; var isFileResponse = !contentType.StartsWith("application/json") && !contentType.StartsWith("text/html") && ( contentType.StartsWith("application/") || contentType.StartsWith("image/") || - contentType.StartsWith("audio/") || - context.Response.Headers.ContainsKey("Content-Disposition") + contentType.StartsWith("audio/") ); if (isFileResponse) @@ -470,15 +482,15 @@ namespace BMA.EHR.Domain.Middlewares var logData = new { logType = logType, - ip = context.Connection.RemoteIpAddress?.ToString(), - rootId = pf?.RootDnaId, + ip = (string)contextData.RemoteIpAddress, + rootId = pf?.RootId, systemName = SystemName, startTimeStamp = startTime.ToString("o"), endTimeStamp = endTime.ToString("o"), processTime = processTime, - host = context.Request.Host.Value, - method = context.Request.Method, - endpoint = context.Request.Path + context.Request.QueryString, + host = (string)contextData.HostValue, + method = (string)contextData.Method, + endpoint = (string)contextData.Path + (string)contextData.QueryString, responseCode = statusCode == 304 ? "200" : statusCode.ToString(), responseDescription = message, input = requestBodyJson, @@ -489,11 +501,19 @@ namespace BMA.EHR.Domain.Middlewares exception = caughtException?.ToString() }; - await client.IndexDocumentAsync(logData); + Console.WriteLine($"[DEBUG] Sending log to Elasticsearch: {logType} - {(string)contextData.Method} {(string)contextData.Path}"); + var response = await client.IndexDocumentAsync(logData); + Console.WriteLine($"[DEBUG] Elasticsearch response: IsValid={response.IsValid}, Index={response.Index}"); + + if (!response.IsValid) + { + Console.WriteLine($"[ERROR] Elasticsearch error: {response.OriginalException?.Message ?? response.ServerError?.ToString()}"); + } } catch (Exception ex) { - Console.WriteLine($"Error logging request: {ex.Message}"); + Console.WriteLine($"[ERROR] Error logging request: {ex.Message}"); + Console.WriteLine($"[ERROR] Stack trace: {ex.StackTrace}"); } } diff --git a/BMA.EHR.Insignia/appsettings.json b/BMA.EHR.Insignia/appsettings.json index 3d74b2ae..4f3d3faf 100644 --- a/BMA.EHR.Insignia/appsettings.json +++ b/BMA.EHR.Insignia/appsettings.json @@ -9,8 +9,8 @@ } }, "ElasticConfiguration": { - "Uri": "http://192.168.1.40:9200", - "IndexFormat": "bma-ehr-log-index", + "Uri": "http://192.168.1.63:9200", + "IndexFormat": "hrms-log-index", "SystemName": "insignia" }, "AllowedHosts": "*", diff --git a/BMA.EHR.Leave/appsettings.json b/BMA.EHR.Leave/appsettings.json index d1f58a39..f8562d6e 100644 --- a/BMA.EHR.Leave/appsettings.json +++ b/BMA.EHR.Leave/appsettings.json @@ -9,8 +9,8 @@ } }, "ElasticConfiguration": { - "Uri": "http://192.168.1.40:9200", - "IndexFormat": "bma-ehr-log-index", + "Uri": "http://192.168.1.63:9200", + "IndexFormat": "hrms-log-index", "SystemName": "leave" }, "AllowedHosts": "*", From 2f366374fabac7b33d19da5b7fa87fd46ca67370 Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Thu, 22 Jan 2026 12:43:52 +0700 Subject: [PATCH 060/178] Refactor user profile retrieval to use new method GetProfileByKeycloakIdNewAsync --- BMA.EHR.Leave/Controllers/LeaveController.cs | 26 ++++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/BMA.EHR.Leave/Controllers/LeaveController.cs b/BMA.EHR.Leave/Controllers/LeaveController.cs index b2544898..32085d94 100644 --- a/BMA.EHR.Leave/Controllers/LeaveController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveController.cs @@ -433,7 +433,7 @@ namespace BMA.EHR.Leave.Service.Controllers var userId = UserId == null ? Guid.Empty : Guid.Parse(UserId); var data = await _userTimeStampRepository.GetLastRecord(userId); - var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(userId, AccessToken); + var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(userId, AccessToken); if (profile == null) { throw new Exception(GlobalMessages.DataNotFound); @@ -896,7 +896,7 @@ namespace BMA.EHR.Leave.Service.Controllers await _checkInJobStatusRepository.UpdateToProcessingAsync(taskId); } - var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(userId, data.Token); + var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(userId, data.Token); if (profile == null) return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound); @@ -1206,7 +1206,7 @@ namespace BMA.EHR.Leave.Service.Controllers public async Task> CheckInOldAsync([FromForm] CheckTimeDto data) { var userId = UserId == null ? Guid.Empty : Guid.Parse(UserId); - var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(userId, AccessToken); + var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(userId, AccessToken); if (profile == null) return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound); @@ -1371,7 +1371,7 @@ namespace BMA.EHR.Leave.Service.Controllers { var userId = UserId == null ? Guid.Empty : Guid.Parse(UserId); - var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(userId, AccessToken); + var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(userId, AccessToken); if (profile == null) { return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound); @@ -1660,7 +1660,7 @@ namespace BMA.EHR.Leave.Service.Controllers } else { - var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(d.KeycloakUserId, AccessToken); + var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(d.KeycloakUserId, AccessToken); if (profile == null) { return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound); @@ -2543,7 +2543,7 @@ namespace BMA.EHR.Leave.Service.Controllers var time = DateTime.Now; var userId = UserId != null ? Guid.Parse(UserId) : Guid.Empty; - var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(userId, AccessToken); + var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(userId, AccessToken); if (profile == null) { throw new Exception(GlobalMessages.DataNotFound); @@ -2663,7 +2663,7 @@ namespace BMA.EHR.Leave.Service.Controllers } var userId = UserId != null ? Guid.Parse(UserId) : Guid.Empty; - var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(userId, AccessToken); + var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(userId, AccessToken); if (profile == null) { throw new Exception(GlobalMessages.DataNotFound); @@ -2826,7 +2826,7 @@ namespace BMA.EHR.Leave.Service.Controllers foreach (var data in rawDataPaged) { - var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(data.KeycloakUserId, AccessToken); + var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(data.KeycloakUserId, AccessToken); if (profile == null) { return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound); @@ -3006,7 +3006,7 @@ namespace BMA.EHR.Leave.Service.Controllers // change user timestamp var processTimeStamp = await _processUserTimeStampRepository.GetTimestampByDateAsync(requestData.KeycloakUserId, requestData.CheckDate.Date); - var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(requestData.KeycloakUserId, AccessToken); + var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(requestData.KeycloakUserId, AccessToken); if (processTimeStamp == null) { @@ -3160,7 +3160,7 @@ namespace BMA.EHR.Leave.Service.Controllers requestData.Comment = req.Reason; await _additionalCheckRequestRepository.UpdateAsync(requestData); - var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(requestData.KeycloakUserId, AccessToken); + var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(requestData.KeycloakUserId, AccessToken); var recvId = new List { profile.Id }; await _notificationRepository.PushNotificationsAsync(recvId.ToArray(), "ลงเวลากรณีพิเศษ", "การขอลงเวลากรณีพิเศษของคุณไม่ได้รับการอนุมัติ", "", "", true, false); @@ -3204,7 +3204,7 @@ namespace BMA.EHR.Leave.Service.Controllers } else { - var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(d.KeycloakUserId, AccessToken); + var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(d.KeycloakUserId, AccessToken); if (profile == null) { return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound); @@ -3298,7 +3298,7 @@ namespace BMA.EHR.Leave.Service.Controllers foreach (var data in rawData) { - var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(data.KeycloakUserId, AccessToken); + var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(data.KeycloakUserId, AccessToken); if (profile == null) { return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound); @@ -3611,7 +3611,7 @@ namespace BMA.EHR.Leave.Service.Controllers [ProducesResponseType(StatusCodes.Status500InternalServerError)] public async Task> GetLeaveSummaryByProfileAsync(Guid id, [FromBody] GetLeaveSummaryDto req) { - var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(id, AccessToken); + var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(id, AccessToken); var thisYear = DateTime.Now.Year; var startDate = req.StartDate; From 4e4eec3d844cea341c3fbb56ff9ee824777e5ccb Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Fri, 23 Jan 2026 09:32:17 +0700 Subject: [PATCH 061/178] Add job status check for pending or processing check-in/check-out requests --- BMA.EHR.Leave/Controllers/LeaveController.cs | 41 +++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/BMA.EHR.Leave/Controllers/LeaveController.cs b/BMA.EHR.Leave/Controllers/LeaveController.cs index 32085d94..8faf42c2 100644 --- a/BMA.EHR.Leave/Controllers/LeaveController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveController.cs @@ -530,6 +530,26 @@ namespace BMA.EHR.Leave.Service.Controllers // prepare data and convert request body and send to queue var userId = UserId == null ? Guid.Empty : Guid.Parse(UserId); var currentDate = DateTime.Now; + + // ตรวจสอบว่ามีงานที่กำลัง pending หรือ processing อยู่หรือไม่ + var existingJobs = await _checkInJobStatusRepository.GetPendingOrProcessingJobsAsync(userId); + if (existingJobs != null && existingJobs.Count > 0) + { + // กรองเฉพาะงานที่เป็นประเภทเดียวกัน (CHECK_IN หรือ CHECK_OUT) + var checkType = data.CheckInId == null ? "CHECK_IN" : "CHECK_OUT"; + var sameTypeJob = existingJobs.FirstOrDefault(j => j.CheckType == checkType); + + if (sameTypeJob != null) + { + // ตรวจสอบว่างานที่มีอยู่ถูกสร้างเมื่อไหร่ ถ้าเกิน 2 นาทีให้สร้างใหม่ได้ + var timeDiff = (currentDate - sameTypeJob.CreatedDate).TotalMinutes; + if (timeDiff < 2) + { + return Error($"มีงาน {checkType} กำลังดำเนินการอยู่ กรุณารอสักครู่", StatusCodes.Status409Conflict); + } + } + } + var checkFileBytes = new byte[0]; // fix issue : ระบบลงเวลาปฏิบัติงาน>>รูปภาพไม่แสดงในฝั่งของ Admin #804 @@ -564,6 +584,8 @@ namespace BMA.EHR.Leave.Service.Controllers }; var channel = _objectPool.Get(); + CheckInJobStatus? jobStatus = null; + try { var queue = _configuration["Rabbit:Queue"] ?? "basic-queue"; @@ -576,7 +598,7 @@ namespace BMA.EHR.Leave.Service.Controllers properties.MessageId = taskId; // บันทึกสถานะงานก่อนส่งไป RabbitMQ - var jobStatus = new CheckInJobStatus + jobStatus = new CheckInJobStatus { TaskId = Guid.Parse(taskId), KeycloakUserId = userId, @@ -593,6 +615,7 @@ namespace BMA.EHR.Leave.Service.Controllers }; await _checkInJobStatusRepository.AddAsync(jobStatus); + // ส่งไป RabbitMQ channel.BasicPublish(exchange: "", routingKey: queue, basicProperties: properties, @@ -600,6 +623,22 @@ namespace BMA.EHR.Leave.Service.Controllers return Success(new { date = currentDate, taskId = taskId, keycloakId = userId }); } + catch (Exception ex) + { + // ถ้าส่งไป queue ไม่สำเร็จ ให้ลบ job status ที่สร้างไว้ออก + if (jobStatus != null) + { + try + { + await _checkInJobStatusRepository.DeleteAsync(jobStatus); + } + catch + { + // Ignore delete error + } + } + throw new Exception($"ไม่สามารถส่งงานไปยัง Queue ได้: {ex.Message}"); + } finally { _objectPool.Return(channel); From e1c7688913936a8e062712e69704d375911c26aa Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Fri, 23 Jan 2026 20:27:22 +0700 Subject: [PATCH 062/178] Update LeaveController to set end times based on duty schedule for check-in/check-out #2228 --- BMA.EHR.Leave/Controllers/LeaveController.cs | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/BMA.EHR.Leave/Controllers/LeaveController.cs b/BMA.EHR.Leave/Controllers/LeaveController.cs index 8faf42c2..7f90fbe1 100644 --- a/BMA.EHR.Leave/Controllers/LeaveController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveController.cs @@ -1015,13 +1015,18 @@ namespace BMA.EHR.Leave.Service.Controllers }; var startTime = ""; + var endTime = ""; if (!data.IsLocation && data.LocationName == "ไปประชุม / อบรม / สัมมนา") { //startTime = "09:30"; startTime = "10:30"; + endTime = "12:00"; } else + { startTime = duty.StartTimeMorning; + endTime = duty.EndTimeMorning; + } string checkInStatus = "NORMAL"; var leaveReq = await _leaveRequestRepository.GetLeavePeriodAsync(userId, currentDate.Date); @@ -1126,15 +1131,22 @@ namespace BMA.EHR.Leave.Service.Controllers } var endTime = ""; + var startTime = ""; + var endTimeMorning = ""; if (!data.IsLocation && data.LocationName == "ไปประชุม / อบรม / สัมมนา") { - //startTime = "09:30"; + startTime = "13:00"; endTime = "14:30"; + endTimeMorning = "12:00"; } else + { endTime = duty.EndTimeAfternoon; + startTime = duty.StartTimeAfternoon; + endTimeMorning = duty.EndTimeMorning; + } - var endTimeMorning = duty.EndTimeMorning; + string checkOutStatus = "NORMAL"; var leaveReq = await _leaveRequestRepository.GetLeavePeriodAsync(userId, currentDate.Date); @@ -1161,7 +1173,7 @@ namespace BMA.EHR.Leave.Service.Controllers "NORMAL" : "ABSENT" : DateTime.Parse(currentDate.ToString("yyyy-MM-dd HH:mm")) < - DateTime.Parse($"{currentDate.ToString("yyyy-MM-dd")} {duty.EndTimeMorning}") ? + DateTime.Parse($"{currentDate.ToString("yyyy-MM-dd")} {endTimeMorning}") ? "ABSENT" : "NORMAL"; } @@ -1178,7 +1190,7 @@ namespace BMA.EHR.Leave.Service.Controllers "NORMAL" : "ABSENT" : DateTime.Parse(currentDate.ToString("yyyy-MM-dd HH:mm")) < - DateTime.Parse($"{currentDate.ToString("yyyy-MM-dd")} {duty.EndTimeMorning}") ? + DateTime.Parse($"{currentDate.ToString("yyyy-MM-dd")} {endTimeMorning}") ? "ABSENT" : "NORMAL"; } From 9bd6017ded190cf9e87a4c31d21bc95304c41fc0 Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Fri, 23 Jan 2026 20:35:54 +0700 Subject: [PATCH 063/178] Update LeaveController to adjust check-in/check-out times based on user duty schedule #2223 --- BMA.EHR.Leave/Controllers/LeaveController.cs | 24 ++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/BMA.EHR.Leave/Controllers/LeaveController.cs b/BMA.EHR.Leave/Controllers/LeaveController.cs index 7f90fbe1..ebfd5c96 100644 --- a/BMA.EHR.Leave/Controllers/LeaveController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveController.cs @@ -3634,6 +3634,30 @@ namespace BMA.EHR.Leave.Service.Controllers var data = await _processUserTimeStampRepository.GetByIdAsync(id); if (data == null) return Error(GlobalMessages.DataNotFound); + + if (data.CheckInStatus == "NORMAL" || data.CheckOutStatus == "NORMAL") + { + var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(id, AccessToken); + var defaultRound = await _dutyTimeRepository.GetDefaultAsync(); + if (defaultRound == null) + { + return Error("ไม่พบรอบการลงเวลา Default", StatusCodes.Status404NotFound); + } + var effectiveDate = await _userDutyTimeRepository.GetLastEffectRound(profile!.Id); + var roundId = effectiveDate != null ? effectiveDate.DutyTimeId : Guid.Empty; + var userRound = await _dutyTimeRepository.GetByIdAsync(roundId); + + var duty = userRound ?? defaultRound; + if (req.CheckInStatus == "NORMAL") + { + data.CheckIn = DateTime.Parse($"{data.CheckIn.Date.ToString("yyyy-MM-dd")} {duty.StartTimeMorning}"); + } + if (req.CheckOutStatus == "NORMAL" ) + { + var checkOutTime = data.CheckOut != null ? data.CheckOut.Value : data.CheckIn; + data.CheckOut = DateTime.Parse($"{checkOutTime.Date.ToString("yyyy-MM-dd")} {duty.EndTimeAfternoon}"); + } + } data.CheckInStatus = req.CheckInStatus; data.CheckOutStatus = req.CheckOutStatus; From ecf5ada7edaf242c76f2ebf4a6e609a272d76ca4 Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Fri, 23 Jan 2026 20:55:21 +0700 Subject: [PATCH 064/178] Update LeaveController to conditionally set check-out time based on existing value and duty schedule --- BMA.EHR.Leave/Controllers/LeaveController.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/BMA.EHR.Leave/Controllers/LeaveController.cs b/BMA.EHR.Leave/Controllers/LeaveController.cs index ebfd5c96..3346a4a4 100644 --- a/BMA.EHR.Leave/Controllers/LeaveController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveController.cs @@ -3655,7 +3655,12 @@ namespace BMA.EHR.Leave.Service.Controllers if (req.CheckOutStatus == "NORMAL" ) { var checkOutTime = data.CheckOut != null ? data.CheckOut.Value : data.CheckIn; - data.CheckOut = DateTime.Parse($"{checkOutTime.Date.ToString("yyyy-MM-dd")} {duty.EndTimeAfternoon}"); + var oldCheckOutTime = data.CheckOut != null ? data.CheckOut.Value : DateTime.Now; + var roundCheckOutTime = DateTime.Parse($"{checkOutTime.Date.ToString("yyyy-MM-dd")} {duty.EndTimeAfternoon}"); + if (oldCheckOutTime < roundCheckOutTime) + { + data.CheckOut = roundCheckOutTime; + } } } From c1d689ebfafe6f8687b484966c866038ec214ac9 Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Fri, 23 Jan 2026 21:25:34 +0700 Subject: [PATCH 065/178] Update LeaveController to adjust check-in/check-out times based on location for meetings --- BMA.EHR.Leave/Controllers/LeaveController.cs | 24 +++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/BMA.EHR.Leave/Controllers/LeaveController.cs b/BMA.EHR.Leave/Controllers/LeaveController.cs index 3346a4a4..9de17c1f 100644 --- a/BMA.EHR.Leave/Controllers/LeaveController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveController.cs @@ -3637,7 +3637,8 @@ namespace BMA.EHR.Leave.Service.Controllers if (data.CheckInStatus == "NORMAL" || data.CheckOutStatus == "NORMAL") { - var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(id, AccessToken); + var userId = UserId == null ? Guid.Empty : Guid.Parse(UserId); + var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(userId, AccessToken); var defaultRound = await _dutyTimeRepository.GetDefaultAsync(); if (defaultRound == null) { @@ -3650,13 +3651,30 @@ namespace BMA.EHR.Leave.Service.Controllers var duty = userRound ?? defaultRound; if (req.CheckInStatus == "NORMAL") { - data.CheckIn = DateTime.Parse($"{data.CheckIn.Date.ToString("yyyy-MM-dd")} {duty.StartTimeMorning}"); + if(data.CheckInLocationName == "ไปประชุม / อบรม / สัมมนา") + { + data.CheckIn = DateTime.Parse($"{data.CheckIn.Date.ToString("yyyy-MM-dd")} 10:30"); + } + else + { + data.CheckIn = DateTime.Parse($"{data.CheckIn.Date.ToString("yyyy-MM-dd")} {duty.StartTimeMorning}"); + } + } if (req.CheckOutStatus == "NORMAL" ) { var checkOutTime = data.CheckOut != null ? data.CheckOut.Value : data.CheckIn; var oldCheckOutTime = data.CheckOut != null ? data.CheckOut.Value : DateTime.Now; - var roundCheckOutTime = DateTime.Parse($"{checkOutTime.Date.ToString("yyyy-MM-dd")} {duty.EndTimeAfternoon}"); + var roundCheckOutTime = DateTime.Now; + if(data.CheckOutLocationName == "ไปประชุม / อบรม / สัมมนา") + { + roundCheckOutTime = DateTime.Parse($"{checkOutTime.Date.ToString("yyyy-MM-dd")} 14:30"); + } + else + { + roundCheckOutTime = DateTime.Parse($"{checkOutTime.Date.ToString("yyyy-MM-dd")} {duty.EndTimeAfternoon}"); + } + if (oldCheckOutTime < roundCheckOutTime) { data.CheckOut = roundCheckOutTime; From 22a7a8c17c6e7051ed96b2922363733196c55f5e Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Mon, 26 Jan 2026 12:04:58 +0700 Subject: [PATCH 066/178] Update LeaveController to refine check-in/check-out logic based on meeting location #2223 --- BMA.EHR.Leave/Controllers/LeaveController.cs | 79 ++++++++++---------- 1 file changed, 39 insertions(+), 40 deletions(-) diff --git a/BMA.EHR.Leave/Controllers/LeaveController.cs b/BMA.EHR.Leave/Controllers/LeaveController.cs index 9de17c1f..4787de39 100644 --- a/BMA.EHR.Leave/Controllers/LeaveController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveController.cs @@ -3635,53 +3635,52 @@ namespace BMA.EHR.Leave.Service.Controllers if (data == null) return Error(GlobalMessages.DataNotFound); - if (data.CheckInStatus == "NORMAL" || data.CheckOutStatus == "NORMAL") +  //if (data.CheckInStatus == "NORMAL" || data.CheckOutStatus == "NORMAL") + + var userId = UserId == null ? Guid.Empty : Guid.Parse(UserId); + var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(userId, AccessToken); + var defaultRound = await _dutyTimeRepository.GetDefaultAsync(); + if (defaultRound == null) { - var userId = UserId == null ? Guid.Empty : Guid.Parse(UserId); - var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(userId, AccessToken); - var defaultRound = await _dutyTimeRepository.GetDefaultAsync(); - if (defaultRound == null) - { - return Error("ไม่พบรอบการลงเวลา Default", StatusCodes.Status404NotFound); - } - var effectiveDate = await _userDutyTimeRepository.GetLastEffectRound(profile!.Id); - var roundId = effectiveDate != null ? effectiveDate.DutyTimeId : Guid.Empty; - var userRound = await _dutyTimeRepository.GetByIdAsync(roundId); + return Error("ไม่พบรอบการลงเวลา Default", StatusCodes.Status404NotFound); + } + var effectiveDate = await _userDutyTimeRepository.GetLastEffectRound(profile!.Id); + var roundId = effectiveDate != null ? effectiveDate.DutyTimeId : Guid.Empty; + var userRound = await _dutyTimeRepository.GetByIdAsync(roundId); - var duty = userRound ?? defaultRound; - if (req.CheckInStatus == "NORMAL") + var duty = userRound ?? defaultRound; + if (req.CheckInStatus == "NORMAL") + { + if(data.CheckInLocationName == "ไปประชุม / อบรม / สัมมนา") { - if(data.CheckInLocationName == "ไปประชุม / อบรม / สัมมนา") - { - data.CheckIn = DateTime.Parse($"{data.CheckIn.Date.ToString("yyyy-MM-dd")} 10:30"); - } - else - { - data.CheckIn = DateTime.Parse($"{data.CheckIn.Date.ToString("yyyy-MM-dd")} {duty.StartTimeMorning}"); - } - + data.CheckIn = DateTime.Parse($"{data.CheckIn.Date.ToString("yyyy-MM-dd")} 10:30"); } - if (req.CheckOutStatus == "NORMAL" ) + else { - var checkOutTime = data.CheckOut != null ? data.CheckOut.Value : data.CheckIn; - var oldCheckOutTime = data.CheckOut != null ? data.CheckOut.Value : DateTime.Now; - var roundCheckOutTime = DateTime.Now; - if(data.CheckOutLocationName == "ไปประชุม / อบรม / สัมมนา") - { - roundCheckOutTime = DateTime.Parse($"{checkOutTime.Date.ToString("yyyy-MM-dd")} 14:30"); - } - else - { - roundCheckOutTime = DateTime.Parse($"{checkOutTime.Date.ToString("yyyy-MM-dd")} {duty.EndTimeAfternoon}"); - } - - if (oldCheckOutTime < roundCheckOutTime) - { - data.CheckOut = roundCheckOutTime; - } + data.CheckIn = DateTime.Parse($"{data.CheckIn.Date.ToString("yyyy-MM-dd")} {duty.StartTimeMorning}"); + } + + } + if (req.CheckOutStatus == "NORMAL" ) + { + var checkOutTime = data.CheckOut != null ? data.CheckOut.Value : data.CheckIn; + var oldCheckOutTime = data.CheckOut != null ? data.CheckOut.Value : DateTime.Now; + var roundCheckOutTime = DateTime.Now; + if(data.CheckOutLocationName == "ไปประชุม / อบรม / สัมมนา") + { + roundCheckOutTime = DateTime.Parse($"{checkOutTime.Date.ToString("yyyy-MM-dd")} 14:30"); + } + else + { + roundCheckOutTime = DateTime.Parse($"{checkOutTime.Date.ToString("yyyy-MM-dd")} {duty.EndTimeAfternoon}"); + } + + if (oldCheckOutTime < roundCheckOutTime) + { + data.CheckOut = roundCheckOutTime; } } - + data.CheckInStatus = req.CheckInStatus; data.CheckOutStatus = req.CheckOutStatus; data.EditReason = req.Reason; From 2e6a81ff31b186b6964da750f51b27765a16c297 Mon Sep 17 00:00:00 2001 From: harid Date: Mon, 26 Jan 2026 18:21:33 +0700 Subject: [PATCH 067/178] =?UTF-8?q?Task=20#2233=20=E0=B8=A3=E0=B8=B2?= =?UTF-8?q?=E0=B8=A2=E0=B8=87=E0=B8=B2=E0=B8=99=E0=B9=83=E0=B8=9A=E0=B8=A5?= =?UTF-8?q?=E0=B8=B2=20=E0=B9=81=E0=B8=AA=E0=B8=94=E0=B8=87=E0=B8=AA?= =?UTF-8?q?=E0=B8=B3=E0=B8=99=E0=B8=B1=E0=B8=81=E0=B8=87=E0=B8=B2=E0=B8=99?= =?UTF-8?q?=20=E0=B8=81.=E0=B8=81.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Repositories/UserProfileRepository.cs | 30 ++ .../Leaves/GetProfileLeaveByKeycloakDto.cs | 10 +- .../Controllers/LeaveReportController.cs | 302 +++++++++++------- 3 files changed, 227 insertions(+), 115 deletions(-) diff --git a/BMA.EHR.Application/Repositories/UserProfileRepository.cs b/BMA.EHR.Application/Repositories/UserProfileRepository.cs index 9ef3f76e..b868a4e9 100644 --- a/BMA.EHR.Application/Repositories/UserProfileRepository.cs +++ b/BMA.EHR.Application/Repositories/UserProfileRepository.cs @@ -233,6 +233,36 @@ namespace BMA.EHR.Application.Repositories } } + public async Task GetProfileLeaveReportByKeycloakIdAsync(Guid keycloakId, string? accessToken, string? report) + { + try + { + var apiPath = $"{_configuration["API"]}/org/dotnet/profile-leave/keycloak"; + var apiKey = _configuration["API_KEY"]; + var body = new + { + keycloakId = keycloakId, + report = report + }; + + var profiles = new List(); + + var apiResult = await PostExternalAPIAsync(apiPath, accessToken, body, apiKey); + if (apiResult != null) + { + var raw = JsonConvert.DeserializeObject(apiResult); + if (raw != null) + return raw.Result; + } + + return null; + } + catch + { + throw; + } + } + public async Task GetProfileByProfileIdAsync(Guid profileId, string? accessToken) { try diff --git a/BMA.EHR.Application/Responses/Leaves/GetProfileLeaveByKeycloakDto.cs b/BMA.EHR.Application/Responses/Leaves/GetProfileLeaveByKeycloakDto.cs index 52a9b948..c6c79bfd 100644 --- a/BMA.EHR.Application/Responses/Leaves/GetProfileLeaveByKeycloakDto.cs +++ b/BMA.EHR.Application/Responses/Leaves/GetProfileLeaveByKeycloakDto.cs @@ -2,6 +2,11 @@ { public class GetProfileLeaveByKeycloakDto { + public string ProfileType { get; set; } + public string Prefix { get; set; } + public string FirstName { get; set; } + public string LastName { get; set; } + public string CitizenId { get; set; } public DateTime BirthDate { get; set; } public DateTime RetireDate { get; set; } public string GovAge { get; set; } = string.Empty; @@ -10,11 +15,14 @@ public DateTime DateCurrent { get; set; } public int Amount { get; set; } public string? TelephoneNumber { get; set; } = string.Empty; - public string PositionName { get; set; } = string.Empty; + public string Position { get; set; } = string.Empty; public string PosLevel { get; set; } = string.Empty; public string PosType { get; set; } = string.Empty; + public string? PositionLeaveName { get; set; } + public string? PosExecutiveName { get; set; } public string CurrentAddress { get; set; } = string.Empty; public string Oc { get; set; } = string.Empty; + public bool isCommission { get; set; } = false; public string Root { get; set; } = string.Empty; public string? Child1 { get; set; } public string? Child2 { get; set; } diff --git a/BMA.EHR.Leave/Controllers/LeaveReportController.cs b/BMA.EHR.Leave/Controllers/LeaveReportController.cs index 5dad15fa..f8c22956 100644 --- a/BMA.EHR.Leave/Controllers/LeaveReportController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveReportController.cs @@ -137,7 +137,12 @@ namespace BMA.EHR.Leave.Service.Controllers private async Task GetReport01(LeaveRequest data) { - var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(data.KeycloakUserId, AccessToken); + //var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(data.KeycloakUserId, AccessToken); + //if (profile == null) + //{ + // return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound); + //} + var profile = await _userProfileRepository.GetProfileLeaveReportByKeycloakIdAsync(data.KeycloakUserId, AccessToken, "leave9"); if (profile == null) { return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound); @@ -152,7 +157,11 @@ namespace BMA.EHR.Leave.Service.Controllers var startFiscalYear = new DateTime(data.LeaveStartDate.Year - 1, 10, 1); var endFiscalYear = data.LeaveStartDate.Date.AddDays(-1); // นับจากวันที่ยื่นลา var sumLeave = await _leaveRequestRepository.GetSumApproveLeaveTotalByTypeAndRangeForUser(data.KeycloakUserId, data.Type.Id, startFiscalYear, endFiscalYear); - var approveResult = await GetApproverData(data.Approvers); + var Oc = profile.isCommission == false + ? profile.Oc.ToThaiNumber() + : profile.Oc.Replace("สำนักงานคณะกรรมการข้าราชการกรุงเทพมหานคร", "สำนักงาน ก.ก.").ToThaiNumber(); + var approveResult = await GetApproverData(data.Approvers, profile.isCommission); + return new { template = "leave9", @@ -183,10 +192,10 @@ namespace BMA.EHR.Leave.Service.Controllers organizationName = profile!.Oc!.ToThaiNumber(), posExOrg = !string.IsNullOrEmpty(profile.PositionLeaveName) && (profile.PositionLeaveName.Contains("อำนวยการ") || profile.PositionLeaveName.Contains("บริหาร")) - ? new[] { (profile!.Oc!.ToThaiNumber()) } + ? new[] { Oc } : !string.IsNullOrEmpty(profile.PosExecutiveName) - ? new[] { (profile.PosExecutiveName.ToThaiNumber()), (profile!.Oc!.ToThaiNumber()) } - : new[] { (profile!.Oc!.ToThaiNumber()) }, + ? new[] { profile.PosExecutiveName.ToThaiNumber(), Oc } + : new[] { Oc }, leaveDetail = data.LeaveDetail.ToThaiNumber(), leaveDateStart = data.LeaveStartDate.Date.ToThaiShortDate().ToThaiNumber(), leaveDateEnd = data.LeaveEndDate.Date.ToThaiShortDate().ToThaiNumber(), @@ -227,14 +236,22 @@ namespace BMA.EHR.Leave.Service.Controllers private async Task GetReport02(LeaveRequest data) { - var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(data.KeycloakUserId, AccessToken); + //var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(data.KeycloakUserId, AccessToken); + //if (profile == null) + //{ + // return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound); + //} + var profile = await _userProfileRepository.GetProfileLeaveReportByKeycloakIdAsync(data.KeycloakUserId, AccessToken, "leave10"); if (profile == null) { return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound); } var fullName = $"{profile!.Prefix}{profile!.FirstName} {profile!.LastName}"; - var approveResult = await GetApproverData(data.Approvers); + var Oc = profile.isCommission == false + ? profile.Oc.ToThaiNumber() + : profile.Oc.Replace("สำนักงานคณะกรรมการข้าราชการกรุงเทพมหานคร", "สำนักงาน ก.ก.").ToThaiNumber(); + var approveResult = await GetApproverData(data.Approvers, profile.isCommission); return new { template = "leave10", @@ -265,10 +282,10 @@ namespace BMA.EHR.Leave.Service.Controllers organizationName = profile!.Oc!.ToThaiNumber(), posExOrg = !string.IsNullOrEmpty(profile.PositionLeaveName) && (profile.PositionLeaveName.Contains("อำนวยการ") || profile.PositionLeaveName.Contains("บริหาร")) - ? new[] { (profile!.Oc!.ToThaiNumber()) } + ? new[] { Oc } : !string.IsNullOrEmpty(profile.PosExecutiveName) - ? new[] { (profile.PosExecutiveName.ToThaiNumber()), (profile!.Oc!.ToThaiNumber()) } - : new[] { (profile!.Oc!.ToThaiNumber()) }, + ? new[] { profile.PosExecutiveName.ToThaiNumber(), Oc } + : new[] { Oc }, wifeDayName = data.WifeDayName ?? "", wifeDayDateBorn = data.WifeDayDateBorn == null || data.WifeDayDateBorn == "" ? "" : DateTime.Parse(data.WifeDayDateBorn).ToThaiShortDate().ToThaiNumber(), leaveDateStart = data.LeaveStartDate.Date.ToThaiShortDate().ToThaiNumber(), @@ -289,7 +306,12 @@ namespace BMA.EHR.Leave.Service.Controllers private async Task GetReport03(LeaveRequest data) { - var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(data.KeycloakUserId, AccessToken); + //var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(data.KeycloakUserId, AccessToken); + //if (profile == null) + //{ + // return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound); + //} + var profile = await _userProfileRepository.GetProfileLeaveReportByKeycloakIdAsync(data.KeycloakUserId, AccessToken, "leave11"); if (profile == null) { return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound); @@ -313,8 +335,10 @@ namespace BMA.EHR.Leave.Service.Controllers var sumLeave = leaveData == null ? 0.0 : leaveData.LeaveDaysUsed; var leaveLimit = leaveData == null ? 0.0 : leaveData.LeaveDays; var extendLeave = leaveLimit - 10; - - var approveResult = await GetApproverData(data.Approvers); + var Oc = profile.isCommission == false + ? profile.Oc.ToThaiNumber() + : profile.Oc.Replace("สำนักงานคณะกรรมการข้าราชการกรุงเทพมหานคร", "สำนักงาน ก.ก.").ToThaiNumber(); + var approveResult = await GetApproverData(data.Approvers, profile.isCommission); return new { template = "leave11", @@ -345,10 +369,10 @@ namespace BMA.EHR.Leave.Service.Controllers organizationName = profile!.Oc!.ToThaiNumber(), posExOrg = !string.IsNullOrEmpty(profile.PositionLeaveName) && (profile.PositionLeaveName.Contains("อำนวยการ") || profile.PositionLeaveName.Contains("บริหาร")) - ? new[] { (profile!.Oc!.ToThaiNumber()) } + ? new[] { Oc } : !string.IsNullOrEmpty(profile.PosExecutiveName) - ? new[] { (profile.PosExecutiveName.ToThaiNumber()), (profile!.Oc!.ToThaiNumber()) } - : new[] { (profile!.Oc!.ToThaiNumber()) }, + ? new[] { profile.PosExecutiveName.ToThaiNumber(), Oc } + : new[] { Oc }, restDayOldTotal = extendLeave.ToString().ToThaiNumber(), restDayCurrentTotal = (10).ToString().ToThaiNumber(), @@ -379,7 +403,12 @@ namespace BMA.EHR.Leave.Service.Controllers private async Task GetReport04(LeaveRequest data, bool isHajj = false) { - var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(data.KeycloakUserId, AccessToken); + //var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(data.KeycloakUserId, AccessToken); + //if (profile == null) + //{ + // return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound); + //} + var profile = await _userProfileRepository.GetProfileLeaveReportByKeycloakIdAsync(data.KeycloakUserId, AccessToken, isHajj ? "leave13": "leave12"); if (profile == null) { return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound); @@ -399,7 +428,10 @@ namespace BMA.EHR.Leave.Service.Controllers { isHajj = true; } - var approveResult = await GetApproverData(data.Approvers); + var Oc = profile.isCommission == false + ? profile.Oc.ToThaiNumber() + : profile.Oc.Replace("สำนักงานคณะกรรมการข้าราชการกรุงเทพมหานคร", "สำนักงาน ก.ก.").ToThaiNumber(); + var approveResult = await GetApproverData(data.Approvers, profile.isCommission); if (isHajj == true) { return new @@ -432,11 +464,10 @@ namespace BMA.EHR.Leave.Service.Controllers organizationName = profile!.Oc!.ToThaiNumber(), posExOrg = !string.IsNullOrEmpty(profile.PositionLeaveName) && (profile.PositionLeaveName.Contains("อำนวยการ") || profile.PositionLeaveName.Contains("บริหาร")) - ? new[] { (profile!.Oc!.ToThaiNumber()) } + ? new[] { Oc } : !string.IsNullOrEmpty(profile.PosExecutiveName) - ? new[] { (profile.PosExecutiveName.ToThaiNumber()), (profile!.Oc!.ToThaiNumber()) } - : new[] { (profile!.Oc!.ToThaiNumber()) }, - + ? new[] { profile.PosExecutiveName.ToThaiNumber(), Oc } + : new[] { Oc }, leavegovernmentDate = data.LeaveGovernmentDate == null ? "" : data.LeaveGovernmentDate.Value.Date.ToThaiShortDate().ToThaiNumber(), hajjDayStatus = data.HajjDayStatus, @@ -487,10 +518,10 @@ namespace BMA.EHR.Leave.Service.Controllers organizationName = profile!.Oc!.ToThaiNumber(), posExOrg = !string.IsNullOrEmpty(profile.PositionLeaveName) && (profile.PositionLeaveName.Contains("อำนวยการ") || profile.PositionLeaveName.Contains("บริหาร")) - ? new[] { (profile!.Oc!.ToThaiNumber()) } + ? new[] { Oc } : !string.IsNullOrEmpty(profile.PosExecutiveName) - ? new[] { (profile.PosExecutiveName.ToThaiNumber()), (profile!.Oc!.ToThaiNumber()) } - : new[] { (profile!.Oc!.ToThaiNumber()) }, + ? new[] { profile.PosExecutiveName.ToThaiNumber(), Oc } + : new[] { Oc }, leavebirthDate = data.LeaveBirthDate == null ? "" : data.LeaveBirthDate.Value.Date.ToThaiShortDate().ToThaiNumber(), leavegovernmentDate = data.LeaveGovernmentDate == null ? "" : data.LeaveGovernmentDate.Value.Date.ToThaiShortDate().ToThaiNumber(), @@ -520,12 +551,16 @@ namespace BMA.EHR.Leave.Service.Controllers private async Task GetReport05(LeaveRequest data) { - var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(data.KeycloakUserId, AccessToken); + //var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(data.KeycloakUserId, AccessToken); + //if (profile == null) + //{ + // return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound); + //} + var profile = await _userProfileRepository.GetProfileLeaveReportByKeycloakIdAsync(data.KeycloakUserId, AccessToken, "leave14"); if (profile == null) { return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound); } - var fullName = $"{profile!.Prefix}{profile!.FirstName} {profile!.LastName}"; //var rootOc = _userProfileRepository.GetRootOcId(profile.OcId ?? Guid.Empty, AccessToken); @@ -536,7 +571,10 @@ namespace BMA.EHR.Leave.Service.Controllers // if (list.Count > 0) // approver = list.First().Name; //} - var approveResult = await GetApproverData(data.Approvers); + var Oc = profile.isCommission == false + ? profile.Oc.ToThaiNumber() + : profile.Oc.Replace("สำนักงานคณะกรรมการข้าราชการกรุงเทพมหานคร", "สำนักงาน ก.ก.").ToThaiNumber(); + var approveResult = await GetApproverData(data.Approvers, profile.isCommission); return new { template = "leave14", @@ -567,10 +605,10 @@ namespace BMA.EHR.Leave.Service.Controllers organizationName = profile!.Oc!.ToThaiNumber(), posExOrg = !string.IsNullOrEmpty(profile.PositionLeaveName) && (profile.PositionLeaveName.Contains("อำนวยการ") || profile.PositionLeaveName.Contains("บริหาร")) - ? new[] { (profile!.Oc!.ToThaiNumber()) } + ? new[] { Oc } : !string.IsNullOrEmpty(profile.PosExecutiveName) - ? new[] { (profile.PosExecutiveName.ToThaiNumber()), (profile!.Oc!.ToThaiNumber()) } - : new[] { (profile!.Oc!.ToThaiNumber()) }, + ? new[] { profile.PosExecutiveName.ToThaiNumber(), Oc } + : new[] { Oc }, absentDaySummon = data.AbsentDaySummon.ToThaiNumber(), absentDayLocation = data.AbsentDayLocation.ToThaiNumber(), @@ -594,12 +632,16 @@ namespace BMA.EHR.Leave.Service.Controllers private async Task GetReport06(LeaveRequest data) { - var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(data.KeycloakUserId, AccessToken); + //var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(data.KeycloakUserId, AccessToken); + //if (profile == null) + //{ + // return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound); + //} + var profile = await _userProfileRepository.GetProfileLeaveReportByKeycloakIdAsync(data.KeycloakUserId, AccessToken, "leave15"); if (profile == null) { return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound); } - var fullName = $"{profile!.Prefix}{profile!.FirstName} {profile!.LastName}"; //var rootOc = _userProfileRepository.GetRootOcId(profile.OcId ?? Guid.Empty, AccessToken); @@ -610,7 +652,10 @@ namespace BMA.EHR.Leave.Service.Controllers // if (list.Count > 0) // approver = list.First().Name; //} - var approveResult = await GetApproverData(data.Approvers); + var Oc = profile.isCommission == false + ? profile.Oc.ToThaiNumber() + : profile.Oc.Replace("สำนักงานคณะกรรมการข้าราชการกรุงเทพมหานคร", "สำนักงาน ก.ก.").ToThaiNumber(); + var approveResult = await GetApproverData(data.Approvers, profile.isCommission); return new { template = "leave15", @@ -641,10 +686,10 @@ namespace BMA.EHR.Leave.Service.Controllers organizationName = profile!.Oc!.ToThaiNumber(), posExOrg = !string.IsNullOrEmpty(profile.PositionLeaveName) && (profile.PositionLeaveName.Contains("อำนวยการ") || profile.PositionLeaveName.Contains("บริหาร")) - ? new[] { (profile!.Oc!.ToThaiNumber()) } + ? new[] { Oc } : !string.IsNullOrEmpty(profile.PosExecutiveName) - ? new[] { (profile.PosExecutiveName.ToThaiNumber()), (profile!.Oc!.ToThaiNumber()) } - : new[] { (profile!.Oc!.ToThaiNumber()) }, + ? new[] { profile.PosExecutiveName.ToThaiNumber(), Oc } + : new[] { Oc }, leavebirthDate = data.LeaveBirthDate == null ? "" : data.LeaveBirthDate.Value.Date.ToThaiShortDate().ToThaiNumber(), leavegovernmentDate = data.LeaveGovernmentDate == null ? "" : data.LeaveGovernmentDate.Value.Date.ToThaiShortDate().ToThaiNumber(), @@ -684,18 +729,22 @@ namespace BMA.EHR.Leave.Service.Controllers private async Task GetReport07(LeaveRequest data) { - var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(data.KeycloakUserId, AccessToken); + //var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(data.KeycloakUserId, AccessToken); + //if (profile == null) + //{ + // return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound); + //} + + //var profileLeave = await _userProfileRepository.GetProfileLeaveByKeycloakIdAsync(data.KeycloakUserId, AccessToken); + //if (profileLeave == null) + //{ + // return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound); + //} + var profile = await _userProfileRepository.GetProfileLeaveReportByKeycloakIdAsync(data.KeycloakUserId, AccessToken, "leave16"); if (profile == null) { return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound); } - - var profileLeave = await _userProfileRepository.GetProfileLeaveByKeycloakIdAsync(data.KeycloakUserId, AccessToken); - if (profileLeave == null) - { - return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound); - } - var fullName = $"{profile!.Prefix}{profile!.FirstName} {profile!.LastName}"; //var rootOc = _userProfileRepository.GetRootOcId(profile.OcId ?? Guid.Empty, AccessToken); @@ -706,7 +755,10 @@ namespace BMA.EHR.Leave.Service.Controllers // if (list.Count > 0) // approver = list.First().Name; //} - var approveResult = await GetApproverData(data.Approvers); + var Oc = profile.isCommission == false + ? profile.Oc.ToThaiNumber() + : profile.Oc.Replace("สำนักงานคณะกรรมการข้าราชการกรุงเทพมหานคร", "สำนักงาน ก.ก.").ToThaiNumber(); + var approveResult = await GetApproverData(data.Approvers, profile.isCommission); return new { template = "leave16", @@ -738,36 +790,36 @@ namespace BMA.EHR.Leave.Service.Controllers organizationName = profile!.Oc!.ToThaiNumber(), posExOrg = !string.IsNullOrEmpty(profile.PositionLeaveName) && (profile.PositionLeaveName.Contains("อำนวยการ") || profile.PositionLeaveName.Contains("บริหาร")) - ? new[] { (profile!.Oc!.ToThaiNumber()) } + ? new[] { Oc } : !string.IsNullOrEmpty(profile.PosExecutiveName) - ? new[] { (profile.PosExecutiveName.ToThaiNumber()), (profile!.Oc!.ToThaiNumber()) } - : new[] { (profile!.Oc!.ToThaiNumber()) }, + ? new[] { profile.PosExecutiveName.ToThaiNumber(), Oc } + : new[] { Oc }, leaveDateStart = data.LeaveStartDate.Date.ToThaiShortDate().ToThaiNumber(), leaveDateEnd = data.LeaveEndDate.Date.ToThaiShortDate().ToThaiNumber(), profileType = profile.ProfileType, - birthDate = profileLeave.BirthDate.ToThaiShortDate().ToThaiNumber(), - retireDate = profileLeave.RetireDate.ToThaiShortDate().ToThaiNumber(), - govAge = profileLeave.GovAge.ToThaiNumber(), - age = profileLeave.Age.ToThaiNumber(), - dateAppoint = profileLeave.DateAppoint.ToThaiShortDate().ToThaiNumber(), - dateCurrent = profileLeave.DateCurrent.ToThaiShortDate().ToThaiNumber(), - amount = ((double)profileLeave.Amount).ToNumericText().ToThaiNumber(), - telephoneNumber = profileLeave.TelephoneNumber == null ? "" : profileLeave.TelephoneNumber.ToThaiNumber(), + birthDate = profile.BirthDate.ToThaiShortDate().ToThaiNumber(), + retireDate = profile.RetireDate.ToThaiShortDate().ToThaiNumber(), + govAge = profile.GovAge.ToThaiNumber(), + age = profile.Age.ToThaiNumber(), + dateAppoint = profile.DateAppoint.ToThaiShortDate().ToThaiNumber(), + dateCurrent = profile.DateCurrent.ToThaiShortDate().ToThaiNumber(), + amount = ((double)profile.Amount).ToNumericText().ToThaiNumber(), + telephoneNumber = profile.TelephoneNumber == null ? "" : profile.TelephoneNumber.ToThaiNumber(), - posLevel = profileLeave.PosLevel.ToThaiNumber(), - posType = profileLeave.PosType.ToThaiNumber(), - currentAddress = profileLeave.CurrentAddress.ToThaiNumber(), - oc = profileLeave.Oc.ToThaiNumber(), - root = profileLeave.Root.ToThaiNumber(), - child1 = profileLeave.Child1 == null ? "" : profileLeave.Child1!.ToThaiNumber(), - child2 = profileLeave.Child2 == null ? "" : profileLeave.Child2!.ToThaiNumber(), - child3 = profileLeave.Child3 == null ? "" : profileLeave.Child3!.ToThaiNumber(), - child4 = profileLeave.Child4 == null ? "" : profileLeave.Child4!.ToThaiNumber(), + posLevel = profile.PosLevel.ToThaiNumber(), + posType = profile.PosType.ToThaiNumber(), + currentAddress = profile.CurrentAddress.ToThaiNumber(), + oc = profile.Oc.ToThaiNumber(), + root = profile.Root.ToThaiNumber(), + child1 = profile.Child1 == null ? "" : profile.Child1!.ToThaiNumber(), + child2 = profile.Child2 == null ? "" : profile.Child2!.ToThaiNumber(), + child3 = profile.Child3 == null ? "" : profile.Child3!.ToThaiNumber(), + child4 = profile.Child4 == null ? "" : profile.Child4!.ToThaiNumber(), - positions = profileLeave.Positions.Select(x => new + positions = profile.Positions.Select(x => new { positionName = x.PositionName == null ? "" : x.PositionName.ToThaiNumber(), dateStart = x.DateStart.ToThaiShortDate().ToThaiNumber(), @@ -780,7 +832,7 @@ namespace BMA.EHR.Leave.Service.Controllers orgChild3 = x.OrgChild3 == null ? "" : x.OrgChild3.ToThaiNumber(), orgChild4 = x.OrgChild4 == null ? "" : x.OrgChild4.ToThaiNumber(), }).ToList(), - educations = profileLeave.Educations.Select(x => new + educations = profile.Educations.Select(x => new { educationLevel = x.EducationLevel == null ? "" : x.EducationLevel.ToThaiNumber(), institute = x.Institute == null ? "" : x.Institute.ToThaiNumber(), @@ -802,12 +854,16 @@ namespace BMA.EHR.Leave.Service.Controllers private async Task GetReport08(LeaveRequest data) { - var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(data.KeycloakUserId, AccessToken); + //var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(data.KeycloakUserId, AccessToken); + //if (profile == null) + //{ + // return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound); + //} + var profile = await _userProfileRepository.GetProfileLeaveReportByKeycloakIdAsync(data.KeycloakUserId, AccessToken, "leave17"); if (profile == null) { return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound); } - var fullName = $"{profile!.Prefix}{profile!.FirstName} {profile!.LastName}"; //var rootOc = _userProfileRepository.GetRootOcId(profile.OcId ?? Guid.Empty, AccessToken); @@ -818,7 +874,10 @@ namespace BMA.EHR.Leave.Service.Controllers // if (list.Count > 0) // approver = list.First().Name; //} - var approveResult = await GetApproverData(data.Approvers); + var Oc = profile.isCommission == false + ? profile.Oc.ToThaiNumber() + : profile.Oc.Replace("สำนักงานคณะกรรมการข้าราชการกรุงเทพมหานคร", "สำนักงาน ก.ก.").ToThaiNumber(); + var approveResult = await GetApproverData(data.Approvers, profile.isCommission); return new { template = "leave17", @@ -849,10 +908,10 @@ namespace BMA.EHR.Leave.Service.Controllers organizationName = profile!.Oc!.ToThaiNumber(), posExOrg = !string.IsNullOrEmpty(profile.PositionLeaveName) && (profile.PositionLeaveName.Contains("อำนวยการ") || profile.PositionLeaveName.Contains("บริหาร")) - ? new[] { (profile!.Oc!.ToThaiNumber()) } + ? new[] { Oc } : !string.IsNullOrEmpty(profile.PosExecutiveName) - ? new[] { (profile.PosExecutiveName.ToThaiNumber()), (profile!.Oc!.ToThaiNumber()) } - : new[] { (profile!.Oc!.ToThaiNumber()) }, + ? new[] { profile.PosExecutiveName.ToThaiNumber(), Oc } + : new[] { Oc }, leaveSalary = data.LeaveSalary == null ? "" : data.LeaveSalary.Value.ToNumericText().ToThaiNumber(), leaveSalaryText = data.LeaveSalaryText.ToThaiNumber(), @@ -884,14 +943,19 @@ namespace BMA.EHR.Leave.Service.Controllers private async Task GetReport09(LeaveRequest data) { - var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(data.KeycloakUserId, AccessToken); - if (profile == null) - { - return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound); - } + //var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(data.KeycloakUserId, AccessToken); + //if (profile == null) + //{ + // return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound); + //} - var profileLeave = await _userProfileRepository.GetProfileLeaveByKeycloakIdAsync(data.KeycloakUserId, AccessToken); - if (profileLeave == null) + //var profileLeave = await _userProfileRepository.GetProfileLeaveByKeycloakIdAsync(data.KeycloakUserId, AccessToken); + //if (profileLeave == null) + //{ + // return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound); + //} + var profile = await _userProfileRepository.GetProfileLeaveReportByKeycloakIdAsync(data.KeycloakUserId, AccessToken, "leave18"); + if (profile == null) { return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound); } @@ -906,7 +970,10 @@ namespace BMA.EHR.Leave.Service.Controllers // if (list.Count > 0) // approver = list.First().Name; //} - var approveResult = await GetApproverData(data.Approvers); + var Oc = profile.isCommission == false + ? profile.Oc.ToThaiNumber() + : profile.Oc.Replace("สำนักงานคณะกรรมการข้าราชการกรุงเทพมหานคร", "สำนักงาน ก.ก.").ToThaiNumber(); + var approveResult = await GetApproverData(data.Approvers, profile.isCommission); return new { template = "leave18", @@ -937,36 +1004,36 @@ namespace BMA.EHR.Leave.Service.Controllers organizationName = profile!.Oc!.ToThaiNumber(), posExOrg = !string.IsNullOrEmpty(profile.PositionLeaveName) && (profile.PositionLeaveName.Contains("อำนวยการ") || profile.PositionLeaveName.Contains("บริหาร")) - ? new[] { (profile!.Oc!.ToThaiNumber()) } + ? new[] { Oc } : !string.IsNullOrEmpty(profile.PosExecutiveName) - ? new[] { (profile.PosExecutiveName.ToThaiNumber()), (profile!.Oc!.ToThaiNumber()) } - : new[] { (profile!.Oc!.ToThaiNumber()) }, + ? new[] { profile.PosExecutiveName.ToThaiNumber(), Oc } + : new[] { Oc }, leaveDateStart = data.LeaveStartDate.Date.ToThaiShortDate().ToThaiNumber(), leaveDateEnd = data.LeaveEndDate.Date.ToThaiShortDate().ToThaiNumber(), profileType = profile.ProfileType, - birthDate = profileLeave.BirthDate.ToThaiShortDate().ToThaiNumber(), - retireDate = profileLeave.RetireDate.ToThaiShortDate().ToThaiNumber(), - govAge = profileLeave.GovAge.ToThaiNumber(), - age = profileLeave.Age.ToThaiNumber(), - dateAppoint = profileLeave.DateAppoint.ToThaiShortDate().ToThaiNumber(), - dateCurrent = profileLeave.DateCurrent.ToThaiShortDate().ToThaiNumber(), - amount = ((double)profileLeave.Amount).ToNumericText().ToThaiNumber(), - telephoneNumber = profileLeave.TelephoneNumber == null ? "" : profileLeave.TelephoneNumber.ToThaiNumber(), + birthDate = profile.BirthDate.ToThaiShortDate().ToThaiNumber(), + retireDate = profile.RetireDate.ToThaiShortDate().ToThaiNumber(), + govAge = profile.GovAge.ToThaiNumber(), + age = profile.Age.ToThaiNumber(), + dateAppoint = profile.DateAppoint.ToThaiShortDate().ToThaiNumber(), + dateCurrent = profile.DateCurrent.ToThaiShortDate().ToThaiNumber(), + amount = ((double)profile.Amount).ToNumericText().ToThaiNumber(), + telephoneNumber = profile.TelephoneNumber == null ? "" : profile.TelephoneNumber.ToThaiNumber(), - posLevel = profileLeave.PosLevel.ToThaiNumber(), - posType = profileLeave.PosType.ToThaiNumber(), - currentAddress = profileLeave.CurrentAddress.ToThaiNumber(), - oc = profileLeave.Oc.ToThaiNumber(), - root = profileLeave.Root.ToThaiNumber(), - child1 = profileLeave.Child1 == null ? "" : profileLeave.Child1!.ToThaiNumber(), - child2 = profileLeave.Child2 == null ? "" : profileLeave.Child2!.ToThaiNumber(), - child3 = profileLeave.Child3 == null ? "" : profileLeave.Child3!.ToThaiNumber(), - child4 = profileLeave.Child4 == null ? "" : profileLeave.Child4!.ToThaiNumber(), + posLevel = profile.PosLevel.ToThaiNumber(), + posType = profile.PosType.ToThaiNumber(), + currentAddress = profile.CurrentAddress.ToThaiNumber(), + oc = profile.Oc.ToThaiNumber(), + root = profile.Root.ToThaiNumber(), + child1 = profile.Child1 == null ? "" : profile.Child1!.ToThaiNumber(), + child2 = profile.Child2 == null ? "" : profile.Child2!.ToThaiNumber(), + child3 = profile.Child3 == null ? "" : profile.Child3!.ToThaiNumber(), + child4 = profile.Child4 == null ? "" : profile.Child4!.ToThaiNumber(), - positions = profileLeave.Positions.Select(x => new + positions = profile.Positions.Select(x => new { positionName = x.PositionName == null ? "" : x.PositionName.ToThaiNumber(), dateStart = x.DateStart.ToThaiShortDate().ToThaiNumber(), @@ -980,7 +1047,7 @@ namespace BMA.EHR.Leave.Service.Controllers orgChild4 = x.OrgChild4 == null ? "" : x.OrgChild4.ToThaiNumber(), }).ToList(), - educations = profileLeave.Educations.Select(x => new + educations = profile.Educations.Select(x => new { educationLevel = x.EducationLevel == null ? "" : x.EducationLevel.ToThaiNumber(), institute = x.Institute == null ? "" : x.Institute.ToThaiNumber(), @@ -1109,12 +1176,16 @@ namespace BMA.EHR.Leave.Service.Controllers return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound); } - var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(data.KeycloakUserId, AccessToken); + //var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(data.KeycloakUserId, AccessToken); + //if (profile == null) + //{ + // return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound); + //} + var profile = await _userProfileRepository.GetProfileLeaveReportByKeycloakIdAsync(data.KeycloakUserId, AccessToken, "leave9"); if (profile == null) { return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound); } - var fullName = $"{profile!.Prefix}{profile!.FirstName} {profile!.LastName}"; //var rootOc = _userProfileRepository.GetRootOcId(profile.OcId ?? Guid.Empty, AccessToken); @@ -1125,7 +1196,10 @@ namespace BMA.EHR.Leave.Service.Controllers // if (list.Count > 0) // approver = list.First().Name; //} - var approveResult = await GetApproverData(data.Approvers); + var Oc = profile.isCommission == false + ? profile.Oc.ToThaiNumber() + : profile.Oc.Replace("สำนักงานคณะกรรมการข้าราชการกรุงเทพมหานคร", "สำนักงาน ก.ก.").ToThaiNumber(); + var approveResult = await GetApproverData(data.Approvers, profile.isCommission); var result = new { template = "แบบใบขอยกเลิกวันลา", @@ -1154,10 +1228,10 @@ namespace BMA.EHR.Leave.Service.Controllers organizationName = profile!.Oc!.ToThaiNumber(), posExOrg = !string.IsNullOrEmpty(profile.PositionLeaveName) && (profile.PositionLeaveName.Contains("อำนวยการ") || profile.PositionLeaveName.Contains("บริหาร")) - ? new[] { (profile!.Oc!.ToThaiNumber()) } + ? new[] { Oc } : !string.IsNullOrEmpty(profile.PosExecutiveName) - ? new[] { (profile.PosExecutiveName.ToThaiNumber()), (profile!.Oc!.ToThaiNumber()) } - : new[] { (profile!.Oc!.ToThaiNumber()) }, + ? new[] { profile.PosExecutiveName.ToThaiNumber(), Oc } + : new[] { Oc }, leaveDateStart = data.LeaveStartDate.Date.ToThaiShortDate().ToThaiNumber(), leaveDateEnd = data.LeaveEndDate.Date.ToThaiShortDate().ToThaiNumber(), dear = data.CommanderPosition == null ? data.Dear : data.CommanderPosition.ToThaiNumber(), @@ -2811,7 +2885,7 @@ namespace BMA.EHR.Leave.Service.Controllers } #endregion - private async Task GetApproverData(List list) + private async Task GetApproverData(List list, bool? isCommission = false) { var _default = new { @@ -2842,10 +2916,10 @@ namespace BMA.EHR.Leave.Service.Controllers : $"{x.PositionName}{x.PositionLevelName}").ToThaiNumber(), posExOrg = !string.IsNullOrEmpty(x.PositionLevelName) && (x.PositionLevelName.Contains("อำนวยการ") || x.PositionLevelName.Contains("บริหาร")) - ? new[] { (x!.OrganizationName!.ToThaiNumber()) } + ? new[] { (isCommission == false ? x!.OrganizationName : x!.OrganizationName.Replace("สำนักงานคณะกรรมการข้าราชการกรุงเทพมหานคร", "สำนักงาน ก.ก.")).ToThaiNumber() } : !string.IsNullOrEmpty(x.PosExecutiveName) - ? new[] { (x.PosExecutiveName.ToThaiNumber()), (x!.OrganizationName!.ToThaiNumber()) } - : new[] { (x!.OrganizationName!.ToThaiNumber()) }, + ? new[] { (x.PosExecutiveName.ToThaiNumber()), (isCommission == false ? x!.OrganizationName : x!.OrganizationName.Replace("สำนักงานคณะกรรมการข้าราชการกรุงเทพมหานคร", "สำนักงาน ก.ก.")).ToThaiNumber() } + : new[] { (isCommission == false ? x!.OrganizationName : x!.OrganizationName.Replace("สำนักงานคณะกรรมการข้าราชการกรุงเทพมหานคร", "สำนักงาน ก.ก.")).ToThaiNumber() }, positionSign = !string.IsNullOrEmpty(x.PositionSign) ? x.PositionSign.Replace("\r", "").Replace("\n", " ") : "............................................", From 982dfc33d119e46b4ff15177f90464196982bd2a Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Mon, 26 Jan 2026 22:35:24 +0700 Subject: [PATCH 068/178] Refactor LeaveReportController and LeaveRequestController to calculate leave days using repository methods #2246 #2247 --- BMA.EHR.Leave/Controllers/LeaveReportController.cs | 5 ++++- BMA.EHR.Leave/Controllers/LeaveRequestController.cs | 12 +++++++++--- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/BMA.EHR.Leave/Controllers/LeaveReportController.cs b/BMA.EHR.Leave/Controllers/LeaveReportController.cs index f8c22956..eb59f663 100644 --- a/BMA.EHR.Leave/Controllers/LeaveReportController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveReportController.cs @@ -332,7 +332,10 @@ namespace BMA.EHR.Leave.Service.Controllers //var sumLeave = await _leaveRequestRepository.GetSumApproveLeaveTotalByTypeAndRangeForUser(data.KeycloakUserId, data.Type.Id, startFiscalYear, endFiscalYear); - var sumLeave = leaveData == null ? 0.0 : leaveData.LeaveDaysUsed; + + var sumLeave = await _leaveRequestRepository.GetSumApproveLeaveTotalByTypeAndRangeForUser(data.KeycloakUserId, data.Type.Id, startFiscalYear, endFiscalYear); + + //var sumLeave = leaveData == null ? 0.0 : leaveData.LeaveDaysUsed; var leaveLimit = leaveData == null ? 0.0 : leaveData.LeaveDays; var extendLeave = leaveLimit - 10; var Oc = profile.isCommission == false diff --git a/BMA.EHR.Leave/Controllers/LeaveRequestController.cs b/BMA.EHR.Leave/Controllers/LeaveRequestController.cs index dc3afc62..d8da2c15 100644 --- a/BMA.EHR.Leave/Controllers/LeaveRequestController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveRequestController.cs @@ -2388,8 +2388,10 @@ namespace BMA.EHR.Leave.Service.Controllers } } } - var thisYear = DateTime.Now.Year; - var toDay = DateTime.Now.Date; + var thisYear = rawData.LeaveStartDate.Year; + var toDay = rawData.LeaveStartDate.Date; + // var thisYear = DateTime.Now.Year; + // var toDay = DateTime.Now.Date; if (toDay >= new DateTime(toDay.Year, 10, 1) && toDay <= new DateTime(toDay.Year, 12, 31)) thisYear = thisYear + 1; @@ -2441,7 +2443,11 @@ namespace BMA.EHR.Leave.Service.Controllers orgName += $" {rawData.Root}"; var leaveData = await _leaveBeginningRepository.GetByYearAndTypeIdForUser2Async(thisYear, rawData.Type.Id, rawData.KeycloakUserId); - var leaveSummary = leaveData == null ? 0.0 : leaveData.LeaveDaysUsed; + + var startFiscalYear = new DateTime(rawData.LeaveStartDate.Year - 1, 10, 1); + var endFiscalYear = rawData.LeaveStartDate.Date.AddDays(-1); // นับจากวันที่ยื่นลา + var leaveSummary = await _leaveRequestRepository.GetSumApproveLeaveTotalByTypeAndRangeForUser(rawData.KeycloakUserId, rawData.Type.Id, startFiscalYear, endFiscalYear); + //var leaveSummary = leaveData == null ? 0.0 : leaveData.LeaveDaysUsed; var extendLeave = 0.0; var leaveLimit = (double)rawData.Type.Limit; From 4f28b4e9e07c7ecdaac89035bff49c7f9b9351ab Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Mon, 26 Jan 2026 22:58:37 +0700 Subject: [PATCH 069/178] Add LeaveTotal property to LeaveRequest DTOs for better leave tracking #2245 --- BMA.EHR.Leave/Controllers/LeaveRequestController.cs | 4 +++- .../DTOs/LeaveRequest/GetLeaveRequestCalendarResultDto.cs | 2 ++ .../DTOs/LeaveRequest/GetLeaveRequestForAdminResultDto.cs | 2 ++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/BMA.EHR.Leave/Controllers/LeaveRequestController.cs b/BMA.EHR.Leave/Controllers/LeaveRequestController.cs index d8da2c15..fa8cbed3 100644 --- a/BMA.EHR.Leave/Controllers/LeaveRequestController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveRequestController.cs @@ -1326,7 +1326,8 @@ namespace BMA.EHR.Leave.Service.Controllers FullName = $"{d.Prefix}{d.FirstName} {d.LastName}", LeaveEndDate = d.LeaveEndDate, LeaveStartDate = d.LeaveStartDate, - KeycloakId = d.KeycloakUserId + KeycloakId = d.KeycloakUserId, + LeaveTotal = d.LeaveTotal }) .ToList(); @@ -1750,6 +1751,7 @@ namespace BMA.EHR.Leave.Service.Controllers LeaveRange = item.LeaveRange ?? "ALL", LeaveRangeEnd = item.LeaveRangeEnd ?? "ALL", HajjDayStatus = item.HajjDayStatus, + LeaveTotal = item.LeaveTotal }; result.Add(res); diff --git a/BMA.EHR.Leave/DTOs/LeaveRequest/GetLeaveRequestCalendarResultDto.cs b/BMA.EHR.Leave/DTOs/LeaveRequest/GetLeaveRequestCalendarResultDto.cs index ef895786..be03ec4c 100644 --- a/BMA.EHR.Leave/DTOs/LeaveRequest/GetLeaveRequestCalendarResultDto.cs +++ b/BMA.EHR.Leave/DTOs/LeaveRequest/GetLeaveRequestCalendarResultDto.cs @@ -19,5 +19,7 @@ public DateTime LeaveEndDate { get; set; } public Guid KeycloakId { get; set; } + + public double LeaveTotal { get; set; } } } diff --git a/BMA.EHR.Leave/DTOs/LeaveRequest/GetLeaveRequestForAdminResultDto.cs b/BMA.EHR.Leave/DTOs/LeaveRequest/GetLeaveRequestForAdminResultDto.cs index 42a508ff..d723bd66 100644 --- a/BMA.EHR.Leave/DTOs/LeaveRequest/GetLeaveRequestForAdminResultDto.cs +++ b/BMA.EHR.Leave/DTOs/LeaveRequest/GetLeaveRequestForAdminResultDto.cs @@ -37,5 +37,7 @@ public bool? HajjDayStatus { get; set; } public string? ProfileType { get; set; } + + public double LeaveTotal { get; set; } } } From 54c8152752ef9998afa5703c0192620c5cb0afda Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Tue, 27 Jan 2026 16:55:23 +0700 Subject: [PATCH 070/178] Refactor LeaveRequestController to improve filtering logic and update appsettings.json to modify connection strings --- .../Controllers/LeaveRequestController.cs | 23 +++++++++++++------ BMA.EHR.Leave/appsettings.json | 6 ++--- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/BMA.EHR.Leave/Controllers/LeaveRequestController.cs b/BMA.EHR.Leave/Controllers/LeaveRequestController.cs index fa8cbed3..49242df2 100644 --- a/BMA.EHR.Leave/Controllers/LeaveRequestController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveRequestController.cs @@ -1906,11 +1906,20 @@ namespace BMA.EHR.Leave.Service.Controllers var rawData = await _leaveRequestRepository.GetCancelLeaveRequestForAdminAsync(req.Year, req.Type, req.Status, role, nodeId, profileAdmin?.Node); + var recCount = rawData.Count; + + if (req.Keyword != "") + rawData = rawData.Where(x => ($"{x.Prefix}{x.FirstName} {x.LastName}").Contains(req.Keyword)).ToList(); + if (!string.IsNullOrEmpty(req.ProfileType) && req.ProfileType.ToUpper() != "ALL") + rawData = rawData.Where(x => x.ProfileType.ToUpper().Contains(req.ProfileType.ToUpper())).ToList(); + + rawData = rawData.Skip((req.Page - 1) * req.PageSize).Take(req.PageSize).ToList(); + var result = new List(); foreach (var item in rawData) { - var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(item.KeycloakUserId, AccessToken); + var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(item.KeycloakUserId, AccessToken); var res = new GetLeaveCancelRequestResultDto { Id = item.Id, @@ -1925,13 +1934,13 @@ namespace BMA.EHR.Leave.Service.Controllers result.Add(res); } - if (req.Keyword != "") - result = result.Where(x => x.FullName.Contains(req.Keyword)).ToList(); - if (!string.IsNullOrEmpty(req.ProfileType) && req.ProfileType.ToUpper() != "ALL") - result = result.Where(x => x.ProfileType.ToUpper().Contains(req.ProfileType.ToUpper())).ToList(); - var pageResult = result.Skip((req.Page - 1) * req.PageSize).Take(req.PageSize).ToList(); + // if (req.Keyword != "") + // result = result.Where(x => x.FullName.Contains(req.Keyword)).ToList(); + // if (!string.IsNullOrEmpty(req.ProfileType) && req.ProfileType.ToUpper() != "ALL") + // result = result.Where(x => x.ProfileType.ToUpper().Contains(req.ProfileType.ToUpper())).ToList(); + // var pageResult = result.Skip((req.Page - 1) * req.PageSize).Take(req.PageSize).ToList(); - return Success(new { data = pageResult, total = result.Count }); + return Success(new { data = result, total = recCount }); } diff --git a/BMA.EHR.Leave/appsettings.json b/BMA.EHR.Leave/appsettings.json index f8562d6e..40b464cf 100644 --- a/BMA.EHR.Leave/appsettings.json +++ b/BMA.EHR.Leave/appsettings.json @@ -23,9 +23,9 @@ "ExamConnection": "server=192.168.1.63;user=root;password=12345678;port=3306;database=hrms_exam;Convert Zero Datetime=True;Allow User Variables=true;Pooling=True;", "LeaveConnection": "server=192.168.1.63;user=root;password=12345678;port=3306;database=hrms_leave;Convert Zero Datetime=True;Allow User Variables=true;Pooling=True;" - //"DefaultConnection": "server=172.27.17.68;user=user;password=cDldaqkwESWvuZ37Gr0n;port=3306;database=hrms;Convert Zero Datetime=True;Allow User Variables=true;Pooling=True;", - //"ExamConnection": "server=172.27.17.68;user=user;password=cDldaqkwESWvuZ37Gr0n;port=3306;database=hrms_exam;Convert Zero Datetime=True;Allow User Variables=true;Pooling=True;", - //"LeaveConnection": "server=172.27.17.68;user=user;password=cDldaqkwESWvuZ37Gr0n;port=3306;database=hrms_leave;Convert Zero Datetime=True;Allow User Variables=true;Pooling=True;" + // "DefaultConnection": "server=172.27.17.68;user=user;password=cDldaqkwESWvuZ37Gr0n;port=3306;database=hrms;Convert Zero Datetime=True;Allow User Variables=true;Pooling=True;", + // "ExamConnection": "server=172.27.17.68;user=user;password=cDldaqkwESWvuZ37Gr0n;port=3306;database=hrms_exam;Convert Zero Datetime=True;Allow User Variables=true;Pooling=True;", + // "LeaveConnection": "server=172.27.17.68;user=user;password=cDldaqkwESWvuZ37Gr0n;port=3306;database=hrms_leave;Convert Zero Datetime=True;Allow User Variables=true;Pooling=True;" }, "Jwt": { //"Key": "HP-FnQMUj9msHMSD3T9HtdEnphAKoCJLEl85CIqROFI", From e8ffb82eadb4a68642ce4ab4748fb143346f1996 Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Tue, 27 Jan 2026 17:00:22 +0700 Subject: [PATCH 071/178] Comment out profile retrieval and error handling in LeaveRequestController #2250 --- BMA.EHR.Leave/Controllers/LeaveRequestController.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/BMA.EHR.Leave/Controllers/LeaveRequestController.cs b/BMA.EHR.Leave/Controllers/LeaveRequestController.cs index 49242df2..67bb53b4 100644 --- a/BMA.EHR.Leave/Controllers/LeaveRequestController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveRequestController.cs @@ -1967,12 +1967,12 @@ namespace BMA.EHR.Leave.Service.Controllers return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound); } - var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(rawData.KeycloakUserId, AccessToken); + // var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(rawData.KeycloakUserId, AccessToken); - if (profile == null) - { - return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound); - } + // if (profile == null) + // { + // return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound); + // } var result = new GetCancelLeaveRequestByIdDto { From cbc2a1a88dbfdc3b37a83a48bcdf5b08c110fc1d Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Tue, 27 Jan 2026 17:05:07 +0700 Subject: [PATCH 072/178] Comment out profile retrieval in LeaveRequestController to prevent unnecessary calls #2250 --- BMA.EHR.Leave/Controllers/LeaveRequestController.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BMA.EHR.Leave/Controllers/LeaveRequestController.cs b/BMA.EHR.Leave/Controllers/LeaveRequestController.cs index 67bb53b4..5d6c37c4 100644 --- a/BMA.EHR.Leave/Controllers/LeaveRequestController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveRequestController.cs @@ -1919,7 +1919,7 @@ namespace BMA.EHR.Leave.Service.Controllers foreach (var item in rawData) { - var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(item.KeycloakUserId, AccessToken); + //var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(item.KeycloakUserId, AccessToken); var res = new GetLeaveCancelRequestResultDto { Id = item.Id, From 839c35784266ffd135bc9a212dac76b6bbc02ec8 Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Wed, 28 Jan 2026 11:47:10 +0700 Subject: [PATCH 073/178] Update LeaveController to use KeycloakUserId for profile retrieval #2253 --- BMA.EHR.Leave/Controllers/LeaveController.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/BMA.EHR.Leave/Controllers/LeaveController.cs b/BMA.EHR.Leave/Controllers/LeaveController.cs index 4787de39..809a28e3 100644 --- a/BMA.EHR.Leave/Controllers/LeaveController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveController.cs @@ -3637,8 +3637,9 @@ namespace BMA.EHR.Leave.Service.Controllers  //if (data.CheckInStatus == "NORMAL" || data.CheckOutStatus == "NORMAL") - var userId = UserId == null ? Guid.Empty : Guid.Parse(UserId); - var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(userId, AccessToken); + //var userId = UserId == null ? Guid.Empty : Guid.Parse(UserId); + // แก้เป็นมาใช้งาน KeycloakUserId แทน + var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(data.KeycloakUserId, AccessToken); var defaultRound = await _dutyTimeRepository.GetDefaultAsync(); if (defaultRound == null) { From 02487d91ff3073a2293db6a7ef7a2fc146ca84c5 Mon Sep 17 00:00:00 2001 From: harid Date: Wed, 28 Jan 2026 13:27:25 +0700 Subject: [PATCH 074/178] =?UTF-8?q?=E0=B9=80=E0=B8=9B=E0=B8=A5=E0=B8=B5?= =?UTF-8?q?=E0=B9=88=E0=B8=A2=E0=B8=99=E0=B9=80=E0=B8=AA=E0=B9=89=E0=B8=99?= =?UTF-8?q?=20api=20=E0=B8=AA=E0=B8=B3=E0=B8=AB=E0=B8=A3=E0=B8=B1=E0=B8=9A?= =?UTF-8?q?=E0=B9=80=E0=B8=81=E0=B9=87=E0=B8=9A=20logs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Middlewares/CombinedErrorHandlerAndLoggingMiddleware.cs | 6 ++++-- BMA.EHR.Leave/Controllers/LeaveReportController.cs | 4 ++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/BMA.EHR.Domain/Middlewares/CombinedErrorHandlerAndLoggingMiddleware.cs b/BMA.EHR.Domain/Middlewares/CombinedErrorHandlerAndLoggingMiddleware.cs index 57ff69dd..15c88592 100644 --- a/BMA.EHR.Domain/Middlewares/CombinedErrorHandlerAndLoggingMiddleware.cs +++ b/BMA.EHR.Domain/Middlewares/CombinedErrorHandlerAndLoggingMiddleware.cs @@ -483,7 +483,8 @@ namespace BMA.EHR.Domain.Middlewares { logType = logType, ip = (string)contextData.RemoteIpAddress, - rootId = pf?.RootId, + //rootId = pf?.RootId, + rootId = pf?.RootDnaId, systemName = SystemName, startTimeStamp = startTime.ToString("o"), endTimeStamp = endTime.ToString("o"), @@ -684,7 +685,8 @@ namespace BMA.EHR.Domain.Middlewares { try { - var apiPath = $"{_configuration["API"]}/org/dotnet/by-keycloak/{keycloakId}"; + //var apiPath = $"{_configuration["API"]}/org/dotnet/by-keycloak/{keycloakId}"; + var apiPath = $"{_configuration["API"]}/org/dotnet/user-logs/{keycloakId}"; var apiKey = _configuration["API_KEY"]; var apiResult = await GetExternalAPIAsync(apiPath, accessToken ?? "", apiKey); diff --git a/BMA.EHR.Leave/Controllers/LeaveReportController.cs b/BMA.EHR.Leave/Controllers/LeaveReportController.cs index eb59f663..e71b4780 100644 --- a/BMA.EHR.Leave/Controllers/LeaveReportController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveReportController.cs @@ -1248,7 +1248,7 @@ namespace BMA.EHR.Leave.Service.Controllers Type3 = "☐", approve = approveResult, approverComment = !string.IsNullOrEmpty(data.LeaveDirectorComment) - ? data.LeaveDirectorComment.Replace("\r", "").Replace("\n", "").Trim() + ? data.LeaveDirectorComment.Replace("\r", "").Replace("\n", "").Trim().ToThaiNumber() : "......................", approverUpdatedAt = data.LastUpdatedAt.HasValue ? data.LastUpdatedAt.Value.ToThaiShortDate().ToThaiNumber() @@ -2924,7 +2924,7 @@ namespace BMA.EHR.Leave.Service.Controllers ? new[] { (x.PosExecutiveName.ToThaiNumber()), (isCommission == false ? x!.OrganizationName : x!.OrganizationName.Replace("สำนักงานคณะกรรมการข้าราชการกรุงเทพมหานคร", "สำนักงาน ก.ก.")).ToThaiNumber() } : new[] { (isCommission == false ? x!.OrganizationName : x!.OrganizationName.Replace("สำนักงานคณะกรรมการข้าราชการกรุงเทพมหานคร", "สำนักงาน ก.ก.")).ToThaiNumber() }, positionSign = !string.IsNullOrEmpty(x.PositionSign) - ? x.PositionSign.Replace("\r", "").Replace("\n", " ") + ? x.PositionSign.Replace("\r", "").Replace("\n", " ").ToThaiNumber() : "............................................", updatedAt = x.LastUpdatedAt.HasValue ? x.LastUpdatedAt.Value.Date.ToThaiShortDate().ToThaiNumber() From b10ff45d07c0d2c2405edf5e340ac9bf1649c483 Mon Sep 17 00:00:00 2001 From: harid Date: Wed, 28 Jan 2026 15:06:55 +0700 Subject: [PATCH 075/178] =?UTF-8?q?=E0=B9=80=E0=B8=9B=E0=B8=A5=E0=B8=B5?= =?UTF-8?q?=E0=B9=88=E0=B8=A2=E0=B8=99=E0=B9=80=E0=B8=AA=E0=B9=89=E0=B8=99?= =?UTF-8?q?=20call=20api=20=E0=B8=AA=E0=B8=B3=E0=B8=AB=E0=B8=A3=E0=B8=B1?= =?UTF-8?q?=E0=B8=9A=E0=B9=80=E0=B8=8A=E0=B9=87=E0=B8=84=20profileId?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Repositories/MessageQueue/InboxRepository.cs | 3 ++- .../Repositories/MessageQueue/NotificationRepository.cs | 6 ++++-- .../Controllers/DisciplineComplaint_AppealController.cs | 6 ++++-- .../Controllers/PlacementOfficerController.cs | 3 ++- .../Controllers/PlacementTransferController.cs | 3 ++- .../Controllers/RetirementResignController.cs | 6 ++++-- 6 files changed, 18 insertions(+), 9 deletions(-) diff --git a/BMA.EHR.Application/Repositories/MessageQueue/InboxRepository.cs b/BMA.EHR.Application/Repositories/MessageQueue/InboxRepository.cs index f0d0bc0c..46019cfc 100644 --- a/BMA.EHR.Application/Repositories/MessageQueue/InboxRepository.cs +++ b/BMA.EHR.Application/Repositories/MessageQueue/InboxRepository.cs @@ -51,7 +51,8 @@ namespace BMA.EHR.Application.Repositories.MessageQueue // // throw new Exception(GlobalMessages.DataNotFound); // } - var apiUrl = $"{_configuration["API"]}/org/profile/keycloak/position"; + //var apiUrl = $"{_configuration["API"]}/org/profile/keycloak/position"; + var apiUrl = $"{_configuration["API"]}/org/dotnet/get-profileId"; var profileId = ""; using (var client = new HttpClient()) { diff --git a/BMA.EHR.Application/Repositories/MessageQueue/NotificationRepository.cs b/BMA.EHR.Application/Repositories/MessageQueue/NotificationRepository.cs index eeb1d71e..a9d5ceb7 100644 --- a/BMA.EHR.Application/Repositories/MessageQueue/NotificationRepository.cs +++ b/BMA.EHR.Application/Repositories/MessageQueue/NotificationRepository.cs @@ -55,7 +55,8 @@ namespace BMA.EHR.Application.Repositories.MessageQueue // // throw new Exception(GlobalMessages.DataNotFound); // } - var apiUrl = $"{_configuration["API"]}/org/profile/keycloak/position"; + //var apiUrl = $"{_configuration["API"]}/org/profile/keycloak/position"; + var apiUrl = $"{_configuration["API"]}/org/dotnet/get-profileId"; var profileId = ""; using (var client = new HttpClient()) { @@ -131,7 +132,8 @@ namespace BMA.EHR.Application.Repositories.MessageQueue // { // return 0; // } - var apiUrl = $"{_configuration["API"]}/org/profile/keycloak/position"; + //var apiUrl = $"{_configuration["API"]}/org/profile/keycloak/position"; + var apiUrl = $"{_configuration["API"]}/org/dotnet/get-profileId"; var profileId = ""; using (var client = new HttpClient()) { diff --git a/BMA.EHR.Discipline.Service/Controllers/DisciplineComplaint_AppealController.cs b/BMA.EHR.Discipline.Service/Controllers/DisciplineComplaint_AppealController.cs index a9d14a71..21204d6b 100644 --- a/BMA.EHR.Discipline.Service/Controllers/DisciplineComplaint_AppealController.cs +++ b/BMA.EHR.Discipline.Service/Controllers/DisciplineComplaint_AppealController.cs @@ -93,7 +93,8 @@ namespace BMA.EHR.DisciplineComplaint_Appeal.Service.Controllers public async Task> GetDisciplineUser(string status = "ALL", string type = "ALL", int year = 0, int page = 1, int pageSize = 25, string keyword = "", string? sortBy = null, bool descending = false) { var id = ""; - var apiUrl = $"{_configuration["API"]}/org/profile/keycloak/position"; + //var apiUrl = $"{_configuration["API"]}/org/profile/keycloak/position"; + var apiUrl = $"{_configuration["API"]}/org/dotnet/get-profileId"; using (var client = new HttpClient()) { client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token.Replace("Bearer ", "")); @@ -357,7 +358,8 @@ namespace BMA.EHR.DisciplineComplaint_Appeal.Service.Controllers [HttpPost()] public async Task> CreateDiscipline([FromForm] DisciplineComplaint_AppealRequest req) { - var apiUrl = $"{_configuration["API"]}/org/profile/keycloak/position"; + //var apiUrl = $"{_configuration["API"]}/org/profile/keycloak/position"; + var apiUrl = $"{_configuration["API"]}/org/dotnet/get-profileId"; var id = ""; var type = ""; using (var client = new HttpClient()) diff --git a/BMA.EHR.Placement.Service/Controllers/PlacementOfficerController.cs b/BMA.EHR.Placement.Service/Controllers/PlacementOfficerController.cs index 8638291e..19cc4e2d 100644 --- a/BMA.EHR.Placement.Service/Controllers/PlacementOfficerController.cs +++ b/BMA.EHR.Placement.Service/Controllers/PlacementOfficerController.cs @@ -223,7 +223,8 @@ namespace BMA.EHR.Placement.Service.Controllers [HttpGet("keycloak")] public async Task> GetListByKeycloak() { - var apiUrl = $"{_configuration["API"]}/org/profile/keycloak/position"; + // var apiUrl = $"{_configuration["API"]}/org/profile/keycloak/position"; + var apiUrl = $"{_configuration["API"]}/org/dotnet/get-profileId"; using (var client = new HttpClient()) { client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token.Replace("Bearer ", "")); diff --git a/BMA.EHR.Placement.Service/Controllers/PlacementTransferController.cs b/BMA.EHR.Placement.Service/Controllers/PlacementTransferController.cs index f2931e3d..68dd42a9 100644 --- a/BMA.EHR.Placement.Service/Controllers/PlacementTransferController.cs +++ b/BMA.EHR.Placement.Service/Controllers/PlacementTransferController.cs @@ -79,7 +79,8 @@ namespace BMA.EHR.Placement.Service.Controllers [HttpGet("user")] public async Task> GetListByProfile() { - var apiUrl = $"{_configuration["API"]}/org/profile/keycloak/position"; + // var apiUrl = $"{_configuration["API"]}/org/profile/keycloak/position"; + var apiUrl = $"{_configuration["API"]}/org/dotnet/get-profileId"; using (var client = new HttpClient()) { client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token.Replace("Bearer ", "")); diff --git a/BMA.EHR.Retirement.Service/Controllers/RetirementResignController.cs b/BMA.EHR.Retirement.Service/Controllers/RetirementResignController.cs index 2caa44a1..7b33e0fb 100644 --- a/BMA.EHR.Retirement.Service/Controllers/RetirementResignController.cs +++ b/BMA.EHR.Retirement.Service/Controllers/RetirementResignController.cs @@ -135,7 +135,8 @@ namespace BMA.EHR.Retirement.Service.Controllers [HttpGet("user")] public async Task> GetListByProfile() { - var apiUrl = $"{_configuration["API"]}/org/profile/keycloak/position"; + // var apiUrl = $"{_configuration["API"]}/org/profile/keycloak/position"; + var apiUrl = $"{_configuration["API"]}/org/dotnet/get-profileId"; using (var client = new HttpClient()) { client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token.Replace("Bearer ", "")); @@ -185,7 +186,8 @@ namespace BMA.EHR.Retirement.Service.Controllers [HttpGet("user-cancel")] public async Task> GetListByProfileCancel() { - var apiUrl = $"{_configuration["API"]}/org/profile/keycloak/position"; + // var apiUrl = $"{_configuration["API"]}/org/profile/keycloak/position"; + var apiUrl = $"{_configuration["API"]}/org/dotnet/get-profileId"; using (var client = new HttpClient()) { client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token.Replace("Bearer ", "")); From 90eb94cee327ac194378e4dd163404384bc6b9fd Mon Sep 17 00:00:00 2001 From: harid Date: Wed, 28 Jan 2026 16:24:59 +0700 Subject: [PATCH 076/178] =?UTF-8?q?=E0=B8=81=E0=B8=A3=E0=B8=AD=E0=B8=87?= =?UTF-8?q?=E0=B8=84=E0=B9=89=E0=B8=99=E0=B8=AB=E0=B8=B2=E0=B8=82=E0=B9=89?= =?UTF-8?q?=E0=B8=AD=E0=B8=A1=E0=B8=B9=E0=B8=A5=E0=B8=A3=E0=B8=B2=E0=B8=A2?= =?UTF-8?q?=E0=B8=81=E0=B8=B2=E0=B8=A3=E0=B8=A5=E0=B8=87=E0=B9=80=E0=B8=A7?= =?UTF-8?q?=E0=B8=A5=E0=B8=B2=E0=B8=81=E0=B8=A3=E0=B8=93=E0=B8=B5=E0=B8=9E?= =?UTF-8?q?=E0=B8=B4=E0=B9=80=E0=B8=A8=E0=B8=A9=20#2252?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AdditionalCheckRequestRepository.cs | 13 ++++++++++++- BMA.EHR.Leave/Controllers/LeaveController.cs | 2 +- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/BMA.EHR.Application/Repositories/Leaves/TimeAttendants/AdditionalCheckRequestRepository.cs b/BMA.EHR.Application/Repositories/Leaves/TimeAttendants/AdditionalCheckRequestRepository.cs index 12f51207..64e1e745 100644 --- a/BMA.EHR.Application/Repositories/Leaves/TimeAttendants/AdditionalCheckRequestRepository.cs +++ b/BMA.EHR.Application/Repositories/Leaves/TimeAttendants/AdditionalCheckRequestRepository.cs @@ -144,7 +144,7 @@ namespace BMA.EHR.Application.Repositories.Leaves.TimeAttendants } } - public async Task> GetAdditionalCheckRequestsByAdminRole(int year, int month, string role, string nodeId, int? node) + public async Task> GetAdditionalCheckRequestsByAdminRole(int year, int month, string role, string nodeId, int? node, string? keyword) { try { @@ -152,6 +152,17 @@ namespace BMA.EHR.Application.Repositories.Leaves.TimeAttendants .Where(x => (x.CheckDate.Year == year && x.CheckDate.Month == month)) .OrderByDescending(x => x.CreatedAt.Date) .ToListAsync(); + + if (!string.IsNullOrEmpty(keyword)) + { + data = data.Where(x => + ( + (x.Prefix ?? "") + (x.FirstName ?? "") + " " + (x.LastName ?? "")).Contains(keyword) + || x.Description.Contains(keyword) + + ).ToList(); + } + if (role == "OWNER") { node = null; diff --git a/BMA.EHR.Leave/Controllers/LeaveController.cs b/BMA.EHR.Leave/Controllers/LeaveController.cs index 809a28e3..9d7c4581 100644 --- a/BMA.EHR.Leave/Controllers/LeaveController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveController.cs @@ -2805,7 +2805,7 @@ namespace BMA.EHR.Leave.Service.Controllers } //var rawData = await _additionalCheckRequestRepository.GetAdditionalCheckRequests(year, month); - var rawData = await _additionalCheckRequestRepository.GetAdditionalCheckRequestsByAdminRole(year, month, role, nodeId, profileAdmin?.Node); + var rawData = await _additionalCheckRequestRepository.GetAdditionalCheckRequestsByAdminRole(year, month, role, nodeId, profileAdmin?.Node, keyword); var total = rawData.Count; From 4c189fdc4a5b08c797566ab117c35afd68107f7b Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Thu, 29 Jan 2026 10:07:29 +0700 Subject: [PATCH 077/178] Fix null reference for CheckIn and CheckOut location names in LeaveController --- BMA.EHR.Leave/Controllers/LeaveController.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/BMA.EHR.Leave/Controllers/LeaveController.cs b/BMA.EHR.Leave/Controllers/LeaveController.cs index 9d7c4581..56e79f48 100644 --- a/BMA.EHR.Leave/Controllers/LeaveController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveController.cs @@ -1462,7 +1462,7 @@ namespace BMA.EHR.Leave.Service.Controllers "LATE" : "NORMAL", CheckInIsLocation = d.IsLocationCheckIn, - CheckInLocationName = d.CheckInLocationName, + CheckInLocationName = d.CheckInLocationName ?? "", CheckOutDate = d.CheckOut == null ? null : d.CheckOut.Value.Date, CheckOutTime = d.CheckOut == null ? "" : d.CheckOut.Value.ToString("HH:mm:ss"), CheckOutLocation = d.CheckOutPOI ?? "", @@ -1477,7 +1477,7 @@ namespace BMA.EHR.Leave.Service.Controllers "NORMAL", CheckOutIsLocation = d.IsLocationCheckOut, - CheckOutLocationName = d.CheckOutLocationName, + CheckOutLocationName = d.CheckOutLocationName ?? "", IsEdit = _processUserTimeStampRepository.IsEditRequest(userId, d.CheckIn.Date) From e80f89117ce3b6fa36636e78a459e318e030dced Mon Sep 17 00:00:00 2001 From: harid Date: Thu, 29 Jan 2026 13:22:41 +0700 Subject: [PATCH 078/178] Change Call Org --- .../LeaveRequests/LeaveRequestRepository.cs | 3 +- .../AdditionalCheckRequestRepository.cs | 3 +- .../Repositories/UserProfileRepository.cs | 23 +++++ .../DisciplineDirectorController.cs | 2 +- .../Controllers/LeaveRequestController.cs | 99 ++++++++++++------- 5 files changed, 93 insertions(+), 37 deletions(-) diff --git a/BMA.EHR.Application/Repositories/Leaves/LeaveRequests/LeaveRequestRepository.cs b/BMA.EHR.Application/Repositories/Leaves/LeaveRequests/LeaveRequestRepository.cs index e0e519e8..57572c33 100644 --- a/BMA.EHR.Application/Repositories/Leaves/LeaveRequests/LeaveRequestRepository.cs +++ b/BMA.EHR.Application/Repositories/Leaves/LeaveRequests/LeaveRequestRepository.cs @@ -253,7 +253,8 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests public async Task> GetLeaveRequestByYearAsync(int year, Guid userId) { - var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(userId, AccessToken); + // var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(userId, AccessToken); + var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(userId, AccessToken); if (profile == null) { diff --git a/BMA.EHR.Application/Repositories/Leaves/TimeAttendants/AdditionalCheckRequestRepository.cs b/BMA.EHR.Application/Repositories/Leaves/TimeAttendants/AdditionalCheckRequestRepository.cs index 64e1e745..3dca61e1 100644 --- a/BMA.EHR.Application/Repositories/Leaves/TimeAttendants/AdditionalCheckRequestRepository.cs +++ b/BMA.EHR.Application/Repositories/Leaves/TimeAttendants/AdditionalCheckRequestRepository.cs @@ -74,7 +74,8 @@ namespace BMA.EHR.Application.Repositories.Leaves.TimeAttendants await base.AddAsync(entity); var userId = UserId != null ? Guid.Parse(UserId) : Guid.Empty; - var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(userId, AccessToken ?? ""); + // var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(userId, AccessToken ?? ""); + var profile = await _userProfileRepository.GetProfileByKeycloakIdNew2Async(userId, AccessToken ?? ""); // fix issue : SIT ระบบบันทึกเวลาปฏิบัติงาน>>ลงเวลากรณีพิเศษ (ไม่มีแจ้งเตือนไปยังผู้บังคับบัญชา) #969 // send noti + inbox + mail diff --git a/BMA.EHR.Application/Repositories/UserProfileRepository.cs b/BMA.EHR.Application/Repositories/UserProfileRepository.cs index b868a4e9..ebf5113f 100644 --- a/BMA.EHR.Application/Repositories/UserProfileRepository.cs +++ b/BMA.EHR.Application/Repositories/UserProfileRepository.cs @@ -209,6 +209,29 @@ namespace BMA.EHR.Application.Repositories } } + public async Task GetProfileByKeycloakIdNew2Async(Guid keycloakId, string? accessToken) + { + try + { + var apiPath = $"{_configuration["API"]}/org/dotnet/by-keycloak2/{keycloakId}"; + var apiKey = _configuration["API_KEY"]; + + var apiResult = await GetExternalAPIAsync(apiPath, accessToken ?? "", apiKey); + if (apiResult != null) + { + var raw = JsonConvert.DeserializeObject(apiResult); + if (raw != null) + return raw.Result; + } + + return null; + } + catch + { + throw; + } + } + public async Task GetProfileLeaveByKeycloakIdAsync(Guid keycloakId, string? accessToken) { diff --git a/BMA.EHR.Discipline.Service/Controllers/DisciplineDirectorController.cs b/BMA.EHR.Discipline.Service/Controllers/DisciplineDirectorController.cs index 202eaa44..c4bd24ae 100644 --- a/BMA.EHR.Discipline.Service/Controllers/DisciplineDirectorController.cs +++ b/BMA.EHR.Discipline.Service/Controllers/DisciplineDirectorController.cs @@ -392,7 +392,7 @@ namespace BMA.EHR.DisciplineDirector.Service.Controllers return Error(new Exception(GlobalMessages.DataNotFound), StatusCodes.Status404NotFound); var userId = UserId == null ? Guid.Empty : Guid.Parse(UserId); - var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(userId, token.Replace("Bearer ", "")); + var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(userId, token.Replace("Bearer ", "")); if (profile == null) return Error(GlobalMessages.DataNotFound); diff --git a/BMA.EHR.Leave/Controllers/LeaveRequestController.cs b/BMA.EHR.Leave/Controllers/LeaveRequestController.cs index 5d6c37c4..d0edb337 100644 --- a/BMA.EHR.Leave/Controllers/LeaveRequestController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveRequestController.cs @@ -212,7 +212,8 @@ namespace BMA.EHR.Leave.Service.Controllers var thisYear = DateTime.Now.Year; - var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(userId, AccessToken); + // var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(userId, AccessToken); + var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(userId, AccessToken); if (profile == null) { @@ -377,11 +378,15 @@ namespace BMA.EHR.Leave.Service.Controllers break; case "LV-008": { - var lastSalary = profile.ProfileSalary; - var lastSalaryAmount = lastSalary == null ? 0 : lastSalary.Amount == null ? 0 : lastSalary.Amount; - var lastSalaryAmountText = lastSalary == null ? "" : lastSalary.Amount == null ? "" : ((int)lastSalary.Amount).ToThaiBahtText(false); - leaveRequest.LeaveSalary = (int)lastSalaryAmount; - leaveRequest.LeaveSalaryText = lastSalaryAmountText; + // var lastSalary = profile.ProfileSalary; + // var lastSalaryAmount = lastSalary == null ? 0 : lastSalary.Amount == null ? 0 : lastSalary.Amount; + // var lastSalaryAmountText = lastSalary == null ? "" : lastSalary.Amount == null ? "" : ((int)lastSalary.Amount).ToThaiBahtText(false); + // leaveRequest.LeaveSalary = (int)lastSalaryAmount; + // leaveRequest.LeaveSalaryText = lastSalaryAmountText; + leaveRequest.LeaveSalary = profile.Amount.HasValue && profile.Amount > 0 + ? (int)profile.Amount : 0; + leaveRequest.LeaveSalaryText = profile.Amount.HasValue && profile.Amount > 0 + ? ((int)profile.Amount).ToThaiBahtText(false) : ""; //leaveRequest.LeaveSalary = lastSalary == null ? 0 : (int)lastSalary.Amount.Value; //leaveRequest.LeaveSalaryText = // lastSalary == null ? "" : ((int)lastSalary.Amount.Value).ToThaiBahtText(false); @@ -401,11 +406,15 @@ namespace BMA.EHR.Leave.Service.Controllers break; case "LV-010": { - var lastSalary = profile.ProfileSalary; - var lastSalaryAmount = lastSalary == null ? 0 : lastSalary.Amount == null ? 0 : lastSalary.Amount; - var lastSalaryAmountText = lastSalary == null ? "" : lastSalary.Amount == null ? "" : ((int)lastSalary.Amount).ToThaiBahtText(false); - leaveRequest.LeaveSalary = (int)lastSalaryAmount; - leaveRequest.LeaveSalaryText = lastSalaryAmountText; + // var lastSalary = profile.ProfileSalary; + // var lastSalaryAmount = lastSalary == null ? 0 : lastSalary.Amount == null ? 0 : lastSalary.Amount; + // var lastSalaryAmountText = lastSalary == null ? "" : lastSalary.Amount == null ? "" : ((int)lastSalary.Amount).ToThaiBahtText(false); + // leaveRequest.LeaveSalary = (int)lastSalaryAmount; + // leaveRequest.LeaveSalaryText = lastSalaryAmountText; + leaveRequest.LeaveSalary = profile.Amount.HasValue && profile.Amount > 0 + ? (int)profile.Amount : 0; + leaveRequest.LeaveSalaryText = profile.Amount.HasValue && profile.Amount > 0 + ? ((int)profile.Amount).ToThaiBahtText(false) : ""; //leaveRequest.LeaveSalary = lastSalary == null ? 0 : (int)lastSalary.Amount.Value; //leaveRequest.LeaveSalaryText = // lastSalary == null ? "" : ((int)lastSalary.Amount.Value).ToThaiBahtText(false); @@ -491,7 +500,8 @@ namespace BMA.EHR.Leave.Service.Controllers foreach (var leave in leaves) { - var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(leave.KeycloakUserId, AccessToken); + // var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(leave.KeycloakUserId, AccessToken); + var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(leave.KeycloakUserId, AccessToken); if (profile != null) { leave.Prefix = profile.Prefix; @@ -551,7 +561,8 @@ namespace BMA.EHR.Leave.Service.Controllers // return Error("ไม่สามารถขอลาในช่วงเวลาเดียวกันได้"); // } - var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(userId, AccessToken); + // var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(userId, AccessToken); + var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(userId, AccessToken); if (profile == null) { @@ -676,11 +687,15 @@ namespace BMA.EHR.Leave.Service.Controllers break; case "LV-008": { - var lastSalary = profile.ProfileSalary; + // var lastSalary = profile.ProfileSalary; - oldData.LeaveSalary = lastSalary == null ? 0 : (int)lastSalary.Amount.Value; - oldData.LeaveSalaryText = - lastSalary == null ? "" : ((int)lastSalary.Amount.Value).ToThaiBahtText(false); + // oldData.LeaveSalary = lastSalary == null ? 0 : (int)lastSalary.Amount.Value; + // oldData.LeaveSalaryText = + // lastSalary == null ? "" : ((int)lastSalary.Amount.Value).ToThaiBahtText(false); + oldData.LeaveSalary = profile.Amount.HasValue && profile.Amount > 0 + ? (int)profile.Amount : 0; + oldData.LeaveSalaryText = profile.Amount.HasValue && profile.Amount > 0 + ? ((int)profile.Amount).ToThaiBahtText(false) : ""; oldData.LeaveBirthDate = profile.BirthDate; oldData.LeaveGovernmentDate = profile.DateStart; @@ -696,12 +711,15 @@ namespace BMA.EHR.Leave.Service.Controllers break; case "LV-010": { - var lastSalary = profile.ProfileSalary; - - oldData.LeaveSalary = lastSalary == null ? 0 : (int)lastSalary.Amount.Value; - oldData.LeaveSalaryText = - lastSalary == null ? "" : ((int)lastSalary.Amount.Value).ToThaiBahtText(false); + // var lastSalary = profile.ProfileSalary; + // oldData.LeaveSalary = lastSalary == null ? 0 : (int)lastSalary.Amount.Value; + // oldData.LeaveSalaryText = + // lastSalary == null ? "" : ((int)lastSalary.Amount.Value).ToThaiBahtText(false); + oldData.LeaveSalary = profile.Amount.HasValue && profile.Amount > 0 + ? (int)profile.Amount : 0; + oldData.LeaveSalaryText = profile.Amount.HasValue && profile.Amount > 0 + ? ((int)profile.Amount).ToThaiBahtText(false) : ""; oldData.CoupleDayName = req.CoupleDayName ?? ""; oldData.CoupleDayPosition = req.CoupleDayPosition ?? ""; oldData.CoupleDayLevel = req.CoupleDayLevel ?? ""; @@ -844,7 +862,8 @@ namespace BMA.EHR.Leave.Service.Controllers if (toDay >= startFiscalDate && toDay <= endFiscalDate) thisYear = thisYear + 1; - var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(userId, AccessToken); + // var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(userId, AccessToken); + var profile = await _userProfileRepository.GetProfileByKeycloakIdNew2Async(userId, AccessToken); if (profile == null) { return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound); @@ -887,7 +906,7 @@ namespace BMA.EHR.Leave.Service.Controllers var restCurrentDay = 10.0; var sumLeave = leaveData == null ? 0 : leaveData.LeaveDaysUsed; - var lastSalary = profile.ProfileSalary; + // var lastSalary = profile.ProfileSalary; var leaveLast = await _leaveRequestRepository.GetLeaveLastByTypeForUserAsync(userId, req.Type); @@ -912,8 +931,12 @@ namespace BMA.EHR.Leave.Service.Controllers RestDayTotalCurrent = restCurrentDay,// 10 วันเสมอ (LV-005) BirthDate = profile.BirthDate.Date, DateAppoint = profile.DateAppoint == null ? null : profile.DateAppoint.Value.Date, - Salary = lastSalary == null ? 0 : lastSalary.Amount == null ? 0 : (int)lastSalary.Amount.Value, - SalaryText = lastSalary == null ? "" : lastSalary.Amount == null ? "" : ((int)lastSalary.Amount.Value).ToThaiBahtText(false), + // Salary = lastSalary == null ? 0 : lastSalary.Amount == null ? 0 : (int)lastSalary.Amount.Value, + // SalaryText = lastSalary == null ? "" : lastSalary.Amount == null ? "" : ((int)lastSalary.Amount.Value).ToThaiBahtText(false), + Salary = profile.Amount.HasValue && profile.Amount > 0 + ? (int)profile.Amount : 0, + SalaryText = profile.Amount.HasValue && profile.Amount > 0 + ? ((int)profile.Amount).ToThaiBahtText(false) : "", LeaveLast = leaveLast == null ? null : leaveLast, TelephoneNumber = profile.TelephoneNumber ?? "", @@ -945,7 +968,8 @@ namespace BMA.EHR.Leave.Service.Controllers var userId = UserId == null ? Guid.Empty : Guid.Parse(UserId); - var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(userId, AccessToken); + // var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(userId, AccessToken); + var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(userId, AccessToken); var govAge = (profile?.DateStart?.Date ?? DateTime.Now.Date).DiffDay(DateTime.Now.Date); var thisYear = DateTime.Now.Year; @@ -1495,7 +1519,8 @@ namespace BMA.EHR.Leave.Service.Controllers return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound); } - var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(rawData.KeycloakUserId, AccessToken); + // var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(rawData.KeycloakUserId, AccessToken); + var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(rawData.KeycloakUserId, AccessToken); if (profile == null) { @@ -1507,8 +1532,8 @@ namespace BMA.EHR.Leave.Service.Controllers var userCalendar = await _userCalendarRepository.GetExist(profile.Id); var category = userCalendar == null ? "NORMAL" : userCalendar.Calendar; - var lastSalary = profile.ProfileSalary; - var lastSalaryAmount = lastSalary == null ? 0 : lastSalary.Amount ?? 0; + // var lastSalary = profile.ProfileSalary; + // var lastSalaryAmount = lastSalary == null ? 0 : lastSalary.Amount ?? 0; var lastLeaveRequest = await _leaveRequestRepository.GetLastLeaveRequestByTypeForUserAsync(rawData.KeycloakUserId, @@ -1573,8 +1598,12 @@ namespace BMA.EHR.Leave.Service.Controllers LeaveBirthDate = profile.BirthDate, LeaveGovernmentDate = profile.DateAppoint == null ? null : profile.DateAppoint.Value, - LeaveSalary = lastSalary == null ? 0 : lastSalaryAmount, - LeaveSalaryText = lastSalary == null ? "" : ((int)lastSalaryAmount).ToThaiBahtText(false), + // LeaveSalary = lastSalary == null ? 0 : lastSalaryAmount, + // LeaveSalaryText = lastSalary == null ? "" : ((int)lastSalaryAmount).ToThaiBahtText(false), + LeaveSalary = profile.Amount.HasValue && profile.Amount > 0 + ? (int)profile.Amount : 0, + LeaveSalaryText = profile.Amount.HasValue && profile.Amount > 0 + ? ((int)profile.Amount).ToThaiBahtText(false) : "", WifeDayName = rawData.WifeDayName, WifeDayDateBorn = rawData.WifeDayDateBorn, @@ -2024,7 +2053,8 @@ namespace BMA.EHR.Leave.Service.Controllers return Error(jsonData["message"]?.ToString(), StatusCodes.Status403Forbidden); } - var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(Guid.Parse(UserId!), AccessToken); + // var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(Guid.Parse(UserId!), AccessToken); + var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(Guid.Parse(UserId!), AccessToken); if (profile == null) { @@ -2649,7 +2679,8 @@ namespace BMA.EHR.Leave.Service.Controllers var sendList = await _leaveRequestRepository.GetSumSendLeaveAsync(thisYear); var rejectList = await _leaveRequestRepository.GetSumRejectLeaveAsync(thisYear); var deleteList = await _leaveRequestRepository.GetSumDeleteLeaveAsync(thisYear); - var pf = await _userProfileRepository.GetProfileByKeycloakIdAsync(userId, AccessToken); + // var pf = await _userProfileRepository.GetProfileByKeycloakIdAsync(userId, AccessToken); + var pf = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(userId, AccessToken); if (pf == null) { From 46504c9e30a692e5c1836efb722b0849e4db20ec Mon Sep 17 00:00:00 2001 From: harid Date: Thu, 29 Jan 2026 13:37:38 +0700 Subject: [PATCH 079/178] Change Call Org --- .../LeaveRequests/LeaveBeginingRepository.cs | 9 ++++++--- .../LeaveRequests/LeaveRequestRepository.cs | 18 ++++++++++++------ 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/BMA.EHR.Application/Repositories/Leaves/LeaveRequests/LeaveBeginingRepository.cs b/BMA.EHR.Application/Repositories/Leaves/LeaveRequests/LeaveBeginingRepository.cs index 575a85bb..dfb5897d 100644 --- a/BMA.EHR.Application/Repositories/Leaves/LeaveRequests/LeaveBeginingRepository.cs +++ b/BMA.EHR.Application/Repositories/Leaves/LeaveRequests/LeaveBeginingRepository.cs @@ -79,7 +79,8 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests public async Task UpdateLeaveUsageAsync(int year, Guid typeId, Guid userId, double day) { - var pf = await _userProfileRepository.GetProfileByKeycloakIdAsync(userId, AccessToken); + // var pf = await _userProfileRepository.GetProfileByKeycloakIdAsync(userId, AccessToken); + var pf = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(userId, AccessToken); if (pf == null) { throw new Exception(GlobalMessages.DataNotFound); @@ -100,7 +101,8 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests public async Task GetByYearAndTypeIdForUserAsync(int year, Guid typeId, Guid userId) { - var pf = await _userProfileRepository.GetProfileByKeycloakIdAsync(userId, AccessToken); + // var pf = await _userProfileRepository.GetProfileByKeycloakIdAsync(userId, AccessToken); + var pf = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(userId, AccessToken); if (pf == null) { throw new Exception(GlobalMessages.DataNotFound); @@ -238,7 +240,8 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests public async Task GetByYearAndTypeIdForUser2Async(int year, Guid typeId, Guid userId) { - var pf = await _userProfileRepository.GetProfileByKeycloakIdAsync(userId, AccessToken); + // var pf = await _userProfileRepository.GetProfileByKeycloakIdAsync(userId, AccessToken); + var pf = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(userId, AccessToken); if (pf == null) { return null; diff --git a/BMA.EHR.Application/Repositories/Leaves/LeaveRequests/LeaveRequestRepository.cs b/BMA.EHR.Application/Repositories/Leaves/LeaveRequests/LeaveRequestRepository.cs index 57572c33..b97441be 100644 --- a/BMA.EHR.Application/Repositories/Leaves/LeaveRequests/LeaveRequestRepository.cs +++ b/BMA.EHR.Application/Repositories/Leaves/LeaveRequests/LeaveRequestRepository.cs @@ -496,7 +496,8 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests public async Task GetSumLeaveByTypeForUserAsync(Guid keycloakUserId, Guid leaveTypeId, int year) { - var pf = await _userProfileRepository.GetProfileByKeycloakIdAsync(keycloakUserId, AccessToken); + // var pf = await _userProfileRepository.GetProfileByKeycloakIdAsync(keycloakUserId, AccessToken); + var pf = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(keycloakUserId, AccessToken); if (pf == null) throw new Exception(GlobalMessages.DataNotFound); @@ -632,7 +633,8 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests { try { - var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(data.KeycloakUserId, AccessToken ?? ""); + // var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(data.KeycloakUserId, AccessToken ?? ""); + var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(data.KeycloakUserId, AccessToken ?? ""); if (profile == null) { throw new Exception(GlobalMessages.DataNotFound); @@ -705,7 +707,8 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests throw new Exception(GlobalMessages.DataNotFound); } - var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(rawData.KeycloakUserId, AccessToken ?? ""); + // var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(rawData.KeycloakUserId, AccessToken ?? ""); + var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(rawData.KeycloakUserId, AccessToken ?? ""); if (profile == null) { throw new Exception(GlobalMessages.DataNotFound); @@ -791,7 +794,8 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests throw new Exception(GlobalMessages.DataNotFound); } - var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(rawData.KeycloakUserId, AccessToken ?? ""); + // var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(rawData.KeycloakUserId, AccessToken ?? ""); + var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(rawData.KeycloakUserId, AccessToken ?? ""); if (profile == null) { throw new Exception(GlobalMessages.DataNotFound); @@ -1215,7 +1219,8 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests } else { - var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(rawData.KeycloakUserId, AccessToken); + // var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(rawData.KeycloakUserId, AccessToken); + var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(rawData.KeycloakUserId, AccessToken); if (profile == null) { throw new Exception(GlobalMessages.DataNotFound); @@ -1378,7 +1383,8 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests } else { - var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(rawData.KeycloakUserId, AccessToken); + // var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(rawData.KeycloakUserId, AccessToken); + var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(rawData.KeycloakUserId, AccessToken); if (profile == null) { throw new Exception(GlobalMessages.DataNotFound); From 06b53ddeaa41c96e3d6e62df6fe01daae2b214fa Mon Sep 17 00:00:00 2001 From: harid Date: Thu, 29 Jan 2026 15:09:14 +0700 Subject: [PATCH 080/178] Fix Bug Leave Report Issue #2233 --- .../Controllers/LeaveReportController.cs | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/BMA.EHR.Leave/Controllers/LeaveReportController.cs b/BMA.EHR.Leave/Controllers/LeaveReportController.cs index e71b4780..b89f22a4 100644 --- a/BMA.EHR.Leave/Controllers/LeaveReportController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveReportController.cs @@ -160,7 +160,7 @@ namespace BMA.EHR.Leave.Service.Controllers var Oc = profile.isCommission == false ? profile.Oc.ToThaiNumber() : profile.Oc.Replace("สำนักงานคณะกรรมการข้าราชการกรุงเทพมหานคร", "สำนักงาน ก.ก.").ToThaiNumber(); - var approveResult = await GetApproverData(data.Approvers, profile.isCommission); + var approveResult = await GetApproverData(data.Approvers); return new { @@ -251,7 +251,7 @@ namespace BMA.EHR.Leave.Service.Controllers var Oc = profile.isCommission == false ? profile.Oc.ToThaiNumber() : profile.Oc.Replace("สำนักงานคณะกรรมการข้าราชการกรุงเทพมหานคร", "สำนักงาน ก.ก.").ToThaiNumber(); - var approveResult = await GetApproverData(data.Approvers, profile.isCommission); + var approveResult = await GetApproverData(data.Approvers); return new { template = "leave10", @@ -341,7 +341,7 @@ namespace BMA.EHR.Leave.Service.Controllers var Oc = profile.isCommission == false ? profile.Oc.ToThaiNumber() : profile.Oc.Replace("สำนักงานคณะกรรมการข้าราชการกรุงเทพมหานคร", "สำนักงาน ก.ก.").ToThaiNumber(); - var approveResult = await GetApproverData(data.Approvers, profile.isCommission); + var approveResult = await GetApproverData(data.Approvers); return new { template = "leave11", @@ -434,7 +434,7 @@ namespace BMA.EHR.Leave.Service.Controllers var Oc = profile.isCommission == false ? profile.Oc.ToThaiNumber() : profile.Oc.Replace("สำนักงานคณะกรรมการข้าราชการกรุงเทพมหานคร", "สำนักงาน ก.ก.").ToThaiNumber(); - var approveResult = await GetApproverData(data.Approvers, profile.isCommission); + var approveResult = await GetApproverData(data.Approvers); if (isHajj == true) { return new @@ -577,7 +577,7 @@ namespace BMA.EHR.Leave.Service.Controllers var Oc = profile.isCommission == false ? profile.Oc.ToThaiNumber() : profile.Oc.Replace("สำนักงานคณะกรรมการข้าราชการกรุงเทพมหานคร", "สำนักงาน ก.ก.").ToThaiNumber(); - var approveResult = await GetApproverData(data.Approvers, profile.isCommission); + var approveResult = await GetApproverData(data.Approvers); return new { template = "leave14", @@ -658,7 +658,7 @@ namespace BMA.EHR.Leave.Service.Controllers var Oc = profile.isCommission == false ? profile.Oc.ToThaiNumber() : profile.Oc.Replace("สำนักงานคณะกรรมการข้าราชการกรุงเทพมหานคร", "สำนักงาน ก.ก.").ToThaiNumber(); - var approveResult = await GetApproverData(data.Approvers, profile.isCommission); + var approveResult = await GetApproverData(data.Approvers); return new { template = "leave15", @@ -761,7 +761,7 @@ namespace BMA.EHR.Leave.Service.Controllers var Oc = profile.isCommission == false ? profile.Oc.ToThaiNumber() : profile.Oc.Replace("สำนักงานคณะกรรมการข้าราชการกรุงเทพมหานคร", "สำนักงาน ก.ก.").ToThaiNumber(); - var approveResult = await GetApproverData(data.Approvers, profile.isCommission); + var approveResult = await GetApproverData(data.Approvers); return new { template = "leave16", @@ -880,7 +880,7 @@ namespace BMA.EHR.Leave.Service.Controllers var Oc = profile.isCommission == false ? profile.Oc.ToThaiNumber() : profile.Oc.Replace("สำนักงานคณะกรรมการข้าราชการกรุงเทพมหานคร", "สำนักงาน ก.ก.").ToThaiNumber(); - var approveResult = await GetApproverData(data.Approvers, profile.isCommission); + var approveResult = await GetApproverData(data.Approvers); return new { template = "leave17", @@ -976,7 +976,7 @@ namespace BMA.EHR.Leave.Service.Controllers var Oc = profile.isCommission == false ? profile.Oc.ToThaiNumber() : profile.Oc.Replace("สำนักงานคณะกรรมการข้าราชการกรุงเทพมหานคร", "สำนักงาน ก.ก.").ToThaiNumber(); - var approveResult = await GetApproverData(data.Approvers, profile.isCommission); + var approveResult = await GetApproverData(data.Approvers); return new { template = "leave18", @@ -1202,7 +1202,7 @@ namespace BMA.EHR.Leave.Service.Controllers var Oc = profile.isCommission == false ? profile.Oc.ToThaiNumber() : profile.Oc.Replace("สำนักงานคณะกรรมการข้าราชการกรุงเทพมหานคร", "สำนักงาน ก.ก.").ToThaiNumber(); - var approveResult = await GetApproverData(data.Approvers, profile.isCommission); + var approveResult = await GetApproverData(data.Approvers); var result = new { template = "แบบใบขอยกเลิกวันลา", @@ -2888,7 +2888,7 @@ namespace BMA.EHR.Leave.Service.Controllers } #endregion - private async Task GetApproverData(List list, bool? isCommission = false) + private async Task GetApproverData(List list) { var _default = new { @@ -2919,10 +2919,10 @@ namespace BMA.EHR.Leave.Service.Controllers : $"{x.PositionName}{x.PositionLevelName}").ToThaiNumber(), posExOrg = !string.IsNullOrEmpty(x.PositionLevelName) && (x.PositionLevelName.Contains("อำนวยการ") || x.PositionLevelName.Contains("บริหาร")) - ? new[] { (isCommission == false ? x!.OrganizationName : x!.OrganizationName.Replace("สำนักงานคณะกรรมการข้าราชการกรุงเทพมหานคร", "สำนักงาน ก.ก.")).ToThaiNumber() } + ? new[] { (x!.OrganizationName.Replace("สำนักงานคณะกรรมการข้าราชการกรุงเทพมหานคร", "สำนักงาน ก.ก.")).ToThaiNumber() } : !string.IsNullOrEmpty(x.PosExecutiveName) - ? new[] { (x.PosExecutiveName.ToThaiNumber()), (isCommission == false ? x!.OrganizationName : x!.OrganizationName.Replace("สำนักงานคณะกรรมการข้าราชการกรุงเทพมหานคร", "สำนักงาน ก.ก.")).ToThaiNumber() } - : new[] { (isCommission == false ? x!.OrganizationName : x!.OrganizationName.Replace("สำนักงานคณะกรรมการข้าราชการกรุงเทพมหานคร", "สำนักงาน ก.ก.")).ToThaiNumber() }, + ? new[] { x.PosExecutiveName.ToThaiNumber(), x!.OrganizationName.Replace("สำนักงานคณะกรรมการข้าราชการกรุงเทพมหานคร", "สำนักงาน ก.ก.").ToThaiNumber() } + : new[] { x!.OrganizationName.Replace("สำนักงานคณะกรรมการข้าราชการกรุงเทพมหานคร", "สำนักงาน ก.ก.").ToThaiNumber() }, positionSign = !string.IsNullOrEmpty(x.PositionSign) ? x.PositionSign.Replace("\r", "").Replace("\n", " ").ToThaiNumber() : "............................................", From 5c05f1123a393f05a7978e3be208587504c1b461 Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Fri, 30 Jan 2026 09:49:54 +0700 Subject: [PATCH 081/178] Increase HttpClient timeout for long-running operations --- BMA.EHR.Leave/Controllers/LeaveController.cs | 499 ++++++++++--------- BMA.EHR.Leave/Program.cs | 6 +- 2 files changed, 259 insertions(+), 246 deletions(-) diff --git a/BMA.EHR.Leave/Controllers/LeaveController.cs b/BMA.EHR.Leave/Controllers/LeaveController.cs index 56e79f48..062ceab1 100644 --- a/BMA.EHR.Leave/Controllers/LeaveController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveController.cs @@ -948,93 +948,114 @@ namespace BMA.EHR.Leave.Service.Controllers var fileName = $"{_bucketName}/{userId}/{currentDate.ToString("dd-MM-yyyy")}/{check_status}/{data.CheckInFileName}"; using (var ms = new MemoryStream(data.CheckInFileBytes ?? new byte[0])) { - await _minIOService.UploadFileAsync(fileName, ms); + try + { + await _minIOService.UploadFileAsync(fileName, ms); + } + catch (Exception ex) + { + await _checkInJobStatusRepository.UpdateToFailedAsync(taskId, $"ไม่สามารถอัปโหลดรูปภาพได้: {ex.Message}"); + return Error($"ไม่สามารถอัปโหลดรูปภาพได้: {ex.Message}", StatusCodes.Status500InternalServerError); + } + } - var defaultRound = await _dutyTimeRepository.GetDefaultAsync(); - if (defaultRound == null) - { - return Error("ไม่พบรอบการลงเวลาทำงาน Default", StatusCodes.Status404NotFound); - } - - var effectiveDate = await _userDutyTimeRepository.GetLastEffectRound(profile.Id, currentDate); - var roundId = effectiveDate != null ? effectiveDate.DutyTimeId : Guid.Empty; - var userRound = await _dutyTimeRepository.GetByIdAsync(roundId); - - // TODO : รอดุึงรอบที่ผูกกับ user - var duty = userRound ?? defaultRound; - - // create check in object - if (data.CheckInId == null) - { - // validate duplicate check in - var currentCheckIn = await _userTimeStampRepository.GetTimestampByDateAsync(userId, currentDate); - - if (currentCheckIn != null) + var defaultRound = await _dutyTimeRepository.GetDefaultAsync(); + if (defaultRound == null) { - return Error(new Exception("ไม่สามารถลงเวลาได้ เนื่องจากมีการลงเวลาในวันนี้แล้ว!"), StatusCodes.Status400BadRequest); + await _checkInJobStatusRepository.UpdateToFailedAsync(taskId, "ไม่พบรอบการลงเวลาทำงาน Default"); + return Error("ไม่พบรอบการลงเวลาทำงาน Default", StatusCodes.Status404NotFound); } - var checkin = new UserTimeStamp + var effectiveDate = await _userDutyTimeRepository.GetLastEffectRound(profile.Id, currentDate); + var roundId = effectiveDate != null ? effectiveDate.DutyTimeId : Guid.Empty; + var userRound = await _dutyTimeRepository.GetByIdAsync(roundId); + + // TODO : รอดุึงรอบที่ผูกกับ user + var duty = userRound ?? defaultRound; + + // create check in object + if (data.CheckInId == null) { - KeycloakUserId = userId, - CheckInLat = data.Lat, - CheckInLon = data.Lon, - IsLocationCheckIn = data.IsLocation, - CheckInLocationName = data.LocationName, - CheckInPOI = data.POI, - CheckInRemark = data.Remark, - CheckInImageUrl = fileName, - CheckIn = currentDate, - Prefix = profile.Prefix, - FirstName = profile.FirstName, - LastName = profile.LastName, - CitizenId = profile.CitizenId, + // validate duplicate check in + var currentCheckIn = await _userTimeStampRepository.GetTimestampByDateAsync(userId, currentDate); - Root = profile.Root, - Child1 = profile.Child1, - Child2 = profile.Child2, - Child3 = profile.Child3, - Child4 = profile.Child4, + if (currentCheckIn != null) + { + return Error(new Exception("ไม่สามารถลงเวลาได้ เนื่องจากมีการลงเวลาในวันนี้แล้ว!"), StatusCodes.Status400BadRequest); + } - RootId = profile.RootId, - Child1Id = profile.Child1Id, - Child2Id = profile.Child2Id, - Child3Id = profile.Child3Id, - Child4Id = profile.Child4Id, - Gender = profile.Gender, + var checkin = new UserTimeStamp + { + KeycloakUserId = userId, + CheckInLat = data.Lat, + CheckInLon = data.Lon, + IsLocationCheckIn = data.IsLocation, + CheckInLocationName = data.LocationName, + CheckInPOI = data.POI, + CheckInRemark = data.Remark, + CheckInImageUrl = fileName, + CheckIn = currentDate, + Prefix = profile.Prefix, + FirstName = profile.FirstName, + LastName = profile.LastName, + CitizenId = profile.CitizenId, - ProfileId = profile.Id, - ProfileType = profile.ProfileType, + Root = profile.Root, + Child1 = profile.Child1, + Child2 = profile.Child2, + Child3 = profile.Child3, + Child4 = profile.Child4, - RootDnaId = profile.RootDnaId, - Child1DnaId = profile.Child1DnaId, - Child2DnaId = profile.Child2DnaId, - Child3DnaId = profile.Child3DnaId, - Child4DnaId = profile.Child4DnaId, - }; + RootId = profile.RootId, + Child1Id = profile.Child1Id, + Child2Id = profile.Child2Id, + Child3Id = profile.Child3Id, + Child4Id = profile.Child4Id, + Gender = profile.Gender, - var startTime = ""; - var endTime = ""; - if (!data.IsLocation && data.LocationName == "ไปประชุม / อบรม / สัมมนา") - { - //startTime = "09:30"; - startTime = "10:30"; - endTime = "12:00"; - } - else - { - startTime = duty.StartTimeMorning; - endTime = duty.EndTimeMorning; - } + ProfileId = profile.Id, + ProfileType = profile.ProfileType, - string checkInStatus = "NORMAL"; - var leaveReq = await _leaveRequestRepository.GetLeavePeriodAsync(userId, currentDate.Date); - if (leaveReq != null) - { - var leaveRange = leaveReq.LeaveRange == null ? "" : leaveReq.LeaveRange.ToUpper(); - if (leaveRange == "MORNING" || leaveRange == "ALL") - checkInStatus = "NORMAL"; + RootDnaId = profile.RootDnaId, + Child1DnaId = profile.Child1DnaId, + Child2DnaId = profile.Child2DnaId, + Child3DnaId = profile.Child3DnaId, + Child4DnaId = profile.Child4DnaId, + }; + + var startTime = ""; + var endTime = ""; + if (!data.IsLocation && data.LocationName == "ไปประชุม / อบรม / สัมมนา") + { + //startTime = "09:30"; + startTime = "10:30"; + endTime = "12:00"; + } + else + { + startTime = duty.StartTimeMorning; + endTime = duty.EndTimeMorning; + } + + string checkInStatus = "NORMAL"; + var leaveReq = await _leaveRequestRepository.GetLeavePeriodAsync(userId, currentDate.Date); + if (leaveReq != null) + { + var leaveRange = leaveReq.LeaveRange == null ? "" : leaveReq.LeaveRange.ToUpper(); + if (leaveRange == "MORNING" || leaveRange == "ALL") + checkInStatus = "NORMAL"; + else + { + checkInStatus = DateTime.Parse(currentDate.ToString("yyyy-MM-dd HH:mm")) > + DateTime.Parse($"{currentDate.ToString("yyyy-MM-dd")} {startTime}") ? + DateTime.Parse(currentDate.ToString("yyyy-MM-dd HH:mm")) > + DateTime.Parse($"{currentDate.ToString("yyyy-MM-dd")} {duty.EndTimeMorning}") ? + "ABSENT" : + "LATE" : + "NORMAL"; + } + } else { checkInStatus = DateTime.Parse(currentDate.ToString("yyyy-MM-dd HH:mm")) > @@ -1045,123 +1066,127 @@ namespace BMA.EHR.Leave.Service.Controllers "LATE" : "NORMAL"; } - } - else - { - checkInStatus = DateTime.Parse(currentDate.ToString("yyyy-MM-dd HH:mm")) > - DateTime.Parse($"{currentDate.ToString("yyyy-MM-dd")} {startTime}") ? - DateTime.Parse(currentDate.ToString("yyyy-MM-dd HH:mm")) > - DateTime.Parse($"{currentDate.ToString("yyyy-MM-dd")} {duty.EndTimeMorning}") ? - "ABSENT" : - "LATE" : - "NORMAL"; - } - + - // process - รอทำใน queue - var checkin_process = new ProcessUserTimeStamp - { - KeycloakUserId = userId, - CheckInLat = data.Lat, - CheckInLon = data.Lon, - IsLocationCheckIn = data.IsLocation, - CheckInLocationName = data.LocationName, - CheckInPOI = data.POI, - CheckInRemark = data.Remark, - CheckInImageUrl = fileName, - CheckIn = currentDate, - CheckInStatus = checkInStatus, - Prefix = profile.Prefix, - FirstName = profile.FirstName, - LastName = profile.LastName, - CitizenId = profile.CitizenId, - - Root = profile.Root, - Child1 = profile.Child1, - Child2 = profile.Child2, - Child3 = profile.Child3, - Child4 = profile.Child4, - - RootId = profile.RootId, - Child1Id = profile.Child1Id, - Child2Id = profile.Child2Id, - Child3Id = profile.Child3Id, - Child4Id = profile.Child4Id, - Gender = profile.Gender, - - ProfileId = profile.Id, - ProfileType = profile.ProfileType, - - - RootDnaId = profile.RootDnaId, - Child1DnaId = profile.Child1DnaId, - Child2DnaId = profile.Child2DnaId, - Child3DnaId = profile.Child3DnaId, - Child4DnaId = profile.Child4DnaId, - }; - - await _userTimeStampRepository.AddAsync(checkin); - await _processUserTimeStampRepository.AddAsync(checkin_process); - } - else - { - var checkout = await _userTimeStampRepository.GetByIdAsync(data.CheckInId.Value); - - var currentCheckInProcess = await _processUserTimeStampRepository.GetTimestampByDateAsync(userId, checkout.CheckIn.Date); - - var checkout_process = await _processUserTimeStampRepository.GetByIdAsync(currentCheckInProcess.Id); - - - if (checkout != null) - { - checkout.CheckOutLat = data.Lat; - checkout.CheckOutLon = data.Lon; - checkout.IsLocationCheckOut = data.IsLocation; - checkout.CheckOutLocationName = data.LocationName; - checkout.CheckOutPOI = data.POI; - checkout.CheckOutRemark = data.Remark; - checkout.CheckOutImageUrl = fileName; - checkout.CheckOut = currentDate; - - await _userTimeStampRepository.UpdateAsync(checkout); - } - else - { - return Error(new Exception(GlobalMessages.DataNotFound), StatusCodes.Status404NotFound); - } - - var endTime = ""; - var startTime = ""; - var endTimeMorning = ""; - if (!data.IsLocation && data.LocationName == "ไปประชุม / อบรม / สัมมนา") - { - startTime = "13:00"; - endTime = "14:30"; - endTimeMorning = "12:00"; - } - else - { - endTime = duty.EndTimeAfternoon; - startTime = duty.StartTimeAfternoon; - endTimeMorning = duty.EndTimeMorning; - } - - - - string checkOutStatus = "NORMAL"; - var leaveReq = await _leaveRequestRepository.GetLeavePeriodAsync(userId, currentDate.Date); - if (leaveReq != null) - { - var leaveRange = leaveReq.LeaveRangeEnd == null ? "" : leaveReq.LeaveRangeEnd.ToUpper(); - if (leaveRange == "AFTERNOON" || leaveRange == "ALL") + // process - รอทำใน queue + var checkin_process = new ProcessUserTimeStamp { - if(DateTime.Parse(currentDate.ToString("yyyy-MM-dd HH:mm")) < - DateTime.Parse($"{currentDate.ToString("yyyy-MM-dd")} {endTimeMorning}")) - checkOutStatus = "ABSENT"; - else - checkOutStatus = "NORMAL"; + KeycloakUserId = userId, + CheckInLat = data.Lat, + CheckInLon = data.Lon, + IsLocationCheckIn = data.IsLocation, + CheckInLocationName = data.LocationName, + CheckInPOI = data.POI, + CheckInRemark = data.Remark, + CheckInImageUrl = fileName, + CheckIn = currentDate, + CheckInStatus = checkInStatus, + Prefix = profile.Prefix, + FirstName = profile.FirstName, + LastName = profile.LastName, + CitizenId = profile.CitizenId, + + Root = profile.Root, + Child1 = profile.Child1, + Child2 = profile.Child2, + Child3 = profile.Child3, + Child4 = profile.Child4, + + RootId = profile.RootId, + Child1Id = profile.Child1Id, + Child2Id = profile.Child2Id, + Child3Id = profile.Child3Id, + Child4Id = profile.Child4Id, + Gender = profile.Gender, + + ProfileId = profile.Id, + ProfileType = profile.ProfileType, + + + RootDnaId = profile.RootDnaId, + Child1DnaId = profile.Child1DnaId, + Child2DnaId = profile.Child2DnaId, + Child3DnaId = profile.Child3DnaId, + Child4DnaId = profile.Child4DnaId, + }; + + await _userTimeStampRepository.AddAsync(checkin); + await _processUserTimeStampRepository.AddAsync(checkin_process); + } + else + { + var checkout = await _userTimeStampRepository.GetByIdAsync(data.CheckInId.Value); + + var currentCheckInProcess = await _processUserTimeStampRepository.GetTimestampByDateAsync(userId, checkout.CheckIn.Date); + + var checkout_process = await _processUserTimeStampRepository.GetByIdAsync(currentCheckInProcess.Id); + + + if (checkout != null) + { + checkout.CheckOutLat = data.Lat; + checkout.CheckOutLon = data.Lon; + checkout.IsLocationCheckOut = data.IsLocation; + checkout.CheckOutLocationName = data.LocationName; + checkout.CheckOutPOI = data.POI; + checkout.CheckOutRemark = data.Remark; + checkout.CheckOutImageUrl = fileName; + checkout.CheckOut = currentDate; + + await _userTimeStampRepository.UpdateAsync(checkout); } else + { + await _checkInJobStatusRepository.UpdateToFailedAsync(taskId, "ไม่พบข้อมูลการลงเวลาทำงาน"); + return Error(new Exception(GlobalMessages.DataNotFound), StatusCodes.Status404NotFound); + } + + var endTime = ""; + var startTime = ""; + var endTimeMorning = ""; + if (!data.IsLocation && data.LocationName == "ไปประชุม / อบรม / สัมมนา") + { + startTime = "13:00"; + endTime = "14:30"; + endTimeMorning = "12:00"; + } + else + { + endTime = duty.EndTimeAfternoon; + startTime = duty.StartTimeAfternoon; + endTimeMorning = duty.EndTimeMorning; + } + string checkOutStatus = "NORMAL"; + var leaveReq = await _leaveRequestRepository.GetLeavePeriodAsync(userId, currentDate.Date); + if (leaveReq != null) + { + var leaveRange = leaveReq.LeaveRangeEnd == null ? "" : leaveReq.LeaveRangeEnd.ToUpper(); + if (leaveRange == "AFTERNOON" || leaveRange == "ALL") + { + if(DateTime.Parse(currentDate.ToString("yyyy-MM-dd HH:mm")) < + DateTime.Parse($"{currentDate.ToString("yyyy-MM-dd")} {endTimeMorning}")) + checkOutStatus = "ABSENT"; + else + checkOutStatus = "NORMAL"; + } + else + { + // fix issue : SIT ระบบบันทึกเวลาปฏิบัติงาน>>ลงเวลาเข้า-ออกงาน (กรณีลงเวลาออกอีกวัน) #921 + checkOutStatus = DateTime.Parse(currentDate.ToString("yyyy-MM-dd HH:mm")) < + DateTime.Parse($"{currentDate.ToString("yyyy-MM-dd")} {duty.EndTimeAfternoon}") ? + // "ABSENT" : + checkout.CheckIn.Date < currentDate.Date ? "NORMAL" : + DateTime.Parse(currentDate.ToString("yyyy-MM-dd HH:mm")) >= + DateTime.Parse($"{currentDate.ToString("yyyy-MM-dd")} {endTime}") ? + "NORMAL" : + "ABSENT" : + DateTime.Parse(currentDate.ToString("yyyy-MM-dd HH:mm")) < + DateTime.Parse($"{currentDate.ToString("yyyy-MM-dd")} {endTimeMorning}") ? + "ABSENT" : + "NORMAL"; + } + } + else { // fix issue : SIT ระบบบันทึกเวลาปฏิบัติงาน>>ลงเวลาเข้า-ออกงาน (กรณีลงเวลาออกอีกวัน) #921 checkOutStatus = DateTime.Parse(currentDate.ToString("yyyy-MM-dd HH:mm")) < @@ -1176,71 +1201,55 @@ namespace BMA.EHR.Leave.Service.Controllers DateTime.Parse($"{currentDate.ToString("yyyy-MM-dd")} {endTimeMorning}") ? "ABSENT" : "NORMAL"; - } - } - else - { - // fix issue : SIT ระบบบันทึกเวลาปฏิบัติงาน>>ลงเวลาเข้า-ออกงาน (กรณีลงเวลาออกอีกวัน) #921 - checkOutStatus = DateTime.Parse(currentDate.ToString("yyyy-MM-dd HH:mm")) < - DateTime.Parse($"{currentDate.ToString("yyyy-MM-dd")} {duty.EndTimeAfternoon}") ? - // "ABSENT" : - checkout.CheckIn.Date < currentDate.Date ? "NORMAL" : - DateTime.Parse(currentDate.ToString("yyyy-MM-dd HH:mm")) >= - DateTime.Parse($"{currentDate.ToString("yyyy-MM-dd")} {endTime}") ? - "NORMAL" : - "ABSENT" : - DateTime.Parse(currentDate.ToString("yyyy-MM-dd HH:mm")) < - DateTime.Parse($"{currentDate.ToString("yyyy-MM-dd")} {endTimeMorning}") ? - "ABSENT" : - "NORMAL"; - } + } - if (checkout_process != null) - { - checkout_process.CheckOutLat = data.Lat; - checkout_process.CheckOutLon = data.Lon; - checkout_process.IsLocationCheckOut = data.IsLocation; - checkout_process.CheckOutLocationName = data.LocationName; - checkout_process.CheckOutPOI = data.POI; - checkout_process.CheckOutRemark = data.Remark; - checkout_process.CheckOutImageUrl = fileName; - checkout_process.CheckOut = currentDate; - checkout_process.CheckOutStatus = checkOutStatus; + if (checkout_process != null) + { + checkout_process.CheckOutLat = data.Lat; + checkout_process.CheckOutLon = data.Lon; + checkout_process.IsLocationCheckOut = data.IsLocation; + checkout_process.CheckOutLocationName = data.LocationName; + checkout_process.CheckOutPOI = data.POI; + checkout_process.CheckOutRemark = data.Remark; + checkout_process.CheckOutImageUrl = fileName; + checkout_process.CheckOut = currentDate; + checkout_process.CheckOutStatus = checkOutStatus; + + await _processUserTimeStampRepository.UpdateAsync(checkout_process); + } + else + { + await _checkInJobStatusRepository.UpdateToFailedAsync(taskId, "ไม่พบข้อมูลการประมวลผลเวลาทำงาน"); + return Error(new Exception(GlobalMessages.DataNotFound), StatusCodes.Status404NotFound); + } - await _processUserTimeStampRepository.UpdateAsync(checkout_process); - } - else - { - return Error(new Exception(GlobalMessages.DataNotFound), StatusCodes.Status404NotFound); } - } - - // อัปเดตสถานะเป็น COMPLETED - if (taskId != Guid.Empty) - { - var additionalData = JsonConvert.SerializeObject(new + // อัปเดตสถานะเป็น COMPLETED + if (taskId != Guid.Empty) { - CheckInType = data.CheckInId == null ? "check-in" : "check-out", - FileName = fileName, - ProcessedDate = currentDate - }); - await _checkInJobStatusRepository.UpdateToCompletedAsync(taskId, additionalData); - } + var additionalData = JsonConvert.SerializeObject(new + { + CheckInType = data.CheckInId == null ? "check-in" : "check-out", + FileName = fileName, + ProcessedDate = currentDate + }); + await _checkInJobStatusRepository.UpdateToCompletedAsync(taskId, additionalData); + } - var checkInType = data.CheckInId == null ? "check-in" : "check-out"; - return Success(new { user = $"{profile.FirstName} {profile.LastName}", date = currentDate, type = checkInType }); ; - } - catch (Exception ex) - { - // อัปเดตสถานะเป็น FAILED - if (taskId != Guid.Empty) - { - await _checkInJobStatusRepository.UpdateToFailedAsync(taskId, ex.Message); + var checkInType = data.CheckInId == null ? "check-in" : "check-out"; + return Success(new { user = $"{profile.FirstName} {profile.LastName}", date = currentDate, type = checkInType }); ; + } + catch (Exception ex) + { + // อัปเดตสถานะเป็น FAILED + if (taskId != Guid.Empty) + { + await _checkInJobStatusRepository.UpdateToFailedAsync(taskId, ex.Message); + } + throw; } - throw; } - } /// /// LV1_005 - ลงเวลาเข้า-ออกงาน (USER) diff --git a/BMA.EHR.Leave/Program.cs b/BMA.EHR.Leave/Program.cs index 928c8fc2..1b868cfb 100644 --- a/BMA.EHR.Leave/Program.cs +++ b/BMA.EHR.Leave/Program.cs @@ -96,7 +96,11 @@ builder.Services.AddPersistence(builder.Configuration); builder.Services.AddLeavePersistence(builder.Configuration); builder.Services.AddTransient(); -builder.Services.AddHttpClient(); +// Configure HttpClient with increased timeout for long-running operations (e.g., RabbitMQ Management API) +builder.Services.AddHttpClient(client => +{ + client.Timeout = TimeSpan.FromMinutes(10); // Set timeout to 10 minutes +}); builder.Services.AddControllers(options => { From 0a170fd259414cdd37aaeb2fd594277aadb4b3a7 Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Fri, 30 Jan 2026 10:09:37 +0700 Subject: [PATCH 082/178] Configure HttpClient to use a 10-minute timeout for long-running operations --- BMA.EHR.Leave/Program.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/BMA.EHR.Leave/Program.cs b/BMA.EHR.Leave/Program.cs index 1b868cfb..e25a59fe 100644 --- a/BMA.EHR.Leave/Program.cs +++ b/BMA.EHR.Leave/Program.cs @@ -97,9 +97,12 @@ builder.Services.AddLeavePersistence(builder.Configuration); builder.Services.AddTransient(); // Configure HttpClient with increased timeout for long-running operations (e.g., RabbitMQ Management API) -builder.Services.AddHttpClient(client => +builder.Services.AddHttpClient(); +builder.Services.AddTransient(sp => { - client.Timeout = TimeSpan.FromMinutes(10); // Set timeout to 10 minutes + var httpClient = sp.GetRequiredService().CreateClient(); + httpClient.Timeout = TimeSpan.FromMinutes(10); // Set timeout to 10 minutes + return httpClient; }); builder.Services.AddControllers(options => From 659e06a08d9f7e03ad50bfddf4c443e69fe02187 Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Fri, 30 Jan 2026 13:35:58 +0700 Subject: [PATCH 083/178] Add cancellation token support and extend timeout to 30 minutes for external API calls --- .../Repositories/GenericRepository.cs | 29 +++++-- .../TimeAttendants/DutyTimeRepository.cs | 7 +- .../TimeAttendants/UserDutyTimeRepository.cs | 7 +- .../TimeAttendants/UserTimeStampRepository.cs | 8 +- .../Repositories/UserProfileRepository.cs | 4 +- BMA.EHR.Leave/Controllers/LeaveController.cs | 86 ++++++++----------- dotnet_leave_test.js | 11 +-- 7 files changed, 76 insertions(+), 76 deletions(-) diff --git a/BMA.EHR.Application/Repositories/GenericRepository.cs b/BMA.EHR.Application/Repositories/GenericRepository.cs index fcc2aef0..805b88ba 100644 --- a/BMA.EHR.Application/Repositories/GenericRepository.cs +++ b/BMA.EHR.Application/Repositories/GenericRepository.cs @@ -53,15 +53,18 @@ namespace BMA.EHR.Application.Repositories #region " For Call External API " - protected async Task GetExternalAPIAsync(string apiPath, string accessToken, string apiKey) + protected async Task GetExternalAPIAsync(string apiPath, string accessToken, string apiKey, CancellationToken cancellationToken = default) { try { + // กำหนด timeout เป็น 30 นาที + using var timeoutCts = new CancellationTokenSource(TimeSpan.FromMinutes(30)); + using var combinedCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, timeoutCts.Token); using (var client = new HttpClient()) { client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken.Replace("Bearer ", "")); client.DefaultRequestHeaders.Add("api-key", apiKey); - var _res = await client.GetAsync(apiPath); + var _res = await client.GetAsync(apiPath,cancellationToken: combinedCts.Token); if (_res.IsSuccessStatusCode) { var _result = await _res.Content.ReadAsStringAsync(); @@ -77,10 +80,13 @@ namespace BMA.EHR.Application.Repositories } } - protected async Task SendExternalAPIAsync(HttpMethod method, string apiPath, string accessToken, object? body, string apiKey) + protected async Task SendExternalAPIAsync(HttpMethod method, string apiPath, string accessToken, object? body, string apiKey, CancellationToken cancellationToken = default) { try { + // กำหนด timeout เป็น 30 นาที + using var timeoutCts = new CancellationTokenSource(TimeSpan.FromMinutes(30)); + using var combinedCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, timeoutCts.Token); // สร้าง request message var request = new HttpRequestMessage(method, apiPath); @@ -92,7 +98,7 @@ namespace BMA.EHR.Application.Repositories { client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken.Replace("Bearer ", "")); client.DefaultRequestHeaders.Add("api-key", apiKey); - var _res = await client.SendAsync(request); + var _res = await client.SendAsync(request, combinedCts.Token); if (_res.IsSuccessStatusCode) { var _result = await _res.Content.ReadAsStringAsync(); @@ -109,11 +115,13 @@ namespace BMA.EHR.Application.Repositories } - protected async Task PostExternalAPIAsync(string apiPath, string accessToken, object? body, string apiKey) + protected async Task PostExternalAPIAsync(string apiPath, string accessToken, object? body, string apiKey, CancellationToken cancellationToken = default) { try { - + // กำหนด timeout เป็น 30 นาที + using var timeoutCts = new CancellationTokenSource(TimeSpan.FromMinutes(30)); + using var combinedCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, timeoutCts.Token); var json = JsonConvert.SerializeObject(body); var stringContent = new StringContent(json, Encoding.UTF8, "application/json"); //stringContent.Headers.ContentType = new MediaTypeHeaderValue("application/json"); @@ -122,7 +130,7 @@ namespace BMA.EHR.Application.Repositories { client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken.Replace("Bearer ", "")); client.DefaultRequestHeaders.Add("api-key", apiKey); - var _res = await client.PostAsync(apiPath, stringContent); + var _res = await client.PostAsync(apiPath, stringContent, combinedCts.Token); if (_res.IsSuccessStatusCode) { var _result = await _res.Content.ReadAsStringAsync(); @@ -138,10 +146,13 @@ namespace BMA.EHR.Application.Repositories } } - protected async Task PostExternalAPIBooleanAsync(string apiPath, string accessToken, object? body, string apiKey) + protected async Task PostExternalAPIBooleanAsync(string apiPath, string accessToken, object? body, string apiKey, CancellationToken cancellationToken = default) { try { + // กำหนด timeout เป็น 30 นาที + using var timeoutCts = new CancellationTokenSource(TimeSpan.FromMinutes(30)); + using var combinedCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, timeoutCts.Token); var json = JsonConvert.SerializeObject(body); var stringContent = new StringContent(json, UnicodeEncoding.UTF8, "application/json"); stringContent.Headers.ContentType = new MediaTypeHeaderValue("application/json"); @@ -150,7 +161,7 @@ namespace BMA.EHR.Application.Repositories { client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken.Replace("Bearer ", "")); client.DefaultRequestHeaders.Add("api-key", apiKey); - var _res = await client.PostAsync(apiPath, stringContent); + var _res = await client.PostAsync(apiPath, stringContent, combinedCts.Token); return _res.IsSuccessStatusCode; } } diff --git a/BMA.EHR.Application/Repositories/Leaves/TimeAttendants/DutyTimeRepository.cs b/BMA.EHR.Application/Repositories/Leaves/TimeAttendants/DutyTimeRepository.cs index 2db889ac..e96c35a9 100644 --- a/BMA.EHR.Application/Repositories/Leaves/TimeAttendants/DutyTimeRepository.cs +++ b/BMA.EHR.Application/Repositories/Leaves/TimeAttendants/DutyTimeRepository.cs @@ -61,9 +61,12 @@ namespace BMA.EHR.Application.Repositories.Leaves.TimeAttendants return await _dbContext.Set().Where(x => x.IsActive).ToListAsync(); } - public async Task GetDefaultAsync() + public async Task GetDefaultAsync(CancellationToken cancellationToken = default) { - return await _dbContext.Set().Where(x => x.IsDefault).FirstOrDefaultAsync(); + // กำหนด timeout เป็น 30 นาที + using var timeoutCts = new CancellationTokenSource(TimeSpan.FromMinutes(30)); + using var combinedCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, timeoutCts.Token); + return await _dbContext.Set().Where(x => x.IsDefault).FirstOrDefaultAsync(combinedCts.Token); } #endregion diff --git a/BMA.EHR.Application/Repositories/Leaves/TimeAttendants/UserDutyTimeRepository.cs b/BMA.EHR.Application/Repositories/Leaves/TimeAttendants/UserDutyTimeRepository.cs index 5ca823d9..b34bc4db 100644 --- a/BMA.EHR.Application/Repositories/Leaves/TimeAttendants/UserDutyTimeRepository.cs +++ b/BMA.EHR.Application/Repositories/Leaves/TimeAttendants/UserDutyTimeRepository.cs @@ -101,14 +101,17 @@ namespace BMA.EHR.Application.Repositories.Leaves.TimeAttendants return data; } - public async Task GetLastEffectRound(Guid profileId, DateTime? effectiveDate = null) + public async Task GetLastEffectRound(Guid profileId, DateTime? effectiveDate = null, CancellationToken cancellationToken = default) { + // กำหนด timeout เป็น 30 นาที + using var timeoutCts = new CancellationTokenSource(TimeSpan.FromMinutes(30)); + using var combinedCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, timeoutCts.Token); effectiveDate ??= DateTime.Now; var data = await _dbContext.Set() .Where(x => x.ProfileId == profileId) .Where(x => x.EffectiveDate.Value.Date <= effectiveDate.Value.Date) .OrderByDescending(x => x.EffectiveDate) - .FirstOrDefaultAsync(); + .FirstOrDefaultAsync(combinedCts.Token); return data; } diff --git a/BMA.EHR.Application/Repositories/Leaves/TimeAttendants/UserTimeStampRepository.cs b/BMA.EHR.Application/Repositories/Leaves/TimeAttendants/UserTimeStampRepository.cs index b7a766e9..2598dace 100644 --- a/BMA.EHR.Application/Repositories/Leaves/TimeAttendants/UserTimeStampRepository.cs +++ b/BMA.EHR.Application/Repositories/Leaves/TimeAttendants/UserTimeStampRepository.cs @@ -74,12 +74,16 @@ namespace BMA.EHR.Application.Repositories.Leaves.TimeAttendants return data; } - public async Task GetLastRecord(Guid keycloakId) + public async Task GetLastRecord(Guid keycloakId, CancellationToken cancellationToken = default) { + // กำหนด timeout เป็น 30 นาที + using var timeoutCts = new CancellationTokenSource(TimeSpan.FromMinutes(30)); + using var combinedCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, timeoutCts.Token); + var data = await _dbContext.Set() .Where(u => u.KeycloakUserId == keycloakId) .OrderByDescending(u => u.CheckIn) - .FirstOrDefaultAsync(); + .FirstOrDefaultAsync(combinedCts.Token); return data; } diff --git a/BMA.EHR.Application/Repositories/UserProfileRepository.cs b/BMA.EHR.Application/Repositories/UserProfileRepository.cs index ebf5113f..4e0e986f 100644 --- a/BMA.EHR.Application/Repositories/UserProfileRepository.cs +++ b/BMA.EHR.Application/Repositories/UserProfileRepository.cs @@ -186,14 +186,14 @@ namespace BMA.EHR.Application.Repositories } } - public async Task GetProfileByKeycloakIdNewAsync(Guid keycloakId, string? accessToken) + public async Task GetProfileByKeycloakIdNewAsync(Guid keycloakId, string? accessToken,CancellationToken cancellationToken = default) { try { var apiPath = $"{_configuration["API"]}/org/dotnet/by-keycloak/{keycloakId}"; var apiKey = _configuration["API_KEY"]; - var apiResult = await GetExternalAPIAsync(apiPath, accessToken ?? "", apiKey); + var apiResult = await GetExternalAPIAsync(apiPath, accessToken ?? "", apiKey, cancellationToken); if (apiResult != null) { var raw = JsonConvert.DeserializeObject(apiResult); diff --git a/BMA.EHR.Leave/Controllers/LeaveController.cs b/BMA.EHR.Leave/Controllers/LeaveController.cs index 062ceab1..9819ecd0 100644 --- a/BMA.EHR.Leave/Controllers/LeaveController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveController.cs @@ -428,18 +428,26 @@ namespace BMA.EHR.Leave.Service.Controllers [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] [ProducesResponseType(StatusCodes.Status500InternalServerError)] - public async Task> CheckTimeAsync() + public async Task> CheckTimeAsync(CancellationToken cancellationToken = default) { var userId = UserId == null ? Guid.Empty : Guid.Parse(UserId); - var data = await _userTimeStampRepository.GetLastRecord(userId); + + // Get user's last check-in record and profile in parallel + var dataTask = _userTimeStampRepository.GetLastRecord(userId); + var profileTask = _userProfileRepository.GetProfileByKeycloakIdNewAsync(userId, AccessToken); + var defaultRoundTask = _dutyTimeRepository.GetDefaultAsync(); + + await Task.WhenAll(dataTask, profileTask, defaultRoundTask); + + var data = await dataTask; + var profile = await profileTask; + var getDefaultRound = await defaultRoundTask; - var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(userId, AccessToken); if (profile == null) { throw new Exception(GlobalMessages.DataNotFound); } - var getDefaultRound = await _dutyTimeRepository.GetDefaultAsync(); if (getDefaultRound == null) { return Error("ไม่พบรอบลงเวลา Default", StatusCodes.Status404NotFound); @@ -451,65 +459,43 @@ namespace BMA.EHR.Leave.Service.Controllers var duty = userRound ?? getDefaultRound; - // TODO : รอดุึงรอบที่ผูกกับ user - //var duty = await _dutyTimeRepository.GetDefaultAsync(); - CheckInResultDto ret; - - if (data == null) - { - ret = new CheckInResultDto - { - StartTimeMorning = duty == null ? "00:00" : duty.StartTimeMorning, - EndTimeMorning = duty == null ? "00:00" : duty.EndTimeMorning, - StartTimeAfternoon = duty == null ? "00:00" : duty.StartTimeAfternoon, - EndTimeAfternoon = duty == null ? "00:00" : duty.EndTimeAfternoon, - Description = duty == null ? "-" : duty.Description, - CheckInTime = null, - CheckInId = null, - }; - } - else + // Determine check-in status and data + DateTime? checkInTime = null; + Guid? checkInId = null; + + if (data != null) { if (data.CheckOut != null) { // fix issue SIT ระบบบันทึกเวลาปฏิบัติงาน>>ลงเวลาเข้า-ออกงาน (กรณีลงเวลาออกอีกวัน) #921 - var cur_date = DateTime.Now.Date; - // ถ้า check-in + check-out ไปแล้ว - if (data.CheckIn.Date == cur_date && data.CheckOut.Value.Date == cur_date) + var currentDate = DateTime.Now.Date; + // ถ้า check-in + check-out ไปแล้วในวันเดียวกัน + if (data.CheckIn.Date == currentDate && data.CheckOut.Value.Date == currentDate) { return Error("คุณได้ทำการลงเวลาเข้าและออกเรียบร้อยแล้ว คุณจะสามารถลงเวลาได้อีกครั้งในวันถัดไป"); } - else - { - ret = new CheckInResultDto - { - StartTimeMorning = duty == null ? "00:00" : duty.StartTimeMorning, - EndTimeMorning = duty == null ? "00:00" : duty.EndTimeMorning, - StartTimeAfternoon = duty == null ? "00:00" : duty.StartTimeAfternoon, - EndTimeAfternoon = duty == null ? "00:00" : duty.EndTimeAfternoon, - Description = duty == null ? "-" : duty.Description, - CheckInTime = null, - CheckInId = null, - }; - } + // ถ้า check-out คนละวัน ให้แสดงว่ายังไม่ได้ check-in วันนี้ } else { - ret = new CheckInResultDto - { - StartTimeMorning = duty == null ? "00:00" : duty.StartTimeMorning, - EndTimeMorning = duty == null ? "00:00" : duty.EndTimeMorning, - StartTimeAfternoon = duty == null ? "00:00" : duty.StartTimeAfternoon, - EndTimeAfternoon = duty == null ? "00:00" : duty.EndTimeAfternoon, - Description = duty == null ? "-" : duty.Description, - CheckInTime = data.CheckIn, - CheckInId = data.Id, - }; + // มี check-in แต่ยังไม่ check-out + checkInTime = data.CheckIn; + checkInId = data.Id; } - - } + // Create response DTO (duty is never null here due to fallback logic) + var ret = new CheckInResultDto + { + StartTimeMorning = duty.StartTimeMorning, + EndTimeMorning = duty.EndTimeMorning, + StartTimeAfternoon = duty.StartTimeAfternoon, + EndTimeAfternoon = duty.EndTimeAfternoon, + Description = duty.Description, + CheckInTime = checkInTime, + CheckInId = checkInId, + }; + return Success(ret); } diff --git a/dotnet_leave_test.js b/dotnet_leave_test.js index 5add397d..658848ec 100644 --- a/dotnet_leave_test.js +++ b/dotnet_leave_test.js @@ -31,18 +31,11 @@ export default function () { // ตัวเลือก headers let headers = { "Content-Type": "application/json", - Authorization: - "Bearer eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJ4WTJWUi1FRnZ2TlBzTXMzOXU4b29WQldRTDZtUHdyTkpPaDNrb0pGVGdVIn0.eyJleHAiOjE3NzYyMTkxNjgsImlhdCI6MTc2ODQ0MzE2OCwianRpIjoiZDQxMmI5MWEtZmZhMi00N2JiLTliZDUtZDE5NTdmMDFjYzQyIiwiaXNzIjoiaHR0cHM6Ly9ocm1zLWlkLmJhbmdrb2suZ28udGgvcmVhbG1zL2hybXMiLCJhdWQiOiJhY2NvdW50Iiwic3ViIjoiYmFmYzU3OTUtYmVmYy00ZDNmLWE0NjEtMzUzM2MzOGE1ZmMxIiwidHlwIjoiQmVhcmVyIiwiYXpwIjoiZ2V0dG9rZW4tY2hlY2tpbiIsInNpZCI6IjBkNzdiY2Y5LTE4YWQtNGQyMS1hYjBjLTI4Y2ZiZjUyZGZiNCIsImFjciI6IjEiLCJhbGxvd2VkLW9yaWdpbnMiOlsiaHR0cHM6Ly9ocm1zLmJhbmdrb2suZ28udGgiLCJodHRwczovL2hybXMtY2hlY2tpbi5iYW5na29rLmdvLnRoIl0sInJlYWxtX2FjY2VzcyI6eyJyb2xlcyI6WyJTVVBFUl9BRE1JTiIsInN0b3JhZ2VfbWFuYWdlbWVudCIsIm9mZmxpbmVfYWNjZXNzIiwiU1RBRkYiLCJkZWZhdWx0LXJvbGVzLWhybXMiLCJ1bWFfYXV0aG9yaXphdGlvbiIsIkFETUlOIiwiVVNFUiJdfSwicmVzb3VyY2VfYWNjZXNzIjp7ImFjY291bnQiOnsicm9sZXMiOlsibWFuYWdlLWFjY291bnQiLCJtYW5hZ2UtYWNjb3VudC1saW5rcyIsInZpZXctcHJvZmlsZSJdfX0sInNjb3BlIjoiZW1haWwgb3BlbmlkIHByb2ZpbGUiLCJlbWFpbF92ZXJpZmllZCI6ZmFsc2UsInJvbGUiOlsiU1VQRVJfQURNSU4iLCJzdG9yYWdlX21hbmFnZW1lbnQiLCJvZmZsaW5lX2FjY2VzcyIsIlNUQUZGIiwiZGVmYXVsdC1yb2xlcy1ocm1zIiwidW1hX2F1dGhvcml6YXRpb24iLCJBRE1JTiIsIlVTRVIiXSwibmFtZSI6IuC4p-C4seC4meC5gOC4ieC4peC4tOC4oSDguInguLHguJXguKPguJfguK3guIciLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiIzMTIwMjAwNDI0OTc1IiwiZ2l2ZW5fbmFtZSI6IuC4p-C4seC4meC5gOC4ieC4peC4tOC4oSIsImZhbWlseV9uYW1lIjoi4LiJ4Lix4LiV4Lij4LiX4Lit4LiHIn0.UhMn0NEkymPxMAcb4noZedHCSqXotCyD2RziBtLYHn5OhA9yk1915Rrt9iV4wVaebr74iZ2eZMpBwp8YVy8-3cPXSv9T3vzbXwFP7IeICPCDDf4bOPFEHP5FYow2s9v48qG81wnu01AG7_EL2-CQKh1sBVrCVUUlATlf-P4lT_lHeHOCKNXTmw4V0IWm96ec6pk-jFY3KH2JdRSWR7wq8g-KVxhLOxk_pF72kMwOpdvcr_99byg28zzj6QfeNYXLt61koHXnZppUqytt86mQQgfamv2FNVywCEzbRITUceu2rmJnwQE8ubeoCh4UOsYauUuSKd7RPqvvXxL_Vg__8Q", - //"Bearer eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJTT2wwWmFidm9rRzZET3pDZVBtT09Kek5haTdMUldkci1zV3lEYjRELTc0In0.eyJleHAiOjE3Njg4ODAzMjgsImlhdCI6MTc2ODc5MzkyOCwianRpIjoiMDYxODBlMWYtNTQzYy00MjU0LWFmN2QtYWI1NDA5NzFmNWY2IiwiaXNzIjoiaHR0cHM6Ly9ocm1zYmtrLWlkLmNhc2UtY29sbGVjdGlvbi5jb20vcmVhbG1zL2hybXMiLCJhdWQiOlsiYWNjb3VudCIsImdldHRva2VuIl0sInN1YiI6IjQzOWZhMzZkLTZiYzUtNGVmNS05NWFhLWVmMjllNjRkMmU5ZiIsInR5cCI6IkJlYXJlciIsImF6cCI6ImdldHRva2VuIiwic2lkIjoiZGI2YzUxNjItNzZhYS00MmVmLWI0ZDMtYThmOTk2N2NjZWM2IiwiYWNyIjoiMSIsImFsbG93ZWQtb3JpZ2lucyI6WyIqIl0sInJlYWxtX2FjY2VzcyI6eyJyb2xlcyI6WyJTVVBFUl9BRE1JTiIsIm9mZmxpbmVfYWNjZXNzIiwiU1RBRkYiLCJkZWZhdWx0LXJvbGVzLWhybXMiLCJ1bWFfYXV0aG9yaXphdGlvbiIsIkFETUlOIiwiVVNFUiJdfSwicmVzb3VyY2VfYWNjZXNzIjp7ImFjY291bnQiOnsicm9sZXMiOlsibWFuYWdlLWFjY291bnQiLCJtYW5hZ2UtYWNjb3VudC1saW5rcyIsInZpZXctcHJvZmlsZSJdfX0sInNjb3BlIjoiZW1haWwgb3BlbmlkIHByb2ZpbGUiLCJlbWFpbF92ZXJpZmllZCI6ZmFsc2UsInJvbGUiOlsiU1VQRVJfQURNSU4iLCJvZmZsaW5lX2FjY2VzcyIsIlNUQUZGIiwiZGVmYXVsdC1yb2xlcy1ocm1zIiwidW1hX2F1dGhvcml6YXRpb24iLCJBRE1JTiIsIlVTRVIiXSwibmFtZSI6IuC4p-C4seC4meC5gOC4ieC4peC4tOC4oSDguInguLHguJXguKPguJfguK3guIciLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiIzMTIwMjAwNDI0OTc1IiwiZ2l2ZW5fbmFtZSI6IuC4p-C4seC4meC5gOC4ieC4peC4tOC4oSIsImZhbWlseV9uYW1lIjoi4LiJ4Lix4LiV4Lij4LiX4Lit4LiHIn0.fHdMzpHMD4JcbzYnUrfM473FSXka2Z4lz_S3HI2c-dPXfO5ATpijqsi12C6-ExE0RJRXUK671erMuyVXL6u2qj-FvdliBL3ubKy4J3jIT3svkcZxZL2ib16dRg375dITefvqd-J4vw6MR4bq8YAGPbqRIy6BQ2pdEiZgNiwUUihHAFwZlVER1lNbaqlbL6vk_L4k-g25DBVnDr756BFvrw7zEDbawkKZ31EZF5_DYk4RWej0wvWrGHQWLw-RyzYVSBB_AooqHkncHn_CwLBGC5juOEfFO4a2ThuKwoxYCstjtBj-zmjpHFs-Hh3CBTWJCGFcKst1Ey28StlKtNkLiw", + Authorization: "Bearer {Token}", }; // ส่ง GET request - let response = http.get( - //"https://bma-hrms.bangkok.go.th/api/v1/leave/fake-check-in", - //"https://hrmsbkk.case-collection.com/api/v1/org/dotnet/keycloak/439fa36d-6bc5-4ef5-95aa-ef29e64d2e9f", - "https://hrms.bangkok.go.th/api/v1/leave/check-time", - { headers: headers }, - ); + let response = http.get("https://{URL}", { headers: headers }); // ตรวจสอบการตอบสนอง check(response, { From c25bef067265c6e8d95dddff2edb1a1ced116d4b Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Tue, 3 Feb 2026 20:46:20 +0700 Subject: [PATCH 084/178] Update leave calculations to use DateAppoint and adjust leave limits based on government age #2266 --- .../LeaveRequests/LeaveBeginingRepository.cs | 10 ++++++---- .../Controllers/LeaveReportController.cs | 1 + .../Controllers/LeaveRequestController.cs | 18 ++++++++++++++---- 3 files changed, 21 insertions(+), 8 deletions(-) diff --git a/BMA.EHR.Application/Repositories/Leaves/LeaveRequests/LeaveBeginingRepository.cs b/BMA.EHR.Application/Repositories/Leaves/LeaveRequests/LeaveBeginingRepository.cs index dfb5897d..c2d08f70 100644 --- a/BMA.EHR.Application/Repositories/Leaves/LeaveRequests/LeaveBeginingRepository.cs +++ b/BMA.EHR.Application/Repositories/Leaves/LeaveRequests/LeaveBeginingRepository.cs @@ -108,7 +108,7 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests throw new Exception(GlobalMessages.DataNotFound); } - var govAge = (pf?.DateStart?.Date ?? DateTime.Now.Date).DiffDay(DateTime.Now.Date); + var govAge = (pf?.DateAppoint?.Date ?? DateTime.Now.Date).DiffDay(DateTime.Now.Date); var leaveType = await _dbContext.Set().FirstOrDefaultAsync(x => x.Id == typeId); @@ -174,7 +174,7 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests public async Task GetByYearAndTypeIdForUser(int year, Guid typeId, GetProfileByKeycloakIdDto? pf) { - var govAge = (pf?.DateStart?.Date ?? DateTime.Now.Date).DiffDay(DateTime.Now.Date); + var govAge = (pf?.DateAppoint?.Date ?? DateTime.Now.Date).DiffDay(DateTime.Now.Date); var leaveType = await _dbContext.Set().FirstOrDefaultAsync(x => x.Id == typeId); @@ -247,7 +247,7 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests return null; } - var govAge = (pf?.DateStart?.Date ?? DateTime.Now.Date).DiffDay(DateTime.Now.Date); + var govAge = (pf?.DateAppoint?.Date ?? DateTime.Now.Date).DiffDay(DateTime.Now.Date); var leaveType = await _dbContext.Set().FirstOrDefaultAsync(x => x.Id == typeId); @@ -337,7 +337,7 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests return null; } - var govAge = (pf?.DateStart?.Date ?? DateTime.Now.Date).DiffDay(DateTime.Now.Date); + var govAge = (pf?.DateAppoint?.Date ?? DateTime.Now.Date).DiffDay(DateTime.Now.Date); var leaveType = await _dbContext.Set().FirstOrDefaultAsync(x => x.Id == typeId); @@ -416,5 +416,7 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests public string LastName { get; set; } = string.Empty; public DateTime? DateStart { get; set; } = null; + + public DateTime? DateAppoint { get; set; } = null; } } diff --git a/BMA.EHR.Leave/Controllers/LeaveReportController.cs b/BMA.EHR.Leave/Controllers/LeaveReportController.cs index b89f22a4..2a113d94 100644 --- a/BMA.EHR.Leave/Controllers/LeaveReportController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveReportController.cs @@ -1360,6 +1360,7 @@ namespace BMA.EHR.Leave.Service.Controllers FirstName = x.FirstName ?? "", LastName = x.LastName ?? "", DateStart = x.DateStart ?? x.DateAppoint, + DateAppoint = x.DateAppoint, }).Distinct().ToList(); var beginningData = await _leaveBeginningRepository.GetAllByYearAsync(year); diff --git a/BMA.EHR.Leave/Controllers/LeaveRequestController.cs b/BMA.EHR.Leave/Controllers/LeaveRequestController.cs index d0edb337..61ac7c4f 100644 --- a/BMA.EHR.Leave/Controllers/LeaveRequestController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveRequestController.cs @@ -869,7 +869,7 @@ namespace BMA.EHR.Leave.Service.Controllers return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound); } - var govAge = (profile?.DateStart?.Date ?? DateTime.Now.Date).DiffDay(DateTime.Now.Date); + var govAge = (profile?.DateAppoint?.Date ?? DateTime.Now.Date).DiffDay(DateTime.Now.Date); var leaveType = await _leaveTypeRepository.GetByIdAsync(req.Type); if (leaveType == null) @@ -897,13 +897,23 @@ namespace BMA.EHR.Leave.Service.Controllers if (leaveType.Code.Trim().ToUpper() == "LV-005") { - leaveLimit = leaveData == null ? 0 : leaveData.LeaveDays; + if (govAge < 180) + leaveLimit = 0; + else + leaveLimit = leaveData == null ? 0 : leaveData.LeaveDays; } else leaveLimit = leaveType.Limit; var restOldDay = leaveData == null ? 0 : leaveData.LeaveDays - 10; var restCurrentDay = 10.0; + if (govAge < 180) + { + restOldDay = 0; + restCurrentDay = 0; + } + if(restOldDay < 0) + restOldDay = 0; var sumLeave = leaveData == null ? 0 : leaveData.LeaveDaysUsed; // var lastSalary = profile.ProfileSalary; @@ -970,7 +980,7 @@ namespace BMA.EHR.Leave.Service.Controllers // var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(userId, AccessToken); var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(userId, AccessToken); - var govAge = (profile?.DateStart?.Date ?? DateTime.Now.Date).DiffDay(DateTime.Now.Date); + var govAge = (profile?.DateAppoint?.Date ?? DateTime.Now.Date).DiffDay(DateTime.Now.Date); var thisYear = DateTime.Now.Year; var message = string.Empty; @@ -1527,7 +1537,7 @@ namespace BMA.EHR.Leave.Service.Controllers return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound); } - var govAge = (profile?.DateStart?.Date ?? DateTime.Now.Date).DiffDay(DateTime.Now.Date); + var govAge = (profile?.DateAppoint?.Date ?? DateTime.Now.Date).DiffDay(DateTime.Now.Date); var userCalendar = await _userCalendarRepository.GetExist(profile.Id); var category = userCalendar == null ? "NORMAL" : userCalendar.Calendar; From 1a0e712a1c6681dfc227d5fa892bc5067618e3f4 Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Tue, 3 Feb 2026 21:03:40 +0700 Subject: [PATCH 085/178] Update leave limit logic and add GovAge property to user leave profile DTO --- BMA.EHR.Leave/Controllers/LeaveRequestController.cs | 7 ++++++- .../DTOs/LeaveRequest/GetUserLeaveProfileResultDto.cs | 2 ++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/BMA.EHR.Leave/Controllers/LeaveRequestController.cs b/BMA.EHR.Leave/Controllers/LeaveRequestController.cs index 61ac7c4f..a961bfa2 100644 --- a/BMA.EHR.Leave/Controllers/LeaveRequestController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveRequestController.cs @@ -900,7 +900,11 @@ namespace BMA.EHR.Leave.Service.Controllers if (govAge < 180) leaveLimit = 0; else - leaveLimit = leaveData == null ? 0 : leaveData.LeaveDays; + { + leaveLimit = leaveData == null ? + govAge < 180 ? 0 : 10 + : leaveData.LeaveDays; + } } else leaveLimit = leaveType.Limit; @@ -955,6 +959,7 @@ namespace BMA.EHR.Leave.Service.Controllers CurrentDistrict = profile.CurrentDistrict ?? "", CurrentProvince = profile.CurrentProvince ?? "", CurrentZipCode = profile.CurrentZipCode ?? "", + GovAge = govAge }; return Success(result); diff --git a/BMA.EHR.Leave/DTOs/LeaveRequest/GetUserLeaveProfileResultDto.cs b/BMA.EHR.Leave/DTOs/LeaveRequest/GetUserLeaveProfileResultDto.cs index 6ddc93cb..e64b5ac3 100644 --- a/BMA.EHR.Leave/DTOs/LeaveRequest/GetUserLeaveProfileResultDto.cs +++ b/BMA.EHR.Leave/DTOs/LeaveRequest/GetUserLeaveProfileResultDto.cs @@ -51,5 +51,7 @@ public string? CurrentProvince { get; set; } public string? CurrentZipCode { get; set; } + + public int GovAge { get; set; } = 0; } } From 19000b2e42dfe441c3f7c31c82d0166fe75ad032 Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Wed, 4 Feb 2026 10:18:44 +0700 Subject: [PATCH 086/178] Refactor check-out status logic to improve clarity and handle edge cases for same-day and next-day check-outs --- BMA.EHR.Leave/Controllers/LeaveController.cs | 102 ++++++++++++++----- 1 file changed, 78 insertions(+), 24 deletions(-) diff --git a/BMA.EHR.Leave/Controllers/LeaveController.cs b/BMA.EHR.Leave/Controllers/LeaveController.cs index 9819ecd0..3f8c9536 100644 --- a/BMA.EHR.Leave/Controllers/LeaveController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveController.cs @@ -1158,35 +1158,89 @@ namespace BMA.EHR.Leave.Service.Controllers else { // fix issue : SIT ระบบบันทึกเวลาปฏิบัติงาน>>ลงเวลาเข้า-ออกงาน (กรณีลงเวลาออกอีกวัน) #921 - checkOutStatus = DateTime.Parse(currentDate.ToString("yyyy-MM-dd HH:mm")) < - DateTime.Parse($"{currentDate.ToString("yyyy-MM-dd")} {duty.EndTimeAfternoon}") ? - // "ABSENT" : - checkout.CheckIn.Date < currentDate.Date ? "NORMAL" : - DateTime.Parse(currentDate.ToString("yyyy-MM-dd HH:mm")) >= - DateTime.Parse($"{currentDate.ToString("yyyy-MM-dd")} {endTime}") ? - "NORMAL" : - "ABSENT" : - DateTime.Parse(currentDate.ToString("yyyy-MM-dd HH:mm")) < - DateTime.Parse($"{currentDate.ToString("yyyy-MM-dd")} {endTimeMorning}") ? - "ABSENT" : - "NORMAL"; + var currentDateTime = DateTime.Parse(currentDate.ToString("yyyy-MM-dd HH:mm")); + var dutyEndTimeAfternoon = DateTime.Parse($"{checkout.CheckIn.ToString("yyyy-MM-dd")} {endTime}"); + var dutyEndTimeMorning = DateTime.Parse($"{checkout.CheckIn.ToString("yyyy-MM-dd")} {endTimeMorning}"); + + + if(currentDateTime.Date > checkout.CheckIn.Date) + { + // ถ้า check-out เป็นวันถัดไป สถานะปกติเสมอ + checkOutStatus = "NORMAL"; + } + else + { + // ถ้า check-out เป็นวันเดียวกับ check-in + // ตรวจสอบเวลาว่าสิ้นสุดก่อนบ่ายหรือไม่ + if(currentDateTime < dutyEndTimeMorning) // ถ้าออกก่อนเวลาสิ้นสุดตอนเช้า ขาดราชการ + { + checkOutStatus = "ABSENT"; + } + else if(currentDateTime >= dutyEndTimeAfternoon) // ถ้าออกหลังเวลาสิ้นสุดตอนบ่าย ปกติ + { + checkOutStatus = "NORMAL"; + } + else + { + checkOutStatus = "ABSENT"; + } + } + // checkOutStatus = DateTime.Parse(currentDate.ToString("yyyy-MM-dd HH:mm")) < + // DateTime.Parse($"{checkout.CheckIn.ToString("yyyy-MM-dd")} {duty.EndTimeAfternoon}") ? + // // "ABSENT" : + // checkout.CheckIn.Date < currentDate.Date ? "NORMAL" : + // DateTime.Parse(currentDate.ToString("yyyy-MM-dd HH:mm")) >= + // DateTime.Parse($"{checkout.CheckIn.ToString("yyyy-MM-dd")} {endTime}") ? + // "NORMAL" : + // "ABSENT" : + // DateTime.Parse(currentDate.ToString("yyyy-MM-dd HH:mm")) < + // DateTime.Parse($"{currentDate.ToString("yyyy-MM-dd")} {endTimeMorning}") ? + // "ABSENT" : + // "NORMAL"; } } else { // fix issue : SIT ระบบบันทึกเวลาปฏิบัติงาน>>ลงเวลาเข้า-ออกงาน (กรณีลงเวลาออกอีกวัน) #921 - checkOutStatus = DateTime.Parse(currentDate.ToString("yyyy-MM-dd HH:mm")) < - DateTime.Parse($"{currentDate.ToString("yyyy-MM-dd")} {duty.EndTimeAfternoon}") ? - // "ABSENT" : - checkout.CheckIn.Date < currentDate.Date ? "NORMAL" : - DateTime.Parse(currentDate.ToString("yyyy-MM-dd HH:mm")) >= - DateTime.Parse($"{currentDate.ToString("yyyy-MM-dd")} {endTime}") ? - "NORMAL" : - "ABSENT" : - DateTime.Parse(currentDate.ToString("yyyy-MM-dd HH:mm")) < - DateTime.Parse($"{currentDate.ToString("yyyy-MM-dd")} {endTimeMorning}") ? - "ABSENT" : - "NORMAL"; + var currentDateTime = DateTime.Parse(currentDate.ToString("yyyy-MM-dd HH:mm")); + var dutyEndTimeAfternoon = DateTime.Parse($"{checkout.CheckIn.ToString("yyyy-MM-dd")} {endTime}"); + var dutyEndTimeMorning = DateTime.Parse($"{checkout.CheckIn.ToString("yyyy-MM-dd")} {endTimeMorning}"); + + + if(currentDateTime.Date > checkout.CheckIn.Date) + { + // ถ้า check-out เป็นวันถัดไป สถานะปกติเสมอ + checkOutStatus = "NORMAL"; + } + else + { + // ถ้า check-out เป็นวันเดียวกับ check-in + // ตรวจสอบเวลาว่าสิ้นสุดก่อนบ่ายหรือไม่ + if(currentDateTime < dutyEndTimeMorning) // ถ้าออกก่อนเวลาสิ้นสุดตอนเช้า ขาดราชการ + { + checkOutStatus = "ABSENT"; + } + else if(currentDateTime >= dutyEndTimeAfternoon) // ถ้าออกหลังเวลาสิ้นสุดตอนบ่าย ปกติ + { + checkOutStatus = "NORMAL"; + } + else + { + checkOutStatus = "ABSENT"; + } + } + // checkOutStatus = DateTime.Parse(currentDate.ToString("yyyy-MM-dd HH:mm")) < + // DateTime.Parse($"{currentDate.ToString("yyyy-MM-dd")} {duty.EndTimeAfternoon}") ? + // // "ABSENT" : + // checkout.CheckIn.Date < currentDate.Date ? "NORMAL" : + // DateTime.Parse(currentDate.ToString("yyyy-MM-dd HH:mm")) >= + // DateTime.Parse($"{currentDate.ToString("yyyy-MM-dd")} {endTime}") ? + // "NORMAL" : + // "ABSENT" : + // DateTime.Parse(currentDate.ToString("yyyy-MM-dd HH:mm")) < + // DateTime.Parse($"{currentDate.ToString("yyyy-MM-dd")} {endTimeMorning}") ? + // "ABSENT" : + // "NORMAL"; } if (checkout_process != null) From 7775ea85c3e483aa88e79250de6a4e3911ca3e37 Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Wed, 4 Feb 2026 10:32:44 +0700 Subject: [PATCH 087/178] Refactor error handling in LeaveController to return appropriate error responses instead of throwing exceptions --- BMA.EHR.Leave/Controllers/LeaveController.cs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/BMA.EHR.Leave/Controllers/LeaveController.cs b/BMA.EHR.Leave/Controllers/LeaveController.cs index 3f8c9536..d90321ba 100644 --- a/BMA.EHR.Leave/Controllers/LeaveController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveController.cs @@ -527,12 +527,13 @@ namespace BMA.EHR.Leave.Service.Controllers if (sameTypeJob != null) { - // ตรวจสอบว่างานที่มีอยู่ถูกสร้างเมื่อไหร่ ถ้าเกิน 2 นาทีให้สร้างใหม่ได้ - var timeDiff = (currentDate - sameTypeJob.CreatedDate).TotalMinutes; - if (timeDiff < 2) - { - return Error($"มีงาน {checkType} กำลังดำเนินการอยู่ กรุณารอสักครู่", StatusCodes.Status409Conflict); - } + + return Error($"มีงาน {checkType} กำลังดำเนินการอยู่", StatusCodes.Status500InternalServerError); + // var timeDiff = (currentDate - sameTypeJob.CreatedDate).TotalMinutes; + // if (timeDiff < 2) + // { + // return Error($"มีงาน {checkType} กำลังดำเนินการอยู่ กรุณารอสักครู่", StatusCodes.Status409Conflict); + // } } } @@ -623,7 +624,8 @@ namespace BMA.EHR.Leave.Service.Controllers // Ignore delete error } } - throw new Exception($"ไม่สามารถส่งงานไปยัง Queue ได้: {ex.Message}"); + return Error($"ไม่สามารถส่งงานไปยัง Queue ได้: {ex.Message}"); + //throw new Exception($"ไม่สามารถส่งงานไปยัง Queue ได้: {ex.Message}"); } finally { From 09a720807461ccb2bc866bb00806f3186204e9b9 Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Wed, 4 Feb 2026 10:49:13 +0700 Subject: [PATCH 088/178] Update govAge calculation to use DateStart instead of DateAppoint in Leave repositories and controller --- .../Repositories/InsigniaPeriodsRepository.cs | 176 +++++++++--------- .../LeaveRequests/LeaveBeginingRepository.cs | 8 +- .../Controllers/LeaveRequestController.cs | 6 +- 3 files changed, 95 insertions(+), 95 deletions(-) diff --git a/BMA.EHR.Application/Repositories/InsigniaPeriodsRepository.cs b/BMA.EHR.Application/Repositories/InsigniaPeriodsRepository.cs index ddee97cb..1c0741cb 100644 --- a/BMA.EHR.Application/Repositories/InsigniaPeriodsRepository.cs +++ b/BMA.EHR.Application/Repositories/InsigniaPeriodsRepository.cs @@ -280,8 +280,8 @@ namespace BMA.EHR.Application.Repositories FullName = $"{(p.Prefix ?? "")}{p.FirstName} {p.LastName}", Position = p.Position ?? "", Rank = p.PosLevel ?? "", - ProfileDateAppoint = p.DateAppoint.Value, - GovAge = p.DateAppoint.Value.CalculateGovAgeStr(0, 0), + ProfileDateAppoint = p.DateAppoint!.Value, + GovAge = p.DateStart!.Value.CalculateGovAgeStr(0, 0), PosNo = p.PosNo, PositionLevelId = p.PosLevelId, PositionLevelName = p.PosLevel, @@ -425,8 +425,8 @@ namespace BMA.EHR.Application.Repositories FullName = $"{(p.Prefix ?? "")}{p.FirstName} {p.LastName}", Position = p.Position ?? "", Rank = p.PosLevel ?? "", - ProfileDateAppoint = p.DateAppoint.Value, - GovAge = p.DateAppoint.Value.CalculateGovAgeStr(0, 0), + ProfileDateAppoint = p.DateAppoint!.Value, + GovAge = p.DateStart!.Value.CalculateGovAgeStr(0, 0), PosNo = p.PosNo, PositionLevelId = p.PosLevelId, PositionLevelName = p.PosLevel, @@ -571,8 +571,8 @@ namespace BMA.EHR.Application.Repositories FullName = $"{(p.Prefix ?? "")}{p.FirstName} {p.LastName}", Position = p.Position ?? "", Rank = p.PosLevel ?? "", - ProfileDateAppoint = p.DateAppoint.Value, - GovAge = p.DateAppoint.Value.CalculateGovAgeStr(0, 0), + ProfileDateAppoint = p.DateAppoint!.Value, + GovAge = p.DateStart!.Value.CalculateGovAgeStr(0, 0), PosNo = p.PosNo, PositionLevelId = p.PosLevel == null ? Guid.Parse("00000000-0000-0000-0000-000000000000") @@ -794,8 +794,8 @@ namespace BMA.EHR.Application.Repositories FullName = $"{(p.Prefix ?? "")}{p.FirstName} {p.LastName}", Position = p.Position ?? "", Rank = p.PosLevel ?? "", - ProfileDateAppoint = p.DateAppoint.Value, - GovAge = p.DateAppoint.Value.CalculateGovAgeStr(0, 0), + ProfileDateAppoint = p.DateAppoint!.Value, + GovAge = p.DateStart!.Value.CalculateGovAgeStr(0, 0), PosNo = p.PosNo, PositionLevelId = p.PosLevelId, PositionLevelName = p.PosLevel, @@ -939,8 +939,8 @@ namespace BMA.EHR.Application.Repositories FullName = $"{(p.Prefix ?? "")}{p.FirstName} {p.LastName}", Position = p.Position ?? "", Rank = p.PosLevel ?? "", - ProfileDateAppoint = p.DateAppoint.Value, - GovAge = p.DateAppoint.Value.CalculateGovAgeStr(0, 0), + ProfileDateAppoint = p.DateAppoint!.Value, + GovAge = p.DateStart!.Value.CalculateGovAgeStr(0, 0), PosNo = p.PosNo, PositionLevelId = p.PosLevelId, PositionLevelName = p.PosLevel, @@ -1081,8 +1081,8 @@ namespace BMA.EHR.Application.Repositories FullName = $"{(p.Prefix ?? "")}{p.FirstName} {p.LastName}", Position = p.Position ?? "", Rank = p.PosLevel ?? "", - ProfileDateAppoint = p.DateAppoint.Value, - GovAge = p.DateAppoint.Value.CalculateGovAgeStr(0, 0), + ProfileDateAppoint = p.DateAppoint!.Value, + GovAge = p.DateStart!.Value.CalculateGovAgeStr(0, 0), PosNo = p.PosNo, PositionLevelId = p.PosLevelId, PositionLevelName = p.PosLevel, @@ -1301,8 +1301,8 @@ namespace BMA.EHR.Application.Repositories FullName = $"{(p.Prefix ?? "")}{p.FirstName} {p.LastName}", Position = p.Position ?? "", Rank = p.PosLevel ?? "", - ProfileDateAppoint = p.DateAppoint.Value, - GovAge = p.DateAppoint.Value.CalculateGovAgeStr(0, 0), + ProfileDateAppoint = p.DateAppoint!.Value, + GovAge = p.DateStart!.Value.CalculateGovAgeStr(0, 0), PosNo = p.PosNo ?? "", PositionLevelId = p.PosLevelId, PositionLevelName = p.PosLevel, @@ -1443,8 +1443,8 @@ namespace BMA.EHR.Application.Repositories FullName = $"{(p.Prefix == null ? null : p.Prefix)}{p.FirstName} {p.LastName}", Position = p.Position == null ? "" : p.Position, Rank = p.PosLevel ?? "", - GovAge = p.DateAppoint.Value.CalculateGovAgeStr(0, 0), - ProfileDateAppoint = p.DateAppoint.Value, + GovAge = p.DateStart!.Value.CalculateGovAgeStr(0, 0), + ProfileDateAppoint = p.DateAppoint!.Value, LastInsignia = p.ProfileInsignia == null || p.ProfileInsignia.Count == 0 ? "" : p.ProfileInsignia .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) @@ -1591,8 +1591,8 @@ namespace BMA.EHR.Application.Repositories FullName = $"{(p.Prefix == null ? null : p.Prefix)}{p.FirstName} {p.LastName}", Position = p.Position == null ? "" : p.Position, Rank = p.PosLevel ?? "", - GovAge = p.DateAppoint.Value.CalculateGovAgeStr(0, 0), - ProfileDateAppoint = p.DateAppoint.Value, + GovAge = p.DateStart!.Value.CalculateGovAgeStr(0, 0), + ProfileDateAppoint = p.DateAppoint!.Value, LastInsignia = p.ProfileInsignia == null || p.ProfileInsignia.Count == 0 ? "" : p.ProfileInsignia .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) @@ -1738,8 +1738,8 @@ namespace BMA.EHR.Application.Repositories FullName = $"{(p.Prefix == null ? null : p.Prefix)}{p.FirstName} {p.LastName}", Position = p.Position == null ? "" : p.Position, Rank = p.PosLevel ?? "", - GovAge = p.DateAppoint.Value.CalculateGovAgeStr(0, 0), - ProfileDateAppoint = p.DateAppoint.Value, + GovAge = p.DateStart!.Value.CalculateGovAgeStr(0, 0), + ProfileDateAppoint = p.DateAppoint!.Value, LastInsignia = p.ProfileInsignia == null || p.ProfileInsignia.Count == 0 ? "" : p.ProfileInsignia .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) @@ -1992,8 +1992,8 @@ namespace BMA.EHR.Application.Repositories FullName = $"{(p.Prefix == null ? null : p.Prefix)}{p.FirstName} {p.LastName}", Position = p.Position == null ? null : p.Position, Rank = p.PosLevel ?? "", - GovAge = p.DateAppoint.Value.CalculateGovAgeStr(0, 0), - ProfileDateAppoint = p.DateAppoint.Value, + GovAge = p.DateStart!.Value.CalculateGovAgeStr(0, 0), + ProfileDateAppoint = p.DateAppoint!.Value, LastInsignia = p.ProfileInsignia == null || p.ProfileInsignia.Count == 0 ? "" : p.ProfileInsignia .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) @@ -2139,8 +2139,8 @@ namespace BMA.EHR.Application.Repositories FullName = $"{(p.Prefix == null ? null : p.Prefix)}{p.FirstName} {p.LastName}", Position = p.Position == null ? null : p.Position, Rank = p.PosLevel ?? "", - GovAge = p.DateAppoint.Value.CalculateGovAgeStr(0, 0), - ProfileDateAppoint = p.DateAppoint.Value, + GovAge = p.DateStart!.Value.CalculateGovAgeStr(0, 0), + ProfileDateAppoint = p.DateAppoint!.Value, LastInsignia = p.ProfileInsignia == null || p.ProfileInsignia.Count == 0 ? "" : p.ProfileInsignia .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) @@ -2358,8 +2358,8 @@ namespace BMA.EHR.Application.Repositories FullName = $"{(p.Prefix == null ? null : p.Prefix)}{p.FirstName} {p.LastName}", Position = p.Position == null ? null : p.Position, Rank = p.PosLevel ?? "", - GovAge = p.DateAppoint.Value.CalculateGovAgeStr(0, 0), - ProfileDateAppoint = p.DateAppoint.Value, + GovAge = p.DateStart!.Value.CalculateGovAgeStr(0, 0), + ProfileDateAppoint = p.DateAppoint!.Value, LastInsignia = p.ProfileInsignia == null || p.ProfileInsignia.Count == 0 ? "" : p.ProfileInsignia .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) @@ -2510,8 +2510,8 @@ namespace BMA.EHR.Application.Repositories FullName = $"{(p.Prefix == null ? null : p.Prefix)}{p.FirstName} {p.LastName}", Position = p.Position == null ? null : p.Position, Rank = p.PosLevel ?? "", - GovAge = p.DateAppoint.Value.CalculateGovAgeStr(0, 0), - ProfileDateAppoint = p.DateAppoint.Value, + GovAge = p.DateStart!.Value.CalculateGovAgeStr(0, 0), + ProfileDateAppoint = p.DateAppoint!.Value, LastInsignia = p.ProfileInsignia == null || p.ProfileInsignia.Count == 0 ? "" : p.ProfileInsignia .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) @@ -2730,8 +2730,8 @@ namespace BMA.EHR.Application.Repositories FullName = $"{(p.Prefix == null ? null : p.Prefix)}{p.FirstName} {p.LastName}", Position = p.Position == null ? null : p.Position, Rank = p.PosLevel ?? "", - GovAge = p.DateAppoint.Value.CalculateGovAgeStr(0, 0), - ProfileDateAppoint = p.DateAppoint.Value, + GovAge = p.DateStart!.Value.CalculateGovAgeStr(0, 0), + ProfileDateAppoint = p.DateAppoint!.Value, LastInsignia = p.ProfileInsignia == null || p.ProfileInsignia.Count == 0 ? "" : p.ProfileInsignia .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) @@ -2883,8 +2883,8 @@ namespace BMA.EHR.Application.Repositories FullName = $"{(p.Prefix == null ? null : p.Prefix)}{p.FirstName} {p.LastName}", Position = p.Position == null ? null : p.Position, Rank = p.PosLevel ?? "", - GovAge = p.DateAppoint.Value.CalculateGovAgeStr(0, 0), - ProfileDateAppoint = p.DateAppoint.Value, + GovAge = p.DateStart!.Value.CalculateGovAgeStr(0, 0), + ProfileDateAppoint = p.DateAppoint!.Value, LastInsignia = p.ProfileInsignia == null || p.ProfileInsignia.Count == 0 ? "" : p.ProfileInsignia .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) @@ -3045,8 +3045,8 @@ namespace BMA.EHR.Application.Repositories FullName = $"{(p.Prefix == null ? null : p.Prefix)}{p.FirstName} {p.LastName}", Position = p.Position == null ? null : p.Position, Rank = p.PosLevel ?? "", - GovAge = p.DateAppoint.Value.CalculateGovAgeStr(0, 0), - ProfileDateAppoint = p.DateAppoint.Value, + GovAge = p.DateStart!.Value.CalculateGovAgeStr(0, 0), + ProfileDateAppoint = p.DateAppoint!.Value, LastInsignia = p.ProfileInsignia == null || p.ProfileInsignia.Count == 0 ? "" : p.ProfileInsignia .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) @@ -3292,8 +3292,8 @@ namespace BMA.EHR.Application.Repositories FullName = $"{(p.Prefix == null ? null : p.Prefix)}{p.FirstName} {p.LastName}", Position = p.Position == null ? null : p.Position, Rank = p.PosLevel ?? "", - GovAge = p.DateAppoint.Value.CalculateGovAgeStr(0, 0), - ProfileDateAppoint = p.DateAppoint.Value, + GovAge = p.DateStart!.Value.CalculateGovAgeStr(0, 0), + ProfileDateAppoint = p.DateAppoint!.Value, LastInsignia = p.ProfileInsignia == null || p.ProfileInsignia.Count == 0 ? "" : p.ProfileInsignia .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) @@ -3498,8 +3498,8 @@ namespace BMA.EHR.Application.Repositories FullName = $"{(p.Prefix == null ? null : p.Prefix)}{p.FirstName} {p.LastName}", Position = p.Position == null ? null : p.Position, Rank = p.PosLevel ?? "", - GovAge = p.DateAppoint.Value.CalculateGovAgeStr(0, 0), - ProfileDateAppoint = p.DateAppoint.Value, + GovAge = p.DateStart!.Value.CalculateGovAgeStr(0, 0), + ProfileDateAppoint = p.DateAppoint!.Value, LastInsignia = p.ProfileInsignia == null || p.ProfileInsignia.Count == 0 ? "" : p.ProfileInsignia .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) @@ -3657,8 +3657,8 @@ namespace BMA.EHR.Application.Repositories FullName = $"{(p.Prefix == null ? null : p.Prefix)}{p.FirstName} {p.LastName}", Position = p.Position == null ? null : p.Position, Rank = p.PosLevel ?? "", - GovAge = p.DateAppoint.Value.CalculateGovAgeStr(0, 0), - ProfileDateAppoint = p.DateAppoint.Value, + GovAge = p.DateStart!.Value.CalculateGovAgeStr(0, 0), + ProfileDateAppoint = p.DateAppoint!.Value, LastInsignia = p.ProfileInsignia == null || p.ProfileInsignia.Count == 0 ? "" : p.ProfileInsignia .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) @@ -3819,8 +3819,8 @@ namespace BMA.EHR.Application.Repositories FullName = $"{(p.Prefix == null ? null : p.Prefix)}{p.FirstName} {p.LastName}", Position = p.Position == null ? null : p.Position, Rank = p.PosLevel ?? "", - GovAge = p.DateAppoint.Value.CalculateGovAgeStr(0, 0), - ProfileDateAppoint = p.DateAppoint.Value, + GovAge = p.DateStart!.Value.CalculateGovAgeStr(0, 0), + ProfileDateAppoint = p.DateAppoint!.Value, LastInsignia = p.ProfileInsignia == null || p.ProfileInsignia.Count == 0 ? "" : p.ProfileInsignia .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) @@ -4065,8 +4065,8 @@ namespace BMA.EHR.Application.Repositories FullName = $"{(p.Prefix == null ? null : p.Prefix)}{p.FirstName} {p.LastName}", Position = p.Position == null ? null : p.Position, Rank = p.PosLevel ?? "", - GovAge = p.DateAppoint.Value.CalculateGovAgeStr(0, 0), - ProfileDateAppoint = p.DateAppoint.Value, + GovAge = p.DateStart!.Value.CalculateGovAgeStr(0, 0), + ProfileDateAppoint = p.DateAppoint!.Value, LastInsignia = p.ProfileInsignia == null || p.ProfileInsignia.Count == 0 ? "" : p.ProfileInsignia .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) @@ -4207,8 +4207,8 @@ namespace BMA.EHR.Application.Repositories FullName = $"{(p.Prefix == null ? null : p.Prefix)}{p.FirstName} {p.LastName}", Position = p.Position == null ? null : p.Position, Rank = p.PosLevel ?? "", - GovAge = p.DateAppoint.Value.CalculateGovAgeStr(0, 0), - ProfileDateAppoint = p.DateAppoint.Value, + GovAge = p.DateStart!.Value.CalculateGovAgeStr(0, 0), + ProfileDateAppoint = p.DateAppoint!.Value, LastInsignia = p.ProfileInsignia == null || p.ProfileInsignia.Count == 0 ? "" : p.ProfileInsignia .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) @@ -4442,8 +4442,8 @@ namespace BMA.EHR.Application.Repositories FullName = $"{(p.Prefix == null ? null : p.Prefix)}{p.FirstName} {p.LastName}", Position = p.Position == null ? null : p.Position, Rank = p.PosLevel ?? "", - GovAge = p.DateAppoint.Value.CalculateGovAgeStr(0, 0), - ProfileDateAppoint = p.DateAppoint.Value, + GovAge = p.DateStart!.Value.CalculateGovAgeStr(0, 0), + ProfileDateAppoint = p.DateAppoint!.Value, LastInsignia = p.ProfileInsignia == null || p.ProfileInsignia.Count == 0 ? "" : p.ProfileInsignia .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) @@ -4602,8 +4602,8 @@ namespace BMA.EHR.Application.Repositories FullName = $"{(p.Prefix == null ? null : p.Prefix)}{p.FirstName} {p.LastName}", Position = p.Position == null ? null : p.Position, Rank = p.PosLevel ?? "", - GovAge = p.DateAppoint.Value.CalculateGovAgeStr(0, 0), - ProfileDateAppoint = p.DateAppoint.Value, + GovAge = p.DateStart!.Value.CalculateGovAgeStr(0, 0), + ProfileDateAppoint = p.DateAppoint!.Value, LastInsignia = p.ProfileInsignia == null || p.ProfileInsignia.Count == 0 ? "" : p.ProfileInsignia .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) @@ -4764,8 +4764,8 @@ namespace BMA.EHR.Application.Repositories FullName = $"{(p.Prefix == null ? null : p.Prefix)}{p.FirstName} {p.LastName}", Position = p.Position == null ? null : p.Position, Rank = p.PosLevel ?? "", - GovAge = p.DateAppoint.Value.CalculateGovAgeStr(0, 0), - ProfileDateAppoint = p.DateAppoint.Value, + GovAge = p.DateStart!.Value.CalculateGovAgeStr(0, 0), + ProfileDateAppoint = p.DateAppoint!.Value, LastInsignia = p.ProfileInsignia == null || p.ProfileInsignia.Count == 0 ? "" : p.ProfileInsignia .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) @@ -5008,8 +5008,8 @@ namespace BMA.EHR.Application.Repositories FullName = $"{(p.Prefix == null ? null : p.Prefix)}{p.FirstName} {p.LastName}", Position = p.Position == null ? null : p.Position, Rank = p.PosLevel ?? "", - GovAge = p.DateAppoint.Value.CalculateGovAgeStr(0, 0), - ProfileDateAppoint = p.DateAppoint.Value, + GovAge = p.DateStart!.Value.CalculateGovAgeStr(0, 0), + ProfileDateAppoint = p.DateAppoint!.Value, LastInsignia = p.ProfileInsignia == null || p.ProfileInsignia.Count == 0 ? "" : p.ProfileInsignia .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) @@ -5176,8 +5176,8 @@ namespace BMA.EHR.Application.Repositories FullName = $"{(p.Prefix == null ? null : p.Prefix)}{p.FirstName} {p.LastName}", Position = p.Position == null ? null : p.Position, Rank = p.PosLevel ?? "", - GovAge = p.DateAppoint.Value.CalculateGovAgeStr(0, 0), - ProfileDateAppoint = p.DateAppoint.Value, + GovAge = p.DateStart!.Value.CalculateGovAgeStr(0, 0), + ProfileDateAppoint = p.DateAppoint!.Value, LastInsignia = p.ProfileInsignia == null || p.ProfileInsignia.Count == 0 ? "" : p.ProfileInsignia .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) @@ -5344,8 +5344,8 @@ namespace BMA.EHR.Application.Repositories FullName = $"{(p.Prefix == null ? null : p.Prefix)}{p.FirstName} {p.LastName}", Position = p.Position == null ? null : p.Position, Rank = p.PosLevel ?? "", - GovAge = p.DateAppoint.Value.CalculateGovAgeStr(0, 0), - ProfileDateAppoint = p.DateAppoint.Value, + GovAge = p.DateStart!.Value.CalculateGovAgeStr(0, 0), + ProfileDateAppoint = p.DateAppoint!.Value, LastInsignia = p.ProfileInsignia == null || p.ProfileInsignia.Count == 0 ? "" : p.ProfileInsignia .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) @@ -5594,8 +5594,8 @@ namespace BMA.EHR.Application.Repositories FullName = $"{(p.Prefix == null ? null : p.Prefix)}{p.FirstName} {p.LastName}", Position = p.Position == null ? null : p.Position, Rank = p.PosLevel ?? "", - GovAge = p.DateAppoint.Value.CalculateGovAgeStr(0, 0), - ProfileDateAppoint = p.DateAppoint.Value, + GovAge = p.DateStart!.Value.CalculateGovAgeStr(0, 0), + ProfileDateAppoint = p.DateAppoint!.Value, LastInsignia = p.ProfileInsignia == null || p.ProfileInsignia.Count == 0 ? "" : p.ProfileInsignia .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) @@ -5762,8 +5762,8 @@ namespace BMA.EHR.Application.Repositories FullName = $"{(p.Prefix == null ? null : p.Prefix)}{p.FirstName} {p.LastName}", Position = p.Position == null ? null : p.Position, Rank = p.PosLevel ?? "", - GovAge = p.DateAppoint.Value.CalculateGovAgeStr(0, 0), - ProfileDateAppoint = p.DateAppoint.Value, + GovAge = p.DateStart!.Value.CalculateGovAgeStr(0, 0), + ProfileDateAppoint = p.DateAppoint!.Value, LastInsignia = p.ProfileInsignia == null || p.ProfileInsignia.Count == 0 ? "" : p.ProfileInsignia .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) @@ -5930,8 +5930,8 @@ namespace BMA.EHR.Application.Repositories FullName = $"{(p.Prefix == null ? null : p.Prefix)}{p.FirstName} {p.LastName}", Position = p.Position == null ? null : p.Position, Rank = p.PosLevel ?? "", - GovAge = p.DateAppoint.Value.CalculateGovAgeStr(0, 0), - ProfileDateAppoint = p.DateAppoint.Value, + GovAge = p.DateStart!.Value.CalculateGovAgeStr(0, 0), + ProfileDateAppoint = p.DateAppoint!.Value, LastInsignia = p.ProfileInsignia == null || p.ProfileInsignia.Count == 0 ? "" : p.ProfileInsignia .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) @@ -6176,8 +6176,8 @@ namespace BMA.EHR.Application.Repositories FullName = $"{(p.Prefix == null ? null : p.Prefix)}{p.FirstName} {p.LastName}", Position = p.Position == null ? "" : p.Position, Rank = p.PosLevel ?? "", - GovAge = p.DateAppoint.Value.CalculateGovAgeStr(0, 0), - ProfileDateAppoint = p.DateAppoint.Value, + GovAge = p.DateStart!.Value.CalculateGovAgeStr(0, 0), + ProfileDateAppoint = p.DateAppoint!.Value, LastInsignia = p.ProfileInsignia == null || p.ProfileInsignia.Count == 0 ? "" : p.ProfileInsignia .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) @@ -6334,8 +6334,8 @@ namespace BMA.EHR.Application.Repositories FullName = $"{(p.Prefix == null ? null : p.Prefix)}{p.FirstName} {p.LastName}", Position = p.Position == null ? "" : p.Position, Rank = p.PosLevel ?? "", - GovAge = p.DateAppoint.Value.CalculateGovAgeStr(0, 0), - ProfileDateAppoint = p.DateAppoint.Value, + GovAge = p.DateStart!.Value.CalculateGovAgeStr(0, 0), + ProfileDateAppoint = p.DateAppoint!.Value, LastInsignia = p.ProfileInsignia == null || p.ProfileInsignia.Count == 0 ? "" : p.ProfileInsignia .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) @@ -6563,8 +6563,8 @@ namespace BMA.EHR.Application.Repositories FullName = $"{(p.Prefix == null ? null : p.Prefix)}{p.FirstName} {p.LastName}", Position = p.Position == null ? "" : p.Position, Rank = p.PosLevel ?? "", - GovAge = p.DateAppoint.Value.CalculateGovAgeStr(0, 0), - ProfileDateAppoint = p.DateAppoint.Value, + GovAge = p.DateStart!.Value.CalculateGovAgeStr(0, 0), + ProfileDateAppoint = p.DateAppoint!.Value, LastInsignia = p.ProfileInsignia == null || p.ProfileInsignia.Count == 0 ? "" : p.ProfileInsignia .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) @@ -6725,8 +6725,8 @@ namespace BMA.EHR.Application.Repositories FullName = $"{(p.Prefix == null ? null : p.Prefix)}{p.FirstName} {p.LastName}", Position = p.Position == null ? "" : p.Position, Rank = p.PosLevel ?? "", - GovAge = p.DateAppoint.Value.CalculateGovAgeStr(0, 0), - ProfileDateAppoint = p.DateAppoint.Value, + GovAge = p.DateStart!.Value.CalculateGovAgeStr(0, 0), + ProfileDateAppoint = p.DateAppoint!.Value, LastInsignia = p.ProfileInsignia == null || p.ProfileInsignia.Count == 0 ? "" : p.ProfileInsignia .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) @@ -6888,8 +6888,8 @@ namespace BMA.EHR.Application.Repositories FullName = $"{(p.Prefix == null ? null : p.Prefix)}{p.FirstName} {p.LastName}", Position = p.Position == null ? null : p.Position, Rank = p.PosLevel ?? "", - GovAge = p.DateAppoint.Value.CalculateGovAgeStr(0, 0), - ProfileDateAppoint = p.DateAppoint.Value, + GovAge = p.DateStart!.Value.CalculateGovAgeStr(0, 0), + ProfileDateAppoint = p.DateAppoint!.Value, LastInsignia = p.ProfileInsignia == null || p.ProfileInsignia.Count == 0 ? "" : p.ProfileInsignia .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) @@ -7130,8 +7130,8 @@ namespace BMA.EHR.Application.Repositories FullName = $"{(p.Prefix == null ? null : p.Prefix)}{p.FirstName} {p.LastName}", Position = p.Position == null ? null : p.Position, Rank = p.PosLevel ?? "", - GovAge = p.DateAppoint.Value.CalculateGovAgeStr(0, 0), - ProfileDateAppoint = p.DateAppoint.Value, + GovAge = p.DateStart!.Value.CalculateGovAgeStr(0, 0), + ProfileDateAppoint = p.DateAppoint!.Value, LastInsignia = p.ProfileInsignia == null || p.ProfileInsignia.Count == 0 ? "" : p.ProfileInsignia .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) @@ -7292,8 +7292,8 @@ namespace BMA.EHR.Application.Repositories FullName = $"{(p.Prefix == null ? null : p.Prefix)}{p.FirstName} {p.LastName}", Position = p.Position == null ? null : p.Position, Rank = p.PosLevel ?? "", - GovAge = p.DateAppoint.Value.CalculateGovAgeStr(0, 0), - ProfileDateAppoint = p.DateAppoint.Value, + GovAge = p.DateStart!.Value.CalculateGovAgeStr(0, 0), + ProfileDateAppoint = p.DateAppoint!.Value, LastInsignia = p.ProfileInsignia == null || p.ProfileInsignia.Count == 0 ? "" : p.ProfileInsignia .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) @@ -7454,8 +7454,8 @@ namespace BMA.EHR.Application.Repositories FullName = $"{(p.Prefix == null ? null : p.Prefix)}{p.FirstName} {p.LastName}", Position = p.Position == null ? null : p.Position, Rank = p.PosLevel ?? "", - GovAge = p.DateAppoint.Value.CalculateGovAgeStr(0, 0), - ProfileDateAppoint = p.DateAppoint.Value, + GovAge = p.DateStart!.Value.CalculateGovAgeStr(0, 0), + ProfileDateAppoint = p.DateAppoint!.Value, LastInsignia = p.ProfileInsignia == null || p.ProfileInsignia.Count == 0 ? "" : p.ProfileInsignia .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) @@ -7667,8 +7667,8 @@ namespace BMA.EHR.Application.Repositories FullName = $"{(p.Prefix == null ? null : p.Prefix)}{p.FirstName} {p.LastName}", Position = p.Position == null ? null : p.Position, Rank = p.PosLevel ?? "", - GovAge = p.DateAppoint.Value.CalculateGovAgeStr(0, 0), - ProfileDateAppoint = p.DateAppoint.Value, + GovAge = p.DateStart!.Value.CalculateGovAgeStr(0, 0), + ProfileDateAppoint = p.DateAppoint!.Value, LastInsignia = p.ProfileInsignia == null || p.ProfileInsignia.Count == 0 ? "" : p.ProfileInsignia .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) @@ -7835,8 +7835,8 @@ namespace BMA.EHR.Application.Repositories FullName = $"{(p.Prefix == null ? null : p.Prefix)}{p.FirstName} {p.LastName}", Position = p.Position == null ? null : p.Position, Rank = p.PosLevel ?? "", - GovAge = p.DateAppoint.Value.CalculateGovAgeStr(0, 0), - ProfileDateAppoint = p.DateAppoint.Value, + GovAge = p.DateStart!.Value.CalculateGovAgeStr(0, 0), + ProfileDateAppoint = p.DateAppoint!.Value, LastInsignia = p.ProfileInsignia == null || p.ProfileInsignia.Count == 0 ? "" : p.ProfileInsignia .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) @@ -8003,8 +8003,8 @@ namespace BMA.EHR.Application.Repositories FullName = $"{(p.Prefix == null ? null : p.Prefix)}{p.FirstName} {p.LastName}", Position = p.Position == null ? null : p.Position, Rank = p.PosLevel ?? "", - GovAge = p.DateAppoint.Value.CalculateGovAgeStr(0, 0), - ProfileDateAppoint = p.DateAppoint.Value, + GovAge = p.DateStart!.Value.CalculateGovAgeStr(0, 0), + ProfileDateAppoint = p.DateAppoint!.Value, LastInsignia = p.ProfileInsignia == null || p.ProfileInsignia.Count == 0 ? "" : p.ProfileInsignia .Where(x => x.InsigniaId.HasValue && x.InsigniaId.Value != GetInsigniaByName("เหรียญจักรพรรดิมาลา")?.Id) @@ -8625,8 +8625,8 @@ namespace BMA.EHR.Application.Repositories FullName = $"{(p.Prefix ?? "")}{p.FirstName} {p.LastName}", Position = p.Position ?? "", Rank = p.PosLevel ?? "", - ProfileDateAppoint = p.DateAppoint.Value, - GovAge = p.DateAppoint.Value.CalculateGovAgeStr(0, 0), + ProfileDateAppoint = p.DateAppoint!.Value, + GovAge = p.DateStart!.Value.CalculateGovAgeStr(0, 0), PosNo = p.PosNo, PositionLevelId = p.PosLevelId, PositionLevelName = p.PosLevel, diff --git a/BMA.EHR.Application/Repositories/Leaves/LeaveRequests/LeaveBeginingRepository.cs b/BMA.EHR.Application/Repositories/Leaves/LeaveRequests/LeaveBeginingRepository.cs index c2d08f70..ec280b48 100644 --- a/BMA.EHR.Application/Repositories/Leaves/LeaveRequests/LeaveBeginingRepository.cs +++ b/BMA.EHR.Application/Repositories/Leaves/LeaveRequests/LeaveBeginingRepository.cs @@ -108,7 +108,7 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests throw new Exception(GlobalMessages.DataNotFound); } - var govAge = (pf?.DateAppoint?.Date ?? DateTime.Now.Date).DiffDay(DateTime.Now.Date); + var govAge = (pf?.DateStart?.Date ?? DateTime.Now.Date).DiffDay(DateTime.Now.Date); var leaveType = await _dbContext.Set().FirstOrDefaultAsync(x => x.Id == typeId); @@ -174,7 +174,7 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests public async Task GetByYearAndTypeIdForUser(int year, Guid typeId, GetProfileByKeycloakIdDto? pf) { - var govAge = (pf?.DateAppoint?.Date ?? DateTime.Now.Date).DiffDay(DateTime.Now.Date); + var govAge = (pf?.DateStart?.Date ?? DateTime.Now.Date).DiffDay(DateTime.Now.Date); var leaveType = await _dbContext.Set().FirstOrDefaultAsync(x => x.Id == typeId); @@ -247,7 +247,7 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests return null; } - var govAge = (pf?.DateAppoint?.Date ?? DateTime.Now.Date).DiffDay(DateTime.Now.Date); + var govAge = (pf?.DateStart?.Date ?? DateTime.Now.Date).DiffDay(DateTime.Now.Date); var leaveType = await _dbContext.Set().FirstOrDefaultAsync(x => x.Id == typeId); @@ -337,7 +337,7 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests return null; } - var govAge = (pf?.DateAppoint?.Date ?? DateTime.Now.Date).DiffDay(DateTime.Now.Date); + var govAge = (pf?.DateStart?.Date ?? DateTime.Now.Date).DiffDay(DateTime.Now.Date); var leaveType = await _dbContext.Set().FirstOrDefaultAsync(x => x.Id == typeId); diff --git a/BMA.EHR.Leave/Controllers/LeaveRequestController.cs b/BMA.EHR.Leave/Controllers/LeaveRequestController.cs index a961bfa2..a90ad763 100644 --- a/BMA.EHR.Leave/Controllers/LeaveRequestController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveRequestController.cs @@ -869,7 +869,7 @@ namespace BMA.EHR.Leave.Service.Controllers return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound); } - var govAge = (profile?.DateAppoint?.Date ?? DateTime.Now.Date).DiffDay(DateTime.Now.Date); + var govAge = (profile?.DateStart?.Date ?? DateTime.Now.Date).DiffDay(DateTime.Now.Date); var leaveType = await _leaveTypeRepository.GetByIdAsync(req.Type); if (leaveType == null) @@ -985,7 +985,7 @@ namespace BMA.EHR.Leave.Service.Controllers // var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(userId, AccessToken); var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(userId, AccessToken); - var govAge = (profile?.DateAppoint?.Date ?? DateTime.Now.Date).DiffDay(DateTime.Now.Date); + var govAge = (profile?.DateStart?.Date ?? DateTime.Now.Date).DiffDay(DateTime.Now.Date); var thisYear = DateTime.Now.Year; var message = string.Empty; @@ -1542,7 +1542,7 @@ namespace BMA.EHR.Leave.Service.Controllers return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound); } - var govAge = (profile?.DateAppoint?.Date ?? DateTime.Now.Date).DiffDay(DateTime.Now.Date); + var govAge = (profile?.DateStart?.Date ?? DateTime.Now.Date).DiffDay(DateTime.Now.Date); var userCalendar = await _userCalendarRepository.GetExist(profile.Id); var category = userCalendar == null ? "NORMAL" : userCalendar.Calendar; From 970319e8c2daada5aa4ecb5723ac1a331aa21584 Mon Sep 17 00:00:00 2001 From: harid Date: Wed, 4 Feb 2026 11:05:02 +0700 Subject: [PATCH 089/178] =?UTF-8?q?API=20=E0=B8=AD=E0=B8=B1=E0=B8=9E?= =?UTF-8?q?=E0=B9=80=E0=B8=94=E0=B8=97=E0=B8=AA=E0=B8=96=E0=B8=B2=E0=B8=99?= =?UTF-8?q?=E0=B8=B0=E0=B9=80=E0=B8=9B=E0=B9=87=E0=B8=99=E0=B8=9A=E0=B8=A3?= =?UTF-8?q?=E0=B8=A3=E0=B8=88=E0=B8=B8=20=E0=B9=80=E0=B8=89=E0=B8=9E?= =?UTF-8?q?=E0=B8=B2=E0=B8=B0=20Super=5Fadmin?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Controllers/PlacementController.cs | 35 ++++++++++++++++++- .../Requests/PersonUpdateStatusRequest.cs | 10 ++++++ 2 files changed, 44 insertions(+), 1 deletion(-) create mode 100644 BMA.EHR.Placement.Service/Requests/PersonUpdateStatusRequest.cs diff --git a/BMA.EHR.Placement.Service/Controllers/PlacementController.cs b/BMA.EHR.Placement.Service/Controllers/PlacementController.cs index a485a42d..1a9260ef 100644 --- a/BMA.EHR.Placement.Service/Controllers/PlacementController.cs +++ b/BMA.EHR.Placement.Service/Controllers/PlacementController.cs @@ -62,9 +62,9 @@ namespace BMA.EHR.Placement.Service.Controllers #region " Properties " private string? UserId => _httpContextAccessor?.HttpContext?.User?.FindFirst(ClaimTypes.NameIdentifier)?.Value; - private string? FullName => _httpContextAccessor?.HttpContext?.User?.FindFirst("name")?.Value; private string? token => _httpContextAccessor.HttpContext.Request.Headers["Authorization"]; + private bool isSuperAdmin => _httpContextAccessor?.HttpContext?.User?.IsInRole("SUPER_ADMIN") ?? false; #endregion @@ -852,6 +852,39 @@ namespace BMA.EHR.Placement.Service.Controllers return Success(); } + /// + /// API อัพเดทสถานะเป็นบรรจุ + /// + /// + /// + /// ค่าตัวแปรที่ส่งมาไม่ถูกต้อง + /// ไม่ได้ Login เข้าระบบ + /// เมื่อเกิดข้อผิดพลาดในการทำงาน + [HttpPost("pass/update-status")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + [ProducesResponseType(StatusCodes.Status401Unauthorized)] + [ProducesResponseType(StatusCodes.Status500InternalServerError)] + public async Task> PersonUpdateStatus([FromBody] PersonUpdateStatusRequest req) + { + if (isSuperAdmin == false) + return Success(); + + var person = await _context.PlacementProfiles + .FirstOrDefaultAsync(x => x.Id == req.PersonalId); + if (person == null) + return Error(GlobalMessages.DataNotFound, 404); + + person.PlacementStatus = "DONE"; + person.LastUpdateFullName = FullName ?? "System Administrator"; + person.LastUpdateUserId = UserId ?? ""; + person.LastUpdatedAt = DateTime.Now; + + await _context.SaveChangesAsync(); + + return Success(); + } + [HttpGet("pass/deferment/{personalId:length(36)}")] public async Task> GetPersonDeferment(Guid personalId) { diff --git a/BMA.EHR.Placement.Service/Requests/PersonUpdateStatusRequest.cs b/BMA.EHR.Placement.Service/Requests/PersonUpdateStatusRequest.cs new file mode 100644 index 00000000..aef70fb5 --- /dev/null +++ b/BMA.EHR.Placement.Service/Requests/PersonUpdateStatusRequest.cs @@ -0,0 +1,10 @@ +using BMA.EHR.Domain.Models.MetaData; +using Microsoft.EntityFrameworkCore; + +namespace BMA.EHR.Placement.Service.Requests +{ + public class PersonUpdateStatusRequest + { + public Guid PersonalId { get; set; } + } +} From 358fd47b991c2468bff88dba285f13014e35f8e6 Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Thu, 5 Feb 2026 10:39:57 +0700 Subject: [PATCH 090/178] Add IsProbatin property to GetProfileByKeycloakIdDto and update leave request logic for probationary users #2266 --- .../Profiles/GetProfileByKeycloakIdDto.cs | 2 + BMA.EHR.Leave/Controllers/LeaveController.cs | 57 ++++++++++++------- .../Controllers/LeaveRequestController.cs | 8 ++- 3 files changed, 43 insertions(+), 24 deletions(-) diff --git a/BMA.EHR.Application/Responses/Profiles/GetProfileByKeycloakIdDto.cs b/BMA.EHR.Application/Responses/Profiles/GetProfileByKeycloakIdDto.cs index b2e71129..7db0e212 100644 --- a/BMA.EHR.Application/Responses/Profiles/GetProfileByKeycloakIdDto.cs +++ b/BMA.EHR.Application/Responses/Profiles/GetProfileByKeycloakIdDto.cs @@ -44,6 +44,8 @@ namespace BMA.EHR.Application.Responses.Profiles public string? ProfileType { get; set; } public bool? IsLeave { get; set; } + public bool? IsProbatin { get; set; } + public string? Root { get; set; } public string? Child1 { get; set; } public string? Child2 { get; set; } diff --git a/BMA.EHR.Leave/Controllers/LeaveController.cs b/BMA.EHR.Leave/Controllers/LeaveController.cs index d90321ba..85b782cd 100644 --- a/BMA.EHR.Leave/Controllers/LeaveController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveController.cs @@ -926,9 +926,17 @@ namespace BMA.EHR.Leave.Service.Controllers var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(userId, data.Token); if (profile == null) + { + await _checkInJobStatusRepository.UpdateToFailedAsync(taskId, "ไม่พบข้อมูลผู้ใช้"); return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound); + } - if (data.CheckInFileName == "no-file") throw new Exception(GlobalMessages.NoFileToUpload); + if (data.CheckInFileName == "no-file") + { + //throw new Exception(GlobalMessages.NoFileToUpload); + await _checkInJobStatusRepository.UpdateToFailedAsync(taskId, GlobalMessages.NoFileToUpload); + return Error(GlobalMessages.NoFileToUpload, StatusCodes.Status400BadRequest); + } var currentDate = data.CurrentDate ?? DateTime.Now; var check_status = data.CheckInId == null ? "check-in-picture" : "check-out-picture"; @@ -970,6 +978,7 @@ namespace BMA.EHR.Leave.Service.Controllers if (currentCheckIn != null) { + await _checkInJobStatusRepository.UpdateToFailedAsync(taskId, "ไม่สามารถลงเวลาได้ เนื่องจากมีการลงเวลาในวันนี้แล้ว"); return Error(new Exception("ไม่สามารถลงเวลาได้ เนื่องจากมีการลงเวลาในวันนี้แล้ว!"), StatusCodes.Status400BadRequest); } @@ -1104,31 +1113,35 @@ namespace BMA.EHR.Leave.Service.Controllers else { var checkout = await _userTimeStampRepository.GetByIdAsync(data.CheckInId.Value); - - var currentCheckInProcess = await _processUserTimeStampRepository.GetTimestampByDateAsync(userId, checkout.CheckIn.Date); - - var checkout_process = await _processUserTimeStampRepository.GetByIdAsync(currentCheckInProcess.Id); - - - if (checkout != null) - { - checkout.CheckOutLat = data.Lat; - checkout.CheckOutLon = data.Lon; - checkout.IsLocationCheckOut = data.IsLocation; - checkout.CheckOutLocationName = data.LocationName; - checkout.CheckOutPOI = data.POI; - checkout.CheckOutRemark = data.Remark; - checkout.CheckOutImageUrl = fileName; - checkout.CheckOut = currentDate; - - await _userTimeStampRepository.UpdateAsync(checkout); - } - else + + if (checkout == null) { await _checkInJobStatusRepository.UpdateToFailedAsync(taskId, "ไม่พบข้อมูลการลงเวลาทำงาน"); return Error(new Exception(GlobalMessages.DataNotFound), StatusCodes.Status404NotFound); } + var currentCheckInProcess = await _processUserTimeStampRepository.GetTimestampByDateAsync(userId, checkout.CheckIn.Date); + + if (currentCheckInProcess == null) + { + await _checkInJobStatusRepository.UpdateToFailedAsync(taskId, "ไม่พบข้อมูลการประมวลผลเวลาทำงาน (CheckIn)"); + return Error(new Exception(GlobalMessages.DataNotFound), StatusCodes.Status404NotFound); + } + + var checkout_process = await _processUserTimeStampRepository.GetByIdAsync(currentCheckInProcess.Id); + + // Update checkout record + checkout.CheckOutLat = data.Lat; + checkout.CheckOutLon = data.Lon; + checkout.IsLocationCheckOut = data.IsLocation; + checkout.CheckOutLocationName = data.LocationName; + checkout.CheckOutPOI = data.POI; + checkout.CheckOutRemark = data.Remark; + checkout.CheckOutImageUrl = fileName; + checkout.CheckOut = currentDate; + + await _userTimeStampRepository.UpdateAsync(checkout); + var endTime = ""; var startTime = ""; var endTimeMorning = ""; @@ -1289,7 +1302,7 @@ namespace BMA.EHR.Leave.Service.Controllers { await _checkInJobStatusRepository.UpdateToFailedAsync(taskId, ex.Message); } - throw; + return Error(ex); } } diff --git a/BMA.EHR.Leave/Controllers/LeaveRequestController.cs b/BMA.EHR.Leave/Controllers/LeaveRequestController.cs index a90ad763..1a00d285 100644 --- a/BMA.EHR.Leave/Controllers/LeaveRequestController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveRequestController.cs @@ -1084,8 +1084,12 @@ namespace BMA.EHR.Leave.Service.Controllers // fix issue : ระบบลา (ขรก.) >> ลาพักผ่อน (กรณีรับราชการไม่ถึง 6 เดือน) #838 //var leavePrevYear = (await _leaveRequestRepository.GetSumApproveLeaveAsync(fiscalYear - 1)).Where(x => x.LeaveTypeCode == "LV-005" && x.KeycloakUserId == userId).FirstOrDefault(); //var leavePrevYearRemain = 10 - (leavePrevYear == null ? 0 : leavePrevYear.SumLeaveDay); // หายอดวันลาที่เหลือของปีก่อน - - if (govAge >= 180) + if (profile.IsProbatin! == true) + { + isLeave = false; + if (!isLeave) message = "ยังอยู่ในช่วงทดลองปฏิบัติราชการ ไม่สามารถลาพักผ่อนได้"; + } + else if (govAge >= 180) { isLeave = (totalDay - (sumWorkDay + sumWeekend) + approveDay) <= (limitDay); if (!isLeave) message = "จำนวนวันลาเกินที่กำหนด"; From 639d41649c041634dbde63f0ca09a0ee5d114808 Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Thu, 5 Feb 2026 10:54:44 +0700 Subject: [PATCH 091/178] Add LeaveCount column to LeaveBeginnings table MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Introduced a new column 'LeaveCount' of type int to the LeaveBeginnings table. - Set default value to 0 and added a comment for clarity in Thai: "จำนวนครั้งที่ลาสะสม". --- .../Models/Leave/Requests/LeaveBeginning.cs | 3 + ...d LeaveCount to LeaveBeginning.Designer.cs | 1709 +++++++++++++++++ ...034753_Add LeaveCount to LeaveBeginning.cs | 30 + .../LeaveDb/LeaveDbContextModelSnapshot.cs | 4 + .../Controllers/LeaveBeginningController.cs | 3 + .../LeaveBeginnings/EditLeaveBeginningDto.cs | 3 + 6 files changed, 1752 insertions(+) create mode 100644 BMA.EHR.Infrastructure/Migrations/LeaveDb/20260205034753_Add LeaveCount to LeaveBeginning.Designer.cs create mode 100644 BMA.EHR.Infrastructure/Migrations/LeaveDb/20260205034753_Add LeaveCount to LeaveBeginning.cs diff --git a/BMA.EHR.Domain/Models/Leave/Requests/LeaveBeginning.cs b/BMA.EHR.Domain/Models/Leave/Requests/LeaveBeginning.cs index 9c459c69..d68f0229 100644 --- a/BMA.EHR.Domain/Models/Leave/Requests/LeaveBeginning.cs +++ b/BMA.EHR.Domain/Models/Leave/Requests/LeaveBeginning.cs @@ -30,6 +30,9 @@ namespace BMA.EHR.Domain.Models.Leave.Requests [Required, Comment("จำนวนวันลาที่ใช้ไป")] public double LeaveDaysUsed { get; set; } = 0.0; + [Comment("จำนวนครั้งที่ลาสะสม")] + public int LeaveCount { get; set; } = 0; + public Guid? RootDnaId { get; set; } public Guid? Child1DnaId { get; set; } diff --git a/BMA.EHR.Infrastructure/Migrations/LeaveDb/20260205034753_Add LeaveCount to LeaveBeginning.Designer.cs b/BMA.EHR.Infrastructure/Migrations/LeaveDb/20260205034753_Add LeaveCount to LeaveBeginning.Designer.cs new file mode 100644 index 00000000..acd7320f --- /dev/null +++ b/BMA.EHR.Infrastructure/Migrations/LeaveDb/20260205034753_Add LeaveCount to LeaveBeginning.Designer.cs @@ -0,0 +1,1709 @@ +// +using System; +using BMA.EHR.Infrastructure.Persistence; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace BMA.EHR.Infrastructure.Migrations.LeaveDb +{ + [DbContext(typeof(LeaveDbContext))] + [Migration("20260205034753_Add LeaveCount to LeaveBeginning")] + partial class AddLeaveCounttoLeaveBeginning + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "7.0.9") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + modelBuilder.Entity("BMA.EHR.Domain.Models.Documents.Document", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedDate") + .HasColumnType("datetime(6)"); + + b.Property("Detail") + .IsRequired() + .HasColumnType("text"); + + b.Property("FileName") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("FileSize") + .HasColumnType("int"); + + b.Property("FileType") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("varchar(128)"); + + b.Property("ObjectRefId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.ToTable("Document"); + }); + + modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.Commons.LeaveType", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)") + .HasColumnOrder(0) + .HasComment("PrimaryKey") + .HasAnnotation("Relational:JsonPropertyName", "id"); + + b.Property("Code") + .IsRequired() + .HasColumnType("longtext") + .HasComment("รหัสประเภทการลา"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(100) + .HasComment("สร้างข้อมูลเมื่อ"); + + b.Property("CreatedFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(104) + .HasComment("ชื่อ User ที่สร้างข้อมูล"); + + b.Property("CreatedUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(101) + .HasComment("User Id ที่สร้างข้อมูล"); + + b.Property("LastUpdateFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(105) + .HasComment("ชื่อ User ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdateUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(103) + .HasComment("User Id ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(102) + .HasComment("แก้ไขข้อมูลล่าสุดเมื่อ"); + + b.Property("Limit") + .HasColumnType("int") + .HasComment("จำนวนวันลาสูงสุดประจำปี"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext") + .HasComment("ชื่อประเภทการลา"); + + b.HasKey("Id"); + + b.ToTable("LeaveTypes"); + }); + + modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.Requests.LeaveBeginning", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)") + .HasColumnOrder(0) + .HasComment("PrimaryKey") + .HasAnnotation("Relational:JsonPropertyName", "id"); + + b.Property("Child1DnaId") + .HasColumnType("char(36)"); + + b.Property("Child2DnaId") + .HasColumnType("char(36)"); + + b.Property("Child3DnaId") + .HasColumnType("char(36)"); + + b.Property("Child4DnaId") + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(100) + .HasComment("สร้างข้อมูลเมื่อ"); + + b.Property("CreatedFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(104) + .HasComment("ชื่อ User ที่สร้างข้อมูล"); + + b.Property("CreatedUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(101) + .HasComment("User Id ที่สร้างข้อมูล"); + + b.Property("FirstName") + .HasColumnType("longtext"); + + b.Property("LastName") + .HasColumnType("longtext"); + + b.Property("LastUpdateFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(105) + .HasComment("ชื่อ User ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdateUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(103) + .HasComment("User Id ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(102) + .HasComment("แก้ไขข้อมูลล่าสุดเมื่อ"); + + b.Property("LeaveCount") + .HasColumnType("int") + .HasComment("จำนวนครั้งที่ลาสะสม"); + + b.Property("LeaveDays") + .HasColumnType("double") + .HasComment("จำนวนวันลายกมา"); + + b.Property("LeaveDaysUsed") + .HasColumnType("double") + .HasComment("จำนวนวันลาที่ใช้ไป"); + + b.Property("LeaveTypeId") + .HasColumnType("char(36)") + .HasComment("รหัสประเภทการลา"); + + b.Property("LeaveYear") + .HasColumnType("int") + .HasComment("ปีงบประมาณ"); + + b.Property("Prefix") + .HasColumnType("longtext"); + + b.Property("ProfileId") + .HasColumnType("char(36)") + .HasComment("รหัส Profile ในระบบทะเบียนประวัติ"); + + b.Property("RootDnaId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("LeaveTypeId"); + + b.ToTable("LeaveBeginnings"); + }); + + modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.Requests.LeaveDocument", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)") + .HasColumnOrder(0) + .HasComment("PrimaryKey") + .HasAnnotation("Relational:JsonPropertyName", "id"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(100) + .HasComment("สร้างข้อมูลเมื่อ"); + + b.Property("CreatedFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(104) + .HasComment("ชื่อ User ที่สร้างข้อมูล"); + + b.Property("CreatedUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(101) + .HasComment("User Id ที่สร้างข้อมูล"); + + b.Property("DocumentId") + .HasColumnType("char(36)"); + + b.Property("LastUpdateFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(105) + .HasComment("ชื่อ User ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdateUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(103) + .HasComment("User Id ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(102) + .HasComment("แก้ไขข้อมูลล่าสุดเมื่อ"); + + b.Property("LeaveRequestId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("DocumentId"); + + b.HasIndex("LeaveRequestId"); + + b.ToTable("LeaveDocuments"); + }); + + modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.Requests.LeaveRequest", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)") + .HasColumnOrder(0) + .HasComment("PrimaryKey") + .HasAnnotation("Relational:JsonPropertyName", "id"); + + b.Property("AbsentDayAt") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("AbsentDayGetIn") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("AbsentDayLocation") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("AbsentDayRegistorDate") + .HasColumnType("datetime(6)"); + + b.Property("AbsentDaySummon") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Amount") + .HasColumnType("double"); + + b.Property("ApproveStep") + .HasColumnType("longtext") + .HasComment("step การอนุมัติ st1 = จทน.อนุมัตื,st2 = ผู้บังคับบัญชา อนุมัติ "); + + b.Property("BirthDate") + .HasColumnType("datetime(6)"); + + b.Property("CancelLeaveWrote") + .HasColumnType("longtext") + .HasComment("เขียนที่ (ขอยกเลิก)"); + + b.Property("Child1") + .HasColumnType("longtext"); + + b.Property("Child1DnaId") + .HasColumnType("char(36)"); + + b.Property("Child1Id") + .HasColumnType("char(36)"); + + b.Property("Child2") + .HasColumnType("longtext"); + + b.Property("Child2DnaId") + .HasColumnType("char(36)"); + + b.Property("Child2Id") + .HasColumnType("char(36)"); + + b.Property("Child3") + .HasColumnType("longtext"); + + b.Property("Child3DnaId") + .HasColumnType("char(36)"); + + b.Property("Child3Id") + .HasColumnType("char(36)"); + + b.Property("Child4") + .HasColumnType("longtext"); + + b.Property("Child4DnaId") + .HasColumnType("char(36)"); + + b.Property("Child4Id") + .HasColumnType("char(36)"); + + b.Property("CitizenId") + .HasColumnType("longtext"); + + b.Property("CommanderPosition") + .HasColumnType("longtext"); + + b.Property("CoupleDayCountryHistory") + .HasColumnType("longtext"); + + b.Property("CoupleDayEndDateHistory") + .HasColumnType("datetime(6)"); + + b.Property("CoupleDayLevel") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("CoupleDayLevelCountry") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("CoupleDayName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("CoupleDayPosition") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("CoupleDayStartDateHistory") + .HasColumnType("datetime(6)"); + + b.Property("CoupleDaySumTotalHistory") + .HasColumnType("longtext"); + + b.Property("CoupleDayTotalHistory") + .HasColumnType("longtext"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(100) + .HasComment("สร้างข้อมูลเมื่อ"); + + b.Property("CreatedFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(104) + .HasComment("ชื่อ User ที่สร้างข้อมูล"); + + b.Property("CreatedUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(101) + .HasComment("User Id ที่สร้างข้อมูล"); + + b.Property("DateAppoint") + .HasColumnType("datetime(6)"); + + b.Property("Dear") + .HasColumnType("longtext") + .HasComment("เรียนใคร"); + + b.Property("FirstName") + .HasColumnType("longtext"); + + b.Property("Gender") + .HasColumnType("longtext"); + + b.Property("HajjDayStatus") + .HasColumnType("tinyint(1)"); + + b.Property("KeycloakUserId") + .HasColumnType("char(36)"); + + b.Property("LastName") + .HasColumnType("longtext"); + + b.Property("LastUpdateFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(105) + .HasComment("ชื่อ User ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdateUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(103) + .HasComment("User Id ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(102) + .HasComment("แก้ไขข้อมูลล่าสุดเมื่อ"); + + b.Property("LeaveAddress") + .IsRequired() + .HasColumnType("longtext") + .HasComment("สถานที่ติดต่อขณะลา"); + + b.Property("LeaveBirthDate") + .HasColumnType("datetime(6)"); + + b.Property("LeaveCancelComment") + .HasColumnType("longtext") + .HasComment("เหตุผลในการขอยกเลิก"); + + b.Property("LeaveCancelDocumentId") + .HasColumnType("char(36)"); + + b.Property("LeaveCancelStatus") + .HasColumnType("longtext") + .HasComment("สถานะของคำขอยกเลิก"); + + b.Property("LeaveComment") + .HasColumnType("longtext") + .HasComment("ความเห็นของผู้บังคับบัญชา"); + + b.Property("LeaveDetail") + .IsRequired() + .HasColumnType("longtext") + .HasComment("รายละเอียดการลา"); + + b.Property("LeaveDirectorComment") + .HasColumnType("longtext") + .HasComment("ความเห็นของผู้อำนวยการสำนัก"); + + b.Property("LeaveDraftDocumentId") + .HasColumnType("char(36)"); + + b.Property("LeaveEndDate") + .HasColumnType("datetime(6)") + .HasComment("วัน เดือน ปีสิ้นสุดลา"); + + b.Property("LeaveGovernmentDate") + .HasColumnType("datetime(6)"); + + b.Property("LeaveLast") + .HasColumnType("datetime(6)"); + + b.Property("LeaveNumber") + .IsRequired() + .HasColumnType("longtext") + .HasComment("หมายเลขที่ติดต่อขณะลา"); + + b.Property("LeaveRange") + .HasColumnType("longtext") + .HasComment("ช่วงของการลาของวันเริ่ม เช่น ลาทั้งวัน ครึ่งวันเช้า ครึ่งวันบ่าย"); + + b.Property("LeaveRangeEnd") + .HasColumnType("longtext") + .HasComment("ช่วงของการลาของวันสิ้นสุด เช่น ลาทั้งวัน ครึ่งวันเช้า ครึ่งวันบ่าย"); + + b.Property("LeaveSalary") + .HasColumnType("int"); + + b.Property("LeaveSalaryText") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("LeaveStartDate") + .HasColumnType("datetime(6)") + .HasComment("วัน เดือน ปีเริ่มต้นลา"); + + b.Property("LeaveStatus") + .IsRequired() + .HasColumnType("longtext") + .HasComment("สถานะของคำร้อง"); + + b.Property("LeaveSubTypeName") + .HasColumnType("longtext"); + + b.Property("LeaveTotal") + .HasColumnType("double"); + + b.Property("LeaveTypeCode") + .HasColumnType("longtext") + .HasComment("code ของประเภทการลา"); + + b.Property("LeaveWrote") + .IsRequired() + .HasColumnType("longtext") + .HasComment("เขียนที่"); + + b.Property("OrdainDayBuddhistLentAddress") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("OrdainDayBuddhistLentName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("OrdainDayLocationAddress") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("OrdainDayLocationName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("OrdainDayLocationNumber") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("OrdainDayOrdination") + .HasColumnType("datetime(6)"); + + b.Property("OrdainDayStatus") + .HasColumnType("tinyint(1)"); + + b.Property("OrganizationName") + .HasColumnType("longtext") + .HasComment("สังกัดผู้ยื่นขอ"); + + b.Property("PositionLevelName") + .HasColumnType("longtext") + .HasComment("ระดับผู้ยื่นขอ"); + + b.Property("PositionName") + .HasColumnType("longtext") + .HasComment("ตำแหน่งผู้ยื่นขอ"); + + b.Property("Prefix") + .HasColumnType("longtext"); + + b.Property("ProfileId") + .HasColumnType("char(36)"); + + b.Property("ProfileType") + .HasColumnType("longtext"); + + b.Property("RestDayCurrentTotal") + .HasColumnType("double"); + + b.Property("RestDayOldTotal") + .HasColumnType("double"); + + b.Property("Root") + .HasColumnType("longtext"); + + b.Property("RootDnaId") + .HasColumnType("char(36)"); + + b.Property("RootId") + .HasColumnType("char(36)"); + + b.Property("StudyDayCountry") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("StudyDayDegreeLevel") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("StudyDayScholarship") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("StudyDaySubject") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("StudyDayTrainingName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("StudyDayTrainingSubject") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("StudyDayUniversityName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("TypeId") + .HasColumnType("char(36)"); + + b.Property("WifeDayDateBorn") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("WifeDayName") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("LeaveCancelDocumentId"); + + b.HasIndex("LeaveDraftDocumentId"); + + b.HasIndex("TypeId"); + + b.ToTable("LeaveRequests"); + }); + + modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.Requests.LeaveRequestApprover", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)") + .HasColumnOrder(0) + .HasComment("PrimaryKey") + .HasAnnotation("Relational:JsonPropertyName", "id"); + + b.Property("ApproveStatus") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("ApproveType") + .HasColumnType("longtext"); + + b.Property("Comment") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(100) + .HasComment("สร้างข้อมูลเมื่อ"); + + b.Property("CreatedFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(104) + .HasComment("ชื่อ User ที่สร้างข้อมูล"); + + b.Property("CreatedUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(101) + .HasComment("User Id ที่สร้างข้อมูล"); + + b.Property("FirstName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("KeycloakId") + .HasColumnType("char(36)"); + + b.Property("LastName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("LastUpdateFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(105) + .HasComment("ชื่อ User ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdateUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(103) + .HasComment("User Id ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(102) + .HasComment("แก้ไขข้อมูลล่าสุดเมื่อ"); + + b.Property("LeaveRequestId") + .HasColumnType("char(36)"); + + b.Property("OrganizationName") + .IsRequired() + .HasColumnType("longtext") + .HasComment("สังกัด"); + + b.Property("PosExecutiveName") + .IsRequired() + .HasColumnType("longtext") + .HasComment("ตำแหน่งทางการบริหาร"); + + b.Property("PositionLevelName") + .IsRequired() + .HasColumnType("longtext") + .HasComment("ประเภทระดับตำแหน่ง"); + + b.Property("PositionName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("PositionSign") + .HasColumnType("longtext") + .HasComment("ตำแหน่งใต้ลายเช็นต์"); + + b.Property("Prefix") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("ProfileId") + .HasColumnType("char(36)"); + + b.Property("Seq") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("LeaveRequestId"); + + b.ToTable("LeaveRequestApprovers"); + }); + + modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.TimeAttendants.AdditionalCheckRequest", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)") + .HasColumnOrder(0) + .HasComment("PrimaryKey") + .HasAnnotation("Relational:JsonPropertyName", "id"); + + b.Property("CheckDate") + .HasColumnType("datetime(6)") + .HasComment("*วันที่ลงเวลา"); + + b.Property("CheckInEdit") + .HasColumnType("tinyint(1)") + .HasComment("*ขอลงเวลาช่วงเช้า"); + + b.Property("CheckOutEdit") + .HasColumnType("tinyint(1)") + .HasComment("*ขอลงเวลาช่วงบ่าย"); + + b.Property("Child1DnaId") + .HasColumnType("char(36)"); + + b.Property("Child2DnaId") + .HasColumnType("char(36)"); + + b.Property("Child3DnaId") + .HasColumnType("char(36)"); + + b.Property("Child4DnaId") + .HasColumnType("char(36)"); + + b.Property("Comment") + .HasColumnType("longtext") + .HasComment("หมายเหตุในการการอนุมัติ/ไม่อนุมัติ"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(100) + .HasComment("สร้างข้อมูลเมื่อ"); + + b.Property("CreatedFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(104) + .HasComment("ชื่อ User ที่สร้างข้อมูล"); + + b.Property("CreatedUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(101) + .HasComment("User Id ที่สร้างข้อมูล"); + + b.Property("Description") + .IsRequired() + .HasColumnType("longtext") + .HasComment("*หมายเหตุขอลงเวลาพิเศษ"); + + b.Property("FirstName") + .HasColumnType("longtext"); + + b.Property("KeycloakUserId") + .HasColumnType("char(36)") + .HasComment("รหัส User ของ Keycloak ที่ร้องขอ"); + + b.Property("LastName") + .HasColumnType("longtext"); + + b.Property("LastUpdateFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(105) + .HasComment("ชื่อ User ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdateUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(103) + .HasComment("User Id ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(102) + .HasComment("แก้ไขข้อมูลล่าสุดเมื่อ"); + + b.Property("Latitude") + .HasColumnType("double"); + + b.Property("Longitude") + .HasColumnType("double"); + + b.Property("POI") + .HasColumnType("longtext"); + + b.Property("Prefix") + .HasColumnType("longtext"); + + b.Property("RootDnaId") + .HasColumnType("char(36)"); + + b.Property("Status") + .IsRequired() + .HasColumnType("longtext") + .HasComment("สถานะการอนุมัติ"); + + b.HasKey("Id"); + + b.ToTable("AdditionalCheckRequests"); + }); + + modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.TimeAttendants.CheckInJobStatus", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)") + .HasColumnOrder(0) + .HasComment("PrimaryKey") + .HasAnnotation("Relational:JsonPropertyName", "id"); + + b.Property("AdditionalData") + .HasColumnType("longtext") + .HasComment("ข้อมูลเพิ่มเติม (JSON)"); + + b.Property("CheckInId") + .HasColumnType("char(36)") + .HasComment("CheckInId สำหรับ Check-Out"); + + b.Property("CheckType") + .HasColumnType("longtext") + .HasComment("ประเภทการลงเวลา: CHECK_IN, CHECK_OUT"); + + b.Property("CompletedDate") + .HasColumnType("datetime(6)") + .HasComment("วันเวลาที่เสร็จสิ้นการประมวลผล"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(100) + .HasComment("สร้างข้อมูลเมื่อ"); + + b.Property("CreatedDate") + .HasColumnType("datetime(6)") + .HasComment("วันเวลาที่สร้างงาน"); + + b.Property("CreatedFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(104) + .HasComment("ชื่อ User ที่สร้างข้อมูล"); + + b.Property("CreatedUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(101) + .HasComment("User Id ที่สร้างข้อมูล"); + + b.Property("ErrorMessage") + .HasColumnType("longtext") + .HasComment("ข้อความแสดงข้อผิดพลาด"); + + b.Property("KeycloakUserId") + .HasColumnType("char(36)") + .HasComment("รหัส User ของ Keycloak"); + + b.Property("LastUpdateFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(105) + .HasComment("ชื่อ User ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdateUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(103) + .HasComment("User Id ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(102) + .HasComment("แก้ไขข้อมูลล่าสุดเมื่อ"); + + b.Property("ProcessingDate") + .HasColumnType("datetime(6)") + .HasComment("วันเวลาที่เริ่มประมวลผล"); + + b.Property("Status") + .IsRequired() + .HasColumnType("longtext") + .HasComment("สถานะงาน: PENDING, PROCESSING, COMPLETED, FAILED"); + + b.Property("TaskId") + .HasColumnType("char(36)") + .HasComment("Task ID สำหรับติดตามสถานะงาน"); + + b.HasKey("Id"); + + b.ToTable("CheckInJobStatuses"); + }); + + modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.TimeAttendants.DutyTime", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)") + .HasColumnOrder(0) + .HasComment("PrimaryKey") + .HasAnnotation("Relational:JsonPropertyName", "id"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(100) + .HasComment("สร้างข้อมูลเมื่อ"); + + b.Property("CreatedFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(104) + .HasComment("ชื่อ User ที่สร้างข้อมูล"); + + b.Property("CreatedUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(101) + .HasComment("User Id ที่สร้างข้อมูล"); + + b.Property("Description") + .IsRequired() + .HasColumnType("longtext") + .HasComment("คำอธิบาย"); + + b.Property("EndTimeAfternoon") + .IsRequired() + .HasColumnType("longtext") + .HasComment("เวลาออกงานช่วงบ่าย"); + + b.Property("EndTimeMorning") + .IsRequired() + .HasColumnType("longtext") + .HasComment("เวลาออกงานช่วงเช้า"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)") + .HasComment("สถานะการเปิดใช้งาน (เปิด/ปิด)"); + + b.Property("IsDefault") + .HasColumnType("tinyint(1)") + .HasComment("สถานะว่ารอบใดเป็นค่า Default ของข้าราชการ (สำหรับทุกคนที่ยังไม่ได้ทำการเลือกรอบ)"); + + b.Property("LastUpdateFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(105) + .HasComment("ชื่อ User ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdateUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(103) + .HasComment("User Id ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(102) + .HasComment("แก้ไขข้อมูลล่าสุดเมื่อ"); + + b.Property("StartTimeAfternoon") + .IsRequired() + .HasColumnType("longtext") + .HasComment("เวลาเข้างานช่วงบ่าย"); + + b.Property("StartTimeMorning") + .IsRequired() + .HasColumnType("longtext") + .HasComment("เวลาเข้างานช่วงเช้า"); + + b.HasKey("Id"); + + b.ToTable("DutyTimes"); + }); + + modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.TimeAttendants.ProcessUserTimeStamp", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)") + .HasColumnOrder(0) + .HasComment("PrimaryKey") + .HasAnnotation("Relational:JsonPropertyName", "id"); + + b.Property("CheckIn") + .HasColumnType("datetime(6)") + .HasComment("วัน เวลา เข้างาน"); + + b.Property("CheckInImageUrl") + .IsRequired() + .HasColumnType("longtext") + .HasComment("รูปถ่ายสถานที่ Check-In"); + + b.Property("CheckInLat") + .HasColumnType("double") + .HasComment("พิกัดละติจูด Check-In"); + + b.Property("CheckInLocationName") + .HasColumnType("longtext") + .HasComment("กรณีเลือกนอกสถานที่ตั้ง ต้องระบุข้อมูลชื่อสถานะที่ Check-In"); + + b.Property("CheckInLon") + .HasColumnType("double") + .HasComment("พิกัดลองจิจูด Check-In"); + + b.Property("CheckInPOI") + .IsRequired() + .HasColumnType("longtext") + .HasComment("ชื่อสถานที่ ได้มาจากระบบ ArcGis ของกองสารสนเทศภูมิศาสตร์ Check-In"); + + b.Property("CheckInRemark") + .HasColumnType("longtext") + .HasComment("ข้อความหมายเหตุที่ต้องการระบุเพิ่ม(มีเผื่อไว้อาจไม่ได้ใช้) Check-In"); + + b.Property("CheckInStatus") + .IsRequired() + .HasColumnType("longtext") + .HasComment("สถานะ Check-In"); + + b.Property("CheckOut") + .HasColumnType("datetime(6)") + .HasComment("วัน เวลา ออกงาน"); + + b.Property("CheckOutImageUrl") + .IsRequired() + .HasColumnType("longtext") + .HasComment("รูปถ่ายสถานที่ Check-Out"); + + b.Property("CheckOutLat") + .HasColumnType("double") + .HasComment("พิกัดละติจูด Check-Out"); + + b.Property("CheckOutLocationName") + .HasColumnType("longtext") + .HasComment("กรณีเลือกนอกสถานที่ตั้ง ต้องระบุข้อมูลชื่อสถานะที่ Check-Out"); + + b.Property("CheckOutLon") + .HasColumnType("double") + .HasComment("พิกัดลองจิจูด Check-Out"); + + b.Property("CheckOutPOI") + .IsRequired() + .HasColumnType("longtext") + .HasComment("ชื่อสถานที่ ได้มาจากระบบ ArcGis ของกองสารสนเทศภูมิศาสตร์ Check-Out"); + + b.Property("CheckOutRemark") + .HasColumnType("longtext") + .HasComment("ข้อความหมายเหตุที่ต้องการระบุเพิ่ม(มีเผื่อไว้อาจไม่ได้ใช้) Check-Out"); + + b.Property("CheckOutStatus") + .HasColumnType("longtext") + .HasComment("สถานะ Check-Out"); + + b.Property("Child1") + .HasColumnType("longtext"); + + b.Property("Child1DnaId") + .HasColumnType("char(36)"); + + b.Property("Child1Id") + .HasColumnType("char(36)"); + + b.Property("Child2") + .HasColumnType("longtext"); + + b.Property("Child2DnaId") + .HasColumnType("char(36)"); + + b.Property("Child2Id") + .HasColumnType("char(36)"); + + b.Property("Child3") + .HasColumnType("longtext"); + + b.Property("Child3DnaId") + .HasColumnType("char(36)"); + + b.Property("Child3Id") + .HasColumnType("char(36)"); + + b.Property("Child4") + .HasColumnType("longtext"); + + b.Property("Child4DnaId") + .HasColumnType("char(36)"); + + b.Property("Child4Id") + .HasColumnType("char(36)"); + + b.Property("CitizenId") + .HasColumnType("longtext"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(100) + .HasComment("สร้างข้อมูลเมื่อ"); + + b.Property("CreatedFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(104) + .HasComment("ชื่อ User ที่สร้างข้อมูล"); + + b.Property("CreatedUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(101) + .HasComment("User Id ที่สร้างข้อมูล"); + + b.Property("EditReason") + .HasColumnType("longtext") + .HasComment("เหตุผลการอนุมัติ/ไม่อนุมัติขอลงเวลาพิเศษ"); + + b.Property("EditStatus") + .HasColumnType("longtext") + .HasComment("สถานะการของลงเวลาพิเศษ"); + + b.Property("FirstName") + .HasColumnType("longtext"); + + b.Property("Gender") + .HasColumnType("longtext"); + + b.Property("IsLocationCheckIn") + .HasColumnType("tinyint(1)") + .HasComment("true คือ ณ สถานที่ตั้ง, false คือ นอกสถานที่ตั้ง Check-In"); + + b.Property("IsLocationCheckOut") + .HasColumnType("tinyint(1)") + .HasComment("true คือ ณ สถานที่ตั้ง, false คือ นอกสถานที่ตั้ง Check-Out"); + + b.Property("IsProcess") + .HasColumnType("tinyint(1)") + .HasComment("นำไปประมวลผลแล้วหรือยัง"); + + b.Property("KeycloakUserId") + .HasColumnType("char(36)") + .HasComment("รหัส User ของ Keycloak"); + + b.Property("LastName") + .HasColumnType("longtext"); + + b.Property("LastUpdateFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(105) + .HasComment("ชื่อ User ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdateUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(103) + .HasComment("User Id ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(102) + .HasComment("แก้ไขข้อมูลล่าสุดเมื่อ"); + + b.Property("Prefix") + .HasColumnType("longtext"); + + b.Property("ProfileId") + .HasColumnType("char(36)"); + + b.Property("ProfileType") + .HasColumnType("longtext"); + + b.Property("Root") + .HasColumnType("longtext"); + + b.Property("RootDnaId") + .HasColumnType("char(36)"); + + b.Property("RootId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.ToTable("ProcessUserTimeStamps"); + }); + + modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.TimeAttendants.UserCalendar", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)") + .HasColumnOrder(0) + .HasComment("PrimaryKey") + .HasAnnotation("Relational:JsonPropertyName", "id"); + + b.Property("Calendar") + .IsRequired() + .HasColumnType("longtext") + .HasComment("ปฏิทินการทำงานของ ขรก ปกติ หรือ 6 วันต่อสัปดาห์"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(100) + .HasComment("สร้างข้อมูลเมื่อ"); + + b.Property("CreatedFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(104) + .HasComment("ชื่อ User ที่สร้างข้อมูล"); + + b.Property("CreatedUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(101) + .HasComment("User Id ที่สร้างข้อมูล"); + + b.Property("LastUpdateFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(105) + .HasComment("ชื่อ User ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdateUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(103) + .HasComment("User Id ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(102) + .HasComment("แก้ไขข้อมูลล่าสุดเมื่อ"); + + b.Property("ProfileId") + .HasColumnType("char(36)") + .HasComment("รหัส Profile ในระบบทะเบียนประวัติ"); + + b.HasKey("Id"); + + b.ToTable("UserCalendars"); + }); + + modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.TimeAttendants.UserDutyTime", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)") + .HasColumnOrder(0) + .HasComment("PrimaryKey") + .HasAnnotation("Relational:JsonPropertyName", "id"); + + b.Property("Child1DnaId") + .HasColumnType("char(36)"); + + b.Property("Child2DnaId") + .HasColumnType("char(36)"); + + b.Property("Child3DnaId") + .HasColumnType("char(36)"); + + b.Property("Child4DnaId") + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(100) + .HasComment("สร้างข้อมูลเมื่อ"); + + b.Property("CreatedFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(104) + .HasComment("ชื่อ User ที่สร้างข้อมูล"); + + b.Property("CreatedUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(101) + .HasComment("User Id ที่สร้างข้อมูล"); + + b.Property("DutyTimeId") + .HasColumnType("char(36)") + .HasComment("รหัสรอบการลงเวลา"); + + b.Property("EffectiveDate") + .HasColumnType("datetime(6)") + .HasComment("วันที่มีผล"); + + b.Property("IsProcess") + .HasColumnType("tinyint(1)") + .HasComment("ทำการประมวลผลแล้วหรือยัง"); + + b.Property("LastUpdateFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(105) + .HasComment("ชื่อ User ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdateUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(103) + .HasComment("User Id ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(102) + .HasComment("แก้ไขข้อมูลล่าสุดเมื่อ"); + + b.Property("ProfileId") + .HasColumnType("char(36)") + .HasComment("รหัส Profile ในระบบทะเบียนประวัติ"); + + b.Property("Remark") + .HasColumnType("longtext") + .HasComment("หมายเหตุ"); + + b.Property("RootDnaId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("DutyTimeId"); + + b.ToTable("UserDutyTimes"); + }); + + modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.TimeAttendants.UserTimeStamp", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)") + .HasColumnOrder(0) + .HasComment("PrimaryKey") + .HasAnnotation("Relational:JsonPropertyName", "id"); + + b.Property("CheckIn") + .HasColumnType("datetime(6)") + .HasComment("วัน เวลา เข้างาน"); + + b.Property("CheckInImageUrl") + .IsRequired() + .HasColumnType("longtext") + .HasComment("รูปถ่ายสถานที่ Check-In"); + + b.Property("CheckInLat") + .HasColumnType("double") + .HasComment("พิกัดละติจูด Check-In"); + + b.Property("CheckInLocationName") + .HasColumnType("longtext") + .HasComment("กรณีเลือกนอกสถานที่ตั้ง ต้องระบุข้อมูลชื่อสถานะที่ Check-In"); + + b.Property("CheckInLon") + .HasColumnType("double") + .HasComment("พิกัดลองจิจูด Check-In"); + + b.Property("CheckInPOI") + .IsRequired() + .HasColumnType("longtext") + .HasComment("ชื่อสถานที่ ได้มาจากระบบ ArcGis ของกองสารสนเทศภูมิศาสตร์ Check-In"); + + b.Property("CheckInRemark") + .HasColumnType("longtext") + .HasComment("ข้อความหมายเหตุที่ต้องการระบุเพิ่ม(มีเผื่อไว้อาจไม่ได้ใช้) Check-In"); + + b.Property("CheckOut") + .HasColumnType("datetime(6)") + .HasComment("วัน เวลา ออกงาน"); + + b.Property("CheckOutImageUrl") + .IsRequired() + .HasColumnType("longtext") + .HasComment("รูปถ่ายสถานที่ Check-Out"); + + b.Property("CheckOutLat") + .HasColumnType("double") + .HasComment("พิกัดละติจูด Check-Out"); + + b.Property("CheckOutLocationName") + .HasColumnType("longtext") + .HasComment("กรณีเลือกนอกสถานที่ตั้ง ต้องระบุข้อมูลชื่อสถานะที่ Check-Out"); + + b.Property("CheckOutLon") + .HasColumnType("double") + .HasComment("พิกัดลองจิจูด Check-Out"); + + b.Property("CheckOutPOI") + .IsRequired() + .HasColumnType("longtext") + .HasComment("ชื่อสถานที่ ได้มาจากระบบ ArcGis ของกองสารสนเทศภูมิศาสตร์ Check-Out"); + + b.Property("CheckOutRemark") + .HasColumnType("longtext") + .HasComment("ข้อความหมายเหตุที่ต้องการระบุเพิ่ม(มีเผื่อไว้อาจไม่ได้ใช้) Check-Out"); + + b.Property("Child1") + .HasColumnType("longtext"); + + b.Property("Child1DnaId") + .HasColumnType("char(36)"); + + b.Property("Child1Id") + .HasColumnType("char(36)"); + + b.Property("Child2") + .HasColumnType("longtext"); + + b.Property("Child2DnaId") + .HasColumnType("char(36)"); + + b.Property("Child2Id") + .HasColumnType("char(36)"); + + b.Property("Child3") + .HasColumnType("longtext"); + + b.Property("Child3DnaId") + .HasColumnType("char(36)"); + + b.Property("Child3Id") + .HasColumnType("char(36)"); + + b.Property("Child4") + .HasColumnType("longtext"); + + b.Property("Child4DnaId") + .HasColumnType("char(36)"); + + b.Property("Child4Id") + .HasColumnType("char(36)"); + + b.Property("CitizenId") + .HasColumnType("longtext"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(100) + .HasComment("สร้างข้อมูลเมื่อ"); + + b.Property("CreatedFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(104) + .HasComment("ชื่อ User ที่สร้างข้อมูล"); + + b.Property("CreatedUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(101) + .HasComment("User Id ที่สร้างข้อมูล"); + + b.Property("FirstName") + .HasColumnType("longtext"); + + b.Property("Gender") + .HasColumnType("longtext"); + + b.Property("IsLocationCheckIn") + .HasColumnType("tinyint(1)") + .HasComment("true คือ ณ สถานที่ตั้ง, false คือ นอกสถานที่ตั้ง Check-In"); + + b.Property("IsLocationCheckOut") + .HasColumnType("tinyint(1)") + .HasComment("true คือ ณ สถานที่ตั้ง, false คือ นอกสถานที่ตั้ง Check-Out"); + + b.Property("IsProcess") + .HasColumnType("tinyint(1)") + .HasComment("นำไปประมวลผลแล้วหรือยัง"); + + b.Property("KeycloakUserId") + .HasColumnType("char(36)") + .HasComment("รหัส User ของ Keycloak"); + + b.Property("LastName") + .HasColumnType("longtext"); + + b.Property("LastUpdateFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(105) + .HasComment("ชื่อ User ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdateUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(103) + .HasComment("User Id ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(102) + .HasComment("แก้ไขข้อมูลล่าสุดเมื่อ"); + + b.Property("Prefix") + .HasColumnType("longtext"); + + b.Property("ProfileId") + .HasColumnType("char(36)"); + + b.Property("ProfileType") + .HasColumnType("longtext"); + + b.Property("Root") + .HasColumnType("longtext"); + + b.Property("RootDnaId") + .HasColumnType("char(36)"); + + b.Property("RootId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.ToTable("UserTimeStamps"); + }); + + modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.Requests.LeaveBeginning", b => + { + b.HasOne("BMA.EHR.Domain.Models.Leave.Commons.LeaveType", "LeaveType") + .WithMany() + .HasForeignKey("LeaveTypeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("LeaveType"); + }); + + modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.Requests.LeaveDocument", b => + { + b.HasOne("BMA.EHR.Domain.Models.Documents.Document", "Document") + .WithMany() + .HasForeignKey("DocumentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("BMA.EHR.Domain.Models.Leave.Requests.LeaveRequest", "LeaveRequest") + .WithMany("LeaveDocument") + .HasForeignKey("LeaveRequestId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Document"); + + b.Navigation("LeaveRequest"); + }); + + modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.Requests.LeaveRequest", b => + { + b.HasOne("BMA.EHR.Domain.Models.Documents.Document", "LeaveCancelDocument") + .WithMany() + .HasForeignKey("LeaveCancelDocumentId"); + + b.HasOne("BMA.EHR.Domain.Models.Documents.Document", "LeaveDraftDocument") + .WithMany() + .HasForeignKey("LeaveDraftDocumentId"); + + b.HasOne("BMA.EHR.Domain.Models.Leave.Commons.LeaveType", "Type") + .WithMany() + .HasForeignKey("TypeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("LeaveCancelDocument"); + + b.Navigation("LeaveDraftDocument"); + + b.Navigation("Type"); + }); + + modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.Requests.LeaveRequestApprover", b => + { + b.HasOne("BMA.EHR.Domain.Models.Leave.Requests.LeaveRequest", "LeaveRequest") + .WithMany("Approvers") + .HasForeignKey("LeaveRequestId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("LeaveRequest"); + }); + + modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.TimeAttendants.UserDutyTime", b => + { + b.HasOne("BMA.EHR.Domain.Models.Leave.TimeAttendants.DutyTime", "DutyTime") + .WithMany() + .HasForeignKey("DutyTimeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("DutyTime"); + }); + + modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.Requests.LeaveRequest", b => + { + b.Navigation("Approvers"); + + b.Navigation("LeaveDocument"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/BMA.EHR.Infrastructure/Migrations/LeaveDb/20260205034753_Add LeaveCount to LeaveBeginning.cs b/BMA.EHR.Infrastructure/Migrations/LeaveDb/20260205034753_Add LeaveCount to LeaveBeginning.cs new file mode 100644 index 00000000..35fecaea --- /dev/null +++ b/BMA.EHR.Infrastructure/Migrations/LeaveDb/20260205034753_Add LeaveCount to LeaveBeginning.cs @@ -0,0 +1,30 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace BMA.EHR.Infrastructure.Migrations.LeaveDb +{ + /// + public partial class AddLeaveCounttoLeaveBeginning : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "LeaveCount", + table: "LeaveBeginnings", + type: "int", + nullable: false, + defaultValue: 0, + comment: "จำนวนครั้งที่ลาสะสม"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "LeaveCount", + table: "LeaveBeginnings"); + } + } +} diff --git a/BMA.EHR.Infrastructure/Migrations/LeaveDb/LeaveDbContextModelSnapshot.cs b/BMA.EHR.Infrastructure/Migrations/LeaveDb/LeaveDbContextModelSnapshot.cs index d12bf747..b59264fc 100644 --- a/BMA.EHR.Infrastructure/Migrations/LeaveDb/LeaveDbContextModelSnapshot.cs +++ b/BMA.EHR.Infrastructure/Migrations/LeaveDb/LeaveDbContextModelSnapshot.cs @@ -184,6 +184,10 @@ namespace BMA.EHR.Infrastructure.Migrations.LeaveDb .HasColumnOrder(102) .HasComment("แก้ไขข้อมูลล่าสุดเมื่อ"); + b.Property("LeaveCount") + .HasColumnType("int") + .HasComment("จำนวนครั้งที่ลาสะสม"); + b.Property("LeaveDays") .HasColumnType("double") .HasComment("จำนวนวันลายกมา"); diff --git a/BMA.EHR.Leave/Controllers/LeaveBeginningController.cs b/BMA.EHR.Leave/Controllers/LeaveBeginningController.cs index 4c37f19d..3a3140e8 100644 --- a/BMA.EHR.Leave/Controllers/LeaveBeginningController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveBeginningController.cs @@ -201,6 +201,7 @@ namespace BMA.EHR.Leave.Service.Controllers item.LeaveYear, item.LeaveDays, item.LeaveDaysUsed, + item.LeaveCount, item.CreatedAt, item.CreatedFullName, item.LastUpdatedAt, @@ -394,6 +395,7 @@ namespace BMA.EHR.Leave.Service.Controllers leaveBeginning.LeaveYear = req.LeaveYear; leaveBeginning.LeaveDays = req.LeaveDays; leaveBeginning.LeaveDaysUsed = req.LeaveDaysUsed; + leaveBeginning.LeaveCount = req.LeaveCount; leaveBeginning.ProfileId = req.ProfileId; leaveBeginning.Prefix = profile.Prefix; @@ -462,6 +464,7 @@ namespace BMA.EHR.Leave.Service.Controllers leaveBeginning.LeaveYear = req.LeaveYear; leaveBeginning.LeaveDays = req.LeaveDays; leaveBeginning.LeaveDaysUsed = req.LeaveDaysUsed; + leaveBeginning.LeaveCount = req.LeaveCount; leaveBeginning.ProfileId = req.ProfileId; leaveBeginning.Prefix = profile.Prefix; diff --git a/BMA.EHR.Leave/DTOs/LeaveBeginnings/EditLeaveBeginningDto.cs b/BMA.EHR.Leave/DTOs/LeaveBeginnings/EditLeaveBeginningDto.cs index 6ecfe1f4..a73bdfd4 100644 --- a/BMA.EHR.Leave/DTOs/LeaveBeginnings/EditLeaveBeginningDto.cs +++ b/BMA.EHR.Leave/DTOs/LeaveBeginnings/EditLeaveBeginningDto.cs @@ -19,5 +19,8 @@ namespace BMA.EHR.Leave.Service.DTOs.LeaveBeginnings [Required, Comment("จำนวนวันลาที่ใช้ไป")] public double LeaveDaysUsed { get; set; } = 0.0; + + [Required, Comment("จำนวนครั้งที่ลาสะสม")] + public int LeaveCount { get; set; } = 0; } } From d3cc0781cfad3ee47a7a9a9961de56f66dd1c064 Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Thu, 5 Feb 2026 11:01:49 +0700 Subject: [PATCH 092/178] Add UpdateLeaveCountAsync method to LeaveBeginningRepository and integrate it into leave request logic #2288 --- .../LeaveRequests/LeaveBeginingRepository.cs | 22 +++++++++++++++++++ .../LeaveRequests/LeaveRequestRepository.cs | 4 ++++ 2 files changed, 26 insertions(+) diff --git a/BMA.EHR.Application/Repositories/Leaves/LeaveRequests/LeaveBeginingRepository.cs b/BMA.EHR.Application/Repositories/Leaves/LeaveRequests/LeaveBeginingRepository.cs index ec280b48..2d14db9c 100644 --- a/BMA.EHR.Application/Repositories/Leaves/LeaveRequests/LeaveBeginingRepository.cs +++ b/BMA.EHR.Application/Repositories/Leaves/LeaveRequests/LeaveBeginingRepository.cs @@ -99,6 +99,28 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests await _dbContext.SaveChangesAsync(); } + public async Task UpdateLeaveCountAsync(int year, Guid typeId, Guid userId, int count) + { + // var pf = await _userProfileRepository.GetProfileByKeycloakIdAsync(userId, AccessToken); + var pf = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(userId, AccessToken); + if (pf == null) + { + throw new Exception(GlobalMessages.DataNotFound); + } + + var data = await _dbContext.Set() + .Include(x => x.LeaveType) + .FirstOrDefaultAsync(x => x.LeaveYear == year && x.LeaveTypeId == typeId && x.ProfileId == pf.Id); + + if (data == null) + { + throw new Exception(GlobalMessages.DataNotFound); + } + data.LeaveCount += count; + + await _dbContext.SaveChangesAsync(); + } + public async Task GetByYearAndTypeIdForUserAsync(int year, Guid typeId, Guid userId) { // var pf = await _userProfileRepository.GetProfileByKeycloakIdAsync(userId, AccessToken); diff --git a/BMA.EHR.Application/Repositories/Leaves/LeaveRequests/LeaveRequestRepository.cs b/BMA.EHR.Application/Repositories/Leaves/LeaveRequests/LeaveRequestRepository.cs index b97441be..972a228d 100644 --- a/BMA.EHR.Application/Repositories/Leaves/LeaveRequests/LeaveRequestRepository.cs +++ b/BMA.EHR.Application/Repositories/Leaves/LeaveRequests/LeaveRequestRepository.cs @@ -733,6 +733,8 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests } await _leaveBeginningRepository.UpdateLeaveUsageAsync(thisYear, rawData.Type.Id, rawData.KeycloakUserId, -1 * rawData.LeaveTotal); + // update leave count ลดลง 1 ครั้ง + await _leaveBeginningRepository.UpdateLeaveCountAsync(thisYear, rawData.Type.Id, rawData.KeycloakUserId, -1); var _baseAPI = _configuration["API"]; var apiUrlSalary = $"{_baseAPI}/org/profile/leave/cancel/{rawData.Id}"; @@ -1241,6 +1243,8 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests // TODO : Update ไปตาราง beginning await _leaveBeginningRepository.UpdateLeaveUsageAsync(thisYear, rawData.Type.Id, rawData.KeycloakUserId, rawData.LeaveTotal); + // update leave count เพิ่มขึ้น 1 ครั้ง + await _leaveBeginningRepository.UpdateLeaveCountAsync(thisYear, rawData.Type.Id, rawData.KeycloakUserId, 1); var _baseAPI = _configuration["API"]; From 4f18a97d0b886859cf2ed2491fe902a8b9e9da39 Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Thu, 5 Feb 2026 11:57:19 +0700 Subject: [PATCH 093/178] Add GetOCStaffAsync method to UserProfileRepository and create GetOcStaff response models --- .../Repositories/UserProfileRepository.cs | 34 ++++++- .../Responses/Profiles/GetOcStaff.cs | 35 +++++++ BMA.EHR.Leave/Controllers/LeaveController.cs | 94 ++++++++++++++++++- 3 files changed, 158 insertions(+), 5 deletions(-) create mode 100644 BMA.EHR.Application/Responses/Profiles/GetOcStaff.cs diff --git a/BMA.EHR.Application/Repositories/UserProfileRepository.cs b/BMA.EHR.Application/Repositories/UserProfileRepository.cs index 4e0e986f..db63e056 100644 --- a/BMA.EHR.Application/Repositories/UserProfileRepository.cs +++ b/BMA.EHR.Application/Repositories/UserProfileRepository.cs @@ -186,6 +186,8 @@ namespace BMA.EHR.Application.Repositories } } + + public async Task GetProfileByKeycloakIdNewAsync(Guid keycloakId, string? accessToken,CancellationToken cancellationToken = default) { try @@ -256,6 +258,36 @@ namespace BMA.EHR.Application.Repositories } } + public async Task?> GetOCStaffAsync(Guid profileId, string? accessToken) + { + try + { + var apiPath = $"{_configuration["API"]}/org/dotnet/find-staff"; + var apiKey = _configuration["API_KEY"]; + var body = new + { + assignId = "SYS_LEAVE_LIST", + profileId = profileId + }; + + //var profiles = new List(); + + var apiResult = await PostExternalAPIAsync(apiPath, accessToken ?? "", body, apiKey); + if (apiResult != null) + { + var raw = JsonConvert.DeserializeObject(apiResult); + if (raw != null) + return raw.Result; + } + + return null; + } + catch + { + throw; + } + } + public async Task GetProfileLeaveReportByKeycloakIdAsync(Guid keycloakId, string? accessToken, string? report) { try @@ -268,7 +300,7 @@ namespace BMA.EHR.Application.Repositories report = report }; - var profiles = new List(); + //var profiles = new List(); var apiResult = await PostExternalAPIAsync(apiPath, accessToken, body, apiKey); if (apiResult != null) diff --git a/BMA.EHR.Application/Responses/Profiles/GetOcStaff.cs b/BMA.EHR.Application/Responses/Profiles/GetOcStaff.cs new file mode 100644 index 00000000..fa3ce936 --- /dev/null +++ b/BMA.EHR.Application/Responses/Profiles/GetOcStaff.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace BMA.EHR.Application.Responses.Profiles +{ + public class GetOcStaff + { + public Guid ProfileId { get; set; } + public Guid Keycloak { get; set; } + public string FullName { get; set; } = null!; + public Guid? RootId { get; set; } + public Guid? OrgChild1Id { get; set; } + public Guid? OrgChild2Id { get; set; } + public Guid? OrgChild3Id { get; set; } + public Guid? OrgChild4Id { get; set; } + public Guid? RootDnaId { get; set; } + public Guid? Child1DnaId { get; set; } + public Guid? Child2DnaId { get; set; } + public Guid? Child3DnaId { get; set; } + public Guid? Child4DnaId { get; set; } + + } + + public class GetOcStaffResultDto + { + public string Message { get; set; } = string.Empty; + + public int Status { get; set; } = -1; + + public List Result { get; set; } = new(); + + } +} \ No newline at end of file diff --git a/BMA.EHR.Leave/Controllers/LeaveController.cs b/BMA.EHR.Leave/Controllers/LeaveController.cs index 85b782cd..665b209c 100644 --- a/BMA.EHR.Leave/Controllers/LeaveController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveController.cs @@ -6,6 +6,7 @@ using BMA.EHR.Application.Repositories.MessageQueue; using BMA.EHR.Application.Responses.Profiles; using BMA.EHR.Domain.Common; using BMA.EHR.Domain.Models.Leave.TimeAttendants; +using BMA.EHR.Domain.Models.Notifications; using BMA.EHR.Domain.Shared; using BMA.EHR.Infrastructure.Persistence; using BMA.EHR.Leave.Service.DTOs.AdditionalCheck; @@ -46,6 +47,7 @@ namespace BMA.EHR.Leave.Service.Controllers private readonly DutyTimeRepository _dutyTimeRepository; private readonly LeaveDbContext _context; + private readonly ApplicationDBContext _appDbContext; private readonly IHttpContextAccessor _httpContextAccessor; private readonly IWebHostEnvironment _hostingEnvironment; private readonly IConfiguration _configuration; @@ -94,10 +96,12 @@ namespace BMA.EHR.Leave.Service.Controllers PermissionRepository permission, NotificationRepository notificationRepository, CheckInJobStatusRepository checkInJobStatusRepository, - HttpClient httpClient) + HttpClient httpClient, + ApplicationDBContext appDbContext) { _dutyTimeRepository = dutyTimeRepository; _context = context; + _appDbContext = appDbContext; _httpContextAccessor = httpContextAccessor; _hostingEnvironment = hostingEnvironment; _configuration = configuration; @@ -928,16 +932,31 @@ namespace BMA.EHR.Leave.Service.Controllers if (profile == null) { await _checkInJobStatusRepository.UpdateToFailedAsync(taskId, "ไม่พบข้อมูลผู้ใช้"); + //var staffList = await _userProfileRepository.GetOCStaffAsync(profile) return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound); } + var currentDate = data.CurrentDate ?? DateTime.Now; + if (data.CheckInFileName == "no-file") { //throw new Exception(GlobalMessages.NoFileToUpload); await _checkInJobStatusRepository.UpdateToFailedAsync(taskId, GlobalMessages.NoFileToUpload); + + // send notification to user + var noti1 = new Notification + { + Body = $"ประมวลผลการลงเวลาวันที่ {currentDate.ToString("dd-MM-yyyy")} ไม่สำเร็จ \r\nเนื่องจาก {GlobalMessages.NoFileToUpload}", + ReceiverUserId = profile.Id, + Type = "", + Payload = "", + }; + _appDbContext.Set().Add(noti1); + await _appDbContext.SaveChangesAsync(); + return Error(GlobalMessages.NoFileToUpload, StatusCodes.Status400BadRequest); } - var currentDate = data.CurrentDate ?? DateTime.Now; + var check_status = data.CheckInId == null ? "check-in-picture" : "check-out-picture"; @@ -951,6 +970,19 @@ namespace BMA.EHR.Leave.Service.Controllers catch (Exception ex) { await _checkInJobStatusRepository.UpdateToFailedAsync(taskId, $"ไม่สามารถอัปโหลดรูปภาพได้: {ex.Message}"); + + // send notification to user + var noti1 = new Notification + { + Body = $"ประมวลผลการลงเวลาวันที่ {currentDate.ToString("dd-MM-yyyy")} ไม่สำเร็จ \r\nเนื่องจากไม่สามารถอัปโหลดรูปภาพได้ {ex.Message}", + ReceiverUserId = profile.Id, + Type = "", + Payload = "", + }; + _appDbContext.Set().Add(noti1); + await _appDbContext.SaveChangesAsync(); + + return Error($"ไม่สามารถอัปโหลดรูปภาพได้: {ex.Message}", StatusCodes.Status500InternalServerError); } @@ -960,6 +992,16 @@ namespace BMA.EHR.Leave.Service.Controllers if (defaultRound == null) { await _checkInJobStatusRepository.UpdateToFailedAsync(taskId, "ไม่พบรอบการลงเวลาทำงาน Default"); + // send notification to user + var noti1 = new Notification + { + Body = $"ประมวลผลการลงเวลาวันที่ {currentDate.ToString("dd-MM-yyyy")} ไม่สำเร็จ \r\nเนื่องจากไม่พบรอบการลงเวลาทำงาน Default", + ReceiverUserId = profile.Id, + Type = "", + Payload = "", + }; + _appDbContext.Set().Add(noti1); + await _appDbContext.SaveChangesAsync(); return Error("ไม่พบรอบการลงเวลาทำงาน Default", StatusCodes.Status404NotFound); } @@ -979,6 +1021,18 @@ namespace BMA.EHR.Leave.Service.Controllers if (currentCheckIn != null) { await _checkInJobStatusRepository.UpdateToFailedAsync(taskId, "ไม่สามารถลงเวลาได้ เนื่องจากมีการลงเวลาในวันนี้แล้ว"); + + // send notification to user + var noti1 = new Notification + { + Body = $"ประมวลผลการลงเวลาวันที่ {currentDate.ToString("dd-MM-yyyy")} ไม่สำเร็จ \r\nเนื่องจากมีการลงเวลาในวันนี้แล้ว", + ReceiverUserId = profile.Id, + Type = "", + Payload = "", + }; + _appDbContext.Set().Add(noti1); + await _appDbContext.SaveChangesAsync(); + return Error(new Exception("ไม่สามารถลงเวลาได้ เนื่องจากมีการลงเวลาในวันนี้แล้ว!"), StatusCodes.Status400BadRequest); } @@ -1117,6 +1171,18 @@ namespace BMA.EHR.Leave.Service.Controllers if (checkout == null) { await _checkInJobStatusRepository.UpdateToFailedAsync(taskId, "ไม่พบข้อมูลการลงเวลาทำงาน"); + + // send notification to user + var noti1 = new Notification + { + Body = $"ประมวลผลการลงเวลาวันที่ {currentDate.ToString("dd-MM-yyyy")} ไม่สำเร็จ \r\nเนื่องจากไม่พบข้อมูลการลงเวลาทำงาน", + ReceiverUserId = profile.Id, + Type = "", + Payload = "", + }; + _appDbContext.Set().Add(noti1); + await _appDbContext.SaveChangesAsync(); + return Error(new Exception(GlobalMessages.DataNotFound), StatusCodes.Status404NotFound); } @@ -1125,6 +1191,18 @@ namespace BMA.EHR.Leave.Service.Controllers if (currentCheckInProcess == null) { await _checkInJobStatusRepository.UpdateToFailedAsync(taskId, "ไม่พบข้อมูลการประมวลผลเวลาทำงาน (CheckIn)"); + + // send notification to user + var noti1 = new Notification + { + Body = $"ประมวลผลการลงเวลาวันที่ {currentDate.ToString("dd-MM-yyyy")} ไม่สำเร็จ \r\nเนื่องจากไม่พบข้อมูลการประมวลผลเวลาทำงาน (CheckIn)", + ReceiverUserId = profile.Id, + Type = "", + Payload = "", + }; + _appDbContext.Set().Add(noti1); + await _appDbContext.SaveChangesAsync(); + return Error(new Exception(GlobalMessages.DataNotFound), StatusCodes.Status404NotFound); } @@ -1275,11 +1353,19 @@ namespace BMA.EHR.Leave.Service.Controllers else { await _checkInJobStatusRepository.UpdateToFailedAsync(taskId, "ไม่พบข้อมูลการประมวลผลเวลาทำงาน"); + // send notification to user + var noti1 = new Notification + { + Body = $"ประมวลผลการลงเวลาวันที่ {currentDate.ToString("dd-MM-yyyy")} ไม่สำเร็จ \r\nเนื่องจากไม่พบข้อมูลการประมวลผลเวลาทำงาน", + ReceiverUserId = profile.Id, + Type = "", + Payload = "", + }; + _appDbContext.Set().Add(noti1); + await _appDbContext.SaveChangesAsync(); return Error(new Exception(GlobalMessages.DataNotFound), StatusCodes.Status404NotFound); } - } - // อัปเดตสถานะเป็น COMPLETED if (taskId != Guid.Empty) { From c693364fe128bbb4f2c81e3176411fa79aff2464 Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Thu, 5 Feb 2026 12:03:48 +0700 Subject: [PATCH 094/178] Refactor LeaveReportController to use LeaveCount instead of CountLeaveDay for leave types --- .../Controllers/LeaveReportController.cs | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/BMA.EHR.Leave/Controllers/LeaveReportController.cs b/BMA.EHR.Leave/Controllers/LeaveReportController.cs index 2a113d94..4e5a0961 100644 --- a/BMA.EHR.Leave/Controllers/LeaveReportController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveReportController.cs @@ -1389,57 +1389,57 @@ namespace BMA.EHR.Leave.Service.Controllers var sickDaySum = beginningData.FirstOrDefault(x => x.ProfileId == p.Id && x.LeaveType.Code == "LV-001"); var sickDay = leaveDays.FirstOrDefault(x => x.KeycloakUserId == keycloakUserId && x.LeaveTypeCode == "LV-001"); var sickDayCount = sickDaySum != null ? sickDaySum.LeaveDaysUsed : 0; - var sickCount = sickDay != null ? sickDay.CountLeaveDay : 0; + var sickCount = sickDaySum != null ? sickDaySum.LeaveCount : 0; var personalDaySum = beginningData.FirstOrDefault(x => x.ProfileId == p.Id && x.LeaveType.Code == "LV-002"); var personalDay = leaveDays.FirstOrDefault(x => x.KeycloakUserId == keycloakUserId && x.LeaveTypeCode == "LV-002"); var personalDayCount = personalDaySum != null ? personalDaySum.LeaveDaysUsed : 0; - var personalCount = personalDay != null ? personalDay.CountLeaveDay : 0; + var personalCount = personalDaySum != null ? personalDaySum.LeaveCount : 0; var maternityDaySum = beginningData.FirstOrDefault(x => x.ProfileId == p.Id && x.LeaveType.Code == "LV-003"); var maternityDay = leaveDays.FirstOrDefault(x => x.KeycloakUserId == keycloakUserId && x.LeaveTypeCode == "LV-003"); var maternityDayCount = maternityDaySum != null ? maternityDaySum.LeaveDaysUsed : 0; - var maternityCount = maternityDay != null ? maternityDay.CountLeaveDay : 0; + var maternityCount = maternityDaySum != null ? maternityDaySum.LeaveCount : 0; var wifeDaySum = beginningData.FirstOrDefault(x => x.ProfileId == p.Id && x.LeaveType.Code == "LV-004"); var wifeDay = leaveDays.FirstOrDefault(x => x.KeycloakUserId == keycloakUserId && x.LeaveTypeCode == "LV-004"); var wifeDayCount = wifeDaySum != null ? wifeDaySum.LeaveDaysUsed : 0; - var wifeCount = wifeDay != null ? wifeDay.CountLeaveDay : 0; + var wifeCount = wifeDaySum != null ? wifeDaySum.LeaveCount : 0; var restDaySum = beginningData.FirstOrDefault(x => x.ProfileId == p.Id && x.LeaveType.Code == "LV-005"); var restDay = leaveDays.FirstOrDefault(x => x.KeycloakUserId == keycloakUserId && x.LeaveTypeCode == "LV-005"); var restDayCount = restDaySum != null ? restDaySum.LeaveDaysUsed : 0; - var restCount = restDay != null ? restDay.CountLeaveDay : 0; + var restCount = restDaySum != null ? restDaySum.LeaveCount : 0; var ordainDaySum = beginningData.FirstOrDefault(x => x.ProfileId == p.Id && x.LeaveType.Code == "LV-006"); var ordainDay = leaveDays.FirstOrDefault(x => x.KeycloakUserId == keycloakUserId && x.LeaveTypeCode == "LV-006"); var ordainDayCount = ordainDaySum != null ? ordainDaySum.LeaveDaysUsed : 0; - var ordainCount = ordainDay != null ? ordainDay.CountLeaveDay : 0; + var ordainCount = ordainDaySum != null ? ordainDaySum.LeaveCount : 0; var absentDaySum = beginningData.FirstOrDefault(x => x.ProfileId == p.Id && x.LeaveType.Code == "LV-007"); var absentDay = leaveDays.FirstOrDefault(x => x.KeycloakUserId == keycloakUserId && x.LeaveTypeCode == "LV-007"); var absentDayCount = absentDaySum != null ? absentDaySum.LeaveDaysUsed : 0; - var absentCount = absentDay != null ? absentDay.CountLeaveDay : 0; + var absentCount = absentDaySum != null ? absentDaySum.LeaveCount : 0; var studyDaySum = beginningData.FirstOrDefault(x => x.ProfileId == p.Id && x.LeaveType.Code == "LV-008"); var studyDay = leaveDays.FirstOrDefault(x => x.KeycloakUserId == keycloakUserId && x.LeaveTypeCode == "LV-008"); var studyDayCount = studyDaySum != null ? studyDaySum.LeaveDaysUsed : 0; - var studyCount = studyDay != null ? studyDay.CountLeaveDay : 0; + var studyCount = studyDaySum != null ? studyDaySum.LeaveCount : 0; var agencyDaySum = beginningData.FirstOrDefault(x => x.ProfileId == p.Id && x.LeaveType.Code == "LV-009"); var agencyDay = leaveDays.FirstOrDefault(x => x.KeycloakUserId == keycloakUserId && x.LeaveTypeCode == "LV-009"); var agencyDayCount = agencyDaySum != null ? agencyDaySum.LeaveDaysUsed : 0; - var agencyCount = agencyDay != null ? agencyDay.CountLeaveDay : 0; + var agencyCount = agencyDaySum != null ? agencyDaySum.LeaveCount : 0; var coupleDaySum = beginningData.FirstOrDefault(x => x.ProfileId == p.Id && x.LeaveType.Code == "LV-010"); var coupleDay = leaveDays.FirstOrDefault(x => x.KeycloakUserId == keycloakUserId && x.LeaveTypeCode == "LV-010"); var coupleDayCount = coupleDaySum != null ? coupleDaySum.LeaveDaysUsed : 0; - var coupleCount = coupleDay != null ? coupleDay.CountLeaveDay : 0; + var coupleCount = coupleDaySum != null ? coupleDaySum.LeaveCount : 0; var therapyDaySum = beginningData.FirstOrDefault(x => x.ProfileId == p.Id && x.LeaveType.Code == "LV-011"); var therapyDay = leaveDays.FirstOrDefault(x => x.KeycloakUserId == keycloakUserId && x.LeaveTypeCode == "LV-011"); var therapyDayCount = therapyDaySum != null ? therapyDaySum.LeaveDaysUsed : 0; - var therapyCount = therapyDay != null ? therapyDay.CountLeaveDay : 0; + var therapyCount = therapyDaySum != null ? therapyDaySum.LeaveCount : 0; var timeStamps = await _processUserTimeStampRepository.GetTimeStampHistoryByRangeForUserAsync(p.Keycloak ?? Guid.Empty, req.StartDate, req.EndDate); From 1d8ef79373191a6b656e0df46aaac710aae899c4 Mon Sep 17 00:00:00 2001 From: harid Date: Fri, 6 Feb 2026 10:18:47 +0700 Subject: [PATCH 095/178] =?UTF-8?q?api=20=E0=B8=AD=E0=B8=B1=E0=B8=9E?= =?UTF-8?q?=E0=B9=80=E0=B8=94=E0=B8=97=E0=B8=AA=E0=B8=96=E0=B8=B2=E0=B8=99?= =?UTF-8?q?=E0=B8=B0=E0=B9=80=E0=B8=9B=E0=B9=87=E0=B8=99=E0=B8=9A=E0=B8=A3?= =?UTF-8?q?=E0=B8=A3=E0=B8=88=E0=B8=B8=20=E0=B9=80=E0=B8=9B=E0=B8=A5?= =?UTF-8?q?=E0=B8=B5=E0=B9=88=E0=B8=A2=E0=B8=99=E0=B8=AA=E0=B8=B4=E0=B8=97?= =?UTF-8?q?=E0=B8=98=E0=B8=B4=E0=B9=8C=E0=B8=88=E0=B8=B2=E0=B8=81=20super?= =?UTF-8?q?=5Fadmin=20=E0=B9=80=E0=B8=9B=E0=B9=87=E0=B8=99=20owner?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Controllers/PlacementController.cs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/BMA.EHR.Placement.Service/Controllers/PlacementController.cs b/BMA.EHR.Placement.Service/Controllers/PlacementController.cs index 1a9260ef..51a09094 100644 --- a/BMA.EHR.Placement.Service/Controllers/PlacementController.cs +++ b/BMA.EHR.Placement.Service/Controllers/PlacementController.cs @@ -64,7 +64,7 @@ namespace BMA.EHR.Placement.Service.Controllers private string? UserId => _httpContextAccessor?.HttpContext?.User?.FindFirst(ClaimTypes.NameIdentifier)?.Value; private string? FullName => _httpContextAccessor?.HttpContext?.User?.FindFirst("name")?.Value; private string? token => _httpContextAccessor.HttpContext.Request.Headers["Authorization"]; - private bool isSuperAdmin => _httpContextAccessor?.HttpContext?.User?.IsInRole("SUPER_ADMIN") ?? false; + //private bool isSuperAdmin => _httpContextAccessor?.HttpContext?.User?.IsInRole("SUPER_ADMIN") ?? false; #endregion @@ -867,8 +867,17 @@ namespace BMA.EHR.Placement.Service.Controllers [ProducesResponseType(StatusCodes.Status500InternalServerError)] public async Task> PersonUpdateStatus([FromBody] PersonUpdateStatusRequest req) { - if (isSuperAdmin == false) - return Success(); + var getPermission = await _permission.GetPermissionAPIAsync("UPDATE", "SYS_PLACEMENT_PASS"); + var jsonData = JsonConvert.DeserializeObject(getPermission); + if (jsonData["status"]?.ToString() != "200") + { + return Error(jsonData["message"]?.ToString(), StatusCodes.Status403Forbidden); + } + string role = jsonData["result"]?.ToString(); + if (role != "OWNER") + { + return Error(jsonData["message"]?.ToString(), StatusCodes.Status403Forbidden); + } var person = await _context.PlacementProfiles .FirstOrDefaultAsync(x => x.Id == req.PersonalId); From 35310f78548a0c60045f3bb4afd3324d2ff55e9c Mon Sep 17 00:00:00 2001 From: harid Date: Tue, 10 Feb 2026 12:07:32 +0700 Subject: [PATCH 096/178] =?UTF-8?q?Fix=20=E0=B8=9B=E0=B8=A3=E0=B8=B0?= =?UTF-8?q?=E0=B8=81=E0=B8=B2=E0=B8=A8=E0=B9=80=E0=B8=81=E0=B8=A9=E0=B8=B5?= =?UTF-8?q?=E0=B8=A2=E0=B8=93=E0=B8=A5=E0=B8=B9=E0=B8=81=E0=B8=88=E0=B9=89?= =?UTF-8?q?=E0=B8=B2=E0=B8=87=20=E0=B8=9A=E0=B8=B1=E0=B8=99=E0=B8=97?= =?UTF-8?q?=E0=B8=B6=E0=B8=81=E0=B8=82=E0=B9=89=E0=B8=AD=E0=B8=A1=E0=B8=B9?= =?UTF-8?q?=E0=B8=A5=E0=B8=A7=E0=B8=B1=E0=B8=99=E0=B8=97=E0=B8=B5=E0=B9=88?= =?UTF-8?q?=E0=B8=9B=E0=B8=A3=E0=B8=B0=E0=B8=81=E0=B8=B2=E0=B8=A8=20?= =?UTF-8?q?=E0=B8=A3=E0=B8=B0=E0=B8=9A=E0=B8=9A=E0=B9=81=E0=B8=88=E0=B9=89?= =?UTF-8?q?=E0=B8=87=20Error=20#2260?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Controllers/RetirementController.cs | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/BMA.EHR.Retirement.Service/Controllers/RetirementController.cs b/BMA.EHR.Retirement.Service/Controllers/RetirementController.cs index 74f217a2..ad3105ba 100644 --- a/BMA.EHR.Retirement.Service/Controllers/RetirementController.cs +++ b/BMA.EHR.Retirement.Service/Controllers/RetirementController.cs @@ -1306,12 +1306,20 @@ namespace BMA.EHR.Retirement.Service.Controllers { return Error(jsonData["message"]?.ToString(), StatusCodes.Status403Forbidden); } + + // แยกดึงข้อมูลเพื่อลดภาระ Database var retire = await _context.RetirementPeriods - .Include(x => x.RetirementProfiles) - .Include(x => x.RetirementRawProfiles) + //.Include(x => x.RetirementProfiles) + //.Include(x => x.RetirementRawProfiles) .FirstOrDefaultAsync(x => x.Id == retireId); + if (retire == null) return Error(GlobalMessages.RetirementNotFound, 404); + + // โหลดข้อมูลลูกแยกกัน + var profiles = await _context.RetirementProfiles.Where(x => x.RetirementPeriod.Id == retireId).ToListAsync(); + var rawProfiles = await _context.RetirementRawProfiles.Where(x => x.RetirementPeriod.Id == retireId).ToListAsync(); + if (Request.Form.Files != null && Request.Form.Files.Count != 0) { var file = Request.Form.Files[0]; @@ -1366,7 +1374,7 @@ namespace BMA.EHR.Retirement.Service.Controllers } var order = 1; - foreach (var profile in retire.RetirementProfiles + foreach (var profile in profiles .OrderBy(x => string.IsNullOrEmpty(x.root) ? int.MaxValue : rootOrder.ToObject>().IndexOf(x.root)) .ThenBy(x => child1Order.ToObject>().IndexOf(x.child1 ?? "")) .ThenBy(x => child2Order.ToObject>().IndexOf(x.child2 ?? "")) @@ -1381,7 +1389,7 @@ namespace BMA.EHR.Retirement.Service.Controllers } order = 1; - foreach (var profile in retire.RetirementRawProfiles + foreach (var profile in rawProfiles .OrderBy(x => string.IsNullOrEmpty(x.root) ? int.MaxValue : rootOrder.ToObject>().IndexOf(x.root)) .ThenBy(x => child1Order.ToObject>().IndexOf(x.child1 ?? "")) .ThenBy(x => child2Order.ToObject>().IndexOf(x.child2 ?? "")) From 05ec0cccced9255571d3074273edf312e107801c Mon Sep 17 00:00:00 2001 From: harid Date: Tue, 10 Feb 2026 15:29:35 +0700 Subject: [PATCH 097/178] =?UTF-8?q?Fix=20Bug=20=E0=B8=88=E0=B8=B3=E0=B8=99?= =?UTF-8?q?=E0=B8=A7=E0=B8=99=E0=B8=84=E0=B8=99=E0=B8=A5=E0=B8=B2=E0=B9=84?= =?UTF-8?q?=E0=B8=A1=E0=B9=88=E0=B9=81=E0=B8=AA=E0=B8=94=E0=B8=87=E0=B9=83?= =?UTF-8?q?=E0=B8=99=E0=B8=A3=E0=B8=B2=E0=B8=A2=E0=B8=87=E0=B8=B2=E0=B8=99?= =?UTF-8?q?=20#2299?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Repositories/Leaves/LeaveRequests/LeaveRequestRepository.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BMA.EHR.Application/Repositories/Leaves/LeaveRequests/LeaveRequestRepository.cs b/BMA.EHR.Application/Repositories/Leaves/LeaveRequests/LeaveRequestRepository.cs index 972a228d..57a7fdc4 100644 --- a/BMA.EHR.Application/Repositories/Leaves/LeaveRequests/LeaveRequestRepository.cs +++ b/BMA.EHR.Application/Repositories/Leaves/LeaveRequests/LeaveRequestRepository.cs @@ -1694,7 +1694,7 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests if (role == "ROOT" || role == "OWNER" || role == "CHILD" || role == "BROTHER" || role == "PARENT") { data = data - .Where(x => nodeByReq == 4 ? x.Child4Id == Guid.Parse(nodeIdByReq) : nodeByReq == 3 ? x.Child3Id == Guid.Parse(nodeIdByReq) : nodeByReq == 2 ? x.Child2Id == Guid.Parse(nodeIdByReq) : nodeByReq == 1 ? x.Child1Id == Guid.Parse(nodeIdByReq) : nodeByReq == 0 ? x.RootId == Guid.Parse(nodeIdByReq) : true) + .Where(x => nodeByReq == 4 ? x.Child4DnaId == Guid.Parse(nodeIdByReq) : nodeByReq == 3 ? x.Child3DnaId == Guid.Parse(nodeIdByReq) : nodeByReq == 2 ? x.Child2DnaId == Guid.Parse(nodeIdByReq) : nodeByReq == 1 ? x.Child1DnaId == Guid.Parse(nodeIdByReq) : nodeByReq == 0 ? x.RootDnaId == Guid.Parse(nodeIdByReq) : true) .ToList(); } // รายงานการลางานจำแนกตามเพศฯ Template ให้หน่วยงานแสดงก่อนส่วนราชการ From 682c88c2dbc196622ca80dcdd47cea12449f8252 Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Wed, 11 Feb 2026 09:44:18 +0700 Subject: [PATCH 098/178] Add BeginningLeaveCount and BeginningLeaveDays to LeaveBeginnings table - Altered LeaveDays column to update its comment. - Added BeginningLeaveCount column to track the number of leave occurrences. - Added BeginningLeaveDays column to store the total days of leave carried over. --- .../Models/Leave/Requests/LeaveBeginning.cs | 8 +- ...d LeaveCount to LeaveBeginning.Designer.cs | 1717 +++++++++++++++++ ...gLeave and LeaveCount to LeaveBeginning.cs | 62 + .../LeaveDb/LeaveDbContextModelSnapshot.cs | 10 +- 4 files changed, 1795 insertions(+), 2 deletions(-) create mode 100644 BMA.EHR.Infrastructure/Migrations/LeaveDb/20260210091134_Add BeginningLeave and LeaveCount to LeaveBeginning.Designer.cs create mode 100644 BMA.EHR.Infrastructure/Migrations/LeaveDb/20260210091134_Add BeginningLeave and LeaveCount to LeaveBeginning.cs diff --git a/BMA.EHR.Domain/Models/Leave/Requests/LeaveBeginning.cs b/BMA.EHR.Domain/Models/Leave/Requests/LeaveBeginning.cs index d68f0229..153b7d22 100644 --- a/BMA.EHR.Domain/Models/Leave/Requests/LeaveBeginning.cs +++ b/BMA.EHR.Domain/Models/Leave/Requests/LeaveBeginning.cs @@ -24,7 +24,7 @@ namespace BMA.EHR.Domain.Models.Leave.Requests [Required, Comment("ปีงบประมาณ")] public int LeaveYear { get; set; } = 0; - [Required, Comment("จำนวนวันลายกมา")] + [Required, Comment("จำนวนวันลาทั้งหมด")] public double LeaveDays { get; set; } = 0.0; [Required, Comment("จำนวนวันลาที่ใช้ไป")] @@ -42,5 +42,11 @@ namespace BMA.EHR.Domain.Models.Leave.Requests public Guid? Child3DnaId { get; set; } public Guid? Child4DnaId { get; set; } + + [Required, Comment("จำนวนวันลายกมา")] + public double BeginningLeaveDays { get; set; } = 0.0; + + [Comment("จำนวนครั้งที่ลายกมา")] + public int BeginningLeaveCount { get; set; } = 0; } } diff --git a/BMA.EHR.Infrastructure/Migrations/LeaveDb/20260210091134_Add BeginningLeave and LeaveCount to LeaveBeginning.Designer.cs b/BMA.EHR.Infrastructure/Migrations/LeaveDb/20260210091134_Add BeginningLeave and LeaveCount to LeaveBeginning.Designer.cs new file mode 100644 index 00000000..934ba1a9 --- /dev/null +++ b/BMA.EHR.Infrastructure/Migrations/LeaveDb/20260210091134_Add BeginningLeave and LeaveCount to LeaveBeginning.Designer.cs @@ -0,0 +1,1717 @@ +// +using System; +using BMA.EHR.Infrastructure.Persistence; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace BMA.EHR.Infrastructure.Migrations.LeaveDb +{ + [DbContext(typeof(LeaveDbContext))] + [Migration("20260210091134_Add BeginningLeave and LeaveCount to LeaveBeginning")] + partial class AddBeginningLeaveandLeaveCounttoLeaveBeginning + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "7.0.9") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + modelBuilder.Entity("BMA.EHR.Domain.Models.Documents.Document", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedDate") + .HasColumnType("datetime(6)"); + + b.Property("Detail") + .IsRequired() + .HasColumnType("text"); + + b.Property("FileName") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("FileSize") + .HasColumnType("int"); + + b.Property("FileType") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("varchar(128)"); + + b.Property("ObjectRefId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.ToTable("Document"); + }); + + modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.Commons.LeaveType", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)") + .HasColumnOrder(0) + .HasComment("PrimaryKey") + .HasAnnotation("Relational:JsonPropertyName", "id"); + + b.Property("Code") + .IsRequired() + .HasColumnType("longtext") + .HasComment("รหัสประเภทการลา"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(100) + .HasComment("สร้างข้อมูลเมื่อ"); + + b.Property("CreatedFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(104) + .HasComment("ชื่อ User ที่สร้างข้อมูล"); + + b.Property("CreatedUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(101) + .HasComment("User Id ที่สร้างข้อมูล"); + + b.Property("LastUpdateFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(105) + .HasComment("ชื่อ User ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdateUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(103) + .HasComment("User Id ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(102) + .HasComment("แก้ไขข้อมูลล่าสุดเมื่อ"); + + b.Property("Limit") + .HasColumnType("int") + .HasComment("จำนวนวันลาสูงสุดประจำปี"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext") + .HasComment("ชื่อประเภทการลา"); + + b.HasKey("Id"); + + b.ToTable("LeaveTypes"); + }); + + modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.Requests.LeaveBeginning", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)") + .HasColumnOrder(0) + .HasComment("PrimaryKey") + .HasAnnotation("Relational:JsonPropertyName", "id"); + + b.Property("BeginningLeaveCount") + .HasColumnType("int") + .HasComment("จำนวนครั้งที่ลายกมา"); + + b.Property("BeginningLeaveDays") + .HasColumnType("double") + .HasComment("จำนวนวันลายกมา"); + + b.Property("Child1DnaId") + .HasColumnType("char(36)"); + + b.Property("Child2DnaId") + .HasColumnType("char(36)"); + + b.Property("Child3DnaId") + .HasColumnType("char(36)"); + + b.Property("Child4DnaId") + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(100) + .HasComment("สร้างข้อมูลเมื่อ"); + + b.Property("CreatedFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(104) + .HasComment("ชื่อ User ที่สร้างข้อมูล"); + + b.Property("CreatedUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(101) + .HasComment("User Id ที่สร้างข้อมูล"); + + b.Property("FirstName") + .HasColumnType("longtext"); + + b.Property("LastName") + .HasColumnType("longtext"); + + b.Property("LastUpdateFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(105) + .HasComment("ชื่อ User ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdateUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(103) + .HasComment("User Id ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(102) + .HasComment("แก้ไขข้อมูลล่าสุดเมื่อ"); + + b.Property("LeaveCount") + .HasColumnType("int") + .HasComment("จำนวนครั้งที่ลาสะสม"); + + b.Property("LeaveDays") + .HasColumnType("double") + .HasComment("จำนวนวันลาทั้งหมด"); + + b.Property("LeaveDaysUsed") + .HasColumnType("double") + .HasComment("จำนวนวันลาที่ใช้ไป"); + + b.Property("LeaveTypeId") + .HasColumnType("char(36)") + .HasComment("รหัสประเภทการลา"); + + b.Property("LeaveYear") + .HasColumnType("int") + .HasComment("ปีงบประมาณ"); + + b.Property("Prefix") + .HasColumnType("longtext"); + + b.Property("ProfileId") + .HasColumnType("char(36)") + .HasComment("รหัส Profile ในระบบทะเบียนประวัติ"); + + b.Property("RootDnaId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("LeaveTypeId"); + + b.ToTable("LeaveBeginnings"); + }); + + modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.Requests.LeaveDocument", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)") + .HasColumnOrder(0) + .HasComment("PrimaryKey") + .HasAnnotation("Relational:JsonPropertyName", "id"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(100) + .HasComment("สร้างข้อมูลเมื่อ"); + + b.Property("CreatedFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(104) + .HasComment("ชื่อ User ที่สร้างข้อมูล"); + + b.Property("CreatedUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(101) + .HasComment("User Id ที่สร้างข้อมูล"); + + b.Property("DocumentId") + .HasColumnType("char(36)"); + + b.Property("LastUpdateFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(105) + .HasComment("ชื่อ User ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdateUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(103) + .HasComment("User Id ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(102) + .HasComment("แก้ไขข้อมูลล่าสุดเมื่อ"); + + b.Property("LeaveRequestId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("DocumentId"); + + b.HasIndex("LeaveRequestId"); + + b.ToTable("LeaveDocuments"); + }); + + modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.Requests.LeaveRequest", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)") + .HasColumnOrder(0) + .HasComment("PrimaryKey") + .HasAnnotation("Relational:JsonPropertyName", "id"); + + b.Property("AbsentDayAt") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("AbsentDayGetIn") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("AbsentDayLocation") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("AbsentDayRegistorDate") + .HasColumnType("datetime(6)"); + + b.Property("AbsentDaySummon") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Amount") + .HasColumnType("double"); + + b.Property("ApproveStep") + .HasColumnType("longtext") + .HasComment("step การอนุมัติ st1 = จทน.อนุมัตื,st2 = ผู้บังคับบัญชา อนุมัติ "); + + b.Property("BirthDate") + .HasColumnType("datetime(6)"); + + b.Property("CancelLeaveWrote") + .HasColumnType("longtext") + .HasComment("เขียนที่ (ขอยกเลิก)"); + + b.Property("Child1") + .HasColumnType("longtext"); + + b.Property("Child1DnaId") + .HasColumnType("char(36)"); + + b.Property("Child1Id") + .HasColumnType("char(36)"); + + b.Property("Child2") + .HasColumnType("longtext"); + + b.Property("Child2DnaId") + .HasColumnType("char(36)"); + + b.Property("Child2Id") + .HasColumnType("char(36)"); + + b.Property("Child3") + .HasColumnType("longtext"); + + b.Property("Child3DnaId") + .HasColumnType("char(36)"); + + b.Property("Child3Id") + .HasColumnType("char(36)"); + + b.Property("Child4") + .HasColumnType("longtext"); + + b.Property("Child4DnaId") + .HasColumnType("char(36)"); + + b.Property("Child4Id") + .HasColumnType("char(36)"); + + b.Property("CitizenId") + .HasColumnType("longtext"); + + b.Property("CommanderPosition") + .HasColumnType("longtext"); + + b.Property("CoupleDayCountryHistory") + .HasColumnType("longtext"); + + b.Property("CoupleDayEndDateHistory") + .HasColumnType("datetime(6)"); + + b.Property("CoupleDayLevel") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("CoupleDayLevelCountry") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("CoupleDayName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("CoupleDayPosition") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("CoupleDayStartDateHistory") + .HasColumnType("datetime(6)"); + + b.Property("CoupleDaySumTotalHistory") + .HasColumnType("longtext"); + + b.Property("CoupleDayTotalHistory") + .HasColumnType("longtext"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(100) + .HasComment("สร้างข้อมูลเมื่อ"); + + b.Property("CreatedFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(104) + .HasComment("ชื่อ User ที่สร้างข้อมูล"); + + b.Property("CreatedUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(101) + .HasComment("User Id ที่สร้างข้อมูล"); + + b.Property("DateAppoint") + .HasColumnType("datetime(6)"); + + b.Property("Dear") + .HasColumnType("longtext") + .HasComment("เรียนใคร"); + + b.Property("FirstName") + .HasColumnType("longtext"); + + b.Property("Gender") + .HasColumnType("longtext"); + + b.Property("HajjDayStatus") + .HasColumnType("tinyint(1)"); + + b.Property("KeycloakUserId") + .HasColumnType("char(36)"); + + b.Property("LastName") + .HasColumnType("longtext"); + + b.Property("LastUpdateFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(105) + .HasComment("ชื่อ User ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdateUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(103) + .HasComment("User Id ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(102) + .HasComment("แก้ไขข้อมูลล่าสุดเมื่อ"); + + b.Property("LeaveAddress") + .IsRequired() + .HasColumnType("longtext") + .HasComment("สถานที่ติดต่อขณะลา"); + + b.Property("LeaveBirthDate") + .HasColumnType("datetime(6)"); + + b.Property("LeaveCancelComment") + .HasColumnType("longtext") + .HasComment("เหตุผลในการขอยกเลิก"); + + b.Property("LeaveCancelDocumentId") + .HasColumnType("char(36)"); + + b.Property("LeaveCancelStatus") + .HasColumnType("longtext") + .HasComment("สถานะของคำขอยกเลิก"); + + b.Property("LeaveComment") + .HasColumnType("longtext") + .HasComment("ความเห็นของผู้บังคับบัญชา"); + + b.Property("LeaveDetail") + .IsRequired() + .HasColumnType("longtext") + .HasComment("รายละเอียดการลา"); + + b.Property("LeaveDirectorComment") + .HasColumnType("longtext") + .HasComment("ความเห็นของผู้อำนวยการสำนัก"); + + b.Property("LeaveDraftDocumentId") + .HasColumnType("char(36)"); + + b.Property("LeaveEndDate") + .HasColumnType("datetime(6)") + .HasComment("วัน เดือน ปีสิ้นสุดลา"); + + b.Property("LeaveGovernmentDate") + .HasColumnType("datetime(6)"); + + b.Property("LeaveLast") + .HasColumnType("datetime(6)"); + + b.Property("LeaveNumber") + .IsRequired() + .HasColumnType("longtext") + .HasComment("หมายเลขที่ติดต่อขณะลา"); + + b.Property("LeaveRange") + .HasColumnType("longtext") + .HasComment("ช่วงของการลาของวันเริ่ม เช่น ลาทั้งวัน ครึ่งวันเช้า ครึ่งวันบ่าย"); + + b.Property("LeaveRangeEnd") + .HasColumnType("longtext") + .HasComment("ช่วงของการลาของวันสิ้นสุด เช่น ลาทั้งวัน ครึ่งวันเช้า ครึ่งวันบ่าย"); + + b.Property("LeaveSalary") + .HasColumnType("int"); + + b.Property("LeaveSalaryText") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("LeaveStartDate") + .HasColumnType("datetime(6)") + .HasComment("วัน เดือน ปีเริ่มต้นลา"); + + b.Property("LeaveStatus") + .IsRequired() + .HasColumnType("longtext") + .HasComment("สถานะของคำร้อง"); + + b.Property("LeaveSubTypeName") + .HasColumnType("longtext"); + + b.Property("LeaveTotal") + .HasColumnType("double"); + + b.Property("LeaveTypeCode") + .HasColumnType("longtext") + .HasComment("code ของประเภทการลา"); + + b.Property("LeaveWrote") + .IsRequired() + .HasColumnType("longtext") + .HasComment("เขียนที่"); + + b.Property("OrdainDayBuddhistLentAddress") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("OrdainDayBuddhistLentName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("OrdainDayLocationAddress") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("OrdainDayLocationName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("OrdainDayLocationNumber") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("OrdainDayOrdination") + .HasColumnType("datetime(6)"); + + b.Property("OrdainDayStatus") + .HasColumnType("tinyint(1)"); + + b.Property("OrganizationName") + .HasColumnType("longtext") + .HasComment("สังกัดผู้ยื่นขอ"); + + b.Property("PositionLevelName") + .HasColumnType("longtext") + .HasComment("ระดับผู้ยื่นขอ"); + + b.Property("PositionName") + .HasColumnType("longtext") + .HasComment("ตำแหน่งผู้ยื่นขอ"); + + b.Property("Prefix") + .HasColumnType("longtext"); + + b.Property("ProfileId") + .HasColumnType("char(36)"); + + b.Property("ProfileType") + .HasColumnType("longtext"); + + b.Property("RestDayCurrentTotal") + .HasColumnType("double"); + + b.Property("RestDayOldTotal") + .HasColumnType("double"); + + b.Property("Root") + .HasColumnType("longtext"); + + b.Property("RootDnaId") + .HasColumnType("char(36)"); + + b.Property("RootId") + .HasColumnType("char(36)"); + + b.Property("StudyDayCountry") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("StudyDayDegreeLevel") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("StudyDayScholarship") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("StudyDaySubject") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("StudyDayTrainingName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("StudyDayTrainingSubject") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("StudyDayUniversityName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("TypeId") + .HasColumnType("char(36)"); + + b.Property("WifeDayDateBorn") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("WifeDayName") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("LeaveCancelDocumentId"); + + b.HasIndex("LeaveDraftDocumentId"); + + b.HasIndex("TypeId"); + + b.ToTable("LeaveRequests"); + }); + + modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.Requests.LeaveRequestApprover", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)") + .HasColumnOrder(0) + .HasComment("PrimaryKey") + .HasAnnotation("Relational:JsonPropertyName", "id"); + + b.Property("ApproveStatus") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("ApproveType") + .HasColumnType("longtext"); + + b.Property("Comment") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(100) + .HasComment("สร้างข้อมูลเมื่อ"); + + b.Property("CreatedFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(104) + .HasComment("ชื่อ User ที่สร้างข้อมูล"); + + b.Property("CreatedUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(101) + .HasComment("User Id ที่สร้างข้อมูล"); + + b.Property("FirstName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("KeycloakId") + .HasColumnType("char(36)"); + + b.Property("LastName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("LastUpdateFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(105) + .HasComment("ชื่อ User ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdateUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(103) + .HasComment("User Id ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(102) + .HasComment("แก้ไขข้อมูลล่าสุดเมื่อ"); + + b.Property("LeaveRequestId") + .HasColumnType("char(36)"); + + b.Property("OrganizationName") + .IsRequired() + .HasColumnType("longtext") + .HasComment("สังกัด"); + + b.Property("PosExecutiveName") + .IsRequired() + .HasColumnType("longtext") + .HasComment("ตำแหน่งทางการบริหาร"); + + b.Property("PositionLevelName") + .IsRequired() + .HasColumnType("longtext") + .HasComment("ประเภทระดับตำแหน่ง"); + + b.Property("PositionName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("PositionSign") + .HasColumnType("longtext") + .HasComment("ตำแหน่งใต้ลายเช็นต์"); + + b.Property("Prefix") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("ProfileId") + .HasColumnType("char(36)"); + + b.Property("Seq") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("LeaveRequestId"); + + b.ToTable("LeaveRequestApprovers"); + }); + + modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.TimeAttendants.AdditionalCheckRequest", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)") + .HasColumnOrder(0) + .HasComment("PrimaryKey") + .HasAnnotation("Relational:JsonPropertyName", "id"); + + b.Property("CheckDate") + .HasColumnType("datetime(6)") + .HasComment("*วันที่ลงเวลา"); + + b.Property("CheckInEdit") + .HasColumnType("tinyint(1)") + .HasComment("*ขอลงเวลาช่วงเช้า"); + + b.Property("CheckOutEdit") + .HasColumnType("tinyint(1)") + .HasComment("*ขอลงเวลาช่วงบ่าย"); + + b.Property("Child1DnaId") + .HasColumnType("char(36)"); + + b.Property("Child2DnaId") + .HasColumnType("char(36)"); + + b.Property("Child3DnaId") + .HasColumnType("char(36)"); + + b.Property("Child4DnaId") + .HasColumnType("char(36)"); + + b.Property("Comment") + .HasColumnType("longtext") + .HasComment("หมายเหตุในการการอนุมัติ/ไม่อนุมัติ"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(100) + .HasComment("สร้างข้อมูลเมื่อ"); + + b.Property("CreatedFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(104) + .HasComment("ชื่อ User ที่สร้างข้อมูล"); + + b.Property("CreatedUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(101) + .HasComment("User Id ที่สร้างข้อมูล"); + + b.Property("Description") + .IsRequired() + .HasColumnType("longtext") + .HasComment("*หมายเหตุขอลงเวลาพิเศษ"); + + b.Property("FirstName") + .HasColumnType("longtext"); + + b.Property("KeycloakUserId") + .HasColumnType("char(36)") + .HasComment("รหัส User ของ Keycloak ที่ร้องขอ"); + + b.Property("LastName") + .HasColumnType("longtext"); + + b.Property("LastUpdateFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(105) + .HasComment("ชื่อ User ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdateUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(103) + .HasComment("User Id ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(102) + .HasComment("แก้ไขข้อมูลล่าสุดเมื่อ"); + + b.Property("Latitude") + .HasColumnType("double"); + + b.Property("Longitude") + .HasColumnType("double"); + + b.Property("POI") + .HasColumnType("longtext"); + + b.Property("Prefix") + .HasColumnType("longtext"); + + b.Property("RootDnaId") + .HasColumnType("char(36)"); + + b.Property("Status") + .IsRequired() + .HasColumnType("longtext") + .HasComment("สถานะการอนุมัติ"); + + b.HasKey("Id"); + + b.ToTable("AdditionalCheckRequests"); + }); + + modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.TimeAttendants.CheckInJobStatus", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)") + .HasColumnOrder(0) + .HasComment("PrimaryKey") + .HasAnnotation("Relational:JsonPropertyName", "id"); + + b.Property("AdditionalData") + .HasColumnType("longtext") + .HasComment("ข้อมูลเพิ่มเติม (JSON)"); + + b.Property("CheckInId") + .HasColumnType("char(36)") + .HasComment("CheckInId สำหรับ Check-Out"); + + b.Property("CheckType") + .HasColumnType("longtext") + .HasComment("ประเภทการลงเวลา: CHECK_IN, CHECK_OUT"); + + b.Property("CompletedDate") + .HasColumnType("datetime(6)") + .HasComment("วันเวลาที่เสร็จสิ้นการประมวลผล"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(100) + .HasComment("สร้างข้อมูลเมื่อ"); + + b.Property("CreatedDate") + .HasColumnType("datetime(6)") + .HasComment("วันเวลาที่สร้างงาน"); + + b.Property("CreatedFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(104) + .HasComment("ชื่อ User ที่สร้างข้อมูล"); + + b.Property("CreatedUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(101) + .HasComment("User Id ที่สร้างข้อมูล"); + + b.Property("ErrorMessage") + .HasColumnType("longtext") + .HasComment("ข้อความแสดงข้อผิดพลาด"); + + b.Property("KeycloakUserId") + .HasColumnType("char(36)") + .HasComment("รหัส User ของ Keycloak"); + + b.Property("LastUpdateFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(105) + .HasComment("ชื่อ User ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdateUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(103) + .HasComment("User Id ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(102) + .HasComment("แก้ไขข้อมูลล่าสุดเมื่อ"); + + b.Property("ProcessingDate") + .HasColumnType("datetime(6)") + .HasComment("วันเวลาที่เริ่มประมวลผล"); + + b.Property("Status") + .IsRequired() + .HasColumnType("longtext") + .HasComment("สถานะงาน: PENDING, PROCESSING, COMPLETED, FAILED"); + + b.Property("TaskId") + .HasColumnType("char(36)") + .HasComment("Task ID สำหรับติดตามสถานะงาน"); + + b.HasKey("Id"); + + b.ToTable("CheckInJobStatuses"); + }); + + modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.TimeAttendants.DutyTime", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)") + .HasColumnOrder(0) + .HasComment("PrimaryKey") + .HasAnnotation("Relational:JsonPropertyName", "id"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(100) + .HasComment("สร้างข้อมูลเมื่อ"); + + b.Property("CreatedFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(104) + .HasComment("ชื่อ User ที่สร้างข้อมูล"); + + b.Property("CreatedUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(101) + .HasComment("User Id ที่สร้างข้อมูล"); + + b.Property("Description") + .IsRequired() + .HasColumnType("longtext") + .HasComment("คำอธิบาย"); + + b.Property("EndTimeAfternoon") + .IsRequired() + .HasColumnType("longtext") + .HasComment("เวลาออกงานช่วงบ่าย"); + + b.Property("EndTimeMorning") + .IsRequired() + .HasColumnType("longtext") + .HasComment("เวลาออกงานช่วงเช้า"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)") + .HasComment("สถานะการเปิดใช้งาน (เปิด/ปิด)"); + + b.Property("IsDefault") + .HasColumnType("tinyint(1)") + .HasComment("สถานะว่ารอบใดเป็นค่า Default ของข้าราชการ (สำหรับทุกคนที่ยังไม่ได้ทำการเลือกรอบ)"); + + b.Property("LastUpdateFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(105) + .HasComment("ชื่อ User ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdateUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(103) + .HasComment("User Id ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(102) + .HasComment("แก้ไขข้อมูลล่าสุดเมื่อ"); + + b.Property("StartTimeAfternoon") + .IsRequired() + .HasColumnType("longtext") + .HasComment("เวลาเข้างานช่วงบ่าย"); + + b.Property("StartTimeMorning") + .IsRequired() + .HasColumnType("longtext") + .HasComment("เวลาเข้างานช่วงเช้า"); + + b.HasKey("Id"); + + b.ToTable("DutyTimes"); + }); + + modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.TimeAttendants.ProcessUserTimeStamp", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)") + .HasColumnOrder(0) + .HasComment("PrimaryKey") + .HasAnnotation("Relational:JsonPropertyName", "id"); + + b.Property("CheckIn") + .HasColumnType("datetime(6)") + .HasComment("วัน เวลา เข้างาน"); + + b.Property("CheckInImageUrl") + .IsRequired() + .HasColumnType("longtext") + .HasComment("รูปถ่ายสถานที่ Check-In"); + + b.Property("CheckInLat") + .HasColumnType("double") + .HasComment("พิกัดละติจูด Check-In"); + + b.Property("CheckInLocationName") + .HasColumnType("longtext") + .HasComment("กรณีเลือกนอกสถานที่ตั้ง ต้องระบุข้อมูลชื่อสถานะที่ Check-In"); + + b.Property("CheckInLon") + .HasColumnType("double") + .HasComment("พิกัดลองจิจูด Check-In"); + + b.Property("CheckInPOI") + .IsRequired() + .HasColumnType("longtext") + .HasComment("ชื่อสถานที่ ได้มาจากระบบ ArcGis ของกองสารสนเทศภูมิศาสตร์ Check-In"); + + b.Property("CheckInRemark") + .HasColumnType("longtext") + .HasComment("ข้อความหมายเหตุที่ต้องการระบุเพิ่ม(มีเผื่อไว้อาจไม่ได้ใช้) Check-In"); + + b.Property("CheckInStatus") + .IsRequired() + .HasColumnType("longtext") + .HasComment("สถานะ Check-In"); + + b.Property("CheckOut") + .HasColumnType("datetime(6)") + .HasComment("วัน เวลา ออกงาน"); + + b.Property("CheckOutImageUrl") + .IsRequired() + .HasColumnType("longtext") + .HasComment("รูปถ่ายสถานที่ Check-Out"); + + b.Property("CheckOutLat") + .HasColumnType("double") + .HasComment("พิกัดละติจูด Check-Out"); + + b.Property("CheckOutLocationName") + .HasColumnType("longtext") + .HasComment("กรณีเลือกนอกสถานที่ตั้ง ต้องระบุข้อมูลชื่อสถานะที่ Check-Out"); + + b.Property("CheckOutLon") + .HasColumnType("double") + .HasComment("พิกัดลองจิจูด Check-Out"); + + b.Property("CheckOutPOI") + .IsRequired() + .HasColumnType("longtext") + .HasComment("ชื่อสถานที่ ได้มาจากระบบ ArcGis ของกองสารสนเทศภูมิศาสตร์ Check-Out"); + + b.Property("CheckOutRemark") + .HasColumnType("longtext") + .HasComment("ข้อความหมายเหตุที่ต้องการระบุเพิ่ม(มีเผื่อไว้อาจไม่ได้ใช้) Check-Out"); + + b.Property("CheckOutStatus") + .HasColumnType("longtext") + .HasComment("สถานะ Check-Out"); + + b.Property("Child1") + .HasColumnType("longtext"); + + b.Property("Child1DnaId") + .HasColumnType("char(36)"); + + b.Property("Child1Id") + .HasColumnType("char(36)"); + + b.Property("Child2") + .HasColumnType("longtext"); + + b.Property("Child2DnaId") + .HasColumnType("char(36)"); + + b.Property("Child2Id") + .HasColumnType("char(36)"); + + b.Property("Child3") + .HasColumnType("longtext"); + + b.Property("Child3DnaId") + .HasColumnType("char(36)"); + + b.Property("Child3Id") + .HasColumnType("char(36)"); + + b.Property("Child4") + .HasColumnType("longtext"); + + b.Property("Child4DnaId") + .HasColumnType("char(36)"); + + b.Property("Child4Id") + .HasColumnType("char(36)"); + + b.Property("CitizenId") + .HasColumnType("longtext"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(100) + .HasComment("สร้างข้อมูลเมื่อ"); + + b.Property("CreatedFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(104) + .HasComment("ชื่อ User ที่สร้างข้อมูล"); + + b.Property("CreatedUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(101) + .HasComment("User Id ที่สร้างข้อมูล"); + + b.Property("EditReason") + .HasColumnType("longtext") + .HasComment("เหตุผลการอนุมัติ/ไม่อนุมัติขอลงเวลาพิเศษ"); + + b.Property("EditStatus") + .HasColumnType("longtext") + .HasComment("สถานะการของลงเวลาพิเศษ"); + + b.Property("FirstName") + .HasColumnType("longtext"); + + b.Property("Gender") + .HasColumnType("longtext"); + + b.Property("IsLocationCheckIn") + .HasColumnType("tinyint(1)") + .HasComment("true คือ ณ สถานที่ตั้ง, false คือ นอกสถานที่ตั้ง Check-In"); + + b.Property("IsLocationCheckOut") + .HasColumnType("tinyint(1)") + .HasComment("true คือ ณ สถานที่ตั้ง, false คือ นอกสถานที่ตั้ง Check-Out"); + + b.Property("IsProcess") + .HasColumnType("tinyint(1)") + .HasComment("นำไปประมวลผลแล้วหรือยัง"); + + b.Property("KeycloakUserId") + .HasColumnType("char(36)") + .HasComment("รหัส User ของ Keycloak"); + + b.Property("LastName") + .HasColumnType("longtext"); + + b.Property("LastUpdateFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(105) + .HasComment("ชื่อ User ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdateUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(103) + .HasComment("User Id ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(102) + .HasComment("แก้ไขข้อมูลล่าสุดเมื่อ"); + + b.Property("Prefix") + .HasColumnType("longtext"); + + b.Property("ProfileId") + .HasColumnType("char(36)"); + + b.Property("ProfileType") + .HasColumnType("longtext"); + + b.Property("Root") + .HasColumnType("longtext"); + + b.Property("RootDnaId") + .HasColumnType("char(36)"); + + b.Property("RootId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.ToTable("ProcessUserTimeStamps"); + }); + + modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.TimeAttendants.UserCalendar", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)") + .HasColumnOrder(0) + .HasComment("PrimaryKey") + .HasAnnotation("Relational:JsonPropertyName", "id"); + + b.Property("Calendar") + .IsRequired() + .HasColumnType("longtext") + .HasComment("ปฏิทินการทำงานของ ขรก ปกติ หรือ 6 วันต่อสัปดาห์"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(100) + .HasComment("สร้างข้อมูลเมื่อ"); + + b.Property("CreatedFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(104) + .HasComment("ชื่อ User ที่สร้างข้อมูล"); + + b.Property("CreatedUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(101) + .HasComment("User Id ที่สร้างข้อมูล"); + + b.Property("LastUpdateFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(105) + .HasComment("ชื่อ User ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdateUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(103) + .HasComment("User Id ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(102) + .HasComment("แก้ไขข้อมูลล่าสุดเมื่อ"); + + b.Property("ProfileId") + .HasColumnType("char(36)") + .HasComment("รหัส Profile ในระบบทะเบียนประวัติ"); + + b.HasKey("Id"); + + b.ToTable("UserCalendars"); + }); + + modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.TimeAttendants.UserDutyTime", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)") + .HasColumnOrder(0) + .HasComment("PrimaryKey") + .HasAnnotation("Relational:JsonPropertyName", "id"); + + b.Property("Child1DnaId") + .HasColumnType("char(36)"); + + b.Property("Child2DnaId") + .HasColumnType("char(36)"); + + b.Property("Child3DnaId") + .HasColumnType("char(36)"); + + b.Property("Child4DnaId") + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(100) + .HasComment("สร้างข้อมูลเมื่อ"); + + b.Property("CreatedFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(104) + .HasComment("ชื่อ User ที่สร้างข้อมูล"); + + b.Property("CreatedUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(101) + .HasComment("User Id ที่สร้างข้อมูล"); + + b.Property("DutyTimeId") + .HasColumnType("char(36)") + .HasComment("รหัสรอบการลงเวลา"); + + b.Property("EffectiveDate") + .HasColumnType("datetime(6)") + .HasComment("วันที่มีผล"); + + b.Property("IsProcess") + .HasColumnType("tinyint(1)") + .HasComment("ทำการประมวลผลแล้วหรือยัง"); + + b.Property("LastUpdateFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(105) + .HasComment("ชื่อ User ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdateUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(103) + .HasComment("User Id ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(102) + .HasComment("แก้ไขข้อมูลล่าสุดเมื่อ"); + + b.Property("ProfileId") + .HasColumnType("char(36)") + .HasComment("รหัส Profile ในระบบทะเบียนประวัติ"); + + b.Property("Remark") + .HasColumnType("longtext") + .HasComment("หมายเหตุ"); + + b.Property("RootDnaId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("DutyTimeId"); + + b.ToTable("UserDutyTimes"); + }); + + modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.TimeAttendants.UserTimeStamp", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)") + .HasColumnOrder(0) + .HasComment("PrimaryKey") + .HasAnnotation("Relational:JsonPropertyName", "id"); + + b.Property("CheckIn") + .HasColumnType("datetime(6)") + .HasComment("วัน เวลา เข้างาน"); + + b.Property("CheckInImageUrl") + .IsRequired() + .HasColumnType("longtext") + .HasComment("รูปถ่ายสถานที่ Check-In"); + + b.Property("CheckInLat") + .HasColumnType("double") + .HasComment("พิกัดละติจูด Check-In"); + + b.Property("CheckInLocationName") + .HasColumnType("longtext") + .HasComment("กรณีเลือกนอกสถานที่ตั้ง ต้องระบุข้อมูลชื่อสถานะที่ Check-In"); + + b.Property("CheckInLon") + .HasColumnType("double") + .HasComment("พิกัดลองจิจูด Check-In"); + + b.Property("CheckInPOI") + .IsRequired() + .HasColumnType("longtext") + .HasComment("ชื่อสถานที่ ได้มาจากระบบ ArcGis ของกองสารสนเทศภูมิศาสตร์ Check-In"); + + b.Property("CheckInRemark") + .HasColumnType("longtext") + .HasComment("ข้อความหมายเหตุที่ต้องการระบุเพิ่ม(มีเผื่อไว้อาจไม่ได้ใช้) Check-In"); + + b.Property("CheckOut") + .HasColumnType("datetime(6)") + .HasComment("วัน เวลา ออกงาน"); + + b.Property("CheckOutImageUrl") + .IsRequired() + .HasColumnType("longtext") + .HasComment("รูปถ่ายสถานที่ Check-Out"); + + b.Property("CheckOutLat") + .HasColumnType("double") + .HasComment("พิกัดละติจูด Check-Out"); + + b.Property("CheckOutLocationName") + .HasColumnType("longtext") + .HasComment("กรณีเลือกนอกสถานที่ตั้ง ต้องระบุข้อมูลชื่อสถานะที่ Check-Out"); + + b.Property("CheckOutLon") + .HasColumnType("double") + .HasComment("พิกัดลองจิจูด Check-Out"); + + b.Property("CheckOutPOI") + .IsRequired() + .HasColumnType("longtext") + .HasComment("ชื่อสถานที่ ได้มาจากระบบ ArcGis ของกองสารสนเทศภูมิศาสตร์ Check-Out"); + + b.Property("CheckOutRemark") + .HasColumnType("longtext") + .HasComment("ข้อความหมายเหตุที่ต้องการระบุเพิ่ม(มีเผื่อไว้อาจไม่ได้ใช้) Check-Out"); + + b.Property("Child1") + .HasColumnType("longtext"); + + b.Property("Child1DnaId") + .HasColumnType("char(36)"); + + b.Property("Child1Id") + .HasColumnType("char(36)"); + + b.Property("Child2") + .HasColumnType("longtext"); + + b.Property("Child2DnaId") + .HasColumnType("char(36)"); + + b.Property("Child2Id") + .HasColumnType("char(36)"); + + b.Property("Child3") + .HasColumnType("longtext"); + + b.Property("Child3DnaId") + .HasColumnType("char(36)"); + + b.Property("Child3Id") + .HasColumnType("char(36)"); + + b.Property("Child4") + .HasColumnType("longtext"); + + b.Property("Child4DnaId") + .HasColumnType("char(36)"); + + b.Property("Child4Id") + .HasColumnType("char(36)"); + + b.Property("CitizenId") + .HasColumnType("longtext"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(100) + .HasComment("สร้างข้อมูลเมื่อ"); + + b.Property("CreatedFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(104) + .HasComment("ชื่อ User ที่สร้างข้อมูล"); + + b.Property("CreatedUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(101) + .HasComment("User Id ที่สร้างข้อมูล"); + + b.Property("FirstName") + .HasColumnType("longtext"); + + b.Property("Gender") + .HasColumnType("longtext"); + + b.Property("IsLocationCheckIn") + .HasColumnType("tinyint(1)") + .HasComment("true คือ ณ สถานที่ตั้ง, false คือ นอกสถานที่ตั้ง Check-In"); + + b.Property("IsLocationCheckOut") + .HasColumnType("tinyint(1)") + .HasComment("true คือ ณ สถานที่ตั้ง, false คือ นอกสถานที่ตั้ง Check-Out"); + + b.Property("IsProcess") + .HasColumnType("tinyint(1)") + .HasComment("นำไปประมวลผลแล้วหรือยัง"); + + b.Property("KeycloakUserId") + .HasColumnType("char(36)") + .HasComment("รหัส User ของ Keycloak"); + + b.Property("LastName") + .HasColumnType("longtext"); + + b.Property("LastUpdateFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(105) + .HasComment("ชื่อ User ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdateUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(103) + .HasComment("User Id ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(102) + .HasComment("แก้ไขข้อมูลล่าสุดเมื่อ"); + + b.Property("Prefix") + .HasColumnType("longtext"); + + b.Property("ProfileId") + .HasColumnType("char(36)"); + + b.Property("ProfileType") + .HasColumnType("longtext"); + + b.Property("Root") + .HasColumnType("longtext"); + + b.Property("RootDnaId") + .HasColumnType("char(36)"); + + b.Property("RootId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.ToTable("UserTimeStamps"); + }); + + modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.Requests.LeaveBeginning", b => + { + b.HasOne("BMA.EHR.Domain.Models.Leave.Commons.LeaveType", "LeaveType") + .WithMany() + .HasForeignKey("LeaveTypeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("LeaveType"); + }); + + modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.Requests.LeaveDocument", b => + { + b.HasOne("BMA.EHR.Domain.Models.Documents.Document", "Document") + .WithMany() + .HasForeignKey("DocumentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("BMA.EHR.Domain.Models.Leave.Requests.LeaveRequest", "LeaveRequest") + .WithMany("LeaveDocument") + .HasForeignKey("LeaveRequestId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Document"); + + b.Navigation("LeaveRequest"); + }); + + modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.Requests.LeaveRequest", b => + { + b.HasOne("BMA.EHR.Domain.Models.Documents.Document", "LeaveCancelDocument") + .WithMany() + .HasForeignKey("LeaveCancelDocumentId"); + + b.HasOne("BMA.EHR.Domain.Models.Documents.Document", "LeaveDraftDocument") + .WithMany() + .HasForeignKey("LeaveDraftDocumentId"); + + b.HasOne("BMA.EHR.Domain.Models.Leave.Commons.LeaveType", "Type") + .WithMany() + .HasForeignKey("TypeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("LeaveCancelDocument"); + + b.Navigation("LeaveDraftDocument"); + + b.Navigation("Type"); + }); + + modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.Requests.LeaveRequestApprover", b => + { + b.HasOne("BMA.EHR.Domain.Models.Leave.Requests.LeaveRequest", "LeaveRequest") + .WithMany("Approvers") + .HasForeignKey("LeaveRequestId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("LeaveRequest"); + }); + + modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.TimeAttendants.UserDutyTime", b => + { + b.HasOne("BMA.EHR.Domain.Models.Leave.TimeAttendants.DutyTime", "DutyTime") + .WithMany() + .HasForeignKey("DutyTimeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("DutyTime"); + }); + + modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.Requests.LeaveRequest", b => + { + b.Navigation("Approvers"); + + b.Navigation("LeaveDocument"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/BMA.EHR.Infrastructure/Migrations/LeaveDb/20260210091134_Add BeginningLeave and LeaveCount to LeaveBeginning.cs b/BMA.EHR.Infrastructure/Migrations/LeaveDb/20260210091134_Add BeginningLeave and LeaveCount to LeaveBeginning.cs new file mode 100644 index 00000000..a54ad519 --- /dev/null +++ b/BMA.EHR.Infrastructure/Migrations/LeaveDb/20260210091134_Add BeginningLeave and LeaveCount to LeaveBeginning.cs @@ -0,0 +1,62 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace BMA.EHR.Infrastructure.Migrations.LeaveDb +{ + /// + public partial class AddBeginningLeaveandLeaveCounttoLeaveBeginning : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterColumn( + name: "LeaveDays", + table: "LeaveBeginnings", + type: "double", + nullable: false, + comment: "จำนวนวันลาทั้งหมด", + oldClrType: typeof(double), + oldType: "double", + oldComment: "จำนวนวันลายกมา"); + + migrationBuilder.AddColumn( + name: "BeginningLeaveCount", + table: "LeaveBeginnings", + type: "int", + nullable: false, + defaultValue: 0, + comment: "จำนวนครั้งที่ลายกมา"); + + migrationBuilder.AddColumn( + name: "BeginningLeaveDays", + table: "LeaveBeginnings", + type: "double", + nullable: false, + defaultValue: 0.0, + comment: "จำนวนวันลายกมา"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "BeginningLeaveCount", + table: "LeaveBeginnings"); + + migrationBuilder.DropColumn( + name: "BeginningLeaveDays", + table: "LeaveBeginnings"); + + migrationBuilder.AlterColumn( + name: "LeaveDays", + table: "LeaveBeginnings", + type: "double", + nullable: false, + comment: "จำนวนวันลายกมา", + oldClrType: typeof(double), + oldType: "double", + oldComment: "จำนวนวันลาทั้งหมด"); + } + } +} diff --git a/BMA.EHR.Infrastructure/Migrations/LeaveDb/LeaveDbContextModelSnapshot.cs b/BMA.EHR.Infrastructure/Migrations/LeaveDb/LeaveDbContextModelSnapshot.cs index b59264fc..510601d8 100644 --- a/BMA.EHR.Infrastructure/Migrations/LeaveDb/LeaveDbContextModelSnapshot.cs +++ b/BMA.EHR.Infrastructure/Migrations/LeaveDb/LeaveDbContextModelSnapshot.cs @@ -128,6 +128,14 @@ namespace BMA.EHR.Infrastructure.Migrations.LeaveDb .HasComment("PrimaryKey") .HasAnnotation("Relational:JsonPropertyName", "id"); + b.Property("BeginningLeaveCount") + .HasColumnType("int") + .HasComment("จำนวนครั้งที่ลายกมา"); + + b.Property("BeginningLeaveDays") + .HasColumnType("double") + .HasComment("จำนวนวันลายกมา"); + b.Property("Child1DnaId") .HasColumnType("char(36)"); @@ -190,7 +198,7 @@ namespace BMA.EHR.Infrastructure.Migrations.LeaveDb b.Property("LeaveDays") .HasColumnType("double") - .HasComment("จำนวนวันลายกมา"); + .HasComment("จำนวนวันลาทั้งหมด"); b.Property("LeaveDaysUsed") .HasColumnType("double") From 2410574d42c42d8eaf2356f25fbc1605b6fb03d7 Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Wed, 11 Feb 2026 10:47:01 +0700 Subject: [PATCH 099/178] Fix typo in IsProbatin property name and update related condition check in LeaveRequestController #2306 --- .../Profiles/GetProfileByKeycloakIdDto.cs | 2 +- .../Controllers/LeaveRequestController.cs | 15 ++++++++------- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/BMA.EHR.Application/Responses/Profiles/GetProfileByKeycloakIdDto.cs b/BMA.EHR.Application/Responses/Profiles/GetProfileByKeycloakIdDto.cs index 7db0e212..8e319904 100644 --- a/BMA.EHR.Application/Responses/Profiles/GetProfileByKeycloakIdDto.cs +++ b/BMA.EHR.Application/Responses/Profiles/GetProfileByKeycloakIdDto.cs @@ -44,7 +44,7 @@ namespace BMA.EHR.Application.Responses.Profiles public string? ProfileType { get; set; } public bool? IsLeave { get; set; } - public bool? IsProbatin { get; set; } + public bool? IsProbation { get; set; } public string? Root { get; set; } public string? Child1 { get; set; } diff --git a/BMA.EHR.Leave/Controllers/LeaveRequestController.cs b/BMA.EHR.Leave/Controllers/LeaveRequestController.cs index 1a00d285..391ec6d4 100644 --- a/BMA.EHR.Leave/Controllers/LeaveRequestController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveRequestController.cs @@ -1084,12 +1084,13 @@ namespace BMA.EHR.Leave.Service.Controllers // fix issue : ระบบลา (ขรก.) >> ลาพักผ่อน (กรณีรับราชการไม่ถึง 6 เดือน) #838 //var leavePrevYear = (await _leaveRequestRepository.GetSumApproveLeaveAsync(fiscalYear - 1)).Where(x => x.LeaveTypeCode == "LV-005" && x.KeycloakUserId == userId).FirstOrDefault(); //var leavePrevYearRemain = 10 - (leavePrevYear == null ? 0 : leavePrevYear.SumLeaveDay); // หายอดวันลาที่เหลือของปีก่อน - if (profile.IsProbatin! == true) + if (profile.IsProbation! == true) { isLeave = false; if (!isLeave) message = "ยังอยู่ในช่วงทดลองปฏิบัติราชการ ไม่สามารถลาพักผ่อนได้"; } - else if (govAge >= 180) + //else if (govAge >= 180) + else { isLeave = (totalDay - (sumWorkDay + sumWeekend) + approveDay) <= (limitDay); if (!isLeave) message = "จำนวนวันลาเกินที่กำหนด"; @@ -1112,11 +1113,11 @@ namespace BMA.EHR.Leave.Service.Controllers // if (!isLeave) message = "จำนวนวันลาเกินที่กำหนด"; // } - else - { - isLeave = false; - if (!isLeave) message = "อายุราชการน้อยกว่า 6 เดือนหรือ 180 วัน"; - } + // else + // { + // isLeave = false; + // if (!isLeave) message = "อายุราชการน้อยกว่า 6 เดือนหรือ 180 วัน"; + // } break; case "LV-006": From a2ac05ed61cc1028ba882cb1efd82e614bd866ef Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Wed, 11 Feb 2026 11:11:19 +0700 Subject: [PATCH 100/178] Add BeginningLeaveDays and BeginningLeaveCount to LeaveBeginning DTOs and update controller logic #2304 #2305 --- .../Controllers/LeaveBeginningController.cs | 84 +++++++++++++++++++ .../CreateLeaveBeginningDto.cs | 11 ++- .../LeaveBeginnings/EditLeaveBeginningDto.cs | 6 ++ 3 files changed, 100 insertions(+), 1 deletion(-) diff --git a/BMA.EHR.Leave/Controllers/LeaveBeginningController.cs b/BMA.EHR.Leave/Controllers/LeaveBeginningController.cs index 3a3140e8..c99aeaea 100644 --- a/BMA.EHR.Leave/Controllers/LeaveBeginningController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveBeginningController.cs @@ -202,6 +202,8 @@ namespace BMA.EHR.Leave.Service.Controllers item.LeaveDays, item.LeaveDaysUsed, item.LeaveCount, + item.BeginningLeaveDays, + item.BeginningLeaveCount, item.CreatedAt, item.CreatedFullName, item.LastUpdatedAt, @@ -396,6 +398,8 @@ namespace BMA.EHR.Leave.Service.Controllers leaveBeginning.LeaveDays = req.LeaveDays; leaveBeginning.LeaveDaysUsed = req.LeaveDaysUsed; leaveBeginning.LeaveCount = req.LeaveCount; + leaveBeginning.BeginningLeaveDays = req.BeginningLeaveDays; + leaveBeginning.BeginningLeaveCount = req.BeginningLeaveCount; leaveBeginning.ProfileId = req.ProfileId; leaveBeginning.Prefix = profile.Prefix; @@ -465,6 +469,8 @@ namespace BMA.EHR.Leave.Service.Controllers leaveBeginning.LeaveDays = req.LeaveDays; leaveBeginning.LeaveDaysUsed = req.LeaveDaysUsed; leaveBeginning.LeaveCount = req.LeaveCount; + leaveBeginning.BeginningLeaveDays = req.BeginningLeaveDays; + leaveBeginning.BeginningLeaveCount = req.BeginningLeaveCount; leaveBeginning.ProfileId = req.ProfileId; leaveBeginning.Prefix = profile.Prefix; @@ -492,6 +498,84 @@ namespace BMA.EHR.Leave.Service.Controllers } } + [HttpPut("schedule")] + [AllowAnonymous] + public async Task> ScheduleLeaveBeginning([FromBody] EditLeaveBeginningDto req) + { + try + { + var profile = await _userProfileRepository.GetProfileByProfileIdAsync(req.ProfileId, AccessToken); + if(profile == null) + { + return Error("ไม่พบข้อมูลข้าราชการหรือลูกจ้าง", StatusCodes.Status404NotFound); + } + // check duplicate + var oldData = await _context.LeaveBeginnings.FirstOrDefaultAsync(x => x.ProfileId == req.ProfileId + && x.LeaveTypeId == req.LeaveTypeId + && x.LeaveYear == req.LeaveYear); + + if (oldData is not null) + { + //return Error("ไม่สามารถบันทึกข้อมูล เนื่องจากมีข้อมูลในระบบแล้ว"); + oldData.LeaveTypeId = req.LeaveTypeId; + oldData.LeaveYear = req.LeaveYear; + oldData.LeaveDays = req.LeaveDays; + oldData.LeaveDaysUsed = req.LeaveDaysUsed; + oldData.LeaveCount = req.LeaveCount; + oldData.BeginningLeaveDays = req.BeginningLeaveDays; + oldData.BeginningLeaveCount = req.BeginningLeaveCount; + + oldData.ProfileId = req.ProfileId; + oldData.Prefix = profile.Prefix; + oldData.FirstName = profile.FirstName; + oldData.LastName = profile.LastName; + oldData.RootDnaId = profile.RootDnaId; + oldData.Child1DnaId = profile.Child1DnaId; + oldData.Child2DnaId = profile.Child2DnaId; + oldData.Child3DnaId = profile.Child3DnaId; + oldData.Child4DnaId = profile.Child4DnaId; + + oldData.LastUpdateUserId = ""; + oldData.LastUpdateFullName = FullName ?? ""; + oldData.LastUpdatedAt = DateTime.Now; + + await _leaveBeginningRepository.UpdateAsync(oldData); + } + else + { + var leaveBeginning = new LeaveBeginning(); + leaveBeginning.LeaveTypeId = req.LeaveTypeId; + leaveBeginning.LeaveYear = req.LeaveYear; + leaveBeginning.LeaveDays = req.LeaveDays; + leaveBeginning.LeaveDaysUsed = req.LeaveDaysUsed; + leaveBeginning.LeaveCount = req.LeaveCount; + leaveBeginning.BeginningLeaveDays = req.BeginningLeaveDays; + leaveBeginning.BeginningLeaveCount = req.BeginningLeaveCount; + + leaveBeginning.ProfileId = req.ProfileId; + leaveBeginning.Prefix = profile.Prefix; + leaveBeginning.FirstName = profile.FirstName; + leaveBeginning.LastName = profile.LastName; + + leaveBeginning.RootDnaId = profile.RootDnaId; + leaveBeginning.Child1DnaId = profile.Child1DnaId; + leaveBeginning.Child2DnaId = profile.Child2DnaId; + leaveBeginning.Child3DnaId = profile.Child3DnaId; + leaveBeginning.Child4DnaId = profile.Child4DnaId; + + leaveBeginning.CreatedUserId = ""; + leaveBeginning.CreatedFullName = FullName ?? ""; + leaveBeginning.CreatedAt = DateTime.Now; + + await _leaveBeginningRepository.AddAsync(leaveBeginning); + } + return Success(); + } + catch (Exception ex) + { + return Error(ex); + } + } #endregion } diff --git a/BMA.EHR.Leave/DTOs/LeaveBeginnings/CreateLeaveBeginningDto.cs b/BMA.EHR.Leave/DTOs/LeaveBeginnings/CreateLeaveBeginningDto.cs index 79d33052..423385ed 100644 --- a/BMA.EHR.Leave/DTOs/LeaveBeginnings/CreateLeaveBeginningDto.cs +++ b/BMA.EHR.Leave/DTOs/LeaveBeginnings/CreateLeaveBeginningDto.cs @@ -14,10 +14,19 @@ namespace BMA.EHR.Leave.Service.DTOs.LeaveBeginnings [Required, Comment("ปีงบประมาณ")] public int LeaveYear { get; set; } = 0; - [Required, Comment("จำนวนวันลายกมา")] + [Required, Comment("จำนวนวันลาที่ได้รับ")] public double LeaveDays { get; set; } = 0.0; [Required, Comment("จำนวนวันลาที่ใช้ไป")] public double LeaveDaysUsed { get; set; } = 0.0; + + [Required, Comment("จำนวนครั้งที่ลาสะสม")] + public int LeaveCount { get; set; } = 0; + + [Required, Comment("จำนวนวันลายกมา")] + public double BeginningLeaveDays { get; set; } = 0.0; + + [Comment("จำนวนครั้งที่ลายกมา")] + public int BeginningLeaveCount { get; set; } = 0; } } diff --git a/BMA.EHR.Leave/DTOs/LeaveBeginnings/EditLeaveBeginningDto.cs b/BMA.EHR.Leave/DTOs/LeaveBeginnings/EditLeaveBeginningDto.cs index a73bdfd4..d4a2661f 100644 --- a/BMA.EHR.Leave/DTOs/LeaveBeginnings/EditLeaveBeginningDto.cs +++ b/BMA.EHR.Leave/DTOs/LeaveBeginnings/EditLeaveBeginningDto.cs @@ -22,5 +22,11 @@ namespace BMA.EHR.Leave.Service.DTOs.LeaveBeginnings [Required, Comment("จำนวนครั้งที่ลาสะสม")] public int LeaveCount { get; set; } = 0; + + [Required, Comment("จำนวนวันลายกมา")] + public double BeginningLeaveDays { get; set; } = 0.0; + + [Comment("จำนวนครั้งที่ลายกมา")] + public int BeginningLeaveCount { get; set; } = 0; } } From e5e7c7788096d8d2c3deed2f2cbc5c2a72b4a77f Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Wed, 11 Feb 2026 11:47:49 +0700 Subject: [PATCH 101/178] Add GetProfileByProfileIdNoAuthAsync method and update related controller logic --- .../Repositories/UserProfileRepository.cs | 25 +++++++++++++++++++ .../Controllers/LeaveBeginningController.cs | 4 +-- .../Controllers/LeaveReportController.cs | 16 ++++++++++++ .../Controllers/LeaveRequestController.cs | 11 ++++---- 4 files changed, 49 insertions(+), 7 deletions(-) diff --git a/BMA.EHR.Application/Repositories/UserProfileRepository.cs b/BMA.EHR.Application/Repositories/UserProfileRepository.cs index db63e056..23a33870 100644 --- a/BMA.EHR.Application/Repositories/UserProfileRepository.cs +++ b/BMA.EHR.Application/Repositories/UserProfileRepository.cs @@ -341,6 +341,31 @@ namespace BMA.EHR.Application.Repositories } } + + public async Task GetProfileByProfileIdNoAuthAsync(Guid profileId, string? accessToken) + { + try + { + var apiPath = $"{_configuration["API"]}/api/v1/org/unauthorize/profile/{profileId}"; + var apiKey = _configuration["API_KEY"]; + + var apiResult = await GetExternalAPIAsync(apiPath, accessToken ?? "", apiKey); + if (apiResult != null) + { + var raw = JsonConvert.DeserializeObject(apiResult); + if (raw != null) + return raw.Result; + } + + return null; + } + catch + { + throw; + } + } + + public async Task UpdateDutyTimeAsync(Guid profileId, Guid roundId, DateTime effectiveDate, string? accessToken) { try diff --git a/BMA.EHR.Leave/Controllers/LeaveBeginningController.cs b/BMA.EHR.Leave/Controllers/LeaveBeginningController.cs index c99aeaea..245176da 100644 --- a/BMA.EHR.Leave/Controllers/LeaveBeginningController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveBeginningController.cs @@ -500,11 +500,11 @@ namespace BMA.EHR.Leave.Service.Controllers [HttpPut("schedule")] [AllowAnonymous] - public async Task> ScheduleLeaveBeginning([FromBody] EditLeaveBeginningDto req) + public async Task> ScheduleUpdateLeaveBeginningAsync([FromBody] EditLeaveBeginningDto req) { try { - var profile = await _userProfileRepository.GetProfileByProfileIdAsync(req.ProfileId, AccessToken); + var profile = await _userProfileRepository.GetProfileByProfileIdNoAuthAsync(req.ProfileId, AccessToken); if(profile == null) { return Error("ไม่พบข้อมูลข้าราชการหรือลูกจ้าง", StatusCodes.Status404NotFound); diff --git a/BMA.EHR.Leave/Controllers/LeaveReportController.cs b/BMA.EHR.Leave/Controllers/LeaveReportController.cs index 4e5a0961..51f8e6f0 100644 --- a/BMA.EHR.Leave/Controllers/LeaveReportController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveReportController.cs @@ -149,6 +149,7 @@ namespace BMA.EHR.Leave.Service.Controllers } var fullName = $"{profile!.Prefix}{profile!.FirstName} {profile!.LastName}"; + var lastLeaveRequest = await _leaveRequestRepository.GetLastLeaveRequestByTypeForUserAsync(data.KeycloakUserId, @@ -156,7 +157,18 @@ namespace BMA.EHR.Leave.Service.Controllers var startFiscalYear = new DateTime(data.LeaveStartDate.Year - 1, 10, 1); var endFiscalYear = data.LeaveStartDate.Date.AddDays(-1); // นับจากวันที่ยื่นลา + + var thisYear = data.LeaveStartDate.Year; + var toDay = data.LeaveStartDate.Date; + if (toDay >= new DateTime(toDay.Year, 10, 1) && toDay <= new DateTime(toDay.Year, 12, 31)) + thisYear = thisYear + 1; + var leaveData = await _leaveBeginningRepository.GetByYearAndTypeIdForUser2Async(thisYear, data.Type.Id, data.KeycloakUserId); var sumLeave = await _leaveRequestRepository.GetSumApproveLeaveTotalByTypeAndRangeForUser(data.KeycloakUserId, data.Type.Id, startFiscalYear, endFiscalYear); + if (leaveData != null) + { + sumLeave += leaveData.BeginningLeaveDays; + } + var Oc = profile.isCommission == false ? profile.Oc.ToThaiNumber() : profile.Oc.Replace("สำนักงานคณะกรรมการข้าราชการกรุงเทพมหานคร", "สำนักงาน ก.ก.").ToThaiNumber(); @@ -334,6 +346,10 @@ namespace BMA.EHR.Leave.Service.Controllers var sumLeave = await _leaveRequestRepository.GetSumApproveLeaveTotalByTypeAndRangeForUser(data.KeycloakUserId, data.Type.Id, startFiscalYear, endFiscalYear); + if (leaveData != null) + { + sumLeave += leaveData.BeginningLeaveDays; + } //var sumLeave = leaveData == null ? 0.0 : leaveData.LeaveDaysUsed; var leaveLimit = leaveData == null ? 0.0 : leaveData.LeaveDays; diff --git a/BMA.EHR.Leave/Controllers/LeaveRequestController.cs b/BMA.EHR.Leave/Controllers/LeaveRequestController.cs index 391ec6d4..b2982860 100644 --- a/BMA.EHR.Leave/Controllers/LeaveRequestController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveRequestController.cs @@ -1579,7 +1579,8 @@ namespace BMA.EHR.Leave.Service.Controllers var leaveData = await _leaveBeginningRepository.GetByYearAndTypeIdForUserAsync(thisYear, rawData.Type.Id, rawData.KeycloakUserId); - var restDayOld = govAge < 180 ? 0 : leaveData == null ? 0 : leaveData.LeaveDays - 10; + var restDayOld = govAge < 180 ? 0 : leaveData == null ? 0 : (leaveData.LeaveDays + leaveData.BeginningLeaveDays - 10); + if (restDayOld < 0) restDayOld = 0; var restDayCurrent = govAge < 180 ? 0 : 10; @@ -2509,6 +2510,8 @@ namespace BMA.EHR.Leave.Service.Controllers var endFiscalYear = rawData.LeaveStartDate.Date.AddDays(-1); // นับจากวันที่ยื่นลา var leaveSummary = await _leaveRequestRepository.GetSumApproveLeaveTotalByTypeAndRangeForUser(rawData.KeycloakUserId, rawData.Type.Id, startFiscalYear, endFiscalYear); //var leaveSummary = leaveData == null ? 0.0 : leaveData.LeaveDaysUsed; + if (leaveData != null) + leaveSummary += leaveData.LeaveDaysUsed; var extendLeave = 0.0; var leaveLimit = (double)rawData.Type.Limit; @@ -2516,7 +2519,7 @@ namespace BMA.EHR.Leave.Service.Controllers if (rawData.Type.Code == "LV-005") { leaveLimit = leaveData == null ? 0.0 : leaveData.LeaveDays; - extendLeave = leaveLimit - 10; + extendLeave = leaveLimit <= 0 ? 0 : leaveLimit - 10; } var result = new GetLeaveRequestForAdminByIdDto @@ -2615,8 +2618,6 @@ namespace BMA.EHR.Leave.Service.Controllers //OrganizationName = rawData.OrganizationName ?? "", // fix SIT ระบบบันทึกการลา>>รายการลา (ข้อมูลผู้สังกัดและเรียนไม่แสดง) #971 - - ApproveStep = rawData.ApproveStep ?? "-", LeaveLimit = rawData.Type.Limit + extendLeave, @@ -2742,7 +2743,7 @@ namespace BMA.EHR.Leave.Service.Controllers if (leaveType.Code == "LV-005") { leaveLimit = leaveData?.LeaveDays ?? 0.0; - extendLeave = leaveLimit - 10; + extendLeave = leaveLimit <= 0 ? 0 : leaveLimit - 10; } var data = new From c81220a049224bd91b3445c395c9be72b0d586e6 Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Wed, 11 Feb 2026 11:56:04 +0700 Subject: [PATCH 102/178] Remove unnecessary whitespace and comment out unused code in LeaveReportController --- BMA.EHR.Leave/Controllers/LeaveReportController.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/BMA.EHR.Leave/Controllers/LeaveReportController.cs b/BMA.EHR.Leave/Controllers/LeaveReportController.cs index 51f8e6f0..8c3de6c0 100644 --- a/BMA.EHR.Leave/Controllers/LeaveReportController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveReportController.cs @@ -168,7 +168,7 @@ namespace BMA.EHR.Leave.Service.Controllers { sumLeave += leaveData.BeginningLeaveDays; } - + var Oc = profile.isCommission == false ? profile.Oc.ToThaiNumber() : profile.Oc.Replace("สำนักงานคณะกรรมการข้าราชการกรุงเทพมหานคร", "สำนักงาน ก.ก.").ToThaiNumber(); @@ -2885,6 +2885,14 @@ namespace BMA.EHR.Leave.Service.Controllers var leaveDays = await _leaveRequestRepository.GetSumApproveLeaveByRootAndRange(req.StartDate, req.EndDate, type, jsonData["result"]?.ToString(), nodeId, profileAdmin?.Node, req.nodeId, req.node); var enddate = req.EndDate.Date == req.StartDate.Date ? "" : $" - {req.EndDate.Date.ToThaiShortDate().ToThaiNumber()}"; + + // var thisYear = req.StartDate.Year; + // var toDay = req.StartDate.Date; + // if (toDay >= new DateTime(toDay.Year, 10, 1) && toDay <= new DateTime(toDay.Year, 12, 31)) + // thisYear = thisYear + 1; + + // var leaveData = await _leaveBeginningRepository.GetByYearAndTypeIdForUser2Async(thisYear, type, data.KeycloakUserId); + var result = new { template = "leave2", From 14fd9d5262492c8fd655b30d2412ee42afe930fd Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Wed, 11 Feb 2026 12:14:44 +0700 Subject: [PATCH 103/178] Refactor leave limit logic to use IsProbation property instead of govAge check --- BMA.EHR.Leave/Controllers/LeaveRequestController.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/BMA.EHR.Leave/Controllers/LeaveRequestController.cs b/BMA.EHR.Leave/Controllers/LeaveRequestController.cs index b2982860..120cc8ef 100644 --- a/BMA.EHR.Leave/Controllers/LeaveRequestController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveRequestController.cs @@ -897,12 +897,12 @@ namespace BMA.EHR.Leave.Service.Controllers if (leaveType.Code.Trim().ToUpper() == "LV-005") { - if (govAge < 180) + if (profile.IsProbation! == true) leaveLimit = 0; else { leaveLimit = leaveData == null ? - govAge < 180 ? 0 : 10 + 10 : leaveData.LeaveDays; } } @@ -911,7 +911,7 @@ namespace BMA.EHR.Leave.Service.Controllers var restOldDay = leaveData == null ? 0 : leaveData.LeaveDays - 10; var restCurrentDay = 10.0; - if (govAge < 180) + if (profile.IsProbation! == true) { restOldDay = 0; restCurrentDay = 0; From a8271c8d79d465d3864066cac72decb90024a20f Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Thu, 12 Feb 2026 20:09:05 +0700 Subject: [PATCH 104/178] Update leave summary calculation to use BeginningLeaveDays instead of LeaveDaysUsed #2305 --- BMA.EHR.Leave/Controllers/LeaveRequestController.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BMA.EHR.Leave/Controllers/LeaveRequestController.cs b/BMA.EHR.Leave/Controllers/LeaveRequestController.cs index 120cc8ef..6580dd53 100644 --- a/BMA.EHR.Leave/Controllers/LeaveRequestController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveRequestController.cs @@ -2511,7 +2511,7 @@ namespace BMA.EHR.Leave.Service.Controllers var leaveSummary = await _leaveRequestRepository.GetSumApproveLeaveTotalByTypeAndRangeForUser(rawData.KeycloakUserId, rawData.Type.Id, startFiscalYear, endFiscalYear); //var leaveSummary = leaveData == null ? 0.0 : leaveData.LeaveDaysUsed; if (leaveData != null) - leaveSummary += leaveData.LeaveDaysUsed; + leaveSummary += leaveData.BeginningLeaveDays; var extendLeave = 0.0; var leaveLimit = (double)rawData.Type.Limit; From 1b7bdd82e66bed2d4826bc68443357fd4b91efe4 Mon Sep 17 00:00:00 2001 From: Adisak Date: Fri, 13 Feb 2026 13:04:29 +0700 Subject: [PATCH 105/178] #2313 --- BMA.EHR.Placement.Service/Controllers/PlacementController.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BMA.EHR.Placement.Service/Controllers/PlacementController.cs b/BMA.EHR.Placement.Service/Controllers/PlacementController.cs index 51a09094..d3c28712 100644 --- a/BMA.EHR.Placement.Service/Controllers/PlacementController.cs +++ b/BMA.EHR.Placement.Service/Controllers/PlacementController.cs @@ -1791,7 +1791,7 @@ namespace BMA.EHR.Placement.Service.Controllers isLeave = false, dateRetire = (DateTime?)null, dateAppoint = r.commandDateAffect, - dateStart = r.commandDateAffect, + dateStart = p.ReportingDate, govAgeAbsent = 0, govAgePlus = 0, birthDate = (p.DateOfBirth == null || p.DateOfBirth == DateTime.MinValue) ? (DateTime?)null : p.DateOfBirth, From 7d3ec6c74e8d71a4b4858068982b51bcbb74320d Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Wed, 18 Feb 2026 16:34:35 +0700 Subject: [PATCH 106/178] Refactor ScheduleUpdateLeaveBeginningAsync to use ScheduleEditLeaveBeginningDto and remove unused profile checks --- .../Controllers/LeaveBeginningController.cs | 40 +++++++++---------- .../LeaveBeginnings/EditLeaveBeginningDto.cs | 16 ++++++++ 2 files changed, 36 insertions(+), 20 deletions(-) diff --git a/BMA.EHR.Leave/Controllers/LeaveBeginningController.cs b/BMA.EHR.Leave/Controllers/LeaveBeginningController.cs index 245176da..28f88302 100644 --- a/BMA.EHR.Leave/Controllers/LeaveBeginningController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveBeginningController.cs @@ -500,15 +500,15 @@ namespace BMA.EHR.Leave.Service.Controllers [HttpPut("schedule")] [AllowAnonymous] - public async Task> ScheduleUpdateLeaveBeginningAsync([FromBody] EditLeaveBeginningDto req) + public async Task> ScheduleUpdateLeaveBeginningAsync([FromBody] ScheduleEditLeaveBeginningDto req) { try { - var profile = await _userProfileRepository.GetProfileByProfileIdNoAuthAsync(req.ProfileId, AccessToken); - if(profile == null) - { - return Error("ไม่พบข้อมูลข้าราชการหรือลูกจ้าง", StatusCodes.Status404NotFound); - } + // var profile = await _userProfileRepository.GetProfileByProfileIdNoAuthAsync(req.ProfileId, AccessToken); + // if(profile == null) + // { + // return Error("ไม่พบข้อมูลข้าราชการหรือลูกจ้าง", StatusCodes.Status404NotFound); + // } // check duplicate var oldData = await _context.LeaveBeginnings.FirstOrDefaultAsync(x => x.ProfileId == req.ProfileId && x.LeaveTypeId == req.LeaveTypeId @@ -520,23 +520,23 @@ namespace BMA.EHR.Leave.Service.Controllers oldData.LeaveTypeId = req.LeaveTypeId; oldData.LeaveYear = req.LeaveYear; oldData.LeaveDays = req.LeaveDays; - oldData.LeaveDaysUsed = req.LeaveDaysUsed; - oldData.LeaveCount = req.LeaveCount; - oldData.BeginningLeaveDays = req.BeginningLeaveDays; - oldData.BeginningLeaveCount = req.BeginningLeaveCount; + // oldData.LeaveDaysUsed = req.LeaveDaysUsed; + // oldData.LeaveCount = req.LeaveCount; + // oldData.BeginningLeaveDays = req.BeginningLeaveDays; + // oldData.BeginningLeaveCount = req.BeginningLeaveCount; - oldData.ProfileId = req.ProfileId; - oldData.Prefix = profile.Prefix; - oldData.FirstName = profile.FirstName; - oldData.LastName = profile.LastName; - oldData.RootDnaId = profile.RootDnaId; - oldData.Child1DnaId = profile.Child1DnaId; - oldData.Child2DnaId = profile.Child2DnaId; - oldData.Child3DnaId = profile.Child3DnaId; - oldData.Child4DnaId = profile.Child4DnaId; + // oldData.ProfileId = req.ProfileId; + // oldData.Prefix = profile.Prefix; + // oldData.FirstName = profile.FirstName; + // oldData.LastName = profile.LastName; + // oldData.RootDnaId = profile.RootDnaId; + // oldData.Child1DnaId = profile.Child1DnaId; + // oldData.Child2DnaId = profile.Child2DnaId; + // oldData.Child3DnaId = profile.Child3DnaId; + // oldData.Child4DnaId = profile.Child4DnaId; oldData.LastUpdateUserId = ""; - oldData.LastUpdateFullName = FullName ?? ""; + oldData.LastUpdateFullName = "System"; oldData.LastUpdatedAt = DateTime.Now; await _leaveBeginningRepository.UpdateAsync(oldData); diff --git a/BMA.EHR.Leave/DTOs/LeaveBeginnings/EditLeaveBeginningDto.cs b/BMA.EHR.Leave/DTOs/LeaveBeginnings/EditLeaveBeginningDto.cs index d4a2661f..fe0c433f 100644 --- a/BMA.EHR.Leave/DTOs/LeaveBeginnings/EditLeaveBeginningDto.cs +++ b/BMA.EHR.Leave/DTOs/LeaveBeginnings/EditLeaveBeginningDto.cs @@ -29,4 +29,20 @@ namespace BMA.EHR.Leave.Service.DTOs.LeaveBeginnings [Comment("จำนวนครั้งที่ลายกมา")] public int BeginningLeaveCount { get; set; } = 0; } + + + public class ScheduleEditLeaveBeginningDto + { + [Required] + public Guid ProfileId { get; set; } = Guid.Empty; + + [Required] + public Guid LeaveTypeId { get; set; } = Guid.Empty; + + [Required, Comment("ปีงบประมาณ")] + public int LeaveYear { get; set; } = 0; + + [Required, Comment("จำนวนวันลายกมา")] + public double LeaveDays { get; set; } = 0.0; + } } From de91fd0fa215d6e883c68a54d2268702832c68c2 Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Wed, 18 Feb 2026 16:47:14 +0700 Subject: [PATCH 107/178] Refactor LeaveBeginningController to restore profile checks and reset leave days to zero --- .../Controllers/LeaveBeginningController.cs | 38 +++++++++---------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/BMA.EHR.Leave/Controllers/LeaveBeginningController.cs b/BMA.EHR.Leave/Controllers/LeaveBeginningController.cs index 28f88302..559a784d 100644 --- a/BMA.EHR.Leave/Controllers/LeaveBeginningController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveBeginningController.cs @@ -504,11 +504,11 @@ namespace BMA.EHR.Leave.Service.Controllers { try { - // var profile = await _userProfileRepository.GetProfileByProfileIdNoAuthAsync(req.ProfileId, AccessToken); - // if(profile == null) - // { - // return Error("ไม่พบข้อมูลข้าราชการหรือลูกจ้าง", StatusCodes.Status404NotFound); - // } + var profile = await _userProfileRepository.GetProfileByProfileIdNoAuthAsync(req.ProfileId, AccessToken); + if(profile == null) + { + return Error("ไม่พบข้อมูลข้าราชการหรือลูกจ้าง", StatusCodes.Status404NotFound); + } // check duplicate var oldData = await _context.LeaveBeginnings.FirstOrDefaultAsync(x => x.ProfileId == req.ProfileId && x.LeaveTypeId == req.LeaveTypeId @@ -525,15 +525,15 @@ namespace BMA.EHR.Leave.Service.Controllers // oldData.BeginningLeaveDays = req.BeginningLeaveDays; // oldData.BeginningLeaveCount = req.BeginningLeaveCount; - // oldData.ProfileId = req.ProfileId; - // oldData.Prefix = profile.Prefix; - // oldData.FirstName = profile.FirstName; - // oldData.LastName = profile.LastName; - // oldData.RootDnaId = profile.RootDnaId; - // oldData.Child1DnaId = profile.Child1DnaId; - // oldData.Child2DnaId = profile.Child2DnaId; - // oldData.Child3DnaId = profile.Child3DnaId; - // oldData.Child4DnaId = profile.Child4DnaId; + oldData.ProfileId = req.ProfileId; + oldData.Prefix = profile.Prefix; + oldData.FirstName = profile.FirstName; + oldData.LastName = profile.LastName; + oldData.RootDnaId = profile.RootDnaId; + oldData.Child1DnaId = profile.Child1DnaId; + oldData.Child2DnaId = profile.Child2DnaId; + oldData.Child3DnaId = profile.Child3DnaId; + oldData.Child4DnaId = profile.Child4DnaId; oldData.LastUpdateUserId = ""; oldData.LastUpdateFullName = "System"; @@ -547,10 +547,10 @@ namespace BMA.EHR.Leave.Service.Controllers leaveBeginning.LeaveTypeId = req.LeaveTypeId; leaveBeginning.LeaveYear = req.LeaveYear; leaveBeginning.LeaveDays = req.LeaveDays; - leaveBeginning.LeaveDaysUsed = req.LeaveDaysUsed; - leaveBeginning.LeaveCount = req.LeaveCount; - leaveBeginning.BeginningLeaveDays = req.BeginningLeaveDays; - leaveBeginning.BeginningLeaveCount = req.BeginningLeaveCount; + leaveBeginning.LeaveDaysUsed = 0; + leaveBeginning.LeaveCount = 0; + leaveBeginning.BeginningLeaveDays = 0; + leaveBeginning.BeginningLeaveCount = 0; leaveBeginning.ProfileId = req.ProfileId; leaveBeginning.Prefix = profile.Prefix; @@ -564,7 +564,7 @@ namespace BMA.EHR.Leave.Service.Controllers leaveBeginning.Child4DnaId = profile.Child4DnaId; leaveBeginning.CreatedUserId = ""; - leaveBeginning.CreatedFullName = FullName ?? ""; + leaveBeginning.CreatedFullName = "System"; leaveBeginning.CreatedAt = DateTime.Now; await _leaveBeginningRepository.AddAsync(leaveBeginning); From d70ed254c0f6a3816edfeae47c028d28fbb8ac8d Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Wed, 18 Feb 2026 16:56:48 +0700 Subject: [PATCH 108/178] Enhance LeaveRequestController to restore profile checks and implement officer notification logic #2164 --- .../Controllers/LeaveRequestController.cs | 31 +++++++++++++++---- 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/BMA.EHR.Leave/Controllers/LeaveRequestController.cs b/BMA.EHR.Leave/Controllers/LeaveRequestController.cs index 6580dd53..a8a5e9b4 100644 --- a/BMA.EHR.Leave/Controllers/LeaveRequestController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveRequestController.cs @@ -2323,12 +2323,31 @@ namespace BMA.EHR.Leave.Service.Controllers await _leaveRequestRepository.SendToOfficerAsync(id); // Remove Workflow Integration - // var userId = UserId == null ? Guid.Empty : Guid.Parse(UserId); - // var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(userId, AccessToken); - // if (profile == null) - // { - // return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound); - // } + var userId = UserId == null ? Guid.Empty : Guid.Parse(UserId); + var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(userId, AccessToken); + if (profile == null) + { + return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound); + } + + // Get Officer List + var officers = await _userProfileRepository.GetOCStaffAsync(profile.Id, AccessToken); + if(officers != null && officers.Count > 0) + { + foreach (var officer in officers) + { + // Send Notification + var noti = new Notification + { + Body = $"มีคำร้องขอลาจาก {profile.Prefix}{profile.FirstName} {profile.LastName} รอรับการอนุมัติจากคุณ", + ReceiverUserId = officer.ProfileId, + Type = "", + Payload = $"{URL}/leave/detail/{id}", + }; + _appDbContext.Set().Add(noti); + } + await _appDbContext.SaveChangesAsync(); + } // var baseAPIOrg = _configuration["API"]; // var apiUrlOrg = $"{baseAPIOrg}/org/workflow/add-workflow"; // if (profile.ProfileType == "OFFICER") From ddaa339e9fd3d3ea5d7e6222177a11f7243d7aad Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Wed, 18 Feb 2026 20:24:30 +0700 Subject: [PATCH 109/178] Refactor LeaveRequestController and LeaveReportController to use GetSumApproveLeaveTotalByTypeAndRangeForUser2 method and update fiscal year end date calculation #2305 --- .../LeaveRequests/LeaveRequestRepository.cs | 20 +++++++++++++++++++ .../Controllers/LeaveReportController.cs | 8 ++++---- .../Controllers/LeaveRequestController.cs | 4 ++-- 3 files changed, 26 insertions(+), 6 deletions(-) diff --git a/BMA.EHR.Application/Repositories/Leaves/LeaveRequests/LeaveRequestRepository.cs b/BMA.EHR.Application/Repositories/Leaves/LeaveRequests/LeaveRequestRepository.cs index 57a7fdc4..54107021 100644 --- a/BMA.EHR.Application/Repositories/Leaves/LeaveRequests/LeaveRequestRepository.cs +++ b/BMA.EHR.Application/Repositories/Leaves/LeaveRequests/LeaveRequestRepository.cs @@ -558,6 +558,7 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests var data = await _dbContext.Set().AsQueryable().AsNoTracking() .Include(x => x.Type) .Where(x => x.LeaveStartDate.Date < beforeDate.Date) + //.Where(x => x.CreatedAt < beforeDate) .Where(x => x.KeycloakUserId == keycloakUserId) .Where(x => x.Type.Id == leaveTypeId) .Where(x => x.LeaveStatus == "APPROVE" || x.LeaveStatus == "DELETING") @@ -1835,6 +1836,7 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests .Include(x => x.Type) .Where(x => x.KeycloakUserId == keycloakUserId) .Where(x => x.Type.Id == leaveTypeId) + //.Where(x => x.CreatedAt >= startDate && x.CreatedAt <= endDate) .Where(x => x.LeaveStartDate.Date >= startDate.Date && x.LeaveStartDate.Date <= endDate.Date) .Where(x => x.LeaveStatus == "APPROVE" || x.LeaveStatus == "DELETING") .ToListAsync(); @@ -1845,6 +1847,23 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests return 0; } + public async Task GetSumApproveLeaveTotalByTypeAndRangeForUser2(Guid keycloakUserId, Guid leaveTypeId, DateTime startDate, DateTime endDate) + { + var data = await _dbContext.Set().AsQueryable().AsNoTracking() + .Include(x => x.Type) + .Where(x => x.KeycloakUserId == keycloakUserId) + .Where(x => x.Type.Id == leaveTypeId) + .Where(x => x.CreatedAt.Date >= startDate && x.CreatedAt < endDate) + //.Where(x => x.LeaveStartDate.Date >= startDate.Date && x.LeaveStartDate.Date <= endDate.Date) + .Where(x => x.LeaveStatus == "APPROVE" || x.LeaveStatus == "DELETING") + .ToListAsync(); + + if (data.Count > 0) + return data.Sum(x => x.LeaveTotal); + else + return 0; + } + public async Task GetCountApproveLeaveByTypeAndRangeForUser(Guid keycloakUserId, Guid leaveTypeId, DateTime startDate, DateTime endDate) { var data = await _dbContext.Set().AsQueryable().AsNoTracking() @@ -1852,6 +1871,7 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests .Where(x => x.KeycloakUserId == keycloakUserId) .Where(x => x.Type.Id == leaveTypeId) .Where(x => x.LeaveStartDate.Date >= startDate.Date && x.LeaveStartDate.Date <= endDate.Date) + //.Where(x => x.CreatedAt >= startDate && x.CreatedAt <= endDate) .Where(x => x.LeaveStatus == "APPROVE" || x.LeaveStatus == "DELETING") .ToListAsync(); diff --git a/BMA.EHR.Leave/Controllers/LeaveReportController.cs b/BMA.EHR.Leave/Controllers/LeaveReportController.cs index 8c3de6c0..96ff61ee 100644 --- a/BMA.EHR.Leave/Controllers/LeaveReportController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveReportController.cs @@ -156,14 +156,14 @@ namespace BMA.EHR.Leave.Service.Controllers data.Type.Id, data.LeaveStartDate.Date); var startFiscalYear = new DateTime(data.LeaveStartDate.Year - 1, 10, 1); - var endFiscalYear = data.LeaveStartDate.Date.AddDays(-1); // นับจากวันที่ยื่นลา + var endFiscalYear = data.CreatedAt; var thisYear = data.LeaveStartDate.Year; var toDay = data.LeaveStartDate.Date; if (toDay >= new DateTime(toDay.Year, 10, 1) && toDay <= new DateTime(toDay.Year, 12, 31)) thisYear = thisYear + 1; var leaveData = await _leaveBeginningRepository.GetByYearAndTypeIdForUser2Async(thisYear, data.Type.Id, data.KeycloakUserId); - var sumLeave = await _leaveRequestRepository.GetSumApproveLeaveTotalByTypeAndRangeForUser(data.KeycloakUserId, data.Type.Id, startFiscalYear, endFiscalYear); + var sumLeave = await _leaveRequestRepository.GetSumApproveLeaveTotalByTypeAndRangeForUser2(data.KeycloakUserId, data.Type.Id, startFiscalYear, endFiscalYear); if (leaveData != null) { sumLeave += leaveData.BeginningLeaveDays; @@ -331,7 +331,7 @@ namespace BMA.EHR.Leave.Service.Controllers var fullName = $"{profile!.Prefix}{profile!.FirstName} {profile!.LastName}"; var startFiscalYear = new DateTime(data.LeaveStartDate.Year - 1, 10, 1); - var endFiscalYear = data.LeaveStartDate.Date.AddDays(-1); // นับจากวันที่ยื่นลา + var endFiscalYear = data.CreatedAt; var thisYear = data.LeaveStartDate.Year; var toDay = data.LeaveStartDate.Date; @@ -345,7 +345,7 @@ namespace BMA.EHR.Leave.Service.Controllers //var sumLeave = await _leaveRequestRepository.GetSumApproveLeaveTotalByTypeAndRangeForUser(data.KeycloakUserId, data.Type.Id, startFiscalYear, endFiscalYear); - var sumLeave = await _leaveRequestRepository.GetSumApproveLeaveTotalByTypeAndRangeForUser(data.KeycloakUserId, data.Type.Id, startFiscalYear, endFiscalYear); + var sumLeave = await _leaveRequestRepository.GetSumApproveLeaveTotalByTypeAndRangeForUser2(data.KeycloakUserId, data.Type.Id, startFiscalYear, endFiscalYear); if (leaveData != null) { sumLeave += leaveData.BeginningLeaveDays; diff --git a/BMA.EHR.Leave/Controllers/LeaveRequestController.cs b/BMA.EHR.Leave/Controllers/LeaveRequestController.cs index a8a5e9b4..926842e4 100644 --- a/BMA.EHR.Leave/Controllers/LeaveRequestController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveRequestController.cs @@ -2526,8 +2526,8 @@ namespace BMA.EHR.Leave.Service.Controllers var leaveData = await _leaveBeginningRepository.GetByYearAndTypeIdForUser2Async(thisYear, rawData.Type.Id, rawData.KeycloakUserId); var startFiscalYear = new DateTime(rawData.LeaveStartDate.Year - 1, 10, 1); - var endFiscalYear = rawData.LeaveStartDate.Date.AddDays(-1); // นับจากวันที่ยื่นลา - var leaveSummary = await _leaveRequestRepository.GetSumApproveLeaveTotalByTypeAndRangeForUser(rawData.KeycloakUserId, rawData.Type.Id, startFiscalYear, endFiscalYear); + var endFiscalYear = rawData.CreatedAt; + var leaveSummary = await _leaveRequestRepository.GetSumApproveLeaveTotalByTypeAndRangeForUser2(rawData.KeycloakUserId, rawData.Type.Id, startFiscalYear, endFiscalYear); //var leaveSummary = leaveData == null ? 0.0 : leaveData.LeaveDaysUsed; if (leaveData != null) leaveSummary += leaveData.BeginningLeaveDays; From c42aaa38f649c4e5ac279e31a00efa6b444936ab Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Thu, 19 Feb 2026 10:11:39 +0700 Subject: [PATCH 110/178] Fix API path in UserProfileRepository to remove redundant versioning --- BMA.EHR.Application/Repositories/UserProfileRepository.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BMA.EHR.Application/Repositories/UserProfileRepository.cs b/BMA.EHR.Application/Repositories/UserProfileRepository.cs index 23a33870..6ac6b9ed 100644 --- a/BMA.EHR.Application/Repositories/UserProfileRepository.cs +++ b/BMA.EHR.Application/Repositories/UserProfileRepository.cs @@ -346,7 +346,7 @@ namespace BMA.EHR.Application.Repositories { try { - var apiPath = $"{_configuration["API"]}/api/v1/org/unauthorize/profile/{profileId}"; + var apiPath = $"{_configuration["API"]}/org/unauthorize/profile/{profileId}"; var apiKey = _configuration["API_KEY"]; var apiResult = await GetExternalAPIAsync(apiPath, accessToken ?? "", apiKey); From b8df2d402420ee31950ce80a2190378db336c91d Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Thu, 19 Feb 2026 14:06:59 +0700 Subject: [PATCH 111/178] Add NodaTime package and update LeaveRequestController to use LocalDate for date calculations #2324 --- BMA.EHR.Leave/BMA.EHR.Leave.csproj | 1 + .../Controllers/LeaveRequestController.cs | 41 +++++++++++-------- 2 files changed, 25 insertions(+), 17 deletions(-) diff --git a/BMA.EHR.Leave/BMA.EHR.Leave.csproj b/BMA.EHR.Leave/BMA.EHR.Leave.csproj index 83132dd1..28f42590 100644 --- a/BMA.EHR.Leave/BMA.EHR.Leave.csproj +++ b/BMA.EHR.Leave/BMA.EHR.Leave.csproj @@ -45,6 +45,7 @@ + diff --git a/BMA.EHR.Leave/Controllers/LeaveRequestController.cs b/BMA.EHR.Leave/Controllers/LeaveRequestController.cs index 926842e4..6d16e8de 100644 --- a/BMA.EHR.Leave/Controllers/LeaveRequestController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveRequestController.cs @@ -17,6 +17,7 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using Newtonsoft.Json; using Newtonsoft.Json.Linq; +using NodaTime; using Org.BouncyCastle.Asn1.Pkcs; using Sentry; using Swashbuckle.AspNetCore.Annotations; @@ -986,6 +987,13 @@ namespace BMA.EHR.Leave.Service.Controllers // var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(userId, AccessToken); var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(userId, AccessToken); var govAge = (profile?.DateStart?.Date ?? DateTime.Now.Date).DiffDay(DateTime.Now.Date); + var date1Raw = profile?.DateStart?.Date ?? DateTime.Now.Date; + var date1 = new LocalDate(date1Raw.Year, date1Raw.Month, date1Raw.Day); + var date2 = new LocalDate(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day); + Period period = Period.Between(date1, date2); + var govAgeMonth = period.Months; + var govAgeYear = period.Years; + var thisYear = DateTime.Now.Year; var message = string.Empty; @@ -1055,12 +1063,11 @@ namespace BMA.EHR.Leave.Service.Controllers case "LV-002": // fix issue : ระบบลา (ขรก.) >> ลากิจส่วนตัว (กรณียื่นขอลาเกิน 45 วัน/ปี) #829 // fix issue : ระบบลา (ขรก.) >> ลากิจส่วนตัว (กรณีผู้เข้ารับราชการไม่เกิน 1 ปี ยื่นขอลาเกิน 15 วัน/ปี) #831 - if (govAge <= 365) + if (govAgeYear <= 1) { isLeave = (totalDay - (sumWorkDay + sumWeekend) + approveDay) <= 15; if (!isLeave) message = "จำนวนวันลาเกินที่กำหนด"; } - else { isLeave = (totalDay - (sumWorkDay + sumWeekend) + approveDay) <= 45; @@ -1084,13 +1091,14 @@ namespace BMA.EHR.Leave.Service.Controllers // fix issue : ระบบลา (ขรก.) >> ลาพักผ่อน (กรณีรับราชการไม่ถึง 6 เดือน) #838 //var leavePrevYear = (await _leaveRequestRepository.GetSumApproveLeaveAsync(fiscalYear - 1)).Where(x => x.LeaveTypeCode == "LV-005" && x.KeycloakUserId == userId).FirstOrDefault(); //var leavePrevYearRemain = 10 - (leavePrevYear == null ? 0 : leavePrevYear.SumLeaveDay); // หายอดวันลาที่เหลือของปีก่อน - if (profile.IsProbation! == true) - { - isLeave = false; - if (!isLeave) message = "ยังอยู่ในช่วงทดลองปฏิบัติราชการ ไม่สามารถลาพักผ่อนได้"; - } - //else if (govAge >= 180) - else + // if (profile.IsProbation! == true) + // { + // isLeave = false; + // if (!isLeave) message = "ยังอยู่ในช่วงทดลองปฏิบัติราชการ ไม่สามารถลาพักผ่อนได้"; + // } + // else + if (govAgeMonth >= 6) + //else { isLeave = (totalDay - (sumWorkDay + sumWeekend) + approveDay) <= (limitDay); if (!isLeave) message = "จำนวนวันลาเกินที่กำหนด"; @@ -1112,17 +1120,16 @@ namespace BMA.EHR.Leave.Service.Controllers // isLeave = (totalDay - (sumWorkDay + sumWeekend) + sumApproveLeave) <= (10 + leavePrevYearRemain); // if (!isLeave) message = "จำนวนวันลาเกินที่กำหนด"; // } - - // else - // { - // isLeave = false; - // if (!isLeave) message = "อายุราชการน้อยกว่า 6 เดือนหรือ 180 วัน"; - // } + else + { + isLeave = false; + if (!isLeave) message = "อายุราชการน้อยกว่า 6 เดือนหรือ 180 วัน"; + } break; case "LV-006": // fix issue : ระบบลา(ขรก.) >> ลาอุปสมบทหรือการลาประกอบพิธีฮัจย์ฯ(กรณีรับราชการน้อยกว่า 1 ปี) #840 - if (govAge < 365) + if (govAgeYear < 1) { isLeave = false; if (!isLeave) message = "อายุราชการน้อยกว่า 1 ปีหรือ 365 วัน"; @@ -1148,7 +1155,7 @@ namespace BMA.EHR.Leave.Service.Controllers break; case "LV-008": case "LV-009": - isLeave = govAge >= 365; + isLeave = govAgeYear >= 1; if (!isLeave) message = "อายุราชการน้อยกว่า 1 ปีหรือ 365 วัน"; break; case "LV-010": From d74830841903432a21c59ba8e873817bfda3198f Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Thu, 19 Feb 2026 15:07:04 +0700 Subject: [PATCH 112/178] Enhance LeaveController to implement check-out logic and status validation based on last check-in record --- BMA.EHR.Leave/Controllers/LeaveController.cs | 128 +++++++++++++++++++ 1 file changed, 128 insertions(+) diff --git a/BMA.EHR.Leave/Controllers/LeaveController.cs b/BMA.EHR.Leave/Controllers/LeaveController.cs index 665b209c..08dcb575 100644 --- a/BMA.EHR.Leave/Controllers/LeaveController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveController.cs @@ -957,15 +957,24 @@ namespace BMA.EHR.Leave.Service.Controllers return Error(GlobalMessages.NoFileToUpload, StatusCodes.Status400BadRequest); } + // last check-in record + var lastCheckIn = await _userTimeStampRepository.GetLastRecord(userId); var check_status = data.CheckInId == null ? "check-in-picture" : "check-out-picture"; + var check_out_status = "check-out-picture"; var fileName = $"{_bucketName}/{userId}/{currentDate.ToString("dd-MM-yyyy")}/{check_status}/{data.CheckInFileName}"; + var fileNameCheckOut = $"{_bucketName}/{userId}/{currentDate.ToString("dd-MM-yyyy")}/{check_out_status}/{data.CheckInFileName}"; using (var ms = new MemoryStream(data.CheckInFileBytes ?? new byte[0])) { try { await _minIOService.UploadFileAsync(fileName, ms); + if (lastCheckIn != null && lastCheckIn.CheckOut == null) + { + // ยังไม่เคย check-out มาก่อน หรือ check-out เป็น null ให้ใช้ชื่อไฟล์แบบ check-out + await _minIOService.UploadFileAsync(fileNameCheckOut, ms); + } } catch (Exception ex) { @@ -1015,6 +1024,124 @@ namespace BMA.EHR.Leave.Service.Controllers // create check in object if (data.CheckInId == null) { + if (lastCheckIn != null && lastCheckIn.CheckOut == null) + { + var checkout = await _userTimeStampRepository.GetByIdAsync(lastCheckIn!.Id); + if(checkout != null) + { + var currentCheckInProcess = await _processUserTimeStampRepository.GetTimestampByDateAsync(userId, checkout.CheckIn.Date); + var checkout_process = await _processUserTimeStampRepository.GetByIdAsync(currentCheckInProcess!.Id); + var endTime1 = ""; + var startTime1 = ""; + var endTimeMorning1 = ""; + if (!data.IsLocation && data.LocationName == "ไปประชุม / อบรม / สัมมนา") + { + startTime1 = "13:00"; + endTime1 = "14:30"; + endTimeMorning1 = "12:00"; + } + else + { + endTime1 = duty.EndTimeAfternoon; + startTime1 = duty.StartTimeAfternoon; + endTimeMorning1 = duty.EndTimeMorning; + } + + string checkOutStatus = "NORMAL"; + var leaveReq1 = await _leaveRequestRepository.GetLeavePeriodAsync(userId, currentDate.Date); + if (leaveReq1 != null) + { + var leaveRange = leaveReq1.LeaveRangeEnd == null ? "" : leaveReq1.LeaveRangeEnd.ToUpper(); + if (leaveRange == "AFTERNOON" || leaveRange == "ALL") + { + if(DateTime.Parse(currentDate.ToString("yyyy-MM-dd HH:mm")) < + DateTime.Parse($"{currentDate.ToString("yyyy-MM-dd")} {endTimeMorning1}")) + checkOutStatus = "ABSENT"; + else + checkOutStatus = "NORMAL"; + } + else + { + // fix issue : SIT ระบบบันทึกเวลาปฏิบัติงาน>>ลงเวลาเข้า-ออกงาน (กรณีลงเวลาออกอีกวัน) #921 + var currentDateTime = DateTime.Parse(currentDate.ToString("yyyy-MM-dd HH:mm")); + var dutyEndTimeAfternoon = DateTime.Parse($"{checkout.CheckIn.ToString("yyyy-MM-dd")} {endTime1}"); + var dutyEndTimeMorning = DateTime.Parse($"{checkout.CheckIn.ToString("yyyy-MM-dd")} {endTimeMorning1}"); + + + if(currentDateTime.Date > checkout.CheckIn.Date) + { + // ถ้า check-out เป็นวันถัดไป สถานะปกติเสมอ + checkOutStatus = "NORMAL"; + } + else + { + // ถ้า check-out เป็นวันเดียวกับ check-in + // ตรวจสอบเวลาว่าสิ้นสุดก่อนบ่ายหรือไม่ + if(currentDateTime < dutyEndTimeMorning) // ถ้าออกก่อนเวลาสิ้นสุดตอนเช้า ขาดราชการ + { + checkOutStatus = "ABSENT"; + } + else if(currentDateTime >= dutyEndTimeAfternoon) // ถ้าออกหลังเวลาสิ้นสุดตอนบ่าย ปกติ + { + checkOutStatus = "NORMAL"; + } + else + { + checkOutStatus = "ABSENT"; + } + } + } + } + else + { + // fix issue : SIT ระบบบันทึกเวลาปฏิบัติงาน>>ลงเวลาเข้า-ออกงาน (กรณีลงเวลาออกอีกวัน) #921 + var currentDateTime = DateTime.Parse(currentDate.ToString("yyyy-MM-dd HH:mm")); + var dutyEndTimeAfternoon = DateTime.Parse($"{checkout.CheckIn.ToString("yyyy-MM-dd")} {endTime1}"); + var dutyEndTimeMorning = DateTime.Parse($"{checkout.CheckIn.ToString("yyyy-MM-dd")} {endTimeMorning1}"); + + + if(currentDateTime.Date > checkout.CheckIn.Date) + { + // ถ้า check-out เป็นวันถัดไป สถานะปกติเสมอ + checkOutStatus = "NORMAL"; + } + else + { + // ถ้า check-out เป็นวันเดียวกับ check-in + // ตรวจสอบเวลาว่าสิ้นสุดก่อนบ่ายหรือไม่ + if(currentDateTime < dutyEndTimeMorning) // ถ้าออกก่อนเวลาสิ้นสุดตอนเช้า ขาดราชการ + { + checkOutStatus = "ABSENT"; + } + else if(currentDateTime >= dutyEndTimeAfternoon) // ถ้าออกหลังเวลาสิ้นสุดตอนบ่าย ปกติ + { + checkOutStatus = "NORMAL"; + } + else + { + checkOutStatus = "ABSENT"; + } + } + + } + + if (checkout_process != null) + { + checkout_process.CheckOutLat = data.Lat; + checkout_process.CheckOutLon = data.Lon; + checkout_process.IsLocationCheckOut = data.IsLocation; + checkout_process.CheckOutLocationName = data.LocationName; + checkout_process.CheckOutPOI = data.POI; + checkout_process.CheckOutRemark = data.Remark; + checkout_process.CheckOutImageUrl = fileNameCheckOut; + checkout_process.CheckOut = currentDate; + checkout_process.CheckOutStatus = checkOutStatus; + + await _processUserTimeStampRepository.UpdateAsync(checkout_process); + } + } + } + // validate duplicate check in var currentCheckIn = await _userTimeStampRepository.GetTimestampByDateAsync(userId, currentDate); @@ -1167,6 +1294,7 @@ namespace BMA.EHR.Leave.Service.Controllers else { var checkout = await _userTimeStampRepository.GetByIdAsync(data.CheckInId.Value); + //var currentCheckIn = await _userTimeStampRepository.GetTimestampByDateAsync(userId, currentDate); if (checkout == null) { From 65feb994ee5852e8f77c4655686d5827020973a3 Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Thu, 19 Feb 2026 15:10:44 +0700 Subject: [PATCH 113/178] Add GetLastLeaveRequestByTypeForUserAsync2 method and update LeaveReportController to use new method for fetching last leave request #2305 --- .../LeaveRequests/LeaveRequestRepository.cs | 16 ++++++++++++++++ .../Controllers/LeaveReportController.cs | 4 ++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/BMA.EHR.Application/Repositories/Leaves/LeaveRequests/LeaveRequestRepository.cs b/BMA.EHR.Application/Repositories/Leaves/LeaveRequests/LeaveRequestRepository.cs index 54107021..130dba3b 100644 --- a/BMA.EHR.Application/Repositories/Leaves/LeaveRequests/LeaveRequestRepository.cs +++ b/BMA.EHR.Application/Repositories/Leaves/LeaveRequests/LeaveRequestRepository.cs @@ -569,6 +569,22 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests return data; } + public async Task GetLastLeaveRequestByTypeForUserAsync2(Guid keycloakUserId, Guid leaveTypeId, DateTime beforeDate) + { + var data = await _dbContext.Set().AsQueryable().AsNoTracking() + .Include(x => x.Type) + //.Where(x => x.LeaveStartDate.Date < beforeDate.Date) + .Where(x => x.CreatedAt < beforeDate) + .Where(x => x.KeycloakUserId == keycloakUserId) + .Where(x => x.Type.Id == leaveTypeId) + .Where(x => x.LeaveStatus == "APPROVE" || x.LeaveStatus == "DELETING") + //.Where(x => x.LeaveStatus != "REJECT" && x.LeaveStatus != "DELETE") + .OrderByDescending(x => x.LeaveStartDate.Date) + .FirstOrDefaultAsync(); + + return data; + } + public async Task> GetCancelLeaveRequestForAdminAsync(int year, Guid type, string status, string role, string? nodeId, int? node) { var rawData = _dbContext.Set().AsNoTracking() diff --git a/BMA.EHR.Leave/Controllers/LeaveReportController.cs b/BMA.EHR.Leave/Controllers/LeaveReportController.cs index 96ff61ee..2b555e73 100644 --- a/BMA.EHR.Leave/Controllers/LeaveReportController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveReportController.cs @@ -152,8 +152,8 @@ namespace BMA.EHR.Leave.Service.Controllers var lastLeaveRequest = - await _leaveRequestRepository.GetLastLeaveRequestByTypeForUserAsync(data.KeycloakUserId, - data.Type.Id, data.LeaveStartDate.Date); + await _leaveRequestRepository.GetLastLeaveRequestByTypeForUserAsync2(data.KeycloakUserId, + data.Type.Id, data.CreatedAt); var startFiscalYear = new DateTime(data.LeaveStartDate.Year - 1, 10, 1); var endFiscalYear = data.CreatedAt; From 869defcc7ea18bd65e8ff58d15a4a303d0f06528 Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Thu, 19 Feb 2026 15:19:41 +0700 Subject: [PATCH 114/178] Update LeaveRequestRepository to order leave requests by creation date instead of start date #2305 --- .../Repositories/Leaves/LeaveRequests/LeaveRequestRepository.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BMA.EHR.Application/Repositories/Leaves/LeaveRequests/LeaveRequestRepository.cs b/BMA.EHR.Application/Repositories/Leaves/LeaveRequests/LeaveRequestRepository.cs index 130dba3b..10e7c927 100644 --- a/BMA.EHR.Application/Repositories/Leaves/LeaveRequests/LeaveRequestRepository.cs +++ b/BMA.EHR.Application/Repositories/Leaves/LeaveRequests/LeaveRequestRepository.cs @@ -579,7 +579,7 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests .Where(x => x.Type.Id == leaveTypeId) .Where(x => x.LeaveStatus == "APPROVE" || x.LeaveStatus == "DELETING") //.Where(x => x.LeaveStatus != "REJECT" && x.LeaveStatus != "DELETE") - .OrderByDescending(x => x.LeaveStartDate.Date) + .OrderByDescending(x => x.CreatedAt) .FirstOrDefaultAsync(); return data; From 256da24caf188d695a5498e96a08a42133b06fd3 Mon Sep 17 00:00:00 2001 From: harid Date: Thu, 19 Feb 2026 17:01:44 +0700 Subject: [PATCH 115/178] =?UTF-8?q?=E0=B9=81=E0=B8=81=E0=B9=89=E0=B9=84?= =?UTF-8?q?=E0=B8=82=E0=B8=AA=E0=B8=B4=E0=B8=97=E0=B8=98=E0=B8=B4=E0=B9=8C?= =?UTF-8?q?=20PARENT=20=E0=B9=83=E0=B8=AB=E0=B9=89=E0=B9=80=E0=B8=AB?= =?UTF-8?q?=E0=B9=87=E0=B8=99=E0=B8=82=E0=B9=89=E0=B8=AD=E0=B8=A1=E0=B8=B9?= =?UTF-8?q?=E0=B8=A5=E0=B8=97=E0=B8=B1=E0=B9=89=E0=B8=87=E0=B8=AB=E0=B8=A1?= =?UTF-8?q?=E0=B8=94=E0=B8=97=E0=B8=B8=E0=B8=81=E0=B8=AB=E0=B8=99=E0=B9=88?= =?UTF-8?q?=E0=B8=A7=E0=B8=A2=E0=B8=87=E0=B8=B2=E0=B8=99=20#54?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../LeaveRequests/LeaveRequestRepository.cs | 38 +++++++++---------- .../AdditionalCheckRequestRepository.cs | 10 ++--- .../ProcessUserTimeStampRepository.cs | 20 +++++----- .../TimeAttendants/UserTimeStampRepository.cs | 12 +++--- .../DisciplineComplaint_AppealController.cs | 12 +++--- .../DisciplineSuspendController.cs | 6 +-- .../Controllers/InsigniaManageController.cs | 24 ++++++------ .../Controllers/LeaveBeginningController.cs | 12 +++--- BMA.EHR.Leave/Controllers/LeaveController.cs | 10 ++--- .../Controllers/LeaveReportController.cs | 10 ++--- .../Controllers/LeaveRequestController.cs | 6 +-- .../PlacementAppointmentController.cs | 12 +++--- .../PlacementAppointmentEmployeeController.cs | 12 +++--- .../Controllers/PlacementOfficerController.cs | 12 +++--- .../Controllers/PlacementReceiveController.cs | 12 +++--- .../PlacementRepatriationController.cs | 12 +++--- .../PlacementTransferController.cs | 12 +++--- .../RetirementDeceasedController.cs | 12 +++--- .../Controllers/RetirementOtherController.cs | 12 +++--- .../Controllers/RetirementOutController.cs | 12 +++--- .../Controllers/RetirementResignController.cs | 36 +++++++++--------- .../RetirementResignEmployeeController.cs | 36 +++++++++--------- 22 files changed, 170 insertions(+), 170 deletions(-) diff --git a/BMA.EHR.Application/Repositories/Leaves/LeaveRequests/LeaveRequestRepository.cs b/BMA.EHR.Application/Repositories/Leaves/LeaveRequests/LeaveRequestRepository.cs index 10e7c927..2caecc9f 100644 --- a/BMA.EHR.Application/Repositories/Leaves/LeaveRequests/LeaveRequestRepository.cs +++ b/BMA.EHR.Application/Repositories/Leaves/LeaveRequests/LeaveRequestRepository.cs @@ -307,11 +307,11 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests rawData = rawData .Where(x => x.RootDnaId == Guid.Parse(nodeId!)); } - else if (role == "PARENT") - { - rawData = rawData - .Where(x => x.RootDnaId == Guid.Parse(nodeId!) && x.Child1DnaId != null); - } + // else if (role == "PARENT") + // { + // rawData = rawData + // .Where(x => x.RootDnaId == Guid.Parse(nodeId!) && x.Child1DnaId != null); + // } else if (role == "NORMAL") { rawData = rawData @@ -421,11 +421,11 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests rawData = rawData .Where(x => x.RootDnaId == Guid.Parse(nodeId!)); } - else if (role == "PARENT") - { - rawData = rawData - .Where(x => x.RootDnaId == Guid.Parse(nodeId!) && x.Child1DnaId != null); - } + // else if (role == "PARENT") + // { + // rawData = rawData + // .Where(x => x.RootDnaId == Guid.Parse(nodeId!) && x.Child1DnaId != null); + // } else if (role == "NORMAL") { rawData = rawData @@ -627,11 +627,11 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests rawData = rawData .Where(x => x.RootDnaId == Guid.Parse(nodeId!)); } - else if (role == "PARENT") - { - rawData = rawData - .Where(x => x.RootDnaId == Guid.Parse(nodeId!) && x.Child1DnaId != null); - } + // else if (role == "PARENT") + // { + // rawData = rawData + // .Where(x => x.RootDnaId == Guid.Parse(nodeId!) && x.Child1DnaId != null); + // } else if (role == "NORMAL") { rawData = rawData @@ -1692,10 +1692,10 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests { data = data.Where(x => x.RootDnaId == Guid.Parse(nodeId)).ToList(); } - else if (role == "PARENT") - { - data = data.Where(x => x.RootDnaId == Guid.Parse(nodeId) && x.Child1DnaId != null).ToList(); - } + // else if (role == "PARENT") + // { + // data = data.Where(x => x.RootDnaId == Guid.Parse(nodeId) && x.Child1DnaId != null).ToList(); + // } else if (role == "NORMAL") { data = data.Where(x => diff --git a/BMA.EHR.Application/Repositories/Leaves/TimeAttendants/AdditionalCheckRequestRepository.cs b/BMA.EHR.Application/Repositories/Leaves/TimeAttendants/AdditionalCheckRequestRepository.cs index 3dca61e1..0544e261 100644 --- a/BMA.EHR.Application/Repositories/Leaves/TimeAttendants/AdditionalCheckRequestRepository.cs +++ b/BMA.EHR.Application/Repositories/Leaves/TimeAttendants/AdditionalCheckRequestRepository.cs @@ -185,11 +185,11 @@ namespace BMA.EHR.Application.Repositories.Leaves.TimeAttendants data = data .Where(x => x.RootDnaId == Guid.Parse(nodeId!)).ToList(); } - else if (role == "PARENT") - { - data = data - .Where(x => x.RootDnaId == Guid.Parse(nodeId!) && x.Child1DnaId != null && x.Child1DnaId != Guid.Empty).ToList(); - } + // else if (role == "PARENT") + // { + // data = data + // .Where(x => x.RootDnaId == Guid.Parse(nodeId!) && x.Child1DnaId != null && x.Child1DnaId != Guid.Empty).ToList(); + // } else if (role == "NORMAL") { data = data.Where(x => diff --git a/BMA.EHR.Application/Repositories/Leaves/TimeAttendants/ProcessUserTimeStampRepository.cs b/BMA.EHR.Application/Repositories/Leaves/TimeAttendants/ProcessUserTimeStampRepository.cs index 64d32a19..c1df8798 100644 --- a/BMA.EHR.Application/Repositories/Leaves/TimeAttendants/ProcessUserTimeStampRepository.cs +++ b/BMA.EHR.Application/Repositories/Leaves/TimeAttendants/ProcessUserTimeStampRepository.cs @@ -172,10 +172,10 @@ namespace BMA.EHR.Application.Repositories.Leaves.TimeAttendants { data = data.Where(x => x.RootDnaId == Guid.Parse(nodeId)).ToList(); } - else if (role == "PARENT") - { - data = data.Where(x => x.RootDnaId == Guid.Parse(nodeId) && x.Child1DnaId != null).ToList(); - } + // else if (role == "PARENT") + // { + // data = data.Where(x => x.RootDnaId == Guid.Parse(nodeId) && x.Child1DnaId != null).ToList(); + // } else if (role == "NORMAL") { data = data.Where(x => @@ -288,12 +288,12 @@ namespace BMA.EHR.Application.Repositories.Leaves.TimeAttendants .Where(x => x.RootDnaId == Guid.Parse(nodeId!)) .ToList(); } - else if (role == "PARENT") - { - data = data - .Where(x => x.RootDnaId == Guid.Parse(nodeId!) && x.Child1DnaId != null) - .ToList(); - } + // else if (role == "PARENT") + // { + // data = data + // .Where(x => x.RootDnaId == Guid.Parse(nodeId!) && x.Child1DnaId != null) + // .ToList(); + // } else if (role == "NORMAL") { data = data.Where(x => diff --git a/BMA.EHR.Application/Repositories/Leaves/TimeAttendants/UserTimeStampRepository.cs b/BMA.EHR.Application/Repositories/Leaves/TimeAttendants/UserTimeStampRepository.cs index 2598dace..6dffcb6c 100644 --- a/BMA.EHR.Application/Repositories/Leaves/TimeAttendants/UserTimeStampRepository.cs +++ b/BMA.EHR.Application/Repositories/Leaves/TimeAttendants/UserTimeStampRepository.cs @@ -140,12 +140,12 @@ namespace BMA.EHR.Application.Repositories.Leaves.TimeAttendants .Where(x => x.RootDnaId == Guid.Parse(nodeId!)) .ToList(); } - else if (role == "PARENT") - { - data = data - .Where(x => x.RootDnaId == Guid.Parse(nodeId!) && x.Child1DnaId != null) - .ToList(); - } + // else if (role == "PARENT") + // { + // data = data + // .Where(x => x.RootDnaId == Guid.Parse(nodeId!) && x.Child1DnaId != null) + // .ToList(); + // } else if (role == "NORMAL") { data = data.Where(x => diff --git a/BMA.EHR.Discipline.Service/Controllers/DisciplineComplaint_AppealController.cs b/BMA.EHR.Discipline.Service/Controllers/DisciplineComplaint_AppealController.cs index 21204d6b..619756c2 100644 --- a/BMA.EHR.Discipline.Service/Controllers/DisciplineComplaint_AppealController.cs +++ b/BMA.EHR.Discipline.Service/Controllers/DisciplineComplaint_AppealController.cs @@ -786,7 +786,7 @@ namespace BMA.EHR.DisciplineComplaint_Appeal.Service.Controllers ? profileAdmin?.RootDnaId : ""; } - else if (role == "ROOT" || role == "PARENT") + else if (role == "ROOT" /*|| role == "PARENT"*/) { nodeId = profileAdmin?.RootDnaId; } @@ -826,11 +826,11 @@ namespace BMA.EHR.DisciplineComplaint_Appeal.Service.Controllers data_search = data_search .Where(x => x.rootDnaId == nodeId).ToList(); } - else if (role == "PARENT") - { - data_search = data_search - .Where(x => x.rootDnaId == nodeId && x.child1DnaId != null).ToList(); - } + // else if (role == "PARENT") + // { + // data_search = data_search + // .Where(x => x.rootDnaId == nodeId && x.child1DnaId != null).ToList(); + // } else if (role == "NORMAL") { data_search = data_search.Where(x => diff --git a/BMA.EHR.Discipline.Service/Controllers/DisciplineSuspendController.cs b/BMA.EHR.Discipline.Service/Controllers/DisciplineSuspendController.cs index 732c518b..39b5f06a 100644 --- a/BMA.EHR.Discipline.Service/Controllers/DisciplineSuspendController.cs +++ b/BMA.EHR.Discipline.Service/Controllers/DisciplineSuspendController.cs @@ -102,7 +102,7 @@ namespace BMA.EHR.DisciplineSuspend.Service.Controllers ? profileAdmin?.RootDnaId : ""; } - else if (role == "ROOT" || role == "PARENT") + else if (role == "ROOT" /*|| role == "PARENT"*/) { nodeId = profileAdmin?.RootDnaId; } @@ -143,8 +143,8 @@ namespace BMA.EHR.DisciplineSuspend.Service.Controllers ? true : role == "ROOT" ? x.rootDnaId == nodeId - : role == "PARENT" - ? x.rootDnaId == nodeId && x.child1DnaId != null + // : role == "PARENT" + // ? x.rootDnaId == nodeId && x.child1DnaId != null : role == "CHILD" ? ( profileAdmin.Node == 4 ? x.child4DnaId == nodeId : diff --git a/BMA.EHR.Insignia/Controllers/InsigniaManageController.cs b/BMA.EHR.Insignia/Controllers/InsigniaManageController.cs index 80a8ef31..7d3c9ae4 100644 --- a/BMA.EHR.Insignia/Controllers/InsigniaManageController.cs +++ b/BMA.EHR.Insignia/Controllers/InsigniaManageController.cs @@ -639,7 +639,7 @@ namespace BMA.EHR.Insignia.Service.Controllers ? profileAdmin?.RootDnaId : ""; } - else if (role == "ROOT" || role == "PARENT") + else if (role == "ROOT" /*|| role == "PARENT"*/) { nodeId = profileAdmin?.RootDnaId; } @@ -724,11 +724,11 @@ namespace BMA.EHR.Insignia.Service.Controllers rawData = rawData .Where(x => x.RootDnaId == Guid.Parse(nodeId!)).ToList(); } - else if (role == "PARENT") - { - rawData = rawData - .Where(x => x.RootDnaId == Guid.Parse(nodeId!) && x.Child1DnaId != null).ToList(); - } + // else if (role == "PARENT") + // { + // rawData = rawData + // .Where(x => x.RootDnaId == Guid.Parse(nodeId!) && x.Child1DnaId != null).ToList(); + // } else if (role == "NORMAL") { rawData = rawData.Where(x => @@ -943,7 +943,7 @@ namespace BMA.EHR.Insignia.Service.Controllers ? profileAdmin?.RootDnaId : ""; } - else if (role == "ROOT" || role == "PARENT") + else if (role == "ROOT" /*|| role == "PARENT"*/) { nodeId = profileAdmin?.RootDnaId; } @@ -1026,11 +1026,11 @@ namespace BMA.EHR.Insignia.Service.Controllers rawData = rawData .Where(x => x.RootDnaId == Guid.Parse(nodeId!)).ToList(); } - else if (role == "PARENT") - { - rawData = rawData - .Where(x => x.RootDnaId == Guid.Parse(nodeId!) && x.Child1DnaId != null).ToList(); - } + // else if (role == "PARENT") + // { + // rawData = rawData + // .Where(x => x.RootDnaId == Guid.Parse(nodeId!) && x.Child1DnaId != null).ToList(); + // } else if (role == "NORMAL") { rawData = rawData.Where(x => diff --git a/BMA.EHR.Leave/Controllers/LeaveBeginningController.cs b/BMA.EHR.Leave/Controllers/LeaveBeginningController.cs index 559a784d..f3b661cf 100644 --- a/BMA.EHR.Leave/Controllers/LeaveBeginningController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveBeginningController.cs @@ -141,7 +141,7 @@ namespace BMA.EHR.Leave.Service.Controllers ? profileAdmin?.RootDnaId : ""; } - else if (role == "ROOT" || role == "PARENT") + else if (role == "ROOT" /*|| role == "PARENT"*/) { nodeId = profileAdmin?.RootDnaId; } @@ -166,11 +166,11 @@ namespace BMA.EHR.Leave.Service.Controllers resData = resData .Where(x => x.RootDnaId == Guid.Parse(nodeId!)).ToList(); } - else if (role == "PARENT") - { - resData = resData - .Where(x => x.RootDnaId == Guid.Parse(nodeId!) && x.Child1DnaId != null).ToList(); - } + // else if (role == "PARENT") + // { + // resData = resData + // .Where(x => x.RootDnaId == Guid.Parse(nodeId!) && x.Child1DnaId != null).ToList(); + // } else if (role == "NORMAL") { resData = resData diff --git a/BMA.EHR.Leave/Controllers/LeaveController.cs b/BMA.EHR.Leave/Controllers/LeaveController.cs index 08dcb575..267abee9 100644 --- a/BMA.EHR.Leave/Controllers/LeaveController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveController.cs @@ -1842,7 +1842,7 @@ namespace BMA.EHR.Leave.Service.Controllers ? profileAdmin?.RootDnaId : ""; } - else if (role == "ROOT" || role == "PARENT") + else if (role == "ROOT" /*|| role == "PARENT"*/) { nodeId = profileAdmin?.RootDnaId; } @@ -2151,7 +2151,7 @@ namespace BMA.EHR.Leave.Service.Controllers ? profileAdmin?.RootDnaId : ""; } - else if (role == "ROOT" || role == "PARENT") + else if (role == "ROOT" /*|| role == "PARENT"*/) { nodeId = profileAdmin?.RootDnaId; } @@ -2397,7 +2397,7 @@ namespace BMA.EHR.Leave.Service.Controllers ? profileAdmin?.RootDnaId : ""; } - else if (role == "ROOT" || role == "PARENT") + else if (role == "ROOT" /*|| role == "PARENT"*/) { nodeId = profileAdmin?.RootDnaId; } @@ -2652,7 +2652,7 @@ namespace BMA.EHR.Leave.Service.Controllers ? profileAdmin?.RootDnaId : ""; } - else if (role == "ROOT" || role == "PARENT") + else if (role == "ROOT" /*|| role == "PARENT"*/) { nodeId = profileAdmin?.RootDnaId; } @@ -3077,7 +3077,7 @@ namespace BMA.EHR.Leave.Service.Controllers ? profileAdmin?.RootDnaId : ""; } - else if (role == "ROOT" || role == "PARENT") + else if (role == "ROOT" /*|| role == "PARENT"*/) { nodeId = profileAdmin?.RootDnaId; } diff --git a/BMA.EHR.Leave/Controllers/LeaveReportController.cs b/BMA.EHR.Leave/Controllers/LeaveReportController.cs index 2b555e73..e96fc0a0 100644 --- a/BMA.EHR.Leave/Controllers/LeaveReportController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveReportController.cs @@ -1340,7 +1340,7 @@ namespace BMA.EHR.Leave.Service.Controllers ? profileAdmin?.RootDnaId : ""; } - else if (role == "ROOT" || role == "PARENT") + else if (role == "ROOT" /*|| role == "PARENT"*/) { nodeId = profileAdmin?.RootDnaId; } @@ -1970,7 +1970,7 @@ namespace BMA.EHR.Leave.Service.Controllers ? profileAdmin?.RootDnaId : ""; } - else if (role == "ROOT" || role == "PARENT") + else if (role == "ROOT" /*|| role == "PARENT"*/) { nodeId = profileAdmin?.RootDnaId; } @@ -2312,7 +2312,7 @@ namespace BMA.EHR.Leave.Service.Controllers ? profileAdmin?.RootDnaId : ""; } - else if (role == "ROOT" || role == "PARENT") + else if (role == "ROOT" /*|| role == "PARENT"*/) { nodeId = profileAdmin?.RootDnaId; } @@ -2757,7 +2757,7 @@ namespace BMA.EHR.Leave.Service.Controllers ? profileAdmin?.RootDnaId : ""; } - else if (role == "ROOT" || role == "PARENT") + else if (role == "ROOT" /*|| role == "PARENT"*/) { nodeId = profileAdmin?.RootDnaId; } @@ -2878,7 +2878,7 @@ namespace BMA.EHR.Leave.Service.Controllers ? profileAdmin?.RootDnaId : ""; } - else if (role == "ROOT" || role == "PARENT") + else if (role == "ROOT" /*|| role == "PARENT"*/) { nodeId = profileAdmin?.RootDnaId; } diff --git a/BMA.EHR.Leave/Controllers/LeaveRequestController.cs b/BMA.EHR.Leave/Controllers/LeaveRequestController.cs index 6d16e8de..9af0df96 100644 --- a/BMA.EHR.Leave/Controllers/LeaveRequestController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveRequestController.cs @@ -1359,7 +1359,7 @@ namespace BMA.EHR.Leave.Service.Controllers ? profileAdmin?.RootDnaId : ""; } - else if (role == "ROOT" || role == "PARENT") + else if (role == "ROOT" /*|| role == "PARENT"*/) { nodeId = profileAdmin?.RootDnaId; } @@ -1757,7 +1757,7 @@ namespace BMA.EHR.Leave.Service.Controllers ? profileAdmin?.RootDnaId : ""; } - else if (role == "ROOT" || role == "PARENT") + else if (role == "ROOT" /*|| role == "PARENT"*/) { nodeId = profileAdmin?.RootDnaId; } @@ -1955,7 +1955,7 @@ namespace BMA.EHR.Leave.Service.Controllers ? profileAdmin?.RootDnaId : ""; } - else if (role == "ROOT" || role == "PARENT") + else if (role == "ROOT" /*|| role == "PARENT"*/) { nodeId = profileAdmin?.RootDnaId; } diff --git a/BMA.EHR.Placement.Service/Controllers/PlacementAppointmentController.cs b/BMA.EHR.Placement.Service/Controllers/PlacementAppointmentController.cs index a98abb74..ff010a2a 100644 --- a/BMA.EHR.Placement.Service/Controllers/PlacementAppointmentController.cs +++ b/BMA.EHR.Placement.Service/Controllers/PlacementAppointmentController.cs @@ -112,7 +112,7 @@ namespace BMA.EHR.Placement.Service.Controllers ? profileAdmin?.RootDnaId : ""; } - else if (role == "ROOT" || role == "PARENT") + else if (role == "ROOT" /*|| role == "PARENT"*/) { nodeId = profileAdmin?.RootDnaId; } @@ -239,11 +239,11 @@ namespace BMA.EHR.Placement.Service.Controllers placementAppointments = placementAppointments .Where(x => x.rootDnaId == nodeId).ToList(); } - else if (role == "PARENT") - { - placementAppointments = placementAppointments - .Where(x => x.rootDnaId == nodeId && x.child1DnaId != null).ToList(); - } + // else if (role == "PARENT") + // { + // placementAppointments = placementAppointments + // .Where(x => x.rootDnaId == nodeId && x.child1DnaId != null).ToList(); + // } else if (role == "NORMAL") { placementAppointments = placementAppointments.Where(x => diff --git a/BMA.EHR.Placement.Service/Controllers/PlacementAppointmentEmployeeController.cs b/BMA.EHR.Placement.Service/Controllers/PlacementAppointmentEmployeeController.cs index ae8e40ac..971cd526 100644 --- a/BMA.EHR.Placement.Service/Controllers/PlacementAppointmentEmployeeController.cs +++ b/BMA.EHR.Placement.Service/Controllers/PlacementAppointmentEmployeeController.cs @@ -110,7 +110,7 @@ namespace BMA.EHR.Placement.Service.Controllers ? profileAdmin?.RootDnaId : ""; } - else if (role == "ROOT" || role == "PARENT") + else if (role == "ROOT" /*|| role == "PARENT"*/) { nodeId = profileAdmin?.RootDnaId; } @@ -232,11 +232,11 @@ namespace BMA.EHR.Placement.Service.Controllers placementAppointments = placementAppointments .Where(x => x.rootDnaId == nodeId).ToList(); } - else if (role == "PARENT") - { - placementAppointments = placementAppointments - .Where(x => x.rootDnaId == nodeId && x.child1DnaId != null).ToList(); - } + // else if (role == "PARENT") + // { + // placementAppointments = placementAppointments + // .Where(x => x.rootDnaId == nodeId && x.child1DnaId != null).ToList(); + // } else if (role == "NORMAL") { placementAppointments = placementAppointments.Where(x => diff --git a/BMA.EHR.Placement.Service/Controllers/PlacementOfficerController.cs b/BMA.EHR.Placement.Service/Controllers/PlacementOfficerController.cs index 19cc4e2d..c2033280 100644 --- a/BMA.EHR.Placement.Service/Controllers/PlacementOfficerController.cs +++ b/BMA.EHR.Placement.Service/Controllers/PlacementOfficerController.cs @@ -111,7 +111,7 @@ namespace BMA.EHR.Placement.Service.Controllers ? profileAdmin?.RootDnaId : ""; } - else if (role == "ROOT" || role == "PARENT") + else if (role == "ROOT" /*|| role == "PARENT"*/) { nodeId = profileAdmin?.RootDnaId; } @@ -193,11 +193,11 @@ namespace BMA.EHR.Placement.Service.Controllers placementOfficers = placementOfficers .Where(x => x.rootDnaOldId == nodeId).ToList(); } - else if (role == "PARENT") - { - placementOfficers = placementOfficers - .Where(x => x.rootDnaOldId == nodeId && x.child1DnaOldId != null).ToList(); - } + // else if (role == "PARENT") + // { + // placementOfficers = placementOfficers + // .Where(x => x.rootDnaOldId == nodeId && x.child1DnaOldId != null).ToList(); + // } else if (role == "NORMAL") { placementOfficers = placementOfficers.Where(x => diff --git a/BMA.EHR.Placement.Service/Controllers/PlacementReceiveController.cs b/BMA.EHR.Placement.Service/Controllers/PlacementReceiveController.cs index db8a28a7..ab8a508e 100644 --- a/BMA.EHR.Placement.Service/Controllers/PlacementReceiveController.cs +++ b/BMA.EHR.Placement.Service/Controllers/PlacementReceiveController.cs @@ -112,7 +112,7 @@ namespace BMA.EHR.Placement.Service.Controllers ? profileAdmin?.RootDnaId : ""; } - else if (role == "ROOT" || role == "PARENT") + else if (role == "ROOT" /*|| role == "PARENT"*/) { nodeId = profileAdmin?.RootDnaId; } @@ -231,11 +231,11 @@ namespace BMA.EHR.Placement.Service.Controllers placementReceives = placementReceives .Where(x => (x.rootDnaId == nodeId) || (x.CreatedUserId == UserId)).ToList(); } - else if (role == "PARENT") - { - placementReceives = placementReceives - .Where(x => x.rootDnaId == nodeId && x.child1DnaId != null).ToList(); - } + // else if (role == "PARENT") + // { + // placementReceives = placementReceives + // .Where(x => x.rootDnaId == nodeId && x.child1DnaId != null).ToList(); + // } else if (role == "NORMAL") { placementReceives = placementReceives.Where(x => diff --git a/BMA.EHR.Placement.Service/Controllers/PlacementRepatriationController.cs b/BMA.EHR.Placement.Service/Controllers/PlacementRepatriationController.cs index 45798f6c..5f5668b7 100644 --- a/BMA.EHR.Placement.Service/Controllers/PlacementRepatriationController.cs +++ b/BMA.EHR.Placement.Service/Controllers/PlacementRepatriationController.cs @@ -112,7 +112,7 @@ namespace BMA.EHR.Placement.Service.Controllers ? profileAdmin?.RootDnaId : ""; } - else if (role == "ROOT" || role == "PARENT") + else if (role == "ROOT" /*|| role == "PARENT"*/) { nodeId = profileAdmin?.RootDnaId; } @@ -198,11 +198,11 @@ namespace BMA.EHR.Placement.Service.Controllers placementRepatriations = placementRepatriations .Where(x => x.rootDnaOldId == nodeId).ToList(); } - else if (role == "PARENT") - { - placementRepatriations = placementRepatriations - .Where(x => x.rootDnaOldId == nodeId && x.child1DnaOldId != null).ToList(); - } + // else if (role == "PARENT") + // { + // placementRepatriations = placementRepatriations + // .Where(x => x.rootDnaOldId == nodeId && x.child1DnaOldId != null).ToList(); + // } else if (role == "NORMAL") { placementRepatriations = placementRepatriations.Where(x => diff --git a/BMA.EHR.Placement.Service/Controllers/PlacementTransferController.cs b/BMA.EHR.Placement.Service/Controllers/PlacementTransferController.cs index 68dd42a9..3514e4a5 100644 --- a/BMA.EHR.Placement.Service/Controllers/PlacementTransferController.cs +++ b/BMA.EHR.Placement.Service/Controllers/PlacementTransferController.cs @@ -205,7 +205,7 @@ namespace BMA.EHR.Placement.Service.Controllers ? profileAdmin?.RootDnaId : ""; } - else if (role == "ROOT" || role == "PARENT") + else if (role == "ROOT" /*|| role == "PARENT"*/) { nodeId = profileAdmin?.RootDnaId; } @@ -285,11 +285,11 @@ namespace BMA.EHR.Placement.Service.Controllers placementTransfers = placementTransfers .Where(x => x.rootDnaOldId == nodeId).ToList(); } - else if (role == "PARENT") - { - placementTransfers = placementTransfers - .Where(x => x.rootDnaOldId == nodeId && x.child1DnaOldId != null).ToList(); - } + // else if (role == "PARENT") + // { + // placementTransfers = placementTransfers + // .Where(x => x.rootDnaOldId == nodeId && x.child1DnaOldId != null).ToList(); + // } else if (role == "NORMAL") { placementTransfers = placementTransfers.Where(x => diff --git a/BMA.EHR.Retirement.Service/Controllers/RetirementDeceasedController.cs b/BMA.EHR.Retirement.Service/Controllers/RetirementDeceasedController.cs index 33808435..cf9f153d 100644 --- a/BMA.EHR.Retirement.Service/Controllers/RetirementDeceasedController.cs +++ b/BMA.EHR.Retirement.Service/Controllers/RetirementDeceasedController.cs @@ -116,7 +116,7 @@ namespace BMA.EHR.Retirement.Service.Controllers ? profileAdmin?.RootDnaId : ""; } - else if (role == "ROOT" || role == "PARENT") + else if (role == "ROOT" /*|| role == "PARENT"*/) { nodeId = profileAdmin?.RootDnaId; } @@ -177,11 +177,11 @@ namespace BMA.EHR.Retirement.Service.Controllers retirementDeceaseds = retirementDeceaseds .Where(x => x.rootDnaId == nodeId).ToList(); } - else if (role == "PARENT") - { - retirementDeceaseds = retirementDeceaseds - .Where(x => x.rootDnaId == nodeId && x.child1DnaId != null).ToList(); - } + // else if (role == "PARENT") + // { + // retirementDeceaseds = retirementDeceaseds + // .Where(x => x.rootDnaId == nodeId && x.child1DnaId != null).ToList(); + // } else if (role == "NORMAL") { retirementDeceaseds = retirementDeceaseds.Where(x => diff --git a/BMA.EHR.Retirement.Service/Controllers/RetirementOtherController.cs b/BMA.EHR.Retirement.Service/Controllers/RetirementOtherController.cs index 4db9f778..04a01f77 100644 --- a/BMA.EHR.Retirement.Service/Controllers/RetirementOtherController.cs +++ b/BMA.EHR.Retirement.Service/Controllers/RetirementOtherController.cs @@ -112,7 +112,7 @@ namespace BMA.EHR.Retirement.Service.Controllers ? profileAdmin?.RootDnaId : ""; } - else if (role == "ROOT" || role == "PARENT") + else if (role == "ROOT" /*|| role == "PARENT"*/) { nodeId = profileAdmin?.RootDnaId; } @@ -227,11 +227,11 @@ namespace BMA.EHR.Retirement.Service.Controllers retirementOthers = retirementOthers .Where(x => x.rootDnaOldId == nodeId).ToList(); } - else if (role == "PARENT") - { - retirementOthers = retirementOthers - .Where(x => x.rootDnaOldId == nodeId && x.child1DnaOldId != null).ToList(); - } + // else if (role == "PARENT") + // { + // retirementOthers = retirementOthers + // .Where(x => x.rootDnaOldId == nodeId && x.child1DnaOldId != null).ToList(); + // } else if (role == "NORMAL") { retirementOthers = retirementOthers.Where(x => diff --git a/BMA.EHR.Retirement.Service/Controllers/RetirementOutController.cs b/BMA.EHR.Retirement.Service/Controllers/RetirementOutController.cs index 72a7bc91..ce8b0452 100644 --- a/BMA.EHR.Retirement.Service/Controllers/RetirementOutController.cs +++ b/BMA.EHR.Retirement.Service/Controllers/RetirementOutController.cs @@ -127,7 +127,7 @@ namespace BMA.EHR.Retirement.Service.Controllers ? profileAdmin?.RootDnaId : ""; } - else if (role == "ROOT" || role == "PARENT") + else if (role == "ROOT" /*|| role == "PARENT"*/) { nodeId = profileAdmin?.RootDnaId; } @@ -208,11 +208,11 @@ namespace BMA.EHR.Retirement.Service.Controllers retirementOuts = retirementOuts .Where(x => x.rootDnaOldId == nodeId).ToList(); } - else if (role == "PARENT") - { - retirementOuts = retirementOuts - .Where(x => x.rootDnaOldId == nodeId && x.child1DnaOldId != null).ToList(); - } + // else if (role == "PARENT") + // { + // retirementOuts = retirementOuts + // .Where(x => x.rootDnaOldId == nodeId && x.child1DnaOldId != null).ToList(); + // } else if (role == "NORMAL") { retirementOuts = retirementOuts.Where(x => diff --git a/BMA.EHR.Retirement.Service/Controllers/RetirementResignController.cs b/BMA.EHR.Retirement.Service/Controllers/RetirementResignController.cs index 7b33e0fb..a70e357d 100644 --- a/BMA.EHR.Retirement.Service/Controllers/RetirementResignController.cs +++ b/BMA.EHR.Retirement.Service/Controllers/RetirementResignController.cs @@ -271,7 +271,7 @@ namespace BMA.EHR.Retirement.Service.Controllers ? profileAdmin?.RootDnaId : ""; } - else if (role == "ROOT" || role == "PARENT") + else if (role == "ROOT" /*|| role == "PARENT"*/) { nodeId = profileAdmin?.RootDnaId; } @@ -336,11 +336,11 @@ namespace BMA.EHR.Retirement.Service.Controllers retirementResigns = retirementResigns .Where(x => x.rootDnaOldId == nodeId).ToList(); } - else if (role == "PARENT") - { - retirementResigns = retirementResigns - .Where(x => x.rootDnaOldId == nodeId && x.child1DnaOldId != null).ToList(); - } + // else if (role == "PARENT") + // { + // retirementResigns = retirementResigns + // .Where(x => x.rootDnaOldId == nodeId && x.child1DnaOldId != null).ToList(); + // } else if (role == "NORMAL") { retirementResigns = retirementResigns.Where(x => @@ -403,7 +403,7 @@ namespace BMA.EHR.Retirement.Service.Controllers ? profileAdmin?.RootDnaId : ""; } - else if (role == "ROOT" || role == "PARENT") + else if (role == "ROOT" /*|| role == "PARENT"*/) { nodeId = profileAdmin?.RootDnaId; } @@ -467,11 +467,11 @@ namespace BMA.EHR.Retirement.Service.Controllers retirementResigns = retirementResigns .Where(x => x.rootDnaOldId == nodeId).ToList(); } - else if (role == "PARENT") - { - retirementResigns = retirementResigns - .Where(x => x.rootDnaOldId == nodeId && x.child1DnaOldId != null).ToList(); - } + // else if (role == "PARENT") + // { + // retirementResigns = retirementResigns + // .Where(x => x.rootDnaOldId == nodeId && x.child1DnaOldId != null).ToList(); + // } else if (role == "NORMAL") { retirementResigns = retirementResigns.Where(x => @@ -2170,7 +2170,7 @@ namespace BMA.EHR.Retirement.Service.Controllers ? profileAdmin?.RootDnaId : ""; } - else if (role == "ROOT" || role == "PARENT") + else if (role == "ROOT" /*|| role == "PARENT"*/) { nodeId = profileAdmin?.RootDnaId; } @@ -2228,11 +2228,11 @@ namespace BMA.EHR.Retirement.Service.Controllers data = data .Where(x => x.rootDnaId == nodeId).ToList(); } - else if (role == "PARENT") - { - data = data - .Where(x => x.rootDnaId == nodeId && x.child1DnaId != null).ToList(); - } + // else if (role == "PARENT") + // { + // data = data + // .Where(x => x.rootDnaId == nodeId && x.child1DnaId != null).ToList(); + // } else if (role == "NORMAL") { data = data.Where(x => diff --git a/BMA.EHR.Retirement.Service/Controllers/RetirementResignEmployeeController.cs b/BMA.EHR.Retirement.Service/Controllers/RetirementResignEmployeeController.cs index 8bc6c975..e42e2e6a 100644 --- a/BMA.EHR.Retirement.Service/Controllers/RetirementResignEmployeeController.cs +++ b/BMA.EHR.Retirement.Service/Controllers/RetirementResignEmployeeController.cs @@ -208,7 +208,7 @@ namespace BMA.EHR.Retirement.Service.Controllers ? profileAdmin?.RootDnaId : ""; } - else if (role == "ROOT" || role == "PARENT") + else if (role == "ROOT" /*|| role == "PARENT"*/) { nodeId = profileAdmin?.RootDnaId; } @@ -273,11 +273,11 @@ namespace BMA.EHR.Retirement.Service.Controllers retirementResignEmployees = retirementResignEmployees .Where(x => x.rootDnaOldId == nodeId).ToList(); } - else if (role == "PARENT") - { - retirementResignEmployees = retirementResignEmployees - .Where(x => x.rootDnaOldId == nodeId && x.child1DnaOldId != null).ToList(); - } + // else if (role == "PARENT") + // { + // retirementResignEmployees = retirementResignEmployees + // .Where(x => x.rootDnaOldId == nodeId && x.child1DnaOldId != null).ToList(); + // } else if (role == "NORMAL") { retirementResignEmployees = retirementResignEmployees.Where(x => @@ -340,7 +340,7 @@ namespace BMA.EHR.Retirement.Service.Controllers ? profileAdmin?.RootDnaId : ""; } - else if (role == "ROOT" || role == "PARENT") + else if (role == "ROOT" /*|| role == "PARENT"*/) { nodeId = profileAdmin?.RootDnaId; } @@ -403,11 +403,11 @@ namespace BMA.EHR.Retirement.Service.Controllers retirementResignEmployees = retirementResignEmployees .Where(x => x.rootDnaOldId == nodeId).ToList(); } - else if (role == "PARENT") - { - retirementResignEmployees = retirementResignEmployees - .Where(x => x.rootDnaOldId == nodeId && x.child1DnaOldId != null).ToList(); - } + // else if (role == "PARENT") + // { + // retirementResignEmployees = retirementResignEmployees + // .Where(x => x.rootDnaOldId == nodeId && x.child1DnaOldId != null).ToList(); + // } else if (role == "NORMAL") { retirementResignEmployees = retirementResignEmployees.Where(x => @@ -2078,7 +2078,7 @@ namespace BMA.EHR.Retirement.Service.Controllers ? profileAdmin?.RootDnaId : ""; } - else if (role == "ROOT" || role == "PARENT") + else if (role == "ROOT" /*|| role == "PARENT"*/) { nodeId = profileAdmin?.RootDnaId; } @@ -2136,11 +2136,11 @@ namespace BMA.EHR.Retirement.Service.Controllers data = data .Where(x => x.rootDnaId == nodeId).ToList(); } - else if (role == "PARENT") - { - data = data - .Where(x => x.rootDnaId == nodeId && x.child1DnaId != null).ToList(); - } + // else if (role == "PARENT") + // { + // data = data + // .Where(x => x.rootDnaId == nodeId && x.child1DnaId != null).ToList(); + // } else if (role == "NORMAL") { data = data.Where(x => From 7eade164e9aae4a66a09aee74c4a3dd0f56e23ee Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Thu, 19 Feb 2026 17:33:37 +0700 Subject: [PATCH 116/178] Update LeaveRequestController to use GetLastLeaveRequestByTypeForUserAsync2 method with CreatedAt for fetching last leave request --- BMA.EHR.Leave/Controllers/LeaveRequestController.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/BMA.EHR.Leave/Controllers/LeaveRequestController.cs b/BMA.EHR.Leave/Controllers/LeaveRequestController.cs index 6d16e8de..cf4a73b3 100644 --- a/BMA.EHR.Leave/Controllers/LeaveRequestController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveRequestController.cs @@ -2497,8 +2497,8 @@ namespace BMA.EHR.Leave.Service.Controllers //var lastSalaryAmount = lastSalary == null ? 0 : lastSalary.Amount ?? 0; var lastLeaveRequest = - await _leaveRequestRepository.GetLastLeaveRequestByTypeForUserAsync(rawData.KeycloakUserId, - rawData.Type.Id, rawData.LeaveStartDate.Date); + await _leaveRequestRepository.GetLastLeaveRequestByTypeForUserAsync2(rawData.KeycloakUserId, + rawData.Type.Id, rawData.CreatedAt); //var rootOc = _userProfileRepository.GetRootOcId(profile.OcId ?? Guid.Empty, AccessToken); //var approver = string.Empty; From ecca345407bb2b54e7520ceebf594a34a0eb047a Mon Sep 17 00:00:00 2001 From: harid Date: Fri, 20 Feb 2026 10:46:15 +0700 Subject: [PATCH 117/178] =?UTF-8?q?=E0=B8=A3=E0=B8=B2=E0=B8=A2=E0=B8=8A?= =?UTF-8?q?=E0=B8=B7=E0=B9=88=E0=B8=AD=E0=B8=9C=E0=B8=B9=E0=B9=89=E0=B8=AA?= =?UTF-8?q?=E0=B8=AD=E0=B8=9A=E0=B8=9C=E0=B9=88=E0=B8=B2=E0=B8=99=20?= =?UTF-8?q?=E0=B8=81=E0=B8=A3=E0=B8=93=E0=B8=B5=20OWNER=20=E0=B9=83?= =?UTF-8?q?=E0=B8=AB=E0=B9=89=E0=B9=80=E0=B8=AB=E0=B9=87=E0=B8=99=E0=B8=A3?= =?UTF-8?q?=E0=B8=B2=E0=B8=A2=E0=B8=8A=E0=B8=B7=E0=B9=88=E0=B8=AD=E0=B9=80?= =?UTF-8?q?=E0=B8=AB=E0=B8=A1=E0=B8=B7=E0=B8=AD=E0=B8=99=20=E0=B8=AA?= =?UTF-8?q?=E0=B8=81=E0=B8=88.=20#2319?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Controllers/PlacementController.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/BMA.EHR.Placement.Service/Controllers/PlacementController.cs b/BMA.EHR.Placement.Service/Controllers/PlacementController.cs index d3c28712..d55ae979 100644 --- a/BMA.EHR.Placement.Service/Controllers/PlacementController.cs +++ b/BMA.EHR.Placement.Service/Controllers/PlacementController.cs @@ -140,6 +140,7 @@ namespace BMA.EHR.Placement.Service.Controllers public async Task> GetExamByPlacement(Guid examId) { var getWorkflow = await _permission.GetPermissionAPIWorkflowAsync(examId.ToString(), "SYS_PLACEMENT_PASS"); + var role = string.Empty; if (getWorkflow == false) { var getPermission = await _permission.GetPermissionAPIAsync("GET", "SYS_PLACEMENT_PASS"); @@ -148,6 +149,7 @@ namespace BMA.EHR.Placement.Service.Controllers { return Error(jsonData["message"]?.ToString(), StatusCodes.Status403Forbidden); } + role = jsonData["result"]?.ToString(); } var rootId = ""; @@ -167,7 +169,7 @@ namespace BMA.EHR.Placement.Service.Controllers if (_res.IsSuccessStatusCode) { var org = JsonConvert.DeserializeObject(_result); - if (org.result.isOfficer == false) + if (org.result.isOfficer == false && role?.Trim().ToUpper() != "OWNER") { rootId = org.result.rootId == null ? "" : org.result.rootId; // child1Id = org.result.child1Id == null ? "" : org.result.child1Id; @@ -302,7 +304,7 @@ namespace BMA.EHR.Placement.Service.Controllers } return Success(result1); } - if (org.result.isOfficer == true) + if (org.result.isOfficer == true || role?.Trim().ToUpper() == "OWNER") { var data = await _context.PlacementProfiles.Where(x => x.Placement.Id == examId).Select(x => new { From ddb35f525aecfaaa861a1e01f890438dbfc80852 Mon Sep 17 00:00:00 2001 From: harid Date: Fri, 20 Feb 2026 13:36:50 +0700 Subject: [PATCH 118/178] Fix #2319 --- .../Controllers/PlacementController.cs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/BMA.EHR.Placement.Service/Controllers/PlacementController.cs b/BMA.EHR.Placement.Service/Controllers/PlacementController.cs index d55ae979..2a1e5399 100644 --- a/BMA.EHR.Placement.Service/Controllers/PlacementController.cs +++ b/BMA.EHR.Placement.Service/Controllers/PlacementController.cs @@ -692,6 +692,13 @@ namespace BMA.EHR.Placement.Service.Controllers public async Task> GetDashboardByPlacement(Guid examId) { + var role = string.Empty; + var getPermission = await _permission.GetPermissionAPIAsync("GET", "SYS_PLACEMENT_PASS"); + var jsonData = JsonConvert.DeserializeObject(getPermission); + if (jsonData["status"]?.ToString() == "200") + { + role = jsonData["result"]?.ToString(); + } var rootId = ""; var child1Id = ""; @@ -711,7 +718,7 @@ namespace BMA.EHR.Placement.Service.Controllers if (_res.IsSuccessStatusCode) { var org = JsonConvert.DeserializeObject(_result); - if (org.result.isOfficer == false) + if (org.result.isOfficer == false && role?.Trim().ToUpper() != "OWNER") { rootId = org.result.rootId == null ? "" : org.result.rootId; // child1Id = org.result.child1Id == null ? "" : org.result.child1Id; @@ -735,7 +742,7 @@ namespace BMA.EHR.Placement.Service.Controllers return Success(placement); } - if (org.result.isOfficer == true) + if (org.result.isOfficer == true || role?.Trim().ToUpper() == "OWNER") { var placement = await _context.Placements .Where(x => x.Id == examId) From 3e34aaa1785ea220cd95420c131bc063533fd95c Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Fri, 20 Feb 2026 16:32:57 +0700 Subject: [PATCH 119/178] Refactor file upload logic in LeaveController to handle check-in scenarios more effectively #2328 --- BMA.EHR.Leave/Controllers/LeaveController.cs | 46 +++++++++++++++++--- 1 file changed, 41 insertions(+), 5 deletions(-) diff --git a/BMA.EHR.Leave/Controllers/LeaveController.cs b/BMA.EHR.Leave/Controllers/LeaveController.cs index 267abee9..64d7a92e 100644 --- a/BMA.EHR.Leave/Controllers/LeaveController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveController.cs @@ -970,11 +970,11 @@ namespace BMA.EHR.Leave.Service.Controllers try { await _minIOService.UploadFileAsync(fileName, ms); - if (lastCheckIn != null && lastCheckIn.CheckOut == null) - { - // ยังไม่เคย check-out มาก่อน หรือ check-out เป็น null ให้ใช้ชื่อไฟล์แบบ check-out - await _minIOService.UploadFileAsync(fileNameCheckOut, ms); - } + // if (lastCheckIn != null && lastCheckIn.CheckOut == null) + // { + // // ยังไม่เคย check-out มาก่อน หรือ check-out เป็น null ให้ใช้ชื่อไฟล์แบบ check-out + // await _minIOService.UploadFileAsync(fileNameCheckOut, ms); + // } } catch (Exception ex) { @@ -997,6 +997,42 @@ namespace BMA.EHR.Leave.Service.Controllers } + if (lastCheckIn != null && lastCheckIn.CheckOut == null) + { + using (var ms2 = new MemoryStream(data.CheckInFileBytes ?? new byte[0])) + { + try + { + await _minIOService.UploadFileAsync(fileNameCheckOut, ms2); + // if (lastCheckIn != null && lastCheckIn.CheckOut == null) + // { + // // ยังไม่เคย check-out มาก่อน หรือ check-out เป็น null ให้ใช้ชื่อไฟล์แบบ check-out + // await _minIOService.UploadFileAsync(fileNameCheckOut, ms); + // } + } + catch (Exception ex) + { + await _checkInJobStatusRepository.UpdateToFailedAsync(taskId, $"ไม่สามารถอัปโหลดรูปภาพได้: {ex.Message}"); + + // send notification to user + var noti1 = new Notification + { + Body = $"ประมวลผลการลงเวลาวันที่ {currentDate.ToString("dd-MM-yyyy")} ไม่สำเร็จ \r\nเนื่องจากไม่สามารถอัปโหลดรูปภาพได้ {ex.Message}", + ReceiverUserId = profile.Id, + Type = "", + Payload = "", + }; + _appDbContext.Set().Add(noti1); + await _appDbContext.SaveChangesAsync(); + + + return Error($"ไม่สามารถอัปโหลดรูปภาพได้: {ex.Message}", StatusCodes.Status500InternalServerError); + } + + } + } + + var defaultRound = await _dutyTimeRepository.GetDefaultAsync(); if (defaultRound == null) { From c20e1b48bd7c03d06e7d91a99e34021e334d440c Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Mon, 23 Feb 2026 10:09:36 +0700 Subject: [PATCH 120/178] Add GetDifference method to DateTimeExtension and implement TimeCheck endpoint in LeaveRequestController --- .../Extensions/DateTimeExtension.cs | 23 ++++++ .../Controllers/LeaveRequestController.cs | 70 ++++++++++--------- 2 files changed, 60 insertions(+), 33 deletions(-) diff --git a/BMA.EHR.Domain/Extensions/DateTimeExtension.cs b/BMA.EHR.Domain/Extensions/DateTimeExtension.cs index a6a07c79..bbfc0a12 100644 --- a/BMA.EHR.Domain/Extensions/DateTimeExtension.cs +++ b/BMA.EHR.Domain/Extensions/DateTimeExtension.cs @@ -174,6 +174,29 @@ namespace BMA.EHR.Domain.Extensions } } + public static (int Years, int Months, int Days) GetDifference(this DateTime from, DateTime to) + { + if (from > to) (from, to) = (to, from); // swap ถ้าลำดับสลับ + + int years = to.Year - from.Year; + int months = to.Month - from.Month; + int days = to.Day - from.Day; + + if (days < 0) + { + months--; + days += DateTime.DaysInMonth(to.Year, to.Month == 1 ? 12 : to.Month - 1); + } + + if (months < 0) + { + years--; + months += 12; + } + + return (years, months, days); + } + public static int CalculateAge(this DateTime date, int plusYear = 0, int subtractYear = 0) { try diff --git a/BMA.EHR.Leave/Controllers/LeaveRequestController.cs b/BMA.EHR.Leave/Controllers/LeaveRequestController.cs index f82269db..0a69836a 100644 --- a/BMA.EHR.Leave/Controllers/LeaveRequestController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveRequestController.cs @@ -966,6 +966,33 @@ namespace BMA.EHR.Leave.Service.Controllers return Success(result); } + [HttpGet("time-check")] + [AllowAnonymous] + public async Task> TimeCheckAsync() + { + var startDate = new DateTime(2017, 1, 6); + var govAge = (startDate).DiffDay(DateTime.Now.Date); + var date1Raw = startDate; + var date1 = new LocalDate(date1Raw.Year, date1Raw.Month, date1Raw.Day); + var date2 = new LocalDate(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day); + + + var (yy, mm, dd) = startDate.GetDifference(DateTime.Now.Date); + + + // Period period = Period.Between(date1, date2); + // var govAgeMonth = period.Months; + // var govAgeYear = period.Years; + + return Success(new + { + GovAge = govAge, + GovAgeDay = dd, + GovAgeMonth = mm, + GovAgeYear = yy + }); + } + /// /// LV2_003 - เช็คการยืนขอลา (USER) /// @@ -987,12 +1014,15 @@ namespace BMA.EHR.Leave.Service.Controllers // var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(userId, AccessToken); var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(userId, AccessToken); var govAge = (profile?.DateStart?.Date ?? DateTime.Now.Date).DiffDay(DateTime.Now.Date); - var date1Raw = profile?.DateStart?.Date ?? DateTime.Now.Date; - var date1 = new LocalDate(date1Raw.Year, date1Raw.Month, date1Raw.Day); - var date2 = new LocalDate(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day); - Period period = Period.Between(date1, date2); - var govAgeMonth = period.Months; - var govAgeYear = period.Years; + var startDate = profile?.DateStart?.Date ?? DateTime.Now.Date; + // var date1Raw = profile?.DateStart?.Date ?? DateTime.Now.Date; + // var date1 = new LocalDate(date1Raw.Year, date1Raw.Month, date1Raw.Day); + // var date2 = new LocalDate(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day); + // Period period = Period.Between(date1, date2); + // var govAgeMonth = period.Months; + // var govAgeYear = period.Years; + + var (govAgeYear, govAgeMonth, govAgeDay) = startDate.GetDifference(DateTime.Now.Date); var thisYear = DateTime.Now.Year; @@ -1089,37 +1119,11 @@ namespace BMA.EHR.Leave.Service.Controllers break; case "LV-005": // fix issue : ระบบลา (ขรก.) >> ลาพักผ่อน (กรณีรับราชการไม่ถึง 6 เดือน) #838 - //var leavePrevYear = (await _leaveRequestRepository.GetSumApproveLeaveAsync(fiscalYear - 1)).Where(x => x.LeaveTypeCode == "LV-005" && x.KeycloakUserId == userId).FirstOrDefault(); - //var leavePrevYearRemain = 10 - (leavePrevYear == null ? 0 : leavePrevYear.SumLeaveDay); // หายอดวันลาที่เหลือของปีก่อน - // if (profile.IsProbation! == true) - // { - // isLeave = false; - // if (!isLeave) message = "ยังอยู่ในช่วงทดลองปฏิบัติราชการ ไม่สามารถลาพักผ่อนได้"; - // } - // else - if (govAgeMonth >= 6) - //else + if (govAgeYear >= 1 || (govAgeYear == 0 && govAgeMonth >= 6)) { isLeave = (totalDay - (sumWorkDay + sumWeekend) + approveDay) <= (limitDay); if (!isLeave) message = "จำนวนวันลาเกินที่กำหนด"; } - // else if (govAge >= 3650) - // { - // // ถ้าอายุราชการเกิน 10 ปี ได้บวกเพิ่มอีก 10 วัน - // var leavePrevYearRemain = 30 - (leavePrevYear == null ? 0 : leavePrevYear.SumLeaveDay); // หายอดวันลาที่เหลือของปีก่อน - // if (leavePrevYearRemain >= 20) leavePrevYearRemain = 20; - - // isLeave = (totalDay - (sumWorkDay + sumWeekend) + approveDay) <= (limitDay); - // if (!isLeave) message = "จำนวนวันลาเกินที่กำหนด"; - // } - // else if - // { - // //var leavePrevYearRemain = 20 - (leavePrevYear == null ? 0 : leavePrevYear.SumLeaveDay); // หายอดวันลาที่เหลือของปีก่อน - // //if (leavePrevYearRemain >= 10) leavePrevYearRemain = 10; - - // isLeave = (totalDay - (sumWorkDay + sumWeekend) + sumApproveLeave) <= (10 + leavePrevYearRemain); - // if (!isLeave) message = "จำนวนวันลาเกินที่กำหนด"; - // } else { isLeave = false; From cd991796218973a7c428d2ac3449a99441a62c99 Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Tue, 24 Feb 2026 10:24:24 +0700 Subject: [PATCH 121/178] Enhance leave eligibility check in LeaveRequestController with detailed messaging --- .../Controllers/LeaveRequestController.cs | 25 +++++++++++++------ 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/BMA.EHR.Leave/Controllers/LeaveRequestController.cs b/BMA.EHR.Leave/Controllers/LeaveRequestController.cs index 0a69836a..069d2e31 100644 --- a/BMA.EHR.Leave/Controllers/LeaveRequestController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveRequestController.cs @@ -977,19 +977,30 @@ namespace BMA.EHR.Leave.Service.Controllers var date2 = new LocalDate(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day); - var (yy, mm, dd) = startDate.GetDifference(DateTime.Now.Date); + var (govAgeYear, govAgeMonth, govAgeDay) = startDate.GetDifference(DateTime.Now.Date); + var isLeave = false; + var message = string.Empty; - // Period period = Period.Between(date1, date2); - // var govAgeMonth = period.Months; - // var govAgeYear = period.Years; + if (govAgeYear >= 1 || (govAgeYear == 0 && govAgeMonth >= 6)) + { + isLeave = true; + if (!isLeave) message = "จำนวนวันลาเกินที่กำหนด"; + } + else + { + isLeave = false; + if (!isLeave) message = "อายุราชการน้อยกว่า 6 เดือนหรือ 180 วัน"; + } return Success(new { GovAge = govAge, - GovAgeDay = dd, - GovAgeMonth = mm, - GovAgeYear = yy + GovAgeDay = govAgeDay, + GovAgeMonth = govAgeMonth, + GovAgeYear = govAgeYear, + IsLeave = isLeave, + Message = message }); } From 2ee36af76326eb96c8816d1ecc3f00b92607b144 Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Tue, 24 Feb 2026 19:43:58 +0700 Subject: [PATCH 122/178] Test --- BMA.EHR.Leave/Controllers/LeaveRequestController.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/BMA.EHR.Leave/Controllers/LeaveRequestController.cs b/BMA.EHR.Leave/Controllers/LeaveRequestController.cs index 069d2e31..e658788d 100644 --- a/BMA.EHR.Leave/Controllers/LeaveRequestController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveRequestController.cs @@ -1001,6 +1001,7 @@ namespace BMA.EHR.Leave.Service.Controllers GovAgeYear = govAgeYear, IsLeave = isLeave, Message = message + }); } From 9a74b690cd6574e12210e68a68e1b8ca7c144c80 Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Wed, 25 Feb 2026 10:13:27 +0700 Subject: [PATCH 123/178] =?UTF-8?q?=E0=B8=97=E0=B8=94=E0=B8=AA=E0=B8=AD?= =?UTF-8?q?=E0=B8=9A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BMA.EHR.Leave/Controllers/LeaveRequestController.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/BMA.EHR.Leave/Controllers/LeaveRequestController.cs b/BMA.EHR.Leave/Controllers/LeaveRequestController.cs index e658788d..2300e9ec 100644 --- a/BMA.EHR.Leave/Controllers/LeaveRequestController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveRequestController.cs @@ -1002,6 +1002,8 @@ namespace BMA.EHR.Leave.Service.Controllers IsLeave = isLeave, Message = message + + }); } From 006cea048d78e1862e54d5d51d122153d04a1a47 Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Wed, 25 Feb 2026 15:26:49 +0700 Subject: [PATCH 124/178] Add ScheduleUpdateDna endpoint and DTO for updating DNA information in LeaveBeginningController --- .../Controllers/LeaveBeginningController.cs | 38 +++++++++++++++++++ .../LeaveBeginnings/EditLeaveBeginningDto.cs | 15 ++++++++ 2 files changed, 53 insertions(+) diff --git a/BMA.EHR.Leave/Controllers/LeaveBeginningController.cs b/BMA.EHR.Leave/Controllers/LeaveBeginningController.cs index f3b661cf..29ec361e 100644 --- a/BMA.EHR.Leave/Controllers/LeaveBeginningController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveBeginningController.cs @@ -577,6 +577,44 @@ namespace BMA.EHR.Leave.Service.Controllers } } + + [HttpPut("schedule/update-dna")] + [AllowAnonymous] + public async Task> ScheduleUpdateDnaAsync([FromBody] ScheduleUpdateDnaDto req) + { + try + { + var profile = await _userProfileRepository.GetProfileByProfileIdNoAuthAsync(req.ProfileId, AccessToken); + if(profile == null) + { + return Error("ไม่พบข้อมูลข้าราชการหรือลูกจ้าง", StatusCodes.Status404NotFound); + } + // check duplicate + var oldData = await _context.LeaveBeginnings.Where(x => x.ProfileId == req.ProfileId + && x.LeaveYear == req.LeaveYear).ToListAsync(); + + foreach(var item in oldData) + { + item.RootDnaId = profile.RootDnaId; + item.Child1DnaId = profile.Child1DnaId; + item.Child2DnaId = profile.Child2DnaId; + item.Child3DnaId = profile.Child3DnaId; + item.Child4DnaId = profile.Child4DnaId; + + item.LastUpdateUserId = ""; + item.LastUpdateFullName = "System"; + item.LastUpdatedAt = DateTime.Now; + + await _leaveBeginningRepository.UpdateAsync(item); + } + return Success(); + } + catch (Exception ex) + { + return Error(ex); + } + } + #endregion } } diff --git a/BMA.EHR.Leave/DTOs/LeaveBeginnings/EditLeaveBeginningDto.cs b/BMA.EHR.Leave/DTOs/LeaveBeginnings/EditLeaveBeginningDto.cs index fe0c433f..03c96a49 100644 --- a/BMA.EHR.Leave/DTOs/LeaveBeginnings/EditLeaveBeginningDto.cs +++ b/BMA.EHR.Leave/DTOs/LeaveBeginnings/EditLeaveBeginningDto.cs @@ -45,4 +45,19 @@ namespace BMA.EHR.Leave.Service.DTOs.LeaveBeginnings [Required, Comment("จำนวนวันลายกมา")] public double LeaveDays { get; set; } = 0.0; } + + public class ScheduleUpdateDnaDto + { + [Required] + public Guid ProfileId { get; set; } = Guid.Empty; + + [Required, Comment("ปีงบประมาณ")] + public int LeaveYear { get; set; } = 0; + + public Guid? RootDnaId { get; set; } + public Guid? Child1DnaId { get; set; } + public Guid? Child2DnaId { get; set; } + public Guid? Child3DnaId { get; set; } + public Guid? Child4DnaId { get; set; } + } } From f866435897836b7369c6e41896040fd04c646836 Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Wed, 25 Feb 2026 16:26:28 +0700 Subject: [PATCH 125/178] Refactor LeaveBeginningController to simplify duplicate check and comment out LeaveYear property in EditLeaveBeginningDto #2341 --- BMA.EHR.Leave/Controllers/LeaveBeginningController.cs | 3 +-- BMA.EHR.Leave/DTOs/LeaveBeginnings/EditLeaveBeginningDto.cs | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/BMA.EHR.Leave/Controllers/LeaveBeginningController.cs b/BMA.EHR.Leave/Controllers/LeaveBeginningController.cs index 29ec361e..5e94c1e4 100644 --- a/BMA.EHR.Leave/Controllers/LeaveBeginningController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveBeginningController.cs @@ -590,8 +590,7 @@ namespace BMA.EHR.Leave.Service.Controllers return Error("ไม่พบข้อมูลข้าราชการหรือลูกจ้าง", StatusCodes.Status404NotFound); } // check duplicate - var oldData = await _context.LeaveBeginnings.Where(x => x.ProfileId == req.ProfileId - && x.LeaveYear == req.LeaveYear).ToListAsync(); + var oldData = await _context.LeaveBeginnings.Where(x => x.ProfileId == req.ProfileId).ToListAsync(); foreach(var item in oldData) { diff --git a/BMA.EHR.Leave/DTOs/LeaveBeginnings/EditLeaveBeginningDto.cs b/BMA.EHR.Leave/DTOs/LeaveBeginnings/EditLeaveBeginningDto.cs index 03c96a49..524072ec 100644 --- a/BMA.EHR.Leave/DTOs/LeaveBeginnings/EditLeaveBeginningDto.cs +++ b/BMA.EHR.Leave/DTOs/LeaveBeginnings/EditLeaveBeginningDto.cs @@ -51,8 +51,8 @@ namespace BMA.EHR.Leave.Service.DTOs.LeaveBeginnings [Required] public Guid ProfileId { get; set; } = Guid.Empty; - [Required, Comment("ปีงบประมาณ")] - public int LeaveYear { get; set; } = 0; + // [Required, Comment("ปีงบประมาณ")] + // public int LeaveYear { get; set; } = 0; public Guid? RootDnaId { get; set; } public Guid? Child1DnaId { get; set; } From 4650f7a2ab574b1eef78e6987fb3d2ee8a7107a7 Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Thu, 26 Feb 2026 20:36:48 +0700 Subject: [PATCH 126/178] Refactor ScheduleUpdateDnaAsync to handle a list of ScheduleUpdateDnaDto and streamline profile updates #2341 --- .../Controllers/LeaveBeginningController.cs | 43 +++++++++++-------- 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/BMA.EHR.Leave/Controllers/LeaveBeginningController.cs b/BMA.EHR.Leave/Controllers/LeaveBeginningController.cs index 5e94c1e4..e580b00a 100644 --- a/BMA.EHR.Leave/Controllers/LeaveBeginningController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveBeginningController.cs @@ -580,32 +580,37 @@ namespace BMA.EHR.Leave.Service.Controllers [HttpPut("schedule/update-dna")] [AllowAnonymous] - public async Task> ScheduleUpdateDnaAsync([FromBody] ScheduleUpdateDnaDto req) + public async Task> ScheduleUpdateDnaAsync([FromBody] List req) { try { - var profile = await _userProfileRepository.GetProfileByProfileIdNoAuthAsync(req.ProfileId, AccessToken); - if(profile == null) + foreach(var item in req) { - return Error("ไม่พบข้อมูลข้าราชการหรือลูกจ้าง", StatusCodes.Status404NotFound); - } - // check duplicate - var oldData = await _context.LeaveBeginnings.Where(x => x.ProfileId == req.ProfileId).ToListAsync(); - - foreach(var item in oldData) - { - item.RootDnaId = profile.RootDnaId; - item.Child1DnaId = profile.Child1DnaId; - item.Child2DnaId = profile.Child2DnaId; - item.Child3DnaId = profile.Child3DnaId; - item.Child4DnaId = profile.Child4DnaId; + // var profile = await _userProfileRepository.GetProfileByProfileIdNoAuthAsync(item.ProfileId, AccessToken); + // if(profile == null) + // { + // return Error("ไม่พบข้อมูลข้าราชการหรือลูกจ้าง", StatusCodes.Status404NotFound); + // } + // check duplicate + var oldData = await _context.LeaveBeginnings.Where(x => x.ProfileId == item.ProfileId).ToListAsync(); + + foreach(var o in oldData) + { + o.RootDnaId = item.RootDnaId; + o.Child1DnaId = item.Child1DnaId; + o.Child2DnaId = item.Child2DnaId; + o.Child3DnaId = item.Child3DnaId; + o.Child4DnaId = item.Child4DnaId; - item.LastUpdateUserId = ""; - item.LastUpdateFullName = "System"; - item.LastUpdatedAt = DateTime.Now; + o.LastUpdateUserId = ""; + o.LastUpdateFullName = "System"; + o.LastUpdatedAt = DateTime.Now; - await _leaveBeginningRepository.UpdateAsync(item); + await _leaveBeginningRepository.UpdateAsync(o); + } } + + return Success(); } catch (Exception ex) From 4562029e6ea899524eb922b1ca716494234816bb Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Tue, 10 Mar 2026 14:10:35 +0700 Subject: [PATCH 127/178] Update GetTimeStampHistoryAsync call to include pagination and keyword filtering --- BMA.EHR.Leave/Controllers/LeaveController.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BMA.EHR.Leave/Controllers/LeaveController.cs b/BMA.EHR.Leave/Controllers/LeaveController.cs index 64d7a92e..5bedbae5 100644 --- a/BMA.EHR.Leave/Controllers/LeaveController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveController.cs @@ -1762,7 +1762,7 @@ namespace BMA.EHR.Leave.Service.Controllers // var test = await _processUserTimeStampRepository.GetTimeStampHistoryAsync(userId, year); // return Success(test); - var data = (await _processUserTimeStampRepository.GetTimeStampHistoryAsync(userId, year)) + var data = (await _processUserTimeStampRepository.GetTimeStampHistoryAsync(userId, year, page, pageSize, keyword)) .Select(d => new CheckInHistoryDto { CheckInId = d.Id, From 6902236f48b1111ae7d01ab153ec0c97f1d7e58a Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Wed, 11 Mar 2026 11:57:00 +0700 Subject: [PATCH 128/178] Add GetTimeStampHistoryAsync2 method for fiscal year timestamp retrieval --- .../ProcessUserTimeStampRepository.cs | 13 +++++++++++++ BMA.EHR.Leave/Controllers/LeaveController.cs | 2 +- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/BMA.EHR.Application/Repositories/Leaves/TimeAttendants/ProcessUserTimeStampRepository.cs b/BMA.EHR.Application/Repositories/Leaves/TimeAttendants/ProcessUserTimeStampRepository.cs index c1df8798..e1a25780 100644 --- a/BMA.EHR.Application/Repositories/Leaves/TimeAttendants/ProcessUserTimeStampRepository.cs +++ b/BMA.EHR.Application/Repositories/Leaves/TimeAttendants/ProcessUserTimeStampRepository.cs @@ -227,6 +227,19 @@ namespace BMA.EHR.Application.Repositories.Leaves.TimeAttendants return data; } + public async Task> GetTimeStampHistoryAsync2(Guid keycloakId, int year) + { + var fiscalDateStart = new DateTime(year - 1, 10, 1); + var fiscalDateEnd = new DateTime(year, 9, 30); + + var data = await _dbContext.Set() + .Where(u => u.KeycloakUserId == keycloakId) + .Where(u => u.CheckIn.Date >= fiscalDateStart && u.CheckIn.Date <= fiscalDateEnd) + .OrderByDescending(u => u.CheckIn.Date) + .ToListAsync(); + return data; + } + public async Task GetTimeStampHistoryForAdminCountAsync(DateTime startDate, DateTime endDate) { var data = await _dbContext.Set() diff --git a/BMA.EHR.Leave/Controllers/LeaveController.cs b/BMA.EHR.Leave/Controllers/LeaveController.cs index 5bedbae5..d3907e66 100644 --- a/BMA.EHR.Leave/Controllers/LeaveController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveController.cs @@ -1762,7 +1762,7 @@ namespace BMA.EHR.Leave.Service.Controllers // var test = await _processUserTimeStampRepository.GetTimeStampHistoryAsync(userId, year); // return Success(test); - var data = (await _processUserTimeStampRepository.GetTimeStampHistoryAsync(userId, year, page, pageSize, keyword)) + var data = (await _processUserTimeStampRepository.GetTimeStampHistoryAsync2(userId, year)) .Select(d => new CheckInHistoryDto { CheckInId = d.Id, From b1df33dc209fbc9fa0796a6d6c1604da4b46fd2b Mon Sep 17 00:00:00 2001 From: harid Date: Wed, 11 Mar 2026 14:01:34 +0700 Subject: [PATCH 129/178] fix bug #2183 --- .../Controllers/RetirementResignController.cs | 56 +++++++++---------- .../RetirementResignEmployeeController.cs | 56 +++++++++---------- 2 files changed, 56 insertions(+), 56 deletions(-) diff --git a/BMA.EHR.Retirement.Service/Controllers/RetirementResignController.cs b/BMA.EHR.Retirement.Service/Controllers/RetirementResignController.cs index a70e357d..7b8d27b1 100644 --- a/BMA.EHR.Retirement.Service/Controllers/RetirementResignController.cs +++ b/BMA.EHR.Retirement.Service/Controllers/RetirementResignController.cs @@ -3104,13 +3104,13 @@ namespace BMA.EHR.Retirement.Service.Controllers }).ToList(); var baseAPIOrg = _configuration["API"]; - var reportDone = false; - if (data.Where(profile => profile.Status == "DONE").Any()) - { - reportDone = true; - } - if (reportDone == true) - { + //var reportDone = false; + //if (data.Where(profile => profile.Status == "DONE").Any()) + //{ + // reportDone = true; + //} + //if (reportDone == true) + //{ var apiUrlOrg = $"{baseAPIOrg}/org/command/excexute/salary-leave"; using (var client = new HttpClient()) { @@ -3128,27 +3128,27 @@ namespace BMA.EHR.Retirement.Service.Controllers await _context.SaveChangesAsync(); } } - } - else - { - var apiUrlOrg = $"{baseAPIOrg}/org/command/cancel-resign"; - using (var client = new HttpClient()) - { - client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token.Replace("Bearer ", "")); - client.DefaultRequestHeaders.Add("api-key", _configuration["API_KEY"]); - var _res = await client.PostAsJsonAsync(apiUrlOrg, new - { - resignId = resultData.Select(x => x.resignId).ToList(), - }); - var _result = await _res.Content.ReadAsStringAsync(); - if (_res.IsSuccessStatusCode) - { - data.ForEach(profile => profile.Status = "DONE"); - data.ForEach(profile => profile.RetirementResign.Status = "CANCEL"); - await _context.SaveChangesAsync(); - } - } - } + //} + //else + //{ + // var apiUrlOrg = $"{baseAPIOrg}/org/command/cancel-resign"; + // using (var client = new HttpClient()) + // { + // client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token.Replace("Bearer ", "")); + // client.DefaultRequestHeaders.Add("api-key", _configuration["API_KEY"]); + // var _res = await client.PostAsJsonAsync(apiUrlOrg, new + // { + // resignId = resultData.Select(x => x.resignId).ToList(), + // }); + // var _result = await _res.Content.ReadAsStringAsync(); + // if (_res.IsSuccessStatusCode) + // { + // data.ForEach(profile => profile.Status = "DONE"); + // data.ForEach(profile => profile.RetirementResign.Status = "CANCEL"); + // await _context.SaveChangesAsync(); + // } + // } + //} return Success(); } diff --git a/BMA.EHR.Retirement.Service/Controllers/RetirementResignEmployeeController.cs b/BMA.EHR.Retirement.Service/Controllers/RetirementResignEmployeeController.cs index e42e2e6a..9b176503 100644 --- a/BMA.EHR.Retirement.Service/Controllers/RetirementResignEmployeeController.cs +++ b/BMA.EHR.Retirement.Service/Controllers/RetirementResignEmployeeController.cs @@ -2598,14 +2598,14 @@ namespace BMA.EHR.Retirement.Service.Controllers }).ToList(); var baseAPIOrg = _configuration["API"]; - var reportDone = false; - if (data.Where(profile => profile.Status == "DONE").Any()) - { - reportDone = true; - } + //var reportDone = false; + //if (data.Where(profile => profile.Status == "DONE").Any()) + //{ + // reportDone = true; + //} - if (reportDone == true) - { + //if (reportDone == true) + //{ var apiUrlOrg = $"{baseAPIOrg}/org/command/excexute/salary-employee-leave"; using (var client = new HttpClient()) { @@ -2623,27 +2623,27 @@ namespace BMA.EHR.Retirement.Service.Controllers await _context.SaveChangesAsync(); } } - } - else - { - var apiUrlOrg = $"{baseAPIOrg}/org/command/cancel-resign"; - using (var client = new HttpClient()) - { - client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token.Replace("Bearer ", "")); - client.DefaultRequestHeaders.Add("api-key", _configuration["API_KEY"]); - var _res = await client.PostAsJsonAsync(apiUrlOrg, new - { - resignId = resultData.Select(x => x.resignId).ToList(), - }); - var _result = await _res.Content.ReadAsStringAsync(); - if (_res.IsSuccessStatusCode) - { - data.ForEach(profile => profile.Status = "DONE"); - data.ForEach(profile => profile.RetirementResignEmployee.Status = "CANCEL"); - await _context.SaveChangesAsync(); - } - } - } + //} + //else + //{ + // var apiUrlOrg = $"{baseAPIOrg}/org/command/cancel-resign"; + // using (var client = new HttpClient()) + // { + // client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token.Replace("Bearer ", "")); + // client.DefaultRequestHeaders.Add("api-key", _configuration["API_KEY"]); + // var _res = await client.PostAsJsonAsync(apiUrlOrg, new + // { + // resignId = resultData.Select(x => x.resignId).ToList(), + // }); + // var _result = await _res.Content.ReadAsStringAsync(); + // if (_res.IsSuccessStatusCode) + // { + // data.ForEach(profile => profile.Status = "DONE"); + // data.ForEach(profile => profile.RetirementResignEmployee.Status = "CANCEL"); + // await _context.SaveChangesAsync(); + // } + // } + //} return Success(); } From 7e0f0485fd0a46910fff7899bee593d6773d14f1 Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Tue, 17 Mar 2026 15:47:03 +0700 Subject: [PATCH 130/178] Add TokenUserInfo class and extend ClaimsPrincipal with methods for user claims retrieval --- BMA.EHR.Domain/Common/BaseController.cs | 17 ++- BMA.EHR.Domain/Common/TokenUserInfo.cs | 39 ++++++ .../Extensions/ClaimsPrincipalExtensions.cs | 30 +++++ ...ombinedErrorHandlerAndLoggingMiddleware.cs | 119 ++++++++++++++++-- BMA.EHR.Leave/Controllers/LeaveController.cs | 8 +- 5 files changed, 201 insertions(+), 12 deletions(-) create mode 100644 BMA.EHR.Domain/Common/TokenUserInfo.cs create mode 100644 BMA.EHR.Domain/Extensions/ClaimsPrincipalExtensions.cs diff --git a/BMA.EHR.Domain/Common/BaseController.cs b/BMA.EHR.Domain/Common/BaseController.cs index 44d8dac0..26f71bf5 100644 --- a/BMA.EHR.Domain/Common/BaseController.cs +++ b/BMA.EHR.Domain/Common/BaseController.cs @@ -1,4 +1,5 @@ -using BMA.EHR.Domain.Shared; +using BMA.EHR.Domain.Extensions; +using BMA.EHR.Domain.Shared; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; @@ -81,6 +82,20 @@ namespace BMA.EHR.Domain.Common } + #endregion + + #region " Properties " + + protected string? EmpType => User.GetEmpType(); + protected Guid? OrgChild1DnaId => User.GetOrgChild1DnaId(); + protected Guid? OrgChild2DnaId => User.GetOrgChild2DnaId(); + protected Guid? OrgChild3DnaId => User.GetOrgChild3DnaId(); + protected Guid? OrgChild4DnaId => User.GetOrgChild4DnaId(); + protected Guid? OrgRootDnaId => User.GetOrgRootDnaId(); + protected Guid? ProfileId => User.GetProfileId(); + protected string? Prefix => User.GetPrefix(); + protected string? FullNameFromClaim => User.GetName(); + #endregion #endregion diff --git a/BMA.EHR.Domain/Common/TokenUserInfo.cs b/BMA.EHR.Domain/Common/TokenUserInfo.cs new file mode 100644 index 00000000..cdae6fb1 --- /dev/null +++ b/BMA.EHR.Domain/Common/TokenUserInfo.cs @@ -0,0 +1,39 @@ +namespace BMA.EHR.Domain.Common +{ + public class TokenUserInfo + { + // Existing properties + public string KeycloakId { get; set; } = string.Empty; + public string? PreferredUsername { get; set; } + public string? GivenName { get; set; } + public string? FamilyName { get; set; } + + // New properties to add + public string? EmpType { get; set; } + public Guid? OrgChild1DnaId { get; set; } + public Guid? OrgChild2DnaId { get; set; } + public Guid? OrgChild3DnaId { get; set; } + public Guid? OrgChild4DnaId { get; set; } + public Guid? OrgRootDnaId { get; set; } + public Guid? ProfileId { get; set; } + public string? Prefix { get; set; } + public string? Name { get; set; } + } + + // Claim type constants + public static class BmaClaimTypes + { + public const string EmpType = "empType"; + public const string OrgChild1DnaId = "orgChild1DnaId"; + public const string OrgChild2DnaId = "orgChild2DnaId"; + public const string OrgChild3DnaId = "orgChild3DnaId"; + public const string OrgChild4DnaId = "orgChild4DnaId"; + public const string OrgRootDnaId = "orgRootDnaId"; + public const string ProfileId = "profileId"; + public const string Prefix = "prefix"; + public const string Name = "name"; + public const string GivenName = "given_name"; + public const string FamilyName = "family_name"; + public const string PreferredUsername = "preferred_username"; + } +} diff --git a/BMA.EHR.Domain/Extensions/ClaimsPrincipalExtensions.cs b/BMA.EHR.Domain/Extensions/ClaimsPrincipalExtensions.cs new file mode 100644 index 00000000..26a7c189 --- /dev/null +++ b/BMA.EHR.Domain/Extensions/ClaimsPrincipalExtensions.cs @@ -0,0 +1,30 @@ +using BMA.EHR.Domain.Common; +using System.Security.Claims; + +namespace BMA.EHR.Domain.Extensions +{ + public static class ClaimsPrincipalExtensions + { + public static string? GetClaimValue(this ClaimsPrincipal user, string claimType) + { + return user?.FindFirst(claimType)?.Value; + } + + public static Guid? GetGuidClaim(this ClaimsPrincipal user, string claimType) + { + var value = user?.GetClaimValue(claimType); + return Guid.TryParse(value, out var guid) ? guid : null; + } + + // Convenience methods for common claims + public static string? GetEmpType(this ClaimsPrincipal user) => user.GetClaimValue(BmaClaimTypes.EmpType); + public static Guid? GetOrgChild1DnaId(this ClaimsPrincipal user) => user.GetGuidClaim(BmaClaimTypes.OrgChild1DnaId); + public static Guid? GetOrgChild2DnaId(this ClaimsPrincipal user) => user.GetGuidClaim(BmaClaimTypes.OrgChild2DnaId); + public static Guid? GetOrgChild3DnaId(this ClaimsPrincipal user) => user.GetGuidClaim(BmaClaimTypes.OrgChild3DnaId); + public static Guid? GetOrgChild4DnaId(this ClaimsPrincipal user) => user.GetGuidClaim(BmaClaimTypes.OrgChild4DnaId); + public static Guid? GetOrgRootDnaId(this ClaimsPrincipal user) => user.GetGuidClaim(BmaClaimTypes.OrgRootDnaId); + public static Guid? GetProfileId(this ClaimsPrincipal user) => user.GetGuidClaim(BmaClaimTypes.ProfileId); + public static string? GetPrefix(this ClaimsPrincipal user) => user.GetClaimValue(BmaClaimTypes.Prefix); + public static string? GetName(this ClaimsPrincipal user) => user.GetClaimValue(BmaClaimTypes.Name); + } +} diff --git a/BMA.EHR.Domain/Middlewares/CombinedErrorHandlerAndLoggingMiddleware.cs b/BMA.EHR.Domain/Middlewares/CombinedErrorHandlerAndLoggingMiddleware.cs index 15c88592..4216fa43 100644 --- a/BMA.EHR.Domain/Middlewares/CombinedErrorHandlerAndLoggingMiddleware.cs +++ b/BMA.EHR.Domain/Middlewares/CombinedErrorHandlerAndLoggingMiddleware.cs @@ -79,13 +79,39 @@ namespace BMA.EHR.Domain.Middlewares GetProfileByKeycloakIdLocal? pf = null; var tokenUserInfo = await ExtractTokenUserInfoAsync(token); + // Store tokenUserInfo in HttpContext.Items for controllers to use + context.Items["TokenUserInfo"] = tokenUserInfo; + // ดึง keycloakId จาก JWT token keycloakId = tokenUserInfo.KeycloakId; - // ดึง profile จาก cache หรือ API + // ดึง profile จาก claims หรือ cache หรือ API if (Guid.TryParse(keycloakId, out var parsedId) && parsedId != Guid.Empty) { - pf = await GetProfileWithCacheAsync(parsedId, token); + // Build profile from token claims if available + if (tokenUserInfo.OrgRootDnaId.HasValue && tokenUserInfo.ProfileId.HasValue) + { + pf = new GetProfileByKeycloakIdLocal + { + Id = tokenUserInfo.ProfileId.Value, + CitizenId = tokenUserInfo.PreferredUsername, + Prefix = tokenUserInfo.Prefix, + FirstName = tokenUserInfo.GivenName, + LastName = tokenUserInfo.FamilyName, + RootDnaId = tokenUserInfo.OrgRootDnaId, + Child1DnaId = tokenUserInfo.OrgChild1DnaId, + Child2DnaId = tokenUserInfo.OrgChild2DnaId, + Child3DnaId = tokenUserInfo.OrgChild3DnaId, + Child4DnaId = tokenUserInfo.OrgChild4DnaId, + }; + Console.WriteLine($"[INFO] Using claims for profile - OrgRootDnaId: {pf.RootDnaId}, ProfileId: {pf.Id}"); + } + else + { + // Fallback to API only if critical claims are missing + Console.WriteLine("[WARN] Critical claims missing, falling back to API call"); + pf = await GetProfileWithCacheAsync(parsedId, token); + } } try @@ -649,6 +675,87 @@ namespace BMA.EHR.Domain.Middlewares Console.WriteLine($"Extracted family_name: {result.FamilyName}"); } + // ดึง empType + if (jsonDoc.RootElement.TryGetProperty("empType", out var empTypeElement)) + { + result.EmpType = empTypeElement.GetString(); + Console.WriteLine($"Extracted empType: {result.EmpType}"); + } + + // ดึง orgChild1DnaId + if (jsonDoc.RootElement.TryGetProperty("orgChild1DnaId", out var orgChild1Element)) + { + if (Guid.TryParse(orgChild1Element.GetString(), out var orgChild1Guid)) + { + result.OrgChild1DnaId = orgChild1Guid; + Console.WriteLine($"Extracted orgChild1DnaId: {result.OrgChild1DnaId}"); + } + } + + // ดึง orgChild2DnaId + if (jsonDoc.RootElement.TryGetProperty("orgChild2DnaId", out var orgChild2Element)) + { + if (Guid.TryParse(orgChild2Element.GetString(), out var orgChild2Guid)) + { + result.OrgChild2DnaId = orgChild2Guid; + Console.WriteLine($"Extracted orgChild2DnaId: {result.OrgChild2DnaId}"); + } + } + + // ดึง orgChild3DnaId + if (jsonDoc.RootElement.TryGetProperty("orgChild3DnaId", out var orgChild3Element)) + { + if (Guid.TryParse(orgChild3Element.GetString(), out var orgChild3Guid)) + { + result.OrgChild3DnaId = orgChild3Guid; + Console.WriteLine($"Extracted orgChild3DnaId: {result.OrgChild3DnaId}"); + } + } + + // ดึง orgChild4DnaId + if (jsonDoc.RootElement.TryGetProperty("orgChild4DnaId", out var orgChild4Element)) + { + if (Guid.TryParse(orgChild4Element.GetString(), out var orgChild4Guid)) + { + result.OrgChild4DnaId = orgChild4Guid; + Console.WriteLine($"Extracted orgChild4DnaId: {result.OrgChild4DnaId}"); + } + } + + // ดึง orgRootDnaId + if (jsonDoc.RootElement.TryGetProperty("orgRootDnaId", out var orgRootElement)) + { + if (Guid.TryParse(orgRootElement.GetString(), out var orgRootGuid)) + { + result.OrgRootDnaId = orgRootGuid; + Console.WriteLine($"Extracted orgRootDnaId: {result.OrgRootDnaId}"); + } + } + + // ดึง profileId + if (jsonDoc.RootElement.TryGetProperty("profileId", out var profileIdElement)) + { + if (Guid.TryParse(profileIdElement.GetString(), out var profileIdGuid)) + { + result.ProfileId = profileIdGuid; + Console.WriteLine($"Extracted profileId: {result.ProfileId}"); + } + } + + // ดึง prefix + if (jsonDoc.RootElement.TryGetProperty("prefix", out var prefixElement)) + { + result.Prefix = prefixElement.GetString(); + Console.WriteLine($"Extracted prefix: {result.Prefix}"); + } + + // ดึง name + if (jsonDoc.RootElement.TryGetProperty("name", out var nameElement)) + { + result.Name = nameElement.GetString(); + Console.WriteLine($"Extracted name: {result.Name}"); + } + return result; } catch (Exception ex) @@ -767,14 +874,6 @@ namespace BMA.EHR.Domain.Middlewares } // Model classes - public class TokenUserInfo - { - public string KeycloakId { get; set; } = string.Empty; - public string? PreferredUsername { get; set; } - public string? GivenName { get; set; } - public string? FamilyName { get; set; } - } - public class GetProfileByKeycloakIdLocal { public Guid Id { get; set; } diff --git a/BMA.EHR.Leave/Controllers/LeaveController.cs b/BMA.EHR.Leave/Controllers/LeaveController.cs index d3907e66..acb10e8b 100644 --- a/BMA.EHR.Leave/Controllers/LeaveController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveController.cs @@ -144,7 +144,13 @@ namespace BMA.EHR.Leave.Service.Controllers { get { - if (UserId != null || UserId != "") + // First try to get from claims + var ocIdFromClaims = OrgRootDnaId; + if (ocIdFromClaims.HasValue && ocIdFromClaims.Value != Guid.Empty) + return ocIdFromClaims.Value; + + // Fallback to API call for backward compatibility + if (UserId != null && UserId != "") return _userProfileRepository.GetUserOCId(Guid.Parse(UserId!), AccessToken); else return Guid.Empty; From 23bbd9791e5f1a5f21a2ea3685c7f88714a5aba5 Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Mon, 23 Mar 2026 09:49:17 +0700 Subject: [PATCH 131/178] Add CreateChangeRoundMultipleAsync method for batch processing of duty time changes #1555 --- BMA.EHR.Leave/Controllers/LeaveController.cs | 53 ++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/BMA.EHR.Leave/Controllers/LeaveController.cs b/BMA.EHR.Leave/Controllers/LeaveController.cs index acb10e8b..d9e612e0 100644 --- a/BMA.EHR.Leave/Controllers/LeaveController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveController.cs @@ -2554,6 +2554,59 @@ namespace BMA.EHR.Leave.Service.Controllers } + [HttpPost("round/multiple")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status401Unauthorized)] + [ProducesResponseType(StatusCodes.Status500InternalServerError)] + public async Task> CreateChangeRoundMultipleAsync([FromBody] List reqs) + { + var getPermission = await _permission.GetPermissionAPIAsync("UPDATE", "SYS_WORK_ROUND_EDIT"); + var jsonData = JsonConvert.DeserializeObject(getPermission); + if (jsonData["status"]?.ToString() != "200") + { + return Error(jsonData["message"]?.ToString(), StatusCodes.Status403Forbidden); + } + var currentDate = DateTime.Now.Date; + + foreach(var req in reqs) + { + var profile = await _userProfileRepository.GetProfileByProfileIdAsync(req.ProfileId, AccessToken); + if (profile == null) + { + return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound); + } + + if (req.EffectiveDate.Date < currentDate) + { + return Error(new Exception($"กำหนดรอบลงเวลาของ {profile.FirstName} {profile.LastName} ผิดพลาด เนื่องจากวันที่มีผลต้องมากกว่าหรือเท่ากับวันที่ปัจจุบัน({currentDate.ToString("yyyy-MM-dd")})"), StatusCodes.Status400BadRequest); + } + + var old = await _userDutyTimeRepository.GetExist(req.ProfileId, req.EffectiveDate); + + if (old != null) + { + return Error(new Exception($"กำหนดรอบลงเวลาของ {profile.FirstName} {profile.LastName} ผิดพลาด เนื่องจากมีการกำหนดรอบการทำงานในวันที่นี้ไว้แล้ว"), StatusCodes.Status400BadRequest); + } + + var data = new UserDutyTime + { + ProfileId = req.ProfileId, + DutyTimeId = req.RoundId, + EffectiveDate = req.EffectiveDate, + Remark = req.Remark, + + RootDnaId = profile.RootDnaId, + Child1DnaId = profile.Child1DnaId, + Child2DnaId = profile.Child2DnaId, + Child3DnaId = profile.Child3DnaId, + Child4DnaId = profile.Child4DnaId, + }; + + await _userDutyTimeRepository.AddAsync(data); + } + return Success(); + } + /// /// LV1_015 - ประวัติการเปลี่ยนรอบการลงเวลา (ADMIN) /// From 818ff38e9995c8e3ddae0f09c8d8ec5b6f224fd6 Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Mon, 23 Mar 2026 10:04:09 +0700 Subject: [PATCH 132/178] Add SelectedNodeId parameter to SearchProfile method and update related DTO #1555 --- BMA.EHR.Application/Repositories/UserProfileRepository.cs | 3 ++- BMA.EHR.Leave/Controllers/LeaveController.cs | 2 +- BMA.EHR.Leave/DTOs/ChangeRound/SearchProfileDto.cs | 2 ++ 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/BMA.EHR.Application/Repositories/UserProfileRepository.cs b/BMA.EHR.Application/Repositories/UserProfileRepository.cs index 6ac6b9ed..cb260158 100644 --- a/BMA.EHR.Application/Repositories/UserProfileRepository.cs +++ b/BMA.EHR.Application/Repositories/UserProfileRepository.cs @@ -970,7 +970,7 @@ namespace BMA.EHR.Application.Repositories } } - public async Task SearchProfile(string? citizenId, string? firstName, string? lastName, string accessToken, int page, int pageSize, string? role, string? nodeId, int? node) + public async Task SearchProfile(string? citizenId, string? firstName, string? lastName, string accessToken, int page, int pageSize, string? role, string? nodeId, int? node,Guid? selectedNodeId) { try { @@ -986,6 +986,7 @@ namespace BMA.EHR.Application.Repositories node = node, page = page, pageSize = pageSize, + selectedNodeId = selectedNodeId }; var profiles = new List(); diff --git a/BMA.EHR.Leave/Controllers/LeaveController.cs b/BMA.EHR.Leave/Controllers/LeaveController.cs index d9e612e0..2008620a 100644 --- a/BMA.EHR.Leave/Controllers/LeaveController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveController.cs @@ -2443,7 +2443,7 @@ namespace BMA.EHR.Leave.Service.Controllers { nodeId = profileAdmin?.RootDnaId; } - var profile = await _userProfileRepository.SearchProfile(req.CitizenId, req.FirstName, req.LastName, AccessToken ?? "", req.Page, req.PageSize, role, nodeId, profileAdmin?.Node); + var profile = await _userProfileRepository.SearchProfile(req.CitizenId, req.FirstName, req.LastName, AccessToken ?? "", req.Page, req.PageSize, role, nodeId, profileAdmin?.Node, req.SelectedNodeId); // Get default round once var getDefaultRound = await _dutyTimeRepository.GetDefaultAsync(); diff --git a/BMA.EHR.Leave/DTOs/ChangeRound/SearchProfileDto.cs b/BMA.EHR.Leave/DTOs/ChangeRound/SearchProfileDto.cs index 3b05ad29..4ae9b3c7 100644 --- a/BMA.EHR.Leave/DTOs/ChangeRound/SearchProfileDto.cs +++ b/BMA.EHR.Leave/DTOs/ChangeRound/SearchProfileDto.cs @@ -17,5 +17,7 @@ public string? sortBy { get; set; } public bool? descending { get; set; } + + public Guid? SelectedNodeId { get; set; } } } From 58aca3a328b5e66c719923a0a0796e4ffa71d92e Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Mon, 23 Mar 2026 10:13:13 +0700 Subject: [PATCH 133/178] Add SelectedNode parameter to SearchProfile method for enhanced profile retrieval #1555 --- BMA.EHR.Application/Repositories/UserProfileRepository.cs | 5 +++-- BMA.EHR.Leave/Controllers/LeaveController.cs | 2 +- BMA.EHR.Leave/DTOs/ChangeRound/SearchProfileDto.cs | 2 ++ 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/BMA.EHR.Application/Repositories/UserProfileRepository.cs b/BMA.EHR.Application/Repositories/UserProfileRepository.cs index cb260158..8c5eacb0 100644 --- a/BMA.EHR.Application/Repositories/UserProfileRepository.cs +++ b/BMA.EHR.Application/Repositories/UserProfileRepository.cs @@ -970,7 +970,7 @@ namespace BMA.EHR.Application.Repositories } } - public async Task SearchProfile(string? citizenId, string? firstName, string? lastName, string accessToken, int page, int pageSize, string? role, string? nodeId, int? node,Guid? selectedNodeId) + public async Task SearchProfile(string? citizenId, string? firstName, string? lastName, string accessToken, int page, int pageSize, string? role, string? nodeId, int? node,Guid? selectedNodeId,int? selectedNode ) { try { @@ -986,7 +986,8 @@ namespace BMA.EHR.Application.Repositories node = node, page = page, pageSize = pageSize, - selectedNodeId = selectedNodeId + selectedNodeId = selectedNodeId, + selectedNode = selectedNode }; var profiles = new List(); diff --git a/BMA.EHR.Leave/Controllers/LeaveController.cs b/BMA.EHR.Leave/Controllers/LeaveController.cs index 2008620a..a1b7d381 100644 --- a/BMA.EHR.Leave/Controllers/LeaveController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveController.cs @@ -2443,7 +2443,7 @@ namespace BMA.EHR.Leave.Service.Controllers { nodeId = profileAdmin?.RootDnaId; } - var profile = await _userProfileRepository.SearchProfile(req.CitizenId, req.FirstName, req.LastName, AccessToken ?? "", req.Page, req.PageSize, role, nodeId, profileAdmin?.Node, req.SelectedNodeId); + var profile = await _userProfileRepository.SearchProfile(req.CitizenId, req.FirstName, req.LastName, AccessToken ?? "", req.Page, req.PageSize, role, nodeId, profileAdmin?.Node, req.SelectedNodeId,req.SelectedNode); // Get default round once var getDefaultRound = await _dutyTimeRepository.GetDefaultAsync(); diff --git a/BMA.EHR.Leave/DTOs/ChangeRound/SearchProfileDto.cs b/BMA.EHR.Leave/DTOs/ChangeRound/SearchProfileDto.cs index 4ae9b3c7..69f5cbba 100644 --- a/BMA.EHR.Leave/DTOs/ChangeRound/SearchProfileDto.cs +++ b/BMA.EHR.Leave/DTOs/ChangeRound/SearchProfileDto.cs @@ -19,5 +19,7 @@ public bool? descending { get; set; } public Guid? SelectedNodeId { get; set; } + + public int? SelectedNode { get; set; } } } From 252d8b5fa3b5bbe7677b8bec6796cf640b398499 Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Mon, 23 Mar 2026 10:40:54 +0700 Subject: [PATCH 134/178] Update SearchProfile method parameters to use string for SelectedNodeId #1555 --- BMA.EHR.Application/Repositories/UserProfileRepository.cs | 2 +- BMA.EHR.Leave/Controllers/LeaveController.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/BMA.EHR.Application/Repositories/UserProfileRepository.cs b/BMA.EHR.Application/Repositories/UserProfileRepository.cs index 8c5eacb0..ba1f70c3 100644 --- a/BMA.EHR.Application/Repositories/UserProfileRepository.cs +++ b/BMA.EHR.Application/Repositories/UserProfileRepository.cs @@ -970,7 +970,7 @@ namespace BMA.EHR.Application.Repositories } } - public async Task SearchProfile(string? citizenId, string? firstName, string? lastName, string accessToken, int page, int pageSize, string? role, string? nodeId, int? node,Guid? selectedNodeId,int? selectedNode ) + public async Task SearchProfile(string? citizenId, string? firstName, string? lastName, string accessToken, int page, int pageSize, string? role, string? nodeId, int? node,string? selectedNodeId,int? selectedNode ) { try { diff --git a/BMA.EHR.Leave/Controllers/LeaveController.cs b/BMA.EHR.Leave/Controllers/LeaveController.cs index a1b7d381..23d38e3a 100644 --- a/BMA.EHR.Leave/Controllers/LeaveController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveController.cs @@ -2443,7 +2443,7 @@ namespace BMA.EHR.Leave.Service.Controllers { nodeId = profileAdmin?.RootDnaId; } - var profile = await _userProfileRepository.SearchProfile(req.CitizenId, req.FirstName, req.LastName, AccessToken ?? "", req.Page, req.PageSize, role, nodeId, profileAdmin?.Node, req.SelectedNodeId,req.SelectedNode); + var profile = await _userProfileRepository.SearchProfile(req.CitizenId, req.FirstName, req.LastName, AccessToken ?? "", req.Page, req.PageSize, role, nodeId, profileAdmin?.Node, req.SelectedNodeId == null ? null : req.SelectedNodeId.Value.ToString("D"), req.SelectedNode); // Get default round once var getDefaultRound = await _dutyTimeRepository.GetDefaultAsync(); From 6427cb4344e5d14444f435945fb3f7add44df44d Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Tue, 24 Mar 2026 09:00:06 +0700 Subject: [PATCH 135/178] Comment out probation-related leave limit checks in LeaveRequestController and update appsettings.json to disable unused database connections --- .../Controllers/LeaveRequestController.cs | 16 ++++++++-------- BMA.EHR.Leave/appsettings.json | 6 +++--- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/BMA.EHR.Leave/Controllers/LeaveRequestController.cs b/BMA.EHR.Leave/Controllers/LeaveRequestController.cs index 2300e9ec..8d626d20 100644 --- a/BMA.EHR.Leave/Controllers/LeaveRequestController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveRequestController.cs @@ -898,9 +898,9 @@ namespace BMA.EHR.Leave.Service.Controllers if (leaveType.Code.Trim().ToUpper() == "LV-005") { - if (profile.IsProbation! == true) - leaveLimit = 0; - else + // if (profile.IsProbation! == true) + // leaveLimit = 0; + // else { leaveLimit = leaveData == null ? 10 @@ -912,11 +912,11 @@ namespace BMA.EHR.Leave.Service.Controllers var restOldDay = leaveData == null ? 0 : leaveData.LeaveDays - 10; var restCurrentDay = 10.0; - if (profile.IsProbation! == true) - { - restOldDay = 0; - restCurrentDay = 0; - } + // if (profile.IsProbation! == true) + // { + // restOldDay = 0; + // restCurrentDay = 0; + // } if(restOldDay < 0) restOldDay = 0; var sumLeave = leaveData == null ? 0 : leaveData.LeaveDaysUsed; diff --git a/BMA.EHR.Leave/appsettings.json b/BMA.EHR.Leave/appsettings.json index 40b464cf..f8562d6e 100644 --- a/BMA.EHR.Leave/appsettings.json +++ b/BMA.EHR.Leave/appsettings.json @@ -23,9 +23,9 @@ "ExamConnection": "server=192.168.1.63;user=root;password=12345678;port=3306;database=hrms_exam;Convert Zero Datetime=True;Allow User Variables=true;Pooling=True;", "LeaveConnection": "server=192.168.1.63;user=root;password=12345678;port=3306;database=hrms_leave;Convert Zero Datetime=True;Allow User Variables=true;Pooling=True;" - // "DefaultConnection": "server=172.27.17.68;user=user;password=cDldaqkwESWvuZ37Gr0n;port=3306;database=hrms;Convert Zero Datetime=True;Allow User Variables=true;Pooling=True;", - // "ExamConnection": "server=172.27.17.68;user=user;password=cDldaqkwESWvuZ37Gr0n;port=3306;database=hrms_exam;Convert Zero Datetime=True;Allow User Variables=true;Pooling=True;", - // "LeaveConnection": "server=172.27.17.68;user=user;password=cDldaqkwESWvuZ37Gr0n;port=3306;database=hrms_leave;Convert Zero Datetime=True;Allow User Variables=true;Pooling=True;" + //"DefaultConnection": "server=172.27.17.68;user=user;password=cDldaqkwESWvuZ37Gr0n;port=3306;database=hrms;Convert Zero Datetime=True;Allow User Variables=true;Pooling=True;", + //"ExamConnection": "server=172.27.17.68;user=user;password=cDldaqkwESWvuZ37Gr0n;port=3306;database=hrms_exam;Convert Zero Datetime=True;Allow User Variables=true;Pooling=True;", + //"LeaveConnection": "server=172.27.17.68;user=user;password=cDldaqkwESWvuZ37Gr0n;port=3306;database=hrms_leave;Convert Zero Datetime=True;Allow User Variables=true;Pooling=True;" }, "Jwt": { //"Key": "HP-FnQMUj9msHMSD3T9HtdEnphAKoCJLEl85CIqROFI", From aef81e9f4e7b8de198e30095c318b1aa2dac0f2c Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Wed, 25 Mar 2026 15:17:54 +0700 Subject: [PATCH 136/178] Add support for multiple child DNA IDs in leave processing and enhance batch creation of duty time changes --- .../Leaves/GenericLeaveRepository.cs | 18 ++++++++ .../Profiles/GetProfileByKeycloakIdRootDto.cs | 6 +++ BMA.EHR.Leave/Controllers/LeaveController.cs | 46 ++++++++++++------- .../DTOs/ChangeRound/CreateChangeRoundDto.cs | 21 +++++++++ .../ChangeRound/SearchProfileResultDto.cs | 6 +++ 5 files changed, 81 insertions(+), 16 deletions(-) diff --git a/BMA.EHR.Application/Repositories/Leaves/GenericLeaveRepository.cs b/BMA.EHR.Application/Repositories/Leaves/GenericLeaveRepository.cs index 5fbbb8a1..93ae9cec 100644 --- a/BMA.EHR.Application/Repositories/Leaves/GenericLeaveRepository.cs +++ b/BMA.EHR.Application/Repositories/Leaves/GenericLeaveRepository.cs @@ -68,6 +68,24 @@ namespace BMA.EHR.Application.Repositories.Leaves return entity; } + public virtual async Task> AddRangeAsync(List entities) + { + foreach (var entity in entities) + { + if (entity is EntityBase) + { + (entity as EntityBase).CreatedUserId = UserId ?? ""; + (entity as EntityBase).CreatedFullName = FullName ?? "System Administrator"; + (entity as EntityBase).CreatedAt = DateTime.Now; + } + } + + await _dbSet.AddRangeAsync(entities); + await _dbContext.SaveChangesAsync(); + + return entities; + } + public virtual async Task UpdateAsync(T entity) { if (entity is EntityBase) diff --git a/BMA.EHR.Application/Responses/Profiles/GetProfileByKeycloakIdRootDto.cs b/BMA.EHR.Application/Responses/Profiles/GetProfileByKeycloakIdRootDto.cs index 1110ca9e..2eff51dd 100644 --- a/BMA.EHR.Application/Responses/Profiles/GetProfileByKeycloakIdRootDto.cs +++ b/BMA.EHR.Application/Responses/Profiles/GetProfileByKeycloakIdRootDto.cs @@ -25,6 +25,12 @@ namespace BMA.EHR.Application.Responses.Profiles public DateTime? DateStart { get; set; } public DateTime? DateAppoint { get; set; } + + public string? RootDnaId { get; set; } + public string? Child1DnaId { get; set; } + public string? Child2DnaId { get; set; } + public string? Child3DnaId { get; set; } + public string? Child4DnaId { get; set; } } public class GetProfileByKeycloakIdRootAddTotalDto diff --git a/BMA.EHR.Leave/Controllers/LeaveController.cs b/BMA.EHR.Leave/Controllers/LeaveController.cs index 23d38e3a..0adb3a3a 100644 --- a/BMA.EHR.Leave/Controllers/LeaveController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveController.cs @@ -2487,7 +2487,16 @@ namespace BMA.EHR.Leave.Service.Controllers FullName = $"{p.Prefix ?? ""}{p.FirstName ?? ""} {p.LastName ?? ""}", StartTimeMorning = duty.StartTimeMorning, LeaveTimeAfterNoon = duty.EndTimeAfternoon, - EffectiveDate = effectiveDate?.EffectiveDate?.Date + EffectiveDate = effectiveDate?.EffectiveDate?.Date, + Prefix = p.Prefix ?? "", + FirstName = p.FirstName ?? "", + LastName = p.LastName ?? "", + RootDnaId = p.RootDnaId, + Child1DnaId = p.Child1DnaId, + Child2DnaId = p.Child2DnaId, + Child3DnaId = p.Child3DnaId, + Child4DnaId = p.Child4DnaId + }; resultSet.Add(res); } @@ -2558,7 +2567,7 @@ namespace BMA.EHR.Leave.Service.Controllers [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] [ProducesResponseType(StatusCodes.Status500InternalServerError)] - public async Task> CreateChangeRoundMultipleAsync([FromBody] List reqs) + public async Task> CreateChangeRoundMultipleAsync([FromBody] List reqs) { var getPermission = await _permission.GetPermissionAPIAsync("UPDATE", "SYS_WORK_ROUND_EDIT"); var jsonData = JsonConvert.DeserializeObject(getPermission); @@ -2568,24 +2577,28 @@ namespace BMA.EHR.Leave.Service.Controllers } var currentDate = DateTime.Now.Date; + List dataList = new List(); + foreach(var req in reqs) { - var profile = await _userProfileRepository.GetProfileByProfileIdAsync(req.ProfileId, AccessToken); - if (profile == null) - { - return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound); - } + // var profile = await _userProfileRepository.GetProfileByProfileIdAsync(req.ProfileId, AccessToken); + // if (profile == null) + // { + // return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound); + // } if (req.EffectiveDate.Date < currentDate) { - return Error(new Exception($"กำหนดรอบลงเวลาของ {profile.FirstName} {profile.LastName} ผิดพลาด เนื่องจากวันที่มีผลต้องมากกว่าหรือเท่ากับวันที่ปัจจุบัน({currentDate.ToString("yyyy-MM-dd")})"), StatusCodes.Status400BadRequest); + continue; // move to next item if effective date is in the past, not return error + // return Error(new Exception($"กำหนดรอบลงเวลาของ {req.FirstName} {req.LastName} ผิดพลาด เนื่องจากวันที่มีผลต้องมากกว่าหรือเท่ากับวันที่ปัจจุบัน({currentDate.ToString("yyyy-MM-dd")})"), StatusCodes.Status400BadRequest); } var old = await _userDutyTimeRepository.GetExist(req.ProfileId, req.EffectiveDate); if (old != null) { - return Error(new Exception($"กำหนดรอบลงเวลาของ {profile.FirstName} {profile.LastName} ผิดพลาด เนื่องจากมีการกำหนดรอบการทำงานในวันที่นี้ไว้แล้ว"), StatusCodes.Status400BadRequest); + continue; // move to next item if already exist, not return error + //return Error(new Exception($"กำหนดรอบลงเวลาของ {req.FirstName} {req.LastName} ผิดพลาด เนื่องจากมีการกำหนดรอบการทำงานในวันที่นี้ไว้แล้ว"), StatusCodes.Status400BadRequest); } var data = new UserDutyTime @@ -2595,15 +2608,16 @@ namespace BMA.EHR.Leave.Service.Controllers EffectiveDate = req.EffectiveDate, Remark = req.Remark, - RootDnaId = profile.RootDnaId, - Child1DnaId = profile.Child1DnaId, - Child2DnaId = profile.Child2DnaId, - Child3DnaId = profile.Child3DnaId, - Child4DnaId = profile.Child4DnaId, + RootDnaId = req.RootDnaId, + Child1DnaId = req.Child1DnaId, + Child2DnaId = req.Child2DnaId, + Child3DnaId = req.Child3DnaId, + Child4DnaId = req.Child4DnaId, }; - - await _userDutyTimeRepository.AddAsync(data); + dataList.Add(data); } + + await _userDutyTimeRepository.AddRangeAsync(dataList); return Success(); } diff --git a/BMA.EHR.Leave/DTOs/ChangeRound/CreateChangeRoundDto.cs b/BMA.EHR.Leave/DTOs/ChangeRound/CreateChangeRoundDto.cs index 6ad96fcd..5379abda 100644 --- a/BMA.EHR.Leave/DTOs/ChangeRound/CreateChangeRoundDto.cs +++ b/BMA.EHR.Leave/DTOs/ChangeRound/CreateChangeRoundDto.cs @@ -12,4 +12,25 @@ namespace BMA.EHR.Leave.Service.DTOs.ChangeRound public string Remark { get; set; } } + + public class CreateChangeRoundMultipleDto + { + public Guid ProfileId { get; set; } + + public Guid RoundId { get; set; } + + public DateTime EffectiveDate { get; set; } + + public string Remark { get; set; } + + public Guid? RootDnaId { get; set; } + public Guid? Child1DnaId { get; set; } + public Guid? Child2DnaId { get; set; } + public Guid? Child3DnaId { get; set; } + public Guid? Child4DnaId { get; set; } + + public string? Prefix { get; set; } + public string? FirstName { get; set; } + public string? LastName { get; set; } + } } diff --git a/BMA.EHR.Leave/DTOs/ChangeRound/SearchProfileResultDto.cs b/BMA.EHR.Leave/DTOs/ChangeRound/SearchProfileResultDto.cs index 83b4d7b9..00df91a9 100644 --- a/BMA.EHR.Leave/DTOs/ChangeRound/SearchProfileResultDto.cs +++ b/BMA.EHR.Leave/DTOs/ChangeRound/SearchProfileResultDto.cs @@ -17,5 +17,11 @@ public string LeaveTimeAfterNoon { get;set; } public DateTime? EffectiveDate { get; set; } + + public string? RootDnaId { get; set; } + public string? Child1DnaId { get; set; } + public string? Child2DnaId { get; set; } + public string? Child3DnaId { get; set; } + public string? Child4DnaId { get; set; } } } From a09d5937f9f170d3b96d1346219b34c10d95e3cd Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Thu, 26 Mar 2026 10:33:00 +0700 Subject: [PATCH 137/178] Add leave subtype and couple day level country to leave approval response #2366 --- .../Repositories/Leaves/LeaveRequests/LeaveRequestRepository.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/BMA.EHR.Application/Repositories/Leaves/LeaveRequests/LeaveRequestRepository.cs b/BMA.EHR.Application/Repositories/Leaves/LeaveRequests/LeaveRequestRepository.cs index 2caecc9f..d01dbc55 100644 --- a/BMA.EHR.Application/Repositories/Leaves/LeaveRequests/LeaveRequestRepository.cs +++ b/BMA.EHR.Application/Repositories/Leaves/LeaveRequests/LeaveRequestRepository.cs @@ -1285,6 +1285,8 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests status = "approve", reason = rawData.LeaveDetail, leaveId = rawData.Id, + leaveSubTypeName = rawData.LeaveSubTypeName, + coupleDayLevelCountry = rawData.CoupleDayLevelCountry, }); // var _result = await _res.Content.ReadAsStringAsync(); } From 19b79a162d5cb2f02b182f62af696e5f564903b2 Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Thu, 26 Mar 2026 10:33:44 +0700 Subject: [PATCH 138/178] Add leave subtype name and couple day level country to leave approval response (Employee)#2366 --- .../Repositories/Leaves/LeaveRequests/LeaveRequestRepository.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/BMA.EHR.Application/Repositories/Leaves/LeaveRequests/LeaveRequestRepository.cs b/BMA.EHR.Application/Repositories/Leaves/LeaveRequests/LeaveRequestRepository.cs index d01dbc55..f465191e 100644 --- a/BMA.EHR.Application/Repositories/Leaves/LeaveRequests/LeaveRequestRepository.cs +++ b/BMA.EHR.Application/Repositories/Leaves/LeaveRequests/LeaveRequestRepository.cs @@ -1310,6 +1310,8 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests status = "approve", reason = rawData.LeaveDetail, leaveId = rawData.Id, + leaveSubTypeName = rawData.LeaveSubTypeName, + coupleDayLevelCountry = rawData.CoupleDayLevelCountry, }); } } From 3e3bfff7ba3d3d5c2159f7b275b196538c65b564 Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Thu, 26 Mar 2026 14:10:37 +0700 Subject: [PATCH 139/178] Refactor leave date overlap check in LeaveRequestController for improved readability and performance --- .../Controllers/LeaveRequestController.cs | 50 ++++++++++++------- 1 file changed, 31 insertions(+), 19 deletions(-) diff --git a/BMA.EHR.Leave/Controllers/LeaveRequestController.cs b/BMA.EHR.Leave/Controllers/LeaveRequestController.cs index 8d626d20..7bc3b151 100644 --- a/BMA.EHR.Leave/Controllers/LeaveRequestController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveRequestController.cs @@ -1070,26 +1070,38 @@ namespace BMA.EHR.Leave.Service.Controllers fiscalYear = req.StartLeaveDate.Year + 1; var sumLeaveDay = await _leaveBeginningRepository.GetByYearAndTypeIdForUserAsync(fiscalYear, req.Type, userId); - - - var minLeave = (await _context.Set().Where(x => x.Type.Id == req.Type && - (x.LeaveStatus == "PENDING" || x.LeaveStatus == "APPROVE") && - x.KeycloakUserId == userId) - .OrderBy(x => x.LeaveStartDate) - .FirstOrDefaultAsync()); - - var maxLeave = (await _context.Set().Where(x => x.Type.Id == req.Type && - (x.LeaveStatus == "PENDING" || x.LeaveStatus == "APPROVE") && - x.KeycloakUserId == userId) - .OrderByDescending(x => x.LeaveEndDate) - .FirstOrDefaultAsync()); - var isBetween = false; - if (minLeave != null && maxLeave != null) - { - isBetween = (req.StartLeaveDate.Date >= minLeave.LeaveStartDate.Date && req.StartLeaveDate.Date <= maxLeave.LeaveEndDate.Date) || - (req.EndLeaveDate.Date >= minLeave.LeaveStartDate.Date && req.EndLeaveDate.Date <= maxLeave.LeaveEndDate.Date); - } + var existingLeaves = await _context.Set() + .Where(x => x.Type.Id == req.Type && + (x.LeaveStatus == "PENDING" || x.LeaveStatus == "APPROVE") && + x.KeycloakUserId == userId) + .ToListAsync(); + + isBetween = existingLeaves.Any(leave => + req.StartLeaveDate.Date <= leave.LeaveEndDate.Date && + req.EndLeaveDate.Date >= leave.LeaveStartDate.Date); + + // var minLeave = (await _context.Set().Where(x => x.Type.Id == req.Type && + // (x.LeaveStatus == "PENDING" || x.LeaveStatus == "APPROVE") && + // x.KeycloakUserId == userId) + // .OrderBy(x => x.LeaveStartDate) + // .FirstOrDefaultAsync()); + + // var maxLeave = (await _context.Set().Where(x => x.Type.Id == req.Type && + // (x.LeaveStatus == "PENDING" || x.LeaveStatus == "APPROVE") && + // x.KeycloakUserId == userId) + // .OrderByDescending(x => x.LeaveEndDate) + // .FirstOrDefaultAsync()); + + // var isBetween = false; + // if (minLeave != null && maxLeave != null) + // { + // // isBetween = (req.StartLeaveDate.Date >= minLeave.LeaveStartDate.Date && req.StartLeaveDate.Date <= maxLeave.LeaveEndDate.Date) || + // // (req.EndLeaveDate.Date >= minLeave.LeaveStartDate.Date && req.EndLeaveDate.Date <= maxLeave.LeaveEndDate.Date); + // isBetween = req.StartLeaveDate.Date <= maxLeave.LeaveEndDate.Date && + // req.EndLeaveDate.Date >= minLeave.LeaveStartDate.Date; + + // } var isLeave = false; var approveDay = sumLeaveDay == null ? 0.0 : sumLeaveDay.LeaveDaysUsed; From 7ba429bb64ca711ef27c4e148ab23452bdc457da Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Fri, 27 Mar 2026 09:48:10 +0700 Subject: [PATCH 140/178] Refactor checkout status logic in LeaveController for improved clarity and handling of check-in dates --- BMA.EHR.Leave/Controllers/LeaveController.cs | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/BMA.EHR.Leave/Controllers/LeaveController.cs b/BMA.EHR.Leave/Controllers/LeaveController.cs index 0adb3a3a..2f033adc 100644 --- a/BMA.EHR.Leave/Controllers/LeaveController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveController.cs @@ -1412,11 +1412,20 @@ namespace BMA.EHR.Leave.Service.Controllers var leaveRange = leaveReq.LeaveRangeEnd == null ? "" : leaveReq.LeaveRangeEnd.ToUpper(); if (leaveRange == "AFTERNOON" || leaveRange == "ALL") { - if(DateTime.Parse(currentDate.ToString("yyyy-MM-dd HH:mm")) < - DateTime.Parse($"{currentDate.ToString("yyyy-MM-dd")} {endTimeMorning}")) - checkOutStatus = "ABSENT"; - else + if (checkout.CheckIn.Date < currentDate.Date) + { + // ถ้า check-out เป็นวันถัดไป สถานะปกติเสมอ checkOutStatus = "NORMAL"; + } + else + { + if(DateTime.Parse(currentDate.ToString("yyyy-MM-dd HH:mm")) < + DateTime.Parse($"{currentDate.ToString("yyyy-MM-dd")} {endTimeMorning}")) + checkOutStatus = "ABSENT"; + else + checkOutStatus = "NORMAL"; + } + } else { From d8f11267645d217398d6bb0b38f33d45cb1740f7 Mon Sep 17 00:00:00 2001 From: harid Date: Fri, 27 Mar 2026 14:31:42 +0700 Subject: [PATCH 141/178] =?UTF-8?q?fix=20=E0=B8=AA=E0=B9=88=E0=B8=87?= =?UTF-8?q?=E0=B8=A3=E0=B8=B2=E0=B8=A2=E0=B8=8A=E0=B8=B7=E0=B9=88=E0=B8=AD?= =?UTF-8?q?=E0=B8=9C=E0=B8=B9=E0=B9=89=E0=B8=96=E0=B8=B9=E0=B8=81=E0=B8=9E?= =?UTF-8?q?=E0=B8=B1=E0=B8=81=E0=B8=A3=E0=B8=B2=E0=B8=8A=E0=B8=81=E0=B8=B2?= =?UTF-8?q?=E0=B8=A3=E0=B9=84=E0=B8=9B=E0=B8=AD=E0=B8=AD=E0=B8=81=E0=B8=84?= =?UTF-8?q?=E0=B8=B3=E0=B8=AA=E0=B8=B1=E0=B9=88=E0=B8=87=20#2364?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../DisciplineSuspendController.cs | 244 +++++++++--------- 1 file changed, 125 insertions(+), 119 deletions(-) diff --git a/BMA.EHR.Discipline.Service/Controllers/DisciplineSuspendController.cs b/BMA.EHR.Discipline.Service/Controllers/DisciplineSuspendController.cs index 39b5f06a..3e171f9b 100644 --- a/BMA.EHR.Discipline.Service/Controllers/DisciplineSuspendController.cs +++ b/BMA.EHR.Discipline.Service/Controllers/DisciplineSuspendController.cs @@ -71,6 +71,13 @@ namespace BMA.EHR.DisciplineSuspend.Service.Controllers { return Error(jsonData["message"]?.ToString(), StatusCodes.Status403Forbidden); } + + // ถ้า FE ส่ง status = PENDING กรอง start/end suspend not null + bool isPending = + !string.IsNullOrEmpty(profileType) && + !string.IsNullOrEmpty(status) && + status.Trim().ToUpper() == "PENDING"; + // กรองสิทธิ์ string role = jsonData["result"]?.ToString() ?? ""; var nodeId = string.Empty; @@ -109,7 +116,7 @@ namespace BMA.EHR.DisciplineSuspend.Service.Controllers var data_search = (from x in _context.DisciplineReport_Profiles.Include(x => x.DisciplineDisciplinary) where ( - endDate != null && startDate != null? + endDate != null && startDate != null ? ( (x.StartDateSuspend.Value.Date >= startDate.Value.Date && x.StartDateSuspend.Value.Date <= endDate.Value.Date) || (x.EndDateSuspend.Value.Date >= startDate.Value.Date && x.EndDateSuspend.Value.Date <= endDate.Value.Date) || @@ -137,14 +144,19 @@ namespace BMA.EHR.DisciplineSuspend.Service.Controllers ( !string.IsNullOrEmpty(status) ? x.Status!.Trim().ToUpper() == status : true ) + // ถ้า FE ส่ง status = PENDING กรอง start/end suspend not null + && + ( + isPending + ? x.StartDateSuspend != null && x.EndDateSuspend != null + : true + ) && ( role == "OWNER" ? true : role == "ROOT" ? x.rootDnaId == nodeId - // : role == "PARENT" - // ? x.rootDnaId == nodeId && x.child1DnaId != null : role == "CHILD" ? ( profileAdmin.Node == 4 ? x.child4DnaId == nodeId : @@ -177,125 +189,119 @@ namespace BMA.EHR.DisciplineSuspend.Service.Controllers ) select x).ToList(); var query = data_search - .Select(x => new - { - Id = x.Id, - CitizenId = x.CitizenId, - Prefix = x.Prefix, - FirstName = x.FirstName, - LastName = x.LastName, - ProfileId = x.PersonId, - Organization = x.Organization, - root = x.root, - rootId = x.rootId, - rootDnaId = x.rootDnaId, - rootShortName = x.rootShortName, - child1 = x.child1, - child1Id = x.child1Id, - child1DnaId = x.child1DnaId, - child1ShortName = x.child1ShortName, - child2 = x.child2, - child2Id = x.child2Id, - child2DnaId = x.child2DnaId, - child2ShortName = x.child2ShortName, - child3 = x.child3, - child3Id = x.child3Id, - child3DnaId = x.child3DnaId, - child3ShortName = x.child3ShortName, - child4 = x.child4, - child4Id = x.child4Id, - child4DnaId = x.child4DnaId, - child4ShortName = x.child4ShortName, - posMasterNo = x.posMasterNo, - posTypeId = x.posTypeId, - posTypeName = x.posTypeName, - posLevelId = x.posLevelId, - posLevelName = x.posLevelName, + .Select(x => new + { + Id = x.Id, + CitizenId = x.CitizenId, + Prefix = x.Prefix, + FirstName = x.FirstName, + LastName = x.LastName, + ProfileId = x.PersonId, + Organization = x.Organization, + root = x.root, + rootId = x.rootId, + rootDnaId = x.rootDnaId, + rootShortName = x.rootShortName, + child1 = x.child1, + child1Id = x.child1Id, + child1DnaId = x.child1DnaId, + child1ShortName = x.child1ShortName, + child2 = x.child2, + child2Id = x.child2Id, + child2DnaId = x.child2DnaId, + child2ShortName = x.child2ShortName, + child3 = x.child3, + child3Id = x.child3Id, + child3DnaId = x.child3DnaId, + child3ShortName = x.child3ShortName, + child4 = x.child4, + child4Id = x.child4Id, + child4DnaId = x.child4DnaId, + child4ShortName = x.child4ShortName, + posMasterNo = x.posMasterNo, + posTypeId = x.posTypeId, + posTypeName = x.posTypeName, + posLevelId = x.posLevelId, + posLevelName = x.posLevelName, - Position = x.Position, - PosNo = x.PosNo, - PositionLevel = x.PositionLevel == null ? "" : x.PositionLevel, - PositionType = x.PositionType == null ? "" : x.PositionType, - Salary = x.Salary, - Status = x.Status, - DescriptionSuspend = x.DescriptionSuspend, - StartDateSuspend = x.StartDateSuspend, - EndDateSuspend = x.EndDateSuspend, - Title = x.DisciplineDisciplinary.Title, - OffenseDetails = x.DisciplineDisciplinary.OffenseDetails,//ลักษณะความผิด - DisciplinaryFaultLevel = x.DisciplineDisciplinary.DisciplinaryFaultLevel,//ระดับโทษความผิด - DisciplinaryCaseFault = x.DisciplineDisciplinary.DisciplinaryCaseFault,//กรณีความผิด - profileType = x.profileType, - CreatedAt = x.CreatedAt, - }); + Position = x.Position, + PosNo = x.PosNo, + PositionLevel = x.PositionLevel == null ? "" : x.PositionLevel, + PositionType = x.PositionType == null ? "" : x.PositionType, + Salary = x.Salary, + Status = x.Status, + DescriptionSuspend = x.DescriptionSuspend, + StartDateSuspend = x.StartDateSuspend, + EndDateSuspend = x.EndDateSuspend, + Title = x.DisciplineDisciplinary.Title, + OffenseDetails = x.DisciplineDisciplinary.OffenseDetails,//ลักษณะความผิด + DisciplinaryFaultLevel = x.DisciplineDisciplinary.DisciplinaryFaultLevel,//ระดับโทษความผิด + DisciplinaryCaseFault = x.DisciplineDisciplinary.DisciplinaryCaseFault,//กรณีความผิด + profileType = x.profileType, + CreatedAt = x.CreatedAt, + }); - bool desc = descending ?? false; - if (!string.IsNullOrEmpty(sortBy)) - { - if (sortBy == "title") - { - query = desc ? query.OrderByDescending(x => x.Title) - : query.OrderBy(x => x.Title); - } - else if (sortBy == "prefix" || sortBy == "firstName" || sortBy == "lastName") - { - query = desc ? - query - //.OrderByDescending(x => x.Prefix) - .OrderByDescending(x => x.FirstName) - .ThenByDescending(x => x.LastName) : - query - //.OrderBy(x => x.Prefix) - .OrderBy(x => x.FirstName) - .ThenBy(x => x.LastName); - } - else if (sortBy == "position") - { - query = desc ? query.OrderByDescending(x => x.Position) - : query.OrderBy(x => x.Position); - } - else if (sortBy == "positionType" || sortBy == "positionLevel") - { - query = desc ? - query - .OrderByDescending(x => x.PositionType) - .ThenByDescending(x => x.PositionLevel) : - query - .OrderBy(x => x.PositionType) - .ThenBy(x => x.PositionLevel); - } - else if (sortBy == "organization") - { - query = desc ? query.OrderByDescending(x => x.Organization) - : query.OrderBy(x => x.Organization); - } - else if (sortBy == "startDateSuspend") - { - query = desc ? query.OrderByDescending(x => x.StartDateSuspend) - : query.OrderBy(x => x.StartDateSuspend); - } - else if (sortBy == "endDateSuspend") - { - query = desc ? query.OrderByDescending(x => x.EndDateSuspend) - : query.OrderBy(x => x.EndDateSuspend); - } - else if (sortBy == "descriptionSuspend") - { - query = desc ? query.OrderByDescending(x => x.DescriptionSuspend) - : query.OrderBy(x => x.DescriptionSuspend); - } - else - { - query = query.OrderByDescending(x => x.profileType) - .ThenByDescending(x => x.CreatedAt) - .ThenByDescending(x => x.CitizenId); - } - } + bool desc = descending ?? false; + if (!string.IsNullOrEmpty(sortBy)) + { + if (sortBy == "title") + { + query = desc ? query.OrderByDescending(x => x.Title) + : query.OrderBy(x => x.Title); + } + else if (sortBy == "prefix" || sortBy == "firstName" || sortBy == "lastName") + { + query = desc ? + query.OrderByDescending(x => x.FirstName).ThenByDescending(x => x.LastName) : + query.OrderBy(x => x.FirstName).ThenBy(x => x.LastName); + } + else if (sortBy == "position") + { + query = desc ? query.OrderByDescending(x => x.Position) + : query.OrderBy(x => x.Position); + } + else if (sortBy == "positionType" || sortBy == "positionLevel") + { + query = desc ? + query + .OrderByDescending(x => x.PositionType) + .ThenByDescending(x => x.PositionLevel) : + query + .OrderBy(x => x.PositionType) + .ThenBy(x => x.PositionLevel); + } + else if (sortBy == "organization") + { + query = desc ? query.OrderByDescending(x => x.Organization) + : query.OrderBy(x => x.Organization); + } + else if (sortBy == "startDateSuspend") + { + query = desc ? query.OrderByDescending(x => x.StartDateSuspend) + : query.OrderBy(x => x.StartDateSuspend); + } + else if (sortBy == "endDateSuspend") + { + query = desc ? query.OrderByDescending(x => x.EndDateSuspend) + : query.OrderBy(x => x.EndDateSuspend); + } + else if (sortBy == "descriptionSuspend") + { + query = desc ? query.OrderByDescending(x => x.DescriptionSuspend) + : query.OrderBy(x => x.DescriptionSuspend); + } + else + { + query = query.OrderByDescending(x => x.profileType) + .ThenByDescending(x => x.CreatedAt) + .ThenByDescending(x => x.CitizenId); + } + } - var data = query - .Skip((page - 1) * pageSize) - .Take(pageSize) - .ToList(); + var data = query + .Skip((page - 1) * pageSize) + .Take(pageSize) + .ToList(); return Success(new { data, total = data_search.Count() }); } From c91e6c80307aba151522405ba3f5656778016259 Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Mon, 30 Mar 2026 09:23:13 +0700 Subject: [PATCH 142/178] Add migration for LeaveProcessJobStatuses table - Created a new migration to add the LeaveProcessJobStatuses table. - The table includes fields for job status, timestamps, user information, and error messages. - Supports tracking of leave process job statuses with relevant metadata. --- .../TimeAttendants/LeaveProcessJobStatus.cs | 37 + ...9_Add Leave Process Job Status.Designer.cs | 1802 +++++++++++++++++ ...0330020909_Add Leave Process Job Status.cs | 54 + .../LeaveDb/LeaveDbContextModelSnapshot.cs | 85 + .../Persistence/LeaveDbContext.cs | 2 + 5 files changed, 1980 insertions(+) create mode 100644 BMA.EHR.Domain/Models/Leave/TimeAttendants/LeaveProcessJobStatus.cs create mode 100644 BMA.EHR.Infrastructure/Migrations/LeaveDb/20260330020909_Add Leave Process Job Status.Designer.cs create mode 100644 BMA.EHR.Infrastructure/Migrations/LeaveDb/20260330020909_Add Leave Process Job Status.cs diff --git a/BMA.EHR.Domain/Models/Leave/TimeAttendants/LeaveProcessJobStatus.cs b/BMA.EHR.Domain/Models/Leave/TimeAttendants/LeaveProcessJobStatus.cs new file mode 100644 index 00000000..be986189 --- /dev/null +++ b/BMA.EHR.Domain/Models/Leave/TimeAttendants/LeaveProcessJobStatus.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Threading.Tasks; +using BMA.EHR.Domain.Models.Base; +using Microsoft.EntityFrameworkCore; + +namespace BMA.EHR.Domain.Models.Leave.TimeAttendants +{ + public class LeaveProcessJobStatus: EntityBase + { + [Required, Comment("วันเริ่มต้น")] + public DateTime StartDate { get; set; } + + [Required, Comment("วันสิ้นสุด")] + public DateTime EndDate { get; set; } + + [Required, Comment("รหัส Root DNA Id")] + public Guid RootDnaId { get; set; } = Guid.Empty; + + [Comment("วันเวลาที่สร้างงาน")] + public DateTime CreatedDate { get; set; } = DateTime.Now; + + [Comment("วันเวลาที่เริ่มประมวลผล")] + public DateTime? ProcessingDate { get; set; } + + [Comment("วันเวลาที่เสร็จสิ้นการประมวลผล")] + public DateTime? CompletedDate { get; set; } + + [Required, Comment("สถานะงาน: PENDING, PROCESSING, COMPLETED, FAILED")] + public string Status { get; set; } = "PENDING"; + + [Comment("ข้อความแสดงข้อผิดพลาด")] + public string? ErrorMessage { get; set; } + } +} \ No newline at end of file diff --git a/BMA.EHR.Infrastructure/Migrations/LeaveDb/20260330020909_Add Leave Process Job Status.Designer.cs b/BMA.EHR.Infrastructure/Migrations/LeaveDb/20260330020909_Add Leave Process Job Status.Designer.cs new file mode 100644 index 00000000..815f8188 --- /dev/null +++ b/BMA.EHR.Infrastructure/Migrations/LeaveDb/20260330020909_Add Leave Process Job Status.Designer.cs @@ -0,0 +1,1802 @@ +// +using System; +using BMA.EHR.Infrastructure.Persistence; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace BMA.EHR.Infrastructure.Migrations.LeaveDb +{ + [DbContext(typeof(LeaveDbContext))] + [Migration("20260330020909_Add Leave Process Job Status")] + partial class AddLeaveProcessJobStatus + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "7.0.9") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + modelBuilder.Entity("BMA.EHR.Domain.Models.Documents.Document", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedDate") + .HasColumnType("datetime(6)"); + + b.Property("Detail") + .IsRequired() + .HasColumnType("text"); + + b.Property("FileName") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("FileSize") + .HasColumnType("int"); + + b.Property("FileType") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("varchar(128)"); + + b.Property("ObjectRefId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.ToTable("Document"); + }); + + modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.Commons.LeaveType", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)") + .HasColumnOrder(0) + .HasComment("PrimaryKey") + .HasAnnotation("Relational:JsonPropertyName", "id"); + + b.Property("Code") + .IsRequired() + .HasColumnType("longtext") + .HasComment("รหัสประเภทการลา"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(100) + .HasComment("สร้างข้อมูลเมื่อ"); + + b.Property("CreatedFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(104) + .HasComment("ชื่อ User ที่สร้างข้อมูล"); + + b.Property("CreatedUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(101) + .HasComment("User Id ที่สร้างข้อมูล"); + + b.Property("LastUpdateFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(105) + .HasComment("ชื่อ User ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdateUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(103) + .HasComment("User Id ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(102) + .HasComment("แก้ไขข้อมูลล่าสุดเมื่อ"); + + b.Property("Limit") + .HasColumnType("int") + .HasComment("จำนวนวันลาสูงสุดประจำปี"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext") + .HasComment("ชื่อประเภทการลา"); + + b.HasKey("Id"); + + b.ToTable("LeaveTypes"); + }); + + modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.Requests.LeaveBeginning", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)") + .HasColumnOrder(0) + .HasComment("PrimaryKey") + .HasAnnotation("Relational:JsonPropertyName", "id"); + + b.Property("BeginningLeaveCount") + .HasColumnType("int") + .HasComment("จำนวนครั้งที่ลายกมา"); + + b.Property("BeginningLeaveDays") + .HasColumnType("double") + .HasComment("จำนวนวันลายกมา"); + + b.Property("Child1DnaId") + .HasColumnType("char(36)"); + + b.Property("Child2DnaId") + .HasColumnType("char(36)"); + + b.Property("Child3DnaId") + .HasColumnType("char(36)"); + + b.Property("Child4DnaId") + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(100) + .HasComment("สร้างข้อมูลเมื่อ"); + + b.Property("CreatedFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(104) + .HasComment("ชื่อ User ที่สร้างข้อมูล"); + + b.Property("CreatedUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(101) + .HasComment("User Id ที่สร้างข้อมูล"); + + b.Property("FirstName") + .HasColumnType("longtext"); + + b.Property("LastName") + .HasColumnType("longtext"); + + b.Property("LastUpdateFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(105) + .HasComment("ชื่อ User ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdateUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(103) + .HasComment("User Id ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(102) + .HasComment("แก้ไขข้อมูลล่าสุดเมื่อ"); + + b.Property("LeaveCount") + .HasColumnType("int") + .HasComment("จำนวนครั้งที่ลาสะสม"); + + b.Property("LeaveDays") + .HasColumnType("double") + .HasComment("จำนวนวันลาทั้งหมด"); + + b.Property("LeaveDaysUsed") + .HasColumnType("double") + .HasComment("จำนวนวันลาที่ใช้ไป"); + + b.Property("LeaveTypeId") + .HasColumnType("char(36)") + .HasComment("รหัสประเภทการลา"); + + b.Property("LeaveYear") + .HasColumnType("int") + .HasComment("ปีงบประมาณ"); + + b.Property("Prefix") + .HasColumnType("longtext"); + + b.Property("ProfileId") + .HasColumnType("char(36)") + .HasComment("รหัส Profile ในระบบทะเบียนประวัติ"); + + b.Property("RootDnaId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("LeaveTypeId"); + + b.ToTable("LeaveBeginnings"); + }); + + modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.Requests.LeaveDocument", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)") + .HasColumnOrder(0) + .HasComment("PrimaryKey") + .HasAnnotation("Relational:JsonPropertyName", "id"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(100) + .HasComment("สร้างข้อมูลเมื่อ"); + + b.Property("CreatedFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(104) + .HasComment("ชื่อ User ที่สร้างข้อมูล"); + + b.Property("CreatedUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(101) + .HasComment("User Id ที่สร้างข้อมูล"); + + b.Property("DocumentId") + .HasColumnType("char(36)"); + + b.Property("LastUpdateFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(105) + .HasComment("ชื่อ User ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdateUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(103) + .HasComment("User Id ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(102) + .HasComment("แก้ไขข้อมูลล่าสุดเมื่อ"); + + b.Property("LeaveRequestId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("DocumentId"); + + b.HasIndex("LeaveRequestId"); + + b.ToTable("LeaveDocuments"); + }); + + modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.Requests.LeaveRequest", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)") + .HasColumnOrder(0) + .HasComment("PrimaryKey") + .HasAnnotation("Relational:JsonPropertyName", "id"); + + b.Property("AbsentDayAt") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("AbsentDayGetIn") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("AbsentDayLocation") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("AbsentDayRegistorDate") + .HasColumnType("datetime(6)"); + + b.Property("AbsentDaySummon") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Amount") + .HasColumnType("double"); + + b.Property("ApproveStep") + .HasColumnType("longtext") + .HasComment("step การอนุมัติ st1 = จทน.อนุมัตื,st2 = ผู้บังคับบัญชา อนุมัติ "); + + b.Property("BirthDate") + .HasColumnType("datetime(6)"); + + b.Property("CancelLeaveWrote") + .HasColumnType("longtext") + .HasComment("เขียนที่ (ขอยกเลิก)"); + + b.Property("Child1") + .HasColumnType("longtext"); + + b.Property("Child1DnaId") + .HasColumnType("char(36)"); + + b.Property("Child1Id") + .HasColumnType("char(36)"); + + b.Property("Child2") + .HasColumnType("longtext"); + + b.Property("Child2DnaId") + .HasColumnType("char(36)"); + + b.Property("Child2Id") + .HasColumnType("char(36)"); + + b.Property("Child3") + .HasColumnType("longtext"); + + b.Property("Child3DnaId") + .HasColumnType("char(36)"); + + b.Property("Child3Id") + .HasColumnType("char(36)"); + + b.Property("Child4") + .HasColumnType("longtext"); + + b.Property("Child4DnaId") + .HasColumnType("char(36)"); + + b.Property("Child4Id") + .HasColumnType("char(36)"); + + b.Property("CitizenId") + .HasColumnType("longtext"); + + b.Property("CommanderPosition") + .HasColumnType("longtext"); + + b.Property("CoupleDayCountryHistory") + .HasColumnType("longtext"); + + b.Property("CoupleDayEndDateHistory") + .HasColumnType("datetime(6)"); + + b.Property("CoupleDayLevel") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("CoupleDayLevelCountry") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("CoupleDayName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("CoupleDayPosition") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("CoupleDayStartDateHistory") + .HasColumnType("datetime(6)"); + + b.Property("CoupleDaySumTotalHistory") + .HasColumnType("longtext"); + + b.Property("CoupleDayTotalHistory") + .HasColumnType("longtext"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(100) + .HasComment("สร้างข้อมูลเมื่อ"); + + b.Property("CreatedFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(104) + .HasComment("ชื่อ User ที่สร้างข้อมูล"); + + b.Property("CreatedUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(101) + .HasComment("User Id ที่สร้างข้อมูล"); + + b.Property("DateAppoint") + .HasColumnType("datetime(6)"); + + b.Property("Dear") + .HasColumnType("longtext") + .HasComment("เรียนใคร"); + + b.Property("FirstName") + .HasColumnType("longtext"); + + b.Property("Gender") + .HasColumnType("longtext"); + + b.Property("HajjDayStatus") + .HasColumnType("tinyint(1)"); + + b.Property("KeycloakUserId") + .HasColumnType("char(36)"); + + b.Property("LastName") + .HasColumnType("longtext"); + + b.Property("LastUpdateFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(105) + .HasComment("ชื่อ User ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdateUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(103) + .HasComment("User Id ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(102) + .HasComment("แก้ไขข้อมูลล่าสุดเมื่อ"); + + b.Property("LeaveAddress") + .IsRequired() + .HasColumnType("longtext") + .HasComment("สถานที่ติดต่อขณะลา"); + + b.Property("LeaveBirthDate") + .HasColumnType("datetime(6)"); + + b.Property("LeaveCancelComment") + .HasColumnType("longtext") + .HasComment("เหตุผลในการขอยกเลิก"); + + b.Property("LeaveCancelDocumentId") + .HasColumnType("char(36)"); + + b.Property("LeaveCancelStatus") + .HasColumnType("longtext") + .HasComment("สถานะของคำขอยกเลิก"); + + b.Property("LeaveComment") + .HasColumnType("longtext") + .HasComment("ความเห็นของผู้บังคับบัญชา"); + + b.Property("LeaveDetail") + .IsRequired() + .HasColumnType("longtext") + .HasComment("รายละเอียดการลา"); + + b.Property("LeaveDirectorComment") + .HasColumnType("longtext") + .HasComment("ความเห็นของผู้อำนวยการสำนัก"); + + b.Property("LeaveDraftDocumentId") + .HasColumnType("char(36)"); + + b.Property("LeaveEndDate") + .HasColumnType("datetime(6)") + .HasComment("วัน เดือน ปีสิ้นสุดลา"); + + b.Property("LeaveGovernmentDate") + .HasColumnType("datetime(6)"); + + b.Property("LeaveLast") + .HasColumnType("datetime(6)"); + + b.Property("LeaveNumber") + .IsRequired() + .HasColumnType("longtext") + .HasComment("หมายเลขที่ติดต่อขณะลา"); + + b.Property("LeaveRange") + .HasColumnType("longtext") + .HasComment("ช่วงของการลาของวันเริ่ม เช่น ลาทั้งวัน ครึ่งวันเช้า ครึ่งวันบ่าย"); + + b.Property("LeaveRangeEnd") + .HasColumnType("longtext") + .HasComment("ช่วงของการลาของวันสิ้นสุด เช่น ลาทั้งวัน ครึ่งวันเช้า ครึ่งวันบ่าย"); + + b.Property("LeaveSalary") + .HasColumnType("int"); + + b.Property("LeaveSalaryText") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("LeaveStartDate") + .HasColumnType("datetime(6)") + .HasComment("วัน เดือน ปีเริ่มต้นลา"); + + b.Property("LeaveStatus") + .IsRequired() + .HasColumnType("longtext") + .HasComment("สถานะของคำร้อง"); + + b.Property("LeaveSubTypeName") + .HasColumnType("longtext"); + + b.Property("LeaveTotal") + .HasColumnType("double"); + + b.Property("LeaveTypeCode") + .HasColumnType("longtext") + .HasComment("code ของประเภทการลา"); + + b.Property("LeaveWrote") + .IsRequired() + .HasColumnType("longtext") + .HasComment("เขียนที่"); + + b.Property("OrdainDayBuddhistLentAddress") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("OrdainDayBuddhistLentName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("OrdainDayLocationAddress") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("OrdainDayLocationName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("OrdainDayLocationNumber") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("OrdainDayOrdination") + .HasColumnType("datetime(6)"); + + b.Property("OrdainDayStatus") + .HasColumnType("tinyint(1)"); + + b.Property("OrganizationName") + .HasColumnType("longtext") + .HasComment("สังกัดผู้ยื่นขอ"); + + b.Property("PositionLevelName") + .HasColumnType("longtext") + .HasComment("ระดับผู้ยื่นขอ"); + + b.Property("PositionName") + .HasColumnType("longtext") + .HasComment("ตำแหน่งผู้ยื่นขอ"); + + b.Property("Prefix") + .HasColumnType("longtext"); + + b.Property("ProfileId") + .HasColumnType("char(36)"); + + b.Property("ProfileType") + .HasColumnType("longtext"); + + b.Property("RestDayCurrentTotal") + .HasColumnType("double"); + + b.Property("RestDayOldTotal") + .HasColumnType("double"); + + b.Property("Root") + .HasColumnType("longtext"); + + b.Property("RootDnaId") + .HasColumnType("char(36)"); + + b.Property("RootId") + .HasColumnType("char(36)"); + + b.Property("StudyDayCountry") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("StudyDayDegreeLevel") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("StudyDayScholarship") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("StudyDaySubject") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("StudyDayTrainingName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("StudyDayTrainingSubject") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("StudyDayUniversityName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("TypeId") + .HasColumnType("char(36)"); + + b.Property("WifeDayDateBorn") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("WifeDayName") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("LeaveCancelDocumentId"); + + b.HasIndex("LeaveDraftDocumentId"); + + b.HasIndex("TypeId"); + + b.ToTable("LeaveRequests"); + }); + + modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.Requests.LeaveRequestApprover", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)") + .HasColumnOrder(0) + .HasComment("PrimaryKey") + .HasAnnotation("Relational:JsonPropertyName", "id"); + + b.Property("ApproveStatus") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("ApproveType") + .HasColumnType("longtext"); + + b.Property("Comment") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(100) + .HasComment("สร้างข้อมูลเมื่อ"); + + b.Property("CreatedFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(104) + .HasComment("ชื่อ User ที่สร้างข้อมูล"); + + b.Property("CreatedUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(101) + .HasComment("User Id ที่สร้างข้อมูล"); + + b.Property("FirstName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("KeycloakId") + .HasColumnType("char(36)"); + + b.Property("LastName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("LastUpdateFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(105) + .HasComment("ชื่อ User ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdateUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(103) + .HasComment("User Id ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(102) + .HasComment("แก้ไขข้อมูลล่าสุดเมื่อ"); + + b.Property("LeaveRequestId") + .HasColumnType("char(36)"); + + b.Property("OrganizationName") + .IsRequired() + .HasColumnType("longtext") + .HasComment("สังกัด"); + + b.Property("PosExecutiveName") + .IsRequired() + .HasColumnType("longtext") + .HasComment("ตำแหน่งทางการบริหาร"); + + b.Property("PositionLevelName") + .IsRequired() + .HasColumnType("longtext") + .HasComment("ประเภทระดับตำแหน่ง"); + + b.Property("PositionName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("PositionSign") + .HasColumnType("longtext") + .HasComment("ตำแหน่งใต้ลายเช็นต์"); + + b.Property("Prefix") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("ProfileId") + .HasColumnType("char(36)"); + + b.Property("Seq") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("LeaveRequestId"); + + b.ToTable("LeaveRequestApprovers"); + }); + + modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.TimeAttendants.AdditionalCheckRequest", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)") + .HasColumnOrder(0) + .HasComment("PrimaryKey") + .HasAnnotation("Relational:JsonPropertyName", "id"); + + b.Property("CheckDate") + .HasColumnType("datetime(6)") + .HasComment("*วันที่ลงเวลา"); + + b.Property("CheckInEdit") + .HasColumnType("tinyint(1)") + .HasComment("*ขอลงเวลาช่วงเช้า"); + + b.Property("CheckOutEdit") + .HasColumnType("tinyint(1)") + .HasComment("*ขอลงเวลาช่วงบ่าย"); + + b.Property("Child1DnaId") + .HasColumnType("char(36)"); + + b.Property("Child2DnaId") + .HasColumnType("char(36)"); + + b.Property("Child3DnaId") + .HasColumnType("char(36)"); + + b.Property("Child4DnaId") + .HasColumnType("char(36)"); + + b.Property("Comment") + .HasColumnType("longtext") + .HasComment("หมายเหตุในการการอนุมัติ/ไม่อนุมัติ"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(100) + .HasComment("สร้างข้อมูลเมื่อ"); + + b.Property("CreatedFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(104) + .HasComment("ชื่อ User ที่สร้างข้อมูล"); + + b.Property("CreatedUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(101) + .HasComment("User Id ที่สร้างข้อมูล"); + + b.Property("Description") + .IsRequired() + .HasColumnType("longtext") + .HasComment("*หมายเหตุขอลงเวลาพิเศษ"); + + b.Property("FirstName") + .HasColumnType("longtext"); + + b.Property("KeycloakUserId") + .HasColumnType("char(36)") + .HasComment("รหัส User ของ Keycloak ที่ร้องขอ"); + + b.Property("LastName") + .HasColumnType("longtext"); + + b.Property("LastUpdateFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(105) + .HasComment("ชื่อ User ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdateUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(103) + .HasComment("User Id ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(102) + .HasComment("แก้ไขข้อมูลล่าสุดเมื่อ"); + + b.Property("Latitude") + .HasColumnType("double"); + + b.Property("Longitude") + .HasColumnType("double"); + + b.Property("POI") + .HasColumnType("longtext"); + + b.Property("Prefix") + .HasColumnType("longtext"); + + b.Property("RootDnaId") + .HasColumnType("char(36)"); + + b.Property("Status") + .IsRequired() + .HasColumnType("longtext") + .HasComment("สถานะการอนุมัติ"); + + b.HasKey("Id"); + + b.ToTable("AdditionalCheckRequests"); + }); + + modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.TimeAttendants.CheckInJobStatus", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)") + .HasColumnOrder(0) + .HasComment("PrimaryKey") + .HasAnnotation("Relational:JsonPropertyName", "id"); + + b.Property("AdditionalData") + .HasColumnType("longtext") + .HasComment("ข้อมูลเพิ่มเติม (JSON)"); + + b.Property("CheckInId") + .HasColumnType("char(36)") + .HasComment("CheckInId สำหรับ Check-Out"); + + b.Property("CheckType") + .HasColumnType("longtext") + .HasComment("ประเภทการลงเวลา: CHECK_IN, CHECK_OUT"); + + b.Property("CompletedDate") + .HasColumnType("datetime(6)") + .HasComment("วันเวลาที่เสร็จสิ้นการประมวลผล"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(100) + .HasComment("สร้างข้อมูลเมื่อ"); + + b.Property("CreatedDate") + .HasColumnType("datetime(6)") + .HasComment("วันเวลาที่สร้างงาน"); + + b.Property("CreatedFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(104) + .HasComment("ชื่อ User ที่สร้างข้อมูล"); + + b.Property("CreatedUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(101) + .HasComment("User Id ที่สร้างข้อมูล"); + + b.Property("ErrorMessage") + .HasColumnType("longtext") + .HasComment("ข้อความแสดงข้อผิดพลาด"); + + b.Property("KeycloakUserId") + .HasColumnType("char(36)") + .HasComment("รหัส User ของ Keycloak"); + + b.Property("LastUpdateFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(105) + .HasComment("ชื่อ User ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdateUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(103) + .HasComment("User Id ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(102) + .HasComment("แก้ไขข้อมูลล่าสุดเมื่อ"); + + b.Property("ProcessingDate") + .HasColumnType("datetime(6)") + .HasComment("วันเวลาที่เริ่มประมวลผล"); + + b.Property("Status") + .IsRequired() + .HasColumnType("longtext") + .HasComment("สถานะงาน: PENDING, PROCESSING, COMPLETED, FAILED"); + + b.Property("TaskId") + .HasColumnType("char(36)") + .HasComment("Task ID สำหรับติดตามสถานะงาน"); + + b.HasKey("Id"); + + b.ToTable("CheckInJobStatuses"); + }); + + modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.TimeAttendants.DutyTime", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)") + .HasColumnOrder(0) + .HasComment("PrimaryKey") + .HasAnnotation("Relational:JsonPropertyName", "id"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(100) + .HasComment("สร้างข้อมูลเมื่อ"); + + b.Property("CreatedFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(104) + .HasComment("ชื่อ User ที่สร้างข้อมูล"); + + b.Property("CreatedUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(101) + .HasComment("User Id ที่สร้างข้อมูล"); + + b.Property("Description") + .IsRequired() + .HasColumnType("longtext") + .HasComment("คำอธิบาย"); + + b.Property("EndTimeAfternoon") + .IsRequired() + .HasColumnType("longtext") + .HasComment("เวลาออกงานช่วงบ่าย"); + + b.Property("EndTimeMorning") + .IsRequired() + .HasColumnType("longtext") + .HasComment("เวลาออกงานช่วงเช้า"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)") + .HasComment("สถานะการเปิดใช้งาน (เปิด/ปิด)"); + + b.Property("IsDefault") + .HasColumnType("tinyint(1)") + .HasComment("สถานะว่ารอบใดเป็นค่า Default ของข้าราชการ (สำหรับทุกคนที่ยังไม่ได้ทำการเลือกรอบ)"); + + b.Property("LastUpdateFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(105) + .HasComment("ชื่อ User ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdateUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(103) + .HasComment("User Id ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(102) + .HasComment("แก้ไขข้อมูลล่าสุดเมื่อ"); + + b.Property("StartTimeAfternoon") + .IsRequired() + .HasColumnType("longtext") + .HasComment("เวลาเข้างานช่วงบ่าย"); + + b.Property("StartTimeMorning") + .IsRequired() + .HasColumnType("longtext") + .HasComment("เวลาเข้างานช่วงเช้า"); + + b.HasKey("Id"); + + b.ToTable("DutyTimes"); + }); + + modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.TimeAttendants.LeaveProcessJobStatus", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)") + .HasColumnOrder(0) + .HasComment("PrimaryKey") + .HasAnnotation("Relational:JsonPropertyName", "id"); + + b.Property("CompletedDate") + .HasColumnType("datetime(6)") + .HasComment("วันเวลาที่เสร็จสิ้นการประมวลผล"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(100) + .HasComment("สร้างข้อมูลเมื่อ"); + + b.Property("CreatedDate") + .HasColumnType("datetime(6)") + .HasComment("วันเวลาที่สร้างงาน"); + + b.Property("CreatedFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(104) + .HasComment("ชื่อ User ที่สร้างข้อมูล"); + + b.Property("CreatedUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(101) + .HasComment("User Id ที่สร้างข้อมูล"); + + b.Property("EndDate") + .HasColumnType("datetime(6)") + .HasComment("วันสิ้นสุด"); + + b.Property("ErrorMessage") + .HasColumnType("longtext") + .HasComment("ข้อความแสดงข้อผิดพลาด"); + + b.Property("LastUpdateFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(105) + .HasComment("ชื่อ User ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdateUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(103) + .HasComment("User Id ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(102) + .HasComment("แก้ไขข้อมูลล่าสุดเมื่อ"); + + b.Property("ProcessingDate") + .HasColumnType("datetime(6)") + .HasComment("วันเวลาที่เริ่มประมวลผล"); + + b.Property("RootDnaId") + .HasColumnType("char(36)") + .HasComment("รหัส Root DNA Id"); + + b.Property("StartDate") + .HasColumnType("datetime(6)") + .HasComment("วันเริ่มต้น"); + + b.Property("Status") + .IsRequired() + .HasColumnType("longtext") + .HasComment("สถานะงาน: PENDING, PROCESSING, COMPLETED, FAILED"); + + b.HasKey("Id"); + + b.ToTable("LeaveProcessJobStatuses"); + }); + + modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.TimeAttendants.ProcessUserTimeStamp", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)") + .HasColumnOrder(0) + .HasComment("PrimaryKey") + .HasAnnotation("Relational:JsonPropertyName", "id"); + + b.Property("CheckIn") + .HasColumnType("datetime(6)") + .HasComment("วัน เวลา เข้างาน"); + + b.Property("CheckInImageUrl") + .IsRequired() + .HasColumnType("longtext") + .HasComment("รูปถ่ายสถานที่ Check-In"); + + b.Property("CheckInLat") + .HasColumnType("double") + .HasComment("พิกัดละติจูด Check-In"); + + b.Property("CheckInLocationName") + .HasColumnType("longtext") + .HasComment("กรณีเลือกนอกสถานที่ตั้ง ต้องระบุข้อมูลชื่อสถานะที่ Check-In"); + + b.Property("CheckInLon") + .HasColumnType("double") + .HasComment("พิกัดลองจิจูด Check-In"); + + b.Property("CheckInPOI") + .IsRequired() + .HasColumnType("longtext") + .HasComment("ชื่อสถานที่ ได้มาจากระบบ ArcGis ของกองสารสนเทศภูมิศาสตร์ Check-In"); + + b.Property("CheckInRemark") + .HasColumnType("longtext") + .HasComment("ข้อความหมายเหตุที่ต้องการระบุเพิ่ม(มีเผื่อไว้อาจไม่ได้ใช้) Check-In"); + + b.Property("CheckInStatus") + .IsRequired() + .HasColumnType("longtext") + .HasComment("สถานะ Check-In"); + + b.Property("CheckOut") + .HasColumnType("datetime(6)") + .HasComment("วัน เวลา ออกงาน"); + + b.Property("CheckOutImageUrl") + .IsRequired() + .HasColumnType("longtext") + .HasComment("รูปถ่ายสถานที่ Check-Out"); + + b.Property("CheckOutLat") + .HasColumnType("double") + .HasComment("พิกัดละติจูด Check-Out"); + + b.Property("CheckOutLocationName") + .HasColumnType("longtext") + .HasComment("กรณีเลือกนอกสถานที่ตั้ง ต้องระบุข้อมูลชื่อสถานะที่ Check-Out"); + + b.Property("CheckOutLon") + .HasColumnType("double") + .HasComment("พิกัดลองจิจูด Check-Out"); + + b.Property("CheckOutPOI") + .IsRequired() + .HasColumnType("longtext") + .HasComment("ชื่อสถานที่ ได้มาจากระบบ ArcGis ของกองสารสนเทศภูมิศาสตร์ Check-Out"); + + b.Property("CheckOutRemark") + .HasColumnType("longtext") + .HasComment("ข้อความหมายเหตุที่ต้องการระบุเพิ่ม(มีเผื่อไว้อาจไม่ได้ใช้) Check-Out"); + + b.Property("CheckOutStatus") + .HasColumnType("longtext") + .HasComment("สถานะ Check-Out"); + + b.Property("Child1") + .HasColumnType("longtext"); + + b.Property("Child1DnaId") + .HasColumnType("char(36)"); + + b.Property("Child1Id") + .HasColumnType("char(36)"); + + b.Property("Child2") + .HasColumnType("longtext"); + + b.Property("Child2DnaId") + .HasColumnType("char(36)"); + + b.Property("Child2Id") + .HasColumnType("char(36)"); + + b.Property("Child3") + .HasColumnType("longtext"); + + b.Property("Child3DnaId") + .HasColumnType("char(36)"); + + b.Property("Child3Id") + .HasColumnType("char(36)"); + + b.Property("Child4") + .HasColumnType("longtext"); + + b.Property("Child4DnaId") + .HasColumnType("char(36)"); + + b.Property("Child4Id") + .HasColumnType("char(36)"); + + b.Property("CitizenId") + .HasColumnType("longtext"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(100) + .HasComment("สร้างข้อมูลเมื่อ"); + + b.Property("CreatedFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(104) + .HasComment("ชื่อ User ที่สร้างข้อมูล"); + + b.Property("CreatedUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(101) + .HasComment("User Id ที่สร้างข้อมูล"); + + b.Property("EditReason") + .HasColumnType("longtext") + .HasComment("เหตุผลการอนุมัติ/ไม่อนุมัติขอลงเวลาพิเศษ"); + + b.Property("EditStatus") + .HasColumnType("longtext") + .HasComment("สถานะการของลงเวลาพิเศษ"); + + b.Property("FirstName") + .HasColumnType("longtext"); + + b.Property("Gender") + .HasColumnType("longtext"); + + b.Property("IsLocationCheckIn") + .HasColumnType("tinyint(1)") + .HasComment("true คือ ณ สถานที่ตั้ง, false คือ นอกสถานที่ตั้ง Check-In"); + + b.Property("IsLocationCheckOut") + .HasColumnType("tinyint(1)") + .HasComment("true คือ ณ สถานที่ตั้ง, false คือ นอกสถานที่ตั้ง Check-Out"); + + b.Property("IsProcess") + .HasColumnType("tinyint(1)") + .HasComment("นำไปประมวลผลแล้วหรือยัง"); + + b.Property("KeycloakUserId") + .HasColumnType("char(36)") + .HasComment("รหัส User ของ Keycloak"); + + b.Property("LastName") + .HasColumnType("longtext"); + + b.Property("LastUpdateFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(105) + .HasComment("ชื่อ User ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdateUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(103) + .HasComment("User Id ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(102) + .HasComment("แก้ไขข้อมูลล่าสุดเมื่อ"); + + b.Property("Prefix") + .HasColumnType("longtext"); + + b.Property("ProfileId") + .HasColumnType("char(36)"); + + b.Property("ProfileType") + .HasColumnType("longtext"); + + b.Property("Root") + .HasColumnType("longtext"); + + b.Property("RootDnaId") + .HasColumnType("char(36)"); + + b.Property("RootId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.ToTable("ProcessUserTimeStamps"); + }); + + modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.TimeAttendants.UserCalendar", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)") + .HasColumnOrder(0) + .HasComment("PrimaryKey") + .HasAnnotation("Relational:JsonPropertyName", "id"); + + b.Property("Calendar") + .IsRequired() + .HasColumnType("longtext") + .HasComment("ปฏิทินการทำงานของ ขรก ปกติ หรือ 6 วันต่อสัปดาห์"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(100) + .HasComment("สร้างข้อมูลเมื่อ"); + + b.Property("CreatedFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(104) + .HasComment("ชื่อ User ที่สร้างข้อมูล"); + + b.Property("CreatedUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(101) + .HasComment("User Id ที่สร้างข้อมูล"); + + b.Property("LastUpdateFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(105) + .HasComment("ชื่อ User ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdateUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(103) + .HasComment("User Id ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(102) + .HasComment("แก้ไขข้อมูลล่าสุดเมื่อ"); + + b.Property("ProfileId") + .HasColumnType("char(36)") + .HasComment("รหัส Profile ในระบบทะเบียนประวัติ"); + + b.HasKey("Id"); + + b.ToTable("UserCalendars"); + }); + + modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.TimeAttendants.UserDutyTime", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)") + .HasColumnOrder(0) + .HasComment("PrimaryKey") + .HasAnnotation("Relational:JsonPropertyName", "id"); + + b.Property("Child1DnaId") + .HasColumnType("char(36)"); + + b.Property("Child2DnaId") + .HasColumnType("char(36)"); + + b.Property("Child3DnaId") + .HasColumnType("char(36)"); + + b.Property("Child4DnaId") + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(100) + .HasComment("สร้างข้อมูลเมื่อ"); + + b.Property("CreatedFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(104) + .HasComment("ชื่อ User ที่สร้างข้อมูล"); + + b.Property("CreatedUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(101) + .HasComment("User Id ที่สร้างข้อมูล"); + + b.Property("DutyTimeId") + .HasColumnType("char(36)") + .HasComment("รหัสรอบการลงเวลา"); + + b.Property("EffectiveDate") + .HasColumnType("datetime(6)") + .HasComment("วันที่มีผล"); + + b.Property("IsProcess") + .HasColumnType("tinyint(1)") + .HasComment("ทำการประมวลผลแล้วหรือยัง"); + + b.Property("LastUpdateFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(105) + .HasComment("ชื่อ User ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdateUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(103) + .HasComment("User Id ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(102) + .HasComment("แก้ไขข้อมูลล่าสุดเมื่อ"); + + b.Property("ProfileId") + .HasColumnType("char(36)") + .HasComment("รหัส Profile ในระบบทะเบียนประวัติ"); + + b.Property("Remark") + .HasColumnType("longtext") + .HasComment("หมายเหตุ"); + + b.Property("RootDnaId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("DutyTimeId"); + + b.ToTable("UserDutyTimes"); + }); + + modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.TimeAttendants.UserTimeStamp", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)") + .HasColumnOrder(0) + .HasComment("PrimaryKey") + .HasAnnotation("Relational:JsonPropertyName", "id"); + + b.Property("CheckIn") + .HasColumnType("datetime(6)") + .HasComment("วัน เวลา เข้างาน"); + + b.Property("CheckInImageUrl") + .IsRequired() + .HasColumnType("longtext") + .HasComment("รูปถ่ายสถานที่ Check-In"); + + b.Property("CheckInLat") + .HasColumnType("double") + .HasComment("พิกัดละติจูด Check-In"); + + b.Property("CheckInLocationName") + .HasColumnType("longtext") + .HasComment("กรณีเลือกนอกสถานที่ตั้ง ต้องระบุข้อมูลชื่อสถานะที่ Check-In"); + + b.Property("CheckInLon") + .HasColumnType("double") + .HasComment("พิกัดลองจิจูด Check-In"); + + b.Property("CheckInPOI") + .IsRequired() + .HasColumnType("longtext") + .HasComment("ชื่อสถานที่ ได้มาจากระบบ ArcGis ของกองสารสนเทศภูมิศาสตร์ Check-In"); + + b.Property("CheckInRemark") + .HasColumnType("longtext") + .HasComment("ข้อความหมายเหตุที่ต้องการระบุเพิ่ม(มีเผื่อไว้อาจไม่ได้ใช้) Check-In"); + + b.Property("CheckOut") + .HasColumnType("datetime(6)") + .HasComment("วัน เวลา ออกงาน"); + + b.Property("CheckOutImageUrl") + .IsRequired() + .HasColumnType("longtext") + .HasComment("รูปถ่ายสถานที่ Check-Out"); + + b.Property("CheckOutLat") + .HasColumnType("double") + .HasComment("พิกัดละติจูด Check-Out"); + + b.Property("CheckOutLocationName") + .HasColumnType("longtext") + .HasComment("กรณีเลือกนอกสถานที่ตั้ง ต้องระบุข้อมูลชื่อสถานะที่ Check-Out"); + + b.Property("CheckOutLon") + .HasColumnType("double") + .HasComment("พิกัดลองจิจูด Check-Out"); + + b.Property("CheckOutPOI") + .IsRequired() + .HasColumnType("longtext") + .HasComment("ชื่อสถานที่ ได้มาจากระบบ ArcGis ของกองสารสนเทศภูมิศาสตร์ Check-Out"); + + b.Property("CheckOutRemark") + .HasColumnType("longtext") + .HasComment("ข้อความหมายเหตุที่ต้องการระบุเพิ่ม(มีเผื่อไว้อาจไม่ได้ใช้) Check-Out"); + + b.Property("Child1") + .HasColumnType("longtext"); + + b.Property("Child1DnaId") + .HasColumnType("char(36)"); + + b.Property("Child1Id") + .HasColumnType("char(36)"); + + b.Property("Child2") + .HasColumnType("longtext"); + + b.Property("Child2DnaId") + .HasColumnType("char(36)"); + + b.Property("Child2Id") + .HasColumnType("char(36)"); + + b.Property("Child3") + .HasColumnType("longtext"); + + b.Property("Child3DnaId") + .HasColumnType("char(36)"); + + b.Property("Child3Id") + .HasColumnType("char(36)"); + + b.Property("Child4") + .HasColumnType("longtext"); + + b.Property("Child4DnaId") + .HasColumnType("char(36)"); + + b.Property("Child4Id") + .HasColumnType("char(36)"); + + b.Property("CitizenId") + .HasColumnType("longtext"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(100) + .HasComment("สร้างข้อมูลเมื่อ"); + + b.Property("CreatedFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(104) + .HasComment("ชื่อ User ที่สร้างข้อมูล"); + + b.Property("CreatedUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(101) + .HasComment("User Id ที่สร้างข้อมูล"); + + b.Property("FirstName") + .HasColumnType("longtext"); + + b.Property("Gender") + .HasColumnType("longtext"); + + b.Property("IsLocationCheckIn") + .HasColumnType("tinyint(1)") + .HasComment("true คือ ณ สถานที่ตั้ง, false คือ นอกสถานที่ตั้ง Check-In"); + + b.Property("IsLocationCheckOut") + .HasColumnType("tinyint(1)") + .HasComment("true คือ ณ สถานที่ตั้ง, false คือ นอกสถานที่ตั้ง Check-Out"); + + b.Property("IsProcess") + .HasColumnType("tinyint(1)") + .HasComment("นำไปประมวลผลแล้วหรือยัง"); + + b.Property("KeycloakUserId") + .HasColumnType("char(36)") + .HasComment("รหัส User ของ Keycloak"); + + b.Property("LastName") + .HasColumnType("longtext"); + + b.Property("LastUpdateFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(105) + .HasComment("ชื่อ User ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdateUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(103) + .HasComment("User Id ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(102) + .HasComment("แก้ไขข้อมูลล่าสุดเมื่อ"); + + b.Property("Prefix") + .HasColumnType("longtext"); + + b.Property("ProfileId") + .HasColumnType("char(36)"); + + b.Property("ProfileType") + .HasColumnType("longtext"); + + b.Property("Root") + .HasColumnType("longtext"); + + b.Property("RootDnaId") + .HasColumnType("char(36)"); + + b.Property("RootId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.ToTable("UserTimeStamps"); + }); + + modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.Requests.LeaveBeginning", b => + { + b.HasOne("BMA.EHR.Domain.Models.Leave.Commons.LeaveType", "LeaveType") + .WithMany() + .HasForeignKey("LeaveTypeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("LeaveType"); + }); + + modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.Requests.LeaveDocument", b => + { + b.HasOne("BMA.EHR.Domain.Models.Documents.Document", "Document") + .WithMany() + .HasForeignKey("DocumentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("BMA.EHR.Domain.Models.Leave.Requests.LeaveRequest", "LeaveRequest") + .WithMany("LeaveDocument") + .HasForeignKey("LeaveRequestId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Document"); + + b.Navigation("LeaveRequest"); + }); + + modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.Requests.LeaveRequest", b => + { + b.HasOne("BMA.EHR.Domain.Models.Documents.Document", "LeaveCancelDocument") + .WithMany() + .HasForeignKey("LeaveCancelDocumentId"); + + b.HasOne("BMA.EHR.Domain.Models.Documents.Document", "LeaveDraftDocument") + .WithMany() + .HasForeignKey("LeaveDraftDocumentId"); + + b.HasOne("BMA.EHR.Domain.Models.Leave.Commons.LeaveType", "Type") + .WithMany() + .HasForeignKey("TypeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("LeaveCancelDocument"); + + b.Navigation("LeaveDraftDocument"); + + b.Navigation("Type"); + }); + + modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.Requests.LeaveRequestApprover", b => + { + b.HasOne("BMA.EHR.Domain.Models.Leave.Requests.LeaveRequest", "LeaveRequest") + .WithMany("Approvers") + .HasForeignKey("LeaveRequestId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("LeaveRequest"); + }); + + modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.TimeAttendants.UserDutyTime", b => + { + b.HasOne("BMA.EHR.Domain.Models.Leave.TimeAttendants.DutyTime", "DutyTime") + .WithMany() + .HasForeignKey("DutyTimeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("DutyTime"); + }); + + modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.Requests.LeaveRequest", b => + { + b.Navigation("Approvers"); + + b.Navigation("LeaveDocument"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/BMA.EHR.Infrastructure/Migrations/LeaveDb/20260330020909_Add Leave Process Job Status.cs b/BMA.EHR.Infrastructure/Migrations/LeaveDb/20260330020909_Add Leave Process Job Status.cs new file mode 100644 index 00000000..1567dc5e --- /dev/null +++ b/BMA.EHR.Infrastructure/Migrations/LeaveDb/20260330020909_Add Leave Process Job Status.cs @@ -0,0 +1,54 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace BMA.EHR.Infrastructure.Migrations.LeaveDb +{ + /// + public partial class AddLeaveProcessJobStatus : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "LeaveProcessJobStatuses", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, comment: "PrimaryKey", collation: "ascii_general_ci"), + CreatedAt = table.Column(type: "datetime(6)", nullable: false, comment: "สร้างข้อมูลเมื่อ"), + CreatedUserId = table.Column(type: "varchar(40)", maxLength: 40, nullable: false, comment: "User Id ที่สร้างข้อมูล") + .Annotation("MySql:CharSet", "utf8mb4"), + LastUpdatedAt = table.Column(type: "datetime(6)", nullable: true, comment: "แก้ไขข้อมูลล่าสุดเมื่อ"), + LastUpdateUserId = table.Column(type: "varchar(40)", maxLength: 40, nullable: false, comment: "User Id ที่แก้ไขข้อมูลล่าสุด") + .Annotation("MySql:CharSet", "utf8mb4"), + CreatedFullName = table.Column(type: "varchar(200)", maxLength: 200, nullable: false, comment: "ชื่อ User ที่สร้างข้อมูล") + .Annotation("MySql:CharSet", "utf8mb4"), + LastUpdateFullName = table.Column(type: "varchar(200)", maxLength: 200, nullable: false, comment: "ชื่อ User ที่แก้ไขข้อมูลล่าสุด") + .Annotation("MySql:CharSet", "utf8mb4"), + StartDate = table.Column(type: "datetime(6)", nullable: false, comment: "วันเริ่มต้น"), + EndDate = table.Column(type: "datetime(6)", nullable: false, comment: "วันสิ้นสุด"), + RootDnaId = table.Column(type: "char(36)", nullable: false, comment: "รหัส Root DNA Id", collation: "ascii_general_ci"), + CreatedDate = table.Column(type: "datetime(6)", nullable: false, comment: "วันเวลาที่สร้างงาน"), + ProcessingDate = table.Column(type: "datetime(6)", nullable: true, comment: "วันเวลาที่เริ่มประมวลผล"), + CompletedDate = table.Column(type: "datetime(6)", nullable: true, comment: "วันเวลาที่เสร็จสิ้นการประมวลผล"), + Status = table.Column(type: "longtext", nullable: false, comment: "สถานะงาน: PENDING, PROCESSING, COMPLETED, FAILED") + .Annotation("MySql:CharSet", "utf8mb4"), + ErrorMessage = table.Column(type: "longtext", nullable: true, comment: "ข้อความแสดงข้อผิดพลาด") + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK_LeaveProcessJobStatuses", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "LeaveProcessJobStatuses"); + } + } +} diff --git a/BMA.EHR.Infrastructure/Migrations/LeaveDb/LeaveDbContextModelSnapshot.cs b/BMA.EHR.Infrastructure/Migrations/LeaveDb/LeaveDbContextModelSnapshot.cs index 510601d8..4e384f55 100644 --- a/BMA.EHR.Infrastructure/Migrations/LeaveDb/LeaveDbContextModelSnapshot.cs +++ b/BMA.EHR.Infrastructure/Migrations/LeaveDb/LeaveDbContextModelSnapshot.cs @@ -1072,6 +1072,91 @@ namespace BMA.EHR.Infrastructure.Migrations.LeaveDb b.ToTable("DutyTimes"); }); + modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.TimeAttendants.LeaveProcessJobStatus", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)") + .HasColumnOrder(0) + .HasComment("PrimaryKey") + .HasAnnotation("Relational:JsonPropertyName", "id"); + + b.Property("CompletedDate") + .HasColumnType("datetime(6)") + .HasComment("วันเวลาที่เสร็จสิ้นการประมวลผล"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(100) + .HasComment("สร้างข้อมูลเมื่อ"); + + b.Property("CreatedDate") + .HasColumnType("datetime(6)") + .HasComment("วันเวลาที่สร้างงาน"); + + b.Property("CreatedFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(104) + .HasComment("ชื่อ User ที่สร้างข้อมูล"); + + b.Property("CreatedUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(101) + .HasComment("User Id ที่สร้างข้อมูล"); + + b.Property("EndDate") + .HasColumnType("datetime(6)") + .HasComment("วันสิ้นสุด"); + + b.Property("ErrorMessage") + .HasColumnType("longtext") + .HasComment("ข้อความแสดงข้อผิดพลาด"); + + b.Property("LastUpdateFullName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)") + .HasColumnOrder(105) + .HasComment("ชื่อ User ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdateUserId") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("varchar(40)") + .HasColumnOrder(103) + .HasComment("User Id ที่แก้ไขข้อมูลล่าสุด"); + + b.Property("LastUpdatedAt") + .HasColumnType("datetime(6)") + .HasColumnOrder(102) + .HasComment("แก้ไขข้อมูลล่าสุดเมื่อ"); + + b.Property("ProcessingDate") + .HasColumnType("datetime(6)") + .HasComment("วันเวลาที่เริ่มประมวลผล"); + + b.Property("RootDnaId") + .HasColumnType("char(36)") + .HasComment("รหัส Root DNA Id"); + + b.Property("StartDate") + .HasColumnType("datetime(6)") + .HasComment("วันเริ่มต้น"); + + b.Property("Status") + .IsRequired() + .HasColumnType("longtext") + .HasComment("สถานะงาน: PENDING, PROCESSING, COMPLETED, FAILED"); + + b.HasKey("Id"); + + b.ToTable("LeaveProcessJobStatuses"); + }); + modelBuilder.Entity("BMA.EHR.Domain.Models.Leave.TimeAttendants.ProcessUserTimeStamp", b => { b.Property("Id") diff --git a/BMA.EHR.Infrastructure/Persistence/LeaveDbContext.cs b/BMA.EHR.Infrastructure/Persistence/LeaveDbContext.cs index 19848c6b..10064197 100644 --- a/BMA.EHR.Infrastructure/Persistence/LeaveDbContext.cs +++ b/BMA.EHR.Infrastructure/Persistence/LeaveDbContext.cs @@ -40,6 +40,8 @@ namespace BMA.EHR.Infrastructure.Persistence #endregion + public DbSet LeaveProcessJobStatuses { get; set; } + public LeaveDbContext(DbContextOptions options) : base(options) { From de1773880b671153983a3e137faa11fcbee0d6d6 Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Mon, 30 Mar 2026 09:31:19 +0700 Subject: [PATCH 143/178] Add LeaveProcessJobStatusRepository and register it in ApplicationServicesRegistration --- .../ApplicationServicesRegistration.cs | 2 + .../LeaveProcessJobStatusRepository.cs | 119 ++++++++++++++++++ 2 files changed, 121 insertions(+) create mode 100644 BMA.EHR.Application/Repositories/Leaves/TimeAttendants/LeaveProcessJobStatusRepository.cs diff --git a/BMA.EHR.Application/ApplicationServicesRegistration.cs b/BMA.EHR.Application/ApplicationServicesRegistration.cs index bf6dc6df..0b99a7b6 100644 --- a/BMA.EHR.Application/ApplicationServicesRegistration.cs +++ b/BMA.EHR.Application/ApplicationServicesRegistration.cs @@ -61,6 +61,8 @@ namespace BMA.EHR.Application services.AddTransient(); + services.AddTransient(); + return services; } diff --git a/BMA.EHR.Application/Repositories/Leaves/TimeAttendants/LeaveProcessJobStatusRepository.cs b/BMA.EHR.Application/Repositories/Leaves/TimeAttendants/LeaveProcessJobStatusRepository.cs new file mode 100644 index 00000000..114087b5 --- /dev/null +++ b/BMA.EHR.Application/Repositories/Leaves/TimeAttendants/LeaveProcessJobStatusRepository.cs @@ -0,0 +1,119 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using BMA.EHR.Application.Common.Interfaces; +using BMA.EHR.Domain.Models.Leave.TimeAttendants; +using Microsoft.AspNetCore.Http; +using Microsoft.EntityFrameworkCore; + +namespace BMA.EHR.Application.Repositories.Leaves.TimeAttendants +{ + public class LeaveProcessJobStatusRepository: GenericLeaveRepository + { + #region " Fields " + + private readonly ILeaveDbContext _dbContext; + + #endregion + + #region " Constructor and Destructor " + + public LeaveProcessJobStatusRepository(ILeaveDbContext dbContext, + IHttpContextAccessor httpContextAccessor) : base(dbContext, httpContextAccessor) + { + _dbContext = dbContext; + } + + #endregion + + #region " Methods " + + /// + /// ดึงข้อมูล Job Status จาก TaskId + /// + public async Task GetByTaskIdAsync(Guid id) + { + var data = await _dbContext.Set() + .Where(x => x.Id == id) + .FirstOrDefaultAsync(); + + return data; + } + + /// + /// ดึงข้อมูล Job Status จาก UserId และสถานะ + /// + public async Task> GetByUserIdAndStatusAsync(Guid userId, string status) + { + var data = await _dbContext.Set() + .Where(x => x.CreatedUserId == userId.ToString("D") && x.Status == status) + .OrderByDescending(x => x.CreatedDate) + .ToListAsync(); + + return data; + } + + /// + /// ดึงข้อมูล Job Status ที่ยัง pending หรือ processing + /// + public async Task> GetPendingOrProcessingJobsAsync(Guid userId) + { + var data = await _dbContext.Set() + .Where(x => x.CreatedUserId == userId.ToString("D") && + (x.Status == "PENDING" || x.Status == "PROCESSING")) + //.OrderByDescending(x => x.CreatedDate) + .ToListAsync(); + + return data; + } + + /// + /// อัปเดตสถานะเป็น Processing + /// + public async Task UpdateToProcessingAsync(Guid id) + { + var job = await GetByTaskIdAsync(id); + if (job != null) + { + job.Status = "PROCESSING"; + job.ProcessingDate = DateTime.Now; + await UpdateAsync(job); + } + return job!; + } + + /// + /// อัปเดตสถานะเป็น Completed + /// + public async Task UpdateToCompletedAsync(Guid id, string? additionalData = null) + { + var job = await GetByTaskIdAsync(id); + if (job != null) + { + job.Status = "COMPLETED"; + job.CompletedDate = DateTime.Now; + await UpdateAsync(job); + } + return job!; + } + + /// + /// อัปเดตสถานะเป็น Failed + /// + public async Task UpdateToFailedAsync(Guid id, string errorMessage) + { + var job = await GetByTaskIdAsync(id); + if (job != null) + { + job.Status = "FAILED"; + job.CompletedDate = DateTime.Now; + job.ErrorMessage = errorMessage; + await UpdateAsync(job); + } + return job!; + } + + #endregion + } +} \ No newline at end of file From c1ac687101162d641b3db3ce5ebd09337e263072 Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Mon, 30 Mar 2026 09:52:27 +0700 Subject: [PATCH 144/178] Add CreateLeaveProcessJobDto and implement CreateProcessTaskAsync in LeaveController --- BMA.EHR.Leave/Controllers/LeaveController.cs | 50 ++++++++++++++++++- .../LeaveRequest/CreateLeaveProcessJobDto.cs | 23 +++++++++ 2 files changed, 71 insertions(+), 2 deletions(-) create mode 100644 BMA.EHR.Leave/DTOs/LeaveRequest/CreateLeaveProcessJobDto.cs diff --git a/BMA.EHR.Leave/Controllers/LeaveController.cs b/BMA.EHR.Leave/Controllers/LeaveController.cs index 2f033adc..930c4043 100644 --- a/BMA.EHR.Leave/Controllers/LeaveController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveController.cs @@ -74,6 +74,8 @@ namespace BMA.EHR.Leave.Service.Controllers private readonly HttpClient _httpClient; + private readonly LeaveProcessJobStatusRepository _leaveProcessJobStatusRepository; + #endregion #region " Constuctor and Destructor " @@ -97,7 +99,8 @@ namespace BMA.EHR.Leave.Service.Controllers NotificationRepository notificationRepository, CheckInJobStatusRepository checkInJobStatusRepository, HttpClient httpClient, - ApplicationDBContext appDbContext) + ApplicationDBContext appDbContext, + LeaveProcessJobStatusRepository leaveProcessJobStatusRepository) { _dutyTimeRepository = dutyTimeRepository; _context = context; @@ -116,7 +119,7 @@ namespace BMA.EHR.Leave.Service.Controllers _leaveRequestRepository = leaveRequestRepository; _notificationRepository = notificationRepository; _checkInJobStatusRepository = checkInJobStatusRepository; - + _leaveProcessJobStatusRepository = leaveProcessJobStatusRepository; _objectPool = objectPool; _permission = permission; @@ -4156,6 +4159,49 @@ namespace BMA.EHR.Leave.Service.Controllers #endregion + #region " Process - Leave and Absence " + + + /// + /// สร้าง Task สำหรับ Process ข้อมูลวันลาและขาดราชการ (ADMIN) + /// + /// + /// + /// เมื่อทำรายการสำเร็จ + /// ไม่ได้ Login เข้าระบบ + /// เมื่อเกิดข้อผิดพลาดในการทำงาน + [HttpPost("admin/leave-task/process")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status401Unauthorized)] + [ProducesResponseType(StatusCodes.Status500InternalServerError)] + public async Task> CreateProcessTaskAsync([FromBody] CreateLeaveProcessJobDto req) + { + var userId = UserId == null ? Guid.Empty : Guid.Parse(UserId); + + var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(userId, AccessToken); + if (profile == null) + { + return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound); + } + + var task = new LeaveProcessJobStatus + { + RootDnaId = profile.RootDnaId ?? Guid.Empty, + CreatedUserId = profile.Keycloak?.ToString("D") ?? "", + CreatedFullName = profile.FirstName + " " + profile.LastName, + CreatedAt = DateTime.Now, + Status = "PENDING", + StartDate = req.StartDate, + EndDate = req.EndDate + }; + + await _leaveProcessJobStatusRepository.AddAsync(task); + + return Success(); + } + + #endregion + #endregion } diff --git a/BMA.EHR.Leave/DTOs/LeaveRequest/CreateLeaveProcessJobDto.cs b/BMA.EHR.Leave/DTOs/LeaveRequest/CreateLeaveProcessJobDto.cs new file mode 100644 index 00000000..ae4aacc4 --- /dev/null +++ b/BMA.EHR.Leave/DTOs/LeaveRequest/CreateLeaveProcessJobDto.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace BMA.EHR.Leave.Service.DTOs.LeaveRequest +{ + /// + /// ข้อมูลสำหรับสร้าง Job ประมวลผลวันลา โดยมีช่วงวันที่เริ่มต้นและสิ้นสุดของการประมวลผลวันลา + /// + public class CreateLeaveProcessJobDto + { + /// + /// วันที่เริ่มต้นของการประมวลผลวันลา + /// + public DateTime StartDate { get; set; } + + /// + /// วันที่สิ้นสุดของการประมวลผลวันลา + /// + public DateTime EndDate { get; set; } + } +} \ No newline at end of file From 91e6b1b35bdb8529e1d3b713fbaa385fc99fee3c Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Mon, 30 Mar 2026 10:02:57 +0700 Subject: [PATCH 145/178] Add methods to process pending jobs and update their statuses in LeaveProcessJobStatusRepository --- .../LeaveProcessJobStatusRepository.cs | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/BMA.EHR.Application/Repositories/Leaves/TimeAttendants/LeaveProcessJobStatusRepository.cs b/BMA.EHR.Application/Repositories/Leaves/TimeAttendants/LeaveProcessJobStatusRepository.cs index 114087b5..02670144 100644 --- a/BMA.EHR.Application/Repositories/Leaves/TimeAttendants/LeaveProcessJobStatusRepository.cs +++ b/BMA.EHR.Application/Repositories/Leaves/TimeAttendants/LeaveProcessJobStatusRepository.cs @@ -68,6 +68,15 @@ namespace BMA.EHR.Application.Repositories.Leaves.TimeAttendants return data; } + public async Task> GetPendingOrProcessingJobsAsync() + { + var data = await _dbContext.Set() + .Where(x => x.Status == "PENDING" || x.Status == "PROCESSING") + .ToListAsync(); + + return data; + } + /// /// อัปเดตสถานะเป็น Processing /// @@ -113,6 +122,41 @@ namespace BMA.EHR.Application.Repositories.Leaves.TimeAttendants } return job!; } + + public async Task ProcessTaskAsync(Guid id, CancellationToken cancellationToken = default) + { + + } + + public async Task ProcessPendingJobsAsync(CancellationToken cancellationToken = default) + { + // กำหนด timeout เป็น 60 นาที + using var timeoutCts = new CancellationTokenSource(TimeSpan.FromMinutes(60)); + using var combinedCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, timeoutCts.Token); + + + var pendingJobs = await GetPendingOrProcessingJobsAsync(); + + foreach (var job in pendingJobs) + { + try + { + // อัปเดตสถานะเป็น Processing + await UpdateToProcessingAsync(job.Id); + + // ทำงานที่ต้องการที่นี่ (เช่น เรียก API, ประมวลผลข้อมูล ฯลฯ) + await ProcessTaskAsync(job.RootDnaId, combinedCts.Token); + + // อัปเดตสถานะเป็น Completed + await UpdateToCompletedAsync(job.Id); + } + catch (Exception ex) + { + // หากเกิดข้อผิดพลาด อัปเดตสถานะเป็น Failed พร้อมข้อความแสดงข้อผิดพลาด + await UpdateToFailedAsync(job.Id, ex.Message); + } + } + } #endregion } From 8732c345649c6c8a4d18a864f3c763b8407871a8 Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Mon, 30 Mar 2026 10:06:21 +0700 Subject: [PATCH 146/178] Add scheduled job to process pending jobs in LeaveProcessJobStatusRepository --- .../TimeAttendants/LeaveProcessJobStatusRepository.cs | 8 +++----- BMA.EHR.Leave/Program.cs | 2 ++ 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/BMA.EHR.Application/Repositories/Leaves/TimeAttendants/LeaveProcessJobStatusRepository.cs b/BMA.EHR.Application/Repositories/Leaves/TimeAttendants/LeaveProcessJobStatusRepository.cs index 02670144..87940757 100644 --- a/BMA.EHR.Application/Repositories/Leaves/TimeAttendants/LeaveProcessJobStatusRepository.cs +++ b/BMA.EHR.Application/Repositories/Leaves/TimeAttendants/LeaveProcessJobStatusRepository.cs @@ -128,11 +128,9 @@ namespace BMA.EHR.Application.Repositories.Leaves.TimeAttendants } - public async Task ProcessPendingJobsAsync(CancellationToken cancellationToken = default) + public async Task ProcessPendingJobsAsync() { - // กำหนด timeout เป็น 60 นาที - using var timeoutCts = new CancellationTokenSource(TimeSpan.FromMinutes(60)); - using var combinedCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, timeoutCts.Token); + var pendingJobs = await GetPendingOrProcessingJobsAsync(); @@ -145,7 +143,7 @@ namespace BMA.EHR.Application.Repositories.Leaves.TimeAttendants await UpdateToProcessingAsync(job.Id); // ทำงานที่ต้องการที่นี่ (เช่น เรียก API, ประมวลผลข้อมูล ฯลฯ) - await ProcessTaskAsync(job.RootDnaId, combinedCts.Token); + await ProcessTaskAsync(job.RootDnaId); // อัปเดตสถานะเป็น Completed await UpdateToCompletedAsync(job.Id); diff --git a/BMA.EHR.Leave/Program.cs b/BMA.EHR.Leave/Program.cs index e25a59fe..19959f37 100644 --- a/BMA.EHR.Leave/Program.cs +++ b/BMA.EHR.Leave/Program.cs @@ -190,6 +190,8 @@ if (manager != null) manager.AddOrUpdate("ปรับปรุงรอบการลงเวลาทำงาน", Job.FromExpression(x => x.UpdateUserDutyTime()), "0 1 * * *", bangkokTimeZone); // ทำความสะอาดข้อมูล CheckIn Job Status ที่เก่ากว่า 30 วัน - รันทุกวันเวลา 02:00 น. manager.AddOrUpdate("ทำความสะอาดข้อมูล CheckIn Job Status", Job.FromExpression(x => x.CleanupOldJobsAsync(30)), "0 2 * * *", bangkokTimeZone); + + manager.AddOrUpdate("ประมวลผลงานที่ค้างอยู่ในสถานะ Pending หรือ Processing", Job.FromExpression(x => x.ProcessPendingJobsAsync()), "0 3 * * *", bangkokTimeZone); } // apply migrations From 3dee5f716670a27c10bdb0cfd1bcc9156310d847 Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Mon, 30 Mar 2026 12:08:30 +0700 Subject: [PATCH 147/178] Refactor LeaveProcessJobStatusRepository methods and update Hangfire configuration for improved job processing --- .../LeaveProcessJobStatusRepository.cs | 11 +++++----- BMA.EHR.Insignia/Program.cs | 2 +- BMA.EHR.Leave/Program.cs | 22 ++++++++++++++----- BMA.EHR.Leave/appsettings.json | 6 ++--- 4 files changed, 25 insertions(+), 16 deletions(-) diff --git a/BMA.EHR.Application/Repositories/Leaves/TimeAttendants/LeaveProcessJobStatusRepository.cs b/BMA.EHR.Application/Repositories/Leaves/TimeAttendants/LeaveProcessJobStatusRepository.cs index 87940757..9543820f 100644 --- a/BMA.EHR.Application/Repositories/Leaves/TimeAttendants/LeaveProcessJobStatusRepository.cs +++ b/BMA.EHR.Application/Repositories/Leaves/TimeAttendants/LeaveProcessJobStatusRepository.cs @@ -68,10 +68,10 @@ namespace BMA.EHR.Application.Repositories.Leaves.TimeAttendants return data; } - public async Task> GetPendingOrProcessingJobsAsync() + public async Task> GetPendingJobsAsync() { var data = await _dbContext.Set() - .Where(x => x.Status == "PENDING" || x.Status == "PROCESSING") + .Where(x => x.Status == "PENDING") .ToListAsync(); return data; @@ -125,15 +125,14 @@ namespace BMA.EHR.Application.Repositories.Leaves.TimeAttendants public async Task ProcessTaskAsync(Guid id, CancellationToken cancellationToken = default) { - + Console.WriteLine($"กำลังประมวลผลงานที่มี RootDnaId: {id}"); } public async Task ProcessPendingJobsAsync() { - - - var pendingJobs = await GetPendingOrProcessingJobsAsync(); + var pendingJobs = await GetPendingJobsAsync(); + Console.WriteLine($"พบงานที่ค้างอยู่ในสถานะ PENDING จำนวน {pendingJobs.Count} งาน"); foreach (var job in pendingJobs) { diff --git a/BMA.EHR.Insignia/Program.cs b/BMA.EHR.Insignia/Program.cs index a4dd716a..88c1c2ac 100644 --- a/BMA.EHR.Insignia/Program.cs +++ b/BMA.EHR.Insignia/Program.cs @@ -131,7 +131,7 @@ var builder = WebApplication.CreateBuilder(args); { options.ServerName = "Insignia-Server"; // ← ระบุชื่อ server options.WorkerCount = 5; // ← - options.Queues = new[] { "insignia" }; // ← worker จะรันเฉพาะ queue "insignia" + options.Queues = new[] { "insignia","default" }; // ← worker จะรันเฉพาะ queue "insignia" }); diff --git a/BMA.EHR.Leave/Program.cs b/BMA.EHR.Leave/Program.cs index 19959f37..c4774831 100644 --- a/BMA.EHR.Leave/Program.cs +++ b/BMA.EHR.Leave/Program.cs @@ -119,7 +119,7 @@ builder.Services.AddHealthChecks(); builder.Services.AddRabbitMqConnectionPooling(builder.Configuration); // Add Hangfire services. -var defaultConnection = builder.Configuration.GetConnectionString("DefaultConnection"); +var hangfireConnection = builder.Configuration.GetConnectionString("defaultConnection"); builder.Services.AddHangfire(configuration => configuration .SetDataCompatibilityLevel(CompatibilityLevel.Version_170) @@ -127,19 +127,24 @@ builder.Services.AddHangfire(configuration => configuration .UseRecommendedSerializerSettings() .UseStorage( new MySqlStorage( - defaultConnection, + hangfireConnection, new MySqlStorageOptions { - TransactionIsolationLevel = IsolationLevel.ReadCommitted, QueuePollInterval = TimeSpan.FromSeconds(15), JobExpirationCheckInterval = TimeSpan.FromHours(1), CountersAggregateInterval = TimeSpan.FromMinutes(5), PrepareSchemaIfNecessary = true, DashboardJobListLimit = 50000, TransactionTimeout = TimeSpan.FromMinutes(1), - TablesPrefix = "Hangfire" + InvisibilityTimeout = TimeSpan.FromHours(3), + TablesPrefix = "Hangfire_Leave" }))); -builder.Services.AddHangfireServer(); +builder.Services.AddHangfireServer(options => + { + options.ServerName = "Leave-Server"; // ← ระบุชื่อ server + options.WorkerCount = 5; // ← + options.Queues = new[] { "leave","default" }; // ← worker จะรันเฉพาะ queue "leave" + }); var app = builder.Build(); @@ -191,7 +196,12 @@ if (manager != null) // ทำความสะอาดข้อมูล CheckIn Job Status ที่เก่ากว่า 30 วัน - รันทุกวันเวลา 02:00 น. manager.AddOrUpdate("ทำความสะอาดข้อมูล CheckIn Job Status", Job.FromExpression(x => x.CleanupOldJobsAsync(30)), "0 2 * * *", bangkokTimeZone); - manager.AddOrUpdate("ประมวลผลงานที่ค้างอยู่ในสถานะ Pending หรือ Processing", Job.FromExpression(x => x.ProcessPendingJobsAsync()), "0 3 * * *", bangkokTimeZone); + manager.AddOrUpdate("ประมวลผลงานที่ค้างอยู่ในสถานะ Pending หรือ Processing", Job.FromExpression(x => x.ProcessPendingJobsAsync()), "0 3 * * *", + new RecurringJobOptions + { + TimeZone = bangkokTimeZone, + QueueName = "leave" // ← กำหนด queue + }); } // apply migrations diff --git a/BMA.EHR.Leave/appsettings.json b/BMA.EHR.Leave/appsettings.json index f8562d6e..e55f7ac0 100644 --- a/BMA.EHR.Leave/appsettings.json +++ b/BMA.EHR.Leave/appsettings.json @@ -19,9 +19,9 @@ // "ExamConnection": "server=192.168.1.80;user=root;password=adminVM123;port=3306;database=hrms_exam;Convert Zero Datetime=True;Allow User Variables=true;Pooling=True;", // "LeaveConnection": "server=192.168.1.80;user=root;password=adminVM123;port=3306;database=hrms_leave;Convert Zero Datetime=True;Allow User Variables=true;Pooling=True;" - "DefaultConnection": "server=192.168.1.63;user=root;password=12345678;port=3306;database=hrms;Convert Zero Datetime=True;Allow User Variables=true;Pooling=True;", - "ExamConnection": "server=192.168.1.63;user=root;password=12345678;port=3306;database=hrms_exam;Convert Zero Datetime=True;Allow User Variables=true;Pooling=True;", - "LeaveConnection": "server=192.168.1.63;user=root;password=12345678;port=3306;database=hrms_leave;Convert Zero Datetime=True;Allow User Variables=true;Pooling=True;" + "DefaultConnection": "Server=192.168.1.63;User ID=root;Password=12345678;Port=3306;Database=hrms;Allow User Variables=True;Convert Zero Datetime=True;Pooling=True;", + "ExamConnection": "Server=192.168.1.63;User ID=root;Password=12345678;Port=3306;Database=hrms_exam;Allow User Variables=True;Convert Zero Datetime=True;Pooling=True;", + "LeaveConnection": "Server=192.168.1.63;User ID=root;Password=12345678;Port=3306;Database=hrms_leave;Allow User Variables=True;Convert Zero Datetime=True;Pooling=True;" //"DefaultConnection": "server=172.27.17.68;user=user;password=cDldaqkwESWvuZ37Gr0n;port=3306;database=hrms;Convert Zero Datetime=True;Allow User Variables=true;Pooling=True;", //"ExamConnection": "server=172.27.17.68;user=user;password=cDldaqkwESWvuZ37Gr0n;port=3306;database=hrms_exam;Convert Zero Datetime=True;Allow User Variables=true;Pooling=True;", From 759a51ab58648665d580dfd130ec62353291a490 Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Mon, 30 Mar 2026 15:53:33 +0700 Subject: [PATCH 148/178] Enhance LeaveProcessJobStatusRepository with detailed processing logic and add new methods in UserProfileRepository for fetching officer and employee profiles by RootDnaId --- .../LeaveProcessJobStatusRepository.cs | 268 +++++++++++++++++- .../Repositories/UserProfileRepository.cs | 69 +++++ BMA.EHR.Insignia/appsettings.json | 17 +- 3 files changed, 345 insertions(+), 9 deletions(-) diff --git a/BMA.EHR.Application/Repositories/Leaves/TimeAttendants/LeaveProcessJobStatusRepository.cs b/BMA.EHR.Application/Repositories/Leaves/TimeAttendants/LeaveProcessJobStatusRepository.cs index 9543820f..b57183d2 100644 --- a/BMA.EHR.Application/Repositories/Leaves/TimeAttendants/LeaveProcessJobStatusRepository.cs +++ b/BMA.EHR.Application/Repositories/Leaves/TimeAttendants/LeaveProcessJobStatusRepository.cs @@ -2,7 +2,13 @@ using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using System.Text.Json; +using System.IO; using BMA.EHR.Application.Common.Interfaces; +using BMA.EHR.Application.Repositories.Leaves.LeaveRequests; +using BMA.EHR.Application.Repositories.MetaData; +using BMA.EHR.Application.Responses.Profiles; +using BMA.EHR.Domain.Extensions; using BMA.EHR.Domain.Models.Leave.TimeAttendants; using Microsoft.AspNetCore.Http; using Microsoft.EntityFrameworkCore; @@ -14,15 +20,35 @@ namespace BMA.EHR.Application.Repositories.Leaves.TimeAttendants #region " Fields " private readonly ILeaveDbContext _dbContext; + private readonly IHttpContextAccessor _httpContextAccessor; + private readonly UserProfileRepository _userProfileRepository; + private readonly HolidayRepository _holidayRepository; + private readonly DutyTimeRepository _dutyTimeRepository; + private readonly UserDutyTimeRepository _userDutyTimeRepository; + private readonly ProcessUserTimeStampRepository _processUserTimeStampRepository; + private readonly LeaveRequestRepository _leaveRequestRepository; #endregion #region " Constructor and Destructor " public LeaveProcessJobStatusRepository(ILeaveDbContext dbContext, - IHttpContextAccessor httpContextAccessor) : base(dbContext, httpContextAccessor) + IHttpContextAccessor httpContextAccessor, + UserProfileRepository userProfileRepository, + HolidayRepository holidayRepository, + DutyTimeRepository dutyTimeRepository, + UserDutyTimeRepository userDutyTimeRepository, + ProcessUserTimeStampRepository processUserTimeStampRepository, + LeaveRequestRepository leaveRequestRepository) : base(dbContext, httpContextAccessor) { _dbContext = dbContext; + _httpContextAccessor = httpContextAccessor; + _userProfileRepository = userProfileRepository; + _holidayRepository = holidayRepository; + _leaveRequestRepository = leaveRequestRepository; + _dutyTimeRepository = dutyTimeRepository; + _userDutyTimeRepository = userDutyTimeRepository; + _processUserTimeStampRepository = processUserTimeStampRepository; } #endregion @@ -123,9 +149,214 @@ namespace BMA.EHR.Application.Repositories.Leaves.TimeAttendants return job!; } - public async Task ProcessTaskAsync(Guid id, CancellationToken cancellationToken = default) + public async Task ProcessTaskAsync(Guid rootDnaId, DateTime? startDate, DateTime? endDate) { - Console.WriteLine($"กำลังประมวลผลงานที่มี RootDnaId: {id}"); + + var profiles = new List(); + var dateStart = startDate?.Date ?? DateTime.Now.Date; + var dateEnd = endDate?.Date ?? DateTime.Now.Date; + + var holidays = await _holidayRepository.GetHolidayAsync(dateStart, dateEnd); + var weekend = _holidayRepository.GetWeekEnd(dateStart, dateEnd); + var excludeDates = holidays.Union(weekend).ToList(); + + var dateList = new List(); + for (DateTime i = dateStart; i <= dateEnd; i = i.AddDays(1)) + { + if (holidays.Contains(i)) + { + var d = await _holidayRepository.GetHolidayAsync(i); + dateList.Add(new LoopDate + { + date = i, + isHoliday = true, + isWeekEnd = false, + dateRemark = d + }); + } + else if (weekend.Contains(i)) + { + dateList.Add(new LoopDate + { + date = i, + isHoliday = true, + isWeekEnd = false, + dateRemark = "วันหยุด" + }); + } + else + { + dateList.Add(new LoopDate + { + date = i, + isHoliday = false, + isWeekEnd = false, + dateRemark = "" + }); + } + } + + var defaultRound = await _dutyTimeRepository.GetDefaultAsync(); + if (defaultRound == null) + { + throw new Exception("ไม่พบรอบการลงเวลา Default"); + } + + var employees = new List(); + + foreach (var dd in dateList.Where(x => !x.isHoliday && !x.isWeekEnd)) + { + profiles = await _userProfileRepository.GetAllOfficerByRootDnaId(rootDnaId.ToString(),dd.date); + foreach (var p in profiles) + { + var count = 1; + var keycloakUserId = p.Keycloak ?? Guid.Empty; + + var timeStamps = await _processUserTimeStampRepository.GetTimestampByDateAsync(keycloakUserId, dd.date); + + var fullName = $"{p.Prefix}{p.FirstName} {p.LastName}"; + + var effectiveDate = await _userDutyTimeRepository.GetLastEffectRound(p.Id, dd.date); + var roundId = effectiveDate != null ? effectiveDate.DutyTimeId : Guid.Empty; + var userRound = await _dutyTimeRepository.GetByIdAsync(roundId); + + var duty = userRound ?? defaultRound; + + // check วันลาของแต่ละคน + var leaveReq = await _leaveRequestRepository.GetLeavePeriodAsync(keycloakUserId, dd.date); + var remarkStr = string.Empty; + + if (leaveReq != null) + { + switch (leaveReq.Type.Code.ToUpper()) + { + case "LV-001": + case "LV-002": + case "LV-005": + remarkStr += leaveReq.Type.Name; + var leaveRange = leaveReq.LeaveRange == null ? "" : leaveReq.LeaveRange.ToUpper(); + if (leaveRange == "MORNING") + remarkStr += "ครึ่งวันเช้า"; + else if (leaveRange == "AFTERNOON") + remarkStr += "ครึ่งวันบ่าย"; + + + // var leaveRangeEnd = leaveReq.LeaveRangeEnd == null ? "" : leaveReq.LeaveRangeEnd.ToUpper(); + // if (leaveRangeEnd == "MORNING") + // remarkStr += "ครึ่งวันเช้า"; + // else if (leaveRangeEnd == "AFTERNOON") + // remarkStr += "ครึ่งวันบ่าย"; + + var leaveRangeEnd = leaveReq.LeaveRangeEnd == null ? "" : leaveReq.LeaveRangeEnd.ToUpper(); + if (leaveRange != leaveRangeEnd) + { + if (leaveRangeEnd == "MORNING") + remarkStr += " - ครึ่งวันเช้า"; + else if (leaveRangeEnd == "AFTERNOON") + remarkStr += " - ครึ่งวันบ่าย"; + } + break; + default: + remarkStr += leaveReq.Type.Name; + break; + } + } + else + { + if (timeStamps == null) + { + if (dd.date <= DateTime.Now.Date) + { + remarkStr = "ขาดราชการ"; + if (dd.isHoliday == true) + { + remarkStr = $"วันหยุด ({dd.dateRemark})"; + } + else if (dd.isWeekEnd) + { + remarkStr = dd.dateRemark; + } + } + else remarkStr = ""; + } + else + { + // check status ของการลงเวลา + if (timeStamps.CheckOut != null) + { + if (timeStamps.CheckOutStatus == "ABSENT") + remarkStr = "ขาดราชการ" + (!timeStamps.IsLocationCheckOut ? $" (นอกสถานที่:{timeStamps.CheckOutLocationName})".Trim() : ""); + else if (timeStamps.CheckInStatus == "ABSENT") + remarkStr = "ขาดราชการ" + (!timeStamps.IsLocationCheckIn ? $" (นอกสถานที่:{timeStamps.CheckInLocationName})".Trim() : ""); + else if (timeStamps.CheckInStatus == "LATE") + { + remarkStr = "สาย" + (!timeStamps.IsLocationCheckIn ? $" (นอกสถานที่:{timeStamps.CheckInLocationName})".Trim() : ""); + //lateTotal += 1; + } + else + remarkStr = !timeStamps.IsLocationCheckIn ? $" นอกสถานที่:{timeStamps.CheckInLocationName}".Trim() : ""; + } + else + { + if (timeStamps.CheckInStatus == "ABSENT") + remarkStr = "ขาดราชการ" + (!timeStamps.IsLocationCheckIn ? $" (นอกสถานที่:{timeStamps.CheckInLocationName})".Trim() : ""); + else if (timeStamps.CheckInStatus == "LATE") + { + remarkStr = "สาย" + (!timeStamps.IsLocationCheckIn ? $" (นอกสถานที่:{timeStamps.CheckInLocationName})".Trim() : ""); + //lateTotal += 1; + } + else + remarkStr = !timeStamps.IsLocationCheckIn ? $" นอกสถานที่:{timeStamps.CheckInLocationName}".Trim() : ""; + } + } + } + + var emp = new DateResultReport + { + no = count, + fullName = fullName, + dutyTimeName = $"{duty.StartTimeMorning} - {duty.EndTimeAfternoon} น.", + checkInLocation = timeStamps == null ? "" : timeStamps.CheckInPOI, + checkInTime = timeStamps == null ? "" : $"{timeStamps.CheckIn.ToString("HH:mm")} น.", + checkOutLocation = timeStamps == null ? "" : timeStamps.CheckOutPOI ?? "", + checkOutTime = timeStamps == null ? "" : + timeStamps.CheckOut != null ? + $"{timeStamps.CheckOut.Value.ToString("HH:mm")} น." : + "", + remark = remarkStr, + checkInDate = timeStamps == null ? dd.date.Date.ToThaiFullDate2() : timeStamps.CheckIn.Date.ToThaiFullDate2(), + checkedOutDate = timeStamps == null ? dd.date.Date.ToThaiFullDate2() : + timeStamps.CheckOut != null ? + timeStamps.CheckOut.Value.ToThaiFullDate2() : + "", + checkInTimeRaw = timeStamps == null ? dd.date.Date : timeStamps?.CheckIn, + checkOutTimeRaw = timeStamps == null ? dd.date.Date : timeStamps?.CheckOut != null ? timeStamps?.CheckOut : null, + }; + + employees.Add(emp); + count++; + } + + // Write employees to JSON file + var fileName = $"employees_{DateTime.Now:yyyyMMdd_HHmmss}.txt"; + var filePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Exports", fileName); + + // Ensure directory exists + var directory = Path.GetDirectoryName(filePath); + if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory)) + { + Directory.CreateDirectory(directory); + } + + var jsonOptions = new JsonSerializerOptions + { + WriteIndented = true, + Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping + }; + + var jsonContent = JsonSerializer.Serialize(employees, jsonOptions); + await File.WriteAllTextAsync(filePath, jsonContent); + } } public async Task ProcessPendingJobsAsync() @@ -142,7 +373,7 @@ namespace BMA.EHR.Application.Repositories.Leaves.TimeAttendants await UpdateToProcessingAsync(job.Id); // ทำงานที่ต้องการที่นี่ (เช่น เรียก API, ประมวลผลข้อมูล ฯลฯ) - await ProcessTaskAsync(job.RootDnaId); + await ProcessTaskAsync(job.RootDnaId,job.StartDate, job.EndDate); // อัปเดตสถานะเป็น Completed await UpdateToCompletedAsync(job.Id); @@ -157,4 +388,33 @@ namespace BMA.EHR.Application.Repositories.Leaves.TimeAttendants #endregion } + + class LoopDate + { + public DateTime date { get; set; } + + public bool isHoliday { get; set; } + + public bool isWeekEnd { get; set; } + + public string dateRemark { get; set; } + + } + + class DateResultReport + { + public int no { get; set; } + public string fullName { get; set; } + public string dutyTimeName { get; set; } + public string checkInLocation { get; set; } + public string checkInTime { get; set; } + public string checkOutLocation { get; set; } + public string checkOutTime { get; set; } + public string remark { get; set; } + public string checkInDate { get; set; } + public string checkedOutDate { get; set; } + public DateTime? checkInTimeRaw { get; set; } + public DateTime? checkOutTimeRaw { get; set; } + } + } \ No newline at end of file diff --git a/BMA.EHR.Application/Repositories/UserProfileRepository.cs b/BMA.EHR.Application/Repositories/UserProfileRepository.cs index ba1f70c3..4ae8bcbe 100644 --- a/BMA.EHR.Application/Repositories/UserProfileRepository.cs +++ b/BMA.EHR.Application/Repositories/UserProfileRepository.cs @@ -764,6 +764,75 @@ namespace BMA.EHR.Application.Repositories } } + public async Task> GetAllOfficerByRootDnaId(string? rootDnaId, DateTime date) + { + try + { + var apiPath = $"{_configuration["API"]}/org/unauthorize/officer-list"; + var apiKey = _configuration["API_KEY"]; + var body = new + { + reqNode = 0, + reqNodeId = rootDnaId, + date = date + }; + //Console.WriteLine(body); + + var profiles = new List(); + + var apiResult = await PostExternalAPIAsync(apiPath, "", body, apiKey); + if (apiResult != null) + { + var raw = JsonConvert.DeserializeObject(apiResult); + if (raw != null) + return raw.Result; + else + return new List(); + } + else + return new List(); + } + catch + { + throw; + } + } + + public async Task> GetAllEmployeeByRootDnaId(string? rootDnaId, DateTime date) + { + try + { + var apiPath = $"{_configuration["API"]}/org/unauthorize/employee-list"; + var apiKey = _configuration["API_KEY"]; + var body = new + { + reqNode = 0, + reqNodeId = rootDnaId, + startDate = date, + endDate = date + }; + //Console.WriteLine(body); + + var profiles = new List(); + + var apiResult = await PostExternalAPIAsync(apiPath, "", body, apiKey); + if (apiResult != null) + { + var raw = JsonConvert.DeserializeObject(apiResult); + if (raw != null) + return raw.Result; + else + return new List(); + } + else + return new List(); + } + catch + { + throw; + } + } + public async Task> GetProfileByAdminRolev4(string? accessToken, int? node, string? nodeId, string role, string? revisionId, int? reqNode, string? reqNodeId, DateTime? startDate, DateTime? endDate) { try diff --git a/BMA.EHR.Insignia/appsettings.json b/BMA.EHR.Insignia/appsettings.json index 4f3d3faf..4e160482 100644 --- a/BMA.EHR.Insignia/appsettings.json +++ b/BMA.EHR.Insignia/appsettings.json @@ -31,10 +31,11 @@ //"DisciplineConnection": "server=hrms.chin.in.th;user=root;password=ey2qVVyyqGYw8CyA7h8X72559r2Ad84K;port=53636;database=hrms_discipline;Convert Zero Datetime=True;Allow User Variables=true;Pooling=True;" }, "Jwt": { - //"Key": "j7C9RO_p4nRtuwCH4z9Db_A_6We42tkD_p4lZtDrezc", - //"Issuer": "https://hrms-id.chin.in.th/realms/hrms" - "Key": "HP-FnQMUj9msHMSD3T9HtdEnphAKoCJLEl85CIqROFI", - "Issuer": "https://id.frappet.synology.me/realms/hrms" + //"Key": "HP-FnQMUj9msHMSD3T9HtdEnphAKoCJLEl85CIqROFI", + "Key": "j7C9RO_p4nRtuwCH4z9Db_A_6We42tkD_p4lZtDrezc", + "Issuer": "https://hrmsbkk-id.case-collection.com/realms/hrms" + //"Key": "xY2VR-EFvvNPsMs39u8ooVBWQL6mPwrNJOh3koJFTgU", + //"Issuer": "https://hrms-id.bangkok.go.th/realms/hrms" }, "EPPlus": { "ExcelPackage": { @@ -55,11 +56,17 @@ "Node": { "API": "https://bma-ehr.frappet.synology.me/api/v1/probation" }, - "API": "https://bma-ehr.frappet.synology.me/api/v1", "RabbitMQ": { "URL": "localhost", "UserName": "frappet", "Password": "FPTadmin2357" }, + "Domain": "https://hrmsbkk.case-collection.com", + "APIPROBATION": "https://hrmsbkk.case-collection.com/api/v1/probation", + "API": "https://hrmsbkk.case-collection.com/api/v1", + "APIV2": "https://hrmsbkk.case-collection.com/api/v2", + "VITE_URL_MGT": "https://hrmsbkk-mgt.case-collection.com", + //"API": "https://bma-ehr.frappet.synology.me/api/v1", + //"API": "https://bma-hrms.bangkok.go.th/api/v1", "API_KEY": "fKRL16yyEgbyTEJdsMw2h64tGSCmkW685PRtM3CygzX1JOSdptT9UJtpgWwKM8FybRTJups3GTFwj27ZRvlPdIkv3XgCoVJaD5LmR06ozuEPvCCRSdp2WFthg08V5xHc56fTPfZLpr1VmXrhd6dvYhHIqKkQUJR02Rlkss11cLRWEQOssEFVA4xdu2J5DIRO1EM5m7wRRvEwcDB4mYRXD9HH52SMq6iYqUWEWsMwLdbk7QW9yYESUEuzMW5gWrb6vIeWZxJV5bTz1PcWUyR7eO9Fyw1F5DiQYc9JgzTC1mW7cv31fEtTtrfbJYKIb5EbWilqIEUKC6A0UKBDDek35ML0006cqRVm0pvdOH6jeq7VQyYrhdXe59dBEyhYGUIfozoVBvW7Up4QBuOMjyPjSqJPlMBKwaseptfrblxQV1AOOivSBpf1ZcQyOZ8JktRtKUDSuXsmG0lsXwFlI3JCeSHdpVdgZWFYcJPegqfrB6KotR02t9AVkpLs1ZWrixwz" } From 2cd7798dd91391c2b9b9fe637d430021b0134d27 Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Tue, 31 Mar 2026 09:46:44 +0700 Subject: [PATCH 149/178] Add admin endpoints for processing leave tasks, including retrieval, deletion, and updates --- .../LeaveProcessJobStatusRepository.cs | 84 +++++++---- BMA.EHR.Leave/Controllers/LeaveController.cs | 133 ++++++++++++++++++ 2 files changed, 189 insertions(+), 28 deletions(-) diff --git a/BMA.EHR.Application/Repositories/Leaves/TimeAttendants/LeaveProcessJobStatusRepository.cs b/BMA.EHR.Application/Repositories/Leaves/TimeAttendants/LeaveProcessJobStatusRepository.cs index b57183d2..ded9e370 100644 --- a/BMA.EHR.Application/Repositories/Leaves/TimeAttendants/LeaveProcessJobStatusRepository.cs +++ b/BMA.EHR.Application/Repositories/Leaves/TimeAttendants/LeaveProcessJobStatusRepository.cs @@ -80,6 +80,19 @@ namespace BMA.EHR.Application.Repositories.Leaves.TimeAttendants return data; } + /// + /// ดึงข้อมูล Job Status จาก UserId + /// + public async Task> GetByUserIdAsync(Guid userId) + { + var data = await _dbContext.Set() + .Where(x => x.CreatedUserId == userId.ToString("D")) + .OrderByDescending(x => x.CreatedDate) + .ToListAsync(); + + return data; + } + /// /// ดึงข้อมูล Job Status ที่ยัง pending หรือ processing /// @@ -225,6 +238,9 @@ namespace BMA.EHR.Application.Repositories.Leaves.TimeAttendants // check วันลาของแต่ละคน var leaveReq = await _leaveRequestRepository.GetLeavePeriodAsync(keycloakUserId, dd.date); var remarkStr = string.Empty; + var status = string.Empty; + var stampType = string.Empty; + var stampAmount = 0.0; if (leaveReq != null) { @@ -260,6 +276,9 @@ namespace BMA.EHR.Application.Repositories.Leaves.TimeAttendants remarkStr += leaveReq.Type.Name; break; } + status = "LEAVE"; + stampType = leaveReq.LeaveRange ?? ""; + stampAmount = leaveReq.LeaveRange == "MORNING" || leaveReq.LeaveRangeEnd == "MORNING" ? 0.5 : 1; } else { @@ -268,13 +287,18 @@ namespace BMA.EHR.Application.Repositories.Leaves.TimeAttendants if (dd.date <= DateTime.Now.Date) { remarkStr = "ขาดราชการ"; + status = "ABSENT"; + stampType = "FULL_DAY"; + stampAmount = 1; if (dd.isHoliday == true) { remarkStr = $"วันหยุด ({dd.dateRemark})"; + status = "HOLIDAY"; } else if (dd.isWeekEnd) { remarkStr = dd.dateRemark; + status = "WEEKEND"; } } else remarkStr = ""; @@ -285,12 +309,25 @@ namespace BMA.EHR.Application.Repositories.Leaves.TimeAttendants if (timeStamps.CheckOut != null) { if (timeStamps.CheckOutStatus == "ABSENT") + { remarkStr = "ขาดราชการ" + (!timeStamps.IsLocationCheckOut ? $" (นอกสถานที่:{timeStamps.CheckOutLocationName})".Trim() : ""); + status = "ABSENT"; + stampType = "FULL_DAY"; + stampAmount = 1; + } else if (timeStamps.CheckInStatus == "ABSENT") + { remarkStr = "ขาดราชการ" + (!timeStamps.IsLocationCheckIn ? $" (นอกสถานที่:{timeStamps.CheckInLocationName})".Trim() : ""); + status = "ABSENT"; + stampType = "FULL_DAY"; + stampAmount = 1; + } else if (timeStamps.CheckInStatus == "LATE") { remarkStr = "สาย" + (!timeStamps.IsLocationCheckIn ? $" (นอกสถานที่:{timeStamps.CheckInLocationName})".Trim() : ""); + status = "LATE"; + stampType = "FULL_DAY"; + stampAmount = 1; //lateTotal += 1; } else @@ -299,9 +336,17 @@ namespace BMA.EHR.Application.Repositories.Leaves.TimeAttendants else { if (timeStamps.CheckInStatus == "ABSENT") + { + status = "ABSENT"; + stampType = "FULL_DAY"; + stampAmount = 1; remarkStr = "ขาดราชการ" + (!timeStamps.IsLocationCheckIn ? $" (นอกสถานที่:{timeStamps.CheckInLocationName})".Trim() : ""); + } else if (timeStamps.CheckInStatus == "LATE") { + status = "LATE"; + stampType = "FULL_DAY"; + stampAmount = 1; remarkStr = "สาย" + (!timeStamps.IsLocationCheckIn ? $" (นอกสถานที่:{timeStamps.CheckInLocationName})".Trim() : ""); //lateTotal += 1; } @@ -313,24 +358,13 @@ namespace BMA.EHR.Application.Repositories.Leaves.TimeAttendants var emp = new DateResultReport { - no = count, - fullName = fullName, - dutyTimeName = $"{duty.StartTimeMorning} - {duty.EndTimeAfternoon} น.", - checkInLocation = timeStamps == null ? "" : timeStamps.CheckInPOI, - checkInTime = timeStamps == null ? "" : $"{timeStamps.CheckIn.ToString("HH:mm")} น.", - checkOutLocation = timeStamps == null ? "" : timeStamps.CheckOutPOI ?? "", - checkOutTime = timeStamps == null ? "" : - timeStamps.CheckOut != null ? - $"{timeStamps.CheckOut.Value.ToString("HH:mm")} น." : - "", + profileId = p.Id.ToString(), + stampDate = dd.date, + stampType = stampType, + stampAmount = stampAmount, remark = remarkStr, - checkInDate = timeStamps == null ? dd.date.Date.ToThaiFullDate2() : timeStamps.CheckIn.Date.ToThaiFullDate2(), - checkedOutDate = timeStamps == null ? dd.date.Date.ToThaiFullDate2() : - timeStamps.CheckOut != null ? - timeStamps.CheckOut.Value.ToThaiFullDate2() : - "", - checkInTimeRaw = timeStamps == null ? dd.date.Date : timeStamps?.CheckIn, - checkOutTimeRaw = timeStamps == null ? dd.date.Date : timeStamps?.CheckOut != null ? timeStamps?.CheckOut : null, + status = status + }; employees.Add(emp); @@ -403,18 +437,12 @@ namespace BMA.EHR.Application.Repositories.Leaves.TimeAttendants class DateResultReport { - public int no { get; set; } - public string fullName { get; set; } - public string dutyTimeName { get; set; } - public string checkInLocation { get; set; } - public string checkInTime { get; set; } - public string checkOutLocation { get; set; } - public string checkOutTime { get; set; } + public string? profileId { get; set; } + public DateTime stampDate { get; set; } + public string stampType { get; set; } + public double stampAmount { get; set; } public string remark { get; set; } - public string checkInDate { get; set; } - public string checkedOutDate { get; set; } - public DateTime? checkInTimeRaw { get; set; } - public DateTime? checkOutTimeRaw { get; set; } + public string status { get; set; } } } \ No newline at end of file diff --git a/BMA.EHR.Leave/Controllers/LeaveController.cs b/BMA.EHR.Leave/Controllers/LeaveController.cs index 930c4043..799f3558 100644 --- a/BMA.EHR.Leave/Controllers/LeaveController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveController.cs @@ -4200,6 +4200,139 @@ namespace BMA.EHR.Leave.Service.Controllers return Success(); } + /// + /// แสดงรายการ Task สำหรับ Process ข้อมูลวันลาและขาดราชการ (ADMIN) + /// + /// + /// + /// เมื่อทำรายการสำเร็จ + /// ไม่ได้ Login เข้าระบบ + /// เมื่อเกิดข้อผิดพลาดในการทำงาน + [HttpGet("admin/leave-task/process")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status401Unauthorized)] + [ProducesResponseType(StatusCodes.Status500InternalServerError)] + public async Task> GetProcessTaskAsync() + { + var userId = UserId == null ? Guid.Empty : Guid.Parse(UserId); + + var tasks = await _leaveProcessJobStatusRepository.GetByUserIdAsync(userId); + + var result = tasks.Select(t => new + { + t.Id, + t.CreatedFullName, + t.CreatedAt, + t.Status, + t.StartDate, + t.EndDate, + t.ProcessingDate, + t.CompletedDate, + t.ErrorMessage + }); + + return Success(result); + } + + + /// + /// แสดงรายการ Task สำหรับ Process ข้อมูลวันลาและขาดราชการ (ADMIN) + /// + /// + /// + /// เมื่อทำรายการสำเร็จ + /// ไม่ได้ Login เข้าระบบ + /// เมื่อเกิดข้อผิดพลาดในการทำงาน + [HttpGet("admin/leave-task/process/{id:guid}")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status401Unauthorized)] + [ProducesResponseType(StatusCodes.Status500InternalServerError)] + public async Task> GetProcessTaskByIdAsync(Guid id) + { + var task = await _leaveProcessJobStatusRepository.GetByTaskIdAsync(id); + + if (task == null) + { + return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound); + } + + var result = new + { + task.Id, + task.CreatedFullName, + task.CreatedAt, + task.Status, + task.StartDate, + task.EndDate, + task.ProcessingDate, + task.CompletedDate, + task.ErrorMessage + }; + + return Success(result); + } + + /// + /// ลบ Task สำหรับ Process ข้อมูลวันลาและขาดราชการ (ADMIN) + /// + /// + /// + /// เมื่อทำรายการสำเร็จ + /// ไม่ได้ Login เข้าระบบ + /// เมื่อเกิดข้อผิดพลาดในการทำงาน + [HttpDelete("admin/leave-task/process/{id:guid}")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status401Unauthorized)] + [ProducesResponseType(StatusCodes.Status500InternalServerError)] + public async Task> DeleteProcessTaskByIdAsync(Guid id) + { + var task = await _leaveProcessJobStatusRepository.GetByTaskIdAsync(id); + + if (task == null) + { + return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound); + } + + await _leaveProcessJobStatusRepository.DeleteAsync(task); + + return Success(); + } + + /// + /// อัปเดต Task สำหรับ Process ข้อมูลวันลาและขาดราชการ (ADMIN) + /// + /// + /// + /// เมื่อทำรายการสำเร็จ + /// ไม่ได้ Login เข้าระบบ + /// เมื่อเกิดข้อผิดพลาดในการทำงาน + [HttpPut("admin/leave-task/process/{id:guid}")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status401Unauthorized)] + [ProducesResponseType(StatusCodes.Status500InternalServerError)] + public async Task> UpdateProcessTaskByIdAsync(Guid id,[FromBody] CreateLeaveProcessJobDto req) + { + var task = await _leaveProcessJobStatusRepository.GetByTaskIdAsync(id); + + if (task == null) + { + return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound); + } + + if(task.Status != "PENDING") + { + return Error("ไม่สามารถแก้ไขได้เนื่องจาก Task อยู่ในสถานะกำลังดำเนินการหรือดำเนินการเสร็จสิ้นแล้ว"); + } + + task.StartDate = req.StartDate; + task.EndDate = req.EndDate; + + await _leaveProcessJobStatusRepository.UpdateAsync(task); + + return Success(); + } + + #endregion #endregion From 82c31a0f576d9260253e87bbc14832d744ff7838 Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Tue, 31 Mar 2026 10:18:06 +0700 Subject: [PATCH 150/178] Refactor GenericRepository and GenericLeaveRepository to expose PostExternalAPIAsync method and enhance LeaveProcessJobStatusRepository with API integration for processing employee records --- .../Repositories/GenericRepository.cs | 2 +- .../Leaves/GenericLeaveRepository.cs | 35 ++++++++++++ .../LeaveProcessJobStatusRepository.cs | 54 ++++++++++++------- 3 files changed, 71 insertions(+), 20 deletions(-) diff --git a/BMA.EHR.Application/Repositories/GenericRepository.cs b/BMA.EHR.Application/Repositories/GenericRepository.cs index 805b88ba..d825ccaf 100644 --- a/BMA.EHR.Application/Repositories/GenericRepository.cs +++ b/BMA.EHR.Application/Repositories/GenericRepository.cs @@ -115,7 +115,7 @@ namespace BMA.EHR.Application.Repositories } - protected async Task PostExternalAPIAsync(string apiPath, string accessToken, object? body, string apiKey, CancellationToken cancellationToken = default) + public async Task PostExternalAPIAsync(string apiPath, string accessToken, object? body, string apiKey, CancellationToken cancellationToken = default) { try { diff --git a/BMA.EHR.Application/Repositories/Leaves/GenericLeaveRepository.cs b/BMA.EHR.Application/Repositories/Leaves/GenericLeaveRepository.cs index 93ae9cec..2e550d71 100644 --- a/BMA.EHR.Application/Repositories/Leaves/GenericLeaveRepository.cs +++ b/BMA.EHR.Application/Repositories/Leaves/GenericLeaveRepository.cs @@ -2,8 +2,11 @@ using BMA.EHR.Domain.Models.Base; using Microsoft.AspNetCore.Http; using Microsoft.EntityFrameworkCore; +using Newtonsoft.Json; using System.IO.Pipes; +using System.Net.Http.Headers; using System.Security.Claims; +using System.Text; namespace BMA.EHR.Application.Repositories.Leaves { @@ -43,6 +46,38 @@ namespace BMA.EHR.Application.Repositories.Leaves #region " Methods " + public async Task PostExternalAPIAsync(string apiPath, string accessToken, object? body, string apiKey, CancellationToken cancellationToken = default) + { + try + { + // กำหนด timeout เป็น 30 นาที + using var timeoutCts = new CancellationTokenSource(TimeSpan.FromMinutes(30)); + using var combinedCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, timeoutCts.Token); + var json = JsonConvert.SerializeObject(body); + var stringContent = new StringContent(json, Encoding.UTF8, "application/json"); + //stringContent.Headers.ContentType = new MediaTypeHeaderValue("application/json"); + + using (var client = new HttpClient()) + { + client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken.Replace("Bearer ", "")); + client.DefaultRequestHeaders.Add("api-key", apiKey); + var _res = await client.PostAsync(apiPath, stringContent, combinedCts.Token); + if (_res.IsSuccessStatusCode) + { + var _result = await _res.Content.ReadAsStringAsync(); + + return _result; + } + return string.Empty; + } + } + catch + { + throw; + } + } + + public virtual async Task> GetAllAsync() { return await _dbSet.ToListAsync(); diff --git a/BMA.EHR.Application/Repositories/Leaves/TimeAttendants/LeaveProcessJobStatusRepository.cs b/BMA.EHR.Application/Repositories/Leaves/TimeAttendants/LeaveProcessJobStatusRepository.cs index ded9e370..4eba7b2a 100644 --- a/BMA.EHR.Application/Repositories/Leaves/TimeAttendants/LeaveProcessJobStatusRepository.cs +++ b/BMA.EHR.Application/Repositories/Leaves/TimeAttendants/LeaveProcessJobStatusRepository.cs @@ -12,6 +12,7 @@ using BMA.EHR.Domain.Extensions; using BMA.EHR.Domain.Models.Leave.TimeAttendants; using Microsoft.AspNetCore.Http; using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Configuration; namespace BMA.EHR.Application.Repositories.Leaves.TimeAttendants { @@ -27,6 +28,7 @@ namespace BMA.EHR.Application.Repositories.Leaves.TimeAttendants private readonly UserDutyTimeRepository _userDutyTimeRepository; private readonly ProcessUserTimeStampRepository _processUserTimeStampRepository; private readonly LeaveRequestRepository _leaveRequestRepository; + private readonly IConfiguration _configuration; #endregion @@ -39,12 +41,14 @@ namespace BMA.EHR.Application.Repositories.Leaves.TimeAttendants DutyTimeRepository dutyTimeRepository, UserDutyTimeRepository userDutyTimeRepository, ProcessUserTimeStampRepository processUserTimeStampRepository, - LeaveRequestRepository leaveRequestRepository) : base(dbContext, httpContextAccessor) + LeaveRequestRepository leaveRequestRepository, + IConfiguration configuration) : base(dbContext, httpContextAccessor) { _dbContext = dbContext; _httpContextAccessor = httpContextAccessor; _userProfileRepository = userProfileRepository; _holidayRepository = holidayRepository; + _configuration = configuration; _leaveRequestRepository = leaveRequestRepository; _dutyTimeRepository = dutyTimeRepository; _userDutyTimeRepository = userDutyTimeRepository; @@ -364,38 +368,50 @@ namespace BMA.EHR.Application.Repositories.Leaves.TimeAttendants stampAmount = stampAmount, remark = remarkStr, status = status - }; employees.Add(emp); count++; } - // Write employees to JSON file - var fileName = $"employees_{DateTime.Now:yyyyMMdd_HHmmss}.txt"; - var filePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Exports", fileName); + // // Write employees to JSON file + // var fileName = $"employees_{DateTime.Now:yyyyMMdd_HHmmss}.txt"; + // var filePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Exports", fileName); - // Ensure directory exists - var directory = Path.GetDirectoryName(filePath); - if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory)) - { - Directory.CreateDirectory(directory); - } + // // Ensure directory exists + // var directory = Path.GetDirectoryName(filePath); + // if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory)) + // { + // Directory.CreateDirectory(directory); + // } - var jsonOptions = new JsonSerializerOptions - { - WriteIndented = true, - Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping - }; + // var jsonOptions = new JsonSerializerOptions + // { + // WriteIndented = true, + // Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping + // }; - var jsonContent = JsonSerializer.Serialize(employees, jsonOptions); - await File.WriteAllTextAsync(filePath, jsonContent); + // var jsonContent = JsonSerializer.Serialize(employees, jsonOptions); + // await File.WriteAllTextAsync(filePath, jsonContent); } + + //call api + var apiPath = $"{_configuration["API"]}/org/profile/absent-late/batch"; + var apiKey = _configuration["API_KEY"]; + var body = new + { + records = employees + }; + + var apiResult = await PostExternalAPIAsync(apiPath, AccessToken ?? "", body, apiKey); + if(apiResult == "") + { + throw new Exception($"เรียก API {apiPath} ไม่สำเร็จ"); + } } public async Task ProcessPendingJobsAsync() { - var pendingJobs = await GetPendingJobsAsync(); Console.WriteLine($"พบงานที่ค้างอยู่ในสถานะ PENDING จำนวน {pendingJobs.Count} งาน"); From d85bab11b2cf17bd38464b5e480685203f6b21e5 Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Tue, 31 Mar 2026 10:20:30 +0700 Subject: [PATCH 151/178] Add ProcessEmpTaskAsync method to handle employee task processing and integrate with external API for attendance reporting --- .../LeaveProcessJobStatusRepository.cs | 246 ++++++++++++++++++ 1 file changed, 246 insertions(+) diff --git a/BMA.EHR.Application/Repositories/Leaves/TimeAttendants/LeaveProcessJobStatusRepository.cs b/BMA.EHR.Application/Repositories/Leaves/TimeAttendants/LeaveProcessJobStatusRepository.cs index 4eba7b2a..4a2be392 100644 --- a/BMA.EHR.Application/Repositories/Leaves/TimeAttendants/LeaveProcessJobStatusRepository.cs +++ b/BMA.EHR.Application/Repositories/Leaves/TimeAttendants/LeaveProcessJobStatusRepository.cs @@ -410,6 +410,251 @@ namespace BMA.EHR.Application.Repositories.Leaves.TimeAttendants } } + public async Task ProcessEmpTaskAsync(Guid rootDnaId, DateTime? startDate, DateTime? endDate) + { + + var profiles = new List(); + var dateStart = startDate?.Date ?? DateTime.Now.Date; + var dateEnd = endDate?.Date ?? DateTime.Now.Date; + + var holidays = await _holidayRepository.GetHolidayAsync(dateStart, dateEnd); + var weekend = _holidayRepository.GetWeekEnd(dateStart, dateEnd); + var excludeDates = holidays.Union(weekend).ToList(); + + var dateList = new List(); + for (DateTime i = dateStart; i <= dateEnd; i = i.AddDays(1)) + { + if (holidays.Contains(i)) + { + var d = await _holidayRepository.GetHolidayAsync(i); + dateList.Add(new LoopDate + { + date = i, + isHoliday = true, + isWeekEnd = false, + dateRemark = d + }); + } + else if (weekend.Contains(i)) + { + dateList.Add(new LoopDate + { + date = i, + isHoliday = true, + isWeekEnd = false, + dateRemark = "วันหยุด" + }); + } + else + { + dateList.Add(new LoopDate + { + date = i, + isHoliday = false, + isWeekEnd = false, + dateRemark = "" + }); + } + } + + var defaultRound = await _dutyTimeRepository.GetDefaultAsync(); + if (defaultRound == null) + { + throw new Exception("ไม่พบรอบการลงเวลา Default"); + } + + var employees = new List(); + + foreach (var dd in dateList.Where(x => !x.isHoliday && !x.isWeekEnd)) + { + profiles = await _userProfileRepository.GetAllEmployeeByRootDnaId(rootDnaId.ToString(),dd.date); + foreach (var p in profiles) + { + var count = 1; + var keycloakUserId = p.Keycloak ?? Guid.Empty; + + var timeStamps = await _processUserTimeStampRepository.GetTimestampByDateAsync(keycloakUserId, dd.date); + + var fullName = $"{p.Prefix}{p.FirstName} {p.LastName}"; + + var effectiveDate = await _userDutyTimeRepository.GetLastEffectRound(p.Id, dd.date); + var roundId = effectiveDate != null ? effectiveDate.DutyTimeId : Guid.Empty; + var userRound = await _dutyTimeRepository.GetByIdAsync(roundId); + + var duty = userRound ?? defaultRound; + + // check วันลาของแต่ละคน + var leaveReq = await _leaveRequestRepository.GetLeavePeriodAsync(keycloakUserId, dd.date); + var remarkStr = string.Empty; + var status = string.Empty; + var stampType = string.Empty; + var stampAmount = 0.0; + + if (leaveReq != null) + { + switch (leaveReq.Type.Code.ToUpper()) + { + case "LV-001": + case "LV-002": + case "LV-005": + remarkStr += leaveReq.Type.Name; + var leaveRange = leaveReq.LeaveRange == null ? "" : leaveReq.LeaveRange.ToUpper(); + if (leaveRange == "MORNING") + remarkStr += "ครึ่งวันเช้า"; + else if (leaveRange == "AFTERNOON") + remarkStr += "ครึ่งวันบ่าย"; + + + // var leaveRangeEnd = leaveReq.LeaveRangeEnd == null ? "" : leaveReq.LeaveRangeEnd.ToUpper(); + // if (leaveRangeEnd == "MORNING") + // remarkStr += "ครึ่งวันเช้า"; + // else if (leaveRangeEnd == "AFTERNOON") + // remarkStr += "ครึ่งวันบ่าย"; + + var leaveRangeEnd = leaveReq.LeaveRangeEnd == null ? "" : leaveReq.LeaveRangeEnd.ToUpper(); + if (leaveRange != leaveRangeEnd) + { + if (leaveRangeEnd == "MORNING") + remarkStr += " - ครึ่งวันเช้า"; + else if (leaveRangeEnd == "AFTERNOON") + remarkStr += " - ครึ่งวันบ่าย"; + } + break; + default: + remarkStr += leaveReq.Type.Name; + break; + } + status = "LEAVE"; + stampType = leaveReq.LeaveRange ?? ""; + stampAmount = leaveReq.LeaveRange == "MORNING" || leaveReq.LeaveRangeEnd == "MORNING" ? 0.5 : 1; + } + else + { + if (timeStamps == null) + { + if (dd.date <= DateTime.Now.Date) + { + remarkStr = "ขาดราชการ"; + status = "ABSENT"; + stampType = "FULL_DAY"; + stampAmount = 1; + if (dd.isHoliday == true) + { + remarkStr = $"วันหยุด ({dd.dateRemark})"; + status = "HOLIDAY"; + } + else if (dd.isWeekEnd) + { + remarkStr = dd.dateRemark; + status = "WEEKEND"; + } + } + else remarkStr = ""; + } + else + { + // check status ของการลงเวลา + if (timeStamps.CheckOut != null) + { + if (timeStamps.CheckOutStatus == "ABSENT") + { + remarkStr = "ขาดราชการ" + (!timeStamps.IsLocationCheckOut ? $" (นอกสถานที่:{timeStamps.CheckOutLocationName})".Trim() : ""); + status = "ABSENT"; + stampType = "FULL_DAY"; + stampAmount = 1; + } + else if (timeStamps.CheckInStatus == "ABSENT") + { + remarkStr = "ขาดราชการ" + (!timeStamps.IsLocationCheckIn ? $" (นอกสถานที่:{timeStamps.CheckInLocationName})".Trim() : ""); + status = "ABSENT"; + stampType = "FULL_DAY"; + stampAmount = 1; + } + else if (timeStamps.CheckInStatus == "LATE") + { + remarkStr = "สาย" + (!timeStamps.IsLocationCheckIn ? $" (นอกสถานที่:{timeStamps.CheckInLocationName})".Trim() : ""); + status = "LATE"; + stampType = "FULL_DAY"; + stampAmount = 1; + //lateTotal += 1; + } + else + remarkStr = !timeStamps.IsLocationCheckIn ? $" นอกสถานที่:{timeStamps.CheckInLocationName}".Trim() : ""; + } + else + { + if (timeStamps.CheckInStatus == "ABSENT") + { + status = "ABSENT"; + stampType = "FULL_DAY"; + stampAmount = 1; + remarkStr = "ขาดราชการ" + (!timeStamps.IsLocationCheckIn ? $" (นอกสถานที่:{timeStamps.CheckInLocationName})".Trim() : ""); + } + else if (timeStamps.CheckInStatus == "LATE") + { + status = "LATE"; + stampType = "FULL_DAY"; + stampAmount = 1; + remarkStr = "สาย" + (!timeStamps.IsLocationCheckIn ? $" (นอกสถานที่:{timeStamps.CheckInLocationName})".Trim() : ""); + //lateTotal += 1; + } + else + remarkStr = !timeStamps.IsLocationCheckIn ? $" นอกสถานที่:{timeStamps.CheckInLocationName}".Trim() : ""; + } + } + } + + var emp = new DateResultReport + { + profileId = p.Id.ToString(), + stampDate = dd.date, + stampType = stampType, + stampAmount = stampAmount, + remark = remarkStr, + status = status + }; + + employees.Add(emp); + count++; + } + + // // Write employees to JSON file + // var fileName = $"employees_{DateTime.Now:yyyyMMdd_HHmmss}.txt"; + // var filePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Exports", fileName); + + // // Ensure directory exists + // var directory = Path.GetDirectoryName(filePath); + // if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory)) + // { + // Directory.CreateDirectory(directory); + // } + + // var jsonOptions = new JsonSerializerOptions + // { + // WriteIndented = true, + // Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping + // }; + + // var jsonContent = JsonSerializer.Serialize(employees, jsonOptions); + // await File.WriteAllTextAsync(filePath, jsonContent); + } + + //call api + var apiPath = $"{_configuration["API"]}/org/profile-employee/absent-late/batch"; + var apiKey = _configuration["API_KEY"]; + var body = new + { + records = employees + }; + + var apiResult = await PostExternalAPIAsync(apiPath, AccessToken ?? "", body, apiKey); + if(apiResult == "") + { + throw new Exception($"เรียก API {apiPath} ไม่สำเร็จ"); + } + } + + public async Task ProcessPendingJobsAsync() { var pendingJobs = await GetPendingJobsAsync(); @@ -424,6 +669,7 @@ namespace BMA.EHR.Application.Repositories.Leaves.TimeAttendants // ทำงานที่ต้องการที่นี่ (เช่น เรียก API, ประมวลผลข้อมูล ฯลฯ) await ProcessTaskAsync(job.RootDnaId,job.StartDate, job.EndDate); + await ProcessEmpTaskAsync(job.RootDnaId,job.StartDate, job.EndDate); // อัปเดตสถานะเป็น Completed await UpdateToCompletedAsync(job.Id); From a50153f32cadc6b0a3dca7ab423e391d62ee81db Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Tue, 31 Mar 2026 11:26:28 +0700 Subject: [PATCH 152/178] Refactor LeaveProcessJobStatusRepository to enhance leave remark generation logic and update file export functionality with environment path handling --- .../LeaveProcessJobStatusRepository.cs | 255 ++++++++++++------ 1 file changed, 170 insertions(+), 85 deletions(-) diff --git a/BMA.EHR.Application/Repositories/Leaves/TimeAttendants/LeaveProcessJobStatusRepository.cs b/BMA.EHR.Application/Repositories/Leaves/TimeAttendants/LeaveProcessJobStatusRepository.cs index 4a2be392..3962b53e 100644 --- a/BMA.EHR.Application/Repositories/Leaves/TimeAttendants/LeaveProcessJobStatusRepository.cs +++ b/BMA.EHR.Application/Repositories/Leaves/TimeAttendants/LeaveProcessJobStatusRepository.cs @@ -10,6 +10,7 @@ using BMA.EHR.Application.Repositories.MetaData; using BMA.EHR.Application.Responses.Profiles; using BMA.EHR.Domain.Extensions; using BMA.EHR.Domain.Models.Leave.TimeAttendants; +using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; @@ -29,6 +30,7 @@ namespace BMA.EHR.Application.Repositories.Leaves.TimeAttendants private readonly ProcessUserTimeStampRepository _processUserTimeStampRepository; private readonly LeaveRequestRepository _leaveRequestRepository; private readonly IConfiguration _configuration; + private readonly IWebHostEnvironment _env; #endregion @@ -42,7 +44,8 @@ namespace BMA.EHR.Application.Repositories.Leaves.TimeAttendants UserDutyTimeRepository userDutyTimeRepository, ProcessUserTimeStampRepository processUserTimeStampRepository, LeaveRequestRepository leaveRequestRepository, - IConfiguration configuration) : base(dbContext, httpContextAccessor) + IConfiguration configuration, + IWebHostEnvironment env) : base(dbContext, httpContextAccessor) { _dbContext = dbContext; _httpContextAccessor = httpContextAccessor; @@ -53,6 +56,7 @@ namespace BMA.EHR.Application.Repositories.Leaves.TimeAttendants _dutyTimeRepository = dutyTimeRepository; _userDutyTimeRepository = userDutyTimeRepository; _processUserTimeStampRepository = processUserTimeStampRepository; + _env = env; } #endregion @@ -255,34 +259,75 @@ namespace BMA.EHR.Application.Repositories.Leaves.TimeAttendants case "LV-005": remarkStr += leaveReq.Type.Name; var leaveRange = leaveReq.LeaveRange == null ? "" : leaveReq.LeaveRange.ToUpper(); - if (leaveRange == "MORNING") - remarkStr += "ครึ่งวันเช้า"; - else if (leaveRange == "AFTERNOON") - remarkStr += "ครึ่งวันบ่าย"; - - - // var leaveRangeEnd = leaveReq.LeaveRangeEnd == null ? "" : leaveReq.LeaveRangeEnd.ToUpper(); - // if (leaveRangeEnd == "MORNING") - // remarkStr += "ครึ่งวันเช้า"; - // else if (leaveRangeEnd == "AFTERNOON") - // remarkStr += "ครึ่งวันบ่าย"; - - var leaveRangeEnd = leaveReq.LeaveRangeEnd == null ? "" : leaveReq.LeaveRangeEnd.ToUpper(); - if (leaveRange != leaveRangeEnd) + + if(leaveReq.LeaveStartDate.Date == leaveReq.LeaveEndDate.Date) { - if (leaveRangeEnd == "MORNING") - remarkStr += " - ครึ่งวันเช้า"; - else if (leaveRangeEnd == "AFTERNOON") - remarkStr += " - ครึ่งวันบ่าย"; + if (leaveRange == "MORNING") + remarkStr += "ครึ่งวันเช้า"; + else if (leaveRange == "AFTERNOON") + remarkStr += "ครึ่งวันบ่าย"; + + + // var leaveRangeEnd = leaveReq.LeaveRangeEnd == null ? "" : leaveReq.LeaveRangeEnd.ToUpper(); + // if (leaveRangeEnd == "MORNING") + // remarkStr += "ครึ่งวันเช้า"; + // else if (leaveRangeEnd == "AFTERNOON") + // remarkStr += "ครึ่งวันบ่าย"; + + var leaveRangeEnd = leaveReq.LeaveRangeEnd == null ? "" : leaveReq.LeaveRangeEnd.ToUpper(); + if (leaveRange != leaveRangeEnd) + { + if (leaveRangeEnd == "MORNING") + remarkStr += " - ครึ่งวันเช้า"; + else if (leaveRangeEnd == "AFTERNOON") + remarkStr += " - ครึ่งวันบ่าย"; + } } + else + { + if(dd.date == leaveReq.LeaveStartDate.Date) + { + if (leaveRange == "MORNING") + remarkStr += "ครึ่งวันเช้า"; + else if (leaveRange == "AFTERNOON") + remarkStr += "ครึ่งวันบ่าย"; + } + else if(dd.date == leaveReq.LeaveEndDate.Date) + { + var leaveRangeEnd = leaveReq.LeaveRangeEnd == null ? "" : leaveReq.LeaveRangeEnd.ToUpper(); + if (leaveRangeEnd == "MORNING") + remarkStr += "ครึ่งวันเช้า"; + else if (leaveRangeEnd == "AFTERNOON") + remarkStr += "ครึ่งวันบ่าย"; + else + remarkStr += "เต็มวัน"; + } + else + { + remarkStr += "เต็มวัน"; + } + } + + break; default: remarkStr += leaveReq.Type.Name; break; } status = "LEAVE"; - stampType = leaveReq.LeaveRange ?? ""; - stampAmount = leaveReq.LeaveRange == "MORNING" || leaveReq.LeaveRangeEnd == "MORNING" ? 0.5 : 1; + if(leaveReq.LeaveStartDate.Date == dd.date) + { + stampType = leaveReq.LeaveRange ?? ""; + stampAmount = leaveReq.LeaveRange != "ALL" ? 0.5 : 1; + } + else if(leaveReq.LeaveEndDate.Date == dd.date) + { + stampAmount = leaveReq.LeaveRangeEnd != "ALL" ? 0.5 : 1; + stampType = leaveReq.LeaveRangeEnd ?? ""; + } + else + stampAmount = leaveReq.LeaveRange != "ALL" || leaveReq.LeaveRangeEnd != "ALL" ? 0.5 : 1; + if(stampType == "ALL") stampType = "FULL_DAY"; } else { @@ -374,25 +419,25 @@ namespace BMA.EHR.Application.Repositories.Leaves.TimeAttendants count++; } - // // Write employees to JSON file - // var fileName = $"employees_{DateTime.Now:yyyyMMdd_HHmmss}.txt"; - // var filePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Exports", fileName); + // Write employees to JSON file + var fileName = $"employees_{DateTime.Now:yyyyMMdd_HHmmss}.txt"; + var filePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Exports", fileName); - // // Ensure directory exists - // var directory = Path.GetDirectoryName(filePath); - // if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory)) - // { - // Directory.CreateDirectory(directory); - // } + // Ensure directory exists + var directory = Path.GetDirectoryName(filePath); + if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory)) + { + Directory.CreateDirectory(directory); + } - // var jsonOptions = new JsonSerializerOptions - // { - // WriteIndented = true, - // Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping - // }; + var jsonOptions = new JsonSerializerOptions + { + WriteIndented = true, + Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping + }; - // var jsonContent = JsonSerializer.Serialize(employees, jsonOptions); - // await File.WriteAllTextAsync(filePath, jsonContent); + var jsonContent = JsonSerializer.Serialize(employees, jsonOptions); + await File.WriteAllTextAsync(filePath, jsonContent); } //call api @@ -499,34 +544,72 @@ namespace BMA.EHR.Application.Repositories.Leaves.TimeAttendants case "LV-005": remarkStr += leaveReq.Type.Name; var leaveRange = leaveReq.LeaveRange == null ? "" : leaveReq.LeaveRange.ToUpper(); - if (leaveRange == "MORNING") - remarkStr += "ครึ่งวันเช้า"; - else if (leaveRange == "AFTERNOON") - remarkStr += "ครึ่งวันบ่าย"; - - - // var leaveRangeEnd = leaveReq.LeaveRangeEnd == null ? "" : leaveReq.LeaveRangeEnd.ToUpper(); - // if (leaveRangeEnd == "MORNING") - // remarkStr += "ครึ่งวันเช้า"; - // else if (leaveRangeEnd == "AFTERNOON") - // remarkStr += "ครึ่งวันบ่าย"; - - var leaveRangeEnd = leaveReq.LeaveRangeEnd == null ? "" : leaveReq.LeaveRangeEnd.ToUpper(); - if (leaveRange != leaveRangeEnd) + + if(leaveReq.LeaveStartDate.Date == leaveReq.LeaveEndDate.Date) { - if (leaveRangeEnd == "MORNING") - remarkStr += " - ครึ่งวันเช้า"; - else if (leaveRangeEnd == "AFTERNOON") - remarkStr += " - ครึ่งวันบ่าย"; + if (leaveRange == "MORNING") + remarkStr += "ครึ่งวันเช้า"; + else if (leaveRange == "AFTERNOON") + remarkStr += "ครึ่งวันบ่าย"; + + + // var leaveRangeEnd = leaveReq.LeaveRangeEnd == null ? "" : leaveReq.LeaveRangeEnd.ToUpper(); + // if (leaveRangeEnd == "MORNING") + // remarkStr += "ครึ่งวันเช้า"; + // else if (leaveRangeEnd == "AFTERNOON") + // remarkStr += "ครึ่งวันบ่าย"; + + var leaveRangeEnd = leaveReq.LeaveRangeEnd == null ? "" : leaveReq.LeaveRangeEnd.ToUpper(); + if (leaveRange != leaveRangeEnd) + { + if (leaveRangeEnd == "MORNING") + remarkStr += " - ครึ่งวันเช้า"; + else if (leaveRangeEnd == "AFTERNOON") + remarkStr += " - ครึ่งวันบ่าย"; + } } + else + { + if(dd.date == leaveReq.LeaveStartDate.Date) + { + if (leaveRange == "MORNING") + remarkStr += "ครึ่งวันเช้า"; + else if (leaveRange == "AFTERNOON") + remarkStr += "ครึ่งวันบ่าย"; + } + else if(dd.date == leaveReq.LeaveEndDate.Date) + { + var leaveRangeEnd = leaveReq.LeaveRangeEnd == null ? "" : leaveReq.LeaveRangeEnd.ToUpper(); + if (leaveRangeEnd == "MORNING") + remarkStr += "ครึ่งวันเช้า"; + else if (leaveRangeEnd == "AFTERNOON") + remarkStr += "ครึ่งวันบ่าย"; + } + else + { + remarkStr += "เต็มวัน"; + } + } break; default: remarkStr += leaveReq.Type.Name; break; } status = "LEAVE"; - stampType = leaveReq.LeaveRange ?? ""; - stampAmount = leaveReq.LeaveRange == "MORNING" || leaveReq.LeaveRangeEnd == "MORNING" ? 0.5 : 1; + if(leaveReq.LeaveStartDate.Date == dd.date) + { + stampType = leaveReq.LeaveRange ?? ""; + stampAmount = leaveReq.LeaveRange != "ALL" ? 0.5 : 1; + } + else if(leaveReq.LeaveEndDate.Date == dd.date) + { + stampAmount = leaveReq.LeaveRangeEnd != "ALL" ? 0.5 : 1; + stampType = leaveReq.LeaveRangeEnd ?? ""; + } + else + stampAmount = leaveReq.LeaveRange != "ALL" || leaveReq.LeaveRangeEnd != "ALL" ? 0.5 : 1; + if(stampType == "ALL") stampType = "FULL_DAY"; + //stampAmount = leaveReq.LeaveRange != "ALL" || leaveReq.LeaveRangeEnd != "ALL" ? 0.5 : 1; } else { @@ -618,40 +701,42 @@ namespace BMA.EHR.Application.Repositories.Leaves.TimeAttendants count++; } - // // Write employees to JSON file - // var fileName = $"employees_{DateTime.Now:yyyyMMdd_HHmmss}.txt"; - // var filePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Exports", fileName); + // Write employees to JSON file + var fileName = $"employees_{DateTime.Now:yyyyMMdd_HHmmss}.txt"; + var filePath = Path.Combine(_env.ContentRootPath, "Exports", fileName); - // // Ensure directory exists - // var directory = Path.GetDirectoryName(filePath); - // if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory)) - // { - // Directory.CreateDirectory(directory); - // } + // Ensure directory exists + var directory = Path.GetDirectoryName(filePath); + if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory)) + { + Directory.CreateDirectory(directory); + } - // var jsonOptions = new JsonSerializerOptions - // { - // WriteIndented = true, - // Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping - // }; + var jsonOptions = new JsonSerializerOptions + { + WriteIndented = true, + Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping + }; - // var jsonContent = JsonSerializer.Serialize(employees, jsonOptions); - // await File.WriteAllTextAsync(filePath, jsonContent); + var jsonContent = JsonSerializer.Serialize(employees, jsonOptions); + Console.WriteLine($"Writing file to: {filePath}"); + await File.WriteAllTextAsync(filePath, jsonContent); + Console.WriteLine($"File written successfully: {fileName}"); } //call api - var apiPath = $"{_configuration["API"]}/org/profile-employee/absent-late/batch"; - var apiKey = _configuration["API_KEY"]; - var body = new - { - records = employees - }; + // var apiPath = $"{_configuration["API"]}/org/profile-employee/absent-late/batch"; + // var apiKey = _configuration["API_KEY"]; + // var body = new + // { + // records = employees + // }; - var apiResult = await PostExternalAPIAsync(apiPath, AccessToken ?? "", body, apiKey); - if(apiResult == "") - { - throw new Exception($"เรียก API {apiPath} ไม่สำเร็จ"); - } + // var apiResult = await PostExternalAPIAsync(apiPath, AccessToken ?? "", body, apiKey); + // if(apiResult == "") + // { + // throw new Exception($"เรียก API {apiPath} ไม่สำเร็จ"); + // } } @@ -669,7 +754,7 @@ namespace BMA.EHR.Application.Repositories.Leaves.TimeAttendants // ทำงานที่ต้องการที่นี่ (เช่น เรียก API, ประมวลผลข้อมูล ฯลฯ) await ProcessTaskAsync(job.RootDnaId,job.StartDate, job.EndDate); - await ProcessEmpTaskAsync(job.RootDnaId,job.StartDate, job.EndDate); + //await ProcessEmpTaskAsync(job.RootDnaId,job.StartDate, job.EndDate); // อัปเดตสถานะเป็น Completed await UpdateToCompletedAsync(job.Id); From 932d5e75c795c1eef9bbf165dfbb1e51cfd997f8 Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Tue, 31 Mar 2026 11:28:12 +0700 Subject: [PATCH 153/178] Refactor LeaveProcessJobStatusRepository to update API endpoint paths and comment out JSON file writing logic --- .../LeaveProcessJobStatusRepository.cs | 90 +++++++++---------- 1 file changed, 45 insertions(+), 45 deletions(-) diff --git a/BMA.EHR.Application/Repositories/Leaves/TimeAttendants/LeaveProcessJobStatusRepository.cs b/BMA.EHR.Application/Repositories/Leaves/TimeAttendants/LeaveProcessJobStatusRepository.cs index 3962b53e..1d45e49c 100644 --- a/BMA.EHR.Application/Repositories/Leaves/TimeAttendants/LeaveProcessJobStatusRepository.cs +++ b/BMA.EHR.Application/Repositories/Leaves/TimeAttendants/LeaveProcessJobStatusRepository.cs @@ -420,28 +420,28 @@ namespace BMA.EHR.Application.Repositories.Leaves.TimeAttendants } // Write employees to JSON file - var fileName = $"employees_{DateTime.Now:yyyyMMdd_HHmmss}.txt"; - var filePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Exports", fileName); + // var fileName = $"employees_{DateTime.Now:yyyyMMdd_HHmmss}.txt"; + // var filePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Exports", fileName); - // Ensure directory exists - var directory = Path.GetDirectoryName(filePath); - if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory)) - { - Directory.CreateDirectory(directory); - } + // // Ensure directory exists + // var directory = Path.GetDirectoryName(filePath); + // if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory)) + // { + // Directory.CreateDirectory(directory); + // } - var jsonOptions = new JsonSerializerOptions - { - WriteIndented = true, - Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping - }; + // var jsonOptions = new JsonSerializerOptions + // { + // WriteIndented = true, + // Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping + // }; - var jsonContent = JsonSerializer.Serialize(employees, jsonOptions); - await File.WriteAllTextAsync(filePath, jsonContent); + // var jsonContent = JsonSerializer.Serialize(employees, jsonOptions); + // await File.WriteAllTextAsync(filePath, jsonContent); } //call api - var apiPath = $"{_configuration["API"]}/org/profile/absent-late/batch"; + var apiPath = $"{_configuration["API"]}/org/unauthorize/profile/absent-late/batch"; var apiKey = _configuration["API_KEY"]; var body = new { @@ -702,41 +702,41 @@ namespace BMA.EHR.Application.Repositories.Leaves.TimeAttendants } // Write employees to JSON file - var fileName = $"employees_{DateTime.Now:yyyyMMdd_HHmmss}.txt"; - var filePath = Path.Combine(_env.ContentRootPath, "Exports", fileName); + // var fileName = $"employees_{DateTime.Now:yyyyMMdd_HHmmss}.txt"; + // var filePath = Path.Combine(_env.ContentRootPath, "Exports", fileName); - // Ensure directory exists - var directory = Path.GetDirectoryName(filePath); - if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory)) - { - Directory.CreateDirectory(directory); - } + // // Ensure directory exists + // var directory = Path.GetDirectoryName(filePath); + // if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory)) + // { + // Directory.CreateDirectory(directory); + // } - var jsonOptions = new JsonSerializerOptions - { - WriteIndented = true, - Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping - }; + // var jsonOptions = new JsonSerializerOptions + // { + // WriteIndented = true, + // Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping + // }; - var jsonContent = JsonSerializer.Serialize(employees, jsonOptions); - Console.WriteLine($"Writing file to: {filePath}"); - await File.WriteAllTextAsync(filePath, jsonContent); - Console.WriteLine($"File written successfully: {fileName}"); + // var jsonContent = JsonSerializer.Serialize(employees, jsonOptions); + // Console.WriteLine($"Writing file to: {filePath}"); + // await File.WriteAllTextAsync(filePath, jsonContent); + // Console.WriteLine($"File written successfully: {fileName}"); } - //call api - // var apiPath = $"{_configuration["API"]}/org/profile-employee/absent-late/batch"; - // var apiKey = _configuration["API_KEY"]; - // var body = new - // { - // records = employees - // }; + // call api + var apiPath = $"{_configuration["API"]}/org/unauthorize/profile-employee/absent-late/batch"; + var apiKey = _configuration["API_KEY"]; + var body = new + { + records = employees + }; - // var apiResult = await PostExternalAPIAsync(apiPath, AccessToken ?? "", body, apiKey); - // if(apiResult == "") - // { - // throw new Exception($"เรียก API {apiPath} ไม่สำเร็จ"); - // } + var apiResult = await PostExternalAPIAsync(apiPath, AccessToken ?? "", body, apiKey); + if(apiResult == "") + { + throw new Exception($"เรียก API {apiPath} ไม่สำเร็จ"); + } } From 47c0cfc62a9af2db2b022b14e62f128609f55875 Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Tue, 31 Mar 2026 11:32:17 +0700 Subject: [PATCH 154/178] Refactor leave remark generation logic in LeaveReportController to handle single and multi-day leave requests more accurately --- .../Controllers/LeaveReportController.cs | 146 ++++++++++++++---- 1 file changed, 120 insertions(+), 26 deletions(-) diff --git a/BMA.EHR.Leave/Controllers/LeaveReportController.cs b/BMA.EHR.Leave/Controllers/LeaveReportController.cs index e96fc0a0..a0d0d4b1 100644 --- a/BMA.EHR.Leave/Controllers/LeaveReportController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveReportController.cs @@ -2091,20 +2091,68 @@ namespace BMA.EHR.Leave.Service.Controllers case "LV-005": remarkStr += leaveReq.Type.Name; var leaveRange = leaveReq.LeaveRange == null ? "" : leaveReq.LeaveRange.ToUpper(); - if (leaveRange == "MORNING") - remarkStr += "ครึ่งวันเช้า"; - else if (leaveRange == "AFTERNOON") - remarkStr += "ครึ่งวันบ่าย"; - - var leaveRangeEnd = leaveReq.LeaveRangeEnd == null ? "" : leaveReq.LeaveRangeEnd.ToUpper(); - if (leaveRange != leaveRangeEnd) + if(leaveReq.LeaveStartDate.Date == leaveReq.LeaveEndDate.Date) { - if (leaveRangeEnd == "MORNING") - remarkStr += " - ครึ่งวันเช้า"; - else if (leaveRangeEnd == "AFTERNOON") - remarkStr += " - ครึ่งวันบ่าย"; + if (leaveRange == "MORNING") + remarkStr += "ครึ่งวันเช้า"; + else if (leaveRange == "AFTERNOON") + remarkStr += "ครึ่งวันบ่าย"; + + + // var leaveRangeEnd = leaveReq.LeaveRangeEnd == null ? "" : leaveReq.LeaveRangeEnd.ToUpper(); + // if (leaveRangeEnd == "MORNING") + // remarkStr += "ครึ่งวันเช้า"; + // else if (leaveRangeEnd == "AFTERNOON") + // remarkStr += "ครึ่งวันบ่าย"; + + var leaveRangeEnd = leaveReq.LeaveRangeEnd == null ? "" : leaveReq.LeaveRangeEnd.ToUpper(); + if (leaveRange != leaveRangeEnd) + { + if (leaveRangeEnd == "MORNING") + remarkStr += " - ครึ่งวันเช้า"; + else if (leaveRangeEnd == "AFTERNOON") + remarkStr += " - ครึ่งวันบ่าย"; + } } + else + { + if(dd.date == leaveReq.LeaveStartDate.Date) + { + if (leaveRange == "MORNING") + remarkStr += "ครึ่งวันเช้า"; + else if (leaveRange == "AFTERNOON") + remarkStr += "ครึ่งวันบ่าย"; + } + else if(dd.date == leaveReq.LeaveEndDate.Date) + { + var leaveRangeEnd = leaveReq.LeaveRangeEnd == null ? "" : leaveReq.LeaveRangeEnd.ToUpper(); + if (leaveRangeEnd == "MORNING") + remarkStr += "ครึ่งวันเช้า"; + else if (leaveRangeEnd == "AFTERNOON") + remarkStr += "ครึ่งวันบ่าย"; + } + else + { + remarkStr += "เต็มวัน"; + } + } + + + // if (leaveRange == "MORNING") + // remarkStr += "ครึ่งวันเช้า"; + // else if (leaveRange == "AFTERNOON") + // remarkStr += "ครึ่งวันบ่าย"; + + + // var leaveRangeEnd = leaveReq.LeaveRangeEnd == null ? "" : leaveReq.LeaveRangeEnd.ToUpper(); + // if (leaveRange != leaveRangeEnd) + // { + // if (leaveRangeEnd == "MORNING") + // remarkStr += " - ครึ่งวันเช้า"; + // else if (leaveRangeEnd == "AFTERNOON") + // remarkStr += " - ครึ่งวันบ่าย"; + // } break; default: remarkStr += leaveReq.Type.Name; @@ -2421,26 +2469,72 @@ namespace BMA.EHR.Leave.Service.Controllers case "LV-005": remarkStr += leaveReq.Type.Name; var leaveRange = leaveReq.LeaveRange == null ? "" : leaveReq.LeaveRange.ToUpper(); - if (leaveRange == "MORNING") - remarkStr += "ครึ่งวันเช้า"; - else if (leaveRange == "AFTERNOON") - remarkStr += "ครึ่งวันบ่าย"; + if(leaveReq.LeaveStartDate.Date == leaveReq.LeaveEndDate.Date) + { + if (leaveRange == "MORNING") + remarkStr += "ครึ่งวันเช้า"; + else if (leaveRange == "AFTERNOON") + remarkStr += "ครึ่งวันบ่าย"; - // var leaveRangeEnd = leaveReq.LeaveRangeEnd == null ? "" : leaveReq.LeaveRangeEnd.ToUpper(); - // if (leaveRangeEnd == "MORNING") + // var leaveRangeEnd = leaveReq.LeaveRangeEnd == null ? "" : leaveReq.LeaveRangeEnd.ToUpper(); + // if (leaveRangeEnd == "MORNING") + // remarkStr += "ครึ่งวันเช้า"; + // else if (leaveRangeEnd == "AFTERNOON") + // remarkStr += "ครึ่งวันบ่าย"; + + var leaveRangeEnd = leaveReq.LeaveRangeEnd == null ? "" : leaveReq.LeaveRangeEnd.ToUpper(); + if (leaveRange != leaveRangeEnd) + { + if (leaveRangeEnd == "MORNING") + remarkStr += " - ครึ่งวันเช้า"; + else if (leaveRangeEnd == "AFTERNOON") + remarkStr += " - ครึ่งวันบ่าย"; + } + } + else + { + if(dd.date == leaveReq.LeaveStartDate.Date) + { + if (leaveRange == "MORNING") + remarkStr += "ครึ่งวันเช้า"; + else if (leaveRange == "AFTERNOON") + remarkStr += "ครึ่งวันบ่าย"; + } + else if(dd.date == leaveReq.LeaveEndDate.Date) + { + var leaveRangeEnd = leaveReq.LeaveRangeEnd == null ? "" : leaveReq.LeaveRangeEnd.ToUpper(); + if (leaveRangeEnd == "MORNING") + remarkStr += "ครึ่งวันเช้า"; + else if (leaveRangeEnd == "AFTERNOON") + remarkStr += "ครึ่งวันบ่าย"; + } + else + { + remarkStr += "เต็มวัน"; + } + } + + // if (leaveRange == "MORNING") // remarkStr += "ครึ่งวันเช้า"; - // else if (leaveRangeEnd == "AFTERNOON") + // else if (leaveRange == "AFTERNOON") // remarkStr += "ครึ่งวันบ่าย"; - var leaveRangeEnd = leaveReq.LeaveRangeEnd == null ? "" : leaveReq.LeaveRangeEnd.ToUpper(); - if (leaveRange != leaveRangeEnd) - { - if (leaveRangeEnd == "MORNING") - remarkStr += " - ครึ่งวันเช้า"; - else if (leaveRangeEnd == "AFTERNOON") - remarkStr += " - ครึ่งวันบ่าย"; - } + + // // var leaveRangeEnd = leaveReq.LeaveRangeEnd == null ? "" : leaveReq.LeaveRangeEnd.ToUpper(); + // // if (leaveRangeEnd == "MORNING") + // // remarkStr += "ครึ่งวันเช้า"; + // // else if (leaveRangeEnd == "AFTERNOON") + // // remarkStr += "ครึ่งวันบ่าย"; + + // var leaveRangeEnd = leaveReq.LeaveRangeEnd == null ? "" : leaveReq.LeaveRangeEnd.ToUpper(); + // if (leaveRange != leaveRangeEnd) + // { + // if (leaveRangeEnd == "MORNING") + // remarkStr += " - ครึ่งวันเช้า"; + // else if (leaveRangeEnd == "AFTERNOON") + // remarkStr += " - ครึ่งวันบ่าย"; + // } break; default: remarkStr += leaveReq.Type.Name; From 8fa105606bb41862f1c378a4d5fec51fe98c3271 Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Tue, 31 Mar 2026 11:45:48 +0700 Subject: [PATCH 155/178] Refactor LeaveProcessJobStatusRepository to filter employee records by status and ensure proper task processing --- .../TimeAttendants/LeaveProcessJobStatusRepository.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/BMA.EHR.Application/Repositories/Leaves/TimeAttendants/LeaveProcessJobStatusRepository.cs b/BMA.EHR.Application/Repositories/Leaves/TimeAttendants/LeaveProcessJobStatusRepository.cs index 1d45e49c..5e9944ad 100644 --- a/BMA.EHR.Application/Repositories/Leaves/TimeAttendants/LeaveProcessJobStatusRepository.cs +++ b/BMA.EHR.Application/Repositories/Leaves/TimeAttendants/LeaveProcessJobStatusRepository.cs @@ -445,7 +445,7 @@ namespace BMA.EHR.Application.Repositories.Leaves.TimeAttendants var apiKey = _configuration["API_KEY"]; var body = new { - records = employees + records = employees.Where(x => x.status == "ABSENT" || x.status == "LATE").ToList() }; var apiResult = await PostExternalAPIAsync(apiPath, AccessToken ?? "", body, apiKey); @@ -729,7 +729,7 @@ namespace BMA.EHR.Application.Repositories.Leaves.TimeAttendants var apiKey = _configuration["API_KEY"]; var body = new { - records = employees + records = employees.Where(x => x.status == "ABSENT" || x.status == "LATE").ToList() }; var apiResult = await PostExternalAPIAsync(apiPath, AccessToken ?? "", body, apiKey); @@ -754,7 +754,7 @@ namespace BMA.EHR.Application.Repositories.Leaves.TimeAttendants // ทำงานที่ต้องการที่นี่ (เช่น เรียก API, ประมวลผลข้อมูล ฯลฯ) await ProcessTaskAsync(job.RootDnaId,job.StartDate, job.EndDate); - //await ProcessEmpTaskAsync(job.RootDnaId,job.StartDate, job.EndDate); + await ProcessEmpTaskAsync(job.RootDnaId,job.StartDate, job.EndDate); // อัปเดตสถานะเป็น Completed await UpdateToCompletedAsync(job.Id); From bf6ea555fcfd846102b02d19d7c5d63f53f7b4e3 Mon Sep 17 00:00:00 2001 From: harid Date: Tue, 31 Mar 2026 14:23:51 +0700 Subject: [PATCH 156/178] =?UTF-8?q?fix=20=E0=B8=A3=E0=B8=B2=E0=B8=A2?= =?UTF-8?q?=E0=B8=87=E0=B8=B2=E0=B8=99=E0=B8=A1=E0=B8=B2=E0=B8=AA=E0=B8=B2?= =?UTF-8?q?=E0=B8=A2=20=E0=B8=82=E0=B9=89=E0=B8=AD=E0=B8=A1=E0=B8=B9?= =?UTF-8?q?=E0=B8=A5=E0=B9=84=E0=B8=A1=E0=B9=88=E0=B9=81=E0=B8=AA=E0=B8=94?= =?UTF-8?q?=E0=B8=87=20#2395?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../TimeAttendants/ProcessUserTimeStampRepository.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/BMA.EHR.Application/Repositories/Leaves/TimeAttendants/ProcessUserTimeStampRepository.cs b/BMA.EHR.Application/Repositories/Leaves/TimeAttendants/ProcessUserTimeStampRepository.cs index e1a25780..77aba421 100644 --- a/BMA.EHR.Application/Repositories/Leaves/TimeAttendants/ProcessUserTimeStampRepository.cs +++ b/BMA.EHR.Application/Repositories/Leaves/TimeAttendants/ProcessUserTimeStampRepository.cs @@ -191,11 +191,11 @@ namespace BMA.EHR.Application.Repositories.Leaves.TimeAttendants if (role == "ROOT" || role == "OWNER" || role == "CHILD" || role == "BROTHER" || role == "PARENT") { data = data.Where(x => - nodeByReq == 4 ? x.Child4Id == Guid.Parse(nodeIdByReq) : - nodeByReq == 3 ? x.Child3Id == Guid.Parse(nodeIdByReq) : - nodeByReq == 2 ? x.Child2Id == Guid.Parse(nodeIdByReq) : - nodeByReq == 1 ? x.Child1Id == Guid.Parse(nodeIdByReq) : - nodeByReq == 0 ? x.RootId == Guid.Parse(nodeIdByReq) : true + nodeByReq == 4 ? x.Child4DnaId == Guid.Parse(nodeIdByReq) : + nodeByReq == 3 ? x.Child3DnaId == Guid.Parse(nodeIdByReq) : + nodeByReq == 2 ? x.Child2DnaId == Guid.Parse(nodeIdByReq) : + nodeByReq == 1 ? x.Child1DnaId == Guid.Parse(nodeIdByReq) : + nodeByReq == 0 ? x.RootDnaId == Guid.Parse(nodeIdByReq) : true ).ToList(); } return data; From 1cf780ecd0d9c47edf4f5cfd52e8f0c3ee0679db Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Wed, 1 Apr 2026 12:24:00 +0700 Subject: [PATCH 157/178] Refactor LeaveController to streamline profile retrieval and duty time handling --- BMA.EHR.Leave/Controllers/LeaveController.cs | 9 ++++---- BMA.EHR.Leave/appsettings.json | 23 +++++++++++++------- 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/BMA.EHR.Leave/Controllers/LeaveController.cs b/BMA.EHR.Leave/Controllers/LeaveController.cs index 799f3558..0a423d60 100644 --- a/BMA.EHR.Leave/Controllers/LeaveController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveController.cs @@ -3277,16 +3277,15 @@ namespace BMA.EHR.Leave.Service.Controllers foreach (var data in rawDataPaged) { var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(data.KeycloakUserId, AccessToken); - if (profile == null) + UserDutyTime? effectiveDate = null; + if (profile != null) { - return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound); + effectiveDate = await _userDutyTimeRepository.GetLastEffectRound(profile.Id); + //return Error($"{data.Id} PF{data.FirstName} {data.LastName} : {GlobalMessages.DataNotFound}", StatusCodes.Status404NotFound); } //var userRound = await _dutyTimeRepository.GetByIdAsync(profile.DutyTimeId ?? Guid.Empty); - - var effectiveDate = await _userDutyTimeRepository.GetLastEffectRound(profile.Id); var roundId = effectiveDate != null ? effectiveDate.DutyTimeId : Guid.Empty; var userRound = await _dutyTimeRepository.GetByIdAsync(roundId); - var checkInData = await _userTimeStampRepository.GetTimestampByDateAsync(data.KeycloakUserId, data.CheckDate); var duty = userRound ?? getDefaultRound; diff --git a/BMA.EHR.Leave/appsettings.json b/BMA.EHR.Leave/appsettings.json index e55f7ac0..ee356491 100644 --- a/BMA.EHR.Leave/appsettings.json +++ b/BMA.EHR.Leave/appsettings.json @@ -23,16 +23,15 @@ "ExamConnection": "Server=192.168.1.63;User ID=root;Password=12345678;Port=3306;Database=hrms_exam;Allow User Variables=True;Convert Zero Datetime=True;Pooling=True;", "LeaveConnection": "Server=192.168.1.63;User ID=root;Password=12345678;Port=3306;Database=hrms_leave;Allow User Variables=True;Convert Zero Datetime=True;Pooling=True;" - //"DefaultConnection": "server=172.27.17.68;user=user;password=cDldaqkwESWvuZ37Gr0n;port=3306;database=hrms;Convert Zero Datetime=True;Allow User Variables=true;Pooling=True;", - //"ExamConnection": "server=172.27.17.68;user=user;password=cDldaqkwESWvuZ37Gr0n;port=3306;database=hrms_exam;Convert Zero Datetime=True;Allow User Variables=true;Pooling=True;", - //"LeaveConnection": "server=172.27.17.68;user=user;password=cDldaqkwESWvuZ37Gr0n;port=3306;database=hrms_leave;Convert Zero Datetime=True;Allow User Variables=true;Pooling=True;" + // "DefaultConnection": "server=172.27.17.68;user=root;password=ey2qVVyyqGYw8CyA7h8X72559r2Ad84K;port=3306;database=hrms;Convert Zero Datetime=True;Allow User Variables=true;Pooling=True;Connection Timeout=180;", + // "ExamConnection": "server=172.27.17.68;user=root;password=ey2qVVyyqGYw8CyA7h8X72559r2Ad84K;port=3306;database=hrms_exam;Convert Zero Datetime=True;Allow User Variables=true;Pooling=True;Connection Timeout=180;", + // "LeaveConnection": "server=172.27.17.68;user=root;password=ey2qVVyyqGYw8CyA7h8X72559r2Ad84K;port=3306;database=hrms_leave;Convert Zero Datetime=True;Allow User Variables=true;Pooling=True;Connection Timeout=180;" }, "Jwt": { - //"Key": "HP-FnQMUj9msHMSD3T9HtdEnphAKoCJLEl85CIqROFI", "Key": "j7C9RO_p4nRtuwCH4z9Db_A_6We42tkD_p4lZtDrezc", "Issuer": "https://hrmsbkk-id.case-collection.com/realms/hrms" - //"Key": "xY2VR-EFvvNPsMs39u8ooVBWQL6mPwrNJOh3koJFTgU", - //"Issuer": "https://hrms-id.bangkok.go.th/realms/hrms" + // "Key": "xY2VR-EFvvNPsMs39u8ooVBWQL6mPwrNJOh3koJFTgU", + // "Issuer": "https://hrms-id.bangkok.go.th/realms/hrms" }, "EPPlus": { "ExcelPackage": { @@ -55,6 +54,11 @@ "Password": "12345678", "Queue": "hrms-checkin-queue-dev", "URL": "http://192.168.1.63:9122/api/queues/%2F/" + // "Host": "172.27.17.68", + // "User": "admin", + // "Password": "admin123456", + // "Queue": "hrms-checkin-queue", + // "URL": "http://172.27.17.68:9122/api/queues/%2F/" }, "Mail": { "Server": "mail.bangkok.go.th", @@ -68,7 +72,10 @@ "API": "https://hrmsbkk.case-collection.com/api/v1", "APIV2": "https://hrmsbkk.case-collection.com/api/v2", "VITE_URL_MGT": "https://hrmsbkk-mgt.case-collection.com", - //"API": "https://bma-ehr.frappet.synology.me/api/v1", - //"API": "https://bma-hrms.bangkok.go.th/api/v1", + // "Domain": "https://hrms-exam.bangkok.go.th", + // "APIPROBATION": "https://hrms.bangkok.go.th/api/v1/probation", + // "API": "https://hrms.bangkok.go.th/api/v1", + // "APIV2": "https://hrms.bangkok.go.th/api/v2", + // "VITE_URL_MGT": "https://hrms-mgt.bangkok.go.th", "API_KEY": "fKRL16yyEgbyTEJdsMw2h64tGSCmkW685PRtM3CygzX1JOSdptT9UJtpgWwKM8FybRTJups3GTFwj27ZRvlPdIkv3XgCoVJaD5LmR06ozuEPvCCRSdp2WFthg08V5xHc56fTPfZLpr1VmXrhd6dvYhHIqKkQUJR02Rlkss11cLRWEQOssEFVA4xdu2J5DIRO1EM5m7wRRvEwcDB4mYRXD9HH52SMq6iYqUWEWsMwLdbk7QW9yYESUEuzMW5gWrb6vIeWZxJV5bTz1PcWUyR7eO9Fyw1F5DiQYc9JgzTC1mW7cv31fEtTtrfbJYKIb5EbWilqIEUKC6A0UKBDDek35ML0006cqRVm0pvdOH6jeq7VQyYrhdXe59dBEyhYGUIfozoVBvW7Up4QBuOMjyPjSqJPlMBKwaseptfrblxQV1AOOivSBpf1ZcQyOZ8JktRtKUDSuXsmG0lsXwFlI3JCeSHdpVdgZWFYcJPegqfrB6KotR02t9AVkpLs1ZWrixwz" } From 8ea572d46ce7c42a74b98b3fc4c8462bdc1969e2 Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Wed, 1 Apr 2026 12:30:42 +0700 Subject: [PATCH 158/178] Refactor LeaveReportController to improve duty time retrieval and handle default round logic --- .../Controllers/LeaveReportController.cs | 37 ++++++++++++++----- 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/BMA.EHR.Leave/Controllers/LeaveReportController.cs b/BMA.EHR.Leave/Controllers/LeaveReportController.cs index a0d0d4b1..9c91f891 100644 --- a/BMA.EHR.Leave/Controllers/LeaveReportController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveReportController.cs @@ -7,6 +7,7 @@ using BMA.EHR.Application.Responses.Profiles; using BMA.EHR.Domain.Common; using BMA.EHR.Domain.Extensions; using BMA.EHR.Domain.Models.Leave.Requests; +using BMA.EHR.Domain.Models.Leave.TimeAttendants; using BMA.EHR.Domain.Shared; using BMA.EHR.Leave.Service.DTOs.Reports; using Microsoft.AspNetCore.Authorization; @@ -2855,25 +2856,41 @@ namespace BMA.EHR.Leave.Service.Controllers { nodeId = profileAdmin?.RootDnaId; } + var getDefaultRound = await _dutyTimeRepository.GetDefaultAsync(); + if (getDefaultRound == null) + { + return Error("ไม่พบรอบลงเวลา Default", StatusCodes.Status404NotFound); + } var userTimeStamps = await _processUserTimeStampRepository.GetTimestampByDateLateAsync(type.Trim().ToUpper(), role, nodeId, profileAdmin.Node, req.nodeId, req.node, req.StartDate, req.EndDate); foreach (var p in userTimeStamps) { var fullName = $"{p.Prefix}{p.FirstName} {p.LastName}"; - var effectiveDate = await _userDutyTimeRepository.GetLastEffectRound(p.Id); + UserDutyTime? effectiveDate = null; + + effectiveDate = await _userDutyTimeRepository.GetLastEffectRound(p.Id); + //return Error($"{data.Id} PF{data.FirstName} {data.LastName} : {GlobalMessages.DataNotFound}", StatusCodes.Status404NotFound); + + //var userRound = await _dutyTimeRepository.GetByIdAsync(profile.DutyTimeId ?? Guid.Empty); var roundId = effectiveDate != null ? effectiveDate.DutyTimeId : Guid.Empty; var userRound = await _dutyTimeRepository.GetByIdAsync(roundId); - var duty = userRound; - if (duty == null) - { - duty = await _dutyTimeRepository.GetDefaultAsync(); - if (duty == null) - { - return Error("ไม่พบรอบการลงเวลา Default", StatusCodes.Status404NotFound); - } - } + var duty = userRound ?? getDefaultRound; + + // var effectiveDate = await _userDutyTimeRepository.GetLastEffectRound(p.Id); + // var roundId = effectiveDate != null ? effectiveDate.DutyTimeId : Guid.Empty; + // var userRound = await _dutyTimeRepository.GetByIdAsync(roundId); + + // var duty = userRound; + // if (duty == null) + // { + // duty = await _dutyTimeRepository.GetDefaultAsync(); + // if (duty == null) + // { + // return Error("ไม่พบรอบการลงเวลา Default", StatusCodes.Status404NotFound); + // } + // } DateTime? checkIn = p.CheckIn; DateTime? checkOut = p.CheckOut ?? null; var emp = new From ea694bfda2a3b066c00bc40b0958e1ce8caee1e8 Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Thu, 2 Apr 2026 09:51:45 +0700 Subject: [PATCH 159/178] Fix effective date retrieval in LeaveReportController by using ProfileId instead of Id #2400 --- BMA.EHR.Leave/Controllers/LeaveReportController.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BMA.EHR.Leave/Controllers/LeaveReportController.cs b/BMA.EHR.Leave/Controllers/LeaveReportController.cs index 9c91f891..7e4b7b56 100644 --- a/BMA.EHR.Leave/Controllers/LeaveReportController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveReportController.cs @@ -2869,7 +2869,7 @@ namespace BMA.EHR.Leave.Service.Controllers UserDutyTime? effectiveDate = null; - effectiveDate = await _userDutyTimeRepository.GetLastEffectRound(p.Id); + effectiveDate = await _userDutyTimeRepository.GetLastEffectRound(p.ProfileId ?? Guid.Empty); //return Error($"{data.Id} PF{data.FirstName} {data.LastName} : {GlobalMessages.DataNotFound}", StatusCodes.Status404NotFound); //var userRound = await _dutyTimeRepository.GetByIdAsync(profile.DutyTimeId ?? Guid.Empty); From 69b89dfc90d035a7e04004c8297003d03e6271f2 Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Thu, 2 Apr 2026 11:00:14 +0700 Subject: [PATCH 160/178] Add GetOcByNodeId method to UserProfileRepository and update InsigniaManageController to use it #2389 --- .../Repositories/UserProfileRepository.cs | 32 +++++++++++++++++++ .../Controllers/InsigniaManageController.cs | 9 +++++- 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/BMA.EHR.Application/Repositories/UserProfileRepository.cs b/BMA.EHR.Application/Repositories/UserProfileRepository.cs index 4ae8bcbe..4f081f77 100644 --- a/BMA.EHR.Application/Repositories/UserProfileRepository.cs +++ b/BMA.EHR.Application/Repositories/UserProfileRepository.cs @@ -1259,6 +1259,38 @@ namespace BMA.EHR.Application.Repositories } } + public GetOrganizationResponseDTO? GetOcByNodeId(Guid ocId, int level, string? accessToken) + { + try + { + var apiPath = $"{_configuration["API"]}/org/find/all"; + var apiKey = _configuration["API_KEY"]; + var body = new + { + nodeId = ocId, + node = level + + }; + + var apiResult = PostExternalAPIAsync(apiPath, accessToken ?? "", body, apiKey).Result; + if (apiResult != null) + { + var raw = JsonConvert.DeserializeObject(apiResult); + if (raw != null && raw.Result != null) + { + return raw.Result; + } + } + + return null; + } + catch + { + throw; + } + } + + public GetOrganizationResponseDTO? GetOc(Guid ocId, int level, string? accessToken) { try diff --git a/BMA.EHR.Insignia/Controllers/InsigniaManageController.cs b/BMA.EHR.Insignia/Controllers/InsigniaManageController.cs index 7d3c9ae4..6c0b809f 100644 --- a/BMA.EHR.Insignia/Controllers/InsigniaManageController.cs +++ b/BMA.EHR.Insignia/Controllers/InsigniaManageController.cs @@ -331,7 +331,7 @@ namespace BMA.EHR.Insignia.Service.Controllers if (req.Total + total > insigniaManage.Total) return Error(GlobalMessages.InsigniaManageOrgLimit); - var ocData = _userProfileRepository.GetOc(req.OrganizationOrganizationId, 0, AccessToken); + var ocData = _userProfileRepository.GetOcByNodeId(req.OrganizationOrganizationId, 0, AccessToken); var root = ocData?.Root ?? null; var rootDnaId = ocData?.RootDnaId ?? null; await _context.InsigniaManageOrganiations.AddAsync( @@ -407,6 +407,10 @@ namespace BMA.EHR.Insignia.Service.Controllers if (uppdated == null) return Error(GlobalMessages.InsigniaManageNotFound); + var ocData = _userProfileRepository.GetOcByNodeId(uppdated.OrganizationId, 0, AccessToken); + var root = ocData?.Root ?? null; + var rootDnaId = ocData?.RootDnaId ?? null; + var insigniaManage = await _context.InsigniaManages.AsQueryable() .Include(x => x.InsigniaManageOrganiations) .FirstOrDefaultAsync(x => x.Id == uppdated.InsigniaManage.Id); @@ -416,6 +420,9 @@ namespace BMA.EHR.Insignia.Service.Controllers if (req.Total + total > insigniaManage.Total) return Error(GlobalMessages.InsigniaManageOrgLimit); + uppdated.Organization = root; + uppdated.RootDnaId = rootDnaId; + uppdated.Total = req.Total; uppdated.LastUpdateFullName = FullName ?? "System Administrator"; uppdated.LastUpdateUserId = UserId ?? ""; From a4a5d1320352f4994de2cf19fea6c8bd5e107424 Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Thu, 2 Apr 2026 11:09:36 +0700 Subject: [PATCH 161/178] Update InsigniaRequestController to use GetOcByNodeId and include RootDnaId in insigniaNoteProfile #2390 --- BMA.EHR.Insignia/Controllers/InsigniaRequestController.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/BMA.EHR.Insignia/Controllers/InsigniaRequestController.cs b/BMA.EHR.Insignia/Controllers/InsigniaRequestController.cs index 005f9882..81ffe1f4 100644 --- a/BMA.EHR.Insignia/Controllers/InsigniaRequestController.cs +++ b/BMA.EHR.Insignia/Controllers/InsigniaRequestController.cs @@ -3146,7 +3146,10 @@ namespace BMA.EHR.Insignia.Service.Controllers var doc = await _documentService.UploadFileAsync(file, file.FileName); insigniaNoteProfile.DocReceiveInsignia = doc; } - var root = _userProfileRepository.GetOc(req.OrgId, 0, AccessToken)?.Root ?? null; + + var orgData = _userProfileRepository.GetOcByNodeId(req.OrgId,0, AccessToken); + var root = orgData?.Root ?? null; + var rootDnaId = orgData?.RootDnaId ?? null; if (req.OrgId != Guid.Parse("00000000-0000-0000-0000-000000000000")) { if (root == null) @@ -3157,6 +3160,7 @@ namespace BMA.EHR.Insignia.Service.Controllers root = "สำนักนายกรัฐมนตรี"; } insigniaNoteProfile.OrgReceiveInsignia = root; + insigniaNoteProfile.RootDnaId = rootDnaId; insigniaNoteProfile.OrgReceiveInsigniaId = req.OrgId; insigniaNoteProfile.DateReceiveInsignia = req.Date; insigniaNoteProfile.LastUpdateFullName = FullName ?? "System Administrator"; From 6b8eddcbc0a92943221c411ca610d31f29993e28 Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Thu, 2 Apr 2026 11:36:59 +0700 Subject: [PATCH 162/178] Add Insignia launch configuration and improve null checks in InsigniaRequestController #2388 --- .vscode/launch.json | 25 +++++++++++++++++++ .../Controllers/InsigniaRequestController.cs | 2 ++ 2 files changed, 27 insertions(+) diff --git a/.vscode/launch.json b/.vscode/launch.json index 205e817c..6be3e592 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -26,6 +26,31 @@ "/Views": "${workspaceFolder}/Views" } }, + { + // Use IntelliSense to find out which attributes exist for C# debugging + // Use hover for the description of the existing attributes + // For further information visit https://github.com/dotnet/vscode-csharp/blob/main/debugger-launchjson.md. + "name": ".NET Core Launch (web) - Insignia", + "type": "coreclr", + "request": "launch", + "preLaunchTask": "build", + // If you have changed target frameworks, make sure to update the program path. + "program": "${workspaceFolder}/BMA.EHR.Insignia/bin/Debug/net7.0/BMA.EHR.Insignia.dll", + "args": [], + "cwd": "${workspaceFolder}/BMA.EHR.Insignia", + "stopAtEntry": false, + // Enable launching a web browser when ASP.NET Core starts. For more information: https://aka.ms/VSCode-CS-LaunchJson-WebBrowser + "serverReadyAction": { + "action": "openExternally", + "pattern": "\\bNow listening on:\\s+(https?://\\S+)" + }, + "env": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "sourceFileMap": { + "/Views": "${workspaceFolder}/Views" + } + }, { "name": ".NET Core Attach", "type": "coreclr", diff --git a/BMA.EHR.Insignia/Controllers/InsigniaRequestController.cs b/BMA.EHR.Insignia/Controllers/InsigniaRequestController.cs index 81ffe1f4..0aa8a218 100644 --- a/BMA.EHR.Insignia/Controllers/InsigniaRequestController.cs +++ b/BMA.EHR.Insignia/Controllers/InsigniaRequestController.cs @@ -2641,6 +2641,8 @@ namespace BMA.EHR.Insignia.Service.Controllers { if (item.CitizanId == null) continue; var _profile = await _userProfileRepository.GetOfficerProfileByCitizenId(item.CitizanId, AccessToken); + if (_profile == null) + continue; var profile = insigniaNote.InsigniaNoteProfiles.FirstOrDefault(x => x.ProfileId == _profile.Id); if (profile == null) { From 06956284d7b14ebeb88f2a6a82b1f3a2c28e6195 Mon Sep 17 00:00:00 2001 From: adisak Date: Thu, 2 Apr 2026 17:50:18 +0700 Subject: [PATCH 163/178] #2381 --- .../Controllers/RetirementResignController.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BMA.EHR.Retirement.Service/Controllers/RetirementResignController.cs b/BMA.EHR.Retirement.Service/Controllers/RetirementResignController.cs index 7b8d27b1..147f1adb 100644 --- a/BMA.EHR.Retirement.Service/Controllers/RetirementResignController.cs +++ b/BMA.EHR.Retirement.Service/Controllers/RetirementResignController.cs @@ -165,7 +165,7 @@ namespace BMA.EHR.Retirement.Service.Controllers p.ApproveReason, p.RejectReason, p.CancelReason, - p.Status, + status = p.RetirementResignCancels.FirstOrDefault() == null ? p.Status : p.RetirementResignCancels.FirstOrDefault().Status, statusCancel = p.RetirementResignCancels.FirstOrDefault() == null ? null : p.RetirementResignCancels.FirstOrDefault().Status, p.IsActive, }) From bceb4d3096016ef72133b79bf6b23c8222071267 Mon Sep 17 00:00:00 2001 From: Adisak Date: Fri, 3 Apr 2026 11:45:52 +0700 Subject: [PATCH 164/178] update emp status resign --- .../Controllers/RetirementResignEmployeeController.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/BMA.EHR.Retirement.Service/Controllers/RetirementResignEmployeeController.cs b/BMA.EHR.Retirement.Service/Controllers/RetirementResignEmployeeController.cs index 9b176503..5ad00538 100644 --- a/BMA.EHR.Retirement.Service/Controllers/RetirementResignEmployeeController.cs +++ b/BMA.EHR.Retirement.Service/Controllers/RetirementResignEmployeeController.cs @@ -102,8 +102,8 @@ namespace BMA.EHR.Retirement.Service.Controllers p.Remark, p.ApproveReason, p.RejectReason, - p.CancelReason, - p.Status, + p.CancelReason, + status = p.RetirementResignEmployeeCancels.FirstOrDefault() == null ? p.Status : p.RetirementResignEmployeeCancels.FirstOrDefault().Status, statusCancel = p.RetirementResignEmployeeCancels.FirstOrDefault() == null ? null : p.RetirementResignEmployeeCancels.FirstOrDefault().Status, p.IsActive, }) From cef41506a81ceaf07c4bd7886eda651bb3d95a65 Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Fri, 3 Apr 2026 12:09:00 +0700 Subject: [PATCH 165/178] Change Amount property to nullable int in GetProfileLeaveByKeycloakDto #2411 --- .../Responses/Leaves/GetProfileLeaveByKeycloakDto.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BMA.EHR.Application/Responses/Leaves/GetProfileLeaveByKeycloakDto.cs b/BMA.EHR.Application/Responses/Leaves/GetProfileLeaveByKeycloakDto.cs index c6c79bfd..9d13d834 100644 --- a/BMA.EHR.Application/Responses/Leaves/GetProfileLeaveByKeycloakDto.cs +++ b/BMA.EHR.Application/Responses/Leaves/GetProfileLeaveByKeycloakDto.cs @@ -13,7 +13,7 @@ public string Age { get; set; } = string.Empty; public DateTime DateAppoint { get; set; } public DateTime DateCurrent { get; set; } - public int Amount { get; set; } + public int? Amount { get; set; } = 0; public string? TelephoneNumber { get; set; } = string.Empty; public string Position { get; set; } = string.Empty; public string PosLevel { get; set; } = string.Empty; From bf92f6933e47e5083ac78dc7ce64ab0afd460903 Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Fri, 3 Apr 2026 12:17:39 +0700 Subject: [PATCH 166/178] Refactor user profile retrieval method in InsigniaRequestController #2390 --- BMA.EHR.Insignia/Controllers/InsigniaRequestController.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BMA.EHR.Insignia/Controllers/InsigniaRequestController.cs b/BMA.EHR.Insignia/Controllers/InsigniaRequestController.cs index 0aa8a218..415a0de2 100644 --- a/BMA.EHR.Insignia/Controllers/InsigniaRequestController.cs +++ b/BMA.EHR.Insignia/Controllers/InsigniaRequestController.cs @@ -3098,7 +3098,7 @@ namespace BMA.EHR.Insignia.Service.Controllers var doc = await _documentService.UploadFileAsync(file, file.FileName); insigniaNoteProfile.DocReturnInsignia = doc; } - var root = _userProfileRepository.GetOc(req.OrgId, 0, AccessToken)?.Root ?? null; + var root = _userProfileRepository.GetOcByNodeId(req.OrgId, 0, AccessToken)?.Root ?? null; if (req.OrgId != Guid.Parse("00000000-0000-0000-0000-000000000000")) { if (root == null) From 8950073485bbf0ad36b6b4c7bdf757c9bb6285ad Mon Sep 17 00:00:00 2001 From: harid Date: Fri, 3 Apr 2026 16:03:01 +0700 Subject: [PATCH 167/178] =?UTF-8?q?=E0=B8=AA=E0=B9=88=E0=B8=87=E0=B8=A3?= =?UTF-8?q?=E0=B8=B2=E0=B8=A2=E0=B8=8A=E0=B8=B7=E0=B9=88=E0=B8=AD=E0=B9=84?= =?UTF-8?q?=E0=B8=9B=E0=B8=AD=E0=B8=AD=E0=B8=81=E0=B8=84=E0=B8=B3=E0=B8=AA?= =?UTF-8?q?=E0=B8=B1=E0=B9=88=E0=B8=87=20C-PM-25,=20C-PM-26=20=E0=B9=83?= =?UTF-8?q?=E0=B8=AB=E0=B9=89=E0=B8=9B=E0=B8=B1=E0=B9=8A=E0=B8=A1=20comman?= =?UTF-8?q?dCode=20#2377?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Controllers/DisciplineResultController.cs | 40 ++++++++++++++++--- .../Requests/ReportPersonRequest.cs | 8 ++++ 2 files changed, 42 insertions(+), 6 deletions(-) diff --git a/BMA.EHR.Discipline.Service/Controllers/DisciplineResultController.cs b/BMA.EHR.Discipline.Service/Controllers/DisciplineResultController.cs index e5be6189..85388209 100644 --- a/BMA.EHR.Discipline.Service/Controllers/DisciplineResultController.cs +++ b/BMA.EHR.Discipline.Service/Controllers/DisciplineResultController.cs @@ -1242,14 +1242,22 @@ namespace BMA.EHR.DisciplineResult.Service.Controllers /// ไม่ได้ Login เข้าระบบ /// เมื่อเกิดข้อผิดพลาดในการทำงาน [HttpPost("command25/report")] - public async Task> PostReportCommand25([FromBody] ReportPersonRequest req) + public async Task> PostReportCommand25([FromBody] ReportPersonAndCommandRequest req) { try { var data = await _context.DisciplineReport_Profiles .Where(x => req.refIds.Contains(x.Id.ToString())) .ToListAsync(); - data.ForEach(profile => profile.Status = req.status.Trim().ToUpper()); + // data.ForEach(profile => profile.Status = req.status.Trim().ToUpper()); + data.ForEach(profile => + { + profile.Status = !string.IsNullOrEmpty(req.status) + ? req.status.Trim().ToUpper() : null; + profile.CommandTypeId = !string.IsNullOrEmpty(req.commandTypeId) && Guid.TryParse(req.commandTypeId, out var cmdTypeId) + ? cmdTypeId : null; + profile.CommandCode = req.commandCode ?? null; + }); await _context.SaveChangesAsync(); return Success(); } @@ -1276,7 +1284,13 @@ namespace BMA.EHR.DisciplineResult.Service.Controllers .Where(x => req.refIds.Contains(x.Id.ToString())) // .Where(x => x.Status.ToUpper() == "REPORT") .ToListAsync(); - data.ForEach(profile => profile.Status = "PENDING"); + // data.ForEach(profile => profile.Status = "PENDING"); + data.ForEach(profile => + { + profile.Status = "PENDING"; + profile.CommandTypeId = null; + profile.CommandCode = null; + }); await _context.SaveChangesAsync(); return Success(); } @@ -1429,14 +1443,22 @@ namespace BMA.EHR.DisciplineResult.Service.Controllers /// ไม่ได้ Login เข้าระบบ /// เมื่อเกิดข้อผิดพลาดในการทำงาน [HttpPost("command26/report")] - public async Task> PostReportCommand26([FromBody] ReportPersonRequest req) + public async Task> PostReportCommand26([FromBody] ReportPersonAndCommandRequest req) { try { var data = await _context.DisciplineReport_Profiles .Where(x => req.refIds.Contains(x.Id.ToString())) .ToListAsync(); - data.ForEach(profile => profile.Status = req.status.Trim().ToUpper()); + // data.ForEach(profile => profile.Status = req.status.Trim().ToUpper()); + data.ForEach(profile => + { + profile.Status = !string.IsNullOrEmpty(req.status) + ? req.status.Trim().ToUpper() : null; + profile.CommandTypeId = !string.IsNullOrEmpty(req.commandTypeId) && Guid.TryParse(req.commandTypeId, out var cmdTypeId) + ? cmdTypeId : null; + profile.CommandCode = req.commandCode ?? null; + }); await _context.SaveChangesAsync(); return Success(); } @@ -1463,7 +1485,13 @@ namespace BMA.EHR.DisciplineResult.Service.Controllers .Where(x => req.refIds.Contains(x.Id.ToString())) // .Where(x => x.Status.ToUpper() == "REPORT") .ToListAsync(); - data.ForEach(profile => profile.Status = "PENDING"); + // data.ForEach(profile => profile.Status = "PENDING"); + data.ForEach(profile => + { + profile.Status = "PENDING"; + profile.CommandTypeId = null; + profile.CommandCode = null; + }); await _context.SaveChangesAsync(); return Success(); } diff --git a/BMA.EHR.Discipline.Service/Requests/ReportPersonRequest.cs b/BMA.EHR.Discipline.Service/Requests/ReportPersonRequest.cs index 1bd61c56..f7a16efc 100644 --- a/BMA.EHR.Discipline.Service/Requests/ReportPersonRequest.cs +++ b/BMA.EHR.Discipline.Service/Requests/ReportPersonRequest.cs @@ -8,4 +8,12 @@ namespace BMA.EHR.Discipline.Service.Requests public string[] refIds { get; set; } public string? status { get; set; } } + + public class ReportPersonAndCommandRequest + { + public string[] refIds { get; set; } + public string? status { get; set; } + public string? commandTypeId { get; set; } + public string? commandCode { get; set; } + } } From 057b51390e9291565d5b2a112a9fca0126cdeade Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Thu, 9 Apr 2026 12:07:11 +0700 Subject: [PATCH 168/178] add some code --- .../Leaves/LeaveRequests/LeaveRequestRepository.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/BMA.EHR.Application/Repositories/Leaves/LeaveRequests/LeaveRequestRepository.cs b/BMA.EHR.Application/Repositories/Leaves/LeaveRequests/LeaveRequestRepository.cs index f465191e..8416af4e 100644 --- a/BMA.EHR.Application/Repositories/Leaves/LeaveRequests/LeaveRequestRepository.cs +++ b/BMA.EHR.Application/Repositories/Leaves/LeaveRequests/LeaveRequestRepository.cs @@ -676,6 +676,9 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests thisYear = thisYear + 1; } await _leaveBeginningRepository.UpdateLeaveUsageAsync(thisYear, data.Type.Id, data.KeycloakUserId, -1 * data.LeaveTotal); + // update leave count ลดลง 1 ครั้ง + await _leaveBeginningRepository.UpdateLeaveCountAsync(thisYear, data.Type.Id, data.KeycloakUserId, -1); + var _baseAPI = _configuration["API"]; var apiUrlSalary = $"{_baseAPI}/org/profile/leave/cancel/{data.Id}"; From 678329b5dfed9e81580db093992763e45f53ec27 Mon Sep 17 00:00:00 2001 From: adisak Date: Thu, 16 Apr 2026 11:31:03 +0700 Subject: [PATCH 169/178] #2392 fix departmentName null --- .../Repositories/Reports/InsigniaReportRepository.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BMA.EHR.Application/Repositories/Reports/InsigniaReportRepository.cs b/BMA.EHR.Application/Repositories/Reports/InsigniaReportRepository.cs index c094738e..a1dd7936 100644 --- a/BMA.EHR.Application/Repositories/Reports/InsigniaReportRepository.cs +++ b/BMA.EHR.Application/Repositories/Reports/InsigniaReportRepository.cs @@ -893,7 +893,7 @@ namespace BMA.EHR.Application.Repositories.Reports select new { RowNo = 1, - DepartmentName = _userProfileRepository.GetOc(g.Key.OcId, 0, AccessToken).Root, //_organizationCommonRepository.GetOrganizationNameFullPath(g.Key.OcId, false, false), + DepartmentName = _userProfileRepository.GetOc(g.Key.OcId, 0, AccessToken)?.Root ?? "-", //_organizationCommonRepository.GetOrganizationNameFullPath(g.Key.OcId, false, false), G1Male = g.Sum(x => x.Gendor == "ชาย" && x.RequestInsigniaName == "เหรียญจักรพรรดิมาลา" ? 1 : 0), G1Female = g.Sum(x => x.Gendor == "หญิง" && x.RequestInsigniaName == "เหรียญจักรพรรดิมาลา" ? 1 : 0), G2Male = g.Sum(x => x.Gendor == "ชาย" ? 1 : 0), From c34fe35506aefe49341f265a5f5254093b9f21d7 Mon Sep 17 00:00:00 2001 From: harid Date: Thu, 16 Apr 2026 11:57:47 +0700 Subject: [PATCH 170/178] =?UTF-8?q?API=20=E0=B8=A2=E0=B8=81=E0=B9=80?= =?UTF-8?q?=E0=B8=A5=E0=B8=B4=E0=B8=81=E0=B8=81=E0=B8=B2=E0=B8=A3=E0=B8=AA?= =?UTF-8?q?=E0=B9=88=E0=B8=87=E0=B8=95=E0=B8=B1=E0=B8=A7=E0=B8=9A=E0=B8=A3?= =?UTF-8?q?=E0=B8=A3=E0=B8=88=E0=B8=B8=E0=B8=9C=E0=B8=B9=E0=B9=89=E0=B8=AA?= =?UTF-8?q?=E0=B8=AD=E0=B8=9A=E0=B8=9C=E0=B9=88=E0=B8=B2=E0=B8=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Controllers/PlacementController.cs | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/BMA.EHR.Placement.Service/Controllers/PlacementController.cs b/BMA.EHR.Placement.Service/Controllers/PlacementController.cs index 2a1e5399..e8970095 100644 --- a/BMA.EHR.Placement.Service/Controllers/PlacementController.cs +++ b/BMA.EHR.Placement.Service/Controllers/PlacementController.cs @@ -903,6 +903,49 @@ namespace BMA.EHR.Placement.Service.Controllers return Success(); } + /// + /// API สำหรับยกเลิกการส่งตัว + /// + /// + /// + /// ค่าตัวแปรที่ส่งมาไม่ถูกต้อง + /// ไม่ได้ Login เข้าระบบ + /// เมื่อเกิดข้อผิดพลาดในการทำงาน + [HttpPost("update/draft-status")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + [ProducesResponseType(StatusCodes.Status401Unauthorized)] + [ProducesResponseType(StatusCodes.Status500InternalServerError)] + public async Task> PersonUpdateDraftStatus([FromBody] PersonUpdateStatusRequest req) + { + var getPermission = await _permission.GetPermissionAPIAsync("UPDATE", "SYS_PLACEMENT_PASS"); + var jsonData = JsonConvert.DeserializeObject(getPermission); + if (jsonData["status"]?.ToString() != "200") + return Error(jsonData["message"]?.ToString(), StatusCodes.Status403Forbidden); + + string role = jsonData["result"]?.ToString(); + if (role != "OWNER") + return Error(jsonData["message"]?.ToString(), StatusCodes.Status403Forbidden); + + var person = await _context.PlacementProfiles + .FirstOrDefaultAsync(x => x.Id == req.PersonalId); + if (person == null) + return Error(GlobalMessages.DataNotFound, 404); + + if (person.PlacementStatus == "REPORT") + return Error("ไม่สามารถยกเลิกการส่งตัวได้ เนื่องจากส่งไปออกคำสั่งแล้ว"); + + if (person.PlacementStatus == "DONE") + return Error("ไม่สามารถยกเลิกการส่งตัวได้ เนื่องจากบรรจุไปแล้ว"); + + person.Draft = false; + person.LastUpdateFullName = FullName ?? "System Administrator"; + person.LastUpdateUserId = UserId ?? ""; + person.LastUpdatedAt = DateTime.Now; + await _context.SaveChangesAsync(); + return Success(); + } + [HttpGet("pass/deferment/{personalId:length(36)}")] public async Task> GetPersonDeferment(Guid personalId) { From 6efeec3f1ff329465d7780b9ed6fa17bfa5961e7 Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Thu, 16 Apr 2026 19:05:26 +0700 Subject: [PATCH 171/178] =?UTF-8?q?=E0=B9=80=E0=B8=9E=E0=B8=B4=E0=B9=88?= =?UTF-8?q?=E0=B8=A1=20List=20=E0=B8=82=E0=B8=AD=E0=B8=87=E0=B8=81?= =?UTF-8?q?=E0=B8=B2=E0=B8=A3=E0=B9=80=E0=B8=88=E0=B9=89=E0=B8=AB=E0=B8=99?= =?UTF-8?q?=E0=B9=89=E0=B8=B2=E0=B8=97=E0=B8=B5=E0=B9=88=20=E0=B8=AA?= =?UTF-8?q?=E0=B9=88=E0=B8=87=20noti=20=E0=B8=82=E0=B8=AD=E0=B8=A2?= =?UTF-8?q?=E0=B8=81=E0=B9=80=E0=B8=A5=E0=B8=B4=E0=B8=81=E0=B8=81=E0=B8=B2?= =?UTF-8?q?=E0=B8=A3=E0=B8=A5=E0=B8=B2=20#2432?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BMA.EHR.Domain/Shared/GlobalMessages.cs | 2 ++ .../Controllers/LeaveRequestController.cs | 28 +++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/BMA.EHR.Domain/Shared/GlobalMessages.cs b/BMA.EHR.Domain/Shared/GlobalMessages.cs index 8746de95..a99dc3c9 100644 --- a/BMA.EHR.Domain/Shared/GlobalMessages.cs +++ b/BMA.EHR.Domain/Shared/GlobalMessages.cs @@ -8,6 +8,8 @@ public static readonly string DataNotFound = "ไม่พบข้อมูลในระบบ"; + public static readonly string ProfileNotFound = "ไม่พบข้อมูลในระบบทะเบียนประวัติ"; + public static readonly string NotAuthorized = "กรุณาเข้าสู่ระบบก่อนใช้งาน!"; public static readonly string ForbiddenAccess = "คุณไม่ได้รับอนุญาติให้เข้าใช้งาน!"; diff --git a/BMA.EHR.Leave/Controllers/LeaveRequestController.cs b/BMA.EHR.Leave/Controllers/LeaveRequestController.cs index 7bc3b151..1252eef3 100644 --- a/BMA.EHR.Leave/Controllers/LeaveRequestController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveRequestController.cs @@ -1876,6 +1876,12 @@ namespace BMA.EHR.Leave.Service.Controllers return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound); } + var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(data.KeycloakUserId, AccessToken); + if (profile == null) + { + return Error(GlobalMessages.ProfileNotFound, StatusCodes.Status404NotFound); + } + // change status to delete // แก้จาก DELETE เป็น DELETING ไว้ก่อน รอ approve ค่อยเปลี่ยนเป็น DELETE // data.LeaveStatus = "DELETE"; @@ -1929,6 +1935,28 @@ namespace BMA.EHR.Leave.Service.Controllers _appDbContext.Set().Add(noti1); } + // Get Officer List + var officers = await _userProfileRepository.GetOCStaffAsync(profile.Id, AccessToken); + var approverProfileIdList = approvers.Select(x => x.ProfileId).ToList(); + + if(officers != null && officers.Count > 0) + { + officers = officers.Where(x => !approverProfileIdList.Contains(x.ProfileId)).ToList(); + foreach (var officer in officers) + { + // Send Notification + var noti = new Notification + { + Body = $"คำร้องขอยกเลิกการลาของคุณ {data.FirstName} {data.LastName} รอรับการอนุมัติจากคุณ", + ReceiverUserId = officer.ProfileId, + Type = "", + Payload = $"{URL}/leave/detail/{id}", + }; + _appDbContext.Set().Add(noti); + } + await _appDbContext.SaveChangesAsync(); + } + return Success(); } From ee4e9c36999d3e601be7e27d8f877d1e48545aed Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Thu, 16 Apr 2026 21:30:17 +0700 Subject: [PATCH 172/178] =?UTF-8?q?=E0=B9=81=E0=B8=81=E0=B9=89=20leave/det?= =?UTF-8?q?ail=20=E0=B9=80=E0=B8=9B=E0=B9=87=E0=B8=99=20leave-reject/detai?= =?UTF-8?q?l=20=E0=B8=AA=E0=B8=B3=E0=B8=AB=E0=B8=A3=E0=B8=B1=E0=B8=9A?= =?UTF-8?q?=E0=B8=A3=E0=B8=B2=E0=B8=A2=E0=B8=81=E0=B8=B2=E0=B8=A3=E0=B8=A2?= =?UTF-8?q?=E0=B8=81=E0=B9=80=E0=B8=A5=E0=B8=B4=E0=B8=81=20#2432?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BMA.EHR.Leave/Controllers/LeaveRequestController.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/BMA.EHR.Leave/Controllers/LeaveRequestController.cs b/BMA.EHR.Leave/Controllers/LeaveRequestController.cs index 1252eef3..09836018 100644 --- a/BMA.EHR.Leave/Controllers/LeaveRequestController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveRequestController.cs @@ -1930,7 +1930,7 @@ namespace BMA.EHR.Leave.Service.Controllers Body = $"คำร้องขอยกเลิกการลาของคุณ {data.FirstName} {data.LastName} รอรับการอนุมัติจากคุณ", ReceiverUserId = approver!.ProfileId, Type = "", - Payload = $"{URL}/leave/detail/{id}", + Payload = $"{URL}/leave-reject/detail/{id}", }; _appDbContext.Set().Add(noti1); } @@ -1950,7 +1950,7 @@ namespace BMA.EHR.Leave.Service.Controllers Body = $"คำร้องขอยกเลิกการลาของคุณ {data.FirstName} {data.LastName} รอรับการอนุมัติจากคุณ", ReceiverUserId = officer.ProfileId, Type = "", - Payload = $"{URL}/leave/detail/{id}", + Payload = $"{URL}/leave-reject/detail/{id}", }; _appDbContext.Set().Add(noti); } From 5606e8b50ac126ae4054e126080b0743ad79da78 Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Fri, 17 Apr 2026 09:41:52 +0700 Subject: [PATCH 173/178] Change GetProfileByKeycloakIdNewAsync To GetProfileByKeycloakIdNew2Async --- .../LeaveRequests/LeaveBeginingRepository.cs | 8 ++--- .../LeaveRequests/LeaveRequestRepository.cs | 14 ++++----- BMA.EHR.Leave/Controllers/LeaveController.cs | 30 +++++++++---------- .../Controllers/LeaveRequestController.cs | 16 +++++----- 4 files changed, 34 insertions(+), 34 deletions(-) diff --git a/BMA.EHR.Application/Repositories/Leaves/LeaveRequests/LeaveBeginingRepository.cs b/BMA.EHR.Application/Repositories/Leaves/LeaveRequests/LeaveBeginingRepository.cs index 2d14db9c..a62aa291 100644 --- a/BMA.EHR.Application/Repositories/Leaves/LeaveRequests/LeaveBeginingRepository.cs +++ b/BMA.EHR.Application/Repositories/Leaves/LeaveRequests/LeaveBeginingRepository.cs @@ -80,7 +80,7 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests public async Task UpdateLeaveUsageAsync(int year, Guid typeId, Guid userId, double day) { // var pf = await _userProfileRepository.GetProfileByKeycloakIdAsync(userId, AccessToken); - var pf = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(userId, AccessToken); + var pf = await _userProfileRepository.GetProfileByKeycloakIdNew2Async(userId, AccessToken); if (pf == null) { throw new Exception(GlobalMessages.DataNotFound); @@ -102,7 +102,7 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests public async Task UpdateLeaveCountAsync(int year, Guid typeId, Guid userId, int count) { // var pf = await _userProfileRepository.GetProfileByKeycloakIdAsync(userId, AccessToken); - var pf = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(userId, AccessToken); + var pf = await _userProfileRepository.GetProfileByKeycloakIdNew2Async(userId, AccessToken); if (pf == null) { throw new Exception(GlobalMessages.DataNotFound); @@ -124,7 +124,7 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests public async Task GetByYearAndTypeIdForUserAsync(int year, Guid typeId, Guid userId) { // var pf = await _userProfileRepository.GetProfileByKeycloakIdAsync(userId, AccessToken); - var pf = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(userId, AccessToken); + var pf = await _userProfileRepository.GetProfileByKeycloakIdNew2Async(userId, AccessToken); if (pf == null) { throw new Exception(GlobalMessages.DataNotFound); @@ -263,7 +263,7 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests public async Task GetByYearAndTypeIdForUser2Async(int year, Guid typeId, Guid userId) { // var pf = await _userProfileRepository.GetProfileByKeycloakIdAsync(userId, AccessToken); - var pf = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(userId, AccessToken); + var pf = await _userProfileRepository.GetProfileByKeycloakIdNew2Async(userId, AccessToken); if (pf == null) { return null; diff --git a/BMA.EHR.Application/Repositories/Leaves/LeaveRequests/LeaveRequestRepository.cs b/BMA.EHR.Application/Repositories/Leaves/LeaveRequests/LeaveRequestRepository.cs index 8416af4e..792b57cb 100644 --- a/BMA.EHR.Application/Repositories/Leaves/LeaveRequests/LeaveRequestRepository.cs +++ b/BMA.EHR.Application/Repositories/Leaves/LeaveRequests/LeaveRequestRepository.cs @@ -254,7 +254,7 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests public async Task> GetLeaveRequestByYearAsync(int year, Guid userId) { // var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(userId, AccessToken); - var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(userId, AccessToken); + var profile = await _userProfileRepository.GetProfileByKeycloakIdNew2Async(userId, AccessToken); if (profile == null) { @@ -497,7 +497,7 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests public async Task GetSumLeaveByTypeForUserAsync(Guid keycloakUserId, Guid leaveTypeId, int year) { // var pf = await _userProfileRepository.GetProfileByKeycloakIdAsync(keycloakUserId, AccessToken); - var pf = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(keycloakUserId, AccessToken); + var pf = await _userProfileRepository.GetProfileByKeycloakIdNew2Async(keycloakUserId, AccessToken); if (pf == null) throw new Exception(GlobalMessages.DataNotFound); @@ -651,7 +651,7 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests try { // var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(data.KeycloakUserId, AccessToken ?? ""); - var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(data.KeycloakUserId, AccessToken ?? ""); + var profile = await _userProfileRepository.GetProfileByKeycloakIdNew2Async(data.KeycloakUserId, AccessToken ?? ""); if (profile == null) { throw new Exception(GlobalMessages.DataNotFound); @@ -728,7 +728,7 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests } // var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(rawData.KeycloakUserId, AccessToken ?? ""); - var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(rawData.KeycloakUserId, AccessToken ?? ""); + var profile = await _userProfileRepository.GetProfileByKeycloakIdNew2Async(rawData.KeycloakUserId, AccessToken ?? ""); if (profile == null) { throw new Exception(GlobalMessages.DataNotFound); @@ -817,7 +817,7 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests } // var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(rawData.KeycloakUserId, AccessToken ?? ""); - var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(rawData.KeycloakUserId, AccessToken ?? ""); + var profile = await _userProfileRepository.GetProfileByKeycloakIdNew2Async(rawData.KeycloakUserId, AccessToken ?? ""); if (profile == null) { throw new Exception(GlobalMessages.DataNotFound); @@ -1242,7 +1242,7 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests else { // var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(rawData.KeycloakUserId, AccessToken); - var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(rawData.KeycloakUserId, AccessToken); + var profile = await _userProfileRepository.GetProfileByKeycloakIdNew2Async(rawData.KeycloakUserId, AccessToken); if (profile == null) { throw new Exception(GlobalMessages.DataNotFound); @@ -1412,7 +1412,7 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests else { // var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(rawData.KeycloakUserId, AccessToken); - var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(rawData.KeycloakUserId, AccessToken); + var profile = await _userProfileRepository.GetProfileByKeycloakIdNew2Async(rawData.KeycloakUserId, AccessToken); if (profile == null) { throw new Exception(GlobalMessages.DataNotFound); diff --git a/BMA.EHR.Leave/Controllers/LeaveController.cs b/BMA.EHR.Leave/Controllers/LeaveController.cs index 0a423d60..60841b8d 100644 --- a/BMA.EHR.Leave/Controllers/LeaveController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveController.cs @@ -447,7 +447,7 @@ namespace BMA.EHR.Leave.Service.Controllers // Get user's last check-in record and profile in parallel var dataTask = _userTimeStampRepository.GetLastRecord(userId); - var profileTask = _userProfileRepository.GetProfileByKeycloakIdNewAsync(userId, AccessToken); + var profileTask = _userProfileRepository.GetProfileByKeycloakIdNew2Async(userId, AccessToken); var defaultRoundTask = _dutyTimeRepository.GetDefaultAsync(); await Task.WhenAll(dataTask, profileTask, defaultRoundTask); @@ -936,7 +936,7 @@ namespace BMA.EHR.Leave.Service.Controllers await _checkInJobStatusRepository.UpdateToProcessingAsync(taskId); } - var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(userId, data.Token); + var profile = await _userProfileRepository.GetProfileByKeycloakIdNew2Async(userId, data.Token); if (profile == null) { @@ -1589,7 +1589,7 @@ namespace BMA.EHR.Leave.Service.Controllers public async Task> CheckInOldAsync([FromForm] CheckTimeDto data) { var userId = UserId == null ? Guid.Empty : Guid.Parse(UserId); - var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(userId, AccessToken); + var profile = await _userProfileRepository.GetProfileByKeycloakIdNew2Async(userId, AccessToken); if (profile == null) return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound); @@ -1754,7 +1754,7 @@ namespace BMA.EHR.Leave.Service.Controllers { var userId = UserId == null ? Guid.Empty : Guid.Parse(UserId); - var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(userId, AccessToken); + var profile = await _userProfileRepository.GetProfileByKeycloakIdNew2Async(userId, AccessToken); if (profile == null) { return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound); @@ -2043,7 +2043,7 @@ namespace BMA.EHR.Leave.Service.Controllers } else { - var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(d.KeycloakUserId, AccessToken); + var profile = await _userProfileRepository.GetProfileByKeycloakIdNew2Async(d.KeycloakUserId, AccessToken); if (profile == null) { return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound); @@ -2993,7 +2993,7 @@ namespace BMA.EHR.Leave.Service.Controllers var time = DateTime.Now; var userId = UserId != null ? Guid.Parse(UserId) : Guid.Empty; - var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(userId, AccessToken); + var profile = await _userProfileRepository.GetProfileByKeycloakIdNew2Async(userId, AccessToken); if (profile == null) { throw new Exception(GlobalMessages.DataNotFound); @@ -3113,7 +3113,7 @@ namespace BMA.EHR.Leave.Service.Controllers } var userId = UserId != null ? Guid.Parse(UserId) : Guid.Empty; - var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(userId, AccessToken); + var profile = await _userProfileRepository.GetProfileByKeycloakIdNew2Async(userId, AccessToken); if (profile == null) { throw new Exception(GlobalMessages.DataNotFound); @@ -3276,7 +3276,7 @@ namespace BMA.EHR.Leave.Service.Controllers foreach (var data in rawDataPaged) { - var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(data.KeycloakUserId, AccessToken); + var profile = await _userProfileRepository.GetProfileByKeycloakIdNew2Async(data.KeycloakUserId, AccessToken); UserDutyTime? effectiveDate = null; if (profile != null) { @@ -3455,7 +3455,7 @@ namespace BMA.EHR.Leave.Service.Controllers // change user timestamp var processTimeStamp = await _processUserTimeStampRepository.GetTimestampByDateAsync(requestData.KeycloakUserId, requestData.CheckDate.Date); - var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(requestData.KeycloakUserId, AccessToken); + var profile = await _userProfileRepository.GetProfileByKeycloakIdNew2Async(requestData.KeycloakUserId, AccessToken); if (processTimeStamp == null) { @@ -3609,7 +3609,7 @@ namespace BMA.EHR.Leave.Service.Controllers requestData.Comment = req.Reason; await _additionalCheckRequestRepository.UpdateAsync(requestData); - var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(requestData.KeycloakUserId, AccessToken); + var profile = await _userProfileRepository.GetProfileByKeycloakIdNew2Async(requestData.KeycloakUserId, AccessToken); var recvId = new List { profile.Id }; await _notificationRepository.PushNotificationsAsync(recvId.ToArray(), "ลงเวลากรณีพิเศษ", "การขอลงเวลากรณีพิเศษของคุณไม่ได้รับการอนุมัติ", "", "", true, false); @@ -3653,7 +3653,7 @@ namespace BMA.EHR.Leave.Service.Controllers } else { - var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(d.KeycloakUserId, AccessToken); + var profile = await _userProfileRepository.GetProfileByKeycloakIdNew2Async(d.KeycloakUserId, AccessToken); if (profile == null) { return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound); @@ -3747,7 +3747,7 @@ namespace BMA.EHR.Leave.Service.Controllers foreach (var data in rawData) { - var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(data.KeycloakUserId, AccessToken); + var profile = await _userProfileRepository.GetProfileByKeycloakIdNew2Async(data.KeycloakUserId, AccessToken); if (profile == null) { return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound); @@ -4037,7 +4037,7 @@ namespace BMA.EHR.Leave.Service.Controllers //var userId = UserId == null ? Guid.Empty : Guid.Parse(UserId); // แก้เป็นมาใช้งาน KeycloakUserId แทน - var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(data.KeycloakUserId, AccessToken); + var profile = await _userProfileRepository.GetProfileByKeycloakIdNew2Async(data.KeycloakUserId, AccessToken); var defaultRound = await _dutyTimeRepository.GetDefaultAsync(); if (defaultRound == null) { @@ -4107,7 +4107,7 @@ namespace BMA.EHR.Leave.Service.Controllers [ProducesResponseType(StatusCodes.Status500InternalServerError)] public async Task> GetLeaveSummaryByProfileAsync(Guid id, [FromBody] GetLeaveSummaryDto req) { - var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(id, AccessToken); + var profile = await _userProfileRepository.GetProfileByKeycloakIdNew2Async(id, AccessToken); var thisYear = DateTime.Now.Year; var startDate = req.StartDate; @@ -4177,7 +4177,7 @@ namespace BMA.EHR.Leave.Service.Controllers { var userId = UserId == null ? Guid.Empty : Guid.Parse(UserId); - var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(userId, AccessToken); + var profile = await _userProfileRepository.GetProfileByKeycloakIdNew2Async(userId, AccessToken); if (profile == null) { return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound); diff --git a/BMA.EHR.Leave/Controllers/LeaveRequestController.cs b/BMA.EHR.Leave/Controllers/LeaveRequestController.cs index 09836018..baa6d51b 100644 --- a/BMA.EHR.Leave/Controllers/LeaveRequestController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveRequestController.cs @@ -214,7 +214,7 @@ namespace BMA.EHR.Leave.Service.Controllers var thisYear = DateTime.Now.Year; // var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(userId, AccessToken); - var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(userId, AccessToken); + var profile = await _userProfileRepository.GetProfileByKeycloakIdNew2Async(userId, AccessToken); if (profile == null) { @@ -502,7 +502,7 @@ namespace BMA.EHR.Leave.Service.Controllers foreach (var leave in leaves) { // var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(leave.KeycloakUserId, AccessToken); - var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(leave.KeycloakUserId, AccessToken); + var profile = await _userProfileRepository.GetProfileByKeycloakIdNew2Async(leave.KeycloakUserId, AccessToken); if (profile != null) { leave.Prefix = profile.Prefix; @@ -563,7 +563,7 @@ namespace BMA.EHR.Leave.Service.Controllers // } // var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(userId, AccessToken); - var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(userId, AccessToken); + var profile = await _userProfileRepository.GetProfileByKeycloakIdNew2Async(userId, AccessToken); if (profile == null) { @@ -1026,7 +1026,7 @@ namespace BMA.EHR.Leave.Service.Controllers var userId = UserId == null ? Guid.Empty : Guid.Parse(UserId); // var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(userId, AccessToken); - var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(userId, AccessToken); + var profile = await _userProfileRepository.GetProfileByKeycloakIdNew2Async(userId, AccessToken); var govAge = (profile?.DateStart?.Date ?? DateTime.Now.Date).DiffDay(DateTime.Now.Date); var startDate = profile?.DateStart?.Date ?? DateTime.Now.Date; // var date1Raw = profile?.DateStart?.Date ?? DateTime.Now.Date; @@ -1577,7 +1577,7 @@ namespace BMA.EHR.Leave.Service.Controllers } // var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(rawData.KeycloakUserId, AccessToken); - var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(rawData.KeycloakUserId, AccessToken); + var profile = await _userProfileRepository.GetProfileByKeycloakIdNew2Async(rawData.KeycloakUserId, AccessToken); if (profile == null) { @@ -1876,7 +1876,7 @@ namespace BMA.EHR.Leave.Service.Controllers return Error(GlobalMessages.DataNotFound, StatusCodes.Status404NotFound); } - var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(data.KeycloakUserId, AccessToken); + var profile = await _userProfileRepository.GetProfileByKeycloakIdNew2Async(data.KeycloakUserId, AccessToken); if (profile == null) { return Error(GlobalMessages.ProfileNotFound, StatusCodes.Status404NotFound); @@ -2140,7 +2140,7 @@ namespace BMA.EHR.Leave.Service.Controllers } // var profile = await _userProfileRepository.GetProfileByKeycloakIdAsync(Guid.Parse(UserId!), AccessToken); - var profile = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(Guid.Parse(UserId!), AccessToken); + var profile = await _userProfileRepository.GetProfileByKeycloakIdNew2Async(Guid.Parse(UserId!), AccessToken); if (profile == null) { @@ -2785,7 +2785,7 @@ namespace BMA.EHR.Leave.Service.Controllers var rejectList = await _leaveRequestRepository.GetSumRejectLeaveAsync(thisYear); var deleteList = await _leaveRequestRepository.GetSumDeleteLeaveAsync(thisYear); // var pf = await _userProfileRepository.GetProfileByKeycloakIdAsync(userId, AccessToken); - var pf = await _userProfileRepository.GetProfileByKeycloakIdNewAsync(userId, AccessToken); + var pf = await _userProfileRepository.GetProfileByKeycloakIdNew2Async(userId, AccessToken); if (pf == null) { From 34ec9bb77c2644a987d04c04bab9c119dcd82ede Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Fri, 17 Apr 2026 10:31:09 +0700 Subject: [PATCH 174/178] Remove Check Pending Status For Cancel Leave #2432 --- BMA.EHR.Leave/Controllers/LeaveRequestController.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BMA.EHR.Leave/Controllers/LeaveRequestController.cs b/BMA.EHR.Leave/Controllers/LeaveRequestController.cs index baa6d51b..c2954ca4 100644 --- a/BMA.EHR.Leave/Controllers/LeaveRequestController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveRequestController.cs @@ -1918,7 +1918,7 @@ namespace BMA.EHR.Leave.Service.Controllers // TODO: Send notification to all users who need to approve the cancel leave request var approvers = data.Approvers - .Where(x => x.ApproveStatus!.ToUpper() == "PENDING") + //.Where(x => x.ApproveStatus!.ToUpper() == "PENDING") .OrderBy(x => x.Seq) .ToList(); From db99630e0d629eb9f3f3e3b64ebd1ff317cdfc7a Mon Sep 17 00:00:00 2001 From: harid Date: Fri, 17 Apr 2026 15:18:30 +0700 Subject: [PATCH 175/178] fix #2430 --- BMA.EHR.Placement.Service/Controllers/PlacementController.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BMA.EHR.Placement.Service/Controllers/PlacementController.cs b/BMA.EHR.Placement.Service/Controllers/PlacementController.cs index e8970095..fbb6e2de 100644 --- a/BMA.EHR.Placement.Service/Controllers/PlacementController.cs +++ b/BMA.EHR.Placement.Service/Controllers/PlacementController.cs @@ -2045,7 +2045,7 @@ namespace BMA.EHR.Placement.Service.Controllers .Where(x => req.refIds.Contains(x.Id.ToString())) // .Where(x => x.PlacementStatus.ToUpper() == "REPORT") .ToListAsync(); - placementProfiles.ForEach(profile => profile.PlacementStatus = "PREPARE-CONTAI"); + placementProfiles.ForEach(profile => profile.PlacementStatus = "PREPARE-CONTAIN"); await _context.SaveChangesAsync(); return Success(); } From 7bafbf5001d462edc59b7f215ef71ae446a1f38d Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Fri, 17 Apr 2026 15:35:29 +0700 Subject: [PATCH 176/178] =?UTF-8?q?=E0=B9=80=E0=B8=9E=E0=B8=B4=E0=B9=88?= =?UTF-8?q?=E0=B8=A1=E0=B8=A3=E0=B8=B2=E0=B8=A2=E0=B8=81=E0=B8=B2=E0=B8=A3?= =?UTF-8?q?=E0=B8=A3=E0=B8=B1=E0=B8=81=E0=B8=A9=E0=B8=B2=E0=B8=81=E0=B8=B2?= =?UTF-8?q?=E0=B8=A3=20#2431?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Repositories/PermissionRepository.cs | 34 +++ .../Leaves/GetPermissionWithActingDto.cs | 32 +++ BMA.EHR.Leave/Controllers/LeaveController.cs | 87 +++++- .../Controllers/LeaveRequestController.cs | 268 ++++++++++++++++-- 4 files changed, 398 insertions(+), 23 deletions(-) create mode 100644 BMA.EHR.Application/Responses/Leaves/GetPermissionWithActingDto.cs diff --git a/BMA.EHR.Application/Repositories/PermissionRepository.cs b/BMA.EHR.Application/Repositories/PermissionRepository.cs index 84191f91..a63207ec 100644 --- a/BMA.EHR.Application/Repositories/PermissionRepository.cs +++ b/BMA.EHR.Application/Repositories/PermissionRepository.cs @@ -10,6 +10,7 @@ using System.Net.Http.Headers; using Microsoft.Extensions.Configuration; using System.Security.Claims; using System.Net.Http.Json; +using BMA.EHR.Application.Responses.Leaves; namespace BMA.EHR.Application.Repositories { @@ -76,6 +77,39 @@ namespace BMA.EHR.Application.Repositories } } + public async Task GetPermissionWithActingAPIAsync(string action, string system) + { + try + { + var apiPath = $"{_configuration["API"]}/org/permission/dotnet-acting/{action}/{system}"; + + using (var client = new HttpClient()) + { + client.DefaultRequestHeaders.Authorization = + new AuthenticationHeaderValue("Bearer", AccessToken.Replace("Bearer ", "")); + client.DefaultRequestHeaders.Add("api-key", _configuration["API_KEY"]); + var req = await client.GetAsync(apiPath); + if (!req.IsSuccessStatusCode) + { + throw new Exception("Error calling permission API"); + } + var apiResult = await req.Content.ReadAsStringAsync(); + //return res; + + if (apiResult != null) + { + var raw = JsonConvert.DeserializeObject(apiResult); + return raw; + } + return null; + } + } + catch + { + throw; + } + } + public async Task GetPermissionOrgAPIAsync(string action, string system, string profileId) { try diff --git a/BMA.EHR.Application/Responses/Leaves/GetPermissionWithActingDto.cs b/BMA.EHR.Application/Responses/Leaves/GetPermissionWithActingDto.cs new file mode 100644 index 00000000..0050f7d1 --- /dev/null +++ b/BMA.EHR.Application/Responses/Leaves/GetPermissionWithActingDto.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace BMA.EHR.Application.Responses.Leaves +{ + public class GetPermissionWithActingDto + { + public string privilege {get; set;} = string.Empty; + public bool isAct {get; set;} = false; + public List posMasterActs {get; set;} = new(); + } + + public class ActingPermission + { + public string posNo {get; set;} = string.Empty; + public string privilege {get; set;} = string.Empty; + public Guid? rootDnaId {get; set;} + public Guid? child1DnaId {get; set;} + public Guid? child2DnaId {get; set;} + public Guid? child3DnaId {get; set;} + public Guid? child4DnaId {get; set;} + } + + public class GetPermissionWithActingResultDto + { + public int status {get; set;} = 0; + public string message {get; set;} = string.Empty; + public GetPermissionWithActingDto result {get; set;} = new(); + } +} \ No newline at end of file diff --git a/BMA.EHR.Leave/Controllers/LeaveController.cs b/BMA.EHR.Leave/Controllers/LeaveController.cs index 60841b8d..8ff74510 100644 --- a/BMA.EHR.Leave/Controllers/LeaveController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveController.cs @@ -3,8 +3,10 @@ using BMA.EHR.Application.Repositories.Commands; using BMA.EHR.Application.Repositories.Leaves.LeaveRequests; using BMA.EHR.Application.Repositories.Leaves.TimeAttendants; using BMA.EHR.Application.Repositories.MessageQueue; +using BMA.EHR.Application.Responses.Leaves; using BMA.EHR.Application.Responses.Profiles; using BMA.EHR.Domain.Common; +using BMA.EHR.Domain.Extensions; using BMA.EHR.Domain.Models.Leave.TimeAttendants; using BMA.EHR.Domain.Models.Notifications; using BMA.EHR.Domain.Shared; @@ -3162,13 +3164,14 @@ namespace BMA.EHR.Leave.Service.Controllers [ProducesResponseType(StatusCodes.Status500InternalServerError)] public async Task> GetAdditionalCheckRequestAsync([Required] int year, [Required] int month, [Required] int page = 1, [Required] int pageSize = 10, string keyword = "", string? sortBy = "", bool? descending = false) { - var getPermission = await _permission.GetPermissionAPIAsync("LIST", "SYS_CHECKIN_SPECIAL"); - var jsonData = JsonConvert.DeserializeObject(getPermission); - if (jsonData["status"]?.ToString() != "200") + var jsonData = await _permission.GetPermissionWithActingAPIAsync("LIST", "SYS_CHECKIN_SPECIAL"); + //var jsonData = JsonConvert.DeserializeObject(getPermission); + if (jsonData!.status != 200) { - return Error(jsonData["message"]?.ToString(), StatusCodes.Status403Forbidden); + return Error(jsonData.message, StatusCodes.Status403Forbidden); } - string role = jsonData["result"]?.ToString(); + //string role = jsonData["result"]?.ToString(); + string role = jsonData.result.privilege; var nodeId = string.Empty; var profileAdmin = new GetUserOCAllDto(); profileAdmin = await _userProfileRepository.GetUserOCAll(Guid.Parse(UserId!), AccessToken); @@ -3206,6 +3209,80 @@ namespace BMA.EHR.Leave.Service.Controllers //var rawData = await _additionalCheckRequestRepository.GetAdditionalCheckRequests(year, month); var rawData = await _additionalCheckRequestRepository.GetAdditionalCheckRequestsByAdminRole(year, month, role, nodeId, profileAdmin?.Node, keyword); + // ถ้ามีการรักษาการ + if (jsonData.result.isAct) + { + var posActs = jsonData.result.posMasterActs; + foreach(var act in posActs) + { + var actRole = act.privilege; + string actNodeId = string.Empty; + int? actNode; + + if (role == "NORMAL" || role == "CHILD") + { + actNodeId = act.child4DnaId != null ? + act.child4DnaId.Value.ToString("D") : + act.child3DnaId != null ? + act.child3DnaId.Value.ToString("D") : + act.child2DnaId != null ? + act.child2DnaId.Value.ToString("D") : + act.child1DnaId != null ? + act.child1DnaId.Value.ToString("D") : + act.rootDnaId != null ? + act.rootDnaId.Value.ToString("D") : + ""; + actNode = act.child4DnaId != null ? + 4 : + act.child3DnaId != null ? + 3 : + act.child2DnaId != null ? + 2 : + act.child1DnaId != null ? + 1 : + act.rootDnaId != null ? + 0 : + null; + } + else if (role == "BROTHER") + { + actNodeId = act.child3DnaId != null ? + act.child3DnaId.Value.ToString("D") : + act.child2DnaId != null ? + act.child2DnaId.Value.ToString("D") : + act.child1DnaId != null ? + act.rootDnaId!.Value.ToString("D") : + act.rootDnaId != null ? + act.rootDnaId.Value.ToString("D") : + ""; + actNode = act.child4DnaId != null ? + 4 : + act.child3DnaId != null ? + 4 : + act.child2DnaId != null ? + 3 : + act.child1DnaId != null ? + 2 : + act.rootDnaId != null ? + 0 : + null; + } + else if (role == "ROOT" /*|| role == "PARENT"*/) + { + actNodeId = act.rootDnaId!.Value.ToString("D"); + actNode = 0; + } + + var rawDataAct = await _additionalCheckRequestRepository.GetAdditionalCheckRequestsByAdminRole(year, month, actRole, actNodeId, profileAdmin?.Node, keyword); + if (rawDataAct != null) + { + if (rawData != null) + rawData = rawData.Union(rawDataAct).ToList(); + else + rawData = rawDataAct; + } + } + } var total = rawData.Count; var getDefaultRound = await _dutyTimeRepository.GetDefaultAsync(); diff --git a/BMA.EHR.Leave/Controllers/LeaveRequestController.cs b/BMA.EHR.Leave/Controllers/LeaveRequestController.cs index c2954ca4..5720bccc 100644 --- a/BMA.EHR.Leave/Controllers/LeaveRequestController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveRequestController.cs @@ -1352,14 +1352,14 @@ namespace BMA.EHR.Leave.Service.Controllers public async Task> GetLeaveRequestCalendarAdminAsync( [FromBody] GetLeaveRequestCalendarDto req) { - var getPermission = await _permission.GetPermissionAPIAsync("LIST", "SYS_LEAVE_LIST"); - var jsonData = JsonConvert.DeserializeObject(getPermission); - if (jsonData["status"]?.ToString() != "200") + var jsonData = await _permission.GetPermissionWithActingAPIAsync("LIST", "SYS_LEAVE_LIST"); + //var jsonData = JsonConvert.DeserializeObject(getPermission); + if (jsonData!.status != 200) { - return Error(jsonData["message"]?.ToString(), StatusCodes.Status403Forbidden); + return Error(jsonData.message, StatusCodes.Status403Forbidden); } - - string role = jsonData["result"]?.ToString(); + //string role = jsonData["result"]?.ToString(); + string role = jsonData.result.privilege; var nodeId = string.Empty; var profileAdmin = new GetUserOCAllDto(); profileAdmin = await _userProfileRepository.GetUserOCAll(Guid.Parse(UserId!), AccessToken); @@ -1395,6 +1395,85 @@ namespace BMA.EHR.Leave.Service.Controllers } var data = await _leaveRequestRepository.GetLeaveRequestByYearForAdminAsync(req.Year, role, nodeId, profileAdmin.Node); + + // ถ้ามีการรักษาการ + if (jsonData.result.isAct) + { + var posActs = jsonData.result.posMasterActs; + foreach(var act in posActs) + { + var actRole = act.privilege; + string actNodeId = string.Empty; + int? actNode = null; + + if (role == "NORMAL" || role == "CHILD") + { + actNodeId = act.child4DnaId != null ? + act.child4DnaId.Value.ToString("D") : + act.child3DnaId != null ? + act.child3DnaId.Value.ToString("D") : + act.child2DnaId != null ? + act.child2DnaId.Value.ToString("D") : + act.child1DnaId != null ? + act.child1DnaId.Value.ToString("D") : + act.rootDnaId != null ? + act.rootDnaId.Value.ToString("D") : + ""; + actNode = act.child4DnaId != null ? + 4 : + act.child3DnaId != null ? + 3 : + act.child2DnaId != null ? + 2 : + act.child1DnaId != null ? + 1 : + act.rootDnaId != null ? + 0 : + null; + } + else if (role == "BROTHER") + { + actNodeId = act.child3DnaId != null ? + act.child3DnaId.Value.ToString("D") : + act.child2DnaId != null ? + act.child2DnaId.Value.ToString("D") : + act.child1DnaId != null ? + act.rootDnaId!.Value.ToString("D") : + act.rootDnaId != null ? + act.rootDnaId.Value.ToString("D") : + ""; + actNode = act.child4DnaId != null ? + 4 : + act.child3DnaId != null ? + 4 : + act.child2DnaId != null ? + 3 : + act.child1DnaId != null ? + 2 : + act.rootDnaId != null ? + 0 : + null; + } + else if (role == "ROOT" /*|| role == "PARENT"*/) + { + actNodeId = act.rootDnaId!.Value.ToString("D"); + actNode = 0; + } + + var rawDataAct = await _leaveRequestRepository.GetLeaveRequestByYearForAdminAsync(req.Year, actRole, actNodeId, actNode); + if (rawDataAct != null) + { + if (data != null) + data = data.Union(rawDataAct).ToList(); + else + data = rawDataAct; + } + } + } + + + + var resultData = (from d in data //join p in profileList on d.KeycloakUserId equals p.Keycloak select new GetLeaveRequestCalendarResultDto @@ -1750,14 +1829,14 @@ namespace BMA.EHR.Leave.Service.Controllers public async Task> GetLeaveRequestForAdminAsync( [FromBody] GetLeaveRequestForAdminDto req) { - var getPermission = await _permission.GetPermissionAPIAsync("LIST", "SYS_LEAVE_LIST"); - var jsonData = JsonConvert.DeserializeObject(getPermission); - if (jsonData["status"]?.ToString() != "200") + var jsonData = await _permission.GetPermissionWithActingAPIAsync("LIST", "SYS_LEAVE_LIST"); + //var jsonData = JsonConvert.DeserializeObject(getPermission); + if (jsonData!.status != 200) { - return Error(jsonData["message"]?.ToString(), StatusCodes.Status403Forbidden); + return Error(jsonData.message, StatusCodes.Status403Forbidden); } - - string role = jsonData["result"]?.ToString(); + //string role = jsonData["result"]?.ToString(); + string role = jsonData.result.privilege; var nodeId = string.Empty; var profileAdmin = new GetUserOCAllDto(); profileAdmin = await _userProfileRepository.GetUserOCAll(Guid.Parse(UserId!), AccessToken); @@ -1794,6 +1873,83 @@ namespace BMA.EHR.Leave.Service.Controllers var rawData = await _leaveRequestRepository.GetListLeaveRequestForAdminAsync(req.Year, req.Type, req.Status, req.StartDate, req.EndDate, role, nodeId, profileAdmin?.Node); + + // ถ้ามีการรักษาการ + if (jsonData.result.isAct) + { + var posActs = jsonData.result.posMasterActs; + foreach(var act in posActs) + { + var actRole = act.privilege; + string actNodeId = string.Empty; + int? actNode = null; + + if (role == "NORMAL" || role == "CHILD") + { + actNodeId = act.child4DnaId != null ? + act.child4DnaId.Value.ToString("D") : + act.child3DnaId != null ? + act.child3DnaId.Value.ToString("D") : + act.child2DnaId != null ? + act.child2DnaId.Value.ToString("D") : + act.child1DnaId != null ? + act.child1DnaId.Value.ToString("D") : + act.rootDnaId != null ? + act.rootDnaId.Value.ToString("D") : + ""; + actNode = act.child4DnaId != null ? + 4 : + act.child3DnaId != null ? + 3 : + act.child2DnaId != null ? + 2 : + act.child1DnaId != null ? + 1 : + act.rootDnaId != null ? + 0 : + null; + } + else if (role == "BROTHER") + { + actNodeId = act.child3DnaId != null ? + act.child3DnaId.Value.ToString("D") : + act.child2DnaId != null ? + act.child2DnaId.Value.ToString("D") : + act.child1DnaId != null ? + act.rootDnaId!.Value.ToString("D") : + act.rootDnaId != null ? + act.rootDnaId.Value.ToString("D") : + ""; + actNode = act.child4DnaId != null ? + 4 : + act.child3DnaId != null ? + 4 : + act.child2DnaId != null ? + 3 : + act.child1DnaId != null ? + 2 : + act.rootDnaId != null ? + 0 : + null; + } + else if (role == "ROOT" /*|| role == "PARENT"*/) + { + actNodeId = act.rootDnaId!.Value.ToString("D"); + actNode = 0; + } + + var rawDataAct = await _leaveRequestRepository.GetListLeaveRequestForAdminAsync(req.Year, req.Type, req.Status, req.StartDate, req.EndDate, actRole, actNodeId, actNode); + if (rawDataAct != null) + { + if (rawData != null) + rawData = rawData.Union(rawDataAct).ToList(); + else + rawData = rawDataAct; + } + } + } + + var result = new List(); foreach (var item in rawData) @@ -1976,14 +2132,14 @@ namespace BMA.EHR.Leave.Service.Controllers public async Task> GetCancelLeaveRequestForAdminAsync( [FromBody] GetLeaveRequestForAdminDto req) { - var getPermission = await _permission.GetPermissionAPIAsync("LIST", "SYS_LEAVE_LIST"); - var jsonData = JsonConvert.DeserializeObject(getPermission); - if (jsonData["status"]?.ToString() != "200") + var jsonData = await _permission.GetPermissionWithActingAPIAsync("LIST", "SYS_LEAVE_LIST"); + //var jsonData = JsonConvert.DeserializeObject(getPermission); + if (jsonData!.status != 200) { - return Error(jsonData["message"]?.ToString(), StatusCodes.Status403Forbidden); + return Error(jsonData.message, StatusCodes.Status403Forbidden); } - - string role = jsonData["result"]?.ToString(); + //string role = jsonData["result"]?.ToString(); + string role = jsonData.result.privilege; var nodeId = string.Empty; var profileAdmin = new GetUserOCAllDto(); profileAdmin = await _userProfileRepository.GetUserOCAll(Guid.Parse(UserId!), AccessToken); @@ -2021,6 +2177,82 @@ namespace BMA.EHR.Leave.Service.Controllers var rawData = await _leaveRequestRepository.GetCancelLeaveRequestForAdminAsync(req.Year, req.Type, req.Status, role, nodeId, profileAdmin?.Node); + // ถ้ามีการรักษาการ + if (jsonData.result.isAct) + { + var posActs = jsonData.result.posMasterActs; + foreach(var act in posActs) + { + var actRole = act.privilege; + string actNodeId = string.Empty; + int? actNode = null; + + if (role == "NORMAL" || role == "CHILD") + { + actNodeId = act.child4DnaId != null ? + act.child4DnaId.Value.ToString("D") : + act.child3DnaId != null ? + act.child3DnaId.Value.ToString("D") : + act.child2DnaId != null ? + act.child2DnaId.Value.ToString("D") : + act.child1DnaId != null ? + act.child1DnaId.Value.ToString("D") : + act.rootDnaId != null ? + act.rootDnaId.Value.ToString("D") : + ""; + actNode = act.child4DnaId != null ? + 4 : + act.child3DnaId != null ? + 3 : + act.child2DnaId != null ? + 2 : + act.child1DnaId != null ? + 1 : + act.rootDnaId != null ? + 0 : + null; + } + else if (role == "BROTHER") + { + actNodeId = act.child3DnaId != null ? + act.child3DnaId.Value.ToString("D") : + act.child2DnaId != null ? + act.child2DnaId.Value.ToString("D") : + act.child1DnaId != null ? + act.rootDnaId!.Value.ToString("D") : + act.rootDnaId != null ? + act.rootDnaId.Value.ToString("D") : + ""; + actNode = act.child4DnaId != null ? + 4 : + act.child3DnaId != null ? + 4 : + act.child2DnaId != null ? + 3 : + act.child1DnaId != null ? + 2 : + act.rootDnaId != null ? + 0 : + null; + } + else if (role == "ROOT" /*|| role == "PARENT"*/) + { + actNodeId = act.rootDnaId!.Value.ToString("D"); + actNode = 0; + } + + var rawDataAct = await _leaveRequestRepository.GetCancelLeaveRequestForAdminAsync(req.Year, req.Type, req.Status, actRole, actNodeId, actNode); + if (rawDataAct != null) + { + if (rawData != null) + rawData = rawData.Union(rawDataAct).ToList(); + else + rawData = rawDataAct; + } + } + } + + var recCount = rawData.Count; if (req.Keyword != "") From ee2d16925a380476b13cbaf9fb28fc837acf7a53 Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Fri, 17 Apr 2026 19:21:30 +0700 Subject: [PATCH 177/178] =?UTF-8?q?=E0=B9=81=E0=B8=81=E0=B9=89=E0=B9=84?= =?UTF-8?q?=E0=B8=82=20=E0=B8=A3=E0=B8=B1=E0=B8=81=E0=B8=A9=E0=B8=B2?= =?UTF-8?q?=E0=B8=81=E0=B8=B2=E0=B8=A3=20=E0=B8=96=E0=B9=89=E0=B8=B2?= =?UTF-8?q?=E0=B9=84=E0=B8=A1=E0=B9=88=E0=B9=84=E0=B8=94=E0=B9=89=E0=B9=83?= =?UTF-8?q?=E0=B8=AA=E0=B9=88=20privilage=20=E0=B8=A1=E0=B8=B2=E0=B8=88?= =?UTF-8?q?=E0=B8=B0=E0=B9=83=E0=B8=AB=E0=B9=89=20default=20=3D=20"PARENT"?= =?UTF-8?q?=20#2431?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Leaves/GetPermissionWithActingDto.cs | 7 ++++- BMA.EHR.Domain/Shared/PrivilegeConverter.cs | 30 +++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 BMA.EHR.Domain/Shared/PrivilegeConverter.cs diff --git a/BMA.EHR.Application/Responses/Leaves/GetPermissionWithActingDto.cs b/BMA.EHR.Application/Responses/Leaves/GetPermissionWithActingDto.cs index 0050f7d1..dc4cbdb9 100644 --- a/BMA.EHR.Application/Responses/Leaves/GetPermissionWithActingDto.cs +++ b/BMA.EHR.Application/Responses/Leaves/GetPermissionWithActingDto.cs @@ -2,6 +2,8 @@ using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using BMA.EHR.Domain.Shared; +using Newtonsoft.Json; namespace BMA.EHR.Application.Responses.Leaves { @@ -15,7 +17,10 @@ namespace BMA.EHR.Application.Responses.Leaves public class ActingPermission { public string posNo {get; set;} = string.Empty; - public string privilege {get; set;} = string.Empty; + //public string? privilege {get; set;} = "PARENT"; + [JsonConverter(typeof(PrivilegeConverter))] + public string privilege {get; set;} = "PARENT"; + public Guid? rootDnaId {get; set;} public Guid? child1DnaId {get; set;} public Guid? child2DnaId {get; set;} diff --git a/BMA.EHR.Domain/Shared/PrivilegeConverter.cs b/BMA.EHR.Domain/Shared/PrivilegeConverter.cs new file mode 100644 index 00000000..fa806658 --- /dev/null +++ b/BMA.EHR.Domain/Shared/PrivilegeConverter.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Newtonsoft.Json; + +namespace BMA.EHR.Domain.Shared +{ + public class PrivilegeConverter : JsonConverter +{ + public override bool CanConvert(Type objectType) + { + return objectType == typeof(string); + } + + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + if (reader.TokenType == JsonToken.Null) + { + return "PARENT"; + } + return reader.Value; + } + + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + writer.WriteValue(value); + } +} +} \ No newline at end of file From 058027ea294044ccf02542d13dcfebdca383469c Mon Sep 17 00:00:00 2001 From: Suphonchai Phoonsawat Date: Fri, 17 Apr 2026 20:11:18 +0700 Subject: [PATCH 178/178] =?UTF-8?q?=E0=B9=80=E0=B8=9B=E0=B8=A5=E0=B8=B5?= =?UTF-8?q?=E0=B9=88=E0=B8=A2=E0=B8=99=20DEFAULT=20=E0=B8=AA=E0=B8=B4?= =?UTF-8?q?=E0=B8=97=E0=B8=98=E0=B8=B4=E0=B9=8C=E0=B9=80=E0=B8=9B=E0=B9=87?= =?UTF-8?q?=E0=B8=99=20"CHILD"=20=E0=B8=81=E0=B8=A3=E0=B8=93=E0=B8=B5?= =?UTF-8?q?=E0=B9=84=E0=B8=A1=E0=B9=88=E0=B8=95=E0=B8=B1=E0=B9=89=E0=B8=87?= =?UTF-8?q?=E0=B8=84=E0=B9=88=E0=B8=B2=E0=B8=A1=E0=B8=B2=20#2431?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Responses/Leaves/GetPermissionWithActingDto.cs | 2 +- BMA.EHR.Domain/Shared/PrivilegeConverter.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/BMA.EHR.Application/Responses/Leaves/GetPermissionWithActingDto.cs b/BMA.EHR.Application/Responses/Leaves/GetPermissionWithActingDto.cs index dc4cbdb9..083c4b20 100644 --- a/BMA.EHR.Application/Responses/Leaves/GetPermissionWithActingDto.cs +++ b/BMA.EHR.Application/Responses/Leaves/GetPermissionWithActingDto.cs @@ -19,7 +19,7 @@ namespace BMA.EHR.Application.Responses.Leaves public string posNo {get; set;} = string.Empty; //public string? privilege {get; set;} = "PARENT"; [JsonConverter(typeof(PrivilegeConverter))] - public string privilege {get; set;} = "PARENT"; + public string privilege {get; set;} = "CHILD"; public Guid? rootDnaId {get; set;} public Guid? child1DnaId {get; set;} diff --git a/BMA.EHR.Domain/Shared/PrivilegeConverter.cs b/BMA.EHR.Domain/Shared/PrivilegeConverter.cs index fa806658..4dc7fd85 100644 --- a/BMA.EHR.Domain/Shared/PrivilegeConverter.cs +++ b/BMA.EHR.Domain/Shared/PrivilegeConverter.cs @@ -17,7 +17,7 @@ namespace BMA.EHR.Domain.Shared { if (reader.TokenType == JsonToken.Null) { - return "PARENT"; + return "CHILD"; } return reader.Value; }