Compare commits

...

20 Commits

Author SHA1 Message Date
rowell_m_soriano
227ebe3906 adding variables via gitea variables
All checks were successful
Build and Deploy CPRNIMS / build-and-deploy (push) Successful in 3m36s
2026-06-29 16:21:34 +08:00
rowell_m_soriano
6b28e00102 fix: explicit exit 0 after robocopy success to prevent false step failures
All checks were successful
Build and Deploy CPRNIMS / build-and-deploy (push) Successful in 2m58s
2026-06-29 15:40:02 +08:00
rowell_m_soriano
34097298e0 Update Gitea CI/CD deployment workflow
Some checks failed
Build and Deploy CPRNIMS / build-and-deploy (push) Failing after 2m32s
2026-06-29 14:29:55 +08:00
rowell_m_soriano
2aa375cf55 Update Gitea CI/CD deployment workflow
Some checks are pending
Build and Deploy CPRNIMS / build-and-deploy (push) Waiting to run
2026-06-29 14:04:46 +08:00
rowell_m_soriano
d6c2d668ee Update Gitea CI/CD deployment workflow
Some checks are pending
Build and Deploy CPRNIMS / build-and-deploy (push) Waiting to run
2026-06-29 13:51:54 +08:00
rowell_m_soriano
8fdbe48640 Remove bin/obj/.vs build artifacts from tracking, apply .gitignore
Some checks are pending
Build and Deploy CPRNIMS / build-and-deploy (push) Waiting to run
2026-06-29 07:03:51 +08:00
rowell_m_soriano
12407aeaf5 Remove bin/obj/.vs build artifacts from tracking, apply .gitignore 2026-06-25 17:26:55 +08:00
rowell_m_soriano
84ba85490c Add .gitignore, remove build artifacts from tracking 2026-06-25 17:15:47 +08:00
rowell_m_soriano
c9fc25832f Add .gitignore and remove build artifacts from tracking 2026-06-25 16:43:25 +08:00
rowell_m_soriano
3d82fb67ba CICD deploy.yml 2026-06-25 16:05:33 +08:00
rowell_m_soriano
3c0be5eae2 CRUD for RIS, MRS, Inventory reports are working properly 2026-06-25 08:57:30 +08:00
rowell_m_soriano
440cdfcdb7 Features for option to select currency for each PO during PO Creation 2026-06-24 10:16:45 +08:00
rowell_m_soriano
fa03ef5a3d Full CRUD with RIS, MRS, Inventory including reports 2026-06-18 16:51:31 +08:00
rowell_m_soriano
44862d01b5 RIS creation, approval and Cancel working well 2026-06-15 16:41:50 +08:00
rowell_m_soriano
6710f04bd7 fixing bugs in custom po for unit price consistency 2026-05-21 13:55:15 +08:00
rowell_m_soriano
f2aee9645d Enhanced Shipping instructions and Custom PO for import 2026-05-20 16:50:48 +08:00
rowell_m_soriano
5c6fc08bf3 Roll back the Shipping instruction 2026-05-12 15:48:42 +08:00
rowell_m_soriano
67a501f4fc Fixing status bug in Supplier page 2026-05-05 13:03:52 +08:00
rowell_m_soriano
b02af975ac Adding validaiton for existing suppliers to prevent from duplication 2026-04-27 09:18:38 +08:00
rowell_m_soriano
d1c9c4b52b AI Driven Searching and tagging with button and UI/UX enhancement in canvassing partial only 2026-04-22 17:21:45 +08:00
278 changed files with 26207 additions and 8951 deletions

211
.gitea/workflows/deploy.yml Normal file
View File

@ -0,0 +1,211 @@
name: Build and Deploy CPRNIMS
on:
push:
branches: [ main ]
jobs:
build-and-deploy:
runs-on: windows
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Clean previous publish output
shell: pwsh
run: |
Remove-Item -Recurse -Force "C:\ci-output\webapi" -ErrorAction SilentlyContinue
Remove-Item -Recurse -Force "C:\ci-output\webapps" -ErrorAction SilentlyContinue
- name: Publish WebApi
shell: pwsh
run: dotnet publish .\CPRNIMS.WebApi\CPRNIMS.WebApi.csproj -c Release -o C:\ci-output\webapi
- name: Publish WebApps
shell: pwsh
run: dotnet publish .\CPRNIMS.WebApps\CPRNIMS.WebApps.csproj -c Release -o C:\ci-output\webapps
# ---- Generate production config from Gitea Actions secrets (never committed to git) ----
- name: Write production appsettings - WebApi
shell: pwsh
env:
TAVILY_KEY: ${{ secrets.LLI_NON_INVENTORY_PROD_TAVILY_API_KEY }}
GROQ_KEY: ${{ secrets.LLI_NON_INVENTORY_PROD_GROQ_API_KEY }}
JWT_SECRET: ${{ secrets.LLI_NON_INVENTORY_PROD_JWT_SECRET }}
DB_CONN: ${{ secrets.LLI_NON_INVENTORY_PROD_DB_CONNECTION }}
LOCALPURCH_CONN: ${{ secrets.LLI_NON_INVENTORY_PROD_DB_LOCALPURCH_CONNECTION }}
run: |
$config = @{
Tavily = @{
ApiKey = $env:TAVILY_KEY
SearchUrl = "https://api.tavily.com/search"
}
Groq = @{
ApiKey = $env:GROQ_KEY
ApiUrl = "https://api.groq.com/openai/v1/chat/completions"
Model = "llama-3.1-8b-instant"
}
JWT = @{
ValidAudience = "https://lloydwebapi.lloydlab.com:2021"
ValidIssuer = "https://lloydwebapi.lloydlab.com:2021"
Secret = $env:JWT_SECRET
}
WebEndPoint = @{
ForgotPassword = "https://llipurchasingnoninventory.com:8080/"
SupplierForm = "https://llipurchasingnoninventory.com:8083/"
}
ConnectionStrings = @{
DefaultConnection = $env:DB_CONN
LocalPurchConn = $env:LOCALPURCH_CONN
}
}
$json = $config | ConvertTo-Json -Depth 5
$json | Out-File -FilePath "C:\ci-output\webapi\appsettings.Production.json" -Encoding utf8
Write-Host "Wrote appsettings.Production.json to webapi output (values masked in this log automatically)"
exit 0
# ---- Generate production config for WebApps (uses Variables, not Secrets, since BaseUrl isn't sensitive) ----
- name: Write production appsettings - WebApps
shell: pwsh
env:
API_BASE_URL: ${{ vars.LLI_NON_INVENTORY_PROD_API_BASE_URL }}
run: |
$config = @{
CommonEndpoints = @{
ApiDefaultHeaders = @{
BaseUrl = $env:API_BASE_URL
ESignaturePath = "https://llipurchasingnoninventory.com:8080/Content/Images/Signatures/"
ItemImages = "https://llipurchasingnoninventory.com:8080/content/images/"
ContentTypeMedia = "application/json"
Authorization = "token"
ErrorMessage = "api/ErrorLogs/ErrorMessage/"
}
}
}
$json = $config | ConvertTo-Json -Depth 5
$json | Out-File -FilePath "C:\ci-output\webapps\appsettings.Production.json" -Encoding utf8
Write-Host "Wrote appsettings.Production.json to webapps output"
exit 0
# ---- Backup current live deployment before touching anything ----
- name: Backup current live files
shell: pwsh
run: |
$stamp = Get-Date -Format "yyyyMMdd-HHmmss"
New-Item -ItemType Directory -Force -Path "C:\backups\$stamp" | Out-Null
# Mirror current live folders into the backup location (only if they exist / aren't empty)
if (Test-Path "C:\inetpub\cprnims-api") {
robocopy "C:\inetpub\cprnims-api" "C:\backups\$stamp\webapi" /MIR /R:2 /W:3 | Out-Null
}
if (Test-Path "C:\inetpub\cprnims-web") {
robocopy "C:\inetpub\cprnims-web" "C:\backups\$stamp\webapps" /MIR /R:2 /W:3 | Out-Null
}
# Record this backup's timestamp so later steps know where it lives
$stamp | Out-File -FilePath "C:\backups\latest.txt" -Encoding ascii -NoNewline
# Keep only the last 5 backups to avoid filling the disk
$all = Get-ChildItem "C:\backups" -Directory | Sort-Object Name -Descending
if ($all.Count -gt 5) {
$all | Select-Object -Skip 5 | Remove-Item -Recurse -Force
}
Write-Host "Backed up current deployment to C:\backups\$stamp"
exit 0
- name: Stop app pools
shell: pwsh
run: |
Import-Module WebAdministration
Stop-WebAppPool -Name "CPRNIMS-Api" -ErrorAction SilentlyContinue
Stop-WebAppPool -Name "CPRNIMS-Web" -ErrorAction SilentlyContinue
Start-Sleep -Seconds 3
- name: Deploy WebApi files
id: deploy_api
shell: pwsh
run: |
robocopy "C:\ci-output\webapi" "C:\inetpub\cprnims-api" /MIR /R:3 /W:5
$rc = $LASTEXITCODE
Write-Host "ROBOCOPY EXIT CODE: $rc"
if ($rc -ge 8) {
throw "robocopy failed for WebApi with exit code $rc"
}
exit 0
- name: Deploy WebApps files
id: deploy_web
shell: pwsh
run: |
robocopy "C:\ci-output\webapps" "C:\inetpub\cprnims-web" /MIR /R:3 /W:5
$rc = $LASTEXITCODE
Write-Host "ROBOCOPY EXIT CODE: $rc"
if ($rc -ge 8) {
throw "robocopy failed for WebApps with exit code $rc"
}
exit 0
- name: Start app pools
shell: pwsh
run: |
Import-Module WebAdministration
Start-WebAppPool -Name "CPRNIMS-Api"
Start-WebAppPool -Name "CPRNIMS-Web"
- name: Verify app pools are running
shell: pwsh
run: |
Start-Sleep -Seconds 3
Import-Module WebAdministration
$api = Get-WebAppPoolState -Name "CPRNIMS-Api"
$web = Get-WebAppPoolState -Name "CPRNIMS-Web"
Write-Host "CPRNIMS-Api: $($api.Value)"
Write-Host "CPRNIMS-Web: $($web.Value)"
if ($api.Value -ne "Started" -or $web.Value -ne "Started") {
throw "One or more app pools failed to start"
}
# ---- Rollback path: only runs if any prior step in this job failed ----
- name: ROLLBACK - restore previous backup
if: failure()
shell: pwsh
run: |
$stamp = Get-Content "C:\backups\latest.txt" -Raw
$backupPath = "C:\backups\$stamp"
Write-Host "Deployment failed - rolling back to backup: $backupPath"
Import-Module WebAdministration
Stop-WebAppPool -Name "CPRNIMS-Api" -ErrorAction SilentlyContinue
Stop-WebAppPool -Name "CPRNIMS-Web" -ErrorAction SilentlyContinue
Start-Sleep -Seconds 3
if (Test-Path "$backupPath\webapi") {
robocopy "$backupPath\webapi" "C:\inetpub\cprnims-api" /MIR /R:3 /W:5 | Out-Null
}
if (Test-Path "$backupPath\webapps") {
robocopy "$backupPath\webapps" "C:\inetpub\cprnims-web" /MIR /R:3 /W:5 | Out-Null
}
Start-WebAppPool -Name "CPRNIMS-Api"
Start-WebAppPool -Name "CPRNIMS-Web"
Write-Host "Rollback complete. Restored from $backupPath"
exit 0
- name: ROLLBACK - verify pools after restore
if: failure()
shell: pwsh
run: |
Start-Sleep -Seconds 3
Import-Module WebAdministration
$api = Get-WebAppPoolState -Name "CPRNIMS-Api"
$web = Get-WebAppPoolState -Name "CPRNIMS-Web"
Write-Host "After rollback - CPRNIMS-Api: $($api.Value)"
Write-Host "After rollback - CPRNIMS-Web: $($web.Value)"
if ($api.Value -ne "Started" -or $web.Value -ne "Started") {
Write-Host "WARNING: app pools still not running after rollback. Manual intervention needed."
}

48
.gitignore vendored Normal file
View File

@ -0,0 +1,48 @@
# Build output
[Bb]in/
[Oo]bj/
[Dd]ebug/
[Rr]elease/
x64/
x86/
build/
bld/
# Visual Studio
.vs/
*.user
*.suo
*.userosscache
*.sln.docstates
*.userprefs
# Publish output
publish/
*.publishsettings
PublishScripts/
# NuGet
*.nupkg
*.snupkg
packages/
!packages/build/
project.lock.json
project.fragment.lock.json
artifacts/
# Rider / VS Code (if anyone uses them)
.idea/
.vscode/
# Build results / logs
*.binlog
*.log
msbuild.log
msbuild.err
# Environment-specific config with secrets
appsettings.*.json
!appsettings.json
*.dev.json
secrets.json
*.pfx

Binary file not shown.

View File

@ -1,12 +0,0 @@
{
"Version": 1,
"WorkspaceRootPath": "D:\\sourcecode\\NonInventPurchasing\\",
"Documents": [],
"DocumentGroupContainers": [
{
"Orientation": 0,
"VerticalTabListWidth": 256,
"DocumentGroups": []
}
]
}

View File

@ -1,6 +0,0 @@
{
"ExpandedNodes": [
""
],
"PreviewInSolutionExplorer": false
}

View File

@ -10,6 +10,9 @@
<PackageReference Include="AutoMapper" Version="16.1.1" /> <PackageReference Include="AutoMapper" Version="16.1.1" />
<PackageReference Include="CaptchaGen.NetCore" Version="1.1.2" /> <PackageReference Include="CaptchaGen.NetCore" Version="1.1.2" />
<PackageReference Include="Dapper" Version="2.1.66" /> <PackageReference Include="Dapper" Version="2.1.66" />
<PackageReference Include="FastReport.OpenSource" Version="2026.2.3" />
<PackageReference Include="FastReport.OpenSource.Export.PdfSimple" Version="2026.2.3" />
<PackageReference Include="FastReport.OpenSource.Web" Version="2026.2.3" />
<PackageReference Include="Google.Apis.Drive.v3" Version="1.67.0.3373" /> <PackageReference Include="Google.Apis.Drive.v3" Version="1.67.0.3373" />
<PackageReference Include="Microsoft.AspNet.Identity.Core" Version="2.2.4" /> <PackageReference Include="Microsoft.AspNet.Identity.Core" Version="2.2.4" />
<PackageReference Include="Microsoft.AspNetCore.SignalR.Core" Version="1.2.0" /> <PackageReference Include="Microsoft.AspNetCore.SignalR.Core" Version="1.2.0" />

View File

@ -1,9 +1,12 @@
using CPRNIMS.Domain.Services.ICanvass; using CPRNIMS.Domain.Services;
using CPRNIMS.Domain.Services.ICanvass;
using CPRNIMS.Infrastructure.Dto.Canvass; using CPRNIMS.Infrastructure.Dto.Canvass;
using CPRNIMS.Infrastructure.Dto.Canvass.Request; using CPRNIMS.Infrastructure.Dto.Canvass.Request;
using CPRNIMS.Infrastructure.Dto.Canvass.Response; using CPRNIMS.Infrastructure.Dto.Canvass.Response;
using CPRNIMS.Infrastructure.Dto.Common;
using CPRNIMS.Infrastructure.Entities.Canvass; using CPRNIMS.Infrastructure.Entities.Canvass;
using CPRNIMS.Infrastructure.Entities.Purchasing; using CPRNIMS.Infrastructure.Entities.Purchasing;
using CPRNIMS.Infrastructure.ViewModel.Canvass;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
@ -15,9 +18,9 @@ namespace CPRNIMS.Domain.Contracts.Canvass
public interface ICanvass : ISupplier public interface ICanvass : ISupplier
{ {
#region Post Put #region Post Put
Task<RFQ> PostPerSupplierToken(CanvassDto CanvassDto); Task<Result<StartCanvassResponse>> StartCanvass(CanvassVM canvass,CancellationToken ct);
Task<RFQ> PostPerSupplierToken(ForCanvassDto CanvassDto);
Task<ForCanvassFollowUp> PutSupplierCanvass(long canvassSupplierId); Task<ForCanvassFollowUp> PutSupplierCanvass(long canvassSupplierId);
Task<PRCanvassDetail> PostCanvass(CanvassDto CanvassDto);
Task<SupplierResponse> PostPutSupplier(CanvassDto CanvassDto); Task<SupplierResponse> PostPutSupplier(CanvassDto CanvassDto);
Task<SupplierResponse> PostTaggingSupplier(CanvassDto CanvassDto); Task<SupplierResponse> PostTaggingSupplier(CanvassDto CanvassDto);
Task<SupplierResponse> PostApprovedSupp(CanvassDto CanvassDto); Task<SupplierResponse> PostApprovedSupp(CanvassDto CanvassDto);
@ -32,16 +35,17 @@ namespace CPRNIMS.Domain.Contracts.Canvass
Task<List<PRCanvassDetail>> GetCanvassById(CanvassDto CanvassDto); Task<List<PRCanvassDetail>> GetCanvassById(CanvassDto CanvassDto);
Task<List<WOResponse>> GetCanvassWOResponse(CanvassDto CanvassDto); Task<List<WOResponse>> GetCanvassWOResponse(CanvassDto CanvassDto);
Task<List<WOResponseById>> GetWOResponseBySuppId(CanvassDto CanvassDto); Task<List<WOResponseById>> GetWOResponseBySuppId(CanvassDto CanvassDto);
Task<List<SupplierResponse>> GetSupplierById(CanvassDto CanvassDto); Task<List<SupplierResponseDto>> GetSupplierById(CanvassDto CanvassDto);
Task<List<RFQReference>> GetRFQ(CanvassDto CanvassDto); Task<List<RFQReference>> GetRFQ(ForCanvassDto CanvassDto);
Task<List<BiddingItem>> GetSupplierBid(CanvassDto CanvassDto); Task<List<BiddingItem>> GetSupplierBid(CanvassDto CanvassDto);
Task<List<RFQPerSupplier>> GetSupplierBidByItem(CanvassDto CanvassDto); Task<List<RFQPerSupplier>> GetSupplierBidByItem(CanvassDto CanvassDto);
Task<List<SupplierBidById>> GetSupplierBidById(CanvassDto CanvassDto); Task<List<SupplierBidById>> GetSupplierBidById(CanvassDto CanvassDto);
Task<List<PerSupplier>> GetCanvassPerSupplier(CanvassDto CanvassDto); Task<PagedResult<PerSupplier>> GetCanvassPerSupplier(CanvassDto dto);
Task<PagedResult<ItemsForTagging>> GetItemsForTagging(CanvassDto dto);
Task<List<PRCanvassDetail>> GetCanvassPerSupplierEmail(CanvassDto CanvassDto); Task<List<PRCanvassDetail>> GetCanvassPerSupplierEmail(CanvassDto CanvassDto);
Task<List<PRCanvassDetail>> GetCanvassPerSupplierId(CanvassDto itemCodeDto); Task<List<PRCanvassDetail>> GetCanvassPerSupplierId(CanvassDto itemCodeDto);
Task<List<ItemListWOEmail>> GetItemSupplierWOEmail(CanvassDto CanvassDto); Task<List<ItemListWOEmail>> GetItemSupplierWOEmail(CanvassDto CanvassDto);
Task<List<SupplierResponse>> GetSupplierItemWOEmail(CanvassDto CanvassDto); Task<List<SupplierResponseDto>> GetSupplierItemWOEmail(CanvassDto CanvassDto);
Task<List<PRCanvassDetail>> GetCanvassByPRNo(CanvassDto CanvassDto); Task<List<PRCanvassDetail>> GetCanvassByPRNo(CanvassDto CanvassDto);
Task<List<CanvassGroupByPRNo>> GetCanvassGroupByPRNo(CanvassDto CanvassDto); Task<List<CanvassGroupByPRNo>> GetCanvassGroupByPRNo(CanvassDto CanvassDto);
Task<List<PRCanvassDetail>> GetCanvassByItemNo(CanvassDto CanvassDto); Task<List<PRCanvassDetail>> GetCanvassByItemNo(CanvassDto CanvassDto);
@ -51,7 +55,7 @@ namespace CPRNIMS.Domain.Contracts.Canvass
Task<List<ForCanvass>> GetForCanvassPerItem(CanvassDto CanvassDto); Task<List<ForCanvass>> GetForCanvassPerItem(CanvassDto CanvassDto);
Task<int> GetCanvassNo(); Task<int> GetCanvassNo();
Task<List<ForCanvassFollowUp>> GetCanvassForFollowUp(CanvassDto itemDto); Task<List<ForCanvassFollowUp>> GetCanvassForFollowUp(CanvassDto itemDto);
Task<List<SupplierResponse>> GetMySuppliers(CanvassDto CanvassDto); Task<List<SupplierResponseDto>> GetMySuppliers(CanvassDto CanvassDto);
Task<List<MyPRWOCanvass>> GetMyPRWOCanvass(CanvassDto itemDto); Task<List<MyPRWOCanvass>> GetMyPRWOCanvass(CanvassDto itemDto);
Task<List<AlternativeOfferDetails>> GetAlternativeOfferByPRDetailId(CanvassDto itemDto); Task<List<AlternativeOfferDetails>> GetAlternativeOfferByPRDetailId(CanvassDto itemDto);
Task<List<AllForCanvass>> GetAllForCanvass(); Task<List<AllForCanvass>> GetAllForCanvass();

View File

@ -1,5 +1,6 @@
using CPRNIMS.Infrastructure.Dto.Canvass.Request; using CPRNIMS.Infrastructure.Dto.Canvass.Request;
using CPRNIMS.Infrastructure.Dto.Canvass.Response; using CPRNIMS.Infrastructure.Dto.Canvass.Response;
using CPRNIMS.Infrastructure.Entities.Canvass;
using CPRNIMS.Infrastructure.Entities.Purchasing; using CPRNIMS.Infrastructure.Entities.Purchasing;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
@ -11,9 +12,12 @@ namespace CPRNIMS.Domain.Services.ICanvass
{ {
public interface ISupplier public interface ISupplier
{ {
Task<List<ItemWithoutSupplier>> GetItemWithoutSupplier(); Task<List<Suppliers>> GetSuppliers(CancellationToken ct);
Task<Result<SupplierResponse>> PostSupplierAsync(SupplierRequest request, CancellationToken ct); Task<List<ForAISearchingTagging>> GetForAISearchingTagging(CancellationToken ct);
Task SendRFQ(SupplierEmailRequest supplierEmailRequest); Task<List<ItemWithoutSupplier>> GetItemWithoutSupplier(CancellationToken ct);
Task<bool> SearchingUpdate(long pRDetailsId); Task<List<SupplierForCanvass>> GetSupplierForCanvass(int supplierId, string userName,CancellationToken ct);
Task<Result<SupplierResponse>> PostPutSupplierAsync(SupplierRequest request, CancellationToken ct);
Task<bool> SendRFQ(SupplierEmailRequest supplierEmailRequest);
Task DeleteAsync(long pRDetailsId, CancellationToken ct);
} }
} }

View File

@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CPRNIMS.Domain.Contracts.Common
{
public interface ITransactionFacade
{
Task<T> ExecuteAsync<T>(Func<Task<T>> operation,CancellationToken ct);
Task ExecuteAsync(Func<Task> operation,CancellationToken ct);
}
}

View File

@ -13,6 +13,6 @@ namespace CPRNIMS.Domain.Contracts.Finance
{ {
Task<List<ForPayment>> GetAllClosedPO(RRDetailsDto itemDto); Task<List<ForPayment>> GetAllClosedPO(RRDetailsDto itemDto);
Task<List<ReceivingDetail>> GetRRDetailByPO(RRDetailsDto itemDto); Task<List<ReceivingDetail>> GetRRDetailByPO(RRDetailsDto itemDto);
Task<RRDetail> PostPutPayment(RRDetailsDto itemDto); Task<RRDetailDto> PostPutPayment(RRDetailsDto itemDto);
} }
} }

View File

@ -1,4 +1,6 @@
using CPRNIMS.Infrastructure.Dto.Inventory; using CPRNIMS.Infrastructure.Dto.Inventory;
using CPRNIMS.Infrastructure.Dto.Inventory.Request;
using CPRNIMS.Infrastructure.Dto.Inventory.Response;
using CPRNIMS.Infrastructure.Entities.Inventory; using CPRNIMS.Infrastructure.Entities.Inventory;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
@ -11,11 +13,15 @@ namespace CPRNIMS.Domain.Contracts.Inventory
public interface IInventory public interface IInventory
{ {
Task<List<Lot>> GetLotNo(InventoryDto itemDto); Task<List<Lot>> GetLotNo(InventoryDto itemDto);
Task<List<Lot>> GetLotNoById(InventoryDto itemDto);// Task<List<Lot>> GetLotNoById(InventoryDto itemDto);
Task<List<LotQtyByItem>> GetLotQtyByItem(InventoryDto itemDto); Task<List<LotQtyByItem>> GetLotQtyByItem(InventoryDto itemDto);
Task<List<Infrastructure.Entities.Inventory.Inventory>> GetInventoryByUserId(InventoryDto itemDto); Task<List<Infrastructure.Entities.Inventory.Inventory>> GetInventoryByUserId(InventoryDto itemDto);
Task<List<RequestItemDetail>> GetRequestedItemByUserId(InventoryDto itemDto); Task<List<RequestItemDetail>> GetRequestedItemByUserId(InventoryDto itemDto);
Task<List<ItemDetail>> GetInventoryById(InventoryDto itemDto); Task<List<ItemDetail>> GetInventoryById(InventoryDto itemDto);
Task<PagedResult<InventoryResponse>> GetInventory(InventoryRequest request, CancellationToken ct);
Task<List<InventoryByIdResponse>> GetInventoryById(InventoryRequest itemDto, CancellationToken ct);
Task<TransactContextDto?> GetTransactContextAsync(int inventoryId, CancellationToken ct);
Task<IReadOnlyList<DisciplineDto>> GetDisciplinesAsync(CancellationToken ct);
Task<Infrastructure.Entities.Inventory.Inventory> PostPutReqApproval(InventoryDto itemDto); Task<Infrastructure.Entities.Inventory.Inventory> PostPutReqApproval(InventoryDto itemDto);
Task<RequestItem> PostPutReqItems(InventoryDto itemDto); Task<RequestItem> PostPutReqItems(InventoryDto itemDto);
Task<Lot> PostPutLotNo(InventoryDto itemDto); Task<Lot> PostPutLotNo(InventoryDto itemDto);

View File

@ -0,0 +1,17 @@
using CPRNIMS.Infrastructure.Dto.Inventory.Reports;
using CPRNIMS.Infrastructure.Dto.Inventory.Request;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CPRNIMS.Domain.Contracts.Inventory
{
public interface IInventoryReports
{
Task<RISReportDto> GetRISReportAsync(InventoryReportsRequest request, string userName, int? departmentId, CancellationToken ct);
Task<MRSReportDto> GetMRSReportAsync(InventoryReportsRequest request, string userName, int? departmentId, CancellationToken ct);
Task<InventoryReportDto> GetInventoryReportAsync(InventoryReportsRequest request,string userName, int? departmentId, CancellationToken ct);
}
}

View File

@ -0,0 +1,22 @@
using CPRNIMS.Infrastructure.Dto.Inventory;
using CPRNIMS.Infrastructure.Dto.Inventory.Request;
using CPRNIMS.Infrastructure.Entities.Inventory;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CPRNIMS.Domain.Contracts.Inventory
{
public interface IMRS
{
Task<MRSPagedResult> GetPagedAsync(MRSFilterDto filter, CancellationToken ct,int? departmentId = null, string? userName = "");
Task<IReadOnlyList<RISSearchResultDto>> SearchRISForReturnAsync(string? risNoQuery, int? projectCodeId, CancellationToken ct);
Task<IReadOnlyList<ProjectCodeOptionDto>> GetProjectsWithOpenRISAsync(string? nameQuery, CancellationToken ct);
Task<MRS?> GetByIdAsync(long mrsId, CancellationToken ct);
Task<MRS> CreateAsync(CreateMRSRequest dto, string createdBy, CancellationToken ct);
Task ApproveAsync(long mrsId, string approvedBy, CancellationToken ct);
Task CancelAsync(CancelMRSRequest request, string canceledBy, CancellationToken ct);
}
}

View File

@ -0,0 +1,22 @@
using CPRNIMS.Infrastructure.Dto.Inventory;
using CPRNIMS.Infrastructure.Dto.Inventory.Request;
using CPRNIMS.Infrastructure.Dto.Inventory.Response;
using CPRNIMS.Infrastructure.Entities.Inventory;
using CPRNIMS.Infrastructure.Entities.Purchasing;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CPRNIMS.Domain.Contracts.Inventory
{
public interface IRIS
{
Task<RISPagedResult> GetPagedAsync(RISFilterDto filter, CancellationToken ct, int? departmentId = null, string? userName = "");
Task<RISResponse?> GetByIdAsync(long risId, CancellationToken ct);
Task<Infrastructure.Entities.Inventory.RIS> CreateAsync(CreateRISRequest dto, string createdBy, CancellationToken ct);
Task ApproveAsync(ApproveRISRequest request, string approvedBy, CancellationToken ct);
Task CancelAsync(CancelRISRequest request,string canceledBy, CancellationToken ct);
}
}

View File

@ -16,14 +16,14 @@ namespace CPRNIMS.Domain.Contracts.Items
Task<List<Departments>> GetDepartment(ItemCodeDto itemCode); Task<List<Departments>> GetDepartment(ItemCodeDto itemCode);
Task<PagedResult<ItemList>> GetItemList(ItemCodeDto itemCode); Task<PagedResult<ItemList>> GetItemList(ItemCodeDto itemCode);
Task<List<ItemCart>> GetItemCart(ItemDto itemDto); Task<List<ItemCart>> GetItemCart(ItemDto itemDto);
Task<List<Item>> GetItemDetail(ItemDto itemDto); Task<List<ItemDtos>> GetItemDetail(ItemDto itemDto);
Task<List<ItemLocalization>> GetItemLocalization(ItemDto itemDto); Task<List<ItemLocalization>> GetItemLocalization(ItemDto itemDto);
Task<List<ItemCategory>> GetItemCateg(ItemDto itemDto); Task<List<ItemCategory>> GetItemCateg(ItemDto itemDto);
Task<List<ItemColor>> GetItemColor(ItemDto itemDto); Task<List<ItemColor>> GetItemColor(ItemDto itemDto);
Task<List<UnitOfMessure>> GetItemUOM(ItemDto itemDto); Task<List<UnitOfMessure>> GetItemUOM(ItemDto itemDto);
Task<List<NotifUserKey>> GetNotifUserKey(ItemDto itemDto); Task<List<NotifUserKey>> GetNotifUserKey(ItemDto itemDto);
Task<List<ProjectCodes>> GetProjectCode(); Task<IReadOnlyList<ProjectCodes>> GetProjectCode();
Task<List<ProjectCodes>> GetProjectCodeByTerm(string? fileName); Task<IReadOnlyList<ProjectCodes>> GetProjectCodeByTerm(string? fileName);
Task<(long, long)> GetPRNo(); Task<(long, long)> GetPRNo();
Task<ResponseObject> PostPurchRequest(ItemDto itemDto); Task<ResponseObject> PostPurchRequest(ItemDto itemDto);
Task<ItemCodeDto> PostPutItem(ItemCodeDto itemDto); Task<ItemCodeDto> PostPutItem(ItemCodeDto itemDto);

View File

@ -1,4 +1,5 @@
using CPRNIMS.Infrastructure.Dto.PO; using CPRNIMS.Infrastructure.Dto.Canvass.Response;
using CPRNIMS.Infrastructure.Dto.PO;
using CPRNIMS.Infrastructure.Entities.Canvass; using CPRNIMS.Infrastructure.Entities.Canvass;
using CPRNIMS.Infrastructure.Entities.Common; using CPRNIMS.Infrastructure.Entities.Common;
using CPRNIMS.Infrastructure.Entities.LocalDb.NonInvent; using CPRNIMS.Infrastructure.Entities.LocalDb.NonInvent;
@ -37,7 +38,7 @@ namespace CPRNIMS.Domain.Contracts.PO
Task<List<SystemSettings>> GetLatestPO2(PODto pODto); Task<List<SystemSettings>> GetLatestPO2(PODto pODto);
Task<List<DocRequired>> GetDocRequired(PODto pODto); Task<List<DocRequired>> GetDocRequired(PODto pODto);
Task<List<OtherCharges>> GetOtherCharges(PODto itemDto); Task<List<OtherCharges>> GetOtherCharges(PODto itemDto);
Task<List<Suppliers>> GetSuppliers(PODto itemDto); Task<List<SupplierResponseDto>> GetSuppliers(PODto itemDto);
Task<List<CreatedPO>> GetCreatedPO(PODto pODto); Task<List<CreatedPO>> GetCreatedPO(PODto pODto);
Task<List<POItemDetail>> GetPOItemDetail(PODto pODto); Task<List<POItemDetail>> GetPOItemDetail(PODto pODto);
Task<List<CreatedPO>> GetMyCreatedPO(PODto pODto); Task<List<CreatedPO>> GetMyCreatedPO(PODto pODto);
@ -69,6 +70,7 @@ namespace CPRNIMS.Domain.Contracts.PO
Task<Incoterm> PostPutIncoterms(PODto pODto); Task<Incoterm> PostPutIncoterms(PODto pODto);
Task<bool> DeleteIncShip(PODto poDto); Task<bool> DeleteIncShip(PODto poDto);
Task<bool> PostIncShipFollowUp(PODto pODto); Task<bool> PostIncShipFollowUp(PODto pODto);
Task<IReadOnlyList<Currencies>> GetCurrencies(string currencyName, CancellationToken ct);
#endregion #endregion
} }
} }

View File

@ -12,7 +12,7 @@ namespace CPRNIMS.Domain.Contracts.Receiving
public interface IReceiving public interface IReceiving
{ {
Task<List<ReceivingDetail>> GetRRDetailByPO(ItemDto itemDto); Task<List<ReceivingDetail>> GetRRDetailByPO(ItemDto itemDto);
Task<List<RRDetail>> GetRRDetail(ItemDto itemDto); Task<List<RRDetailDto>> GetRRDetail(ItemDto itemDto);
Task<List<ForReceiving>> GetForReceiving(ItemDto itemDto); Task<List<ForReceiving>> GetForReceiving(ItemDto itemDto);
Task<List<RR>> GetRR(ItemDto itemDto); Task<List<RR>> GetRR(ItemDto itemDto);
Task<List<RRSeries>> GetLatestRRNo(ItemDto itemDto); Task<List<RRSeries>> GetLatestRRNo(ItemDto itemDto);

View File

@ -0,0 +1,16 @@
using CPRNIMS.Infrastructure.Dto.Inventory.Reports;
using FastReport;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CPRNIMS.Domain.Contracts.Reports
{
public interface IReportBuilder
{
Task<Report> RISBuildAsync(DateTime dateFrom, DateTime dateTo, string templatePath, CancellationToken ct);
Task<Report> MRSBuildAsync(DateTime dateFrom, DateTime dateTo, string templatePath, CancellationToken ct);
}
}

View File

@ -0,0 +1,16 @@
using CPRNIMS.Infrastructure.Dto.Inventory.Reports;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CPRNIMS.Domain.Contracts.Reports
{
public interface IReportDataService
{
List<RISRowDto> GetMain(DateTime dateFrom, DateTime dateTo);
List<DisciplineAggDto> GetDisciplines(DateTime dateFrom, DateTime dateTo);
List<TopRecipientDto> GetRecipients(DateTime dateFrom, DateTime dateTo);
}
}

View File

@ -5,8 +5,6 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using AutoMapper;
using CPRNIMS.Infrastructure.ViewModel.Common;
using CPRNIMS.Infrastructure.Entities.Canvass; using CPRNIMS.Infrastructure.Entities.Canvass;
namespace CPRNIMS.Domain.Profile.Canvass namespace CPRNIMS.Domain.Profile.Canvass
@ -15,14 +13,12 @@ namespace CPRNIMS.Domain.Profile.Canvass
{ {
public SupplierRequestProfile() public SupplierRequestProfile()
{ {
// 1. THIS IS THE MISSING LINK: Request -> Entity
CreateMap<SupplierRequest, Suppliers>(); CreateMap<SupplierRequest, Suppliers>();
// 2. Entity -> Response
CreateMap<Suppliers, SupplierResponse>(); CreateMap<Suppliers, SupplierResponse>();
// 3. Response <-> Request (Use ReverseMap to handle both directions automatically)
CreateMap<SupplierResponse, SupplierRequest>().ReverseMap(); CreateMap<SupplierResponse, SupplierRequest>().ReverseMap();
CreateMap<StartCanvassRequest, ForAISearchingTagging>();
CreateMap<ForAISearchingTagging, StartCanvassResponse>().ReverseMap();
} }
} }
} }

View File

@ -57,6 +57,7 @@ namespace CPRNIMS.Domain.Services.Account
new Claim(ClaimTypes.NameIdentifier, user.Id), new Claim(ClaimTypes.NameIdentifier, user.Id),
new Claim("FullName", user.FullName ?? ""), new Claim("FullName", user.FullName ?? ""),
new Claim("Company", user.Company ?? ""), new Claim("Company", user.Company ?? ""),
new Claim("DepartmentId", Convert.ToString(user.DepartmentId)),
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()), new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
}; };

View File

@ -25,8 +25,6 @@ namespace CPRNIMS.Domain.Services.Account
_roleManager = roleManager; _roleManager = roleManager;
} }
public async Task<List<object>> GetAllUsersProfile() public async Task<List<object>> GetAllUsersProfile()
{
try
{ {
var usersWithRolesAndAttachments = await (from user in _userManager.Users var usersWithRolesAndAttachments = await (from user in _userManager.Users
join userRole in _dbContext.IdentityUserRoles on user.Id equals userRole.UserId into userRoles join userRole in _dbContext.IdentityUserRoles on user.Id equals userRole.UserId into userRoles
@ -39,8 +37,8 @@ namespace CPRNIMS.Domain.Services.Account
{ {
user.Id, user.Id,
Role = r != null ? r.Name ?? "N/A" : "N/A", Role = r != null ? r.Name ?? "N/A" : "N/A",
URL = a != null ? a.URL ?? "N/A" : "N/A", URL = a != null ? a.URL ?? "404userImage" : "404userImage",
FileName = a != null ? a.FileName ?? "N/A" : "N/A", FileName = a != null ? a.FileName ?? "404userImage.jpg" : "404userImage.jpg",
user.Company, user.Company,
user.DepartmentId, user.DepartmentId,
user.UserName, user.UserName,
@ -59,12 +57,6 @@ namespace CPRNIMS.Domain.Services.Account
return usersWithRolesAndAttachments.Cast<object>().ToList(); return usersWithRolesAndAttachments.Cast<object>().ToList();
} }
catch (Exception ex)
{
ex.ToString();
throw;
}
}
public async Task AssignUserRole(RegisterModel registerModel) public async Task AssignUserRole(RegisterModel registerModel)
{ {
var user = await _userManager.FindByIdAsync(registerModel.Id); var user = await _userManager.FindByIdAsync(registerModel.Id);

View File

@ -1,17 +1,17 @@
using AutoMapper; using AutoMapper;
using Azure.Core;
using CPRNIMS.Domain.Services.ICanvass; using CPRNIMS.Domain.Services.ICanvass;
using CPRNIMS.Infrastructure.Database; using CPRNIMS.Infrastructure.Database;
using CPRNIMS.Infrastructure.Dto.Canvass; using CPRNIMS.Infrastructure.Dto.Canvass;
using CPRNIMS.Infrastructure.Dto.Canvass.Request; using CPRNIMS.Infrastructure.Dto.Canvass.Request;
using CPRNIMS.Infrastructure.Dto.Canvass.Response; using CPRNIMS.Infrastructure.Dto.Canvass.Response;
using CPRNIMS.Infrastructure.Dto.Common;
using CPRNIMS.Infrastructure.Entities.Canvass; using CPRNIMS.Infrastructure.Entities.Canvass;
using CPRNIMS.Infrastructure.Entities.Purchasing; using CPRNIMS.Infrastructure.Entities.Purchasing;
using CPRNIMS.Infrastructure.Helper; using CPRNIMS.Infrastructure.Helper;
using CPRNIMS.Infrastructure.ViewModel.Canvass;
using CPRNIMS.Infrastructure.ViewModel.Common; using CPRNIMS.Infrastructure.ViewModel.Common;
using Microsoft.Data.SqlClient; using Microsoft.Data.SqlClient;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using System.Data; using System.Data;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -31,6 +31,14 @@ namespace CPRNIMS.Domain.Services.Canvass
_mapper = mapper; _mapper = mapper;
} }
#region Get #region Get
public async Task<List<Suppliers>> GetSuppliers(CancellationToken ct)
{
return await _dbContext.Suppliers
.AsNoTracking()
.Where(s => s.IsActive)
.ToListAsync(ct);
}
public async Task<List<ItemListWOEmail>> GetItemSupplierWOEmail(CanvassDto CanvassDto) public async Task<List<ItemListWOEmail>> GetItemSupplierWOEmail(CanvassDto CanvassDto)
{ {
var allItems = await _dbContext.ItemListWOEmails var allItems = await _dbContext.ItemListWOEmails
@ -49,14 +57,137 @@ namespace CPRNIMS.Domain.Services.Canvass
return allItems ?? new List<BiddingItem>(); return allItems ?? new List<BiddingItem>();
} }
public async Task<List<PerSupplier>> GetCanvassPerSupplier(CanvassDto CanvassDto) public async Task<PagedResult<ItemsForTagging>> GetItemsForTagging(CanvassDto dto)
{ {
var allItems = await _dbContext.PerSuppliers var parameters = new[]
.FromSqlRaw($"EXEC GetCanvassPerSupplier @UserId", {
new SqlParameter("@UserId", CanvassDto.UserId)) new SqlParameter("@UserId", dto.UserId),
.ToListAsync(); new SqlParameter("@SearchPRNo", dto.SearchPRNo ?? ""),
new SqlParameter("@SearchItemNo", dto.SearchItemNo ?? ""),
new SqlParameter("@SearchItemName", dto.SearchItemName ?? ""),
new SqlParameter("@SearchDepartment", dto.SearchDepartment ?? ""),
new SqlParameter("@PageNumber", dto.PageNumber),
new SqlParameter("@PageSize", dto.PageSize)
};
return allItems ?? new List<PerSupplier>(); int totalCount = 0;
var departmentList = new List<string>();
var items = new List<ItemsForTagging>();
var conn = _dbContext.Database.GetDbConnection();
await conn.OpenAsync();
using var cmd = conn.CreateCommand();
cmd.CommandText = @"EXEC GetItemsForTagging @UserId,
@SearchPRNo,@SearchItemNo,@SearchItemName,@SearchDepartment,
@PageNumber, @PageSize";
foreach (var p in parameters) cmd.Parameters.Add(p);
cmd.CommandTimeout = 60;
using var reader = await cmd.ExecuteReaderAsync();
// Result set 1 — distinct supplier list
while (await reader.ReadAsync())
departmentList.Add(reader.GetString(0));
// Result set 2 — total count
await reader.NextResultAsync();
if (await reader.ReadAsync())
totalCount = reader.GetInt32(0);
// Result set 3 — paged rows
await reader.NextResultAsync();
while (await reader.ReadAsync())
{
items.Add(new ItemsForTagging
{
PRDetailsId = Convert.ToInt64(reader["PRDetailsId"]),
PRNo = Convert.ToInt64(reader["PRNo"]),
ItemNo = Convert.ToInt64(reader["ItemNo"]),
ItemName = reader["ItemName"]?.ToString(),
ItemDescription = reader["ItemDescription"]?.ToString(),
Qty = Convert.ToDecimal(reader["Qty"]),
Department = reader["Department"]?.ToString(),
CreatedBy = reader["CreatedBy"]?.ToString(),
CreatedDate =Convert.ToDateTime(reader["CreatedDate"]),
DateNeeded = Convert.ToDateTime(reader["DateNeeded"]),
});
}
await conn.CloseAsync();
return new PagedResult<ItemsForTagging>
{
Data = items,
TotalCount = totalCount,
PageNumber = dto.PageNumber,
PageSize = dto.PageSize,
DepartmentList = departmentList
};
}
public async Task<PagedResult<PerSupplier>> GetCanvassPerSupplier(CanvassDto dto)
{
var parameters = new[]
{
new SqlParameter("@UserId", dto.UserId),
new SqlParameter("@SearchPRNo", dto.SearchPRNo ?? ""),
new SqlParameter("@SearchItemNo", dto.SearchItemNo ?? ""),
new SqlParameter("@SearchItemName", dto.SearchItemName ?? ""),
new SqlParameter("@SearchSupplier", dto.SearchSupplier ?? ""),
new SqlParameter("@PageNumber", dto.PageNumber),
new SqlParameter("@PageSize", dto.PageSize)
};
var supplierList = new List<string>();
int totalCount = 0;
var items = new List<PerSupplier>();
var conn = _dbContext.Database.GetDbConnection();
await conn.OpenAsync();
using var cmd = conn.CreateCommand();
cmd.CommandText = @"EXEC GetCanvassPerSupplier @UserId,
@SearchPRNo,@SearchItemNo,@SearchItemName,@SearchSupplier,
@PageNumber, @PageSize";
foreach (var p in parameters) cmd.Parameters.Add(p);
cmd.CommandTimeout = 60;
using var reader = await cmd.ExecuteReaderAsync();
// Result set 1 — distinct supplier list
while (await reader.ReadAsync())
supplierList.Add(reader.GetString(0));
// Result set 2 — total count
await reader.NextResultAsync();
if (await reader.ReadAsync())
totalCount = reader.GetInt32(0);
// Result set 3 — paged rows
await reader.NextResultAsync();
while (await reader.ReadAsync())
{
items.Add(new PerSupplier
{
SupplierId = reader["SupplierId"] as int? ?? 0,
SupplierName = reader["SupplierName"]?.ToString(),
EmailAddress = reader["EmailAddress"]?.ToString(),
AggreItemName = reader["AggreItemName"]?.ToString(),
AggreItemNo = reader["AggreItemNo"]?.ToString(),
AggrePRNo = reader["AggrePRNo"]?.ToString(),
});
}
await conn.CloseAsync();
return new PagedResult<PerSupplier>
{
Data = items,
TotalCount = totalCount,
PageNumber = dto.PageNumber,
PageSize = dto.PageSize,
SupplierList = supplierList
};
} }
public async Task<List<RFQPerSupplier>> GetSupplierBidByItem(CanvassDto CanvassDto) public async Task<List<RFQPerSupplier>> GetSupplierBidByItem(CanvassDto CanvassDto)
{ {
@ -113,29 +244,26 @@ namespace CPRNIMS.Domain.Services.Canvass
return allItems ?? new List<CanvassGroupByPRNo>(); return allItems ?? new List<CanvassGroupByPRNo>();
} }
public async Task<List<SupplierResponse>> GetSupplierItemWOEmail(CanvassDto canvassDto) public async Task<List<SupplierResponseDto>> GetSupplierItemWOEmail(CanvassDto canvassDto)
{ {
// 1. Use Interpolated string to prevent SQL injection and pass the parameter safely
var suppliers = await _dbContext.Suppliers var suppliers = await _dbContext.SupplierResponses
.FromSqlInterpolated($"EXEC GetSupplierItemWOEmail @ItemNo = {canvassDto.ItemNo}") .FromSqlInterpolated($"EXEC GetSupplierItemWOEmail @ItemNo = {canvassDto.ItemNo}")
.ToListAsync(); .ToListAsync();
// 2. Map the List of entities to a List of Response objects return suppliers ?? new List<SupplierResponseDto>();
// AutoMapper handles collections automatically if the types are configured
return _mapper.Map<List<SupplierResponse>>(suppliers);
} }
public async Task<List<SupplierResponse>> GetSupplierById(CanvassDto CanvassDto) public async Task<List<SupplierResponseDto>> GetSupplierById(CanvassDto CanvassDto)
{ {
var item = await _dbContext.Suppliers var items = await _dbContext.SupplierResponses
.FromSqlRaw($"EXEC GetSupplierById @SupplierId", .FromSqlRaw($"EXEC GetSupplierById @SupplierId",
new SqlParameter("@SupplierId", CanvassDto.SupplierId)) new SqlParameter("@SupplierId", CanvassDto.SupplierId))
.ToListAsync(); .ToListAsync();
// 2. Map the List of entities to a List of Response objects
// AutoMapper handles collections automatically if the types are configured return items ?? new List<SupplierResponseDto>();
return _mapper.Map<List<SupplierResponse>>(item);
} }
public async Task<List<RFQReference>> GetRFQ(CanvassDto CanvassDto) public async Task<List<RFQReference>> GetRFQ(ForCanvassDto CanvassDto)
{ {
var allItems = await _dbContext.RFQReferences var allItems = await _dbContext.RFQReferences
.FromSqlRaw($"EXEC GetRFQPerSupplier @SupplierId,@UserId", .FromSqlRaw($"EXEC GetRFQPerSupplier @SupplierId,@UserId",
@ -192,14 +320,34 @@ namespace CPRNIMS.Domain.Services.Canvass
return 0; return 0;
} }
} }
public async Task<List<ItemWithoutSupplier>> GetItemWithoutSupplier() public async Task<List<ForAISearchingTagging>> GetForAISearchingTagging(CancellationToken ct)
{
var allItems = await _dbContext.ForAISearchingTaggings
.AsNoTracking()
.Take(1)
.ToListAsync(ct);
return allItems ?? new List<ForAISearchingTagging>();
}
public async Task<List<ItemWithoutSupplier>> GetItemWithoutSupplier(CancellationToken ct)
{ {
var allItems = await _dbContext.ItemWithoutSuppliers var allItems = await _dbContext.ItemWithoutSuppliers
.FromSqlRaw("EXEC GetItemWithoutSupplier") .FromSqlRaw("EXEC GetItemWithoutSupplier")
.ToListAsync(); .ToListAsync(ct);
return allItems ?? new List<ItemWithoutSupplier>(); return allItems ?? new List<ItemWithoutSupplier>();
} }
public async Task<List<SupplierForCanvass>> GetSupplierForCanvass(int supplierId,string userName,
CancellationToken ct)
{
var allItems = await _dbContext.SupplierForCanvass
.FromSqlRaw("EXEC GetSupplierForCanvass @UserName,@SupplierId",
new SqlParameter("@UserName", userName),
new SqlParameter("@SupplierId", supplierId)
).AsNoTracking().ToListAsync(ct);
return allItems ?? new List<SupplierForCanvass>();
}
public async Task<List<WOResponseById>> GetWOResponseBySuppId(CanvassDto CanvassDto) public async Task<List<WOResponseById>> GetWOResponseBySuppId(CanvassDto CanvassDto)
{ {
var allItems = await _dbContext.WOResponseByIds var allItems = await _dbContext.WOResponseByIds
@ -208,7 +356,7 @@ namespace CPRNIMS.Domain.Services.Canvass
return allItems ?? new List<WOResponseById>(); return allItems ?? new List<WOResponseById>();
} }
public async Task<RFQ> PostPerSupplierToken(CanvassDto CanvassDto) public async Task<RFQ> PostPerSupplierToken(ForCanvassDto CanvassDto)
{ {
await _dbContext.Database await _dbContext.Database
.ExecuteSqlRawAsync("EXEC PostPerSupplierToken @UserId,@SupplierId,@PRNo,@ItemNo,@CanvassNo,@PRDetailsId", .ExecuteSqlRawAsync("EXEC PostPerSupplierToken @UserId,@SupplierId,@PRNo,@ItemNo,@CanvassNo,@PRDetailsId",
@ -230,15 +378,15 @@ namespace CPRNIMS.Domain.Services.Canvass
return allItems ?? new List<SupplierBidById>(); return allItems ?? new List<SupplierBidById>();
} }
public async Task<List<SupplierResponse>> GetMySuppliers(CanvassDto CanvassDto) public async Task<List<SupplierResponseDto>> GetMySuppliers(CanvassDto CanvassDto)
{ {
var items = await _dbContext.Suppliers var items = await _dbContext.SupplierResponses
.FromSqlRaw($"EXEC GetMySuppliers @UserId,@SupplierId", .FromSqlRaw($"EXEC GetMySuppliers @UserId,@SupplierId",
new SqlParameter("@UserId", CanvassDto.UserId), new SqlParameter("@UserId", CanvassDto.UserId),
new SqlParameter("@SupplierId", CanvassDto.SupplierId)) new SqlParameter("@SupplierId", CanvassDto.SupplierId))
.ToListAsync(); .ToListAsync();
return _mapper.Map<List<SupplierResponse>>(items); return items ?? new List<SupplierResponseDto>();
} }
public async Task<List<MyPRWOCanvass>> GetMyPRWOCanvass(CanvassDto itemDto) public async Task<List<MyPRWOCanvass>> GetMyPRWOCanvass(CanvassDto itemDto)
{ {
@ -311,7 +459,7 @@ namespace CPRNIMS.Domain.Services.Canvass
} }
#endregion #endregion
#region Post Put #region Post Put
public async Task<Result<SupplierResponse>> PostSupplierAsync(SupplierRequest request, CancellationToken ct) public async Task<Result<SupplierResponse>> PostPutSupplierAsync(SupplierRequest request, CancellationToken ct)
{ {
var strategy = _dbContext.Database.CreateExecutionStrategy(); var strategy = _dbContext.Database.CreateExecutionStrategy();
@ -322,7 +470,7 @@ namespace CPRNIMS.Domain.Services.Canvass
try try
{ {
var supplier = await _dbContext.Suppliers var supplier = await _dbContext.Suppliers
.FirstOrDefaultAsync(s => s.SupplierName == request.SupplierName, ct); .FirstOrDefaultAsync(s => s.SupplierId == request.SupplierId, ct);
if (supplier == null) if (supplier == null)
{ {
@ -342,8 +490,6 @@ namespace CPRNIMS.Domain.Services.Canvass
catch (DbUpdateException ex) catch (DbUpdateException ex)
{ {
await transaction.RollbackAsync(ct); await transaction.RollbackAsync(ct);
// handle unique constraint violation here if needed
throw; throw;
} }
}); });
@ -385,16 +531,6 @@ namespace CPRNIMS.Domain.Services.Canvass
new SqlParameter("@CanvassId", CanvassDto.CanvassId)); new SqlParameter("@CanvassId", CanvassDto.CanvassId));
return new CanvassDetail(); return new CanvassDetail();
} }
public async Task<PRCanvassDetail> PostCanvass(CanvassDto CanvassDto)
{
await _dbContext.Database
.ExecuteSqlRawAsync("EXEC PostCanvass @UserId, @SupplierId, @Status,@Remarks",
new SqlParameter("@SupplierId", CanvassDto.SupplierId != null ? CanvassDto.SupplierId : 0L),
new SqlParameter("@UserId", CanvassDto.UserId),
new SqlParameter("@Status", CanvassDto.Status),
new SqlParameter("@Remarks", CanvassDto.Remarks ?? "N/A"));
return new PRCanvassDetail();
}
public async Task<SupplierResponse> PostTaggingSupplier(CanvassDto CanvassDto) public async Task<SupplierResponse> PostTaggingSupplier(CanvassDto CanvassDto)
{ {
var (messCode, message) = CreateOutputParams(); var (messCode, message) = CreateOutputParams();
@ -547,8 +683,7 @@ namespace CPRNIMS.Domain.Services.Canvass
new SqlParameter("@CanvassSupplierId", canvassDto.CanvassSupplierId)); new SqlParameter("@CanvassSupplierId", canvassDto.CanvassSupplierId));
return new CanvassSupplier(); return new CanvassSupplier();
} }
public async Task<bool> SendRFQ(SupplierEmailRequest supplierEmailRequest)
public async Task SendRFQ(SupplierEmailRequest supplierEmailRequest)
{ {
var baseTemplate = EMailTemplate("Content\\SMTPEmailContent", "SendToSupplier.cshtml"); var baseTemplate = EMailTemplate("Content\\SMTPEmailContent", "SendToSupplier.cshtml");
@ -575,7 +710,9 @@ namespace CPRNIMS.Domain.Services.Canvass
AttachPath=supplierEmailRequest.AttachPath AttachPath=supplierEmailRequest.AttachPath
}; };
await _smptHelper.SendEmailAsync(messageDetails); if (await _smptHelper.SendEmailAsync(messageDetails))
return true;
return false;
} }
public string EMailTemplate(string relativePath, string emailTemplate) public string EMailTemplate(string relativePath, string emailTemplate)
{ {
@ -593,14 +730,61 @@ namespace CPRNIMS.Domain.Services.Canvass
return "Template file not found"; return "Template file not found";
} }
} }
public async Task DeleteAsync(long pRDetailsId, CancellationToken ct)
public async Task<bool> SearchingUpdate(long pRDetailsId)
{ {
var rowsAffected = await _dbContext.PRDetails await _dbContext.ForAISearchingTaggings
.Where(p => p.PRDetailsId == pRDetailsId) .Where(p => p.PRDetailsId == pRDetailsId)
.ExecuteUpdateAsync(s => s.SetProperty(p => p.IsSearched, true)); .ExecuteDeleteAsync(ct);
}
return rowsAffected > 0; public async Task<Result<StartCanvassResponse>> StartCanvass(CanvassVM request, CancellationToken ct)
{
var detailIds = request.ForSupplierSearchList?.PRDetailsId;
if (detailIds == null || !detailIds.Any())
return Result<StartCanvassResponse>.Failure("No items provided.");
// 1. Get IDs that already exist in one call to avoid the loop-check
var existingIds = await _dbContext.ForAISearchingTaggings
.Where(f => detailIds.Contains(f.PRDetailsId))
.Select(f => f.PRDetailsId)
.ToListAsync(ct);
var newTags = new List<ForAISearchingTagging>();
var idsToUpdate = new List<long>();
for (int i = 0; i < detailIds.Count; i++)
{
var currentId = detailIds[i];
if (existingIds.Contains(currentId)) continue;
newTags.Add(new ForAISearchingTagging
{
PRDetailsId = currentId,
PRNo = request.ForSupplierSearchList.PRNo[i],
ItemNo = request.ForSupplierSearchList.ItemNo[i],
ItemName = request.ForSupplierSearchList.ItemName[i],
ItemDescription = request.ForSupplierSearchList.ItemDescription[i],
IsInternational = request.IsInternational,
FullName = request.FullName,
UserId = request.UserId
});
idsToUpdate.Add(currentId);
}
if (newTags.Any())
{
// 2. Bulk Add
await _dbContext.ForAISearchingTaggings.AddRangeAsync(newTags, ct);
await _dbContext.SaveChangesAsync(ct);
// 3. Bulk Update the flags AFTER saving the tags
await _dbContext.PRDetails
.Where(p => idsToUpdate.Contains(p.PRDetailsId))
.ExecuteUpdateAsync(s => s.SetProperty(p => p.IsSearched, true), ct);
}
return Result<StartCanvassResponse>.Success(new StartCanvassResponse { });
} }
#endregion #endregion
} }

View File

@ -3,9 +3,11 @@ using CPRNIMS.Infrastructure.Dto.Canvass.Result;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Text.Json; using System.Text.Json;
using System.Text.Json.Serialization;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -19,7 +21,125 @@ namespace CPRNIMS.Domain.Services.Canvass
// Common contact page suffixes to try // Common contact page suffixes to try
private static readonly string[] ContactPaths = private static readonly string[] ContactPaths =
{ "/contact", "/contact-us", "/pages/contact-us", "/about/contact", "/about" }; { "/contact", "/contact-us", "/pages/contact-us", "/about/contact", "/about" };
/// <summary>
/// Uses Groq to fuzzy-match a new supplier against existing ones.
/// Handles rebranding, spacing in phone numbers, name variations, etc.
/// Returns the matched existing SupplierId, or null if no match.
/// </summary>
public async Task<int?> FindMatchingExistingSupplierAsync(
SupplierResponse incoming,
List<SupplierResponse> existingSuppliers)
{
if (!existingSuppliers.Any()) return null;
// ── Layer 1: Exact C# match (fast, free, no API call) ──────────────
var incomingEmail = (incoming.EmailAddress ?? "").Trim().ToLower();
var incomingPhone = NormalizePhone(incoming.ContactNo ?? "");
var incomingDomain = ExtractDomain(incoming.Website ?? "");
foreach (var s in existingSuppliers)
{
var existEmail = (s.EmailAddress ?? "").Trim().ToLower();
var existPhone = NormalizePhone(s.ContactNo ?? "");
var existDomain = ExtractDomain(s.Website ?? "");
if (!string.IsNullOrEmpty(incomingEmail) && incomingEmail == existEmail)
return s.SupplierId;
if (!string.IsNullOrEmpty(incomingPhone) && incomingPhone == existPhone)
return s.SupplierId;
if (!string.IsNullOrEmpty(incomingDomain) && incomingDomain == existDomain)
return s.SupplierId;
}
// ── Layer 2: Fuzzy C# pre-filter — narrow to top candidates ────────
var incomingName = (incoming.SupplierName ?? "").ToLower();
var candidates = existingSuppliers
.Where(s =>
{
var name = (s.SupplierName ?? "").ToLower();
// Keep if first word matches (e.g. "Linde" in "Linde PH" vs "Linde Philippines")
var incomingFirstWord = incomingName.Split(' ').FirstOrDefault() ?? "";
var existFirstWord = name.Split(' ').FirstOrDefault() ?? "";
return !string.IsNullOrEmpty(incomingFirstWord)
&& incomingFirstWord.Length > 2 // ignore short words like "co", "ph"
&& existFirstWord.StartsWith(incomingFirstWord, StringComparison.OrdinalIgnoreCase);
})
.Take(5) // max 5 candidates to Groq — well within token limit
.Select(s => new
{
s.SupplierId,
s.SupplierName,
s.EmailAddress,
s.ContactNo,
s.Website
})
.ToList();
// No fuzzy candidates found — it's a new supplier
if (!candidates.Any()) return null;
// ── Layer 3: Groq fuzzy match — only on small candidate list ────────
var incomingJson = JsonSerializer.Serialize(new
{
incoming.SupplierName,
incoming.EmailAddress,
incoming.ContactNo,
incoming.Website
});
var candidatesJson = JsonSerializer.Serialize(candidates);
var prompt =
"TASK: Determine if the INCOMING supplier already exists in the CANDIDATES list.\n\n" +
"MATCHING RULES (any one is enough):\n" +
"1. Same email address (case-insensitive).\n" +
"2. Same phone number after stripping spaces, dashes, country codes.\n" +
"3. Same company despite rebranding, abbreviation, or spacing differences.\n" +
"4. Same website domain (ignore www, http/https).\n\n" +
"If matched: respond ONLY { \"matched\": true, \"supplierId\": <number> }\n" +
"If not matched: respond ONLY { \"matched\": false, \"supplierId\": null }\n" +
"No explanation. No markdown. JSON only.\n\n" +
$"INCOMING:\n{incomingJson}\n\n" +
$"CANDIDATES:\n{candidatesJson}";
var payload = new
{
model = _config["Groq:Model"] ?? "llama-3.1-8b-instant",
stream = false,
max_tokens = 50,
temperature = 0,
messages = new[]
{
new { role = "system", content = "You are a supplier deduplication engine. Return ONLY valid JSON. No markdown. No explanation." },
new { role = "user", content = prompt }
}
};
var request = new HttpRequestMessage(HttpMethod.Post, _config["Groq:ApiUrl"]);
request.Headers.Add("Authorization", $"Bearer {_config["Groq:ApiKey"]}");
request.Content = new StringContent(
JsonSerializer.Serialize(payload), Encoding.UTF8, "application/json");
var response = await _httpClient.SendAsync(request);
response.EnsureSuccessStatusCode();
var body = await response.Content.ReadAsStringAsync();
var groqResp = JsonSerializer.Deserialize<GroqResponse>(body,
new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
var rawText = groqResp?.Choices?[0]?.Message?.Content ?? string.Empty;
rawText = Regex.Replace(rawText, @"```[a-z]*|```", "").Trim();
var match = JsonSerializer.Deserialize<GroqMatchResult>(rawText,
new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
return match?.Matched == true ? match.SupplierId : null;
}
public SupplierSearchService(HttpClient httpClient, IConfiguration config) public SupplierSearchService(HttpClient httpClient, IConfiguration config)
{ {
_httpClient = httpClient; _httpClient = httpClient;
@ -27,26 +147,32 @@ namespace CPRNIMS.Domain.Services.Canvass
} }
public async Task<List<SupplierResponse>> SearchAndFilterSuppliersAsync( public async Task<List<SupplierResponse>> SearchAndFilterSuppliersAsync(
string itemName, string itemDescription) string itemName, string itemDescription, bool isInternational)
{ {
var locality = isInternational
? "all over Asia including Philippines"
: "Philippines";
// Step 1: Tavily — get supplier URLs // Step 1: Tavily — get supplier URLs
var (searchContent, supplierUrls) = await SearchTavilyAsync(itemName, itemDescription); var (searchContent, supplierUrls) = await SearchTavilyAsync(itemName, itemDescription, locality);
// Step 2: Fetch contact pages from discovered URLs // Step 2: Fetch contact pages from discovered URLs
var contactContent = await FetchContactPagesAsync(supplierUrls); var contactContent = await FetchContactPagesAsync(supplierUrls);
// Step 3: Combine search + contact content, send to Groq // Step 3: Combine search + contact content, send to Groq
var combined = searchContent + " CONTACT_PAGES_DATA: " + contactContent; var combined = searchContent + " CONTACT_PAGES_DATA: " + contactContent;
var suppliers = await FilterWithGroqAsync(itemName, itemDescription, combined); var suppliers = await FilterWithGroqAsync(itemName, itemDescription, combined,isInternational);
return suppliers; return suppliers;
} }
// ── Tavily ────────────────────────────────────────────────────────────── // ── Tavily ──
private async Task<(string content, List<string> urls)> SearchTavilyAsync( private async Task<(string content, List<string> urls)> SearchTavilyAsync(
string itemName, string itemDescription) string itemName, string itemDescription,string locality)
{ {
var query = $"{itemName} {itemDescription} suppliers Philippines budget price contact email phone"; try
{
var query = $"{itemName} {itemDescription} suppliers {locality} budget price contact email phone";
var payload = new var payload = new
{ {
@ -96,8 +222,15 @@ namespace CPRNIMS.Domain.Services.Canvass
return (fullText, urls); return (fullText, urls);
} }
catch (Exception ex)
{
ex.ToString();
throw;
}
// ── Fetch Contact Pages ────────────────────────────────────────────────── }
// ── Fetch Contact Pages ───
private async Task<string> FetchContactPagesAsync(List<string> baseUrls) private async Task<string> FetchContactPagesAsync(List<string> baseUrls)
{ {
var sb = new StringBuilder(); var sb = new StringBuilder();
@ -148,16 +281,42 @@ namespace CPRNIMS.Domain.Services.Canvass
// ── Groq ───────────────────────────────────────────────────────────────── // ── Groq ─────────────────────────────────────────────────────────────────
private async Task<List<SupplierResponse>> FilterWithGroqAsync( private async Task<List<SupplierResponse>> FilterWithGroqAsync(
string itemName, string itemDescription, string searchContent) string itemName, string itemDescription, string searchContent, bool isInternational)
{ {
var prompt = $"Extract top 10 unique suppliers for: {itemName} {itemDescription}. " + try
"Prioritize Philippines suppliers first. " + {
"IMPORTANT: Look carefully in CONTACT_PAGES_DATA section for real phone numbers and emails. " + var localityRule = isInternational
"Extract exact email addresses and phone numbers found. " + ? "1. Include suppliers from Philippines first, then other Asian countries (e.g. China, Japan, South Korea, Taiwan, India, Singapore).\n"
"For domains without contact data found, infer email as sales@domain or info@domain. " + : "1. STRICT: Include ONLY suppliers based in the Philippines. Exclude ANY supplier from other countries — even if they ship to Philippines. If a supplier's country is not Philippines, skip it entirely.\n";
"Prefer budget-friendly suppliers. No duplicates. " +
"Return ONLY a raw JSON array: company_name, country, phone_number, contact_email, website, estimated_price_usd, item_specifications. " + var prompt =
$"Null for missing. JSON array only. Data: {searchContent}"; $"TASK: Extract up to 10 unique suppliers that sell: [{itemName}] — {itemDescription}.\n\n" +
"RULES:\n" +
localityRule +
"2. Prefer budget-friendly suppliers with known pricing.\n" +
"3. DEDUPLICATION (strict): Each entry must have a unique company_name, contact_email, AND phone_number.\n" +
" - If two entries share the same email OR phone number, keep only the first.\n" +
" - If two inferred emails resolve to the same address, keep only one.\n" +
"4. CONTACT EXTRACTION:\n" +
" - Look in the CONTACT_PAGES_DATA section for real emails and phone numbers.\n" +
" - Use exact values found. Do not fabricate contact details.\n" +
" - If no email is found for a domain, infer: sales@domain.com or info@domain.com.\n" +
" - If no phone is found, use null — do not guess.\n" +
"5. estimated_price_usd MUST be a number (e.g. 12.50) or null. NEVER a string.\n" +
"6. Exclude any supplier with no company_name or no contact_email.\n\n" +
"OUTPUT FORMAT:\n" +
"Return ONLY a valid raw JSON array. No markdown. No explanation. No extra text.\n" +
"Each object must have exactly these fields:\n" +
" company_name (string)\n" +
" country (string)\n" +
" phone_number (string | null)\n" +
" contact_email (string | null)\n" +
" website (string | null)\n" +
" estimated_price_usd (number | null)\n" +
" item_specifications (string | null)\n\n" +
$"DATA:\n{searchContent}";
var payload = new var payload = new
{ {
@ -189,20 +348,52 @@ namespace CPRNIMS.Domain.Services.Canvass
var match = Regex.Match(rawText, @"\[[\s\S]*\]"); var match = Regex.Match(rawText, @"\[[\s\S]*\]");
if (!match.Success) return new List<SupplierResponse>(); if (!match.Success) return new List<SupplierResponse>();
var groqList = JsonSerializer.Deserialize<List<GroqSupplierResult>>(match.Value, // Add the converter to the shared options
new JsonSerializerOptions { PropertyNameCaseInsensitive = true }) var jsonOptions = new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true,
Converters = { new FlexibleDecimalConverter() }
};
var groqList = JsonSerializer.Deserialize<List<GroqSupplierResult>>(match.Value, jsonOptions)
?? new List<GroqSupplierResult>(); ?? new List<GroqSupplierResult>();
var seen = new HashSet<string>(StringComparer.OrdinalIgnoreCase); var seenNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
var seenEmails = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
var seenPhones = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
var suppliers = new List<SupplierResponse>(); var suppliers = new List<SupplierResponse>();
var allowedCountries = isInternational
? new HashSet<string>(StringComparer.OrdinalIgnoreCase)
{
"Philippines", "China", "Japan", "South Korea", "Taiwan",
"India", "Singapore", "Malaysia", "Thailand", "Vietnam",
"Indonesia", "Hong Kong"
}
: new HashSet<string>(StringComparer.OrdinalIgnoreCase)
{
"Philippines"
};
foreach (var s in groqList) foreach (var s in groqList)
{ {
var key = (s.CompanyName ?? "").Trim().ToLower(); var key = (s.CompanyName ?? "").Trim().ToLower();
if (string.IsNullOrEmpty(key) || seen.Contains(key)) continue; var email = (s.ContactEmail ?? "").Trim().ToLower();
seen.Add(key); var phone = NormalizePhone(s.PhoneNumber ?? "");
if (string.IsNullOrEmpty(s.ContactEmail)) continue; // Skip if no company name
if (string.IsNullOrEmpty(key)) continue;
// Skip if no email
if (string.IsNullOrEmpty(email)) continue;
// ✅ Skip if company name, email, OR phone already seen
if (seenNames.Contains(key)) continue;
if (seenEmails.Contains(email)) continue;
if (!string.IsNullOrEmpty(phone) && seenPhones.Contains(phone)) continue;
seenNames.Add(key);
seenEmails.Add(email);
if (!string.IsNullOrEmpty(phone)) seenPhones.Add(phone);
suppliers.Add(new SupplierResponse suppliers.Add(new SupplierResponse
{ {
@ -227,5 +418,36 @@ namespace CPRNIMS.Domain.Services.Canvass
return suppliers; return suppliers;
} }
catch (Exception ex)
{
ex.ToString();
throw;
}
}
private static string NormalizePhone(string phone)
{
if (string.IsNullOrWhiteSpace(phone)) return string.Empty;
// Strip everything except digits
var digits = Regex.Replace(phone, @"\D", "");
// Remove leading country code "1" for US/CA numbers (11 digits starting with 1)
if (digits.Length == 11 && digits.StartsWith("1"))
digits = digits[1..];
return digits;
}
private static string ExtractDomain(string url)
{
if (string.IsNullOrWhiteSpace(url)) return string.Empty;
try
{
if (!url.StartsWith("http")) url = "https://" + url;
var host = new Uri(url).Host;
return host.StartsWith("www.") ? host[4..] : host;
}
catch { return string.Empty; }
}
} }
} }

View File

@ -0,0 +1,49 @@
using CPRNIMS.Domain.Contracts.Common;
using CPRNIMS.Infrastructure.Database;
using Microsoft.EntityFrameworkCore;
namespace CPRNIMS.Domain.Services.Common
{
public class TransactionFacade : ITransactionFacade
{
private readonly NonInventoryDbContext _db;
public TransactionFacade(NonInventoryDbContext db)
=> _db = db ?? throw new ArgumentNullException(nameof(db));
public async Task<T> ExecuteAsync<T>(Func<Task<T>> operation, CancellationToken ct = default)
{
ArgumentNullException.ThrowIfNull(operation);
var strategy = _db.Database.CreateExecutionStrategy();
return await strategy.ExecuteAsync(async () =>
{
await using var tx = await _db.Database.BeginTransactionAsync(ct);
try
{
var result = await operation();
await _db.SaveChangesAsync(ct);
await tx.CommitAsync(ct);
return result;
}
catch
{
await tx.RollbackAsync(ct);
throw;
}
});
}
public async Task ExecuteAsync(Func<Task> operation, CancellationToken ct = default)
{
ArgumentNullException.ThrowIfNull(operation);
await ExecuteAsync<bool>(async () =>
{
await operation();
return true;
}, ct);
}
}
}

View File

@ -40,7 +40,7 @@ namespace CPRNIMS.Domain.Services.Finance
return allItems ?? new List<ReceivingDetail>(); return allItems ?? new List<ReceivingDetail>();
} }
public async Task<RRDetail> PostPutPayment(RRDetailsDto itemDto) public async Task<RRDetailDto> PostPutPayment(RRDetailsDto itemDto)
{ {
await _dbContext.Database await _dbContext.Database
.ExecuteSqlRawAsync("EXEC PostPutPayment @UserId, @PONo, @POTypeId, @PRDetailsId", .ExecuteSqlRawAsync("EXEC PostPutPayment @UserId, @PONo, @POTypeId, @PRDetailsId",
@ -48,7 +48,7 @@ namespace CPRNIMS.Domain.Services.Finance
new SqlParameter("@PONo", itemDto.PONo), new SqlParameter("@PONo", itemDto.PONo),
new SqlParameter("@POTypeId", itemDto.POTypeId), new SqlParameter("@POTypeId", itemDto.POTypeId),
new SqlParameter("@PRDetailsId", itemDto.PRDetailsId)); new SqlParameter("@PRDetailsId", itemDto.PRDetailsId));
return new RRDetail(); return new RRDetailDto();
} }
} }
} }

View File

@ -1,9 +1,11 @@
using CPRNIMS.Domain.Contracts.Inventory; using CPRNIMS.Domain.Contracts.Inventory;
using CPRNIMS.Infrastructure.Database; using CPRNIMS.Infrastructure.Database;
using CPRNIMS.Infrastructure.Dto.Inventory; using CPRNIMS.Infrastructure.Dto.Inventory;
using CPRNIMS.Infrastructure.Dto.Items; using CPRNIMS.Infrastructure.Dto.Inventory.Request;
using CPRNIMS.Infrastructure.Entities.Finance; using CPRNIMS.Infrastructure.Dto.Inventory.Response;
using CPRNIMS.Infrastructure.Entities.Account;
using CPRNIMS.Infrastructure.Entities.Inventory; using CPRNIMS.Infrastructure.Entities.Inventory;
using Microsoft.AspNetCore.Identity;
using Microsoft.Data.SqlClient; using Microsoft.Data.SqlClient;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using System; using System;
@ -17,13 +19,113 @@ namespace CPRNIMS.Domain.Services.Inventory
public class Inventory : IInventory public class Inventory : IInventory
{ {
private readonly NonInventoryDbContext _dbContext; private readonly NonInventoryDbContext _dbContext;
public Inventory(NonInventoryDbContext dbContext) private readonly UserManager<ApplicationUser> _userManager;
public Inventory(NonInventoryDbContext dbContext, UserManager<ApplicationUser> userManager)
{ {
_dbContext = dbContext; _dbContext = dbContext;
_userManager = userManager;
} }
public async Task<List<Infrastructure.Entities.Inventory.ItemDetail>> GetInventoryById(InventoryDto itemDto) public async Task<TransactContextDto?> GetTransactContextAsync(int inventoryId, CancellationToken ct)
{ {
try var inv = await _dbContext.Inventories
.Where(i => i.InventoryId == inventoryId && i.IsActive)
.Select(i => new
{
i.InventoryId,
i.QtyOnHand,
i.QtyIn,
i.QtyOut,
LotNo = i.Lot != null ? i.Lot.LotName : null,
Department = i.User != null && i.User.Department != null
? i.User.Department.Department
: null,
i.ItemNo,
FirstDetail = i.InventTrans
.Where(t => t.IsActive)
.SelectMany(t => t.InventTransDetails)
.Where(d => d.PRDetails != null
&& d.PRDetails.PRs != null
&& d.IsActive)
.Select(d => new
{
d.PRDetails!.ItemName,
PRNo = d.PRDetails.PRs!.PRNo
})
.FirstOrDefault()
})
.FirstOrDefaultAsync(ct);
if (inv == null) return null;
// Computed property QtyAvailableToReturn can't be used in EF Where,
// so filter after projection using a raw expression in the query.
var openRIS = await _dbContext.RIS
.Where(r => r.InventoryId == inventoryId
&& r.Status == 1)
.Select(r => new RISReferenceDto
{
RISId = r.RISId,
RISNo = r.RISNo,
QtyIssued = r.QtyIssued,
TotalReturned = r.MaterialReturns
.Where(m => m.Status != 2)
.Sum(m => (int?)m.QtyReturned) ?? 0,
DisciplineName = r.Discipline.DisciplineName,
CreatedDate = r.CreatedDate
})
// Can't use the computed property here — EF won't translate it
// so we repeat the expression inline
.Where(r => r.QtyIssued - r.TotalReturned > 0)
.OrderByDescending(r => r.CreatedDate)
.ToListAsync(ct);
var disciplines = await GetDisciplinesAsync(ct);
var projectCodes = await GetProjectCodesAsync(ct);
return new TransactContextDto
{
InventoryId = inv.InventoryId,
ItemName = inv.FirstDetail?.ItemName ?? "—",
PRNo = inv.FirstDetail.PRNo,
ItemNo = inv.ItemNo,
LotNo = inv.LotNo,
Department = inv.Department,
QtyOnHand = inv.QtyOnHand,
QtyIn = inv.QtyIn,
QtyOut = inv.QtyOut,
ProjectCodes = projectCodes,
Disciplines = disciplines,
OpenRISList = openRIS
};
}
public async Task<IReadOnlyList<DisciplineDto>> GetDisciplinesAsync(CancellationToken ct)
{
return await _dbContext.Disciplines
.AsNoTracking()
.OrderBy(d => d.DisciplineName)
.Select(d => new DisciplineDto
{
DisciplineId = d.DisciplineId,
DisciplineName = d.DisciplineName
})
.ToListAsync(ct);
}
public async Task<IReadOnlyList<ProjectCodeDto>> GetProjectCodesAsync(CancellationToken ct)
{
return await _dbContext.ProjectCodes
.Where(p => p.StatusName != "Completed" && p.IsActive)
.OrderBy(d => d.ProjectName)
.Select(d => new ProjectCodeDto
{
ProjectCodeId = d.ProjectCodeId,
ProjectCode = d.ProjectCode ?? "N/A",
ProjectName = d.ProjectName ?? "N/A"
})
.ToListAsync(ct);
}
public async Task<List<ItemDetail>> GetInventoryById(InventoryDto itemDto)
{ {
var allItems = await _dbContext.ItemDetails var allItems = await _dbContext.ItemDetails
.FromSqlRaw($"EXEC GetInventoryById @UserId = '{itemDto.UserId}',@InventoryId = '{itemDto.InventoryId}'") .FromSqlRaw($"EXEC GetInventoryById @UserId = '{itemDto.UserId}',@InventoryId = '{itemDto.InventoryId}'")
@ -31,17 +133,86 @@ namespace CPRNIMS.Domain.Services.Inventory
return allItems ?? new List<Infrastructure.Entities.Inventory.ItemDetail>(); return allItems ?? new List<Infrastructure.Entities.Inventory.ItemDetail>();
} }
catch (SqlException ex) public async Task<PagedResult<InventoryResponse>> GetInventory(InventoryRequest request, CancellationToken ct)
{ {
ex.ToString(); var parameters = new[]
throw; {
} new SqlParameter("@UserId", request.UserId),
new SqlParameter("@SearchPRNo", request.SearchPRNo ?? ""),
new SqlParameter("@SearchItemNo", request.SearchItemNo ?? ""),
new SqlParameter("@SearchItemName", request.SearchItemName ?? ""),
new SqlParameter("@SearchDept", request.SearchDept ?? ""),
new SqlParameter("@SearchProjectCode",request.SearchProjectCode ?? ""),
new SqlParameter("@PageNumber", request.PageNumber),
new SqlParameter("@PageSize", request.PageSize)
};
var departmentList = new List<string>();
int totalCount = 0;
var items = new List<InventoryResponse>();
await using var conn = _dbContext.Database.GetDbConnection();
await conn.OpenAsync(ct);
using var cmd = conn.CreateCommand();
cmd.CommandText = @"EXEC GetInventory @UserId, @SearchPRNo, @SearchItemNo, @SearchItemName, @SearchDept,@SearchProjectCode, @PageNumber, @PageSize";
foreach (var p in parameters) cmd.Parameters.Add(p);
cmd.CommandTimeout = 60;
using var reader = await cmd.ExecuteReaderAsync(ct);
while (await reader.ReadAsync(ct))
departmentList.Add(reader.GetString(0));
await reader.NextResultAsync(ct);
if (await reader.ReadAsync(ct))
totalCount = reader.GetInt32(0);
await reader.NextResultAsync(ct);
while (await reader.ReadAsync(ct))
{
items.Add(new InventoryResponse
{
InventoryId = reader.GetInt32(reader.GetOrdinal("InventoryId")),
QtyIn = reader["QtyIn"] as decimal? ?? 0,
QtyOut = reader["QtyOut"] as decimal? ?? 0,
QtyOnHand = reader["QtyOnHand"] as decimal? ?? 0,
LotNo = reader["LotNo"]?.ToString(),
PRNo = Convert.ToInt64(reader["PRNo"]),
UserId = reader["UserId"]?.ToString(),
ItemName = reader["ItemName"]?.ToString(),
ItemNo = Convert.ToInt64(reader["ItemNo"]),
ItemDescription = reader["ItemDescription"]?.ToString(),
ItemCategoryName = reader["ItemCategoryName"]?.ToString(),
Department = reader["Department"]?.ToString(),
ProjectCode = reader["ProjectCode"]?.ToString(),
CreatedDate = reader["CreatedDate"] == DBNull.Value
? DateTime.MinValue
: Convert.ToDateTime(reader["CreatedDate"])
});
} }
public async Task<List<Infrastructure.Entities.Inventory.Inventory>> await conn.CloseAsync();
GetInventoryByUserId(InventoryDto itemDto)
return new PagedResult<InventoryResponse>
{ {
try Data = items,
TotalCount = totalCount,
PageNumber = request.PageNumber,
PageSize = request.PageSize,
DepartmentList = departmentList
};
}
public async Task<List<InventoryByIdResponse>> GetInventoryById(InventoryRequest itemDto,CancellationToken ct)
{
var allItems = await _dbContext.InventoryByIdResponses
.FromSqlRaw($"EXEC GetInventoryById @InventoryId",
new SqlParameter("@InventoryId", itemDto.InventoryId))
.ToListAsync(ct);
return allItems ?? new List<InventoryByIdResponse>();
}
public async Task<List<Infrastructure.Entities.Inventory.Inventory>>GetInventoryByUserId(InventoryDto itemDto)
{ {
if (itemDto.IsSorting == false) if (itemDto.IsSorting == false)
{ {
@ -58,16 +229,8 @@ namespace CPRNIMS.Domain.Services.Inventory
return allItems ?? new List<Infrastructure.Entities.Inventory.Inventory>(); return allItems ?? new List<Infrastructure.Entities.Inventory.Inventory>();
} }
catch (SqlException ex)
{
ex.ToString();
throw;
}
}
public async Task<List<Lot>> GetLotNoById(InventoryDto itemDto) public async Task<List<Lot>> GetLotNoById(InventoryDto itemDto)
{
try
{ {
var allItems = await _dbContext.Lots var allItems = await _dbContext.Lots
.FromSqlRaw($"EXEC GetLotById @UserId = '{itemDto.UserId}'") .FromSqlRaw($"EXEC GetLotById @UserId = '{itemDto.UserId}'")
@ -75,12 +238,6 @@ namespace CPRNIMS.Domain.Services.Inventory
return allItems ?? new List<Lot>(); return allItems ?? new List<Lot>();
} }
catch (SqlException ex)
{
ex.ToString();
throw;
}
}
public async Task<List<Lot>> GetLotNo(InventoryDto itemDto) public async Task<List<Lot>> GetLotNo(InventoryDto itemDto)
{ {
@ -93,8 +250,6 @@ namespace CPRNIMS.Domain.Services.Inventory
} }
public async Task<Lot> PostPutLotNo(InventoryDto itemDto) public async Task<Lot> PostPutLotNo(InventoryDto itemDto)
{
try
{ {
await _dbContext.Database await _dbContext.Database
.ExecuteSqlRawAsync("EXEC PostPutLotNo @UserId, @LotId, @LotTypeId,@LotName", .ExecuteSqlRawAsync("EXEC PostPutLotNo @UserId, @LotId, @LotTypeId,@LotName",
@ -104,16 +259,8 @@ namespace CPRNIMS.Domain.Services.Inventory
new SqlParameter("@LotName", itemDto.LotName)); new SqlParameter("@LotName", itemDto.LotName));
return new Lot(); return new Lot();
} }
catch (SqlException ex)
{
ex.ToString();
throw;
}
}
public async Task<Infrastructure.Entities.Inventory.Inventory> PostPutReqApproval(InventoryDto itemDto) public async Task<Infrastructure.Entities.Inventory.Inventory> PostPutReqApproval(InventoryDto itemDto)
{
try
{ {
await _dbContext.Database await _dbContext.Database
.ExecuteSqlRawAsync("EXEC PostPutReqApproval @UserId, @ItemNo, @Status, @Remarks", .ExecuteSqlRawAsync("EXEC PostPutReqApproval @UserId, @ItemNo, @Status, @Remarks",
@ -123,16 +270,8 @@ namespace CPRNIMS.Domain.Services.Inventory
new SqlParameter("@Remarks", itemDto.Remarks ?? "N/A")); new SqlParameter("@Remarks", itemDto.Remarks ?? "N/A"));
return new Infrastructure.Entities.Inventory.Inventory(); return new Infrastructure.Entities.Inventory.Inventory();
} }
catch (SqlException ex)
{
ex.ToString();
throw;
}
}
public async Task<ItemDetail> PostPutLotBin(InventoryDto itemDto) public async Task<ItemDetail> PostPutLotBin(InventoryDto itemDto)
{
try
{ {
await _dbContext.Database await _dbContext.Database
.ExecuteSqlRawAsync("EXEC PostPutLotBin @UserId, @LotId, @InventoryId", .ExecuteSqlRawAsync("EXEC PostPutLotBin @UserId, @LotId, @InventoryId",
@ -141,16 +280,8 @@ namespace CPRNIMS.Domain.Services.Inventory
new SqlParameter("@LotId", itemDto.LotId)); new SqlParameter("@LotId", itemDto.LotId));
return new ItemDetail(); return new ItemDetail();
} }
catch (SqlException ex)
{
ex.ToString();
throw;
}
}
public async Task<RequestItem> PostPutReqItems(InventoryDto itemDto) public async Task<RequestItem> PostPutReqItems(InventoryDto itemDto)
{
try
{ {
if (itemDto.QtyReceived == null || itemDto.QtyReceived == 0) if (itemDto.QtyReceived == null || itemDto.QtyReceived == 0)
{ {
@ -172,16 +303,8 @@ namespace CPRNIMS.Domain.Services.Inventory
new SqlParameter("@LotId", itemDto.LotId)); new SqlParameter("@LotId", itemDto.LotId));
return new RequestItem(); return new RequestItem();
} }
catch (SqlException ex)
{
ex.ToString();
throw;
}
}
public async Task<List<RequestItemDetail>> GetRequestedItemByUserId(InventoryDto itemDto) public async Task<List<RequestItemDetail>> GetRequestedItemByUserId(InventoryDto itemDto)
{
try
{ {
var allItems = await _dbContext.RequestItemDetails var allItems = await _dbContext.RequestItemDetails
.FromSqlRaw($"EXEC GetRequestedItemByUserId @UserId = '{itemDto.UserId}', " + .FromSqlRaw($"EXEC GetRequestedItemByUserId @UserId = '{itemDto.UserId}', " +
@ -190,16 +313,8 @@ namespace CPRNIMS.Domain.Services.Inventory
return allItems ?? new List<RequestItemDetail>(); return allItems ?? new List<RequestItemDetail>();
} }
catch (SqlException ex)
{
ex.ToString();
throw;
}
}
public async Task<List<LotQtyByItem>> GetLotQtyByItem(InventoryDto itemDto) public async Task<List<LotQtyByItem>> GetLotQtyByItem(InventoryDto itemDto)
{
try
{ {
var allItems = await _dbContext.LotQtyByItems var allItems = await _dbContext.LotQtyByItems
.FromSqlRaw($"EXEC GetLotQtyByItem @UserId = '{itemDto.UserId}', " + .FromSqlRaw($"EXEC GetLotQtyByItem @UserId = '{itemDto.UserId}', " +
@ -208,11 +323,5 @@ namespace CPRNIMS.Domain.Services.Inventory
return allItems ?? new List<LotQtyByItem>(); return allItems ?? new List<LotQtyByItem>();
} }
catch (SqlException ex)
{
ex.ToString();
throw;
}
}
} }
} }

View File

@ -0,0 +1,307 @@
using CPRNIMS.Domain.Contracts.Inventory;
using CPRNIMS.Infrastructure.Database;
using CPRNIMS.Infrastructure.Dto.Inventory.Reports;
using CPRNIMS.Infrastructure.Dto.Inventory.Request;
using Microsoft.Data.SqlClient;
using Microsoft.EntityFrameworkCore;
using System.Drawing.Printing;
using static System.Runtime.InteropServices.JavaScript.JSType;
namespace CPRNIMS.Domain.Services.Inventory
{
public class InventoryReports : IInventoryReports
{
private readonly NonInventoryDbContext _db;
public InventoryReports(NonInventoryDbContext db) => _db = db;
public async Task<RISReportDto> GetRISReportAsync(InventoryReportsRequest request, string userName, int? departmentId, CancellationToken ct)
{
var endDate = request.DateTo.Date.AddDays(1);
var allowedAllDeptUsers = new[] { "LSKRISUR24", "LSCYNDIZ25", "LSJONTAN25", "LHRIOCAS24" };
bool seeAllDepartments = !string.IsNullOrWhiteSpace(userName)
&& allowedAllDeptUsers.Contains(userName, StringComparer.OrdinalIgnoreCase);
var dateToInclusive = request.DateTo.AddDays(1);
var query = _db.RIS
.Include(r => r.Discipline)
.Include(r => r.Inventory)
.Include(r => r.MaterialReturns)
.Skip((request.Page - 1) * request.PageSize)
.Take(request.PageSize)
.Where(r => r.CreatedDate >= request.DateFrom && r.CreatedDate < dateToInclusive);
if (departmentId.HasValue && !seeAllDepartments)
{
query = query.Where(m =>
m.Inventory.User != null &&
m.Inventory.User.DepartmentId == departmentId.Value);
}
var rows = await query
.OrderByDescending(r => r.CreatedDate)
.Select(r => new RISReportRow
{
RISNo = r.RISNo,
CreatedDate = r.CreatedDate,
ItemName = r.PRDetail != null ? r.PRDetail.ItemName : "—",
ItemNo = r.Inventory.ItemNo,
DisciplineName = r.Discipline.DisciplineName,
ProjectName = r.ProjectCodes.ProjectName,
QtyIssued = r.QtyIssued,
TotalReturned = r.MaterialReturns
.Where(m => m.Status != 2)
.Sum(m => (int?)m.QtyReturned) ?? 0,
Status = r.Status,
StatusLabel = r.Status == 0 ? "Draft"
: r.Status == 1 ? "Approved"
: "Cancelled"
})
.ToListAsync(ct);
foreach (var row in rows)
row.NetIssued = row.QtyIssued - row.TotalReturned;
var summary = new RISReportSummary
{
TotalRIS = rows.Count,
TotalApproved = rows.Count(r => r.Status == 1),
TotalPending = rows.Count(r => r.Status == 0),
TotalCancelled = rows.Count(r => r.Status == 2),
TotalQtyIssued = rows.Sum(r => r.QtyIssued),
TotalQtyReturned = rows.Sum(r => r.TotalReturned),
TotalNetIssued = rows.Sum(r => r.NetIssued),
ApprovalRatePct = rows.Count > 0
? Math.Round(rows.Count(r => r.Status == 1) * 100m / rows.Count, 1)
: 0
};
var byDiscipline = rows
.GroupBy(r => r.DisciplineName)
.Select(g => new DisciplineCount { DisciplineName = g.Key, Count = g.Count() })
.OrderByDescending(d => d.Count)
.ToList();
var topRecipients = rows
.GroupBy(r => r.ProjectName)
.Select(g => new TopRecipient
{
IssuedTo = g.Key,
SlipCount = g.Count(),
TotalQty = g.Sum(r => r.QtyIssued)
})
.OrderByDescending(t => t.TotalQty)
.Take(5)
.ToList();
return new RISReportDto
{
ReportNo = $"RPT-RIS-{DateTime.Now:yyyyMM}-{Random.Shared.Next(1, 999):D3}",
DateFrom = request.DateFrom,
DateTo = request.DateTo,
Summary = summary,
Rows = rows,
ByDiscipline = byDiscipline,
TopRecipients = topRecipients
};
}
public async Task<MRSReportDto> GetMRSReportAsync(InventoryReportsRequest request, string userName, int? departmentId, CancellationToken ct)
{
var endDate = request.DateTo.Date.AddDays(1);
var allowedAllDeptUsers = new[] { "LSKRISUR24", "LSCYNDIZ25", "LSJONTAN25", "LHRIOCAS24" };
bool seeAllDepartments = !string.IsNullOrWhiteSpace(userName)
&& allowedAllDeptUsers.Contains(userName, StringComparer.OrdinalIgnoreCase);
var query = _db.MRS
.Include(m => m.RIS)
.Include(m => m.Inventory)
.ThenInclude(i => i.User)
.Skip((request.Page - 1) * request.PageSize)
.Take(request.PageSize)
.Where(m => m.CreatedDate >= request.DateFrom &&
m.CreatedDate < endDate);
if (departmentId.HasValue && !seeAllDepartments)
{
query = query.Where(m =>
m.Inventory.User != null &&
m.Inventory.User.DepartmentId == departmentId.Value);
}
var rows = await query
.OrderByDescending(m => m.CreatedDate)
.Select(m => new MRSReportRow
{
MRSNo = m.MRSNo,
CreatedDate = m.CreatedDate,
RISNo = m.RIS.RISNo,
ItemName = m.RIS.PRDetail != null ? m.RIS.PRDetail.ItemName : "—",
ReturnedBy = m.ReturnedBy,
QtyReturned = m.QtyReturned,
Condition = m.Condition ?? "Good",
Status = m.Status,
StatusLabel = m.Status == 0 ? "Draft"
: m.Status == 1 ? "Approved"
: "Cancelled"
})
.ToListAsync(ct);
// Total RIS qty issued in the same period (for the comparison panel)
var totalRISQty = await _db.RIS
.Where(r => r.CreatedDate >= request.DateFrom && r.CreatedDate < endDate
&& r.Status != 2)
.SumAsync(r => (int?)r.QtyIssued, ct) ?? 0;
var totalReturned = rows.Where(r => r.Status != 2).Sum(r => r.QtyReturned);
var goodCount = rows.Count(r => r.Condition == "Good");
var summary = new MRSReportSummary
{
TotalMRS = rows.Count,
TotalQtyReturned = totalReturned,
TotalQtyIssuedRIS = totalRISQty,
NetQtyConsumed = totalRISQty - totalReturned,
ReturnRatePct = totalRISQty > 0
? Math.Round(totalReturned * 100m / totalRISQty, 1)
: 0,
GoodConditionPct = rows.Count > 0
? Math.Round(goodCount * 100m / rows.Count, 1)
: 0
};
var byCondition = rows
.Where(r => r.Status != 2)
.GroupBy(r => r.Condition)
.Select(g => new ConditionTotal { Condition = g.Key, TotalQty = g.Sum(r => r.QtyReturned) })
.OrderByDescending(c => c.TotalQty)
.ToList();
return new MRSReportDto
{
ReportNo = $"RPT-MRS-{DateTime.Now:yyyyMM}-{Random.Shared.Next(1, 999):D3}",
DateFrom = request.DateFrom,
DateTo = request.DateTo,
Summary = summary,
Rows = rows,
ByCondition = byCondition
};
}
public async Task<InventoryReportDto> GetInventoryReportAsync(InventoryReportsRequest request, string userName, int? departmentId, CancellationToken ct)
{
var allowedAllDeptUsers = new[] { "LSKRISUR24", "LSCYNDIZ25", "LSJONTAN25", "LHRIOCAS24" };
bool seeAllDepartments = !string.IsNullOrWhiteSpace(userName)
&& allowedAllDeptUsers.Contains(userName, StringComparer.OrdinalIgnoreCase);
var endDate = request.DateTo.Date.AddDays(1);
var dto = new InventoryReportDto
{
ReportNo = $"RPT-INV-{DateTime.Now:yyyyMM}-{Random.Shared.Next(1, 999):D3}",
AsOf = endDate,
Rows = new List<InventoryReportRow>(),
ByCategory = new List<CategoryStockLevel>(),
Alerts = new List<InventoryAlert>(),
Summary = new InventoryReportSummary(),
Departments = new List<string>(),
Page = request.Page,
PageSize = request.PageSize
};
var conn = _db.Database.GetDbConnection();
await using var cmd = conn.CreateCommand();
cmd.CommandText = "dbo.GetInventoryReport";
cmd.CommandType = System.Data.CommandType.StoredProcedure;
cmd.Parameters.Add(new SqlParameter("@DateFrom", (object?)request.DateFrom ?? DBNull.Value));
cmd.Parameters.Add(new SqlParameter("@DateTo", (object?)request.DateTo ?? DBNull.Value));
cmd.Parameters.Add(new SqlParameter("@Department", (object?)request.Department ?? DBNull.Value));
cmd.Parameters.Add(new SqlParameter("@DepartmentId", (object?)departmentId ?? DBNull.Value));
cmd.Parameters.Add(new SqlParameter("@SeeAllDepartments", seeAllDepartments ? 1 : 0));
cmd.Parameters.Add(new SqlParameter("@Page", request.Page));
cmd.Parameters.Add(new SqlParameter("@PageSize", request.PageSize));
cmd.Parameters.Add(new SqlParameter("@Paginate", request.Paginate ? 1 : 0));
bool openedHere = conn.State != System.Data.ConnectionState.Open;
if (openedHere) await conn.OpenAsync(ct);
try
{
await using var reader = await cmd.ExecuteReaderAsync(ct);
// 0: departments
while (await reader.ReadAsync(ct))
dto.Departments.Add(reader.GetString(reader.GetOrdinal("Department")));
// 1: detail rows
if (await reader.NextResultAsync(ct))
{
while (await reader.ReadAsync(ct))
{
dto.Rows.Add(new InventoryReportRow
{
ItemName = reader.GetString(reader.GetOrdinal("ItemName")),
ItemNo = reader.GetInt64(reader.GetOrdinal("ItemNo")),
ItemCategoryName = reader.GetString(reader.GetOrdinal("ItemCategoryName")),
LotNo = reader.IsDBNull(reader.GetOrdinal("LotNo")) ? null : reader.GetString(reader.GetOrdinal("LotNo")),
Department = reader.IsDBNull(reader.GetOrdinal("Department")) ? null : reader.GetString(reader.GetOrdinal("Department")),
CurrencyCode = reader.IsDBNull(reader.GetOrdinal("CurrencyCode")) ? null : reader.GetString(reader.GetOrdinal("CurrencyCode")),
QtyIn = reader.GetDecimal(reader.GetOrdinal("QtyIn")),
QtyOut = reader.GetDecimal(reader.GetOrdinal("QtyOut")),
QtyOnHand = reader.GetDecimal(reader.GetOrdinal("QtyOnHand")),
UnitPrice = reader.GetDecimal(reader.GetOrdinal("UnitPrice")),
StockPct = reader.GetInt32(reader.GetOrdinal("StockPct"))
});
}
}
// 2: summary
if (await reader.NextResultAsync(ct) && await reader.ReadAsync(ct))
{
dto.Summary = new InventoryReportSummary
{
TotalSKUs = reader.GetInt32(reader.GetOrdinal("TotalSKUs")),
TotalOnHand = reader.GetDecimal(reader.GetOrdinal("TotalOnHand")),
TotalQtyIn = reader.GetDecimal(reader.GetOrdinal("TotalQtyIn")),
TotalQtyOut = reader.GetDecimal(reader.GetOrdinal("TotalQtyOut")),
TotalValue = reader.GetDecimal(reader.GetOrdinal("TotalValue")),
LowStockCount = reader.GetInt32(reader.GetOrdinal("LowStockCount")),
OutOfStockCount = reader.GetInt32(reader.GetOrdinal("OutOfStockCount"))
};
}
// 3: by category
if (await reader.NextResultAsync(ct))
while (await reader.ReadAsync(ct))
dto.ByCategory.Add(new CategoryStockLevel
{
CategoryName = reader.GetString(reader.GetOrdinal("CategoryName")),
AvgStockPct = reader.GetInt32(reader.GetOrdinal("AvgStockPct"))
});
// 4: alerts
if (await reader.NextResultAsync(ct))
while (await reader.ReadAsync(ct))
dto.Alerts.Add(new InventoryAlert
{
ItemName = reader.GetString(reader.GetOrdinal("ItemName")),
QtyOnHand = reader.GetDecimal(reader.GetOrdinal("QtyOnHand")),
Severity = reader.GetString(reader.GetOrdinal("Severity"))
});
// 5: total row count
if (await reader.NextResultAsync(ct) && await reader.ReadAsync(ct))
dto.TotalRows = reader.GetInt32(reader.GetOrdinal("TotalRows"));
}
finally
{
if (conn.State == System.Data.ConnectionState.Open)
await conn.CloseAsync();
}
return dto;
}
}
}

View File

@ -0,0 +1,260 @@
using CPRNIMS.Domain.Contracts.Common;
using CPRNIMS.Domain.Contracts.Inventory;
using CPRNIMS.Infrastructure.Database;
using CPRNIMS.Infrastructure.Dto.Inventory;
using CPRNIMS.Infrastructure.Dto.Inventory.Request;
using CPRNIMS.Infrastructure.Entities.Inventory;
using Microsoft.EntityFrameworkCore;
namespace CPRNIMS.Domain.Services.Inventory
{
public class MRS : IMRS
{
private readonly NonInventoryDbContext _db;
private readonly ITransactionFacade _transactionFacade;
public MRS(NonInventoryDbContext db, ITransactionFacade transactionFacade)
{
_db = db;
_transactionFacade = transactionFacade;
}
public async Task ApproveAsync(long mrsId, string approvedBy, CancellationToken ct)
{
//var user = await _userManager.FindByNameAsync(approvedBy);
//if (user != null)
//{
// bool isApprover2 = await _userManager.IsInRoleAsync(user, "APPROVER2");
// if (!isApprover2)
// {
// throw new Exception("You have no permission to approve this item.");
// }
//}
var rms = await _db.MRS.FindAsync(mrsId)
?? throw new InvalidOperationException("MRS not found.");
if (rms.Status != 0)
throw new InvalidOperationException("Only Draft MRS records can be approved.");
rms.Status = 1; // Approved
rms.ApprovedBy = approvedBy;
rms.ApprovedDate = DateTime.Now;
await _db.SaveChangesAsync(ct);
}
public async Task CancelAsync(CancelMRSRequest request,string canceledBy, CancellationToken ct)
{
//var user = await _userManager.FindByNameAsync(canceledBy);
//if (user != null)
//{
// bool isApprover2 = await _userManager.IsInRoleAsync(user, "APPROVER2");
// if (!isApprover2)
// {
// throw new Exception("You have no permission to cancel this item.");
// }
//}
await _transactionFacade.ExecuteAsync(async () =>
{
var mrs = await _db.MRS
.Include(m => m.Inventory)
.FirstOrDefaultAsync(m => m.MRSId == request.MRSId)
?? throw new InvalidOperationException("MRS not found.");
if (mrs.Status == 2)
throw new InvalidOperationException("MRS is already cancelled.");
// Reverse the return: deduct qty back out
mrs.Inventory.QtyOut = Math.Max(0m, mrs.Inventory.QtyOut) + mrs.QtyReturned;
mrs.Inventory.QtyOnHand = mrs.Inventory.QtyIn - mrs.Inventory.QtyOut;
mrs.Reason = request.Reason;
mrs.Status = 2;
mrs.CanceledDate = DateTime.Now;
mrs.CanceledBy = canceledBy;
}, ct);
}
public async Task<Infrastructure.Entities.Inventory.MRS> CreateAsync(CreateMRSRequest dto, string createdBy, CancellationToken ct)
{
return await _transactionFacade.ExecuteAsync(async () =>
{
var ris = await _db.RIS
.Include(r => r.Inventory)
.FirstOrDefaultAsync(r => r.RISId == dto.RISId, ct)
?? throw new InvalidOperationException("Referenced RIS not found.");
if (dto.QtyReturned > ris.QtyIssued)
throw new InvalidOperationException(
$"Cannot return more than issued. Issued: {ris.QtyIssued}.");
var mrsNo = await GenerateMRSNoAsync();
var mrs = new Infrastructure.Entities.Inventory.MRS
{
MRSNo = mrsNo,
RISId = dto.RISId,
InventoryId = ris.InventoryId,
ReturnedBy = dto.ReturnedBy,
QtyReturned = dto.QtyReturned,
Condition = dto.Condition,
Remarks = dto.Remarks,
Status = 1,//Matic Approve for now
CreatedBy = createdBy,
CreatedDate = DateTime.Now
};
_db.MRS.Add(mrs);
var inventory = ris.Inventory;
inventory.QtyOut = Math.Max(0m, inventory.QtyOut - dto.QtyReturned);
inventory.QtyOnHand = inventory.QtyIn - inventory.QtyOut;
var trans = await _db.InventTrans
.FirstOrDefaultAsync(t => t.InventoryId == ris.InventoryId && t.IsActive == true, ct)!;
_db.InventTransDetails.Add(new InventTransDetail
{
InventTransId = trans.InventTransId,
TransTypeId = 6,
QtyIn = dto.QtyReturned,
CreatedDate = DateTime.Now,
Remarks = $"MRS: {mrsNo} — return from MRS: {ris.RISNo}",
IsActive = true
});
return mrs;
}, ct);
}
public async Task<Infrastructure.Entities.Inventory.MRS?> GetByIdAsync(long mrsId, CancellationToken ct)
=> await _db.MRS
.Include(r => r.Inventory)
.Include(r => r.RIS)
.FirstOrDefaultAsync(r => r.RISId == mrsId, ct);
public async Task<MRSPagedResult> GetPagedAsync(MRSFilterDto filter, CancellationToken ct,
int? departmentId = null, string? userName = "")
{
var allowedAllDeptUsers = new[] { "LSKRISUR24", "LSCYNDIZ25", "LSJONTAN25", "LHRIOCAS24" };
bool seeAllDepartments = !string.IsNullOrWhiteSpace(userName)
&& allowedAllDeptUsers.Contains(userName, StringComparer.OrdinalIgnoreCase);
var q = _db.MRS
.Include(m => m.RIS)
.Include(m => m.Inventory)
.AsQueryable();
if (departmentId.HasValue && !seeAllDepartments)
{
q = q.Where(itd =>
itd.Inventory.User.DepartmentId == departmentId.Value);
}
if (!string.IsNullOrWhiteSpace(filter.SearchMRSNo))
q = q.Where(m => m.MRSNo.Contains(filter.SearchMRSNo));
if (filter.RISId.HasValue)
q = q.Where(m => m.RISId == filter.RISId.Value);
if (filter.Status.HasValue)
q = q.Where(m => m.Status == filter.Status.Value);
if (filter.DateFrom.HasValue)
q = q.Where(m => m.CreatedDate >= filter.DateFrom.Value);
if (filter.DateTo.HasValue)
q = q.Where(m => m.CreatedDate <= filter.DateTo.Value.AddDays(1));
var total = await q.CountAsync(ct);
var data = await q
.OrderByDescending(m => m.CreatedDate)
.Skip((filter.PageNumber - 1) * filter.PageSize)
.Take(filter.PageSize)
.Select(m => new MRSPagedDto
{
MRSId = m.MRSId,
MRSNo = m.MRSNo,
RISId = m.RISId,
RISNo = m.RIS.RISNo,
InventoryId = m.InventoryId,
ItemName = m.Inventory.InventTrans
.SelectMany(t => t.InventTransDetails)
.Select(d => d.PRDetails != null ? d.PRDetails.ItemName : "—")
.FirstOrDefault() ?? "—",
ReturnedBy = m.ReturnedBy,
QtyReturned = m.QtyReturned,
Condition = m.Condition,
Remarks = m.Remarks,
Status = m.Status,
CreatedBy = m.CreatedBy,
CreatedDate = m.CreatedDate,
ApprovedBy = m.ApprovedBy,
ApprovedDate = m.ApprovedDate
})
.ToListAsync(ct);
return new MRSPagedResult { Data = data, RecordsTotal = total };
}
private async Task<string> GenerateMRSNoAsync()
{
var year = DateTime.Now.Year;
var month = DateTime.Now.Month.ToString("D2");
var count = await _db.MRS.CountAsync(m => m.CreatedDate.Year == year) + 1;
return $"MRS-{year}{month}-{count:D4}";
}
public async Task<IReadOnlyList<RISSearchResultDto>> SearchRISForReturnAsync(string? risNoQuery, int? projectCodeId, CancellationToken ct)
{
var query = _db.RIS
.Where(r => r.Status == 1);
if (projectCodeId.HasValue)
query = query.Where(r => r.ProjectCodeId == projectCodeId.Value);
if (!string.IsNullOrWhiteSpace(risNoQuery))
query = query.Where(r => r.RISNo.Contains(risNoQuery));
return await query
.Select(r => new RISSearchResultDto
{
RISId = r.RISId,
RISNo = r.RISNo,
ProjectCodeId = r.ProjectCodeId,
ProjectCode = r.ProjectCodes.ProjectCode ?? "N/A",
ProjectName = r.ProjectCodes.ProjectName ?? "N/A",
DisciplineName = r.Discipline.DisciplineName,
QtyAvailableToReturn = r.QtyIssued - r.MaterialReturns.Sum(m => m.QtyReturned)
})
.Where(r => r.QtyAvailableToReturn > 0)
.OrderByDescending(r => r.RISId)
.Take(20)
.ToListAsync(ct);
}
public async Task<IReadOnlyList<ProjectCodeOptionDto>> GetProjectsWithOpenRISAsync(string? nameQuery, CancellationToken ct)
{
var query = _db.RIS
.Where(r => r.Status == 1 && r.QtyIssued > r.MaterialReturns.Sum(m => m.QtyReturned));
if (!string.IsNullOrWhiteSpace(nameQuery))
query = query.Where(r =>
r.ProjectCodes.ProjectName.Contains(nameQuery) ||
r.ProjectCodes.ProjectCode.Contains(nameQuery));
return await query
.Select(r => new ProjectCodeOptionDto
{
ProjectCodeId = r.ProjectCodeId,
ProjectCode = r.ProjectCodes.ProjectCode ?? "N/A",
ProjectName = r.ProjectCodes.ProjectName ?? "N/A"
})
.Distinct()
.OrderBy(p => p.ProjectName)
.Take(20)
.ToListAsync(ct);
}
}
}

View File

@ -0,0 +1,331 @@
using CPRNIMS.Domain.Contracts.Common;
using CPRNIMS.Domain.Contracts.Inventory;
using CPRNIMS.Infrastructure.Database;
using CPRNIMS.Infrastructure.Dto.Inventory;
using CPRNIMS.Infrastructure.Dto.Inventory.Request;
using CPRNIMS.Infrastructure.Dto.Inventory.Response;
using CPRNIMS.Infrastructure.Entities.Inventory;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CPRNIMS.Domain.Services.Inventory
{
public class RIS : IRIS
{
private readonly NonInventoryDbContext _db;
private readonly ITransactionFacade _transactionFacade;
public RIS(NonInventoryDbContext db, ITransactionFacade transactionFacade)
{
_db = db;
_transactionFacade = transactionFacade;
}
public async Task<Infrastructure.Entities.Inventory.RIS> CreateAsync(CreateRISRequest dto, string createdBy, CancellationToken ct)
{
return await _transactionFacade.ExecuteAsync(async () =>
{
var inventory = await _db.Inventories
.FirstOrDefaultAsync(i => i.InventoryId == dto.InventoryId, ct)
?? throw new InvalidOperationException("Inventory record not found.");
if (inventory.QtyOnHand < dto.QtyIssued)
throw new InvalidOperationException(
$"Insufficient stock. On hand: {inventory.QtyOnHand}, requested: {dto.QtyIssued}.");
var risNo = await GenerateRISNoAsync(ct);
var ris = new Infrastructure.Entities.Inventory.RIS
{
RISNo = risNo,
InventoryId = dto.InventoryId,
PRDetailId = dto.PRDetailId,
ProjectCodeId = dto.ProjectCodeId,
DisciplineId = dto.DisciplineId,
QtyIssued = dto.QtyIssued,
Remarks = dto.Remarks,
Status = 1,//Approved alredy 0 is Draft
CreatedBy = createdBy,
CreatedDate = DateTime.Now
};
_db.RIS.Add(ris);
var trans = await _db.InventTrans
.Where(t => t.InventoryId == dto.InventoryId && t.IsActive == true)
.FirstOrDefaultAsync(ct)
?? throw new InvalidOperationException(
"No active InventTrans found for this inventory record.");
_db.InventTransDetails.Add(new InventTransDetail
{
InventTransId = trans.InventTransId,
TransTypeId = 5,
PRDetailId = dto.PRDetailId,
QtyOut = dto.QtyIssued,
CreatedDate = DateTime.Now,
Remarks = $"RIS: {risNo}",
IsActive = true
});
inventory.QtyOut = Math.Max(0m, inventory.QtyOut) + dto.QtyIssued;
inventory.QtyOnHand = Math.Max(0m, inventory.QtyIn) - (decimal)inventory.QtyOut;
return ris;
}, ct);
}
public async Task ApproveAsync(ApproveRISRequest request, string approvedBy, CancellationToken ct)
{
//var user = await _userManager.FindByNameAsync(approvedBy);
//if (user != null)
//{
// bool isApprover2 = await _userManager.IsInRoleAsync(user, "APPROVER2");
// if (!isApprover2)
// {
// throw new Exception("You have no permission to approved this item.");
// }
//}
var ris = await _db.RIS.FindAsync(request.RISId, ct)
?? throw new InvalidOperationException("RIS not found.");
if (ris.Status != 0)
throw new InvalidOperationException("Only Draft RIS records can be approved.");
ris.Status = 1; // Approved
ris.ApprovedBy = approvedBy;
ris.ApprovedDate = DateTime.Now;
await _db.SaveChangesAsync(ct);
}
public async Task CancelAsync(CancelRISRequest request,string canceledBy, CancellationToken ct)
{
//var user = await _userManager.FindByNameAsync(canceledBy);
//if (user != null)
//{
// bool isApprover2 = await _userManager.IsInRoleAsync(user, "APPROVER2");
// if (!isApprover2)
// {
// throw new Exception("You have no permission to cancel this item.");
// }
//}
await _transactionFacade.ExecuteAsync(async () =>
{
var ris = await _db.RIS
.Include(r => r.Inventory)
.FirstOrDefaultAsync(r => r.RISId == request.RISId, ct)
?? throw new InvalidOperationException("RIS not found.");
if (ris.Status == 2)
throw new InvalidOperationException("RIS is already cancelled.");
//Check if already approved the related RIS No to MRS must cannot be cancel
var mrs = await _db.MRS.FirstOrDefaultAsync(m => m.RISId == request.RISId, ct);
if (mrs != null)
{
if (mrs.Status == 1)
{
throw new InvalidOperationException(
$"MRS #{mrs.MRSNo} has already been approved in relation to RIS #{ris.RISNo}.");
}
mrs.Status = 2;
mrs.CanceledDate = DateTime.Now;
mrs.CanceledBy = canceledBy;
mrs.Reason = "Canceled in RIS already";
}
ris.Inventory.QtyOut = Math.Max(0m, ris.Inventory.QtyOut - ris.QtyIssued);
ris.Inventory.QtyOnHand = ris.Inventory.QtyIn - ris.Inventory.QtyOut;
ris.Reason = request.Reason;
ris.Status = 2; // Cancelled
var trans = await _db.InventTrans
.Where(t => t.InventoryId == ris.InventoryId && t.IsActive == true)
.FirstOrDefaultAsync(ct)
?? throw new InvalidOperationException(
"No active InventTrans found for this inventory record.");
_db.InventTransDetails.Add(new InventTransDetail
{
InventTransId = trans.InventTransId,
TransTypeId = 5,
PRDetailId = ris.PRDetailId,
QtyIn = ris.QtyIssued,//Return the issued Qty
CreatedDate = DateTime.Now,
Remarks = request.Reason,
IsActive = true
});
}, ct);
}
private async Task<string> GenerateRISNoAsync(CancellationToken ct)
{
var year = DateTime.Now.Year;
var month = DateTime.Now.Month.ToString("D2");
var count = await _db.RIS
.CountAsync(r => r.CreatedDate.Year == year,ct) + 1;
return $"RIS-{year}{month}-{count:D4}"; // e.g. RIS-202606-0001
}
public async Task<RISPagedResult> GetPagedAsync(RISFilterDto filter, CancellationToken ct,
int? departmentId = null, string? userName = "")
{
var allowedAllDeptUsers = new[] { "LSKRISUR24", "LSCYNDIZ25", "LSJONTAN25", "LHRIOCAS24" };
bool seeAllDepartments = !string.IsNullOrWhiteSpace(userName)
&& allowedAllDeptUsers.Contains(userName, StringComparer.OrdinalIgnoreCase);
var q = _db.RIS
.Include(r => r.Discipline)
.Include(r => r.Inventory)
.Include(r => r.MaterialReturns)
.AsQueryable();
if (departmentId.HasValue && !seeAllDepartments)
{
q = q.Where(itd =>
itd.Inventory.User.DepartmentId == departmentId.Value);
}
// Status filter (default to Draft=0 if null)
if (filter.Status.HasValue)
q = q.Where(r => r.Status == filter.Status.Value);
//else
// q = q.Where(r => r.Status == 0);
// RIS No
if (!string.IsNullOrWhiteSpace(filter.SearchRISNo))
q = q.Where(r => r.RISNo.Contains(filter.SearchRISNo));
// Item Name
if (!string.IsNullOrWhiteSpace(filter.SearchItemName))
q = q.Where(r => r.Inventory.InventTrans
.SelectMany(t => t.InventTransDetails)
.Any(d => d.PRDetails != null &&
d.PRDetails.ItemName.Contains(filter.SearchItemName)));
// Issued To is Project code/name
if (!string.IsNullOrWhiteSpace(filter.SearchIssuedTo))
q = q.Where(r => r.ProjectCodes.ProjectCode.Contains(filter.SearchIssuedTo)
|| r.ProjectCodes.ProjectName.Contains(filter.SearchIssuedTo));
// Discipline
if (!string.IsNullOrWhiteSpace(filter.Discipline))
q = q.Where(r => r.Discipline.DisciplineName == filter.Discipline);
// Date range (if you ever add date filters)
if (filter.DateFrom.HasValue)
q = q.Where(r => r.CreatedDate >= filter.DateFrom.Value);
if (filter.DateTo.HasValue)
q = q.Where(r => r.CreatedDate <= filter.DateTo.Value.AddDays(1));
var total = await q.CountAsync(ct);
var data = await q
.OrderByDescending(r => r.CreatedDate)
.Skip((filter.PageNumber - 1) * filter.PageSize)
.Take(filter.PageSize)
.Select(r => new RISResponse
{
RISId = r.RISId,
RISNo = r.RISNo,
InventoryId = r.InventoryId,
ItemName = r.Inventory.InventTrans
.SelectMany(t => t.InventTransDetails)
.Select(d => d.PRDetails != null ? d.PRDetails.ItemName : "—")
.FirstOrDefault() ?? "—",
ItemNo = r.Inventory.ItemNo,
LotNo = r.Inventory.Lot != null ? r.Inventory.Lot.LotName : null,
ProjectName = r.ProjectCodes.ProjectName,
ProjectCode = r.ProjectCodes.ProjectCode,
DisciplineName = r.Discipline.DisciplineName,
DisciplineId = r.DisciplineId,
QtyIssued = r.QtyIssued,
Remarks = r.Remarks,
Status = r.Status,
CreatedBy = r.CreatedBy,
CreatedDate = r.CreatedDate,
ApprovedBy = r.ApprovedBy,
ApprovedDate = r.ApprovedDate,
MRSCount = r.MaterialReturns.Count(m => m.Status != 2),
TotalReturned = r.MaterialReturns
.Where(m => m.Status != 2)
.Sum(m => m.QtyReturned)
})
.ToListAsync(ct);
// Full department list (never filtered)
var departments = await _db.Departments
.Select(d => d.Department)
.OrderBy(d => d)
.ToListAsync(ct);
var disciplines = await GetDisciplinesAsync(ct);
return new RISPagedResult
{
Data = data,
RecordsTotal = total,
DepartmentList = departments,
DisciplineList = disciplines
};
}
public async Task<IReadOnlyList<DisciplineDto>> GetDisciplinesAsync(CancellationToken ct)
{
return await _db.Disciplines
.OrderBy(d => d.DisciplineName)
.Select(d => new DisciplineDto
{
DisciplineId = d.DisciplineId,
DisciplineName = d.DisciplineName
})
.ToListAsync(ct);
}
public async Task<IReadOnlyList<ProjectCodeDto>> GetProjectCodesAsync(CancellationToken ct)
{
return await _db.ProjectCodes
.Where(p=>p.StatusName !="Completed")
.OrderBy(d => d.ProjectName)
.Select(d => new ProjectCodeDto
{
ProjectCodeId= d.ProjectCodeId,
ProjectCode = d.ProjectCode ?? "N/A",
ProjectName = d.ProjectName ?? "N/A"
})
.ToListAsync(ct);
}
public async Task<RISResponse?> GetByIdAsync(long risId, CancellationToken ct)
{
return await _db.RIS
.Where(r => r.RISId == risId)
.Select(r => new RISResponse
{
RISId = r.RISId,
RISNo = r.RISNo,
InventoryId = r.InventoryId,
ProjectCodeId = r.ProjectCodeId,
DisciplineName = r.Discipline.DisciplineName,
DisciplineId = r.DisciplineId,
QtyIssued = r.QtyIssued,
Remarks = r.Remarks,
Status = r.Status,
CreatedBy = r.CreatedBy,
CreatedDate = r.CreatedDate,
ApprovedBy = r.ApprovedBy,
ApprovedDate = r.ApprovedDate,
MRSCount = r.MaterialReturns.Count(m => m.Status != 2),
TotalReturned = r.MaterialReturns
.Where(m => m.Status != 2)
.Sum(m => m.QtyReturned)
})
.FirstOrDefaultAsync(ct);
}
}
}

View File

@ -1,6 +1,5 @@
using CPRNIMS.Domain.Contracts.Items; using CPRNIMS.Domain.Contracts.Items;
using CPRNIMS.Infrastructure.Database; using CPRNIMS.Infrastructure.Database;
using CPRNIMS.Infrastructure.Dto.Canvass;
using CPRNIMS.Infrastructure.Dto.Items; using CPRNIMS.Infrastructure.Dto.Items;
using CPRNIMS.Infrastructure.Entities.Account; using CPRNIMS.Infrastructure.Entities.Account;
using CPRNIMS.Infrastructure.Entities.Items; using CPRNIMS.Infrastructure.Entities.Items;
@ -64,7 +63,6 @@ namespace CPRNIMS.Domain.Services.Items
UserId = itemDto.UserId UserId = itemDto.UserId
}; };
} }
public async Task<Infrastructure.Entities.Items.Item> PutItemDetail(ItemDto itemDto) public async Task<Infrastructure.Entities.Items.Item> PutItemDetail(ItemDto itemDto)
{ {
await _dbContext.Database await _dbContext.Database
@ -93,6 +91,139 @@ namespace CPRNIMS.Domain.Services.Items
return new Infrastructure.Entities.Items.Item(); return new Infrastructure.Entities.Items.Item();
} }
public async Task<ItemAttachement> PostPutItemPath(ItemDto itemDto)
{
var isExist = await _dbContext.ItemAttachements
.FirstOrDefaultAsync(ia => ia.IsActive == true
&& ia.ItemNo == itemDto.ItemNo);
if (isExist != null)
{
isExist.ItemAttachPath = itemDto.ItemAttachPath;
await _dbContext.SaveChangesAsync();
return new ItemAttachement();
}
else
{
return new ItemAttachement();
}
}
public async Task<ItemCart> PostPutItemCart(ItemDto itemDto)
{
await _dbContext.Database
.ExecuteSqlRawAsync("EXEC PostPutItemCart @ItemCartId,@ItemNo,@UserId,@UOMId,@ItemColorId,@ItemCategoryId,@PackagingTypeId,@ItemAttachId,@ItemLocalId",
new SqlParameter("@ItemCartId", itemDto.ItemCartId != null ? itemDto.ItemCartId : 0L),
new SqlParameter("@ItemNo", itemDto.ItemNo != null ? itemDto.ItemNo : 0L),
new SqlParameter("@UOMId", itemDto.UOMId),
new SqlParameter("@ItemColorId", itemDto.ItemColorId),
new SqlParameter("@ItemCategoryId", itemDto.ItemCategoryId),
new SqlParameter("@PackagingTypeId", itemDto.PackagingTypeId),
new SqlParameter("@ItemAttachId", itemDto.ItemAttachId),
new SqlParameter("@UserId", itemDto.UserId),
new SqlParameter("@ItemLocalId", itemDto.ItemLocalId));
return new ItemCart();
}
public async Task<ResponseObject> PostPurchRequest(ItemDto itemDto)
{
var (messCode, message) = OutputParamMessage.CreateOutputParams();
if (itemDto.RequestTypeId == 1)//Internal
{
itemDto.Status = 2;
itemDto.IsApproved = 2;
await _dbContext.Database
.ExecuteSqlRawAsync("EXEC PostPutReqItems @UserId, @RequestItemId, @ItemNo, @QtyRequest,@Status,@IsApproved,@QtyReceived,@LotId," +
"@MessCode OUTPUT, @Message OUTPUT",
new SqlParameter("@UserId", itemDto.UserId),
new SqlParameter("@ItemNo", itemDto.ItemNo),
new SqlParameter("@RequestItemId", itemDto.RequestItemId != null ? itemDto.RequestItemId : 0L),
new SqlParameter("@QtyRequest", itemDto.Qty),
new SqlParameter("@Status", itemDto.Status),
new SqlParameter("@IsApproved", itemDto.IsApproved),
new SqlParameter("@QtyReceived", itemDto.QtyReceived),
new SqlParameter("@LotId", itemDto.LotId),
messCode,message);
}
else
{
await _dbContext.Database
.ExecuteSqlRawAsync("EXEC PostPurchRequest @ItemCartId, @IsActive, @UserId,@ItemCount," +
"@PRNo,@DateNeeded,@Qty,@ChargeTo,@Remarks,@ProjectCodeId,@MessCode OUTPUT, @Message OUTPUT",
new SqlParameter("@ItemCartId", itemDto.ItemCartId != null ? itemDto.ItemCartId : 0L),
new SqlParameter("@IsActive", 1),
new SqlParameter("@UserId", itemDto.UserId),
new SqlParameter("@ItemCount", itemDto.ItemCount),
new SqlParameter("@PRNo", itemDto.PRNo),
new SqlParameter("@DateNeeded", itemDto.DateNeeded),
new SqlParameter("@Qty", itemDto.Qty),
new SqlParameter("@ChargeTo", itemDto.ChargeTo),
new SqlParameter("@Remarks", itemDto.Remarks ?? "N/A"),
new SqlParameter("@ProjectCodeId", itemDto.ProjectCodeId),
messCode, message);
}
return new ResponseObject() {
messCode= (byte)messCode.Value,
message= message.Value.ToString()
};
}
public async Task PostPutAttachment(AttachmentRequest attach)
{
var existing = await _dbContext.PRAttachments
.FirstOrDefaultAsync(a => a.PRId == attach.PRId);
if (existing == null)
{
var attchmnt = new PRAttachments()
{
FileName = attach.FileName,
OrigFileName = attach.OrigFileName,
PRId = attach.PRId
};
await _dbContext.PRAttachments.AddAsync(attchmnt);
}
else
{
existing.OrigFileName = attach.OrigFileName;
existing.FileName = attach.FileName;
}
await _dbContext.SaveChangesAsync();
}
Task<ItemCart> IItem.PostPutItemPath(ItemDto itemDto)
{
throw new NotImplementedException();
}
public async Task<List<ItemCart>> GetItemCart(ItemDto itemDto)
{
var allItems = await _dbContext.ItemCarts
.FromSqlRaw($"EXEC GetItemCart @UserId,@RequestTypeId,@IsCount",
new SqlParameter("@UserId", itemDto.UserId),
new SqlParameter("@RequestTypeId", itemDto.RequestTypeId),
new SqlParameter("@IsCount", itemDto.IsCount))
.ToListAsync();
return allItems ?? new List<ItemCart>();
}
public async Task<(long, long)> GetPRNo()
{
try
{
var latestPR = await _dbContext.PRs
.Where(ic => ic.PRNo != null)
.OrderByDescending(ic => ic.PRNo)
.FirstOrDefaultAsync();
if (latestPR != null)
return (latestPR.PRNo + 1, latestPR.PRId + 1);
else
return (0, 0);
}
catch (Exception ex)
{
throw;
}
}
public async Task<List<ItemLocalization>> GetItemLocalization(ItemDto itemDto) public async Task<List<ItemLocalization>> GetItemLocalization(ItemDto itemDto)
{ {
var localizations = await _dbContext.ItemLocalizations var localizations = await _dbContext.ItemLocalizations
@ -120,18 +251,16 @@ namespace CPRNIMS.Domain.Services.Items
return categories ?? new List<ItemCategory>(); return categories ?? new List<ItemCategory>();
} }
} }
public async Task<List<ItemDtos>> GetItemDetail(ItemDto itemDto)
public async Task<List<Infrastructure.Entities.Items.Item>> GetItemDetail(ItemDto itemDto)
{ {
var allItems = await _dbContext.Items var allItems = await _dbContext.ItemDtos
.FromSqlRaw($"EXEC GetItemDetail @ItemCodeId,@UserId", .FromSqlRaw($"EXEC GetItemDetail @ItemCodeId,@UserId",
new SqlParameter("@ItemCodeId", itemDto.ItemCodeId), new SqlParameter("@ItemCodeId", itemDto.ItemCodeId),
new SqlParameter("@UserId", itemDto.UserId)) new SqlParameter("@UserId", itemDto.UserId))
.ToListAsync(); .ToListAsync();
return allItems ?? new List<Infrastructure.Entities.Items.Item>(); return allItems ?? new List<ItemDtos>();
} }
public async Task<PagedResult<ItemList>> GetItemList(ItemCodeDto dto) public async Task<PagedResult<ItemList>> GetItemList(ItemCodeDto dto)
{ {
var parameters = new[] var parameters = new[]
@ -201,7 +330,6 @@ namespace CPRNIMS.Domain.Services.Items
return colors ?? new List<ItemColor>(); return colors ?? new List<ItemColor>();
} }
public async Task<List<UnitOfMessure>> GetItemUOM(ItemDto itemDto) public async Task<List<UnitOfMessure>> GetItemUOM(ItemDto itemDto)
{ {
var uoms = await _dbContext.UnitOfMessures var uoms = await _dbContext.UnitOfMessures
@ -212,143 +340,6 @@ namespace CPRNIMS.Domain.Services.Items
return uoms ?? new List<UnitOfMessure>(); return uoms ?? new List<UnitOfMessure>();
} }
public async Task<ItemAttachement> PostPutItemPath(ItemDto itemDto)
{
var isExist = await _dbContext.ItemAttachements
.FirstOrDefaultAsync(ia => ia.IsActive == true
&& ia.ItemNo == itemDto.ItemNo);
if (isExist != null)
{
isExist.ItemAttachPath = itemDto.ItemAttachPath;
await _dbContext.SaveChangesAsync();
return new ItemAttachement();
}
else
{
return new ItemAttachement();
}
}
public async Task<ItemCart> PostPutItemCart(ItemDto itemDto)
{
await _dbContext.Database
.ExecuteSqlRawAsync("EXEC PostPutItemCart @ItemCartId,@ItemNo,@UserId,@UOMId,@ItemColorId,@ItemCategoryId,@PackagingTypeId,@ItemAttachId,@ItemLocalId",
new SqlParameter("@ItemCartId", itemDto.ItemCartId != null ? itemDto.ItemCartId : 0L),
new SqlParameter("@ItemNo", itemDto.ItemNo != null ? itemDto.ItemNo : 0L),
new SqlParameter("@UOMId", itemDto.UOMId),
new SqlParameter("@ItemColorId", itemDto.ItemColorId),
new SqlParameter("@ItemCategoryId", itemDto.ItemCategoryId),
new SqlParameter("@PackagingTypeId", itemDto.PackagingTypeId),
new SqlParameter("@ItemAttachId", itemDto.ItemAttachId),
new SqlParameter("@UserId", itemDto.UserId),
new SqlParameter("@ItemLocalId", itemDto.ItemLocalId));
return new ItemCart();
}
public async Task<List<ItemCart>> GetItemCart(ItemDto itemDto)
{
var allItems = await _dbContext.ItemCarts
.FromSqlRaw($"EXEC GetItemCart @UserId,@RequestTypeId,@IsCount",
new SqlParameter("@UserId", itemDto.UserId),
new SqlParameter("@RequestTypeId", itemDto.RequestTypeId),
new SqlParameter("@IsCount", itemDto.IsCount))
.ToListAsync();
return allItems ?? new List<ItemCart>();
}
public async Task<ResponseObject> PostPurchRequest(ItemDto itemDto)
{
var (messCode, message) = OutputParamMessage.CreateOutputParams();
if (itemDto.RequestTypeId == 1)//Internal
{
itemDto.Status = 2;
itemDto.IsApproved = 2;
await _dbContext.Database
.ExecuteSqlRawAsync("EXEC PostPutReqItems @UserId, @RequestItemId, @ItemNo, @QtyRequest,@Status,@IsApproved,@QtyReceived,@LotId," +
"@MessCode OUTPUT, @Message OUTPUT",
new SqlParameter("@UserId", itemDto.UserId),
new SqlParameter("@ItemNo", itemDto.ItemNo),
new SqlParameter("@RequestItemId", itemDto.RequestItemId != null ? itemDto.RequestItemId : 0L),
new SqlParameter("@QtyRequest", itemDto.Qty),
new SqlParameter("@Status", itemDto.Status),
new SqlParameter("@IsApproved", itemDto.IsApproved),
new SqlParameter("@QtyReceived", itemDto.QtyReceived),
new SqlParameter("@LotId", itemDto.LotId),
messCode,message);
}
else
{
await _dbContext.Database
.ExecuteSqlRawAsync("EXEC PostPurchRequest @ItemCartId, @IsActive, @UserId,@ItemCount," +
"@PRNo,@DateNeeded,@Qty,@ChargeTo,@Remarks,@ProjectCodeId,@MessCode OUTPUT, @Message OUTPUT",
new SqlParameter("@ItemCartId", itemDto.ItemCartId != null ? itemDto.ItemCartId : 0L),
new SqlParameter("@IsActive", 1),
new SqlParameter("@UserId", itemDto.UserId),
new SqlParameter("@ItemCount", itemDto.ItemCount),
new SqlParameter("@PRNo", itemDto.PRNo),
new SqlParameter("@DateNeeded", itemDto.DateNeeded),
new SqlParameter("@Qty", itemDto.Qty),
new SqlParameter("@ChargeTo", itemDto.ChargeTo),
new SqlParameter("@Remarks", itemDto.Remarks ?? "N/A"),
new SqlParameter("@ProjectCodeId", itemDto.ProjectCodeId),
messCode, message);
}
return new ResponseObject() {
messCode= (byte)messCode.Value,
message= message.Value.ToString()
};
}
public async Task PostPutAttachment(AttachmentRequest attach)
{
var existing = await _dbContext.PRAttachments
.FirstOrDefaultAsync(a => a.PRId == attach.PRId);
if (existing == null)
{
var attchmnt = new PRAttachments()
{
FileName = attach.FileName,
OrigFileName = attach.OrigFileName,
PRId = attach.PRId
};
await _dbContext.PRAttachments.AddAsync(attchmnt);
}
else
{
existing.OrigFileName = attach.OrigFileName;
existing.FileName = attach.FileName;
}
await _dbContext.SaveChangesAsync();
}
public async Task<(long,long)> GetPRNo()
{
try
{
var latestPR = await _dbContext.PRs
.Where(ic => ic.PRNo != null)
.OrderByDescending(ic => ic.PRNo)
.FirstOrDefaultAsync();
if (latestPR != null)
return (latestPR.PRNo + 1,latestPR.PRId + 1);
else
return (0,0);
}
catch (Exception ex)
{
throw;
}
}
Task<ItemCart> IItem.PostPutItemPath(ItemDto itemDto)
{
throw new NotImplementedException();
}
public async Task <List<Departments>> GetDepartment(ItemCodeDto itemCode) public async Task <List<Departments>> GetDepartment(ItemCodeDto itemCode)
{ {
return await _dbContext.Departments return await _dbContext.Departments
@ -367,18 +358,18 @@ namespace CPRNIMS.Domain.Services.Items
return allItems ?? new List<NotifUserKey>(); return allItems ?? new List<NotifUserKey>();
} }
public async Task<IReadOnlyList<ProjectCodes>> GetProjectCode()
public async Task<List<ProjectCodes>> GetProjectCode()
{ {
return await _dbContext.ProjectCodes return await _dbContext.ProjectCodes
.AsNoTracking() .AsNoTracking()
.ToListAsync(); .ToListAsync();
} }
public async Task<List<ProjectCodes>> GetProjectCodeByTerm(string? term) public async Task<IReadOnlyList<ProjectCodes>> GetProjectCodeByTerm(string? term)
{ {
return await _dbContext.ProjectCodes return await _dbContext.ProjectCodes
.AsNoTracking() .AsNoTracking()
.Where(p => EF.Functions.Like(p.ProjectCode, $"%{term}%")) .Where(p => p.StatusName != "Completed" && p.IsActive
&& EF.Functions.Like(p.ProjectCode, $"%{term}%"))
.ToListAsync(); .ToListAsync();
} }
} }

View File

@ -1,7 +1,7 @@
using CPRNIMS.Domain.Contracts.PO; using CPRNIMS.Domain.Contracts.PO;
using CPRNIMS.Infrastructure.Database; using CPRNIMS.Infrastructure.Database;
using CPRNIMS.Infrastructure.Dto.Canvass.Response;
using CPRNIMS.Infrastructure.Dto.PO; using CPRNIMS.Infrastructure.Dto.PO;
using CPRNIMS.Infrastructure.Dto.PR;
using CPRNIMS.Infrastructure.Entities.Canvass; using CPRNIMS.Infrastructure.Entities.Canvass;
using CPRNIMS.Infrastructure.Entities.Common; using CPRNIMS.Infrastructure.Entities.Common;
using CPRNIMS.Infrastructure.Entities.LocalDb.NonInvent; using CPRNIMS.Infrastructure.Entities.LocalDb.NonInvent;
@ -30,6 +30,14 @@ namespace CPRNIMS.Domain.Services.PO
_smptHelper = smptHelper; _smptHelper = smptHelper;
} }
#region Get #region Get
public async Task<IReadOnlyList<Currencies>> GetCurrencies(string currencyName, CancellationToken ct)
{
return await _dbContext.Currencies.AsNoTracking()
.Where(c => c.IsActive==true &&
(string.IsNullOrEmpty(currencyName) || c.CurrencyName.Contains(currencyName)))
.ToListAsync(ct);
}
public async Task<POFormData> GetPOFormDataAsync(long? poId) public async Task<POFormData> GetPOFormDataAsync(long? poId)
{ {
// Reuse the connection from your existing DbContext // Reuse the connection from your existing DbContext
@ -73,15 +81,15 @@ namespace CPRNIMS.Domain.Services.PO
var charges = await _dbContext.OtherCharges.ToListAsync(); var charges = await _dbContext.OtherCharges.ToListAsync();
return charges; return charges;
} }
public async Task<List<Suppliers>> GetSuppliers(PODto itemDto) public async Task<List<SupplierResponseDto>> GetSuppliers(PODto itemDto)
{ {
var allItems = await _dbContext.Suppliers var allItems = await _dbContext.SupplierResponses
.FromSqlRaw($"EXEC GetSuppliers @UserId,@SupplierName", .FromSqlRaw($"EXEC GetSuppliers @UserId,@SupplierName",
new SqlParameter("@UserId", itemDto.UserId), new SqlParameter("@UserId", itemDto.UserId),
new SqlParameter("@SupplierName", itemDto.SupplierName)) new SqlParameter("@SupplierName", itemDto.SupplierName))
.ToListAsync(); .ToListAsync();
return allItems ?? new List<Suppliers>(); return allItems ?? new List<SupplierResponseDto>();
} }
public async Task<List<PRWOCanvass>> GetPRWOCanvass(PODto itemDto) public async Task<List<PRWOCanvass>> GetPRWOCanvass(PODto itemDto)
{ {
@ -493,7 +501,7 @@ namespace CPRNIMS.Domain.Services.PO
await _dbContext.Database await _dbContext.Database
.ExecuteSqlRawAsync("EXEC PostPutCustomPO @UserId,@POTypeId,@PONumber,@PRDetailsId,@Specification,@PRNo,@PORemarks,@IncoTermsId," + .ExecuteSqlRawAsync("EXEC PostPutCustomPO @UserId,@POTypeId,@PONumber,@PRDetailsId,@Specification,@PRNo,@PORemarks,@IncoTermsId," +
$"@PODId,@ProfInvoiceNo,@ProfInvoiceDate,@PaymentTermsId,@ShippingInstructionId,@SupplierId,@DeliveryDate,@Discount,@Amount," + $"@PODId,@ProfInvoiceNo,@ProfInvoiceDate,@PaymentTermsId,@ShippingInstructionId,@SupplierId,@DeliveryDate,@Discount,@Amount," +
$"@UnitPrice,@Quantity,@DeliverTo, @MessCode OUTPUT, @Message OUTPUT", $"@UnitPrice,@Quantity,@DeliverTo,@CountryOrigin,@CurrencyId, @MessCode OUTPUT, @Message OUTPUT",
new SqlParameter("@UserId", pODto.UserId), new SqlParameter("@UserId", pODto.UserId),
new SqlParameter("@POTypeId", pODto.POTypeId), new SqlParameter("@POTypeId", pODto.POTypeId),
new SqlParameter("@PONumber", formattedPONumber), new SqlParameter("@PONumber", formattedPONumber),
@ -515,6 +523,8 @@ namespace CPRNIMS.Domain.Services.PO
new SqlParameter("@UnitPrice", pODto.UnitPrice), new SqlParameter("@UnitPrice", pODto.UnitPrice),
new SqlParameter("@Quantity", pODto.Quantity), new SqlParameter("@Quantity", pODto.Quantity),
new SqlParameter("@DeliverTo", pODto.DeliverTo), new SqlParameter("@DeliverTo", pODto.DeliverTo),
new SqlParameter("@CountryOrigin", pODto.CountryOrigin ?? "N/A"),
new SqlParameter("@CurrencyId", pODto.CurrencyId),
messCode, messCode,
message); message);
@ -543,7 +553,7 @@ namespace CPRNIMS.Domain.Services.PO
await _dbContext.Database await _dbContext.Database
.ExecuteSqlRawAsync("EXEC PutExistingPO @UserId,@POTypeId,@PONumber,@PRDetailsId,@Specification,@PRNo,@PORemarks,@IncoTermsId," + .ExecuteSqlRawAsync("EXEC PutExistingPO @UserId,@POTypeId,@PONumber,@PRDetailsId,@Specification,@PRNo,@PORemarks,@IncoTermsId," +
$"@PODId,@ProfInvoiceNo,@ProfInvoiceDate,@PaymentTermsId,@ShippingInstructionId,@SupplierId,@DeliveryDate,@Discount,@Amount," + $"@PODId,@ProfInvoiceNo,@ProfInvoiceDate,@PaymentTermsId,@ShippingInstructionId,@SupplierId,@DeliveryDate,@Discount,@Amount," +
$"@UnitPrice,@Quantity,@DeliverTo,@IsRemoved, @MessCode OUTPUT, @Message OUTPUT", $"@UnitPrice,@Quantity,@DeliverTo,@IsRemoved,@CountryOrigin,@CurrencyId, @MessCode OUTPUT, @Message OUTPUT",
new SqlParameter("@UserId", pODto.UserId), new SqlParameter("@UserId", pODto.UserId),
new SqlParameter("@POTypeId", pODto.POTypeId), new SqlParameter("@POTypeId", pODto.POTypeId),
new SqlParameter("@PONumber", pODto.PONo), new SqlParameter("@PONumber", pODto.PONo),
@ -566,6 +576,8 @@ namespace CPRNIMS.Domain.Services.PO
new SqlParameter("@Quantity", pODto.Quantity), new SqlParameter("@Quantity", pODto.Quantity),
new SqlParameter("@DeliverTo", pODto.DeliverTo), new SqlParameter("@DeliverTo", pODto.DeliverTo),
new SqlParameter("@IsRemoved", isRemoved), new SqlParameter("@IsRemoved", isRemoved),
new SqlParameter("@CountryOrigin", pODto.CountryOrigin ?? "N/A"),
new SqlParameter("@CurrencyId", pODto.CurrencyId),
messCode, messCode,
message); message);
@ -632,7 +644,9 @@ namespace CPRNIMS.Domain.Services.PO
DeliveryDate = PODto.DeliveryDate, DeliveryDate = PODto.DeliveryDate,
DeliverTo = PODto.DeliverTo ?? "N/A", DeliverTo = PODto.DeliverTo ?? "N/A",
Specification = specification, Specification = specification,
IsUpdate = PODto.IsUpdate IsUpdate = PODto.IsUpdate,
CountryOrigin=PODto.CountryOrigin,
CurrencyId=PODto.CurrencyId
}; };
} }
public async Task<DocRequired> PostSuppDocRequirements(PODto poDto) public async Task<DocRequired> PostSuppDocRequirements(PODto poDto)

View File

@ -633,6 +633,17 @@ namespace CPRNIMS.Domain.Services.PR
&& pr.IsActive && pr.IsActive
select pr).AnyAsync(); select pr).AnyAsync();
} }
private async Task<bool> ProjectStatusChanges(int projectCodeId,string? status)
{
var project = await _dbContext.ProjectCodes.FirstOrDefaultAsync(pc=>pc.ProjectCodeId==projectCodeId);
if (project == null)
return false;
if (project.StatusName == status)
return false;
else
return true;
}
public async Task<MessageResponse> PRItemRemoval(PRDto prDto) public async Task<MessageResponse> PRItemRemoval(PRDto prDto)
{ {
var (messCode, message) = CreateOutputParams(); var (messCode, message) = CreateOutputParams();
@ -653,7 +664,6 @@ namespace CPRNIMS.Domain.Services.PR
}; };
return response; return response;
} }
public async Task<ResponseObject> PostPutProjectCode(PRDto prDto) public async Task<ResponseObject> PostPutProjectCode(PRDto prDto)
{ {
if (prDto == null) throw new ArgumentNullException(nameof(prDto)); if (prDto == null) throw new ArgumentNullException(nameof(prDto));
@ -669,13 +679,14 @@ namespace CPRNIMS.Domain.Services.PR
ProjectName = prDto.ProjectName, ProjectName = prDto.ProjectName,
DeliveryAddress = prDto.DeliveryAddress, DeliveryAddress = prDto.DeliveryAddress,
MaxDays = prDto.MaxDays, MaxDays = prDto.MaxDays,
StatusName = prDto.StatusName,
IsActive = prDto.IsActive IsActive = prDto.IsActive
}; };
await _dbContext.ProjectCodes.AddAsync(project); await _dbContext.ProjectCodes.AddAsync(project);
} }
else else
{ {
if (await IsUsingAsync(prDto.ProjectCodeId)) if (await IsUsingAsync(prDto.ProjectCodeId) && await ProjectStatusChanges(prDto.ProjectCodeId,prDto.StatusName) == false)
{ {
return new ResponseObject() return new ResponseObject()
{ {
@ -684,12 +695,17 @@ namespace CPRNIMS.Domain.Services.PR
success = false, success = false,
}; };
} }
if (await IsUsingAsync(prDto.ProjectCodeId))
{
existing.StatusName = prDto.StatusName;
}
else else
{ {
existing.ProjectCode = prDto.ProjectCode; existing.ProjectCode = prDto.ProjectCode;
existing.ProjectName = prDto.ProjectName; existing.ProjectName = prDto.ProjectName;
existing.DeliveryAddress = prDto.DeliveryAddress; existing.DeliveryAddress = prDto.DeliveryAddress;
existing.MaxDays = prDto.MaxDays; existing.MaxDays = prDto.MaxDays;
existing.StatusName = prDto.StatusName;
existing.IsActive = prDto.IsActive; existing.IsActive = prDto.IsActive;
} }
} }

View File

@ -73,7 +73,7 @@ namespace CPRNIMS.Domain.Services.Receiving
return allItems ?? new List<ReceivingDetail>(); return allItems ?? new List<ReceivingDetail>();
} }
public async Task<List<RRDetail>> GetRRDetail(ItemDto itemDto) public async Task<List<RRDetailDto>> GetRRDetail(ItemDto itemDto)
{ {
var allItems = await _dbContext.RRDetailss var allItems = await _dbContext.RRDetailss
.FromSqlRaw("EXEC GetRRDetail @RRNo,@UserId", .FromSqlRaw("EXEC GetRRDetail @RRNo,@UserId",
@ -81,7 +81,7 @@ namespace CPRNIMS.Domain.Services.Receiving
new SqlParameter("@UserId", itemDto.UserId)) new SqlParameter("@UserId", itemDto.UserId))
.ToListAsync(); .ToListAsync();
return allItems ?? new List<RRDetail>(); return allItems ?? new List<RRDetailDto>();
} }
#endregion #endregion
#region Post Put #region Post Put

View File

@ -0,0 +1,80 @@
using CPRNIMS.Domain.Contracts.Reports;
using CPRNIMS.Domain.UIContracts.Common;
using CPRNIMS.Infrastructure.Dto.Inventory.Reports;
using CPRNIMS.Infrastructure.Helper;
using FastReport;
using Microsoft.Extensions.Configuration;
using System.Data;
using System.Net.Http.Json;
using System.Text.Json;
namespace CPRNIMS.Infrastructure.Reports
{
public class ReportBuilder : IReportBuilder
{
private readonly IConfiguration _configuration;
private readonly TokenHelper _tokenHelper;
private readonly IApiConfigurationService _apiConfigurationService;
public ReportBuilder(IConfiguration configuration, TokenHelper tokenHelper,
IApiConfigurationService apiConfigurationService)
{
_configuration = configuration;
_tokenHelper = tokenHelper;
_apiConfigurationService = apiConfigurationService;
}
private static readonly JsonSerializerOptions _jsonOptions = new()
{
PropertyNameCaseInsensitive = true
};
public Task<Report> MRSBuildAsync(DateTime dateFrom, DateTime dateTo, string templatePath, CancellationToken ct)
{
throw new NotImplementedException();
}
public async Task<Report> RISBuildAsync(
DateTime dateFrom, DateTime dateTo, string templatePath, CancellationToken ct = default)
{
var token = await _tokenHelper.GetValidTokenAsync();
if (string.IsNullOrEmpty(token))
throw new InvalidOperationException("Token has been expired.");
var endpoint = _configuration["LLI:NonInvent:InventoryMgmt:RISReportData"]
?? throw new InvalidOperationException("RISReportData endpoint is not configured.");
// Append the date range as query string
var url = $"{endpoint}?dateFrom={dateFrom:yyyy-MM-dd}&dateTo={dateTo:yyyy-MM-dd}";
using var httpClient = _apiConfigurationService.CreateHttpClientWithDefaultHeaders(token);
var response = await httpClient.GetAsync(url, ct);
var json = await response.Content.ReadAsStringAsync(ct);
if (!response.IsSuccessStatusCode)
throw new InvalidOperationException(
$"Failed to fetch RIS report data. HTTP {(int)response.StatusCode}: {json}");
var dto = JsonSerializer.Deserialize<RISReportDataDto>(json, _jsonOptions)
?? new RISReportDataDto();
var report = new Report();
report.Load(templatePath);
// RegisterData accepts IEnumerable<object> — names must match the .frx datasources.
report.RegisterData(dto.Rows ?? new(), "TRIS");
report.RegisterData(dto.Disciplines ?? new(), "TDisciplineAgg");
report.RegisterData(dto.Recipients ?? new(), "TTopRecipients");
/* report.GetDataSource("Table").Enabled = true;
report.GetDataSource("Table1").Enabled = true;
report.GetDataSource("Table3").Enabled = true;*/
report.GetDataSource("TRIS").Enabled = true;
report.GetDataSource("TDisciplineAgg").Enabled = true;
report.GetDataSource("TTopRecipients").Enabled = true;
report.SetParameterValue("DateFrom", dateFrom);
report.SetParameterValue("DateTo", dateTo);
return report;
}
}
}

View File

@ -0,0 +1,109 @@
using CPRNIMS.Domain.Contracts.Reports;
using CPRNIMS.Infrastructure.Database;
using CPRNIMS.Infrastructure.Dto.Inventory.Reports;
using Dapper;
using Microsoft.Data.SqlClient;
using Microsoft.EntityFrameworkCore;
namespace CPRNIMS.Infrastructure.Reports
{
public class ReportDataService : IReportDataService
{
private readonly NonInventoryDbContext _context;
public ReportDataService(NonInventoryDbContext context) => _context = context;
private const string MainSql = """
SELECT
R.RISNo,
PR.PRNo,
R.QtyIssued,
R.IssuedTo,
CASE R.Status
WHEN 0 THEN 'Draft'
WHEN 1 THEN 'Approved'
WHEN 2 THEN 'Cancelled'
ELSE 'Unknown'
END AS StatusLabel,
CreatedBy.CreatedBy,
ApprovedBy.ApprovedBy,
R.ApprovedDate,
R.CreatedDate,
D.DisciplineName,
PRD.ItemName,
PRD.ItemNo,
IV.QtyIn,
IV.QtyOut,
IV.QtyOnHand,
DEP.Department AS DepartmentName,
ISNULL(MRS_AGG.TotalReturned, 0) AS TotalReturned,
ISNULL(MRS_AGG.MRSCount, 0) AS MRSCount,
R.QtyIssued - ISNULL(MRS_AGG.TotalReturned, 0) AS NetIssued
FROM dbo.RIS R
INNER JOIN dbo.Disciplines D ON R.DisciplineId = D.DisciplineId
INNER JOIN dbo.Inventory IV ON R.InventoryId = IV.InventoryId AND IV.IsActive = 1
LEFT JOIN dbo.Lot L ON IV.LotId = L.LotId
OUTER APPLY (SELECT U.FullName ApprovedBy FROM dbo.Users U WHERE R.ApprovedBy = U.UserName) ApprovedBy
OUTER APPLY (SELECT U2.FullName CreatedBy FROM dbo.Users U2 WHERE R.CreatedBy = U2.UserName) CreatedBy
INNER JOIN Users U ON IV.UserId = U.Id
LEFT JOIN dbo.Departments DEP ON U.DepartmentId = DEP.DepartmentId
LEFT JOIN dbo.PRDetails PRD ON R.PRDetailId = PRD.PRDetailsId AND PRD.IsActive = 1
LEFT JOIN dbo.PR PR ON PRD.PRId = PR.PRId AND PR.IsActive = 1
LEFT JOIN (
SELECT
RISId,
COUNT(*) AS MRSCount,
SUM(QtyReturned) AS TotalReturned
FROM dbo.MRS
WHERE Status != 2
GROUP BY RISId
) MRS_AGG ON R.RISId = MRS_AGG.RISId
WHERE R.Status != 2
AND R.CreatedDate >= @DateFrom
AND R.CreatedDate < DATEADD(DAY, 1, @DateTo)
ORDER BY R.CreatedDate DESC, R.RISNo ASC;
""";
private const string DisciplineSql = """
SELECT
D.DisciplineName,
COUNT(*) AS SlipCount
FROM dbo.RIS R
INNER JOIN dbo.Disciplines D ON R.DisciplineId = D.DisciplineId
WHERE R.Status != 2
AND R.CreatedDate >= @DateFrom
AND R.CreatedDate < DATEADD(DAY, 1, @DateTo)
GROUP BY D.DisciplineName
ORDER BY SlipCount DESC;
""";
private const string RecipientsSql = """
SELECT TOP 5
R.IssuedTo AS Name,
COUNT(*) AS SlipCount,
SUM(IV.QtyOut) AS QtyOut
FROM dbo.RIS R
INNER JOIN dbo.Inventory IV ON R.InventoryId = IV.InventoryId AND IV.IsActive = 1
WHERE R.Status != 2
AND R.CreatedDate >= @DateFrom
AND R.CreatedDate < DATEADD(DAY, 1, @DateTo)
GROUP BY R.IssuedTo
ORDER BY COUNT(*) DESC;
""";
public List<RISRowDto> GetMain(DateTime dateFrom, DateTime dateTo) =>
Query<RISRowDto>(MainSql, dateFrom, dateTo);
public List<DisciplineAggDto> GetDisciplines(DateTime dateFrom, DateTime dateTo) =>
Query<DisciplineAggDto>(DisciplineSql, dateFrom, dateTo);
public List<TopRecipientDto> GetRecipients(DateTime dateFrom, DateTime dateTo) =>
Query<TopRecipientDto>(RecipientsSql, dateFrom, dateTo);
private List<T> Query<T>(string sql, DateTime from, DateTime to)
{
using var conn = new SqlConnection(_context.Database.GetConnectionString());
return conn.Query<T>(sql, new { DateFrom = from, DateTo = to }).ToList();
}
}
}

View File

@ -1,4 +1,5 @@
using CPRNIMS.Infrastructure.Models.Account; using CPRNIMS.Infrastructure.Dto.Common;
using CPRNIMS.Infrastructure.Models.Account;
using CPRNIMS.Infrastructure.ViewModel.Canvass; using CPRNIMS.Infrastructure.ViewModel.Canvass;
using CPRNIMS.Infrastructure.ViewModel.PR; using CPRNIMS.Infrastructure.ViewModel.PR;
using System; using System;
@ -11,10 +12,10 @@ namespace CPRNIMS.Domain.UIContracts.Canvass
{ {
public interface ICanvass public interface ICanvass
{ {
#region
Task<List<CanvassVM>> GetSupplierBid(User user, CanvassVM viewModel); Task<List<CanvassVM>> GetSupplierBid(User user, CanvassVM viewModel);
Task<List<CanvassVM>> GetSupplierBidByItem(User user, CanvassVM viewModel); Task<List<CanvassVM>> GetSupplierBidByItem(User user, CanvassVM viewModel);
Task<List<CanvassVM>> GetSupplierBidById(User user, CanvassVM viewModel); Task<List<CanvassVM>> GetSupplierBidById(User user, CanvassVM viewModel);
Task<List<CanvassVM>> GetCanvassPerSupplier(User user, CanvassVM viewModel);
Task<List<CanvassVM>> GetCanvassPerSupplierEmail(User user, CanvassVM viewModel); Task<List<CanvassVM>> GetCanvassPerSupplierEmail(User user, CanvassVM viewModel);
Task<List<CanvassVM>> GetItemSupplierWOEmail(User user, CanvassVM viewModel); Task<List<CanvassVM>> GetItemSupplierWOEmail(User user, CanvassVM viewModel);
Task<List<CanvassVM>> GetSupplierItemWOEmail(User user, CanvassVM viewModel); Task<List<CanvassVM>> GetSupplierItemWOEmail(User user, CanvassVM viewModel);
@ -32,6 +33,13 @@ namespace CPRNIMS.Domain.UIContracts.Canvass
Task<List<CanvassVM>?> GetCanvassPerSupplierId(User user, CanvassVM viewModel); Task<List<CanvassVM>?> GetCanvassPerSupplierId(User user, CanvassVM viewModel);
Task<List<CanvassVM>?> GetCanvassGroupByPRNo(User user, CanvassVM viewModel); Task<List<CanvassVM>?> GetCanvassGroupByPRNo(User user, CanvassVM viewModel);
Task<List<CanvassVM>?> GetAlternativeOfferByPRDetailId(User user, CanvassVM viewModels); Task<List<CanvassVM>?> GetAlternativeOfferByPRDetailId(User user, CanvassVM viewModels);
Task<PagedResult<CanvassVM>> GetCanvassPerSupplier(User user, CanvassVM viewModel);
Task<PagedResult<CanvassVM>> GetItemsForTagging(User user, CanvassVM dto);
#endregion
#region Post Put
Task<CanvassVM> PostSupplierForCanvass(User user, CanvassVM viewModel);
Task<CanvassVM> PostCanvass(User user, CanvassVM viewModel); Task<CanvassVM> PostCanvass(User user, CanvassVM viewModel);
Task<CanvassVM> PostPutSupplier(User user, CanvassVM viewModel); Task<CanvassVM> PostPutSupplier(User user, CanvassVM viewModel);
Task<CanvassVM> PostTaggingSupplier(User user, CanvassVM viewModel); Task<CanvassVM> PostTaggingSupplier(User user, CanvassVM viewModel);
@ -42,5 +50,7 @@ namespace CPRNIMS.Domain.UIContracts.Canvass
Task<CanvassVM> PostPutMySupplier(User user, CanvassVM viewModel); Task<CanvassVM> PostPutMySupplier(User user, CanvassVM viewModel);
Task<CanvassVM> PostPutItemTagging(User user, CanvassVM viewModel); Task<CanvassVM> PostPutItemTagging(User user, CanvassVM viewModel);
Task<CanvassVM> UnlockFormLink(User user, CanvassVM viewModel); Task<CanvassVM> UnlockFormLink(User user, CanvassVM viewModel);
Task<CanvassVM> StartCanvass(User user, CanvassVM viewModel);
#endregion
} }
} }

View File

@ -1,4 +1,6 @@
using CPRNIMS.Infrastructure.Dto.Inventory; using CPRNIMS.Infrastructure.Dto.Inventory;
using CPRNIMS.Infrastructure.Dto.Inventory.Request;
using CPRNIMS.Infrastructure.Dto.Inventory.Response;
using CPRNIMS.Infrastructure.Models.Account; using CPRNIMS.Infrastructure.Models.Account;
using CPRNIMS.Infrastructure.ViewModel.Inventory; using CPRNIMS.Infrastructure.ViewModel.Inventory;
using System; using System;
@ -11,12 +13,15 @@ namespace CPRNIMS.Domain.UIContracts.Inventory
{ {
public interface IInventory public interface IInventory
{ {
Task<TransactContextDto?> GetTransactContextAsync(int inventoryId);
Task<List<InventoryVM>> GetInventoryByUserId(User user, InventoryVM viewModel); Task<List<InventoryVM>> GetInventoryByUserId(User user, InventoryVM viewModel);
Task<List<InventoryVM>> GetRequestedItemByUserId(User user, InventoryVM viewModel); Task<List<InventoryVM>> GetRequestedItemByUserId(User user, InventoryVM viewModel);
Task<List<InventoryVM>> GetInventoryById(User user, InventoryVM viewModel); Task<List<InventoryVM>> GetInventoryById(User user, InventoryVM viewModel);
Task<List<InventoryVM>> GetLotNo(User user, InventoryVM viewModel); Task<List<InventoryVM>> GetLotNo(User user, InventoryVM viewModel);
Task<List<InventoryVM>> GetLotQtyByItem(User user, InventoryVM viewModel); Task<List<InventoryVM>> GetLotQtyByItem(User user, InventoryVM viewModel);
Task<List<InventoryVM>> GetLotNoById(User user, InventoryVM viewModel); Task<List<InventoryVM>> GetLotNoById(User user, InventoryVM viewModel);
Task<PagedResult<InventoryResponse>> GetInventory(User user, InventoryRequest request);
Task<InventoryVM> PostPutLotNo(User user, InventoryVM viewModel); Task<InventoryVM> PostPutLotNo(User user, InventoryVM viewModel);
Task<InventoryVM> PostPutLotBin(User user, InventoryVM viewModel); Task<InventoryVM> PostPutLotBin(User user, InventoryVM viewModel);
Task<InventoryVM> PostPutReqApproval(User user, InventoryVM viewModel); Task<InventoryVM> PostPutReqApproval(User user, InventoryVM viewModel);

View File

@ -0,0 +1,18 @@
using CPRNIMS.Infrastructure.Dto.Inventory.Reports;
using CPRNIMS.Infrastructure.Dto.Inventory.Request;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CPRNIMS.Domain.UIContracts.Inventory
{
public interface IInventoryReports
{
Task<RISReportDto> GetRISReportAsync(InventoryReportsRequest request, CancellationToken ct);
Task<MRSReportDto> GetMRSReportAsync(InventoryReportsRequest request, CancellationToken ct);
Task<InventoryReportDto> GetInventoryReportAsync(InventoryReportsRequest request, CancellationToken ct);
}
}

View File

@ -0,0 +1,22 @@
using CPRNIMS.Infrastructure.Dto.Common;
using CPRNIMS.Infrastructure.Dto.Inventory;
using CPRNIMS.Infrastructure.Dto.Inventory.Request;
using CPRNIMS.Infrastructure.Dto.Inventory.Response;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CPRNIMS.Domain.UIContracts.Inventory
{
public interface IMRS
{
Task<MRSPagedResponse> GetMRSPaged(MRSPagedRequest request, CancellationToken ct);
Task<ApiResponse<object>> CreateMRS(CreateMRSRequest request, CancellationToken ct);
Task<ApiResponse<object>> ApproveMRS(ApproveMRSRequest request, CancellationToken ct);
Task<ApiResponse<object>> CancelMRS(CancelMRSRequest request, CancellationToken ct);
Task<ApiResponse<List<SearchRISProjectCodeResponse>>> SearchRIS(SearchRISProjectCodeRequest request, CancellationToken ct);
Task<ApiResponse<List<ProjectCodeOptionResponse>>> SearchProjects(SearchRISProjectCodeRequest request, CancellationToken ct);
}
}

View File

@ -0,0 +1,20 @@
using CPRNIMS.Infrastructure.Dto.Common;
using CPRNIMS.Infrastructure.Dto.Inventory.Request;
using CPRNIMS.Infrastructure.Dto.Inventory.Response;
using CPRNIMS.Infrastructure.ViewModel.Inventory;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CPRNIMS.Domain.UIContracts.Inventory
{
public interface IRIS
{
Task<ApiResponse<object>> ApproveRIS(ApproveRISRequest request, CancellationToken ct);
Task<ApiResponse<object>> CancelRIS(CancelRISRequest request, CancellationToken ct);
Task<ApiResponse<object>> CreateRIS(CreateRISRequest request, CancellationToken ct);
Task<RISPagedResponse> GetRISPaged(RISPagedRequest request, CancellationToken ct);
}
}

View File

@ -11,6 +11,7 @@ namespace CPRNIMS.Domain.UIContracts.PO
public interface IPurchaseOrder public interface IPurchaseOrder
{ {
#region Get #region Get
Task<List<POVM>> GetCurrencies(User user, POVM viewModels);
Task<List<POVM>> GetSupplierBidById(User user, POVM viewModel); Task<List<POVM>> GetSupplierBidById(User user, POVM viewModel);
Task<List<POVM>> GetSupplierBidByItem(User user, POVM viewModel); Task<List<POVM>> GetSupplierBidByItem(User user, POVM viewModel);
Task<List<POVM>> GetSupplierBid(User user, POVM viewModel); Task<List<POVM>> GetSupplierBid(User user, POVM viewModel);

View File

@ -1,5 +1,6 @@
using CPRNIMS.Domain.UIContracts.Canvass; using CPRNIMS.Domain.UIContracts.Canvass;
using CPRNIMS.Domain.UIContracts.Common; using CPRNIMS.Domain.UIContracts.Common;
using CPRNIMS.Infrastructure.Dto.Common;
using CPRNIMS.Infrastructure.Helper; using CPRNIMS.Infrastructure.Helper;
using CPRNIMS.Infrastructure.Models.Account; using CPRNIMS.Infrastructure.Models.Account;
using CPRNIMS.Infrastructure.Models.Common; using CPRNIMS.Infrastructure.Models.Common;
@ -123,6 +124,54 @@ namespace CPRNIMS.Domain.UIServices.Canvass
throw; throw;
} }
} }
public async Task<PagedResult<CanvassVM>> SendGetPageListApiRequest
(User user, CanvassVM viewModel, string apiEndpoint)
{
var token = await _tokenHelper.GetValidTokenAsync();
try
{
if (string.IsNullOrEmpty(token))
{
return null;
}
viewModel.UserId = user.UserId;
var jsonContent = JsonSerializer.Serialize(viewModel);
var content = new StringContent(jsonContent, Encoding.UTF8, "application/json");
using (var httpClient = _apiConfigurationService.CreateHttpClientWithDefaultHeaders(token))
{
HttpResponseMessage response;
response = await httpClient.PostAsync(apiEndpoint, content);
var jsonResponse = await response.Content.ReadAsStringAsync();
if (response.IsSuccessStatusCode)
{
var options = new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
};
viewModel.messCode = 1;
var result = JsonSerializer.Deserialize<PagedResult<CanvassVM>>(jsonResponse, options);
return result;
}
else
{
var result = JsonSerializer.Deserialize<PagedResult<CanvassVM>>(jsonResponse);
viewModel.messCode = 0;
viewModel.errMessage = "Bad request";
return result;
}
}
}
catch (Exception ex)
{
throw;
}
}
#endregion #endregion
#region Get #region Get
@ -136,11 +185,6 @@ namespace CPRNIMS.Domain.UIServices.Canvass
return await SendGetApiRequest(user, viewModel, return await SendGetApiRequest(user, viewModel,
_configuration["LLI:NonInvent:CanvassMgmt:GetSupplierBidByItem"]); _configuration["LLI:NonInvent:CanvassMgmt:GetSupplierBidByItem"]);
} }
public async Task<List<CanvassVM>> GetCanvassPerSupplier(User user, CanvassVM viewModel)
{
return await SendGetApiRequest(user, viewModel,
_configuration["LLI:NonInvent:CanvassMgmt:GetCanvassPerSupplier"]);
}
public async Task<List<CanvassVM>> GetCanvassPerSupplierEmail(User user, CanvassVM viewModel) public async Task<List<CanvassVM>> GetCanvassPerSupplierEmail(User user, CanvassVM viewModel)
{ {
return await SendGetApiRequest(user, viewModel, return await SendGetApiRequest(user, viewModel,
@ -236,6 +280,16 @@ namespace CPRNIMS.Domain.UIServices.Canvass
return await SendGetApiRequest(user, viewModel, return await SendGetApiRequest(user, viewModel,
_configuration["LLI:NonInvent:CanvassMgmt:GetAlternativeOfferByPRDetailId"]); _configuration["LLI:NonInvent:CanvassMgmt:GetAlternativeOfferByPRDetailId"]);
} }
public async Task<PagedResult<CanvassVM>> GetCanvassPerSupplier(User user, CanvassVM viewModel)
{
return await SendGetPageListApiRequest(user, viewModel,
_configuration["LLI:NonInvent:CanvassMgmt:GetCanvassPerSupplier"]);
}
public async Task<PagedResult<CanvassVM>> GetItemsForTagging(User user, CanvassVM viewModel)
{
return await SendGetPageListApiRequest(user, viewModel,
_configuration["LLI:NonInvent:CanvassMgmt:GetItemsForTagging"]);
}
#endregion #endregion
#region Post Put #region Post Put
public async Task<CanvassVM> PostCanvass(User user, CanvassVM viewModel) public async Task<CanvassVM> PostCanvass(User user, CanvassVM viewModel)
@ -288,6 +342,19 @@ namespace CPRNIMS.Domain.UIServices.Canvass
return await SendPostApiRequest(user, viewModel, return await SendPostApiRequest(user, viewModel,
_configuration["LLI:NonInvent:CanvassMgmt:UnlockFormLink"]); _configuration["LLI:NonInvent:CanvassMgmt:UnlockFormLink"]);
} }
public async Task<CanvassVM> StartCanvass(User user, CanvassVM viewModel)
{
return await SendPostApiRequest(user, viewModel,
_configuration["LLI:NonInvent:CanvassMgmt:StartCanvass"]);
}
public async Task<CanvassVM> PostSupplierForCanvass(User user, CanvassVM viewModel)
{
return await SendPostApiRequest(user, viewModel,
_configuration["LLI:NonInvent:CanvassMgmt:PostSupplierForCanvass"]);
}
#endregion #endregion
} }
} }

View File

@ -1,9 +1,11 @@
using CPRNIMS.Domain.UIContracts.Common; using CPRNIMS.Domain.UIContracts.Common;
using CPRNIMS.Domain.UIContracts.Inventory; using CPRNIMS.Domain.UIContracts.Inventory;
using CPRNIMS.Infrastructure.Dto.Inventory;
using CPRNIMS.Infrastructure.Dto.Inventory.Request;
using CPRNIMS.Infrastructure.Dto.Inventory.Response;
using CPRNIMS.Infrastructure.Helper; using CPRNIMS.Infrastructure.Helper;
using CPRNIMS.Infrastructure.Models.Account; using CPRNIMS.Infrastructure.Models.Account;
using CPRNIMS.Infrastructure.Models.Common; using CPRNIMS.Infrastructure.Models.Common;
using CPRNIMS.Infrastructure.ViewModel.Finance;
using CPRNIMS.Infrastructure.ViewModel.Inventory; using CPRNIMS.Infrastructure.ViewModel.Inventory;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using System; using System;
@ -123,6 +125,73 @@ namespace CPRNIMS.Domain.UIServices.Inventory
throw; throw;
} }
} }
public async Task<TransactContextDto?> GetTransactContextAsync(int inventoryId)
{
var token = await _tokenHelper.GetValidTokenAsync();
if (string.IsNullOrEmpty(token))
return null;
var apiEndpoint =
_configuration["LLI:NonInvent:InventoryMgmt:GetTransactContext"] ?? "api/InventoryMgmt/GetTransactContext";
using var httpClient = _apiConfigurationService.CreateHttpClientWithDefaultHeaders(token);
var response = await httpClient.GetAsync( $"{apiEndpoint}?inventoryId={inventoryId}");
if (!response.IsSuccessStatusCode)
return null;
var jsonResponse = await response.Content.ReadAsStringAsync();
return JsonSerializer.Deserialize<TransactContextDto>(jsonResponse,
new JsonSerializerOptions{PropertyNameCaseInsensitive = true});
}
public async Task<Infrastructure.Dto.Inventory.PagedResult<InventoryResponse>> SendGetPageListApiRequest
(User user, InventoryRequest request, string apiEndpoint)
{
var token = await _tokenHelper.GetValidTokenAsync();
try
{
if (string.IsNullOrEmpty(token))
{
return null;
}
request.UserId = user.UserId;
var jsonContent = JsonSerializer.Serialize(request);
var content = new StringContent(jsonContent, Encoding.UTF8, "application/json");
using (var httpClient = _apiConfigurationService.CreateHttpClientWithDefaultHeaders(token))
{
HttpResponseMessage response;
response = await httpClient.PostAsync(apiEndpoint, content);
var jsonResponse = await response.Content.ReadAsStringAsync();
if (response.IsSuccessStatusCode)
{
var options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true };
var result = JsonSerializer.Deserialize<PagedResult<InventoryResponse>>(jsonResponse, options);
return result ?? new PagedResult<InventoryResponse>();
}
else
{
return new PagedResult<InventoryResponse>
{
IsSuccess = false,
ErrorMessage = $"API returned {response.StatusCode}"
};
}
}
}
catch (Exception ex)
{
throw;
}
}
#endregion #endregion
#region Get #region Get
public async Task<List<InventoryVM>> GetInventoryById(User user, InventoryVM viewModel) public async Task<List<InventoryVM>> GetInventoryById(User user, InventoryVM viewModel)
@ -141,7 +210,11 @@ namespace CPRNIMS.Domain.UIServices.Inventory
return await SendGetApiRequest(user, viewModel, return await SendGetApiRequest(user, viewModel,
_configuration["LLI:NonInvent:InventoryMgmt:GetLotNoById"]); _configuration["LLI:NonInvent:InventoryMgmt:GetLotNoById"]);
} }
public async Task<Infrastructure.Dto.Inventory.PagedResult<InventoryResponse>> GetInventory(User user, InventoryRequest reqquest)
{
return await SendGetPageListApiRequest(user, reqquest,
_configuration["LLI:NonInvent:InventoryMgmt:GetInventory"]);
}
public async Task<List<InventoryVM>> GetInventoryByUserId(User user, InventoryVM viewModel) public async Task<List<InventoryVM>> GetInventoryByUserId(User user, InventoryVM viewModel)
{ {
return await SendGetApiRequest(user, viewModel, return await SendGetApiRequest(user, viewModel,
@ -153,7 +226,11 @@ namespace CPRNIMS.Domain.UIServices.Inventory
return await SendGetApiRequest(user, viewModel, return await SendGetApiRequest(user, viewModel,
_configuration["LLI:NonInvent:InventoryMgmt:GetRequestedItemByUserId"]); _configuration["LLI:NonInvent:InventoryMgmt:GetRequestedItemByUserId"]);
} }
public async Task<List<InventoryVM>> GetLotQtyByItem(User user, InventoryVM viewModel)
{
return await SendGetApiRequest(user, viewModel,
_configuration["LLI:NonInvent:InventoryMgmt:GetLotQtyByItem"]);
}
#endregion #endregion
#region Post Put #region Post Put
public async Task<InventoryVM> PostPutReqApproval(User user, InventoryVM viewModel) public async Task<InventoryVM> PostPutReqApproval(User user, InventoryVM viewModel)
@ -177,12 +254,6 @@ namespace CPRNIMS.Domain.UIServices.Inventory
return await SendPostApiRequest(user, viewModel, return await SendPostApiRequest(user, viewModel,
_configuration["LLI:NonInvent:InventoryMgmt:PostPutReqItems"]); _configuration["LLI:NonInvent:InventoryMgmt:PostPutReqItems"]);
} }
public async Task<List<InventoryVM>> GetLotQtyByItem(User user, InventoryVM viewModel)
{
return await SendGetApiRequest(user, viewModel,
_configuration["LLI:NonInvent:InventoryMgmt:GetLotQtyByItem"]);
}
#endregion #endregion
} }
} }

View File

@ -0,0 +1,126 @@
using Azure.Core;
using CPRNIMS.Domain.UIContracts.Common;
using CPRNIMS.Domain.UIContracts.Inventory;
using CPRNIMS.Infrastructure.Dto.Inventory.Reports;
using CPRNIMS.Infrastructure.Dto.Inventory.Reports.Response;
using CPRNIMS.Infrastructure.Dto.Inventory.Request;
using CPRNIMS.Infrastructure.Helper;
using Microsoft.AspNetCore.WebUtilities;
using Microsoft.Extensions.Configuration;
using System.Text;
using System.Text.Json;
namespace CPRNIMS.Domain.UIServices.Inventory
{
public class InventoryReports : IInventoryReports
{
private readonly IConfiguration _configuration;
private readonly TokenHelper _tokenHelper;
private readonly IApiConfigurationService _apiConfigurationService;
public InventoryReports(IConfiguration configuration, TokenHelper tokenHelper, IApiConfigurationService apiConfigurationService)
{
_configuration = configuration;
_tokenHelper = tokenHelper;
_apiConfigurationService = apiConfigurationService;
}
private static readonly JsonSerializerOptions _jsonOpts = new()
{
PropertyNameCaseInsensitive = true
};
public async Task<InventoryReportDto> GetInventoryReportAsync(InventoryReportsRequest request, CancellationToken ct)
{
var token = await _tokenHelper.GetValidTokenAsync();
if (string.IsNullOrEmpty(token))
throw new InvalidOperationException("Token has been expired.");
var baseEndpoint = _configuration["LLI:NonInvent:InventoryMgmt:GetInventoryReport"]
?? throw new InvalidOperationException("GetInventoryReport endpoint is not configured.");
var qs = new Dictionary<string, string?>
{
["dateFrom"] = request.DateFrom.ToString("yyyy-MM-dd"),
["dateTo"] = request.DateTo.ToString("yyyy-MM-dd"),
["department"] = request.Department,
["page"] = request.Page.ToString(),
["pageSize"] = request.PageSize.ToString(),
["paginate"] = request.Paginate.ToString().ToLowerInvariant()
};
var url = QueryHelpers.AddQueryString(baseEndpoint, qs);
using var httpClient = _apiConfigurationService.CreateHttpClientWithDefaultHeaders(token);
var response = await httpClient.GetAsync(url, ct);
var json = await response.Content.ReadAsStringAsync(ct);
if (!response.IsSuccessStatusCode)
return new InventoryReportDto();
var result = JsonSerializer.Deserialize<InventoryReportDto>(json, _jsonOpts);
return result ?? new InventoryReportDto();
}
public async Task<MRSReportDto> GetMRSReportAsync(InventoryReportsRequest request, CancellationToken ct)
{
var token = await _tokenHelper.GetValidTokenAsync();
if (string.IsNullOrEmpty(token))
throw new InvalidOperationException("Token has been expired.");
var baseEndpoint = _configuration["LLI:NonInvent:InventoryMgmt:GetMRSReport"]
?? throw new InvalidOperationException("GetMRS endpoint is not configured.");
var qs = new Dictionary<string, string?>
{
["dateFrom"] = request.DateFrom.ToString("yyyy-MM-dd"),
["dateTo"] = request.DateTo.ToString("yyyy-MM-dd"),
["department"] = request.Department,
["page"] = request.Page.ToString(),
["pageSize"] = request.PageSize.ToString(),
["paginate"] = request.Paginate.ToString().ToLowerInvariant()
};
var url = QueryHelpers.AddQueryString(baseEndpoint, qs);
using var httpClient = _apiConfigurationService.CreateHttpClientWithDefaultHeaders(token);
var response = await httpClient.GetAsync(url, ct);
var json = await response.Content.ReadAsStringAsync(ct);
if (!response.IsSuccessStatusCode)
return new MRSReportDto { };
var result = JsonSerializer.Deserialize<MRSReportDto>(json, _jsonOpts);
return result ?? new MRSReportDto();
}
public async Task<RISReportDto> GetRISReportAsync(InventoryReportsRequest request, CancellationToken ct)
{
var token = await _tokenHelper.GetValidTokenAsync();
if (string.IsNullOrEmpty(token))
throw new InvalidOperationException("Token has been expired.");
var baseEndpoint = _configuration["LLI:NonInvent:InventoryMgmt:GetRISReport"]
?? throw new InvalidOperationException("GetMRS endpoint is not configured.");
var qs = new Dictionary<string, string?>
{
["dateFrom"] = request.DateFrom.ToString("yyyy-MM-dd"),
["dateTo"] = request.DateTo.ToString("yyyy-MM-dd"),
["department"] = request.Department,
["page"] = request.Page.ToString(),
["pageSize"] = request.PageSize.ToString(),
["paginate"] = request.Paginate.ToString().ToLowerInvariant()
};
var url = QueryHelpers.AddQueryString(baseEndpoint, qs);
using var httpClient = _apiConfigurationService.CreateHttpClientWithDefaultHeaders(token);
var response = await httpClient.GetAsync(url, ct);
var json = await response.Content.ReadAsStringAsync(ct);
if (!response.IsSuccessStatusCode)
return new RISReportDto {};
var result = JsonSerializer.Deserialize<RISReportDto>(json, _jsonOpts);
return result ?? new RISReportDto();
}
}
}

View File

@ -0,0 +1,197 @@
using Azure.Core;
using CPRNIMS.Domain.Services;
using CPRNIMS.Domain.UIContracts.Common;
using CPRNIMS.Domain.UIContracts.Inventory;
using CPRNIMS.Infrastructure.Dto.Common;
using CPRNIMS.Infrastructure.Dto.Inventory;
using CPRNIMS.Infrastructure.Dto.Inventory.Request;
using CPRNIMS.Infrastructure.Dto.Inventory.Response;
using CPRNIMS.Infrastructure.Helper;
using Microsoft.Extensions.Configuration;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
namespace CPRNIMS.Domain.UIServices.Inventory
{
public class MRS : IMRS
{
private readonly IConfiguration _configuration;
private readonly TokenHelper _tokenHelper;
private readonly IApiConfigurationService _apiConfigurationService;
public MRS(IConfiguration configuration, TokenHelper tokenHelper,IApiConfigurationService apiConfigurationService)
{
_configuration = configuration;
_tokenHelper = tokenHelper;
_apiConfigurationService = apiConfigurationService;
}
private static readonly JsonSerializerOptions _mrsJsonOpts = new()
{
PropertyNameCaseInsensitive = true
};
public async Task<MRSPagedResponse> GetMRSPaged(MRSPagedRequest request, CancellationToken ct)
{
var token = await _tokenHelper.GetValidTokenAsync();
if (string.IsNullOrEmpty(token))
throw new InvalidOperationException("Token has been expired.");
var baseEndpoint = _configuration["LLI:NonInvent:InventoryMgmt:GetMRS"]
?? throw new InvalidOperationException("GetMRS endpoint is not configured.");
var qs = new StringBuilder(baseEndpoint).Append('?');
qs.Append($"pageNumber={request.PageNumber}&pageSize={request.PageSize}");
if (!string.IsNullOrWhiteSpace(request.SearchMRSNo))
qs.Append($"&searchMRSNo={Uri.EscapeDataString(request.SearchMRSNo)}");
if (!string.IsNullOrWhiteSpace(request.SearchRISNo))
qs.Append($"&searchRISNo={Uri.EscapeDataString(request.SearchRISNo)}");
if (!string.IsNullOrWhiteSpace(request.SearchItemName))
qs.Append($"&searchItemName={Uri.EscapeDataString(request.SearchItemName)}");
if (!string.IsNullOrWhiteSpace(request.SearchReturnedBy))
qs.Append($"&searchReturnedBy={Uri.EscapeDataString(request.SearchReturnedBy)}");
if (request.Status.HasValue)
qs.Append($"&status={request.Status.Value}");
if (!string.IsNullOrWhiteSpace(request.Condition))
qs.Append($"&condition={Uri.EscapeDataString(request.Condition)}");
using var http = _apiConfigurationService.CreateHttpClientWithDefaultHeaders(token);
var response = await http.GetAsync(qs.ToString(), ct);
var json = await response.Content.ReadAsStringAsync(ct);
if (!response.IsSuccessStatusCode)
return new MRSPagedResponse { Data = [], RecordsTotal = 0 };
var result = JsonSerializer.Deserialize<MRSPagedResponse>(json, _mrsJsonOpts);
return result ?? new MRSPagedResponse();
}
public async Task<ApiResponse<object>> CreateMRS(CreateMRSRequest request, CancellationToken ct)
{
var token = await _tokenHelper.GetValidTokenAsync();
if (string.IsNullOrEmpty(token))
throw new InvalidOperationException("Token has been expired.");
var endpoint = _configuration["LLI:NonInvent:InventoryMgmt:CreateMRS"]
?? throw new InvalidOperationException("CreateMRS endpoint is not configured.");
using var http = _apiConfigurationService.CreateHttpClientWithDefaultHeaders(token);
using var content = new StringContent(JsonSerializer.Serialize(request), Encoding.UTF8, "application/json");
var response = await http.PostAsync(endpoint, content, ct);
var json = await response.Content.ReadAsStringAsync(ct);
var result = JsonSerializer.Deserialize<ApiResponse<object>>(json, _mrsJsonOpts)
?? new ApiResponse<object> { success = false, message = $"HTTP {(int)response.StatusCode}" };
if (!response.IsSuccessStatusCode)
result.success = false;
return result;
}
public async Task<ApiResponse<object>> ApproveMRS(ApproveMRSRequest request, CancellationToken ct)
{
var token = await _tokenHelper.GetValidTokenAsync();
if (string.IsNullOrEmpty(token))
throw new InvalidOperationException("Token has been expired.");
var endpoint = _configuration["LLI:NonInvent:InventoryMgmt:ApproveMRS"]
?? throw new InvalidOperationException("ApproveMRS endpoint is not configured.");
using var http = _apiConfigurationService.CreateHttpClientWithDefaultHeaders(token);
using var content = new StringContent(
JsonSerializer.Serialize(request),
Encoding.UTF8, "application/json");
var response = await http.PostAsync(endpoint, content, ct);
var json = await response.Content.ReadAsStringAsync(ct);
var result = JsonSerializer.Deserialize<ApiResponse<object>>(json, _mrsJsonOpts)
?? new ApiResponse<object> { success = false, message = $"HTTP {(int)response.StatusCode}" };
if (!response.IsSuccessStatusCode)
result.success = false;
return result;
}
public async Task<ApiResponse<object>> CancelMRS(CancelMRSRequest request, CancellationToken ct)
{
var token = await _tokenHelper.GetValidTokenAsync();
if (string.IsNullOrEmpty(token))
throw new InvalidOperationException("Token has been expired.");
var endpoint = _configuration["LLI:NonInvent:InventoryMgmt:CancelMRS"]
?? throw new InvalidOperationException("CancelMRS endpoint is not configured.");
using var http = _apiConfigurationService.CreateHttpClientWithDefaultHeaders(token);
using var content = new StringContent(
JsonSerializer.Serialize(request),
Encoding.UTF8, "application/json");
var response = await http.PostAsync(endpoint, content, ct);
var json = await response.Content.ReadAsStringAsync(ct);
var result = JsonSerializer.Deserialize<ApiResponse<object>>(json, _mrsJsonOpts)
?? new ApiResponse<object> { success = false, message = $"HTTP {(int)response.StatusCode}" };
if (!response.IsSuccessStatusCode)
result.success = false;
return result;
}
public async Task<ApiResponse<List<SearchRISProjectCodeResponse>>> SearchRIS(SearchRISProjectCodeRequest request, CancellationToken ct)
{
var token = await _tokenHelper.GetValidTokenAsync();
if (string.IsNullOrEmpty(token))
throw new InvalidOperationException("Token has been expired.");
var baseEndpoint = _configuration["LLI:NonInvent:InventoryMgmt:SearchRIS"]
?? throw new InvalidOperationException("GetMRS endpoint is not configured.");
var qs = new StringBuilder(baseEndpoint).Append('?');
if (!string.IsNullOrWhiteSpace(request.SearchRISNo))
qs.Append($"&searchRISNo={Uri.EscapeDataString(request.SearchRISNo)}");
if (!string.IsNullOrWhiteSpace(request.SearchRISNo))
qs.Append($"&projectCodeId= {request.SearchProjectCodeId}");
using var http = _apiConfigurationService.CreateHttpClientWithDefaultHeaders(token);
var response = await http.GetAsync(qs.ToString(), ct);
var json = await response.Content.ReadAsStringAsync(ct);
if (!response.IsSuccessStatusCode)
return new ApiResponse<List<SearchRISProjectCodeResponse>>{ };
var result = JsonSerializer.Deserialize<ApiResponse<List<SearchRISProjectCodeResponse>>>(json, _mrsJsonOpts);
return result ?? new ApiResponse<List<SearchRISProjectCodeResponse>>();
}
public async Task<ApiResponse<List<ProjectCodeOptionResponse>>> SearchProjects(SearchRISProjectCodeRequest request, CancellationToken ct)
{
var token = await _tokenHelper.GetValidTokenAsync();
if (string.IsNullOrEmpty(token))
throw new InvalidOperationException("Token has expired.");
var baseEndpoint = _configuration["LLI:NonInvent:InventoryMgmt:SearchProjects"]
?? throw new InvalidOperationException("SearchProjects endpoint is not configured.");
var qs = new StringBuilder(baseEndpoint);
if (!string.IsNullOrWhiteSpace(request.SearchProjectCode))
qs.Append('?').Append("searchProjectCode=").Append(Uri.EscapeDataString(request.SearchProjectCode));
using var http = _apiConfigurationService.CreateHttpClientWithDefaultHeaders(token);
var response = await http.GetAsync(qs.ToString(), ct);
var json = await response.Content.ReadAsStringAsync(ct);
if (!response.IsSuccessStatusCode)
return new ApiResponse<List<ProjectCodeOptionResponse>>();
var result = JsonSerializer.Deserialize<ApiResponse<List<ProjectCodeOptionResponse>>>(json, _mrsJsonOpts);
return result ?? new ApiResponse<List<ProjectCodeOptionResponse>>();
}
}
}

View File

@ -0,0 +1,157 @@
using CPRNIMS.Domain.UIContracts.Common;
using CPRNIMS.Domain.UIContracts.Inventory;
using CPRNIMS.Infrastructure.Dto.Common;
using CPRNIMS.Infrastructure.Dto.Inventory.Request;
using CPRNIMS.Infrastructure.Dto.Inventory.Response;
using CPRNIMS.Infrastructure.Dto.PR.Response;
using CPRNIMS.Infrastructure.Helper;
using Microsoft.Extensions.Configuration;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
namespace CPRNIMS.Domain.UIServices.Inventory
{
public class RIS : IRIS
{
private readonly IConfiguration _configuration;
private readonly TokenHelper _tokenHelper;
private readonly IApiConfigurationService _apiConfigurationService;
public RIS(IConfiguration configuration, TokenHelper tokenHelper,
IApiConfigurationService apiConfigurationService)
{
_configuration = configuration;
_tokenHelper = tokenHelper;
_apiConfigurationService = apiConfigurationService;
}
#region Get
public async Task<RISPagedResponse> GetRISPaged(RISPagedRequest request, CancellationToken ct)
{
var token = await _tokenHelper.GetValidTokenAsync();
if (string.IsNullOrEmpty(token))
throw new InvalidOperationException("Token has been expired.");
var baseEndpoint = _configuration["LLI:NonInvent:InventoryMgmt:GetRIS"]
?? throw new InvalidOperationException("GetRIS endpoint is not configured.");
// ── Build query string — GET request, no body ──
var qs = new StringBuilder(baseEndpoint).Append('?');
qs.Append($"pageNumber={request.PageNumber}");
qs.Append($"&pageSize={request.PageSize}");
if (!string.IsNullOrWhiteSpace(request.SearchRISNo))
qs.Append($"&searchRISNo={Uri.EscapeDataString(request.SearchRISNo)}");
if (!string.IsNullOrWhiteSpace(request.SearchItemName))
qs.Append($"&searchItemName={Uri.EscapeDataString(request.SearchItemName)}");
if (!string.IsNullOrWhiteSpace(request.SearchIssuedTo))
qs.Append($"&searchIssuedTo={Uri.EscapeDataString(request.SearchIssuedTo)}");
if (!string.IsNullOrWhiteSpace(request.Discipline))
qs.Append($"&discipline={Uri.EscapeDataString(request.Discipline)}");
if (request.Status.HasValue)
qs.Append($"&status={request.Status.Value}");
using var httpClient = _apiConfigurationService.CreateHttpClientWithDefaultHeaders(token);
// ── GET, no content body ──
var response = await httpClient.GetAsync(qs.ToString(), ct);
var json = await response.Content.ReadAsStringAsync(ct);
if (!response.IsSuccessStatusCode)
{
Console.WriteLine($"[GetRISPaged] Error {(int)response.StatusCode}: {json}");
return new RISPagedResponse { Data = [], RecordsTotal = 0 };
}
var result = JsonSerializer.Deserialize<RISPagedResponse>(json, _jsonOptions);
return result ?? new RISPagedResponse();
}
#endregion
#region Post Put
public async Task<ApiResponse<object>> ApproveRIS(ApproveRISRequest request, CancellationToken ct)
{
var token = await _tokenHelper.GetValidTokenAsync();
if (string.IsNullOrEmpty(token))
throw new InvalidOperationException("Token has been expired.");
var endpoint = _configuration["LLI:NonInvent:InventoryMgmt:ApproveRIS"]
?? throw new InvalidOperationException("ApproveRIS endpoint is not configured.");
using var httpClient = _apiConfigurationService.CreateHttpClientWithDefaultHeaders(token);
using var content = new StringContent(JsonSerializer.Serialize(request),Encoding.UTF8,"application/json");
var response = await httpClient.PutAsync(endpoint, content, ct);
var json = await response.Content.ReadAsStringAsync(ct);
var result = JsonSerializer.Deserialize<ApiResponse<object>>(json, _jsonOptions)
?? new ApiResponse<object> { success = false, message = $"HTTP {(int)response.StatusCode}" };
if (!response.IsSuccessStatusCode)
result.success = false;
return result;
}
public async Task<ApiResponse<object>> CancelRIS(CancelRISRequest request, CancellationToken ct)
{
var token = await _tokenHelper.GetValidTokenAsync();
if (string.IsNullOrEmpty(token))
throw new InvalidOperationException("Token has been expired.");
var endpoint = _configuration["LLI:NonInvent:InventoryMgmt:CancelRIS"]
?? throw new InvalidOperationException("CancelRIS endpoint is not configured.");
using var httpClient = _apiConfigurationService.CreateHttpClientWithDefaultHeaders(token);
using var content = new StringContent(JsonSerializer.Serialize(request), Encoding.UTF8, "application/json");
var response = await httpClient.PutAsync(endpoint, content, ct);
var json = await response.Content.ReadAsStringAsync(ct);
var result = JsonSerializer.Deserialize<ApiResponse<object>>(json, _jsonOptions)
?? new ApiResponse<object> { success = false, message = $"HTTP {(int)response.StatusCode}" };
if (!response.IsSuccessStatusCode)
result.success = false;
return result;
}
public async Task<ApiResponse<object>> CreateRIS(CreateRISRequest request, CancellationToken ct)
{
var token = await _tokenHelper.GetValidTokenAsync();
if (string.IsNullOrEmpty(token))
throw new InvalidOperationException("Token has been expired.");
var endpoint = _configuration["LLI:NonInvent:InventoryMgmt:CreateRIS"] ??
throw new InvalidOperationException("CreateRIS endpoint is not configured.");
using var httpClient = _apiConfigurationService.CreateHttpClientWithDefaultHeaders(token);
using var content = new StringContent(JsonSerializer.Serialize(request),Encoding.UTF8,"application/json");
var response = await httpClient.PostAsync(endpoint, content,ct);
var json = await response.Content.ReadAsStringAsync();
var result = JsonSerializer.Deserialize<ApiResponse<object>>(json, _jsonOptions)
?? new ApiResponse<object> { success = false, message = $"HTTP {(int)response.StatusCode}" };
if (!response.IsSuccessStatusCode)
result.success = false;
return result;
}
private static readonly JsonSerializerOptions _jsonOptions = new()
{
PropertyNameCaseInsensitive = true
};
#endregion
}
}

View File

@ -284,6 +284,11 @@ namespace CPRNIMS.Domain.UIServices.PO
return await SendGetApiRequest(user, viewModel, return await SendGetApiRequest(user, viewModel,
_configuration["LLI:NonInvent:POMgmt:GetPortOfDischarge"]); _configuration["LLI:NonInvent:POMgmt:GetPortOfDischarge"]);
} }
public async Task<List<POVM>> GetCurrencies(User user, POVM viewModel)
{
return await SendGetApiRequest(user, viewModel,
_configuration["LLI:NonInvent:POMgmt:GetCurrencies"]);
}
#endregion #endregion
#region Post Put #region Post Put
public async Task<POVM> PostApprovedSupplier(User user, POVM viewModel) public async Task<POVM> PostApprovedSupplier(User user, POVM viewModel)

View File

@ -1,4 +1,6 @@
using CPRNIMS.Infrastructure.Entities.Account; using CPRNIMS.Infrastructure.Dto.Canvass.Response;
using CPRNIMS.Infrastructure.Dto.Inventory.Response;
using CPRNIMS.Infrastructure.Entities.Account;
using CPRNIMS.Infrastructure.Entities.Canvass; using CPRNIMS.Infrastructure.Entities.Canvass;
using CPRNIMS.Infrastructure.Entities.Common; using CPRNIMS.Infrastructure.Entities.Common;
using CPRNIMS.Infrastructure.Entities.Finance; using CPRNIMS.Infrastructure.Entities.Finance;
@ -21,9 +23,7 @@ namespace CPRNIMS.Infrastructure.Database
public class NonInventoryDbContext : IdentityDbContext<ApplicationUser> public class NonInventoryDbContext : IdentityDbContext<ApplicationUser>
{ {
public NonInventoryDbContext(DbContextOptions<NonInventoryDbContext> options) : base(options) { } public NonInventoryDbContext(DbContextOptions<NonInventoryDbContext> options) : base(options) { }
public virtual DbSet<ItemCode> ItemCodes { get; set; } #region Common
public virtual DbSet<ItemList> ItemList { get; set; }
public virtual DbSet<Item> Items { get; set; }
public DbSet<Departments> Departments { get; set; } public DbSet<Departments> Departments { get; set; }
public DbSet<IdentityRole> IdentityRoles { get; set; } public DbSet<IdentityRole> IdentityRoles { get; set; }
public DbSet<AuthorizeRoles> AuthorizeRoles { get; set; } public DbSet<AuthorizeRoles> AuthorizeRoles { get; set; }
@ -31,41 +31,57 @@ namespace CPRNIMS.Infrastructure.Database
public DbSet<IdentityUserRole<string>> IdentityUserRoles { get; set; } public DbSet<IdentityUserRole<string>> IdentityUserRoles { get; set; }
public DbSet<ForgotPassword> ForgotPasswords { get; set; } public DbSet<ForgotPassword> ForgotPasswords { get; set; }
public virtual DbSet<Otps> Otps { get; set; } public virtual DbSet<Otps> Otps { get; set; }
public virtual DbSet<ErrorMessage> ErrorMessages { get; set; }
public virtual DbSet<ControllerAccess> ControllerAccess { get; set; }
public virtual DbSet<SMTPCredential> SMTPCredentials { get; set; }
#endregion
#region Item
public virtual DbSet<ItemCode> ItemCodes { get; set; }
public virtual DbSet<ItemList> ItemList { get; set; }
public virtual DbSet<Item> Items { get; set; }
public DbSet<ItemDtos> ItemDtos { get; set; }
public DbSet<Attachment> Attachments { get; set; } public DbSet<Attachment> Attachments { get; set; }
public virtual DbSet<AttachmentExtension> AttachmentExtensions { get; set; } public virtual DbSet<AttachmentExtension> AttachmentExtensions { get; set; }
public virtual DbSet<AttachmentFileType> AttachmentFileTypes { get; set; } public virtual DbSet<AttachmentFileType> AttachmentFileTypes { get; set; }
public virtual DbSet<ItemAttachement> ItemAttachements { get; set; } public virtual DbSet<ItemAttachement> ItemAttachements { get; set; }
public virtual DbSet<ErrorMessage> ErrorMessages { get; set; }
public virtual DbSet<ControllerAccess> ControllerAccess { get; set; }
public virtual DbSet<ItemCategory> ItemCategories { get; set; } public virtual DbSet<ItemCategory> ItemCategories { get; set; }
public virtual DbSet<UnitOfMessure> UnitOfMessures { get; set; } public virtual DbSet<UnitOfMessure> UnitOfMessures { get; set; }
public virtual DbSet<ItemColor> ItemColors { get; set; } public virtual DbSet<ItemColor> ItemColors { get; set; }
public virtual DbSet<ItemLocalization> ItemLocalizations { get; set; } public virtual DbSet<ItemLocalization> ItemLocalizations { get; set; }
public virtual DbSet<ItemCart> ItemCarts { get; set; } public virtual DbSet<ItemCart> ItemCarts { get; set; }
public virtual DbSet<ItemApproval> ItemApprovals { get; set; }
public virtual DbSet<Entities.Inventory.ItemDetail> ItemDetails { get; set; }
#endregion
#region PR
public virtual DbSet<PR> PRs { get; set; } public virtual DbSet<PR> PRs { get; set; }
public virtual DbSet<Approved> Approved { get; set; } public virtual DbSet<Approved> Approved { get; set; }
public virtual DbSet<ApprovedPR> ApprovedPrs { get; set; } public virtual DbSet<ApprovedPR> ApprovedPrs { get; set; }
public virtual DbSet<DeletedPR> DeletedPRs { get; set; } public virtual DbSet<DeletedPR> DeletedPRs { get; set; }
public virtual DbSet<SMTPCredential> SMTPCredentials { get; set; }
public virtual DbSet<PRDetails> PRDetails { get; set; } public virtual DbSet<PRDetails> PRDetails { get; set; }
public virtual DbSet<PRItemList> PRItemLists { get; set; } public virtual DbSet<PRItemList> PRItemLists { get; set; }
public DbSet<PRAttachments> PRAttachments { get; set; } public DbSet<PRAttachments> PRAttachments { get; set; }
public DbSet<ProjectCodes> ProjectCodes { get; set; } public DbSet<ProjectCodes> ProjectCodes { get; set; }
public virtual DbSet<Entities.Purchasing.PRList> PRLists { get; set; }
public virtual DbSet<Entities.Canvass.PRList> PRItemList { get; set; }
public virtual DbSet<PRWOCanvass> PRWOCanvasses { get; set; }
public virtual DbSet<Entities.PO.ItemDetail> PRItemDetails { get; set; }
public virtual DbSet<DetailedPRTracking> DetailedPRTrackings { get; set; }
public virtual DbSet<PRTracking> PRTrackings { get; set; }
public virtual DbSet<Dashboard> Dashboards { get; set; }
#endregion
#region Canvassing
public virtual DbSet<NotificationById> NotificationByIds { get; set; } public virtual DbSet<NotificationById> NotificationByIds { get; set; }
public virtual DbSet<AlternativeOffer> AlternativeOffers { get; set; } public virtual DbSet<AlternativeOffer> AlternativeOffers { get; set; }
public virtual DbSet<AlternativeOfferDetails> AlternativeOfferDetails { get; set; } public virtual DbSet<AlternativeOfferDetails> AlternativeOfferDetails { get; set; }
public virtual DbSet<MyPRWOCanvass> MyPRWOCanvass { get; set; } public virtual DbSet<MyPRWOCanvass> MyPRWOCanvass { get; set; }
public virtual DbSet<Entities.Purchasing.PRList> PRLists { get; set; }
public virtual DbSet<Entities.Canvass.PRList> PRItemList { get; set; }
public virtual DbSet<ItemApproval> ItemApprovals { get; set; }
public virtual DbSet<ForReceiving> ForReceivings { get; set; }
public virtual DbSet<Infrastructure.Entities.Receiving.RRReport> RRReports { get; set; }
public virtual DbSet<ReceivingDetail> ReceivingDetails { get; set; }
public virtual DbSet<RRDetail> RRDetails { get; set; }
public virtual DbSet<ForRR> ForRRs { get; set; }
public virtual DbSet<RR> RRs { get; set; }
public virtual DbSet<Canvass> Canvasses { get; set; } public virtual DbSet<Canvass> Canvasses { get; set; }
public DbSet<SupplierForCanvass> SupplierForCanvass { get; set; }
public DbSet<SupplierResponseDto> SupplierResponses { get; set; }
public DbSet<SupplierItems> SupplierItems { get; set; } public DbSet<SupplierItems> SupplierItems { get; set; }
public DbSet<ItemsForTagging> ItemsForTaggings { get; set; }
public virtual DbSet<ForCanvassFollowUp> ForCanvassFollowUps { get; set; } public virtual DbSet<ForCanvassFollowUp> ForCanvassFollowUps { get; set; }
public virtual DbSet<WOResponse> WOResponses { get; set; } public virtual DbSet<WOResponse> WOResponses { get; set; }
public virtual DbSet<WOResponseById> WOResponseByIds { get; set; } public virtual DbSet<WOResponseById> WOResponseByIds { get; set; }
@ -80,8 +96,12 @@ namespace CPRNIMS.Infrastructure.Database
public virtual DbSet<RFQReference> RFQReferences { get; set; } public virtual DbSet<RFQReference> RFQReferences { get; set; }
public virtual DbSet<CanvassDetail> CanvassDetails { get; set; } public virtual DbSet<CanvassDetail> CanvassDetails { get; set; }
public virtual DbSet<PRCanvassDetail> PRCanvassDetails { get; set; } public virtual DbSet<PRCanvassDetail> PRCanvassDetails { get; set; }
public DbSet<ForAISearchingTagging> ForAISearchingTaggings { get; set; }
public virtual DbSet<CanvassGroupByPRNo> CanvassGroupByPRNos { get; set; } public virtual DbSet<CanvassGroupByPRNo> CanvassGroupByPRNos { get; set; }
public virtual DbSet<ForCanvass> ForCanvasses { get; set; } public virtual DbSet<ForCanvass> ForCanvasses { get; set; }
#endregion
#region PO
public virtual DbSet<ForPO> ForPOs { get; set; } public virtual DbSet<ForPO> ForPOs { get; set; }
public virtual DbSet<CreatedPO> CreatedPOs { get; set; } public virtual DbSet<CreatedPO> CreatedPOs { get; set; }
public virtual DbSet<CustomPO> CustomPOs { get; set; } public virtual DbSet<CustomPO> CustomPOs { get; set; }
@ -89,6 +109,7 @@ namespace CPRNIMS.Infrastructure.Database
public virtual DbSet<ForPOApproval> ForPOApprovals { get; set; } public virtual DbSet<ForPOApproval> ForPOApprovals { get; set; }
public virtual DbSet<ApprovedPO> ApprovedPOs { get; set; } public virtual DbSet<ApprovedPO> ApprovedPOs { get; set; }
public virtual DbSet<PurchaseOrder> PurchaseOrders { get; set; } public virtual DbSet<PurchaseOrder> PurchaseOrders { get; set; }
public virtual DbSet<ForPayment> ForPayments { get; set; }
public virtual DbSet<PO> POs { get; set; } public virtual DbSet<PO> POs { get; set; }
public DbSet<PODetails> PODetails { get; set; } public DbSet<PODetails> PODetails { get; set; }
public virtual DbSet<PRPOSummaryCount> PRPOSummaryCounts { get; set; } public virtual DbSet<PRPOSummaryCount> PRPOSummaryCounts { get; set; }
@ -98,29 +119,44 @@ namespace CPRNIMS.Infrastructure.Database
public virtual DbSet<PortOfDischarges> PortOfDischarges { get; set; } public virtual DbSet<PortOfDischarges> PortOfDischarges { get; set; }
public virtual DbSet<DocRequired> DocRequireds { get; set; } public virtual DbSet<DocRequired> DocRequireds { get; set; }
public virtual DbSet<BiddingApproval> BiddingApprovals { get; set; } public virtual DbSet<BiddingApproval> BiddingApprovals { get; set; }
public virtual DbSet<Entities.Receiving.RRDetail> RRDetailss { get; set; }
public virtual DbSet<RRSeries> RRSeries { get; set; }
public virtual DbSet<PRWOCanvass> PRWOCanvasses { get; set; }
public virtual DbSet<ForPayment> ForPayments { get; set; }
public virtual DbSet<Inventory> Inventories { get; set; }
public virtual DbSet<Entities.Inventory.ItemDetail> ItemDetails { get; set; }
public virtual DbSet<Entities.PO.ItemDetail> PRItemDetails { get; set; }
public virtual DbSet<Lot> Lots { get; set; }
public virtual DbSet<LotQtyByItem> LotQtyByItems { get; set; }
public virtual DbSet<RequestItem> RequestItems { get; set; }
public virtual DbSet<RequestItemDetail> RequestItemDetails { get; set; }
public virtual DbSet<NotifUserKey> NotifUserKeys { get; set; } public virtual DbSet<NotifUserKey> NotifUserKeys { get; set; }
public virtual DbSet<ItemListForPO> ItemListForPOs { get; set; } public virtual DbSet<ItemListForPO> ItemListForPOs { get; set; }
public virtual DbSet<CreatedPOPerSupId> CreatedPOPerSupIds { get; set; } public virtual DbSet<CreatedPOPerSupId> CreatedPOPerSupIds { get; set; }
public virtual DbSet<PRTracking> PRTrackings { get; set; } public DbSet<IncomingShipment> IncomingShipments { get; set; }
public virtual DbSet<Dashboard> Dashboards { get; set; } public virtual DbSet<IncomingShipmentDto> IncomingShipmentDtos { get; set; }
public virtual DbSet<PaymentTerm> PaymentTerms { get; set; } public virtual DbSet<PaymentTerm> PaymentTerms { get; set; }
public virtual DbSet<Incoterm> Incoterms { get; set; } public virtual DbSet<Incoterm> Incoterms { get; set; }
public virtual DbSet<CentralPONo> CentralPONos { get; set; } public virtual DbSet<CentralPONo> CentralPONos { get; set; }
public virtual DbSet<DetailedPRTracking> DetailedPRTrackings { get; set; } public DbSet<Currencies> Currencies { get; set; }
public DbSet<IncomingShipment> IncomingShipments { get; set; } #endregion
public virtual DbSet<IncomingShipmentDto> IncomingShipmentDtos { get; set; }
#region Inventory
public virtual DbSet<Inventory> Inventories { get; set; }
public virtual DbSet<Lot> Lots { get; set; }
public virtual DbSet<LotType> LotTypes { get; set; }
public virtual DbSet<LotQtyByItem> LotQtyByItems { get; set; }
public virtual DbSet<RequestItem> RequestItems { get; set; }
public virtual DbSet<RequestItemDetail> RequestItemDetails { get; set; }
public virtual DbSet<RIS> RIS { get; set; }
public virtual DbSet<MRS> MRS { get; set; }
public virtual DbSet<Discipline> Disciplines { get; set; }
public virtual DbSet<InventTrans> InventTrans { get; set; }
public virtual DbSet<InventTransDetail> InventTransDetails { get; set; }
public DbSet<InventoryByIdResponse> InventoryByIdResponses { get; set; }
#endregion
#region RR
public virtual DbSet<ForReceiving> ForReceivings { get; set; }
public virtual DbSet<Infrastructure.Entities.Receiving.RRReport> RRReports { get; set; }
public virtual DbSet<ReceivingDetail> ReceivingDetails { get; set; }
public virtual DbSet<RRDetailDto> RRDetailDtos { get; set; }
public virtual DbSet<ForRR> ForRRs { get; set; }
public virtual DbSet<RR> RRs { get; set; }
public virtual DbSet<Entities.Receiving.RRDetailDto> RRDetailss { get; set; }
public virtual DbSet<Entities.Receiving.RRDetails> RRDetails { get; set; }
public virtual DbSet<RRSeries> RRSeries { get; set; }
#endregion
#region Automation Part #region Automation Part
public virtual DbSet<AllForCanvass> AllForCanvasses { get; set; } public virtual DbSet<AllForCanvass> AllForCanvasses { get; set; }
@ -165,7 +201,7 @@ namespace CPRNIMS.Infrastructure.Database
b.HasOne(u => u.Attachment) b.HasOne(u => u.Attachment)
.WithOne(a => a.ApplicationUser) .WithOne(a => a.ApplicationUser)
.HasForeignKey<Attachment>(a => a.AttachmentId) .HasForeignKey<Attachment>(a => a.AttachmentId)
.IsRequired(false); // Allow null if there is no attachment .IsRequired(false);
}); });
modelBuilder.Entity<Attachment>(b => modelBuilder.Entity<Attachment>(b =>
{ {
@ -173,7 +209,7 @@ namespace CPRNIMS.Infrastructure.Database
b.HasOne(a => a.AttachmentExtention) b.HasOne(a => a.AttachmentExtention)
.WithOne() .WithOne()
.HasForeignKey<Attachment>(a => a.ExtensionId) .HasForeignKey<Attachment>(a => a.ExtensionId)
.IsRequired(false); // Allow null if there is no extension .IsRequired(false);
}); });
modelBuilder.Entity<ApplicationUser>(b => modelBuilder.Entity<ApplicationUser>(b =>
{ {
@ -231,6 +267,74 @@ namespace CPRNIMS.Infrastructure.Database
{ {
b.ToTable("UserRoles"); b.ToTable("UserRoles");
}); });
modelBuilder.Entity<Inventory>()
.HasOne(i => i.Lot)
.WithMany()
.HasForeignKey(i => i.LotId)
.OnDelete(DeleteBehavior.Restrict);
modelBuilder.Entity<Inventory>()
.HasOne(i => i.Item)
.WithMany()
.HasForeignKey(i => i.ItemNo)
.OnDelete(DeleteBehavior.Restrict);
modelBuilder.Entity<Item>()
.HasOne(i => i.ItemCode)
.WithMany()
.HasForeignKey(i => i.ItemCodeId)
.OnDelete(DeleteBehavior.Restrict);
modelBuilder.Entity<ItemCode>()
.HasOne(i => i.ItemCategory)
.WithMany()
.HasForeignKey(i => i.ItemCategoryId)
.OnDelete(DeleteBehavior.Restrict);
modelBuilder.Entity<InventTransDetail>(e =>
{
e.HasOne(i => i.PRDetails)
.WithMany()
.HasForeignKey(t => t.PRDetailId)
.OnDelete(DeleteBehavior.Restrict);
e.HasOne(r => r.RRDetails)
.WithMany()
.HasForeignKey(r => r.RRDetailId)
.IsRequired(false)
.HasForeignKey(r => r.RRDetailId)
.OnDelete(DeleteBehavior.Restrict);
});
modelBuilder.Entity<PRDetails>(e =>
{
e.HasOne(i => i.PRs)
.WithMany()
.HasForeignKey(t => t.PRId)
.OnDelete(DeleteBehavior.Restrict);
});
modelBuilder.Entity<RIS>(e => {
e.HasOne(r => r.Inventory)
.WithMany()
.HasForeignKey(r => r.InventoryId)
.OnDelete(DeleteBehavior.Restrict);
e.HasOne(r => r.Discipline)
.WithMany()
.HasForeignKey(r => r.DisciplineId);
e.HasOne(r => r.ProjectCodes)
.WithMany()
.HasForeignKey(r => r.ProjectCodeId);
});
modelBuilder.Entity<MRS>(e => {
e.HasOne(m => m.RIS)
.WithMany(r => r.MaterialReturns)
.HasForeignKey(m => m.RISId)
.OnDelete(DeleteBehavior.Restrict);
});
} }
} }
} }

View File

@ -3,6 +3,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using System.Linq; using System.Linq;
using System.Security.Claims;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -20,6 +21,7 @@ namespace CPRNIMS.Infrastructure.Dto.Account
public string URLAttachment { get; set; } = string.Empty; public string URLAttachment { get; set; } = string.Empty;
public string? token { get; set; } public string? token { get; set; }
public string? company { get; set; } public string? company { get; set; }
public int? departmentId { get; set; }
public string? refreshToken { get; set; } public string? refreshToken { get; set; }
public DateTime expiresAt { get; set; } public DateTime expiresAt { get; set; }
public int expiresInSeconds { get; set; } public int expiresInSeconds { get; set; }

View File

@ -13,5 +13,6 @@ namespace CPRNIMS.Infrastructure.Dto.Account
public string FullName { get; init; } = default!; public string FullName { get; init; } = default!;
public string Company { get; init; } = default!; public string Company { get; init; } = default!;
public IReadOnlyList<string> Roles { get; init; } = []; public IReadOnlyList<string> Roles { get; init; } = [];
public int? DepartmentId { get; set; }
} }
} }

View File

@ -1,4 +1,5 @@
using CPRNIMS.Infrastructure.Entities.Common; using CPRNIMS.Infrastructure.Dto.Canvass.Request;
using CPRNIMS.Infrastructure.Entities.Common;
using CPRNIMS.Infrastructure.ViewModel.Items; using CPRNIMS.Infrastructure.ViewModel.Items;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
@ -9,7 +10,7 @@ using System.Threading.Tasks;
namespace CPRNIMS.Infrastructure.Dto.Canvass namespace CPRNIMS.Infrastructure.Dto.Canvass
{ {
public class CanvassDto : CommonProperties public class CanvassDto : CanvassRequestSearch
{ {
public byte PaymentTermsId { get; set; } = 0; public byte PaymentTermsId { get; set; } = 0;
public bool IsArchived { get; set; } = false; public bool IsArchived { get; set; } = false;
@ -56,7 +57,7 @@ namespace CPRNIMS.Infrastructure.Dto.Canvass
public long CanvassDetailId { get; set; } public long CanvassDetailId { get; set; }
public long CanvassId { get; set; } public long CanvassId { get; set; }
public decimal QtyRequest { get; set; } public decimal QtyRequest { get; set; }
//public byte IsApproved { get; set; } public bool IsInternational { get; set; }
public long RRDetailId { get; set; } public long RRDetailId { get; set; }
public long PRDetailId { get; set; } public long PRDetailId { get; set; }
public decimal Quantity { get; set; } public decimal Quantity { get; set; }
@ -74,6 +75,7 @@ namespace CPRNIMS.Infrastructure.Dto.Canvass
public bool IsDenied { get; set; } = false; public bool IsDenied { get; set; } = false;
public string? FullName { get; set; } public string? FullName { get; set; }
public string? Specification { get; set; } public string? Specification { get; set; }
public byte CurrencyId { get; set; } = 1; public byte CurrencyId { get; set; } = 1;
public bool VatInc { get; set; }=false; public bool VatInc { get; set; }=false;
public string? TinNo { get; set; } public string? TinNo { get; set; }

View File

@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CPRNIMS.Infrastructure.Dto.Canvass
{
public class CanvassItemDto
{
public long PRDetailsId { get; set; }
public long PRNo { get; set; }
public long ItemNo { get; set; }
public string ItemDescription { get; set; } = string.Empty;
public string ItemName { get; set; } = string.Empty;
}
}

View File

@ -0,0 +1,22 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CPRNIMS.Infrastructure.Dto.Canvass
{
public class ForCanvassDto
{
public short Status {get;set;}
public string UserId { get; set; } = string.Empty;
public int SupplierId { get; set; }
public long PRNo { get; set; }
public long ItemNo { get; set; }
public int CanvassNo { get; set; }
public long PRDetailsId { get; set; }
public string FullName { get; set; } = string.Empty;
public string? Remarks { get; set; }
}
}

View File

@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using CPRNIMS.Infrastructure.Dto.Common;
namespace CPRNIMS.Infrastructure.Dto.Canvass.Request
{
public class CanvassRequestSearch : PagedRequest
{
public string SearchPRNo { get; set; } = string.Empty;
public string SearchItemName { get; set; } = string.Empty;
public string SearchItemNo { get; set; } = string.Empty;
public string SearchCreatedBy { get; set; } = string.Empty;
public string SearchSupplier { get; set; } = string.Empty;
public string SearchDepartment { get; set; } = string.Empty;
}
}

View File

@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CPRNIMS.Infrastructure.Dto.Canvass.Request
{
public class ForCanvassRequest
{
public string UserId { get; set; } = string.Empty;
public int SupplierId { get; set; }
public long PRNo { get; set; }
public long ItemNo { get; set; }
public int CanvassNo { get; set; }
public long PRDetailsId { get; set; }
}
}

View File

@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
namespace CPRNIMS.Infrastructure.Dto.Canvass.Request
{
public class StartCanvassRequest
{
[JsonPropertyName("isInternational")]
public bool IsInternational { get; set; }
[JsonPropertyName("items")]
public List<CanvassItemDto> Items { get; set; } = new();
}
}

View File

@ -21,5 +21,6 @@ namespace CPRNIMS.Infrastructure.Dto.Canvass.Request
public string Address { get; set; } = string.Empty; public string Address { get; set; } = string.Empty;
public long ItemNo { get; set; } = 0; public long ItemNo { get; set; } = 0;
public string Website { get; set; } = string.Empty; public string Website { get; set; } = string.Empty;
public int SupplierId { get; set; }
} }
} }

View File

@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CPRNIMS.Infrastructure.Dto.Canvass.Response
{
public class StartCanvassResponse
{
public Guid Id { get; set; }
public bool IsInternational { get; set; }
public string UserId { get; set; } = string.Empty;
public List<CanvassItemDto> Items { get; set; } = new();
}
}

View File

@ -10,18 +10,19 @@ namespace CPRNIMS.Infrastructure.Dto.Canvass.Response
{ {
public int SupplierId { get; set; } public int SupplierId { get; set; }
public string SupplierName { get; set; } = string.Empty; public string SupplierName { get; set; } = string.Empty;
public string EmailAddress { get; set; } = string.Empty; public string? EmailAddress { get; set; }
public bool IsActive { get; set; } = true; public bool IsActive { get; set; }
public string ContactNo { get; set; } = string.Empty; public string? ContactNo { get; set; }
public string ContactPerson { get; set; } = string.Empty; public string? ContactPerson { get; set; }
public string LeadTime { get; set; } = string.Empty; public string? LeadTime { get; set; }
public bool VatInc { get; set; } = false; public bool? IsVatable { get; set; }
public byte? PaymentTermsId { get; set; }
public byte? CurrencyId { get; set; }
public string? TinNo { get; set; }
public string? Address { get; set; }
public string? Website { get; set; }
public bool VatInc { get; set; }
public string Currency { get; set; }=string.Empty; public string Currency { get; set; }=string.Empty;
public string PaymentTerms { get; set; } = string.Empty; public string PaymentTerms { get; set; } = string.Empty;
public byte PaymentTermsId { get; set; } = 1;
public byte CurrencyId { get; set; } = 1;
public string TinNo { get; set; } = string.Empty;
public string Address { get; set; } = string.Empty;
public string Website { get; set; } = string.Empty;
} }
} }

View File

@ -0,0 +1,27 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CPRNIMS.Infrastructure.Dto.Canvass.Response
{
public class SupplierResponseDto
{
[Key]
public int SupplierId { get; set; }
public string? SupplierName { get; set; }
public string? EmailAddress { get; set; }
public string? ContactNo { get; set; }
public string? ContactPerson { get; set; }
public string? LeadTime { get; set; }
public bool VatInc { get; set; }
public string? Currency { get; set; }
public string? PaymentTerms { get; set; }
public byte PaymentTermsId { get; set; }
public byte CurrencyId { get; set; }
public string? TinNo { get; set; }
public bool IsActive { get; set; }
}
}

View File

@ -1,9 +1,11 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Text.Json; using System.Text.Json;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
using System.Text.RegularExpressions;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace CPRNIMS.Infrastructure.Dto.Canvass.Result namespace CPRNIMS.Infrastructure.Dto.Canvass.Result
@ -28,12 +30,43 @@ namespace CPRNIMS.Infrastructure.Dto.Canvass.Result
[JsonPropertyName("source")] [JsonPropertyName("source")]
public string? Source { get; set; } public string? Source { get; set; }
[JsonPropertyName("estimated_price_usd")] [JsonConverter(typeof(FlexibleDecimalConverter))]
public decimal? EstimatedPriceUsd { get; set; } public decimal? EstimatedPriceUsd { get; set; }
[JsonPropertyName("item_specifications")] [JsonPropertyName("item_specifications")]
public JsonElement? ItemSpecifications { get; set; } public JsonElement? ItemSpecifications { get; set; }
} }
public class FlexibleDecimalConverter : JsonConverter<decimal?>
{
public override decimal? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
switch (reader.TokenType)
{
case JsonTokenType.Number:
return reader.TryGetDecimal(out var num) ? num : null;
case JsonTokenType.String:
var str = reader.GetString()?.Trim();
if (string.IsNullOrWhiteSpace(str)) return null;
// Strip currency symbols and commas e.g. "$1,200.00" → 1200.00
str = Regex.Replace(str, @"[^\d.]", "");
return decimal.TryParse(str, NumberStyles.Any, CultureInfo.InvariantCulture, out var parsed)
? parsed
: null; // "contact for pricing", "varies", etc. → null
case JsonTokenType.Null:
default:
return null;
}
}
public override void Write(Utf8JsonWriter writer, decimal? value, JsonSerializerOptions options)
{
if (value.HasValue) writer.WriteNumberValue(value.Value);
else writer.WriteNullValue();
}
}
public class TavilySearchResult public class TavilySearchResult
{ {
public List<TavilyResult> Results { get; set; } = new(); public List<TavilyResult> Results { get; set; } = new();
@ -46,7 +79,11 @@ namespace CPRNIMS.Infrastructure.Dto.Canvass.Result
public string Content { get; set; } = string.Empty; public string Content { get; set; } = string.Empty;
public double Score { get; set; } public double Score { get; set; }
} }
public class GroqMatchResult
{
public bool Matched { get; set; }
public int? SupplierId { get; set; }
}
public class GroqResponse public class GroqResponse
{ {
public List<GroqChoice> Choices { get; set; } = new(); public List<GroqChoice> Choices { get; set; } = new();

View File

@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CPRNIMS.Infrastructure.Dto.Common
{
public class ApiResponse<T>
{
public bool success { get; set; }
public string? message { get; set; }
public T? data { get; set; }
}
}

View File

@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CPRNIMS.Infrastructure.Dto.Common
{
public class PagedRequest
{
public int PageNumber { get; set; }
public int PageSize { get; set; }
}
}

View File

@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CPRNIMS.Infrastructure.Dto.Common
{
public class PagedResult<T>
{
public List<T> Data { get; set; } = new List<T>();
public int TotalCount { get; set; }
public int PageNumber { get; set; }
public int PageSize { get; set; }
public List<string> ClientList { get; set; } = new List<string>();
public List<string> SupplierList { get; set; } = new List<string>();
public List<string> CategoryList { get; set; } = new List<string>();
public List<string> DepartmentList { get; set; } = new List<string>();
}
}

View File

@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CPRNIMS.Infrastructure.Dto.Inventory
{
public class DisciplineDto
{
public byte DisciplineId { get; set; }
public string DisciplineName { get; set; } = string.Empty;
}
public class ProjectCodeDto
{
public string ProjectCode { get; set; } = string.Empty;
public string ProjectName { get; set; } = string.Empty;
public int ProjectCodeId { get; set; }
}
}

View File

@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CPRNIMS.Infrastructure.Dto.Inventory
{
public class MRSFilterDto
{
public int PageNumber { get; set; } = 1;
public int PageSize { get; set; } = 12;
public string? SearchMRSNo { get; set; }
public long? RISId { get; set; }
public short? Status { get; set; }
public DateTime? DateFrom { get; set; }
public DateTime? DateTo { get; set; }
}
}

View File

@ -0,0 +1,30 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
namespace CPRNIMS.Infrastructure.Dto.Inventory
{
public class MRSPagedDto
{
public long MRSId { get; set; }
public string MRSNo { get; set; } = string.Empty;
public long RISId { get; set; }
public string? RISNo { get; set; }
public string? ItemName { get; set; }
public string? ReturnedBy { get; set; }
public decimal QtyReturned { get; set; }
public string? Condition { get; set; }
public string? Remarks { get; set; }
public short Status { get; set; }
public string? StatusLabel { get; set; }
public string? CreatedBy { get; set; }
public DateTime CreatedDate { get; set; }
public string? ApprovedBy { get; set; }
public DateTime? ApprovedDate { get; set; }
public int InventoryId { get; set; }
public List<MRSPagedDto> Data { get; set; } = [];
}
}

View File

@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CPRNIMS.Infrastructure.Dto.Inventory
{
public class MRSPagedRequest
{
public string? SearchMRSNo { get; set; }
public string? SearchRISNo { get; set; }
public string? SearchItemName { get; set; }
public string? SearchReturnedBy { get; set; }
public short? Status { get; set; }
public string? Condition { get; set; }
public int PageNumber { get; set; } = 1;
public int PageSize { get; set; } = 12;
}
}

View File

@ -0,0 +1,17 @@
using CPRNIMS.Infrastructure.Dto.Inventory.Response;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CPRNIMS.Infrastructure.Dto.Inventory
{
public class MRSPagedResult
{
public IEnumerable<MRSPagedDto> Data { get; set; } = [];
public int RecordsTotal { get; set; }
public List<string> DepartmentList { get; set; } = new List<string>();
public List<string> DisciplineList { get; set; } = new List<string>();
}
}

View File

@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CPRNIMS.Infrastructure.Dto.Inventory
{
public class PagedResult<T>
{
public List<T> Data { get; set; } = new List<T>();
public int TotalCount { get; set; }
public int PageNumber { get; set; }
public int PageSize { get; set; }
public List<string> DepartmentList { get; set; } = new List<string>();
public List<string> CategoryList { get; set; } = new List<string>();
public bool IsSuccess { get; set; } = true;
public string? ErrorMessage { get; set; }
}
}

View File

@ -0,0 +1,21 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CPRNIMS.Infrastructure.Dto.Inventory
{
public class RISFilterDto
{
public string? SearchRISNo { get; set; }
public string? SearchItemName { get; set; }
public string? SearchIssuedTo { get; set; }
public string? Discipline { get; set; }
public short? Status { get; set; }
public int PageNumber { get; set; } = 1;
public int PageSize { get; set; } = 12;
public DateTime? DateFrom { get; set; }
public DateTime? DateTo { get; set; }
}
}

View File

@ -0,0 +1,17 @@
using CPRNIMS.Infrastructure.Dto.Inventory.Response;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CPRNIMS.Infrastructure.Dto.Inventory
{
public class RISPagedResult
{
public IEnumerable<RISResponse> Data { get; set; } = [];
public int RecordsTotal { get; set; }
public IEnumerable<string> DepartmentList { get; set; } = [];
public IEnumerable<DisciplineDto> DisciplineList { get; set; } = [];
}
}

View File

@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CPRNIMS.Infrastructure.Dto.Inventory
{
public class RISReferenceDto
{
public long RISId { get; set; }
public string RISNo { get; set; } = string.Empty;
public decimal QtyIssued { get; set; }
public decimal TotalReturned { get; set; }
public decimal QtyAvailableToReturn => QtyIssued - TotalReturned;
public string DisciplineName { get; set; } = string.Empty;
public DateTime CreatedDate { get; set; }
}
}

View File

@ -0,0 +1,26 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CPRNIMS.Infrastructure.Dto.Inventory
{
public class RISSearchResultDto
{
public long RISId { get; set; }
public string RISNo { get; set; } = string.Empty;
public int ProjectCodeId { get; set; }
public string ProjectCode { get; set; } = string.Empty;
public string ProjectName { get; set; } = string.Empty;
public string DisciplineName { get; set; } = string.Empty;
public decimal QtyAvailableToReturn { get; set; }
}
public class ProjectCodeOptionDto
{
public int ProjectCodeId { get; set; }
public string ProjectCode { get; set; } = string.Empty;
public string ProjectName { get; set; } = string.Empty;
}
}

View File

@ -0,0 +1,50 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CPRNIMS.Infrastructure.Dto.Inventory.Reports
{
public class RISReportDataDto
{
public List<RISRowDto> Rows { get; set; } = new();
public List<DisciplineAggDto> Disciplines { get; set; } = new();
public List<TopRecipientDto> Recipients { get; set; } = new();
}
public class RISRowDto
{
public string? RISNo { get; set; }
public long? PRNo { get; set; }
public decimal QtyIssued { get; set; }
public string? IssuedTo { get; set; }
public string? StatusLabel { get; set; }
public string? CreatedBy { get; set; }
public string? ApprovedBy { get; set; }
public DateTime? ApprovedDate { get; set; }
public DateTime? CreatedDate { get; set; }
public string? DisciplineName { get; set; }
public string? ItemName { get; set; }
public long? ItemNo { get; set; }
public decimal QtyIn { get; set; }
public decimal QtyOut { get; set; }
public decimal QtyOnHand { get; set; }
public string? DepartmentName { get; set; }
public decimal TotalReturned { get; set; }
public int MRSCount { get; set; }
public decimal NetIssued { get; set; }
}
public class DisciplineAggDto
{
public string? DisciplineName { get; set; }
public int SlipCount { get; set; }
}
public class TopRecipientDto
{
public string? Name { get; set; }
public int SlipCount { get; set; }
public decimal QtyOut { get; set; }
}
}

View File

@ -0,0 +1,160 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CPRNIMS.Infrastructure.Dto.Inventory.Reports
{
public class RISReportDto
{
public string CompanyName { get; set; } = "Lloyd Laboratories Incorporated";
public string PreparedBy { get; set; } = "Finance Department";
public string ReportNo { get; set; } = string.Empty;
public DateTime DateFrom { get; set; }
public DateTime DateTo { get; set; }
public RISReportSummary Summary { get; set; } = new();
public List<RISReportRow> Rows { get; set; } = [];
public List<DisciplineCount> ByDiscipline { get; set; } = [];
public List<TopRecipient> TopRecipients { get; set; } = [];
}
public class RISReportSummary
{
public int TotalRIS { get; set; }
public int TotalApproved { get; set; }
public int TotalPending { get; set; }
public int TotalCancelled { get; set; }
public decimal TotalQtyIssued { get; set; }
public decimal TotalQtyReturned { get; set; }
public decimal TotalNetIssued { get; set; }
public decimal ApprovalRatePct { get; set; }
}
public class RISReportRow
{
public string RISNo { get; set; } = string.Empty;
public DateTime CreatedDate { get; set; }
public string ItemName { get; set; } = string.Empty;
public long ItemNo { get; set; }
public string DisciplineName { get; set; } = string.Empty;
public string ProjectName { get; set; } = string.Empty;
public decimal QtyIssued { get; set; }
public decimal TotalReturned { get; set; }
public decimal NetIssued { get; set; }
public short Status { get; set; }
public string StatusLabel { get; set; } = string.Empty;
}
public class DisciplineCount
{
public string DisciplineName { get; set; } = string.Empty;
public int Count { get; set; }
}
public class TopRecipient
{
public string IssuedTo { get; set; } = string.Empty;
public int SlipCount { get; set; }
public decimal TotalQty { get; set; }
}
public class MRSReportDto
{
public string CompanyName { get; set; } = "Lloyd Laboratories Incorporated";
public string PreparedBy { get; set; } = "Finance Department";
public string ReportNo { get; set; } = string.Empty;
public DateTime DateFrom { get; set; }
public DateTime DateTo { get; set; }
public MRSReportSummary Summary { get; set; } = new();
public List<MRSReportRow> Rows { get; set; } = [];
public List<ConditionTotal> ByCondition { get; set; } = [];
}
public class MRSReportSummary
{
public int TotalMRS { get; set; }
public decimal TotalQtyReturned { get; set; }
public decimal TotalQtyIssuedRIS { get; set; }
public decimal NetQtyConsumed { get; set; }
public decimal ReturnRatePct { get; set; }
public decimal GoodConditionPct { get; set; }
}
public class MRSReportRow
{
public string MRSNo { get; set; } = string.Empty;
public DateTime CreatedDate { get; set; }
public string RISNo { get; set; } = string.Empty;
public string ItemName { get; set; } = string.Empty;
public string ReturnedBy { get; set; } = string.Empty;
public decimal QtyReturned { get; set; }
public string Condition { get; set; } = string.Empty;
public short Status { get; set; }
public string StatusLabel { get; set; } = string.Empty;
}
public class ConditionTotal
{
public string Condition { get; set; } = string.Empty;
public decimal TotalQty { get; set; }
}
public class InventoryReportDto
{
public string CompanyName { get; set; } = "Lloyd Laboratories Incorporated";
public string PreparedBy { get; set; } = "Finance Department";
public string ReportNo { get; set; } = string.Empty;
public DateTime AsOf { get; set; }
public List<string> Departments { get; set; } = new();
public int Page { get; set; }
public int PageSize { get; set; }
public int TotalRows { get; set; }
public int TotalPages => PageSize > 0 ? (int)Math.Ceiling(TotalRows / (double)PageSize) : 0;
public InventoryReportSummary Summary { get; set; } = new();
public List<InventoryReportRow> Rows { get; set; } = [];
public List<CategoryStockLevel> ByCategory { get; set; } = [];
public List<InventoryAlert> Alerts { get; set; } = [];
}
public class InventoryReportSummary
{
public int TotalSKUs { get; set; }
public decimal TotalOnHand { get; set; }
public decimal TotalQtyIn { get; set; }
public decimal TotalQtyOut { get; set; }
public decimal TotalValue { get; set; }
public int LowStockCount { get; set; }
public int OutOfStockCount { get; set; }
}
public class InventoryReportRow
{
public string ItemName { get; set; } = string.Empty;
public long ItemNo { get; set; }
public string ItemCategoryName { get; set; } = string.Empty;
public string? LotNo { get; set; }
public decimal QtyIn { get; set; }
public decimal QtyOut { get; set; }
public decimal QtyOnHand { get; set; }
public decimal UnitPrice { get; set; }
public int StockPct { get; set; }
public string? Department { get; set; }
public string? CurrencyCode { get; set; }
}
public class CategoryStockLevel
{
public string CategoryName { get; set; } = string.Empty;
public int AvgStockPct { get; set; }
}
public class InventoryAlert
{
public string ItemName { get; set; } = string.Empty;
public decimal QtyOnHand { get; set; }
public string Severity { get; set; } = string.Empty; // "Critical" | "Low"
}
}

View File

@ -0,0 +1,14 @@
using CPRNIMS.Infrastructure.Dto.Inventory.Response;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CPRNIMS.Infrastructure.Dto.Inventory.Reports.Response
{
public class InventoryData
{
public List<InventoryReportDto> Data { get; set; } = [];
}
}

View File

@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CPRNIMS.Infrastructure.Dto.Inventory.Reports.Response
{
public class MRSData
{
public List<MRSReportDto> Data { get; set; } = [];
}
}

View File

@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CPRNIMS.Infrastructure.Dto.Inventory.Reports.Response
{
public class RISData
{
public List<RISReportDto> Data { get; set; } = [];
}
}

View File

@ -0,0 +1,10 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CPRNIMS.Infrastructure.Dto.Inventory.Request
{
public class ApproveMRSRequest { public long MRSId { get; set; } }
}

View File

@ -4,10 +4,10 @@ using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace CPRNIMS.Infrastructure.ViewModel.Canvass namespace CPRNIMS.Infrastructure.Dto.Inventory.Request
{ {
public class XCheckItemVM public class ApproveRISRequest
{ {
public List<long>? ItemNo { get; set; } public long RISId { get; set; }
} }
} }

View File

@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CPRNIMS.Infrastructure.Dto.Inventory.Request
{
public class CancelMRSRequest
{
public long MRSId { get; set; }
public string Reason { get; set; } = string.Empty;
}
}

View File

@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CPRNIMS.Infrastructure.Dto.Inventory.Request
{
public class CancelRISRequest
{
public long RISId { get; set; }
public string Reason { get; set; } = string.Empty;
}
}

View File

@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CPRNIMS.Infrastructure.Dto.Inventory.Request
{
public class CreateMRSRequest
{
public long RISId { get; set; }
public decimal QtyReturned { get; set; }
public string ReturnedBy { get; set; } = string.Empty;
public string? Condition { get; set; }
public string? Remarks { get; set; }
}
}

View File

@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CPRNIMS.Infrastructure.Dto.Inventory.Request
{
public class CreateRISRequest
{
public int InventoryId { get; set; }
public long PRDetailId { get; set; }
public int ProjectCodeId { get; set; }
public byte DisciplineId { get; set; }
public decimal QtyIssued { get; set; }
public string? Remarks { get; set; }
}
}

View File

@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CPRNIMS.Infrastructure.Dto.Inventory.Request
{
public class InventoryReportsRequest
{
public DateTime DateFrom { get; set; }
public DateTime DateTo { get; set; }
public string? Department { get; set; }
public int Page { get; set; }
public int PageSize { get; set; }
public bool Paginate { get; set; }
}
}

View File

@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CPRNIMS.Infrastructure.Dto.Inventory.Request
{
public class InventoryRequest
{
public string? SearchPRNo { get; set; }
public string? SearchItemName { get; set; }
public string? SearchDept { get; set; }
public int PageNumber { get; set; } = 1;
public int PageSize { get; set; } = 10;
public byte MessCode { get; set; }
public string? SearchProjectCode { get; set; }
public string? SearchItemNo { get; set; }
public string? UserId { get; set; }
public int InventoryId { get; set; }
}
}

View File

@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CPRNIMS.Infrastructure.Dto.Inventory.Request
{
public class RISPagedRequest
{
public string? SearchRISNo { get; set; }
public string? SearchItemName { get; set; }
public string? SearchIssuedTo { get; set; }
public string? Discipline { get; set; }
public short? Status { get; set; }
public int PageNumber { get; set; } = 1;
public int PageSize { get; set; } = 12;
}
}

View File

@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CPRNIMS.Infrastructure.Dto.Inventory.Request
{
public class SearchRISProjectCodeRequest
{
public int? SearchProjectCodeId { get; set; }
public string? SearchRISNo { get; set; }
public string? SearchProjectCode { get; set; }
}
}

View File

@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CPRNIMS.Infrastructure.Dto.Inventory.Response
{
public class InventoryByIdResponse
{
[Key]
public int InventoryId { get; set; }
public decimal QtyIn { get; set; }
public decimal QtyOut { get; set; }
public decimal QtyOnHand { get; set; }
public string? LotNo { get; set; }
public string? ProjectCode { get; set; }
public string? UserId { get; set; }
public string? ItemName { get; set; }
public string? ItemDescription { get; set; }
public string? ItemCategoryName { get; set; }
public string? Department { get; set; }
public DateTime CreatedDate { get; set; }
public long ItemNo { get; set; }
public long PRNo { get; set; }
}
}

View File

@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CPRNIMS.Infrastructure.Dto.Inventory.Response
{
public class InventoryResponse
{
[Key]
public int InventoryId { get; set; }
public decimal QtyIn { get; set; }
public decimal QtyOut { get; set; }
public decimal QtyOnHand { get; set; }
public string? LotNo { get; set; }
public string? ProjectCode { get; set; }
public string? UserId { get; set; }
public string? ItemName { get; set; }
public string? ItemDescription { get; set; }
public string? ItemCategoryName { get; set; }
public string? Department { get; set; }
public DateTime CreatedDate { get; set; }
public long ItemNo { get; set; }
public long PRNo { get; set; }
}
}

View File

@ -0,0 +1,34 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
namespace CPRNIMS.Infrastructure.Dto.Inventory.Response
{
public class MRSPagedResponse
{
public List<MRSListItem> Data { get; set; } = [];
public int RecordsTotal { get; set; }
}
public class MRSListItem
{
public long MRSId { get; set; }
public string MRSNo { get; set; } = string.Empty;
public long RISId { get; set; }
public string? RISNo { get; set; }
public string? ItemName { get; set; }
public string? ReturnedBy { get; set; }
public decimal QtyReturned { get; set; }
public string? Condition { get; set; }
public string? Remarks { get; set; }
public short Status { get; set; }
public string? StatusLabel { get; set; }
public string? CreatedBy { get; set; }
public DateTime CreatedDate { get; set; }
public string? ApprovedBy { get; set; }
public DateTime? ApprovedDate { get; set; }
public int InventoryId { get; set; }
}
}

View File

@ -0,0 +1,17 @@
using CPRNIMS.Infrastructure.Models.Inventory;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
namespace CPRNIMS.Infrastructure.Dto.Inventory.Response
{
public class MRSResponse
{
[JsonPropertyName("success")] public bool Success { get; set; }
[JsonPropertyName("message")] public string? Message { get; set; }
[JsonPropertyName("data")] public MRSData? Data { get; set; }
}
}

View File

@ -0,0 +1,58 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CPRNIMS.Infrastructure.Dto.Inventory.Response
{
public class RISPagedResponse
{
public List<RISListItem> Data { get; set; } = [];
public int RecordsTotal { get; set; }
public List<string> DepartmentList { get; set; } = [];
public List<DisciplineItem> DisciplineList { get; set; } = [];
}
public class RISListItem
{
public long RISId { get; set; }
public string RISNo { get; set; } = string.Empty;
public int InventoryId { get; set; }
public string? ItemName { get; set; }
public long ItemNo { get; set; }
public string? ProjectCode { get; set; }
public string? ProjectName { get; set; }
public string? DisciplineName { get; set; }
public decimal QtyIssued { get; set; }
public decimal TotalReturned { get; set; }
public string? Remarks { get; set; }
public short Status { get; set; }
public string? StatusLabel { get; set; }
public string? CreatedBy { get; set; }
public DateTime CreatedDate { get; set; }
public string? ApprovedBy { get; set; }
public DateTime? ApprovedDate { get; set; }
public decimal MRSCount { get; set; }
}
public class DisciplineItem
{
public byte DisciplineId { get; set; }
public string DisciplineName { get; set; } = string.Empty;
}
}

View File

@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CPRNIMS.Infrastructure.Dto.Inventory.Response
{
public class RISProjectCodeResponse
{
public string ProjectCode { get; set; }=string.Empty;
public string ProjectName { get; set; } = string.Empty;
}
}

View File

@ -0,0 +1,34 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CPRNIMS.Infrastructure.Dto.Inventory.Response
{
public class RISResponse
{
public long RISId { get; set; }
public string RISNo { get; set; } = string.Empty;
public int InventoryId { get; set; }
public string ItemName { get; set; } = string.Empty;
public long ItemNo { get; set; }
public string? LotNo { get; set; }
public int ProjectCodeId { get; set; }
public string DisciplineName { get; set; } = string.Empty;
public string? Message { get; set; }
public byte DisciplineId { get; set; }
public decimal QtyIssued { get; set; }
public string? Remarks { get; set; }
public short Status { get; set; }
public string StatusLabel => Status switch { 0 => "Draft", 1 => "Approved", 2 => "Cancelled", _ => "Unknown" };
public string CreatedBy { get; set; } = string.Empty;
public DateTime CreatedDate { get; set; }
public string? ApprovedBy { get; set; }
public DateTime? ApprovedDate { get; set; }
public decimal MRSCount { get; set; }
public decimal TotalReturned { get; set; }
public string? ProjectName { get; set; }
public string? ProjectCode { get; set; }
}
}

View File

@ -0,0 +1,30 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CPRNIMS.Infrastructure.Dto.Inventory.Response
{
public class SearchApiResponse<T>
{
public bool Success { get; set; }
public List<T> data { get; set; } = new List<T>();
}
public class SearchRISProjectCodeResponse
{
public long RISId { get; set; }
public string RISNo { get; set; } = string.Empty;
public int ProjectCodeId { get; set; }
public string ProjectCode { get; set; } = string.Empty;
public string ProjectName { get; set; } = string.Empty;
public string DisciplineName { get; set; } = string.Empty;
public decimal QtyAvailableToReturn { get; set; }
}
public class ProjectCodeOptionResponse
{
public int ProjectCodeId { get; set; }
public string ProjectCode { get; set; } = string.Empty;
public string ProjectName { get; set; } = string.Empty;
}
}

View File

@ -0,0 +1,24 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CPRNIMS.Infrastructure.Dto.Inventory
{
public class TransactContextDto
{
public int InventoryId { get; set; }
public string ItemName { get; set; } = string.Empty;
public long ItemNo { get; set; }
public long PRNo { get; set; }
public string? LotNo { get; set; }
public string? Department { get; set; }
public decimal QtyOnHand { get; set; }
public decimal QtyIn { get; set; }
public decimal QtyOut { get; set; }
public IEnumerable<ProjectCodeDto> ProjectCodes { get; set; } = [];
public IEnumerable<DisciplineDto> Disciplines { get; set; } = [];
public IEnumerable<RISReferenceDto> OpenRISList { get; set; } = [];
}
}

View File

@ -23,7 +23,7 @@ namespace CPRNIMS.Infrastructure.Dto.PO
public string? SupplierName { get; set; } public string? SupplierName { get; set; }
public string? Manufacturer { get; set; } public string? Manufacturer { get; set; }
public int ItemCount { get; set; } public int ItemCount { get; set; }
public short CurrencyId { get; set; } public byte CurrencyId { get; set; }
public string? Department { get; set; } public string? Department { get; set; }
public string? ItemCategoryName { get; set; } public string? ItemCategoryName { get; set; }
public DateTime CommitmentDate { get; set; } public DateTime CommitmentDate { get; set; }
@ -115,5 +115,6 @@ namespace CPRNIMS.Infrastructure.Dto.PO
public DateTime From { get; set; } public DateTime From { get; set; }
public string? OtherChargesName { get; set; } public string? OtherChargesName { get; set; }
public string? IncotermsName { get; set; } public string? IncotermsName { get; set; }
public string? CountryOrigin { get; set; }
} }
} }

Some files were not shown because too many files have changed in this diff Show More