Compare commits

...

359 Commits

Author SHA1 Message Date
Kalana B. Thilakarathna c775146b64 10.
CI/CD - ERP Distribution / Build & Deploy Frontend (pull_request) Failing after 13m39s
CI/CD - ERP Distribution / Build & Deploy Backend (pull_request) Failing after 15m1s
Implementation
A partner, not just software
HexDive Technologies' local team handles setup, data migration, training and ongoing support — in Sinhala, Tamil and English. [confirm]

1
Discovery
Map your network, products and processes.

2
Configuration
Set up depots, pricing, schemes, routes and users.

3
Migration & training
Move your data and train every team.

4
Go-live & support
Dedicated account manager and local support desk.
2026-10-22 17:47:49 +05:30
DeepnaPooja 6e4bebb89b Merge pull request 'Feat notification' (#86) from feat-notification into dev
CI/CD - ERP Distribution / Build & Deploy Backend (pull_request) Failing after 12m33s
CI/CD - ERP Distribution / Build & Deploy Frontend (pull_request) Failing after 16m7s
Reviewed-on: #86
2026-10-22 17:40:06 +05:30
DeepnaPooja 932d09c135 feat: Add Payment Analytics page and integrate payment reports API 2026-10-22 17:33:06 +05:30
DeepnaPooja 455ce699ad feat: Implement Payment Report feature with repository, service, and controller 2026-10-22 17:32:28 +05:30
DeepnaPooja 7d7e9f1318 feat: Create ViewRolePage component and remove RoleViewSheet for role details display 2026-10-22 16:49:14 +05:30
DeepnaPooja 87e3112a9a feat: Implement free issue tracking and scheme discount handling in invoices and Shop POs 2026-10-22 16:39:00 +05:30
DeepnaPooja 801276559d feat: Enhance pricing and sales API with free goods and discount scheme support 2026-10-22 16:38:09 +05:30
DeepnaPooja b1bda1448e feat: Implement Free Issue functionality in Shop PO and Invoice management 2026-10-22 16:37:03 +05:30
DeepnaPooja 58247a1a0b feat: Add SchemeForm component for creating and editing schemes with validation. 2026-10-22 16:36:00 +05:30
DeepnaPooja 3a503abe6a feat: Add Free Issue Lines to ShopPOItems and InvoiceItems 2026-10-22 16:28:46 +05:30
DeepnaPooja 855eb44118 feat: enhance Shop PO handling with discount scheme support and free issue tracking 2026-10-22 16:27:14 +05:30
DeepnaPooja 33dc4adce2 feat: add promotion suggestions for Shop PO processing 2026-10-22 16:26:15 +05:30
DeepnaPooja 5b2b39b7bf feat: add preview totals endpoint and DTOs for invoice calculations 2026-10-22 16:24:09 +05:30
DeepnaPooja e13ec0f109 Merge pull request 'Fix dashboard stat card handling issue' (#84) from feat-notification into dev
CI/CD - ERP Distribution / Build & Deploy Backend (pull_request) Failing after 12m59s
CI/CD - ERP Distribution / Build & Deploy Frontend (pull_request) Failing after 16m29s
Reviewed-on: #84
2026-10-22 10:54:52 +05:30
DeepnaPooja 2c39b34c5f feat: add discount amount to invoice response and display in invoice view 2026-10-22 10:44:18 +05:30
DeepnaPooja 99832df134 feat: add interactive sales trend and region sales pie components to dashboard 2026-10-22 10:43:39 +05:30
DeepnaPooja 2be9d9b396 feat: add DiscountAmount property to Invoice model for discount calculations 2026-10-22 10:41:58 +05:30
DeepnaPooja a12e170205 Add DiscountAmount column to Invoices table 2026-10-22 10:41:20 +05:30
DeepnaPooja fdfca7cf5e feat: add HasLockedInvoiceAsync method to check invoice status and prevent scheme application 2026-10-22 10:40:43 +05:30
DeepnaPooja e84ce29064 feat: update SoldLines method to use InvoiceItem and improve sales calculations 2026-10-22 10:40:09 +05:30
DeepnaPooja 2be2928ad1 feat: add scheme discount calculation to invoice processing 2026-10-22 10:39:01 +05:30
DeepnaPooja 872fd42c4a feat: enhance dashboard report with receivables and discounts calculations 2026-10-22 10:37:56 +05:30
DeepnaPooja 39ad424d21 Merge pull request 'Invoice flow' (#82) from feat-notification into dev
CI/CD - ERP Distribution / Build & Deploy Frontend (pull_request) Failing after 12m58s
CI/CD - ERP Distribution / Build & Deploy Backend (pull_request) Failing after 14m20s
Reviewed-on: #82
2026-10-21 13:13:21 +05:30
DeepnaPooja d79c5e55d0 Update doc for Shop PO to Invoice operations flow 2026-10-21 13:06:06 +05:30
DeepnaPooja 50d3113c28 Update Po invoice flow.md file 2026-10-21 13:04:44 +05:30
DeepnaPooja 34d33571de feat: enhance invoice item updates 2026-10-21 13:01:23 +05:30
DeepnaPooja 0f575370d4 Add migrations 2026-10-21 13:00:30 +05:30
DeepnaPooja e4061fa94c feat: implement invoice formatting and document loading hooks 2026-10-21 12:55:29 +05:30
DeepnaPooja 5f8bc70174 feat: add standalone printable invoice page with paper size selection 2026-10-21 12:54:51 +05:30
DeepnaPooja f2d5c9f50a fix invoice action dialog and order view with editing and printing capabilities 2026-10-21 12:51:02 +05:30
DeepnaPooja 9e39981c9a Fix invoice editing 2026-10-21 12:49:02 +05:30
DeepnaPooja bd8b2d8b44 Replace toggle group with invoice configuration 2026-10-21 12:47:59 +05:30
DeepnaPooja f26557a66e Enhance invoice processing by adding items and versioning 2026-10-21 12:46:56 +05:30
DeepnaPooja 91959913a8 feat: add invoice versioning models and enums 2026-10-21 12:44:58 +05:30
DeepnaPooja 48f607d623 Invoice handling with versioning and item updates 2026-10-21 12:41:20 +05:30
DeepnaPooja 82be251878 Merge dev into feat-notification
CI/CD - ERP Distribution / Build & Deploy Backend (pull_request) Failing after 12m21s
CI/CD - ERP Distribution / Build & Deploy Frontend (pull_request) Failing after 16m5s
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-10-20 17:19:51 +05:30
DeepnaPooja 209ee7da2d fe integration for notification system with context, components, and API integration 2026-10-20 16:53:15 +05:30
DeepnaPooja 6b9f27573b Introduced NotificationService for managing notification logic and recipient resolution.
- Integrated SignalR for real-time notification delivery to users.
2026-10-20 16:51:01 +05:30
DeepnaPooja a0df2a1bbf feat: integrate notification system across PO,store,supplier claims,delivery task and ride 2026-10-20 16:49:05 +05:30
DeepnaPooja d1c3d4e22f feat: implement notification system with repository, service, and controller layers 2026-10-20 16:45:48 +05:30
Kalana B. Thilakarathna a80512994b Add invoice creation mode support to Shop PO flow
CI/CD - ERP Distribution / Build & Deploy Backend (pull_request) Failing after 11m8s
CI/CD - ERP Distribution / Build & Deploy Frontend (pull_request) Failing after 14m56s
Introduce invoice creation mode feature using invoiceCreationModeApi and DEFAULT_INVOICE_CREATION_MODE. Update confirmation logic to call confirmAndCreateInvoice and show draft invoice messaging in ModeA. Update types and UI in page.tsx and confirm-shop-po-dialog.tsx for consistent behavior.
2026-10-20 14:02:11 +05:30
Kalana B. Thilakarathna 2de9e5e4ad Add manual invoice creation mode with new API & UI flow
CI/CD - ERP Distribution / Build & Deploy Frontend (pull_request) Failing after 13m13s
CI/CD - ERP Distribution / Build & Deploy Backend (pull_request) Failing after 14m28s
Introduced "ModeB" for manual invoice creation in system config. Added backend endpoints and service logic for creating invoices from confirmed Shop POs, with validation and error handling. Updated DTOs to support optional CreatedByUserId. On the frontend, added configuration card to toggle modes, a dialog for manual invoice creation, and UI updates to support the new workflow. Enables admins to choose between automatic and manual invoice creation.
2026-10-20 13:38:43 +05:30
Kalana B. Thilakarathna 5707bd4c31 Implement store credit tracking and enforcement
CI/CD - ERP Distribution / Build & Deploy Backend (pull_request) Failing after 12m30s
CI/CD - ERP Distribution / Build & Deploy Frontend (pull_request) Failing after 16m1s
Introduced StoreCreditMovement entity and migration to track store credit usage and settlements. Updated payment flow to enforce credit limits and record credit movements. Modified outstanding balance calculation to use credit movements. Enhanced frontend to display real-time credit info and prevent over-credit payments. Updated API types, seed data, and DI registrations accordingly.
2026-10-20 11:45:11 +05:30
Kalana B. Thilakarathna 28c465f59b feat: refactor TopLoader to use a hook for active state management and improve CSS for fixed positioning
CI/CD - ERP Distribution / Build & Deploy Backend (pull_request) Failing after 10m33s
CI/CD - ERP Distribution / Build & Deploy Frontend (pull_request) Failing after 14m21s
2026-10-19 23:37:45 +05:30
Kalana B. Thilakarathna 21b38b5276 feat: enhance DataTable with custom empty state support and update GrnClient to utilize it 2026-10-19 23:23:18 +05:30
Kalana B. Thilakarathna 992cd292fa feat: add EmptyState component for improved no-data display in supplier payments
CI/CD - ERP Distribution / Build & Deploy Backend (pull_request) Failing after 12m51s
CI/CD - ERP Distribution / Build & Deploy Frontend (pull_request) Failing after 16m39s
2026-10-19 22:35:01 +05:30
Kalana B. Thilakarathna 7b6c9ac92a feat: update row actions to include payment functionality and add corresponding icon
CI/CD - ERP Distribution / Build & Deploy Backend (pull_request) Failing after 10m33s
CI/CD - ERP Distribution / Build & Deploy Frontend (pull_request) Failing after 14m20s
2026-10-19 22:12:40 +05:30
Kalana B. Thilakarathna eb0b042f0c feat: integrate payment functionality with new payment sheet and enhance supplier payments UI
CI/CD - ERP Distribution / Build & Deploy Frontend (pull_request) Successful in 1m51s
CI/CD - ERP Distribution / Build & Deploy Backend (pull_request) Failing after 10m54s
2026-10-19 22:02:15 +05:30
Kalana B. Thilakarathna a980e636f5 Merge branch 'dev' of https://gitea.hexdive.com/New_REP_SYSTEM/ERP_Distribution into dev
CI/CD - ERP Distribution / Build & Deploy Backend (pull_request) Failing after 11m57s
CI/CD - ERP Distribution / Build & Deploy Frontend (pull_request) Failing after 15m40s
2026-10-19 21:36:08 +05:30
Kalana B. Thilakarathna b992d07689 feat: add supplier payment and allocation models, API, and frontend components
- Created `SupplierPayment` and `SupplierPaymentGRN` models for managing supplier payments and their allocations.
- Implemented API endpoints for listing and creating supplier payments.
- Developed frontend components for displaying and recording supplier payments, including a dialog for payment creation.
- Integrated GRN allocations into the payment creation process, ensuring total allocations match the payment amount.
- Added pagination and loading states for the payments list.
2026-10-19 21:36:01 +05:30
DeepnaPooja a4d58f3377 Merge pull request 'Fix changes' (#70) from fix-changes into dev
CI/CD - ERP Distribution / Build & Deploy Backend (pull_request) Failing after 11m33s
CI/CD - ERP Distribution / Build & Deploy Frontend (pull_request) Failing after 15m20s
Reviewed-on: #70
2026-10-19 16:45:55 +05:30
Kalana B. Thilakarathna 94db2a311c Refactor code structure for improved readability and maintainability 2026-10-19 16:42:12 +05:30
DeepnaPooja 6b608ad2ef Update permission feature with edit and create role functionality 2026-10-19 16:39:47 +05:30
Kalana B. Thilakarathna 87c807c16c feat: add cheque books and received cheques management
CI/CD - ERP Distribution / Build & Deploy Backend (pull_request) Failing after 11m8s
CI/CD - ERP Distribution / Build & Deploy Frontend (pull_request) Failing after 14m49s
- Implemented ChequeBooksPage for viewing and filtering cheque books.
- Added NewReceivedChequePage for recording received cheques.
- Created ReceivedChequesPage for listing and managing received cheques.
- Introduced API endpoints for bank accounts, cheque books, and received cheques.
- Defined types for payments including bank accounts, cheque books, and received cheques.
2026-10-19 16:09:05 +05:30
DeepnaPooja 3c75f9786e Add multi-series charts and enhance dashboard functionality 2026-10-19 15:51:39 +05:30
DeepnaPooja 2d6723a040 Update dashboard 2026-10-19 15:48:45 +05:30
kalana_Thilakarathna 2fe2ba007c Merge pull request 'fix: update login function to handle profile data correctly' (#68) from dev into production
Reviewed-on: #68
2026-10-19 10:25:57 +05:30
Kalana B. Thilakarathna f3f2b71bd4 fix: update login function to handle profile data correctly
CI/CD - ERP Distribution / Build & Deploy Backend (pull_request) Failing after 12m43s
CI/CD - ERP Distribution / Build & Deploy Frontend (pull_request) Failing after 16m30s
2026-10-19 10:25:29 +05:30
kalana_Thilakarathna bc72426495 Merge pull request 'auth fix' (#67) from dev into production
Reviewed-on: #67
2026-10-19 10:20:29 +05:30
Kalana B. Thilakarathna da29492798 auth fix
CI/CD - ERP Distribution / Build & Deploy Backend (pull_request) Failing after 13m10s
CI/CD - ERP Distribution / Build & Deploy Frontend (pull_request) Failing after 16m51s
2026-10-19 10:19:40 +05:30
kalana_Thilakarathna ce21aa6588 Merge pull request 'feat: add toast notifications for successful and failed login attempts' (#66) from dev into production
Reviewed-on: #66
2026-10-19 02:50:47 +05:30
Kalana B. Thilakarathna f44cbf711b feat: add toast notifications for successful and failed login attempts
CI/CD - ERP Distribution / Build & Deploy Backend (pull_request) Failing after 12m53s
CI/CD - ERP Distribution / Build & Deploy Frontend (pull_request) Failing after 16m41s
2026-10-19 02:50:06 +05:30
kalana_Thilakarathna aaac42b481 Merge pull request 'Dev' (#65) from dev into production
Reviewed-on: #65
2026-10-19 02:45:35 +05:30
Kalana B. Thilakarathna 7a21cde217 refactor: simplify user API calls by removing pageSize parameter in multiple components
CI/CD - ERP Distribution / Build & Deploy Frontend (pull_request) Failing after 11m52s
CI/CD - ERP Distribution / Build & Deploy Backend (pull_request) Failing after 13m5s
2026-10-19 02:44:19 +05:30
Kalana B. Thilakarathna 531b3a7fef Redesign login UI, add theme toggle, refactor user update
- Redesigned login page with two-panel layout and branding
- Added HexaDistro logo and theme toggle to sidebar/header
- Refactored user update: roles via update endpoint, removed assignRole API/controller
- Extended UpdateUserRequest for username, role, password changes
- UserService.UpdateAsync syncs changes with AuthHex, with rollback
- Stock balances client dedupes by itemId-locationId
- Middleware matcher excludes static assets from auth checks
- Improved localStorage clearing for user context
- Added lucide-react for icons
- Minor UI/code cleanups for consistency and UX
2026-10-19 02:20:14 +05:30
kalana_Thilakarathna a81e32bbe2 Merge pull request 'fe fix for loggin redirection' (#64) from dev into production
Reviewed-on: #64
2026-10-19 02:16:31 +05:30
Kalana B. Thilakarathna 4ef63420b7 fe fix for loggin redirection
CI/CD - ERP Distribution / Build & Deploy Backend (pull_request) Failing after 11m36s
CI/CD - ERP Distribution / Build & Deploy Frontend (pull_request) Failing after 15m19s
2026-10-18 23:38:28 +05:30
Kalana B. Thilakarathna 9fdf5aa8e3 Merge branch 'dev' into production 2026-10-18 23:23:12 +05:30
Kalana B. Thilakarathna d9c339949e conflict fixed 2026-10-18 23:22:04 +05:30
Kalana B. Thilakarathna 0052725b88 feat: add user and role view sheets with detailed information display
- Created UserViewSheet component to display user details including username, full name, role, status, and timestamps.
- Created RoleViewSheet component to display role details including name, level, status, creation date, and associated permissions.
- Introduced permissions configuration in a new permissions.ts file, defining categories and permission codes for better management.
- Implemented middleware for authentication, ensuring access control for protected routes.
2026-10-18 23:11:01 +05:30
DeepnaPooja 5d18aba1f9 Merge pull request 'Dev' (#63) from dev into production
Reviewed-on: #63
2026-10-16 10:53:09 +00:00
DeepnaPooja 5828879ddc Merge pull request 'chore: add exceljs, jspdf, and jspdf-autotable dependencies to package.json' (#62) from refactor/main_flow into dev
CI/CD - ERP Distribution / Build & Deploy Frontend (pull_request) Failing after 14m0s
CI/CD - ERP Distribution / Build & Deploy Backend (pull_request) Failing after 15m11s
Reviewed-on: #62
2026-10-16 10:52:30 +00:00
DeepnaPooja d75a43d45f chore: add exceljs, jspdf, and jspdf-autotable dependencies to package.json 2026-10-16 16:16:43 +05:30
DeepnaPooja 728f2a8b17 Merge pull request 'Dev' (#61) from dev into production
Reviewed-on: #61
2026-10-16 10:26:15 +00:00
DeepnaPooja 1b66d5d767 Merge pull request 'Refactor/main flow' (#60) from refactor/main_flow into dev
CI/CD - ERP Distribution / Build & Deploy Backend (pull_request) Failing after 12m3s
CI/CD - ERP Distribution / Build & Deploy Frontend (pull_request) Failing after 15m51s
Reviewed-on: #60
2026-10-16 10:25:08 +00:00
DeepnaPooja c5a0992b5d Fe changes 2026-10-16 15:47:23 +05:30
DeepnaPooja 09d86835a2 Implement Warehouse Dashboard features 2026-10-16 15:44:35 +05:30
DeepnaPooja d1d2d55352 Add stock report features r 2026-10-16 15:41:00 +05:30
DeepnaPooja c626f56cdb Refactor Return and Ride services to integrate StockMover and enhance error handling 2026-10-16 15:37:05 +05:30
DeepnaPooja 263e2ea9c0 Refactor Delivery and Purchase Return services to use StockMover for inventory movements 2026-10-16 15:34:25 +05:30
DeepnaPooja 5c37f02f19 Merge pull request 'Dev' (#59) from dev into production
Reviewed-on: #59
2026-10-09 04:38:19 +00:00
DeepnaPooja 6b3f28d3ae Merge pull request 'Refactor/main flow' (#58) from refactor/main_flow into dev
CI/CD - ERP Distribution / Build & Deploy Frontend (pull_request) Failing after 13m37s
CI/CD - ERP Distribution / Build & Deploy Backend (pull_request) Failing after 15m0s
Reviewed-on: #58
2026-10-09 04:37:42 +00:00
Kalana B. Thilakarathna 558afc6f9d auth layer implemented 2026-10-09 09:58:45 +05:30
Kalana B. Thilakarathna 5b2ac8cc14 Merge branch 'dev' of https://gitea.hexdive.com/New_REP_SYSTEM/ERP_Distribution into dev 2026-10-09 09:37:38 +05:30
Kalana B. Thilakarathna 5cb32bee95 Revert "auth layer implemented"
This reverts commit 9adfff48a7.
2026-10-09 09:37:28 +05:30
Kalana B. Thilakarathna 9adfff48a7 auth layer implemented 2026-10-09 09:37:05 +05:30
DeepnaPooja e5f6aa15b9 Implement Store analytics and api integrations 2026-10-08 16:32:23 +05:30
DeepnaPooja 4e12b03e4b Add analytics functionality for stores 2026-10-08 16:28:56 +05:30
DeepnaPooja f5967497be Merge pull request 'Dev' (#57) from dev into production
Reviewed-on: #57
2026-10-08 07:36:24 +00:00
DeepnaPooja 74f33d8979 Merge pull request 'Refactor/main flow' (#56) from refactor/main_flow into dev
CI/CD - ERP Distribution / Build & Deploy Frontend (pull_request) Successful in 1m42s
CI/CD - ERP Distribution / Build & Deploy Backend (pull_request) Failing after 11m55s
Reviewed-on: #56
2026-10-08 07:35:34 +00:00
DeepnaPooja dd8f50f54c Update docs for procurement and shop PO processes, including GRN rework functionality 2026-10-08 12:59:08 +05:30
DeepnaPooja 25b21d6fa8 Refactor GRN configuration page layout 2026-10-08 12:57:48 +05:30
DeepnaPooja 106ed57897 Implement GRN tax settings UI and calculations for VAT and NBT 2026-10-08 12:55:45 +05:30
DeepnaPooja 0451a0f418 Add documentation for GRN costing, taxes, and item tracking configuration 2026-10-08 12:54:38 +05:30
DeepnaPooja b12f3c8d08 Add NbtPercent column to GoodsReceipts and GoodsReceiptItems 2026-10-08 12:44:28 +05:30
DeepnaPooja 702efba62e Add GetNextSerialSequence functionality to Stock services and repository 2026-10-08 12:42:50 +05:30
DeepnaPooja 55aa35308a Add NBT support to GRN processing and pricing calculations 2026-10-08 12:41:44 +05:30
DeepnaPooja 40e890d4ff Fix GRN UI 2026-10-08 10:40:43 +05:30
DeepnaPooja 9908a552d1 Fix GRN pricing 2026-10-08 10:37:19 +05:30
DeepnaPooja 4110e2fcfc Merge pull request 'Dev' (#55) from dev into production
Reviewed-on: #55
2026-10-07 05:17:54 +00:00
DeepnaPooja 23fb7361a4 Merge pull request 'Refactor/main flow' (#54) from refactor/main_flow into dev
CI/CD - ERP Distribution / Build & Deploy Frontend (pull_request) Failing after 14m8s
CI/CD - ERP Distribution / Build & Deploy Backend (pull_request) Failing after 15m25s
Reviewed-on: #54
2026-10-07 05:16:58 +00:00
DeepnaPooja ff6f3d3218 Add product reports 2026-10-07 10:41:04 +05:30
DeepnaPooja 92a311e1cc Add region manager assignment endpoints 2026-10-07 10:39:17 +05:30
DeepnaPooja 3538bb5b0e Assign a region manager for the route 2026-10-06 15:40:43 +05:30
DeepnaPooja b75cd01e90 Add region code support 2026-10-06 15:15:04 +05:30
DeepnaPooja c52ecfdebc Add region code 2026-10-06 15:09:22 +05:30
DeepnaPooja edb5fddc3a Add item tracking and the configuration docs 2026-10-06 13:32:37 +05:30
DeepnaPooja 05d44ab9f0 check stock lookup methods 2026-10-06 13:31:19 +05:30
DeepnaPooja c358764f4e Add item tracking migrations 2026-10-06 13:30:12 +05:30
DeepnaPooja 92f1aadb81 Add item and GRN tracking forms 2026-10-06 13:28:37 +05:30
DeepnaPooja a8725bc473 Add GRN batch and serial tracking 2026-10-06 13:26:17 +05:30
DeepnaPooja d578cbad20 fix routing 2026-10-02 14:18:39 +05:30
DeepnaPooja e1db9b1b69 add shared card and toggle components 2026-10-02 13:19:02 +05:30
DeepnaPooja 9707ef596c UI changes 2026-10-02 13:17:32 +05:30
DeepnaPooja bfd3e5cded add product configuration UI 2026-10-02 13:07:45 +05:30
DeepnaPooja d28d996295 Add product configurations 2026-10-02 13:06:22 +05:30
DeepnaPooja 4e493f51f8 Add item tracking and warranty settings 2026-10-02 13:03:45 +05:30
DeepnaPooja c52eaabec4 Merge pull request 'Dev' (#53) from dev into production
Reviewed-on: #53
2026-09-29 12:14:53 +00:00
DeepnaPooja 63c1c4ad1a Merge pull request 'fix' (#52) from refactor/main_flow into dev
CI/CD - ERP Distribution / Build & Deploy Frontend (pull_request) Failing after 12m12s
CI/CD - ERP Distribution / Build & Deploy Backend (pull_request) Failing after 13m26s
Reviewed-on: #52
2026-09-29 12:14:19 +00:00
DeepnaPooja e3af6100ec fix 2026-09-29 17:38:39 +05:30
DeepnaPooja df0eb3cd5a Merge pull request 'Refactor/main flow' (#50) from refactor/main_flow into dev
CI/CD - ERP Distribution / Build & Deploy Frontend (pull_request) Failing after 11m24s
CI/CD - ERP Distribution / Build & Deploy Backend (pull_request) Failing after 12m16s
Reviewed-on: #50
2026-09-29 12:04:26 +00:00
DeepnaPooja d8ba956817 fix loading sheet UI 2026-09-29 17:29:02 +05:30
DeepnaPooja 89c0c17be7 ui fix 2026-09-29 17:07:16 +05:30
DeepnaPooja cc72e22664 Merge pull request 'Refactor/main flow' (#49) from refactor/main_flow into dev
Reviewed-on: #49
2026-09-29 09:45:51 +00:00
DeepnaPooja ef4e0e0873 Add item reorder level migration 2026-09-29 12:32:22 +05:30
DeepnaPooja bd90a3d68e dashboard design 2026-09-29 12:08:54 +05:30
DeepnaPooja 7dac429a1a Add low stock endpoint 2026-09-29 11:48:37 +05:30
DeepnaPooja 3dbfe6db23 Add item reorder level to DTOs 2026-09-29 11:46:11 +05:30
DeepnaPooja a6de58e2e9 Add invoice payment summary 2026-09-29 11:45:08 +05:30
DeepnaPooja 681cf821a6 Add delivery task listing endpoint 2026-09-29 11:43:10 +05:30
DeepnaPooja 37f4a88014 Merge pull request 'Dev' (#48) from dev into production
Reviewed-on: #48
2026-09-28 07:39:55 +00:00
DeepnaPooja a4f5d12234 Merge pull request 'Refactor/main flow' (#47) from refactor/main_flow into dev
CI/CD - ERP Distribution / Build & Deploy Frontend (pull_request) Failing after 12m10s
CI/CD - ERP Distribution / Build & Deploy Backend (pull_request) Failing after 13m24s
Reviewed-on: #47
2026-09-28 07:39:07 +00:00
DeepnaPooja ee50f0932d fix open add product in new tab 2026-09-28 13:03:11 +05:30
DeepnaPooja 7dd012f926 Add PO confirmation btutton and the status badge 2026-09-28 12:36:21 +05:30
DeepnaPooja aebfa6e3cc Add search to dropdown 2026-09-28 12:00:59 +05:30
DeepnaPooja 2d365cd906 Merge pull request 'Dev' (#46) from dev into production
Reviewed-on: #46
2026-09-25 08:38:12 +00:00
DeepnaPooja d6fd685b3c Merge pull request 'Add loading sheet edit flow' (#45) from refactor/main_flow into dev
CI/CD - ERP Distribution / Build & Deploy Frontend (pull_request) Failing after 13m57s
CI/CD - ERP Distribution / Build & Deploy Backend (pull_request) Failing after 15m8s
Reviewed-on: #45
2026-09-25 08:37:39 +00:00
DeepnaPooja a892aec527 Add loading sheet edit flow 2026-09-25 14:02:24 +05:30
DeepnaPooja c68c6e1ab9 Merge pull request 'Dev' (#44) from dev into production
Reviewed-on: #44
2026-09-24 12:00:43 +00:00
DeepnaPooja aaf6e5f12d Merge pull request 'Refactor/main flow' (#43) from refactor/main_flow into dev
CI/CD - ERP Distribution / Build & Deploy Frontend (pull_request) Failing after 11m24s
CI/CD - ERP Distribution / Build & Deploy Backend (pull_request) Failing after 12m36s
Reviewed-on: #43
2026-09-24 12:00:17 +00:00
DeepnaPooja 75b0a802c3 update the migrations 2026-09-24 17:24:43 +05:30
DeepnaPooja e8692b2474 fix the loading sheet view 2026-09-24 17:23:31 +05:30
DeepnaPooja be38777ebb Fixes for the route 2026-09-24 15:50:35 +05:30
DeepnaPooja 41f992b824 store page fixes 2026-09-24 15:05:48 +05:30
DeepnaPooja 1ec5426d15 PO shops fe fixes 2026-09-24 15:04:42 +05:30
DeepnaPooja 3b0a5f0d3f remove store kyc features 2026-09-24 14:56:17 +05:30
DeepnaPooja 96e12972b9 fix store profile fields 2026-09-24 14:18:52 +05:30
DeepnaPooja ddc3ecd3e6 Merge pull request 'Dev' (#42) from dev into production
Reviewed-on: #42
2026-09-23 08:38:21 +00:00
DeepnaPooja 556dedb8a3 Merge pull request 'Refactor/main flow' (#41) from refactor/main_flow into dev
CI/CD - ERP Distribution / Build & Deploy Frontend (pull_request) Failing after 13m49s
CI/CD - ERP Distribution / Build & Deploy Backend (pull_request) Failing after 14m58s
Reviewed-on: #41
2026-09-23 08:36:57 +00:00
DeepnaPooja 80db899091 Fix user role bug for invoice actions 2026-09-23 14:01:32 +05:30
DeepnaPooja c290a1e778 Add user fallback for invoice actions 2026-09-23 13:59:49 +05:30
DeepnaPooja d7b6f29200 Merge pull request 'Dev' (#40) from dev into production
Reviewed-on: #40
2026-09-23 07:23:59 +00:00
DeepnaPooja 6f72f7a0e9 Merge pull request 'Refactor/main flow' (#39) from refactor/main_flow into dev
CI/CD - ERP Distribution / Build & Deploy Frontend (pull_request) Failing after 13m8s
CI/CD - ERP Distribution / Build & Deploy Backend (pull_request) Failing after 14m21s
Reviewed-on: #39
2026-09-23 07:23:19 +00:00
DeepnaPooja ee672da9f7 Add user selection to Shop PO flow 2026-09-23 12:47:53 +05:30
DeepnaPooja 4b95e03e05 Add user fallback for Shop PO actions 2026-09-23 12:47:08 +05:30
DeepnaPooja ae302d0c9c Fix loading sheet creation flow 2026-09-23 12:46:23 +05:30
DeepnaPooja 53dcdf929a Merge pull request 'Refactor/main flow' (#37) from refactor/main_flow into dev
Reviewed-on: #37
2026-09-22 11:43:52 +00:00
DeepnaPooja d2277f626c Merge pull request 'Refactor/main flow' (#38) from refactor/main_flow into production
Reviewed-on: #38
2026-09-22 11:43:07 +00:00
DeepnaPooja 35fa3b19e3 Add loading sheet item selection
CI/CD - ERP Distribution / Build & Deploy Frontend (pull_request) Failing after 13m56s
CI/CD - ERP Distribution / Build & Deploy Backend (pull_request) Failing after 15m12s
2026-09-22 17:03:24 +05:30
DeepnaPooja b7db6a83e6 Add Shop PO management actions 2026-09-22 17:00:38 +05:30
DeepnaPooja 8bb3b3d192 Update loading sheet filters and orders 2026-09-22 16:59:38 +05:30
DeepnaPooja e5dadef863 Add invoice print and finalize flow 2026-09-22 16:58:38 +05:30
DeepnaPooja 44e335d542 Add ride cancellation details for the fe 2026-09-22 12:12:42 +05:30
DeepnaPooja d20e4a532f Add cancel ride type code and the cancel reason 2026-09-22 12:10:37 +05:30
DeepnaPooja 52845b5d87 Use client-side pagination for rides 2026-09-22 11:24:21 +05:30
kalana_Thilakarathna 3149902574 Merge pull request 'Dev' (#36) from dev into production
Reviewed-on: #36
2026-09-21 04:05:56 +00:00
Kalana B. Thilakarathna 3c17047550 Improve filter handling in SalesClient to reset values for empty selections
CI/CD - ERP Distribution / Build & Deploy Backend (pull_request) Failing after 12m24s
CI/CD - ERP Distribution / Build & Deploy Frontend (pull_request) Failing after 16m17s
2026-09-21 09:35:05 +05:30
Kalana B. Thilakarathna 34ffc7f97a Fix ride selection logic to handle empty values in LoadingSheetsClient 2026-09-21 09:30:57 +05:30
kalana_Thilakarathna 37fb72376a Merge pull request 'Dev' (#35) from dev into production
Reviewed-on: #35
2026-09-21 03:47:22 +00:00
kalana_Thilakarathna b3840b47ce Merge pull request 'Update sales pages' (#34) from refactor/FE-fixes into dev
CI/CD - ERP Distribution / Build & Deploy Backend (pull_request) Failing after 10m57s
CI/CD - ERP Distribution / Build & Deploy Frontend (pull_request) Failing after 14m42s
Reviewed-on: #34
2026-09-21 03:46:58 +00:00
DeepnaPooja a9a201cce1 Update sales pages 2026-09-20 21:33:44 +05:30
kalana_Thilakarathna 52a9a3fde7 Merge pull request 'Add build argument for NEXT_PUBLIC_API_URL in Dockerfile and CI/CD workflow' (#32) from dev into production
Reviewed-on: #32
2026-09-20 11:21:49 +00:00
Kalana B. Thilakarathna 3c2dee5b4a Add build argument for NEXT_PUBLIC_API_URL in Dockerfile and CI/CD workflow
CI/CD - ERP Distribution / Build & Deploy Backend (pull_request) Failing after 11m32s
CI/CD - ERP Distribution / Build & Deploy Frontend (pull_request) Failing after 15m25s
2026-09-20 16:51:13 +05:30
kalana_Thilakarathna 1b310c86b3 Merge pull request 'Add Dockerfile and .dockerignore; configure Next.js output settings' (#31) from dev into production
Reviewed-on: #31
2026-09-20 11:07:29 +00:00
Kalana B. Thilakarathna 1579936baf Add Dockerfile and .dockerignore; configure Next.js output settings
CI/CD - ERP Distribution / Build & Deploy Backend (pull_request) Failing after 10m51s
CI/CD - ERP Distribution / Build & Deploy Frontend (pull_request) Failing after 14m44s
2026-09-20 16:36:33 +05:30
kalana_Thilakarathna e398ee9d6b Merge pull request 'docker log' (#30) from dev into production
Reviewed-on: #30
2026-09-20 11:03:16 +00:00
Kalana B. Thilakarathna a8b6503a8a docker log
CI/CD - ERP Distribution / Build & Deploy Frontend (pull_request) Failing after 13m55s
CI/CD - ERP Distribution / Build & Deploy Backend (pull_request) Failing after 15m3s
2026-09-20 16:32:48 +05:30
kalana_Thilakarathna a3c0211654 Merge pull request 'pipline update' (#29) from dev into production
Reviewed-on: #29
2026-09-20 10:53:45 +00:00
Kalana B. Thilakarathna dc32ae7ea6 pipline update
CI/CD - ERP Distribution / Build & Deploy Frontend (pull_request) Failing after 13m27s
CI/CD - ERP Distribution / Build & Deploy Backend (pull_request) Failing after 14m34s
2026-09-20 16:23:16 +05:30
kalana_Thilakarathna e4e12aa05a Merge pull request 'build fix' (#28) from dev into production
Reviewed-on: #28
2026-09-20 10:42:24 +00:00
Kalana B. Thilakarathna 6a31ad0190 build fix
CI/CD - ERP Distribution / Build & Deploy Backend (pull_request) Failing after 10m55s
CI/CD - ERP Distribution / Build & Deploy Frontend (pull_request) Failing after 14m48s
2026-09-20 16:12:04 +05:30
kalana_Thilakarathna 3103fb66ef Merge pull request 'Dev' (#27) from dev into production
Reviewed-on: #27
2026-09-20 10:35:40 +00:00
Kalana B. Thilakarathna 502bf25cc0 Merge branch 'dev' of https://gitea.hexdive.com/New_REP_SYSTEM/ERP_Distribution into dev
CI/CD - ERP Distribution / Build & Deploy Frontend (pull_request) Failing after 11m11s
CI/CD - ERP Distribution / Build & Deploy Backend (pull_request) Failing after 12m39s
2026-09-20 16:03:00 +05:30
Kalana B. Thilakarathna 484aa3fece Update appsettings.json URLs and add CI/CD workflow for ERP Distribution 2026-09-20 16:02:53 +05:30
DeepnaPooja 1c9ca179df Merge pull request 'Refactor/ride lifecycle' (#26) from refactor/ride_lifecycle into dev
Reviewed-on: #26
2026-09-18 06:51:51 +00:00
Kalana B. Thilakarathna c8c04c357d refactor plan created 2026-09-18 12:18:00 +05:30
DeepnaPooja b63f264551 Add cn package as a dependency 2026-09-18 12:00:04 +05:30
DeepnaPooja 7ced279ad5 Add toggle controls and payment API fields 2026-09-18 11:56:11 +05:30
DeepnaPooja 4d7b8d8a56 Add payment method cheque enum 2026-09-18 11:54:01 +05:30
DeepnaPooja 1f6d63a0b3 Add cheque to payment UI 2026-09-18 11:47:34 +05:30
DeepnaPooja 63e7ddba82 Add payment cheque fields and seed invoice payments 2026-09-18 11:45:30 +05:30
DeepnaPooja e132e29c82 Add cheque and entry mode details to payments 2026-09-18 11:43:43 +05:30
DeepnaPooja e2cec66d6b remove the ride cancel request 2026-09-17 14:37:56 +05:30
Kalana B. Thilakarathna 03d01d4c4e name: CI/CD - ERP Core Monorepo
on:
  pull_request:
    branches:
      - production
    types:
      - closed

jobs:
  backend-build-and-deploy:
    name: Build & Deploy ERP Core Backend
    runs-on: linux
    if: github.event.pull_request.merged == true
    steps:
      - name: Checkout code
        run: |
          git clone \
            --depth 1 \
            --branch production \
            https://oauth2:${{ secrets.ERPCORE }}@gitea.hexdive.com/New_REP_SYSTEM/ERP-core.git \
            .

      - name: Restore backend
        run: dotnet restore Backend/ERPCore.sln

      - name: Publish backend
        run: |
          rm -rf /tmp/erpcore-deploy
          mkdir -p /tmp/erpcore-deploy
          dotnet publish Backend/ERPCore/ERPCore.csproj \
            -c Release \
            -r linux-x64 \
            --self-contained true \
            -o /tmp/erpcore-deploy

      - name: Stop backend service
        run: systemctl stop erpcore.service

      - name: Copy backend to deploy folder
        run: |
          mkdir -p /home/erp/core/publish
          rm -rf /home/erp/core/publish/*
          cp -r /tmp/erpcore-deploy/* /home/erp/core/publish/

      - name: Fix backend ownership and permissions
        run: |
          chown -R erpuser:erpuser /home/erp/core/publish
          find /home/erp/core/publish -type d -exec chmod 755 {} +
          find /home/erp/core/publish -type f -exec chmod 644 {} +
          chmod +x /home/erp/core/publish/ERPCore

      - name: Restart backend service
        run: systemctl restart erpcore.service

      - name: Verify backend
        run: |
          systemctl is-active erpcore.service && echo "Backend service running" || echo "Backend service failed"

  frontend-build-and-deploy:
    name: Build & Deploy ERP Core Frontend
    runs-on: linux
    needs: backend-build-and-deploy
    if: github.event.pull_request.merged == true
    steps:
      - name: Checkout code
        run: |
          git clone \
            --depth 1 \
            --branch Production \
            https://oauth2:${{ secrets.ERPCORE }}@gitea.hexdive.com/New_REP_SYSTEM/ERP-core.git \
            .

      - name: Build frontend Docker image
        run: docker build -t erpcore-frontend:latest Frontend/erp-system

      - name: Stop & remove existing frontend container
        run: |
          docker stop erpcore-frontend 2>/dev/null || true
          docker rm erpcore-frontend 2>/dev/null || true

      - name: Start frontend container
        run: |
          docker run -d \
            --name erpcore-frontend \
            -p 6702:6702 \
            --add-host=host.docker.internal:host-gateway \
            -e BACKEND_ORIGIN=http://host.docker.internal:6701 \
            --restart unless-stopped \
            --memory="512m" \
            --cpus="1.0" \
            --cap-drop ALL \
            --security-opt no-new-privileges \
            erpcore-frontend:latest

      - name: Remove old frontend images
        run: docker image prune -f

      - name: Verify frontend
        run: |
          docker ps --filter name=erpcore-frontend --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}" \
            | grep erpcore-frontend && echo "Frontend container running" || echo "Frontend container failed"
2026-09-17 13:59:05 +05:30
Kalana B. Thilakarathna 93f2f1bd3b Refactor sales pipeline: ShopPO, Invoice, SysConfig models
Major refactor replacing Sale/Order with ShopPO → Invoice → LoadingSheet flow. Removed legacy Sale/Order models and features; introduced ShopPO, ShopPOItem, Invoice, LoadingSheetOrder, and SysConfig models. Updated related entities, enums, repositories, and controllers. Added new migrations for schema changes and system config. Overhauled business logic and DI registrations. Updated documentation with migration plan.
2026-09-17 10:03:22 +05:30
Kalana B. Thilakarathna 93355d34f1 Merge branch 'dev' into production 2026-09-16 11:30:37 +05:30
Kalana B. Thilakarathna 6c40421b2d Revamp dispatch pipeline and PO cancellation docs
Updated documentation to clarify and redesign dispatch pipeline, loading event, and order/invoice lifecycle. Added PO cancellation flow for both Shop and Confirmed POs, introduced new API endpoint for cancellation, and simplified loading sheet model and endpoints. Added new permissions for loading operations, improved auditability, and detailed migration steps. Highlighted open questions on cancellation cascades, permissions, and vehicle capacity checks for future resolution.
2026-09-16 11:30:21 +05:30
Kalana B. Thilakarathna 9151f47d15 Merge branch 'production' of https://gitea.hexdive.com/New_REP_SYSTEM/ERP_Distribution into production 2026-09-16 10:58:02 +05:30
Kalana B. Thilakarathna 661b522b7d Merge branch 'dev' into production 2026-09-16 10:57:55 +05:30
Kalana B. Thilakarathna 541f59b15d Merge branch 'dev' of https://gitea.hexdive.com/New_REP_SYSTEM/ERP_Distribution into dev 2026-09-16 10:57:40 +05:30
Kalana B. Thilakarathna b68e5043c7 refactor update 2026-09-16 10:57:31 +05:30
DeepnaPooja e0bb17d0ff Merge pull request 'Dev' (#25) from dev into production
Reviewed-on: #25
2026-09-16 05:13:39 +00:00
DeepnaPooja 436f7c10a0 Merge pull request 'Add invoice collection plan for cash, cheque, and credit payments post-finalization' (#24) from fixes into dev
Reviewed-on: #24
2026-09-16 05:12:49 +00:00
Kalana B. Thilakarathna 0f29413375 Merge branch 'dev' into production 2026-09-16 10:41:32 +05:30
Kalana B. Thilakarathna 8739e8e1a7 refactor 2026-09-16 10:41:16 +05:30
Kalana B. Thilakarathna 308be2f5ce refactor invoice and po life cycle 2026-09-16 10:39:42 +05:30
DeepnaPooja eccc80eaad Add invoice collection plan for cash, cheque, and credit payments post-finalization 2026-09-16 10:35:41 +05:30
DeepnaPooja ef17f7da0a Merge pull request 'Dev' (#23) from dev into production
Reviewed-on: #23
2026-09-14 06:29:20 +00:00
DeepnaPooja ca2507a904 Merge pull request 'Refactor order invoice lifecycle and ride lifecycle documentation' (#22) from fixes into dev
Reviewed-on: #22
2026-09-14 06:28:31 +00:00
DeepnaPooja fb62448e2c Refactor order invoice lifecycle and ride lifecycle documentation 2026-09-14 11:53:04 +05:30
DeepnaPooja 307055f8e1 Merge pull request 'Fixes' (#21) from fixes into dev
Reviewed-on: #21
2026-09-10 10:30:04 +00:00
DeepnaPooja 2000ba67f4 Add refactor order_invoice_lifecycle.md file 2026-09-10 15:53:57 +05:30
DeepnaPooja b8836a1e87 Add refactor ride lifecylcle plan 2026-09-10 15:53:05 +05:30
DeepnaPooja 6c6d30f4f9 Add RetailPrice and AgencyMarginPercent to PriceListItem and related API updates 2026-09-09 16:32:57 +05:30
DeepnaPooja 9bb4c52efb Add AgencyMarginPercent and RetailPrice to PriceListItems 2026-09-09 16:32:20 +05:30
DeepnaPooja 617c0713a1 remove PriceListSheet component and integrate new price list creation page 2026-09-09 16:31:35 +05:30
DeepnaPooja eb0303fa3f Fix Isactive issue 2026-09-09 16:30:38 +05:30
DeepnaPooja f9e4bb3a97 Update scheme management to include IsActive 2026-09-09 16:29:54 +05:30
DeepnaPooja 7dbeaa722a Add PriceList functionality with RetailPrice and AgencyMarginPercent 2026-09-09 16:28:57 +05:30
DeepnaPooja 58d67495d2 Add supplier claims management components and API integration 2026-09-08 16:14:33 +05:30
DeepnaPooja e53db290c8 Add order return management 2026-09-08 16:12:44 +05:30
DeepnaPooja 2a5562a355 Add ClaimSettleDialog, ClaimSheet, ClaimViewSheet, and ClaimsClient 2026-09-08 16:12:05 +05:30
DeepnaPooja 5a6eca1f07 Add GrnViewSheet in GrnClient 2026-09-08 16:11:11 +05:30
DeepnaPooja 6ba8d2fd19 Implement GetAll and Update functionality for Supplier Claims 2026-09-08 16:08:41 +05:30
DeepnaPooja e267f94d46 Add GetAll and UpdateReasons endpoints to ReturnController 2026-09-08 16:08:07 +05:30
DeepnaPooja 81d92e16bb Fix user ID handling in purchase return creation when auth is unavailable 2026-09-08 16:07:28 +05:30
DeepnaPooja ddbe56c5df Fix user ID handling in GRN and PO 2026-09-08 16:06:59 +05:30
DeepnaPooja 5f303321d0 Add CRUD operations for claims 2026-09-08 16:05:45 +05:30
DeepnaPooja 19ff0b5578 Fix seed stock movement external locations in migrations 2026-09-08 13:20:45 +05:30
DeepnaPooja bbf9e5b60e Add external location type and location name fields to stock responses 2026-09-08 13:18:42 +05:30
DeepnaPooja 508149dab8 Add adjustment functionality to stock API 2026-09-08 13:18:26 +05:30
DeepnaPooja eea87c0ee2 Add stock movement view and edit dialogs, and integrate with stock movements client 2026-09-08 13:18:11 +05:30
DeepnaPooja 3bcb42ea9b Add StockAdjustDialog component for stock adjustment 2026-09-08 13:17:46 +05:30
DeepnaPooja 2053b61965 Add external location handling to stock movement processes 2026-09-08 13:17:01 +05:30
DeepnaPooja 7e362be304 Add movement retrieval and update functionality to stock 2026-09-08 13:16:04 +05:30
DeepnaPooja f5872cedaf Add FromLocationId and FromLocationType to stock movement creation 2026-09-08 13:14:36 +05:30
DeepnaPooja cff4f4bf30 Add ToLocationId and ToLocationType to delivery task item movement 2026-09-08 13:14:00 +05:30
DeepnaPooja aee3e840a0 Add category viewing 2026-09-08 11:46:34 +05:30
DeepnaPooja ef0486829c Add brand viewing 2026-09-08 11:45:54 +05:30
DeepnaPooja 7e9110b707 Enhance category and sub-category update requests to include IsActive flag and deletes and inactive categories 2026-09-08 11:45:15 +05:30
DeepnaPooja 68aea8105d UpdateBrandRequest to include IsActive flag and update BrandService to handle inactive brands correctly 2026-09-08 11:44:16 +05:30
DeepnaPooja 04004437bb Add item details viewing 2026-09-04 13:08:07 +05:30
DeepnaPooja 712a163777 fix handle inactive items 2026-09-04 13:07:22 +05:30
DeepnaPooja dfd51b8273 Add user selection for check-in and update API to include user ID 2026-09-04 12:26:59 +05:30
DeepnaPooja 51aaed8fd1 Fix check-in functionality with error handling and temporary user ID fallback 2026-09-04 12:26:42 +05:30
DeepnaPooja 62bfe27a84 Merge branch 'fixes' of https://gitea.hexdive.com/New_REP_SYSTEM/ERP_Distribution into fixes 2026-09-04 11:54:12 +05:30
DeepnaPooja 0523dabc5a Add route to StoresClient and implement updateStatus API method 2026-09-04 11:52:10 +05:30
DeepnaPooja 7d4d07d692 Refactor store management and API integration 2026-09-04 11:50:58 +05:30
DeepnaPooja 1bdda3a8ab Add route management to StoreViewPage 2026-09-04 11:50:06 +05:30
DeepnaPooja 1645eae694 Add SetActiveStatusAsync, and update DTOs for active status management. 2026-09-04 11:48:53 +05:30
DeepnaPooja d0bf5581c5 Update PaymentBody fields in sales.ts 2026-09-03 17:22:49 +05:30
DeepnaPooja 8864f8ad6d Refactor payments UI: dropdowns, view sheet 2026-09-03 17:22:04 +05:30
DeepnaPooja c962ed9425 Add CollectedByUserId to payment DTOs as auth workaround 2026-09-03 17:20:12 +05:30
DeepnaPooja ad74074771 Update test bug log 2026-09-03 17:04:27 +05:30
DeepnaPooja 899d04e4eb Update orders flow 2026-09-03 17:04:13 +05:30
DeepnaPooja 5e48873422 Update sales UI 2026-09-03 16:08:34 +05:30
DeepnaPooja ab2226e394 Update store controller 2026-09-03 16:07:02 +05:30
DeepnaPooja d7546d24d1 Update sale flow 2026-09-03 16:06:12 +05:30
DeepnaPooja be66e65e59 Update order controller 2026-09-03 16:05:19 +05:30
DeepnaPooja 63115f18b4 Validate ride close form 2026-09-03 13:31:33 +05:30
DeepnaPooja 1357f7e3f8 Update delivery tasks client 2026-09-03 13:31:14 +05:30
DeepnaPooja 831f33df34 Add delivery task details sheet 2026-09-03 13:29:17 +05:30
DeepnaPooja 8f5ad872e5 Add delivery task fail dialog 2026-09-03 13:29:02 +05:30
DeepnaPooja 38b893b9f6 Add delivery task deliver dialog 2026-09-03 13:28:55 +05:30
DeepnaPooja 5f82090272 Update delivery task API 2026-09-03 13:28:48 +05:30
DeepnaPooja 5890c03ffe Update test bug log 2026-09-03 13:28:32 +05:30
DeepnaPooja 6a8204b584 Refine sale fulfillment 2026-09-03 13:27:58 +05:30
DeepnaPooja 32f9b079b3 Validate ride meter end 2026-09-03 13:27:49 +05:30
DeepnaPooja b71cfae6c9 Fix delivery task stock check 2026-09-03 13:27:00 +05:30
DeepnaPooja 35fe512bd1 Add loading sheet view fix 2026-09-02 17:01:38 +05:30
DeepnaPooja de3dcb8c52 Add dialogs and update API for cancel requests 2026-09-02 16:30:04 +05:30
DeepnaPooja 692fc4f290 fix the ride create issue 2026-09-02 15:19:46 +05:30
DeepnaPooja 85a16396aa Add the testbug.md file 2026-09-02 13:46:34 +05:30
DeepnaPooja d9031f8e73 Merge pull request 'Dev' (#20) from dev into production
Reviewed-on: #20
2026-09-01 10:28:44 +00:00
DeepnaPooja 12ff9e8fef Merge pull request 'Feat/fe integration' (#19) from feat/FE_Integration into dev
Reviewed-on: #19
2026-09-01 10:27:37 +00:00
DeepnaPooja 7cf272e311 Consolidate region management APIs 2026-09-01 15:46:35 +05:30
DeepnaPooja 82a32de966 add seed data 2026-09-01 15:46:35 +05:30
Kalana B. Thilakarathna 75578ea7f6 Merge branch 'production' of https://gitea.hexdive.com/New_REP_SYSTEM/ERP_Distribution into production 2026-09-01 10:54:20 +05:30
Kalana B. Thilakarathna 68b34b3fc3 Refactor store CRUD to pages, add map-based location picker
Refactored store creation and editing from modal sheets to dedicated pages (`/stores/new`, `/stores/[id]/edit`), and added a new store detail view page (`/stores/[id]`). Introduced a `LocationPicker` component using `react-leaflet` for map-based location selection, replacing manual latitude/longitude inputs. Updated navigation in the store list to use these pages. Improved UI and user feedback for store forms and details. Updated backend API and form logic to match the new flow. Added `leaflet`, `react-leaflet`, and types as dependencies. Temporarily relaxed store scope checks in `StoreService.cs`. Updated table actions and types for the new workflow.
2026-09-01 10:53:55 +05:30
DeepnaPooja 4f02fa303d Merge pull request 'Dev' (#18) from dev into production
Reviewed-on: #18
2026-08-31 07:41:03 +00:00
DeepnaPooja 83167b5eb3 Merge pull request 'Feat/route sales mngt' (#17) from feat/route_sales_mngt into dev
Reviewed-on: #17
2026-08-31 07:38:42 +00:00
DeepnaPooja e67675a12a update docs 2026-08-31 13:00:49 +05:30
DeepnaPooja 2ecda623ac add route service tests 2026-08-31 13:00:42 +05:30
DeepnaPooja da9caa855a add sales visits ui 2026-08-31 13:00:33 +05:30
DeepnaPooja 5c4e1b590a add route visit management 2026-08-31 13:00:13 +05:30
DeepnaPooja 75b5035354 Merge pull request 'Feat/region mngt' (#16) from feat/region_mngt into dev
Reviewed-on: #16
2026-08-31 05:47:39 +00:00
DeepnaPooja c0432a448f Document route-to-region assignment and applied migration
Notes the new PUT /api/routes/{id}/region endpoint as a stand-in for
the still-blocked assignAreaToRegion, and removes the stale "migration
pending" caveat now that AddRegionManagement has been applied.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-31 11:11:25 +05:30
DeepnaPooja bfbfe352ee Add dedicated endpoint to assign a route to a region
PUT /api/routes/{id}/region assigns/reassigns an existing route to a
region directly, validating the target region exists and is active.
Stands in for the blocked assignAreaToRegion until an Area entity
exists between Region and Route.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-31 11:11:15 +05:30
DeepnaPooja 320da0e1c9 Add and apply EF Core migration for region management tables
Creates RegionAgencyMaps, RegionalBudgets, RegionalTargets, and
RegionalTaxRules with their unique indexes and foreign keys, and
applies the migration to the shared MySQL database.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-31 10:38:59 +05:30
DeepnaPooja f0d7f939ef Update mainPlan.md status table for Region Management
The summary table still said "Not Started" for step 10 after the
detailed section below it was updated to reflect the implemented
target/budget/agency-map/tax-rule/hierarchy/dashboard endpoints.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-30 22:53:59 +05:30
DeepnaPooja 8b84c8aeb3 Document the implemented region management endpoints
Updates mainPlan.md's Region Management table, 10_region_management.md's
status/open-questions/implementation-notes sections, and
BACKEND_API_REFERENCE.md's Regions section to reflect the newly added
target/budget/agency-map/tax-rule/hierarchy/dashboard endpoints, and
flags that the EF Core migration for the new tables is still pending.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-30 22:51:46 +05:30
DeepnaPooja 9febae6d63 Add region management dialog for targets, budgets, tax rules & agencies
RegionManageSheet lets a manager set a region's target/budget for a
period, save a tax rule config, map a RegionalAgency user to the
region, and view its route/store hierarchy and dashboard stats. Wired
into the regions table as a new row action.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-30 22:46:05 +05:30
DeepnaPooja df27d58089 Add frontend types and API client methods for region management
Adds RegionalTargetResponse, RegionalBudgetResponse,
RegionAgencyMapResponse, RegionalTaxRuleResponse,
RegionHierarchyResponse, and RegionDashboardResponse types, plus
setTarget/setBudget/mapAgency/setTaxRule/hierarchy/dashboard methods
on the regions API client, mirroring the new backend endpoints.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-30 22:45:55 +05:30
DeepnaPooja 4e12e9f8f1 Update RegionServiceTests for the new RegionService constructor
RegionService now depends on ICurrentUserService and IScopeAccessService
for the target/budget/tax-rule/agency scope checks; update the existing
CRUD test setup to mock them accordingly.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-30 22:45:43 +05:30
DeepnaPooja 39eab2e3a2 Add region target/budget/tax-rule/agency-map/hierarchy/dashboard APIs
Extends RegionService, RegionRepository, and RegionController with the
endpoints from mainPlan.md sec 2 beyond plain CRUD: setRegionalTarget,
setRegionalBudget, mapAgencyToRegion, setRegionalTaxRule,
getRegionHierarchy, and getRegionDashboard. Target/budget/tax-rule
mutation is gated to users with region scope at RegionalManager level
or RegionalAgency level across all regions; mapAgencyToRegion links an
existing User holding the RegionalAgency role rather than introducing
a separate agency entity, matching the convention already used by
Claim.FK_AgencyUserId elsewhere in the codebase.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-30 22:45:35 +05:30
DeepnaPooja 889ca0795d Add data model for region targets, budgets, agency mapping, tax rules
Introduces RegionalTarget, RegionalBudget, RegionAgencyMap, and
RegionalTaxRule entities plus their DbSets and EF constraints, laying
the groundwork for the "beyond CRUD" region management features.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-30 22:44:12 +05:30
Kalana B. Thilakarathna a7801a2bc2 Merge branch 'production' into dev 2026-08-29 21:03:56 +05:30
Kalana B. Thilakarathna f4db83654c Implement GRN and Purchase Order sheets with form handling and data fetching 2026-08-29 21:03:31 +05:30
Kalana B. Thilakarathna d414ec8e38 Merge branch 'dev' into production 2026-08-26 20:20:00 +05:30
Kalana B. Thilakarathna dc7e7a70fc Add FlatPaymentController with RESTful payment endpoints
Introduced FlatPaymentController under Distribution.Features.Payment, providing GET and POST endpoints at /api/payments. Supports listing, retrieving, and creating payments with filtering and error handling. Business logic is delegated to PaymentService.
2026-08-26 20:18:58 +05:30
Kalana B. Thilakarathna 20b82a2734 Merge branch 'dev' into production 2026-08-25 23:48:06 +05:30
Kalana B. Thilakarathna 83d3272784 frontend fully integreated with the backend 2026-08-25 23:47:33 +05:30
DeepnaPooja d6cd9ccd08 Merge pull request 'Dev' (#15) from dev into production
Reviewed-on: #15
2026-08-25 17:24:08 +00:00
DeepnaPooja 1bee5f91c6 Merge branches 'feat/authentication' and 'dev' of https://gitea.hexdive.com/New_REP_SYSTEM/ERP_Distribution into dev 2026-08-25 22:46:27 +05:30
DeepnaPooja 08ad17f15b Document auth flow 2026-08-25 16:46:12 +05:30
DeepnaPooja 97a1db1343 Refactor auth to AuthHex 2026-08-25 16:46:05 +05:30
kalana_Thilakarathna 3268a50c3e Merge pull request 'Production' (#13) from production into dev
Reviewed-on: #13
2026-08-25 10:56:00 +00:00
Kalana B. Thilakarathna f1c036d1d8 frontend initiatewith shadcn uis 2026-08-25 16:20:50 +05:30
Kalana B. Thilakarathna 92fef1dfa8 Merge branch 'dev' into production 2026-08-25 16:12:06 +05:30
Kalana B. Thilakarathna bb33928661 auth clean up 2026-08-25 16:11:37 +05:30
DeepnaPooja b279a121ad Merge pull request 'Dev' (#12) from dev into production
Reviewed-on: #12
2026-08-25 10:15:19 +00:00
DeepnaPooja 916a2b8f0a Merge pull request 'Feat/returns & claims' (#11) from feat/returns-&-claims into dev
Reviewed-on: #11
2026-08-25 10:14:38 +00:00
DeepnaPooja 3caacf44d0 update md files 2026-08-25 15:38:22 +05:30
DeepnaPooja fe9ccb4de7 Update procurement docs 2026-08-25 14:48:02 +05:30
DeepnaPooja f244a04dc0 Add returns and claims 2026-08-25 14:47:51 +05:30
DeepnaPooja aa543d0d0d Merge pull request 'Production' (#10) from production into dev
Reviewed-on: #10
2026-08-25 05:42:16 +00:00
Kalana B. Thilakarathna 87146e4662 Merge branch 'dev' into production 2026-08-24 21:42:41 +05:30
Kalana B. Thilakarathna cefefcbc49 md update 2026-08-24 21:42:13 +05:30
Kalana B. Thilakarathna fcca03d0b2 Merge branch 'dev' into production 2026-08-24 21:38:13 +05:30
Kalana B. Thilakarathna 6d8b6b3e59 Add DeliveryTask, Payment, and Sale features
Implemented DeliveryTask, Payment, and Sale entities with full CRUD, business logic, and API endpoints. Updated Order/OrderItem for sale linkage and fulfillment tracking. Registered new DbSets and DI services. Revised documentation for delivery, sale, and payment workflows.
2026-08-24 21:34:57 +05:30
DeepnaPooja 3837facb02 Merge pull request 'Dev' (#9) from dev into production
Reviewed-on: #9
2026-08-24 11:29:34 +00:00
DeepnaPooja a833586e83 Merge pull request 'Feat/procurement' (#8) from feat/procurement into dev
Reviewed-on: #8
2026-08-24 11:28:48 +00:00
DeepnaPooja 3bc33ce08f Update procurement and master plan docs to match implementation 2026-08-24 16:52:26 +05:30
DeepnaPooja 870b412a06 Check stock availability when creating an order 2026-08-24 16:52:20 +05:30
DeepnaPooja 095d35e1b8 Add supplier evaluation and block delete when referenced by a purchase order 2026-08-24 16:52:12 +05:30
DeepnaPooja d7b03941fb Wire up procurement services in DI 2026-08-24 16:52:06 +05:30
DeepnaPooja 81a49e1a98 Add purchase return feature (returns received stock to a supplier) 2026-08-24 16:51:59 +05:30
DeepnaPooja 2995ce7012 Add goods receipt feature (creates stock from a purchase order) 2026-08-24 16:51:52 +05:30
DeepnaPooja db0850f1f0 Add purchase order feature (create/update/close/cancel/match-invoice) 2026-08-24 16:51:44 +05:30
DeepnaPooja 702dbeb39f Add procurement tables migration 2026-08-24 16:51:37 +05:30
DeepnaPooja c8d23afe59 Add procurement domain models (PurchaseOrder, GoodsReceipt, PurchaseReturn) 2026-08-24 16:51:22 +05:30
Kalana B. Thilakarathna ade41b4a60 Merge branch 'dev' into production 2026-08-24 11:24:06 +05:30
Kalana B. Thilakarathna ac1b2fbc4b loading event 2026-08-24 11:23:32 +05:30
Kalana B. Thilakarathna f381e7a634 Update docs: revise pending tasks and mark step 12 done
- 03_ride_lifecycle.md: Updated pending tasks note to only mention tests for new services, removing reference to database migrations.
- mainPlan.md: Changed status of "Store Management (Beyond CRUD)" (step 12) from "In Progress" to "Done".
2026-08-22 11:16:38 +05:30
DeepnaPooja 3680fc9035 Merge pull request 'Dev' (#6) from dev into production
Reviewed-on: #6
2026-08-21 08:52:16 +00:00
DeepnaPooja 6a0322570b Merge pull request 'Feat/promotions & trade' (#7) from feat/promotions-&-trade into dev
Reviewed-on: #7
2026-08-21 08:51:48 +00:00
DeepnaPooja dc4b1934e1 Wire store scheme eligibility and update docs 2026-08-21 14:14:14 +05:30
DeepnaPooja aabace1bba Add scheme APIs and application services 2026-08-21 14:14:07 +05:30
DeepnaPooja f7101800cd Add scheme data model and migrations 2026-08-21 14:10:40 +05:30
Kalana B. Thilakarathna f6211a8edd Merge branch 'production' of https://gitea.hexdive.com/New_REP_SYSTEM/ERP_Distribution into production 2026-08-21 02:08:50 +05:30
Kalana B. Thilakarathna c9f5006727 03_ride_lifecycle implemented and not tested 2026-08-21 02:08:11 +05:30
DeepnaPooja 942a39b0eb Merge pull request 'Feat/pricing & supplier' (#5) from feat/pricing_&_supplier into dev
Reviewed-on: #5
2026-08-20 12:08:05 +00:00
DeepnaPooja b2681d1236 Update pricing and store implementation plans 2026-08-20 17:29:11 +05:30
DeepnaPooja 63bc6e9cd8 Add pricing and supplier master data 2026-08-20 17:28:55 +05:30
DeepnaPooja 83bf715716 Merge pull request 'Dev' (#4) from dev into production
Reviewed-on: #4
2026-08-20 08:14:20 +00:00
DeepnaPooja 7a2494c156 Merge pull request 'Feat/store management' (#3) from feat/store_management into dev
Reviewed-on: #3
2026-08-20 08:13:40 +00:00
DeepnaPooja e3e331a2bb fix the running issue 2026-08-20 13:24:51 +05:30
DeepnaPooja 4e73dd7fb1 update store mngt.md 2026-08-20 13:10:46 +05:30
DeepnaPooja e88668beca Add store management workflows and KYC order gating 2026-08-20 13:08:00 +05:30
DeepnaPooja c1961ebea2 Add store KYC document management 2026-08-20 13:07:47 +05:30
DeepnaPooja cf9d2c2661 Add store credit and KYC data model 2026-08-20 13:07:27 +05:30
Kalana B. Thilakarathna 429a80b1ed Add planning docs for steps 10–23 and update roadmap
Added detailed implementation planning documents for steps 10–23, covering all major feature areas. Updated mainPlan.md to reference these steps, their dependencies, and planning files. No code changes—these documents clarify requirements and guide future development.
2026-08-20 10:04:09 +05:30
Kalana B. Thilakarathna f95fda3806 stock_ledger completed need to test 2026-08-20 09:42:33 +05:30
Kalana B. Thilakarathna aa7ea37ab6 main plan md update 2026-08-19 16:23:59 +05:30
DeepnaPooja d2390e7b12 Add md file features in region, route, store, fleet, pricing, procurement, SFA, promotions, returns, finance, reporting, notifications, integration, and mobile capabilities 2026-08-19 15:18:31 +05:30
Kalana B. Thilakarathna 4673d72d53 test project created 2026-08-19 14:40:04 +05:30
Kalana B. Thilakarathna 397af8fd59 item mng with sync 2026-08-19 12:49:04 +05:30
DeepnaPooja 45f374133f Refactor authorization folder structure 2026-08-19 11:39:44 +05:30
Kalana B. Thilakarathna 56cd93c341 md file and gap fix 2026-08-19 10:50:28 +05:30
1081 changed files with 315535 additions and 4546 deletions
Submodule .claude/worktrees/agent-a203fc082069b1faf added at ade41b4a60
+89
View File
@@ -0,0 +1,89 @@
name: CI/CD - ERP Distribution
on:
pull_request:
branches:
- production
types:
- closed
jobs:
build-and-deploy-backend:
name: Build & Deploy Backend
runs-on: linux
if: github.event.pull_request.merged == true
steps:
- name: Checkout code
run: |
git clone \
--depth 1 \
--branch production \
https://oauth2:${{ secrets.DISTRIBUTION }}@gitea.hexdive.com/New_REP_SYSTEM/ERP_Distribution.git \
.
- name: Restore
run: dotnet restore Distribution/Distribution.csproj
- name: Publish
run: |
rm -rf /tmp/erpdistribution-deploy
mkdir -p /tmp/erpdistribution-deploy
dotnet publish Distribution/Distribution.csproj \
-c Release \
-r linux-x64 \
--self-contained true \
-o /tmp/erpdistribution-deploy
- name: Stop service
run: systemctl stop erpdistribution.service
- name: Copy to deploy folder
run: |
mkdir -p /home/distribution/core/publish
rm -rf /home/distribution/core/publish/*
cp -r /tmp/erpdistribution-deploy/* /home/distribution/core/publish/
- name: Fix ownership and permissions
run: |
chown -R distributionuser:distributionuser /home/distribution/core/publish
find /home/distribution/core/publish -type d -exec chmod 755 {} +
find /home/distribution/core/publish -type f -exec chmod 644 {} +
chmod +x /home/distribution/core/publish/Distribution
- name: Restart service
run: systemctl restart erpdistribution.service
- name: Verify
run: |
systemctl is-active erpdistribution.service && echo "✅ Service running" || echo "❌ Service failed"
build-and-deploy-frontend:
name: Build & Deploy Frontend
runs-on: linux
if: github.event.pull_request.merged == true
steps:
- name: Checkout code
run: |
git clone \
--depth 1 \
--branch production \
https://oauth2:${{ secrets.DISTRIBUTION }}@gitea.hexdive.com/New_REP_SYSTEM/ERP_Distribution.git \
.
- name: Build Docker image
run: |
docker build --progress=plain --no-cache \
--build-arg NEXT_PUBLIC_API_URL=https://distribution.hexdive.com/api \
-t erpdistributionfe:latest \
./distribution_fe
- name: Stop & remove existing container
run: |
docker stop erpdistributionfe 2>/dev/null || true
docker rm erpdistributionfe 2>/dev/null || true
- name: Start container
run: |
docker run -d \
--name erpdistributionfe \
-p 8801:3000 \
--restart unless-stopped \
--memory="512m" \
--cpus="1.0" \
--cap-drop ALL \
--security-opt no-new-privileges \
erpdistributionfe:latest
- name: Remove old images
run: docker image prune -f
- name: Verify
run: |
docker ps --filter name=erpdistributionfe --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}" \
| grep erpdistributionfe && echo "✅ Container running" || echo "❌ Container failed"
@@ -0,0 +1,28 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
<PackageReference Include="xunit" Version="2.9.3" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Moq" Version="4.20.72" />
<PackageReference Include="coverlet.collector" Version="6.0.2">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Distribution\Distribution.csproj" />
</ItemGroup>
</Project>
@@ -0,0 +1,99 @@
using Distribution.Features.Auth;
using Distribution.Features.User;
using Distribution.Infra.Authorization;
using Distribution.Models;
using Microsoft.AspNetCore.Identity;
using Moq;
using Xunit;
namespace Distribution.Tests.Services;
public class AuthServiceTests
{
private readonly Mock<IUserRepository> _userRepo = new();
private readonly Mock<IJwtTokenService> _tokenService = new();
private readonly AuthService _sut;
private readonly PasswordHasher<User> _hasher = new();
public AuthServiceTests()
{
_sut = new AuthService(_userRepo.Object, _tokenService.Object);
}
private User ActiveUserWithPassword(string password) => new()
{
Id = Guid.NewGuid(),
Username = "testuser",
FullName = "Test User",
IsActive = true,
FK_RoleId = Guid.NewGuid(),
PasswordHash = _hasher.HashPassword(null!, password),
Role = new Role { Name = "Sales", Level = 10 }
};
// LoginAsync
[Fact]
public async Task LoginAsync_ReturnsNull_WhenUserNotFound()
{
_userRepo.Setup(r => r.GetByUsernameAsync(It.IsAny<string>())).ReturnsAsync((User?)null);
var result = await _sut.LoginAsync(new LoginRequest("nobody", "pass"));
Assert.Null(result);
_tokenService.Verify(t => t.GenerateToken(It.IsAny<User>()), Times.Never);
}
[Fact]
public async Task LoginAsync_ReturnsNull_WhenUserIsInactive()
{
var user = ActiveUserWithPassword("secret");
user.IsActive = false;
_userRepo.Setup(r => r.GetByUsernameAsync("testuser")).ReturnsAsync(user);
var result = await _sut.LoginAsync(new LoginRequest("testuser", "secret"));
Assert.Null(result);
_tokenService.Verify(t => t.GenerateToken(It.IsAny<User>()), Times.Never);
}
[Fact]
public async Task LoginAsync_ReturnsNull_WhenPasswordIsWrong()
{
var user = ActiveUserWithPassword("correct");
_userRepo.Setup(r => r.GetByUsernameAsync("testuser")).ReturnsAsync(user);
var result = await _sut.LoginAsync(new LoginRequest("testuser", "wrong"));
Assert.Null(result);
_tokenService.Verify(t => t.GenerateToken(It.IsAny<User>()), Times.Never);
}
[Fact]
public async Task LoginAsync_ReturnsLoginResponse_WhenCredentialsAreValid()
{
var user = ActiveUserWithPassword("secret");
_userRepo.Setup(r => r.GetByUsernameAsync("testuser")).ReturnsAsync(user);
_tokenService.Setup(t => t.GenerateToken(user)).Returns("jwt-token");
var result = await _sut.LoginAsync(new LoginRequest("testuser", "secret"));
Assert.NotNull(result);
Assert.Equal("jwt-token", result.Token);
Assert.Equal(user.Id, result.UserId);
Assert.Equal("testuser", result.Username);
Assert.Equal("Sales", result.RoleName);
}
[Fact]
public async Task LoginAsync_CallsGenerateToken_OnSuccess()
{
var user = ActiveUserWithPassword("pass");
_userRepo.Setup(r => r.GetByUsernameAsync(It.IsAny<string>())).ReturnsAsync(user);
_tokenService.Setup(t => t.GenerateToken(user)).Returns("token");
await _sut.LoginAsync(new LoginRequest("testuser", "pass"));
_tokenService.Verify(t => t.GenerateToken(user), Times.Once);
}
}
@@ -0,0 +1,259 @@
using Distribution.Features.Item;
using Distribution.Infra.UoW;
using Distribution.Models;
using Moq;
using Xunit;
namespace Distribution.Tests.Services;
public class ItemServiceTests
{
private readonly Mock<IItemRepository> _repo = new();
private readonly Mock<IUnitOfWork> _uow = new();
private readonly ItemService _sut;
public ItemServiceTests()
{
_sut = new ItemService(_repo.Object, _uow.Object);
}
private static Item ManualItem(string sku = "SKU-001") => new()
{
Id = Guid.NewGuid(),
Sku = sku,
Name = "Test Item",
IsActive = true,
SyncSource = SyncSource.Manual,
FK_CategoryId = Guid.NewGuid(),
FK_BaseUomId = Guid.NewGuid(),
Category = new Category { Name = "Cat" },
BaseUom = new Uom { Name = "Each" },
Conversions = []
};
private static Item ErpItem() => new()
{
Id = Guid.NewGuid(),
Sku = "ERP-001",
Name = "Erp Item",
IsActive = true,
SyncSource = SyncSource.Erp,
FK_CategoryId = Guid.NewGuid(),
FK_BaseUomId = Guid.NewGuid(),
Category = new Category { Name = "Cat" },
BaseUom = new Uom { Name = "Each" },
Conversions = []
};
private static Item InactiveItem() => new()
{
Id = Guid.NewGuid(),
Sku = "OLD-001",
Name = "Old",
IsActive = false,
SyncSource = SyncSource.Manual,
FK_CategoryId = Guid.NewGuid(),
FK_BaseUomId = Guid.NewGuid(),
Category = new Category { Name = "Cat" },
BaseUom = new Uom { Name = "Each" },
Conversions = []
};
private static CreateItemRequest SampleCreateRequest(string sku = "SKU-001") =>
new(sku, "Test Item", null, Guid.NewGuid(), null, null, Guid.NewGuid(), null, null);
// CreateAsync
[Fact]
public async Task CreateAsync_Throws_WhenSkuAlreadyExists()
{
_repo.Setup(r => r.SkuExistsAsync(It.IsAny<string>(), It.IsAny<Guid?>())).ReturnsAsync(true);
await Assert.ThrowsAsync<InvalidOperationException>(
() => _sut.CreateAsync(SampleCreateRequest("DUP")));
_repo.Verify(r => r.AddAsync(It.IsAny<Item>()), Times.Never);
}
[Fact]
public async Task CreateAsync_SetsManualSyncSource()
{
var item = ManualItem();
_repo.Setup(r => r.SkuExistsAsync(It.IsAny<string>(), It.IsAny<Guid?>())).ReturnsAsync(false);
_repo.Setup(r => r.AddAsync(It.IsAny<Item>())).Returns(Task.CompletedTask);
_repo.Setup(r => r.GetByIdAsync(It.IsAny<Guid>())).ReturnsAsync(item);
await _sut.CreateAsync(SampleCreateRequest());
_repo.Verify(r => r.AddAsync(It.Is<Item>(i => i.SyncSource == SyncSource.Manual)), Times.Once);
}
[Fact]
public async Task CreateAsync_BeginsAndCommitsTransaction()
{
var item = ManualItem();
_repo.Setup(r => r.SkuExistsAsync(It.IsAny<string>(), It.IsAny<Guid?>())).ReturnsAsync(false);
_repo.Setup(r => r.AddAsync(It.IsAny<Item>())).Returns(Task.CompletedTask);
_repo.Setup(r => r.GetByIdAsync(It.IsAny<Guid>())).ReturnsAsync(item);
await _sut.CreateAsync(SampleCreateRequest());
_uow.Verify(u => u.BeginAsync(default), Times.Once);
_uow.Verify(u => u.CommitAsync(default), Times.Once);
}
[Fact]
public async Task CreateAsync_Rollbacks_OnAddException()
{
_repo.Setup(r => r.SkuExistsAsync(It.IsAny<string>(), It.IsAny<Guid?>())).ReturnsAsync(false);
_repo.Setup(r => r.AddAsync(It.IsAny<Item>())).ThrowsAsync(new Exception("db error"));
await Assert.ThrowsAsync<Exception>(() => _sut.CreateAsync(SampleCreateRequest()));
_uow.Verify(u => u.RollbackAsync(default), Times.Once);
_uow.Verify(u => u.CommitAsync(default), Times.Never);
}
// UpdateAsync
[Fact]
public async Task UpdateAsync_ReturnsNull_WhenItemNotFound()
{
_repo.Setup(r => r.GetByIdAsync(It.IsAny<Guid>())).ReturnsAsync((Item?)null);
var result = await _sut.UpdateAsync(Guid.NewGuid(),
new UpdateItemRequest("Name", null, Guid.NewGuid(), null, null, Guid.NewGuid(), null));
Assert.Null(result);
_uow.Verify(u => u.BeginAsync(default), Times.Never);
}
[Fact]
public async Task UpdateAsync_ReturnsNull_WhenItemInactive()
{
_repo.Setup(r => r.GetByIdAsync(It.IsAny<Guid>())).ReturnsAsync(InactiveItem());
var result = await _sut.UpdateAsync(Guid.NewGuid(),
new UpdateItemRequest("Name", null, Guid.NewGuid(), null, null, Guid.NewGuid(), null));
Assert.Null(result);
}
[Fact]
public async Task UpdateAsync_ReturnsNull_WhenItemIsErpSynced()
{
_repo.Setup(r => r.GetByIdAsync(It.IsAny<Guid>())).ReturnsAsync(ErpItem());
var result = await _sut.UpdateAsync(Guid.NewGuid(),
new UpdateItemRequest("Name", null, Guid.NewGuid(), null, null, Guid.NewGuid(), null));
Assert.Null(result);
_uow.Verify(u => u.BeginAsync(default), Times.Never);
}
[Fact]
public async Task UpdateAsync_UpdatesFields_AndCommits()
{
var item = ManualItem();
_repo.Setup(r => r.GetByIdAsync(item.Id)).ReturnsAsync(item);
_repo.Setup(r => r.GetByIdAsync(It.IsAny<Guid>())).ReturnsAsync(item);
var catId = Guid.NewGuid();
var uomId = Guid.NewGuid();
item.Category = new Category { Name = "Cat" };
item.BaseUom = new Uom { Name = "Each" };
var result = await _sut.UpdateAsync(item.Id,
new UpdateItemRequest("Updated", "desc", catId, null, null, uomId, 9.99m));
_uow.Verify(u => u.BeginAsync(default), Times.Once);
_uow.Verify(u => u.CommitAsync(default), Times.Once);
}
// DeleteAsync
[Fact]
public async Task DeleteAsync_ReturnsFalse_WhenItemNotFound()
{
_repo.Setup(r => r.GetByIdAsync(It.IsAny<Guid>())).ReturnsAsync((Item?)null);
var result = await _sut.DeleteAsync(Guid.NewGuid());
Assert.False(result);
_uow.Verify(u => u.BeginAsync(default), Times.Never);
}
[Fact]
public async Task DeleteAsync_ReturnsFalse_WhenItemInactive()
{
_repo.Setup(r => r.GetByIdAsync(It.IsAny<Guid>())).ReturnsAsync(InactiveItem());
var result = await _sut.DeleteAsync(Guid.NewGuid());
Assert.False(result);
}
[Fact]
public async Task DeleteAsync_ReturnsFalse_WhenItemIsErpSynced()
{
_repo.Setup(r => r.GetByIdAsync(It.IsAny<Guid>())).ReturnsAsync(ErpItem());
var result = await _sut.DeleteAsync(Guid.NewGuid());
Assert.False(result);
_uow.Verify(u => u.BeginAsync(default), Times.Never);
}
[Fact]
public async Task DeleteAsync_CallsRemove_AndCommits_ForManualItem()
{
var item = ManualItem();
_repo.Setup(r => r.GetByIdAsync(item.Id)).ReturnsAsync(item);
var result = await _sut.DeleteAsync(item.Id);
Assert.True(result);
_repo.Verify(r => r.Remove(item), Times.Once);
_uow.Verify(u => u.CommitAsync(default), Times.Once);
}
// ReplaceConversionsAsync
[Fact]
public async Task ReplaceConversionsAsync_ReturnsNull_WhenItemNotFound()
{
_repo.Setup(r => r.GetByIdAsync(It.IsAny<Guid>())).ReturnsAsync((Item?)null);
var result = await _sut.ReplaceConversionsAsync(Guid.NewGuid(),
new ReplaceConversionsRequest([]));
Assert.Null(result);
}
[Fact]
public async Task ReplaceConversionsAsync_ReturnsNull_WhenItemIsErpSynced()
{
_repo.Setup(r => r.GetByIdAsync(It.IsAny<Guid>())).ReturnsAsync(ErpItem());
var result = await _sut.ReplaceConversionsAsync(Guid.NewGuid(),
new ReplaceConversionsRequest([]));
Assert.Null(result);
_repo.Verify(r => r.ReplaceConversionsAsync(It.IsAny<Guid>(), It.IsAny<List<UomConversion>>()), Times.Never);
}
[Fact]
public async Task ReplaceConversionsAsync_CallsReplace_AndCommits()
{
var item = ManualItem();
_repo.Setup(r => r.GetByIdAsync(item.Id)).ReturnsAsync(item);
_repo.Setup(r => r.ReplaceConversionsAsync(It.IsAny<Guid>(), It.IsAny<List<UomConversion>>()))
.Returns(Task.CompletedTask);
_repo.Setup(r => r.GetByIdAsync(It.IsAny<Guid>())).ReturnsAsync(item);
await _sut.ReplaceConversionsAsync(item.Id, new ReplaceConversionsRequest([]));
_repo.Verify(r => r.ReplaceConversionsAsync(item.Id, It.IsAny<List<UomConversion>>()), Times.Once);
_uow.Verify(u => u.CommitAsync(default), Times.Once);
}
}
@@ -0,0 +1,209 @@
using Distribution.Features.Notifications;
using Distribution.Infra.UoW;
using Microsoft.AspNetCore.SignalR;
using Microsoft.Extensions.Logging.Abstractions;
using Moq;
using Xunit;
namespace Distribution.Tests.Services;
public class NotificationServiceTests
{
private readonly Mock<INotificationRepository> _repo = new();
private readonly Mock<IUnitOfWork> _uow = new();
private readonly Mock<IHubContext<NotificationHub>> _hub = new();
private readonly Mock<IHubClients> _clients = new();
private readonly Mock<IClientProxy> _proxy = new();
private readonly NotificationService _sut;
public NotificationServiceTests()
{
_repo.Setup(r => r.FilterActiveUserIdsAsync(It.IsAny<IEnumerable<Guid>>()))
.ReturnsAsync((IEnumerable<Guid> ids) => ids.ToList());
_hub.SetupGet(h => h.Clients).Returns(_clients.Object);
_clients.Setup(c => c.User(It.IsAny<string>())).Returns(_proxy.Object);
_sut = new NotificationService(
_repo.Object,
new NotificationRecipientResolver(_repo.Object),
_hub.Object,
_uow.Object,
NullLogger<NotificationService>.Instance);
}
private static NotificationArgs Args(Guid? actor = null) =>
new(new { StoreName = "Fort Mini Mart" }, "DeliveryTask", Guid.NewGuid(), "/operations/delivery-tasks/1", actor);
// Registry
[Fact]
public void EveryNotificationTypeConstant_HasADefinition()
{
var constants = typeof(NotificationTypes)
.GetFields()
.Where(f => f.IsLiteral && f.FieldType == typeof(string))
.Select(f => (string)f.GetRawConstantValue()!)
.ToList();
Assert.NotEmpty(constants);
Assert.Equal(constants.Count, constants.Distinct().Count());
Assert.All(constants, c => Assert.True(NotificationTypes.All.ContainsKey(c), $"{c} has no definition"));
}
// NotifyAsync
[Fact]
public async Task NotifyAsync_DoesNothing_WhenTypeIsNotRegistered()
{
await _sut.NotifyAsync("NotARealType", [RecipientRule.Users(Guid.NewGuid())], Args());
_repo.Verify(r => r.AddAsync(It.IsAny<Models.Notification>()), Times.Never);
_uow.Verify(u => u.CommitAsync(It.IsAny<CancellationToken>()), Times.Never);
}
[Fact]
public async Task NotifyAsync_DoesNothing_WhenThereAreNoRecipients()
{
await _sut.NotifyAsync(NotificationTypes.DeliveryTaskFailed, [RecipientRule.Users()], Args());
_repo.Verify(r => r.AddAsync(It.IsAny<Models.Notification>()), Times.Never);
}
[Fact]
public async Task NotifyAsync_ExcludesTheActingUser()
{
var actor = Guid.NewGuid();
var other = Guid.NewGuid();
Models.Notification? saved = null;
_repo.Setup(r => r.AddAsync(It.IsAny<Models.Notification>()))
.Callback<Models.Notification>(n => saved = n)
.Returns(Task.CompletedTask);
await _sut.NotifyAsync(NotificationTypes.DeliveryTaskFailed,
[RecipientRule.Users(actor, other)], Args(actor));
Assert.NotNull(saved);
var recipient = Assert.Single(saved!.Recipients);
Assert.Equal(other, recipient.FK_UserId);
}
[Fact]
public async Task NotifyAsync_RendersTemplate_CommitsAndPushesToEachRecipient()
{
var a = Guid.NewGuid();
var b = Guid.NewGuid();
Models.Notification? saved = null;
_repo.Setup(r => r.AddAsync(It.IsAny<Models.Notification>()))
.Callback<Models.Notification>(n => saved = n)
.Returns(Task.CompletedTask);
await _sut.NotifyAsync(NotificationTypes.DeliveryTaskFailed, [RecipientRule.Users(a, b)], Args());
Assert.Equal("Delivery to Fort Mini Mart failed.", saved!.Message);
Assert.Equal(Models.NotificationSeverity.Warning, saved.Severity);
_uow.Verify(u => u.CommitAsync(It.IsAny<CancellationToken>()), Times.Once);
_clients.Verify(c => c.User(a.ToString()), Times.Once);
_clients.Verify(c => c.User(b.ToString()), Times.Once);
_proxy.Verify(p => p.SendCoreAsync(NotificationHub.ClientEvent,
It.IsAny<object?[]>(), It.IsAny<CancellationToken>()), Times.Exactly(2));
}
[Fact]
public async Task NotifyAsync_DoesNotThrow_WhenSavingFails_AndDiscardsTheEntity()
{
_uow.Setup(u => u.CommitAsync(It.IsAny<CancellationToken>())).ThrowsAsync(new InvalidOperationException("db down"));
var ex = await Record.ExceptionAsync(() =>
_sut.NotifyAsync(NotificationTypes.DeliveryTaskFailed, [RecipientRule.Users(Guid.NewGuid())], Args()));
Assert.Null(ex);
_repo.Verify(r => r.Discard(It.IsAny<Models.Notification>()), Times.Once);
_proxy.Verify(p => p.SendCoreAsync(It.IsAny<string>(), It.IsAny<object?[]>(), It.IsAny<CancellationToken>()), Times.Never);
}
[Fact]
public async Task NotifyAsync_DoesNotThrow_WhenPushFails_AndKeepsTheSavedNotification()
{
_proxy.Setup(p => p.SendCoreAsync(It.IsAny<string>(), It.IsAny<object?[]>(), It.IsAny<CancellationToken>()))
.ThrowsAsync(new InvalidOperationException("socket gone"));
var ex = await Record.ExceptionAsync(() =>
_sut.NotifyAsync(NotificationTypes.DeliveryTaskFailed, [RecipientRule.Users(Guid.NewGuid())], Args()));
Assert.Null(ex);
_uow.Verify(u => u.CommitAsync(It.IsAny<CancellationToken>()), Times.Once);
_repo.Verify(r => r.Discard(It.IsAny<Models.Notification>()), Times.Never);
}
// Recipient rules
[Fact]
public async Task PermissionInScope_ResolvesToTheIntersectionOfPermissionAndScope()
{
var both = Guid.NewGuid();
var permissionOnly = Guid.NewGuid();
var scopeOnly = Guid.NewGuid();
var storeId = Guid.NewGuid();
_repo.Setup(r => r.GetUserIdsByPermissionAsync("store.manage")).ReturnsAsync([both, permissionOnly]);
_repo.Setup(r => r.GetUserIdsByScopeAsync(ScopeKind.Store, storeId)).ReturnsAsync([both, scopeOnly]);
var ids = await new NotificationRecipientResolver(_repo.Object)
.ResolveAsync([RecipientRule.PermissionInScope("store.manage", ScopeKind.Store, storeId)], actingUserId: null);
Assert.Equal([both], ids);
}
[Fact]
public async Task ReportsTo_ResolvesToTheSupervisor_AndIsEmptyWhenThereIsNone()
{
var user = Guid.NewGuid();
var supervisor = Guid.NewGuid();
var resolver = new NotificationRecipientResolver(_repo.Object);
_repo.Setup(r => r.GetReportsToUserIdAsync(user)).ReturnsAsync(supervisor);
Assert.Equal([supervisor], await resolver.ResolveAsync([RecipientRule.ReportsTo(user)], null));
_repo.Setup(r => r.GetReportsToUserIdAsync(user)).ReturnsAsync((Guid?)null);
Assert.Empty(await resolver.ResolveAsync([RecipientRule.ReportsTo(user)], null));
}
[Fact]
public async Task RouteOwner_ResolvesToTheOwner_AndIsEmptyWhenTheRouteHasNone()
{
var storeId = Guid.NewGuid();
var owner = Guid.NewGuid();
var resolver = new NotificationRecipientResolver(_repo.Object);
_repo.Setup(r => r.GetRouteOwnerUserIdAsync(ScopeKind.Store, storeId)).ReturnsAsync(owner);
Assert.Equal([owner], await resolver.ResolveAsync([RecipientRule.RouteOwner(ScopeKind.Store, storeId)], null));
_repo.Setup(r => r.GetRouteOwnerUserIdAsync(ScopeKind.Store, storeId)).ReturnsAsync((Guid?)null);
Assert.Empty(await resolver.ResolveAsync([RecipientRule.RouteOwner(ScopeKind.Store, storeId)], null));
}
[Fact]
public async Task OptionalTokenAtTheEndOfATemplate_LeavesNoTrailingSpace()
{
Models.Notification? saved = null;
_repo.Setup(r => r.AddAsync(It.IsAny<Models.Notification>()))
.Callback<Models.Notification>(n => saved = n)
.Returns(Task.CompletedTask);
await _sut.NotifyAsync(NotificationTypes.DeliveryTaskFailed, [RecipientRule.Users(Guid.NewGuid())],
new NotificationArgs(new { StoreName = "Fort Mini Mart", Note = (string?)null }, "DeliveryTask", Guid.NewGuid(), "/x"));
Assert.Equal("Delivery to Fort Mini Mart failed.", saved!.Message);
}
[Fact]
public async Task Resolve_DeduplicatesUsersAcrossRules()
{
var user = Guid.NewGuid();
_repo.Setup(r => r.GetUserIdsByPermissionAsync("stock.manage")).ReturnsAsync([user]);
var ids = await new NotificationRecipientResolver(_repo.Object)
.ResolveAsync([RecipientRule.Users(user), RecipientRule.Permission("stock.manage")], null);
Assert.Equal([user], ids);
}
}
@@ -0,0 +1,229 @@
using Distribution.Features.Order;
using Distribution.Infra.Authorization;
using Distribution.Infra.UoW;
using Distribution.Models;
using Moq;
using Xunit;
namespace Distribution.Tests.Services;
public class OrderServiceTests
{
private readonly Mock<IOrderRepository> _repo = new();
private readonly Mock<IUnitOfWork> _uow = new();
private readonly Mock<ICurrentUserService> _currentUser = new();
private readonly Mock<IScopeAccessService> _scopeAccess = new();
private readonly OrderService _sut;
public OrderServiceTests()
{
_sut = new OrderService(_repo.Object, _uow.Object, _currentUser.Object, _scopeAccess.Object);
}
private static Order ActiveOrder(Guid storeId) => new()
{
Id = Guid.NewGuid(),
FK_StoreId = storeId,
OrderDate = DateTime.Now,
TotalAmount = 100,
IsActive = true
};
private void SetupGlobalUser(Guid userId)
{
_currentUser.Setup(u => u.UserId).Returns(userId);
_currentUser.Setup(u => u.RoleLevel).Returns(50);
}
private void SetupScopedUser(Guid userId)
{
_currentUser.Setup(u => u.UserId).Returns(userId);
_currentUser.Setup(u => u.RoleLevel).Returns(10);
}
// GetAllAsync
[Fact]
public async Task GetAllAsync_GlobalUser_ReturnsAllActiveOrders()
{
var userId = Guid.NewGuid();
var storeA = Guid.NewGuid();
var storeB = Guid.NewGuid();
SetupGlobalUser(userId);
var orders = new List<Order> { ActiveOrder(storeA), ActiveOrder(storeB) };
_repo.Setup(r => r.GetAllAsync()).ReturnsAsync(orders);
_repo.Setup(r => r.GetItemsByOrderIdAsync(It.IsAny<Guid>())).ReturnsAsync([]);
var result = await _sut.GetAllAsync();
Assert.Equal(2, result.Count);
}
[Fact]
public async Task GetAllAsync_ScopedUser_FiltersToAccessibleStores()
{
var userId = Guid.NewGuid();
var accessibleStore = Guid.NewGuid();
var restrictedStore = Guid.NewGuid();
SetupScopedUser(userId);
_scopeAccess.Setup(s => s.GetAccessibleStoreIdsAsync(userId)).ReturnsAsync([accessibleStore]);
var orders = new List<Order> { ActiveOrder(accessibleStore), ActiveOrder(restrictedStore) };
_repo.Setup(r => r.GetAllAsync()).ReturnsAsync(orders);
_repo.Setup(r => r.GetItemsByOrderIdAsync(It.IsAny<Guid>())).ReturnsAsync([]);
var result = await _sut.GetAllAsync();
Assert.Single(result);
Assert.Equal(accessibleStore, result[0].StoreId);
}
[Fact]
public async Task GetAllAsync_FiltersOutInactiveOrders()
{
var userId = Guid.NewGuid();
SetupGlobalUser(userId);
var inactiveOrder = ActiveOrder(Guid.NewGuid());
inactiveOrder.IsActive = false;
_repo.Setup(r => r.GetAllAsync()).ReturnsAsync([inactiveOrder]);
_repo.Setup(r => r.GetItemsByOrderIdAsync(It.IsAny<Guid>())).ReturnsAsync([]);
var result = await _sut.GetAllAsync();
Assert.Empty(result);
}
// GetByIdAsync
[Fact]
public async Task GetByIdAsync_ReturnsNull_WhenOrderNotFound()
{
SetupGlobalUser(Guid.NewGuid());
_repo.Setup(r => r.GetByIdAsync(It.IsAny<Guid>())).ReturnsAsync((Order?)null);
var result = await _sut.GetByIdAsync(Guid.NewGuid());
Assert.Null(result);
}
[Fact]
public async Task GetByIdAsync_ReturnsNull_WhenUserLacksStoreScope()
{
var userId = Guid.NewGuid();
var storeId = Guid.NewGuid();
SetupScopedUser(userId);
_repo.Setup(r => r.GetByIdAsync(It.IsAny<Guid>())).ReturnsAsync(ActiveOrder(storeId));
_scopeAccess.Setup(s => s.HasStoreScopeAsync(userId, storeId)).ReturnsAsync(false);
var result = await _sut.GetByIdAsync(Guid.NewGuid());
Assert.Null(result);
}
[Fact]
public async Task GetByIdAsync_ReturnsOrder_WhenGlobalUser()
{
var userId = Guid.NewGuid();
var order = ActiveOrder(Guid.NewGuid());
SetupGlobalUser(userId);
_repo.Setup(r => r.GetByIdAsync(order.Id)).ReturnsAsync(order);
_repo.Setup(r => r.GetItemsByOrderIdAsync(order.Id)).ReturnsAsync([]);
var result = await _sut.GetByIdAsync(order.Id);
Assert.NotNull(result);
Assert.Equal(order.Id, result.Id);
}
// CreateAsync
[Fact]
public async Task CreateAsync_ReturnsNull_WhenUserLacksStoreScope()
{
var userId = Guid.NewGuid();
var storeId = Guid.NewGuid();
SetupScopedUser(userId);
_scopeAccess.Setup(s => s.HasStoreScopeAsync(userId, storeId)).ReturnsAsync(false);
var result = await _sut.CreateAsync(
new CreateOrderRequest(storeId, DateTime.Now, 100, []));
Assert.Null(result);
_uow.Verify(u => u.BeginAsync(default), Times.Never);
}
[Fact]
public async Task CreateAsync_CreatesOrder_WhenGlobalUser()
{
var userId = Guid.NewGuid();
var storeId = Guid.NewGuid();
SetupGlobalUser(userId);
_repo.Setup(r => r.AddAsync(It.IsAny<Order>())).Returns(Task.CompletedTask);
_repo.Setup(r => r.AddItemsAsync(It.IsAny<IEnumerable<OrderItem>>())).Returns(Task.CompletedTask);
var result = await _sut.CreateAsync(
new CreateOrderRequest(storeId, DateTime.Now, 250, []));
Assert.NotNull(result);
Assert.Equal(storeId, result.StoreId);
Assert.Equal(250, result.TotalAmount);
_uow.Verify(u => u.BeginAsync(default), Times.Once);
_uow.Verify(u => u.CommitAsync(default), Times.Once);
}
[Fact]
public async Task CreateAsync_Rollbacks_OnException()
{
var userId = Guid.NewGuid();
SetupGlobalUser(userId);
_repo.Setup(r => r.AddAsync(It.IsAny<Order>())).ThrowsAsync(new Exception("db error"));
await Assert.ThrowsAsync<Exception>(() =>
_sut.CreateAsync(new CreateOrderRequest(Guid.NewGuid(), DateTime.Now, 100, [])));
_uow.Verify(u => u.RollbackAsync(default), Times.Once);
_uow.Verify(u => u.CommitAsync(default), Times.Never);
}
// DeleteAsync
[Fact]
public async Task DeleteAsync_ReturnsFalse_WhenOrderNotFound()
{
SetupGlobalUser(Guid.NewGuid());
_repo.Setup(r => r.GetByIdAsync(It.IsAny<Guid>())).ReturnsAsync((Order?)null);
var result = await _sut.DeleteAsync(Guid.NewGuid());
Assert.False(result);
}
[Fact]
public async Task DeleteAsync_ReturnsFalse_WhenUserLacksStoreScope()
{
var userId = Guid.NewGuid();
var storeId = Guid.NewGuid();
SetupScopedUser(userId);
_repo.Setup(r => r.GetByIdAsync(It.IsAny<Guid>())).ReturnsAsync(ActiveOrder(storeId));
_scopeAccess.Setup(s => s.HasStoreScopeAsync(userId, storeId)).ReturnsAsync(false);
var result = await _sut.DeleteAsync(Guid.NewGuid());
Assert.False(result);
_uow.Verify(u => u.BeginAsync(default), Times.Never);
}
[Fact]
public async Task DeleteAsync_RemovesOrder_WhenAuthorized()
{
var userId = Guid.NewGuid();
var order = ActiveOrder(Guid.NewGuid());
SetupGlobalUser(userId);
_repo.Setup(r => r.GetByIdAsync(order.Id)).ReturnsAsync(order);
var result = await _sut.DeleteAsync(order.Id);
Assert.True(result);
_repo.Verify(r => r.Remove(order), Times.Once);
_uow.Verify(u => u.CommitAsync(default), Times.Once);
}
}
@@ -0,0 +1,405 @@
using Distribution.Features.Region;
using Distribution.Infra.Authorization;
using Distribution.Infra.UoW;
using Distribution.Models;
using Moq;
using Xunit;
namespace Distribution.Tests.Services;
public class RegionServiceTests
{
private readonly Mock<IRegionRepository> _repo = new();
private readonly Mock<IUnitOfWork> _uow = new();
private readonly Mock<ICurrentUserService> _currentUser = new();
private readonly Mock<IScopeAccessService> _scopeAccess = new();
private readonly RegionService _sut;
public RegionServiceTests()
{
_currentUser.Setup(u => u.RoleLevel).Returns(60);
_repo.Setup(r => r.GetAllCodesAsync()).ReturnsAsync([]);
_sut = new RegionService(_repo.Object, _uow.Object, _currentUser.Object, _scopeAccess.Object);
}
private static Region ActiveRegion(string name = "North") =>
new() { Id = Guid.NewGuid(), Name = name, IsActive = true };
private static Region InactiveRegion() =>
new() { Id = Guid.NewGuid(), Name = "Old", IsActive = false };
// GetAllAsync
[Fact]
public async Task GetAllAsync_ReturnsOnlyActiveRegions()
{
_repo.Setup(r => r.GetAllAsync()).ReturnsAsync([ActiveRegion(), InactiveRegion()]);
var result = await _sut.GetAllAsync();
Assert.Single(result);
Assert.True(result[0].IsActive);
}
[Fact]
public async Task GetAllAsync_ReturnsEmpty_WhenNoneActive()
{
_repo.Setup(r => r.GetAllAsync()).ReturnsAsync([InactiveRegion()]);
var result = await _sut.GetAllAsync();
Assert.Empty(result);
}
// GetByIdAsync
[Fact]
public async Task GetByIdAsync_ReturnsNull_WhenNotFound()
{
_repo.Setup(r => r.GetByIdAsync(It.IsAny<Guid>())).ReturnsAsync((Region?)null);
var result = await _sut.GetByIdAsync(Guid.NewGuid());
Assert.Null(result);
}
[Fact]
public async Task GetByIdAsync_ReturnsNull_WhenInactive()
{
_repo.Setup(r => r.GetByIdAsync(It.IsAny<Guid>())).ReturnsAsync(InactiveRegion());
var result = await _sut.GetByIdAsync(Guid.NewGuid());
Assert.Null(result);
}
[Fact]
public async Task GetByIdAsync_ReturnsResponse_WhenActiveRegionFound()
{
var region = ActiveRegion("South");
_repo.Setup(r => r.GetByIdAsync(region.Id)).ReturnsAsync(region);
var result = await _sut.GetByIdAsync(region.Id);
Assert.NotNull(result);
Assert.Equal("South", result.Name);
Assert.Equal(region.Id, result.Id);
}
// CreateAsync
[Fact]
public async Task CreateAsync_AddsRegion_AndCommits()
{
var request = new CreateRegionRequest("East");
var result = await _sut.CreateAsync(request);
_uow.Verify(u => u.BeginAsync(default), Times.Once);
_repo.Verify(r => r.AddAsync(It.Is<Region>(x => x.Name == "East")), Times.Once);
_uow.Verify(u => u.CommitAsync(default), Times.Once);
Assert.Equal("East", result.Name);
}
[Fact]
public async Task CreateAsync_Rollbacks_WhenAddThrows()
{
_repo.Setup(r => r.AddAsync(It.IsAny<Region>())).ThrowsAsync(new Exception("db error"));
await Assert.ThrowsAsync<Exception>(() => _sut.CreateAsync(new CreateRegionRequest("X")));
_uow.Verify(u => u.RollbackAsync(default), Times.Once);
_uow.Verify(u => u.CommitAsync(default), Times.Never);
}
// UpdateAsync
[Fact]
public async Task UpdateAsync_ReturnsNull_WhenNotFound()
{
_repo.Setup(r => r.GetByIdAsync(It.IsAny<Guid>())).ReturnsAsync((Region?)null);
var result = await _sut.UpdateAsync(Guid.NewGuid(), new UpdateRegionRequest("New"));
Assert.Null(result);
_uow.Verify(u => u.BeginAsync(default), Times.Never);
}
[Fact]
public async Task UpdateAsync_ReturnsNull_WhenInactive()
{
_repo.Setup(r => r.GetByIdAsync(It.IsAny<Guid>())).ReturnsAsync(InactiveRegion());
var result = await _sut.UpdateAsync(Guid.NewGuid(), new UpdateRegionRequest("New"));
Assert.Null(result);
}
[Fact]
public async Task UpdateAsync_UpdatesName_AndCommits()
{
var region = ActiveRegion("Old Name");
_repo.Setup(r => r.GetByIdAsync(region.Id)).ReturnsAsync(region);
var result = await _sut.UpdateAsync(region.Id, new UpdateRegionRequest("New Name"));
Assert.NotNull(result);
Assert.Equal("New Name", result.Name);
_uow.Verify(u => u.BeginAsync(default), Times.Once);
_uow.Verify(u => u.CommitAsync(default), Times.Once);
}
// Region code
[Fact]
public async Task CreateAsync_GeneratesFirstCode_WhenNoRegionsExist()
{
_repo.Setup(r => r.GetAllCodesAsync()).ReturnsAsync([]);
var result = await _sut.CreateAsync(new CreateRegionRequest("North"));
Assert.Equal("REG-0001", result.Code);
}
[Fact]
public async Task CreateAsync_GeneratesNextCode_FromHighestExisting_IgnoringNonMatching()
{
_repo.Setup(r => r.GetAllCodesAsync()).ReturnsAsync(["REG-0002", "REG-0009", "CUSTOM", "REG-abc", ""]);
var result = await _sut.CreateAsync(new CreateRegionRequest("North"));
Assert.Equal("REG-0010", result.Code);
}
[Fact]
public async Task CreateAsync_StoresProvidedCode_AsEntered()
{
var result = await _sut.CreateAsync(new CreateRegionRequest("North", " nth-1 "));
Assert.Equal("nth-1", result.Code);
_repo.Verify(r => r.GetAllCodesAsync(), Times.Never);
}
[Fact]
public async Task CreateAsync_Throws_WhenProvidedCodeInUse()
{
_repo.Setup(r => r.CodeExistsAsync("NTH-1", null)).ReturnsAsync(true);
await Assert.ThrowsAsync<InvalidOperationException>(() =>
_sut.CreateAsync(new CreateRegionRequest("North", "NTH-1")));
_uow.Verify(u => u.CommitAsync(default), Times.Never);
}
[Fact]
public async Task GetNextCodeAsync_ReturnsSuggestion()
{
_repo.Setup(r => r.GetAllCodesAsync()).ReturnsAsync(["REG-0003"]);
var result = await _sut.GetNextCodeAsync();
Assert.Equal("REG-0004", result.Code);
}
[Fact]
public async Task UpdateAsync_ChangesCode_StoredAsEntered()
{
var region = ActiveRegion();
region.Code = "REG-0001";
_repo.Setup(r => r.GetByIdAsync(region.Id)).ReturnsAsync(region);
_repo.Setup(r => r.CodeExistsAsync("north-1", region.Id)).ReturnsAsync(false);
var result = await _sut.UpdateAsync(region.Id, new UpdateRegionRequest("North", " north-1 "));
Assert.Equal("north-1", result!.Code);
_uow.Verify(u => u.CommitAsync(default), Times.Once);
}
[Fact]
public async Task UpdateAsync_KeepsCode_WhenCodeBlank()
{
var region = ActiveRegion();
region.Code = "REG-0001";
_repo.Setup(r => r.GetByIdAsync(region.Id)).ReturnsAsync(region);
var result = await _sut.UpdateAsync(region.Id, new UpdateRegionRequest("North", " "));
Assert.Equal("REG-0001", result!.Code);
_repo.Verify(r => r.CodeExistsAsync(It.IsAny<string>(), It.IsAny<Guid?>()), Times.Never);
}
[Fact]
public async Task UpdateAsync_Throws_WhenCodeUsedByAnotherRegion()
{
var region = ActiveRegion();
region.Code = "REG-0001";
_repo.Setup(r => r.GetByIdAsync(region.Id)).ReturnsAsync(region);
_repo.Setup(r => r.CodeExistsAsync("REG-0002", region.Id)).ReturnsAsync(true);
await Assert.ThrowsAsync<InvalidOperationException>(() =>
_sut.UpdateAsync(region.Id, new UpdateRegionRequest("North", "REG-0002")));
_uow.Verify(u => u.CommitAsync(default), Times.Never);
}
[Fact]
public async Task UpdateAsync_AllowsResubmittingOwnCode()
{
var region = ActiveRegion();
region.Code = "REG-0001";
_repo.Setup(r => r.GetByIdAsync(region.Id)).ReturnsAsync(region);
var result = await _sut.UpdateAsync(region.Id, new UpdateRegionRequest("North", "REG-0001"));
Assert.Equal("REG-0001", result!.Code);
}
[Fact]
public async Task GetByIdAsync_ReturnsCode()
{
var region = ActiveRegion();
region.Code = "REG-0007";
_repo.Setup(r => r.GetByIdAsync(region.Id)).ReturnsAsync(region);
var result = await _sut.GetByIdAsync(region.Id);
Assert.Equal("REG-0007", result!.Code);
}
// DeleteAsync
[Fact]
public async Task DeleteAsync_ReturnsFalse_WhenNotFound()
{
_repo.Setup(r => r.GetByIdAsync(It.IsAny<Guid>())).ReturnsAsync((Region?)null);
var result = await _sut.DeleteAsync(Guid.NewGuid());
Assert.False(result);
_uow.Verify(u => u.BeginAsync(default), Times.Never);
}
[Fact]
public async Task DeleteAsync_ReturnsFalse_WhenInactive()
{
_repo.Setup(r => r.GetByIdAsync(It.IsAny<Guid>())).ReturnsAsync(InactiveRegion());
var result = await _sut.DeleteAsync(Guid.NewGuid());
Assert.False(result);
}
[Fact]
public async Task DeleteAsync_CallsRemove_AndCommits()
{
var region = ActiveRegion();
_repo.Setup(r => r.GetByIdAsync(region.Id)).ReturnsAsync(region);
var result = await _sut.DeleteAsync(region.Id);
Assert.True(result);
_repo.Verify(r => r.Remove(region), Times.Once);
_uow.Verify(u => u.CommitAsync(default), Times.Once);
}
[Fact]
public async Task DeleteAsync_Rollbacks_WhenRemoveThrows()
{
var region = ActiveRegion();
_repo.Setup(r => r.GetByIdAsync(region.Id)).ReturnsAsync(region);
_repo.Setup(r => r.Remove(It.IsAny<Region>())).Throws(new Exception("db error"));
await Assert.ThrowsAsync<Exception>(() => _sut.DeleteAsync(region.Id));
_uow.Verify(u => u.RollbackAsync(default), Times.Once);
}
// Region manager
private static User ManagerUser(string role = "RegionalManager", bool active = true) => new()
{
Id = Guid.NewGuid(), Username = "mgr", FullName = "Mgr One", IsActive = active,
Role = new Role { Name = role, Level = 50 }
};
[Fact]
public async Task SetManagerAsync_ReturnsNull_WhenRegionMissing()
{
_repo.Setup(r => r.GetByIdAsync(It.IsAny<Guid>())).ReturnsAsync((Region?)null);
Assert.Null(await _sut.SetManagerAsync(Guid.NewGuid(), new SetRegionManagerRequest(Guid.NewGuid())));
}
[Fact]
public async Task SetManagerAsync_Throws_WhenUserNotRegionalManager()
{
var region = ActiveRegion();
var user = ManagerUser("SalesRep");
_repo.Setup(r => r.GetByIdAsync(region.Id)).ReturnsAsync(region);
_repo.Setup(r => r.GetUserWithRoleAsync(user.Id)).ReturnsAsync(user);
await Assert.ThrowsAsync<InvalidOperationException>(() =>
_sut.SetManagerAsync(region.Id, new SetRegionManagerRequest(user.Id)));
_uow.Verify(u => u.CommitAsync(default), Times.Never);
}
[Fact]
public async Task SetManagerAsync_ReplacesExistingManager_AndAddsScope()
{
var region = ActiveRegion();
var user = ManagerUser();
var old = new UserRegionScope { FK_UserId = Guid.NewGuid(), FK_RegionId = region.Id };
_repo.Setup(r => r.GetByIdAsync(region.Id)).ReturnsAsync(region);
_repo.Setup(r => r.GetUserWithRoleAsync(user.Id)).ReturnsAsync(user);
_repo.Setup(r => r.GetManagerScopesAsync(region.Id)).ReturnsAsync([old]);
_repo.Setup(r => r.GetRegionScopeAsync(user.Id, region.Id)).ReturnsAsync((UserRegionScope?)null);
var result = await _sut.SetManagerAsync(region.Id, new SetRegionManagerRequest(user.Id));
Assert.Equal(user.Id, result!.UserId);
_repo.Verify(r => r.RemoveRegionScopes(It.Is<IEnumerable<UserRegionScope>>(s => s.Single() == old)), Times.Once);
_repo.Verify(r => r.AddRegionScopeAsync(It.Is<UserRegionScope>(s => s.FK_UserId == user.Id && s.FK_RegionId == region.Id)), Times.Once);
_uow.Verify(u => u.CommitAsync(default), Times.Once);
}
[Fact]
public async Task SetManagerAsync_DoesNotDuplicateScope_WhenAlreadyManager()
{
var region = ActiveRegion();
var user = ManagerUser();
var existing = new UserRegionScope { FK_UserId = user.Id, FK_RegionId = region.Id };
_repo.Setup(r => r.GetByIdAsync(region.Id)).ReturnsAsync(region);
_repo.Setup(r => r.GetUserWithRoleAsync(user.Id)).ReturnsAsync(user);
_repo.Setup(r => r.GetManagerScopesAsync(region.Id)).ReturnsAsync([existing]);
_repo.Setup(r => r.GetRegionScopeAsync(user.Id, region.Id)).ReturnsAsync(existing);
await _sut.SetManagerAsync(region.Id, new SetRegionManagerRequest(user.Id));
_repo.Verify(r => r.AddRegionScopeAsync(It.IsAny<UserRegionScope>()), Times.Never);
_repo.Verify(r => r.RemoveRegionScopes(It.Is<IEnumerable<UserRegionScope>>(s => !s.Any())), Times.Once);
}
[Fact]
public async Task RemoveManagerAsync_ReturnsFalse_WhenNoManager()
{
var region = ActiveRegion();
_repo.Setup(r => r.GetByIdAsync(region.Id)).ReturnsAsync(region);
_repo.Setup(r => r.GetManagerScopesAsync(region.Id)).ReturnsAsync([]);
Assert.False(await _sut.RemoveManagerAsync(region.Id));
}
[Fact]
public async Task GetManagerAsync_ReturnsManager()
{
var region = ActiveRegion();
var user = ManagerUser();
_repo.Setup(r => r.GetByIdAsync(region.Id)).ReturnsAsync(region);
_repo.Setup(r => r.GetManagerScopesAsync(region.Id))
.ReturnsAsync([new UserRegionScope { FK_UserId = user.Id, FK_RegionId = region.Id, User = user }]);
var result = await _sut.GetManagerAsync(region.Id);
Assert.Equal("mgr", result!.Username);
}
}
@@ -0,0 +1,343 @@
using Distribution.Features.Region;
using Distribution.Features.Route;
using Distribution.Infra.Authorization;
using Distribution.Infra.UoW;
using Distribution.Models;
using Moq;
using Xunit;
namespace Distribution.Tests.Services;
public class RouteServiceTests
{
private readonly Mock<IRouteRepository> _repo = new();
private readonly Mock<IRegionRepository> _regionRepo = new();
private readonly Mock<IUnitOfWork> _uow = new();
private readonly Mock<ICurrentUserService> _currentUser = new();
private readonly Mock<IScopeAccessService> _scopeAccess = new();
private readonly RouteService _sut;
private static readonly Guid RepUserId = Guid.NewGuid();
public RouteServiceTests()
{
_currentUser.Setup(u => u.RoleLevel).Returns(20);
_currentUser.Setup(u => u.UserId).Returns(RepUserId);
_sut = new RouteService(_repo.Object, _regionRepo.Object, _uow.Object, _currentUser.Object, _scopeAccess.Object);
}
private static Models.Route ActiveRoute(Guid? ownerId = null) =>
new() { Id = Guid.NewGuid(), RouteName = "R1", StartPoint = "A", EndPoint = "B", Length = 10, IsActive = true, FK_OwnerUserId = ownerId };
private static Models.Route InactiveRoute() =>
new() { Id = Guid.NewGuid(), RouteName = "Old", StartPoint = "A", EndPoint = "B", Length = 10, IsActive = false };
private static Store ActiveStore(Guid routeId, decimal lat = 10m, decimal lon = 20m) =>
new() { Id = Guid.NewGuid(), FK_RouteId = routeId, IsActive = true, Latitude = lat, Longitude = lon };
// AssignStoresToRouteAsync
[Fact]
public async Task AssignStoresToRouteAsync_ReturnsNull_WhenRouteNotFound()
{
_repo.Setup(r => r.GetByIdAsync(It.IsAny<Guid>())).ReturnsAsync((Models.Route?)null);
var result = await _sut.AssignStoresToRouteAsync(Guid.NewGuid(), new AssignStoresToRouteRequest([Guid.NewGuid()]));
Assert.Null(result);
_uow.Verify(u => u.BeginAsync(default), Times.Never);
}
[Fact]
public async Task AssignStoresToRouteAsync_ReturnsNull_WhenRouteInactive()
{
_repo.Setup(r => r.GetByIdAsync(It.IsAny<Guid>())).ReturnsAsync(InactiveRoute());
var result = await _sut.AssignStoresToRouteAsync(Guid.NewGuid(), new AssignStoresToRouteRequest([Guid.NewGuid()]));
Assert.Null(result);
}
[Fact]
public async Task AssignStoresToRouteAsync_ReturnsNull_WhenCallerLacksRouteScope()
{
var route = ActiveRoute();
_repo.Setup(r => r.GetByIdAsync(route.Id)).ReturnsAsync(route);
_scopeAccess.Setup(s => s.HasRouteScopeAsync(RepUserId, route.Id)).ReturnsAsync(false);
var result = await _sut.AssignStoresToRouteAsync(route.Id, new AssignStoresToRouteRequest([Guid.NewGuid()]));
Assert.Null(result);
_uow.Verify(u => u.BeginAsync(default), Times.Never);
}
[Fact]
public async Task AssignStoresToRouteAsync_ReturnsNull_WhenCallerBelowRoleFloor()
{
_currentUser.Setup(u => u.RoleLevel).Returns(10);
var route = ActiveRoute();
_repo.Setup(r => r.GetByIdAsync(route.Id)).ReturnsAsync(route);
var result = await _sut.AssignStoresToRouteAsync(route.Id, new AssignStoresToRouteRequest([Guid.NewGuid()]));
Assert.Null(result);
}
[Fact]
public async Task AssignStoresToRouteAsync_ReturnsNull_WhenAnyStoreIdMissing()
{
var route = ActiveRoute();
_repo.Setup(r => r.GetByIdAsync(route.Id)).ReturnsAsync(route);
_scopeAccess.Setup(s => s.HasRouteScopeAsync(RepUserId, route.Id)).ReturnsAsync(true);
var storeIds = new List<Guid> { Guid.NewGuid(), Guid.NewGuid() };
_repo.Setup(r => r.GetStoresByIdsAsync(storeIds)).ReturnsAsync([ActiveStore(route.Id)]); // only one of two found
var result = await _sut.AssignStoresToRouteAsync(route.Id, new AssignStoresToRouteRequest(storeIds));
Assert.Null(result);
_uow.Verify(u => u.BeginAsync(default), Times.Never);
}
[Fact]
public async Task AssignStoresToRouteAsync_UpdatesEachStore_AndCommits()
{
var route = ActiveRoute();
var otherRouteId = Guid.NewGuid();
var stores = new List<Store> { ActiveStore(otherRouteId), ActiveStore(otherRouteId) };
var storeIds = stores.Select(s => s.Id).ToList();
_repo.Setup(r => r.GetByIdAsync(route.Id)).ReturnsAsync(route);
_scopeAccess.Setup(s => s.HasRouteScopeAsync(RepUserId, route.Id)).ReturnsAsync(true);
_repo.Setup(r => r.GetStoresByIdsAsync(storeIds)).ReturnsAsync(stores);
var result = await _sut.AssignStoresToRouteAsync(route.Id, new AssignStoresToRouteRequest(storeIds));
Assert.NotNull(result);
Assert.All(stores, s => Assert.Equal(route.Id, s.FK_RouteId));
_repo.Verify(r => r.UpdateStore(It.IsAny<Store>()), Times.Exactly(2));
_uow.Verify(u => u.CommitAsync(default), Times.Once);
}
[Fact]
public async Task AssignStoresToRouteAsync_Rollbacks_WhenUpdateThrows()
{
var route = ActiveRoute();
var stores = new List<Store> { ActiveStore(route.Id) };
var storeIds = stores.Select(s => s.Id).ToList();
_repo.Setup(r => r.GetByIdAsync(route.Id)).ReturnsAsync(route);
_scopeAccess.Setup(s => s.HasRouteScopeAsync(RepUserId, route.Id)).ReturnsAsync(true);
_repo.Setup(r => r.GetStoresByIdsAsync(storeIds)).ReturnsAsync(stores);
_repo.Setup(r => r.UpdateStore(It.IsAny<Store>())).Throws(new Exception("db error"));
await Assert.ThrowsAsync<Exception>(() => _sut.AssignStoresToRouteAsync(route.Id, new AssignStoresToRouteRequest(storeIds)));
_uow.Verify(u => u.RollbackAsync(default), Times.Once);
_uow.Verify(u => u.CommitAsync(default), Times.Never);
}
// AssignOwnerAsync
[Fact]
public async Task AssignOwnerAsync_ReturnsNull_WhenRouteNotFound()
{
_repo.Setup(r => r.GetByIdAsync(It.IsAny<Guid>())).ReturnsAsync((Models.Route?)null);
var result = await _sut.AssignOwnerAsync(Guid.NewGuid(), new AssignRouteOwnerRequest(Guid.NewGuid()));
Assert.Null(result);
}
[Fact]
public async Task AssignOwnerAsync_ReturnsNull_WhenTargetUserNotActive()
{
var route = ActiveRoute();
_repo.Setup(r => r.GetByIdAsync(route.Id)).ReturnsAsync(route);
_scopeAccess.Setup(s => s.HasRouteScopeAsync(RepUserId, route.Id)).ReturnsAsync(true);
_repo.Setup(r => r.GetActiveUserAsync(It.IsAny<Guid>())).ReturnsAsync((User?)null);
var result = await _sut.AssignOwnerAsync(route.Id, new AssignRouteOwnerRequest(Guid.NewGuid()));
Assert.Null(result);
_uow.Verify(u => u.BeginAsync(default), Times.Never);
}
[Fact]
public async Task AssignOwnerAsync_SetsOwner_AndCommits()
{
var route = ActiveRoute();
var salesRep = new User { Id = Guid.NewGuid(), IsActive = true };
_repo.Setup(r => r.GetByIdAsync(route.Id)).ReturnsAsync(route);
_scopeAccess.Setup(s => s.HasRouteScopeAsync(RepUserId, route.Id)).ReturnsAsync(true);
_repo.Setup(r => r.GetActiveUserAsync(salesRep.Id)).ReturnsAsync(salesRep);
var result = await _sut.AssignOwnerAsync(route.Id, new AssignRouteOwnerRequest(salesRep.Id));
Assert.NotNull(result);
Assert.Equal(salesRep.Id, result.OwnerUserId);
Assert.Equal(salesRep.Id, route.FK_OwnerUserId);
_uow.Verify(u => u.CommitAsync(default), Times.Once);
}
[Fact]
public async Task AssignOwnerAsync_AllowsReassignment_ToDifferentSalesRep()
{
var firstOwner = Guid.NewGuid();
var route = ActiveRoute(firstOwner);
var newRep = new User { Id = Guid.NewGuid(), IsActive = true };
_repo.Setup(r => r.GetByIdAsync(route.Id)).ReturnsAsync(route);
_scopeAccess.Setup(s => s.HasRouteScopeAsync(RepUserId, route.Id)).ReturnsAsync(true);
_repo.Setup(r => r.GetActiveUserAsync(newRep.Id)).ReturnsAsync(newRep);
var result = await _sut.AssignOwnerAsync(route.Id, new AssignRouteOwnerRequest(newRep.Id));
Assert.NotNull(result);
Assert.Equal(newRep.Id, result.OwnerUserId);
Assert.NotEqual(firstOwner, result.OwnerUserId);
}
// RecordCheckInAsync
[Fact]
public async Task RecordCheckInAsync_ReturnsNull_WhenCallerNotAuthenticated()
{
_currentUser.Setup(u => u.UserId).Returns((Guid?)null);
var result = await _sut.RecordCheckInAsync(Guid.NewGuid(), new RecordCheckInRequest(Guid.NewGuid(), 1, 2, null));
Assert.Null(result);
}
[Fact]
public async Task RecordCheckInAsync_ReturnsNull_WhenRouteNotFoundOrInactive()
{
_repo.Setup(r => r.GetByIdAsync(It.IsAny<Guid>())).ReturnsAsync(InactiveRoute());
var result = await _sut.RecordCheckInAsync(Guid.NewGuid(), new RecordCheckInRequest(Guid.NewGuid(), 1, 2, null));
Assert.Null(result);
}
[Fact]
public async Task RecordCheckInAsync_ReturnsNull_WhenCallerLacksRouteScope()
{
var route = ActiveRoute();
_repo.Setup(r => r.GetByIdAsync(route.Id)).ReturnsAsync(route);
_scopeAccess.Setup(s => s.HasRouteScopeAsync(RepUserId, route.Id)).ReturnsAsync(false);
var result = await _sut.RecordCheckInAsync(route.Id, new RecordCheckInRequest(Guid.NewGuid(), 1, 2, null));
Assert.Null(result);
}
[Fact]
public async Task RecordCheckInAsync_ReturnsNull_WhenStoreNotOnRoute()
{
var route = ActiveRoute();
var store = ActiveStore(Guid.NewGuid()); // belongs to a different route
_repo.Setup(r => r.GetByIdAsync(route.Id)).ReturnsAsync(route);
_scopeAccess.Setup(s => s.HasRouteScopeAsync(RepUserId, route.Id)).ReturnsAsync(true);
_repo.Setup(r => r.GetStoreByIdAsync(store.Id)).ReturnsAsync(store);
var result = await _sut.RecordCheckInAsync(route.Id, new RecordCheckInRequest(store.Id, 1, 2, null));
Assert.Null(result);
}
[Fact]
public async Task RecordCheckInAsync_NotFlagged_WhenGpsMatchesStoreLocation()
{
var route = ActiveRoute();
var store = ActiveStore(route.Id, lat: 12.9716m, lon: 77.5946m);
_repo.Setup(r => r.GetByIdAsync(route.Id)).ReturnsAsync(route);
_scopeAccess.Setup(s => s.HasRouteScopeAsync(RepUserId, route.Id)).ReturnsAsync(true);
_repo.Setup(r => r.GetStoreByIdAsync(store.Id)).ReturnsAsync(store);
var result = await _sut.RecordCheckInAsync(route.Id, new RecordCheckInRequest(store.Id, 12.9716m, 77.5946m, null));
Assert.NotNull(result);
Assert.False(result.IsDeviationFlagged);
_repo.Verify(r => r.AddVisitLogAsync(It.IsAny<RouteVisitLog>()), Times.Once);
_uow.Verify(u => u.CommitAsync(default), Times.Once);
}
[Fact]
public async Task RecordCheckInAsync_Flagged_WhenGpsFarFromStoreLocation()
{
var route = ActiveRoute();
var store = ActiveStore(route.Id, lat: 12.9716m, lon: 77.5946m); // Bengaluru
_repo.Setup(r => r.GetByIdAsync(route.Id)).ReturnsAsync(route);
_scopeAccess.Setup(s => s.HasRouteScopeAsync(RepUserId, route.Id)).ReturnsAsync(true);
_repo.Setup(r => r.GetStoreByIdAsync(store.Id)).ReturnsAsync(store);
// Mumbai coordinates - hundreds of km away
var result = await _sut.RecordCheckInAsync(route.Id, new RecordCheckInRequest(store.Id, 19.0760m, 72.8777m, null));
Assert.NotNull(result);
Assert.True(result.IsDeviationFlagged);
}
[Fact]
public async Task RecordCheckInAsync_UsesCurrentUserId_AsVisitingRep()
{
var route = ActiveRoute();
var store = ActiveStore(route.Id);
_repo.Setup(r => r.GetByIdAsync(route.Id)).ReturnsAsync(route);
_scopeAccess.Setup(s => s.HasRouteScopeAsync(RepUserId, route.Id)).ReturnsAsync(true);
_repo.Setup(r => r.GetStoreByIdAsync(store.Id)).ReturnsAsync(store);
var result = await _sut.RecordCheckInAsync(route.Id, new RecordCheckInRequest(store.Id, store.Latitude, store.Longitude, null));
Assert.NotNull(result);
Assert.Equal(RepUserId, result.UserId);
}
// RecordCheckOutAsync
[Fact]
public async Task RecordCheckOutAsync_ReturnsNull_WhenNoOpenVisit()
{
var route = ActiveRoute();
_repo.Setup(r => r.GetByIdAsync(route.Id)).ReturnsAsync(route);
_scopeAccess.Setup(s => s.HasRouteScopeAsync(RepUserId, route.Id)).ReturnsAsync(true);
_repo.Setup(r => r.GetOpenVisitAsync(route.Id, It.IsAny<Guid>())).ReturnsAsync((RouteVisitLog?)null);
var result = await _sut.RecordCheckOutAsync(route.Id, new RecordCheckOutRequest(Guid.NewGuid(), null));
Assert.Null(result);
_uow.Verify(u => u.BeginAsync(default), Times.Never);
}
[Fact]
public async Task RecordCheckOutAsync_SetsCheckOutAt_AndCommits()
{
var route = ActiveRoute();
var storeId = Guid.NewGuid();
var log = new RouteVisitLog { Id = Guid.NewGuid(), FK_RouteId = route.Id, FK_StoreId = storeId, FK_UserId = RepUserId, CheckInAt = DateTime.Now.AddHours(-1) };
_repo.Setup(r => r.GetByIdAsync(route.Id)).ReturnsAsync(route);
_scopeAccess.Setup(s => s.HasRouteScopeAsync(RepUserId, route.Id)).ReturnsAsync(true);
_repo.Setup(r => r.GetOpenVisitAsync(route.Id, storeId)).ReturnsAsync(log);
var result = await _sut.RecordCheckOutAsync(route.Id, new RecordCheckOutRequest(storeId, null));
Assert.NotNull(result);
Assert.NotNull(result.CheckOutAt);
Assert.NotNull(log.CheckOutAt);
_repo.Verify(r => r.UpdateVisitLog(log), Times.Once);
_uow.Verify(u => u.CommitAsync(default), Times.Once);
}
[Fact]
public async Task RecordCheckOutAsync_Rollbacks_WhenUpdateThrows()
{
var route = ActiveRoute();
var storeId = Guid.NewGuid();
var log = new RouteVisitLog { Id = Guid.NewGuid(), FK_RouteId = route.Id, FK_StoreId = storeId, FK_UserId = RepUserId, CheckInAt = DateTime.Now };
_repo.Setup(r => r.GetByIdAsync(route.Id)).ReturnsAsync(route);
_scopeAccess.Setup(s => s.HasRouteScopeAsync(RepUserId, route.Id)).ReturnsAsync(true);
_repo.Setup(r => r.GetOpenVisitAsync(route.Id, storeId)).ReturnsAsync(log);
_repo.Setup(r => r.UpdateVisitLog(It.IsAny<RouteVisitLog>())).Throws(new Exception("db error"));
await Assert.ThrowsAsync<Exception>(() => _sut.RecordCheckOutAsync(route.Id, new RecordCheckOutRequest(storeId, null)));
_uow.Verify(u => u.RollbackAsync(default), Times.Once);
}
}
@@ -0,0 +1,163 @@
using Distribution.Infra.Authorization;
using Moq;
using Xunit;
namespace Distribution.Tests.Services;
public class ScopeAccessServiceTests
{
private readonly Mock<IScopeRepository> _repo = new();
private readonly ScopeAccessService _sut;
private readonly Guid _userId = Guid.NewGuid();
public ScopeAccessServiceTests()
{
_sut = new ScopeAccessService(_repo.Object);
}
// Delegation tests
[Fact]
public async Task HasStoreScopeAsync_DelegatesToRepository()
{
var storeId = Guid.NewGuid();
_repo.Setup(r => r.HasStoreScopeAsync(_userId, storeId)).ReturnsAsync(true);
var result = await _sut.HasStoreScopeAsync(_userId, storeId);
Assert.True(result);
_repo.Verify(r => r.HasStoreScopeAsync(_userId, storeId), Times.Once);
}
[Fact]
public async Task HasRouteScopeAsync_DelegatesToRepository()
{
var routeId = Guid.NewGuid();
_repo.Setup(r => r.HasRouteScopeAsync(_userId, routeId)).ReturnsAsync(false);
var result = await _sut.HasRouteScopeAsync(_userId, routeId);
Assert.False(result);
}
[Fact]
public async Task HasRegionScopeAsync_DelegatesToRepository()
{
var regionId = Guid.NewGuid();
_repo.Setup(r => r.HasRegionScopeAsync(_userId, regionId)).ReturnsAsync(true);
var result = await _sut.HasRegionScopeAsync(_userId, regionId);
Assert.True(result);
}
// GetAccessibleStoreIdsAsync
[Fact]
public async Task GetAccessibleStoreIdsAsync_ReturnsEmpty_WhenNoAssignments()
{
_repo.Setup(r => r.GetDirectStoreIdsAsync(_userId)).ReturnsAsync([]);
_repo.Setup(r => r.GetRouteIdsAsync(_userId)).ReturnsAsync([]);
_repo.Setup(r => r.GetRegionIdsAsync(_userId)).ReturnsAsync([]);
_repo.Setup(r => r.GetStoreIdsByRouteIdsAsync(It.IsAny<List<Guid>>())).ReturnsAsync([]);
_repo.Setup(r => r.GetRouteIdsByRegionIdsAsync(It.IsAny<List<Guid>>())).ReturnsAsync([]);
var result = await _sut.GetAccessibleStoreIdsAsync(_userId);
Assert.Empty(result);
}
[Fact]
public async Task GetAccessibleStoreIdsAsync_ReturnsDirectStores()
{
var storeId = Guid.NewGuid();
_repo.Setup(r => r.GetDirectStoreIdsAsync(_userId)).ReturnsAsync([storeId]);
_repo.Setup(r => r.GetRouteIdsAsync(_userId)).ReturnsAsync([]);
_repo.Setup(r => r.GetRegionIdsAsync(_userId)).ReturnsAsync([]);
_repo.Setup(r => r.GetStoreIdsByRouteIdsAsync(It.IsAny<List<Guid>>())).ReturnsAsync([]);
_repo.Setup(r => r.GetRouteIdsByRegionIdsAsync(It.IsAny<List<Guid>>())).ReturnsAsync([]);
var result = await _sut.GetAccessibleStoreIdsAsync(_userId);
Assert.Single(result);
Assert.Contains(storeId, result);
}
[Fact]
public async Task GetAccessibleStoreIdsAsync_IncludesStoresViaRoute()
{
var routeId = Guid.NewGuid();
var storeViaRoute = Guid.NewGuid();
_repo.Setup(r => r.GetDirectStoreIdsAsync(_userId)).ReturnsAsync([]);
_repo.Setup(r => r.GetRouteIdsAsync(_userId)).ReturnsAsync([routeId]);
_repo.Setup(r => r.GetRegionIdsAsync(_userId)).ReturnsAsync([]);
_repo.Setup(r => r.GetStoreIdsByRouteIdsAsync(new List<Guid> { routeId })).ReturnsAsync([storeViaRoute]);
_repo.Setup(r => r.GetRouteIdsByRegionIdsAsync(It.IsAny<List<Guid>>())).ReturnsAsync([]);
_repo.Setup(r => r.GetStoreIdsByRouteIdsAsync(new List<Guid>())).ReturnsAsync([]);
var result = await _sut.GetAccessibleStoreIdsAsync(_userId);
Assert.Contains(storeViaRoute, result);
}
[Fact]
public async Task GetAccessibleStoreIdsAsync_IncludesStoresViaRegion()
{
var regionId = Guid.NewGuid();
var routeViaRegion = Guid.NewGuid();
var storeViaRegion = Guid.NewGuid();
_repo.Setup(r => r.GetDirectStoreIdsAsync(_userId)).ReturnsAsync([]);
_repo.Setup(r => r.GetRouteIdsAsync(_userId)).ReturnsAsync([]);
_repo.Setup(r => r.GetRegionIdsAsync(_userId)).ReturnsAsync([regionId]);
_repo.Setup(r => r.GetStoreIdsByRouteIdsAsync(new List<Guid>())).ReturnsAsync([]);
_repo.Setup(r => r.GetRouteIdsByRegionIdsAsync(new List<Guid> { regionId })).ReturnsAsync([routeViaRegion]);
_repo.Setup(r => r.GetStoreIdsByRouteIdsAsync(new List<Guid> { routeViaRegion })).ReturnsAsync([storeViaRegion]);
var result = await _sut.GetAccessibleStoreIdsAsync(_userId);
Assert.Contains(storeViaRegion, result);
}
[Fact]
public async Task GetAccessibleStoreIdsAsync_DeduplicatesStores()
{
var sharedStoreId = Guid.NewGuid();
var routeId = Guid.NewGuid();
_repo.Setup(r => r.GetDirectStoreIdsAsync(_userId)).ReturnsAsync([sharedStoreId]);
_repo.Setup(r => r.GetRouteIdsAsync(_userId)).ReturnsAsync([routeId]);
_repo.Setup(r => r.GetRegionIdsAsync(_userId)).ReturnsAsync([]);
_repo.Setup(r => r.GetStoreIdsByRouteIdsAsync(new List<Guid> { routeId })).ReturnsAsync([sharedStoreId]);
_repo.Setup(r => r.GetRouteIdsByRegionIdsAsync(It.IsAny<List<Guid>>())).ReturnsAsync([]);
_repo.Setup(r => r.GetStoreIdsByRouteIdsAsync(new List<Guid>())).ReturnsAsync([]);
var result = await _sut.GetAccessibleStoreIdsAsync(_userId);
Assert.Single(result);
Assert.Equal(sharedStoreId, result[0]);
}
[Fact]
public async Task GetAccessibleStoreIdsAsync_CombinesAllSources()
{
var directStore = Guid.NewGuid();
var routeStore = Guid.NewGuid();
var regionStore = Guid.NewGuid();
var routeId = Guid.NewGuid();
var regionId = Guid.NewGuid();
var routeViaRegion = Guid.NewGuid();
_repo.Setup(r => r.GetDirectStoreIdsAsync(_userId)).ReturnsAsync([directStore]);
_repo.Setup(r => r.GetRouteIdsAsync(_userId)).ReturnsAsync([routeId]);
_repo.Setup(r => r.GetRegionIdsAsync(_userId)).ReturnsAsync([regionId]);
_repo.Setup(r => r.GetStoreIdsByRouteIdsAsync(new List<Guid> { routeId })).ReturnsAsync([routeStore]);
_repo.Setup(r => r.GetRouteIdsByRegionIdsAsync(new List<Guid> { regionId })).ReturnsAsync([routeViaRegion]);
_repo.Setup(r => r.GetStoreIdsByRouteIdsAsync(new List<Guid> { routeViaRegion })).ReturnsAsync([regionStore]);
var result = await _sut.GetAccessibleStoreIdsAsync(_userId);
Assert.Equal(3, result.Count);
Assert.Contains(directStore, result);
Assert.Contains(routeStore, result);
Assert.Contains(regionStore, result);
}
}
@@ -0,0 +1,85 @@
using Distribution.Features.Item;
using Distribution.Features.Order;
using Distribution.Features.Route;
using Distribution.Features.Scheme;
using Distribution.Features.Stock;
using Distribution.Features.Store;
using Distribution.Infra.Authorization;
using Distribution.Infra.UoW;
using Distribution.Models;
using Moq;
using Xunit;
namespace Distribution.Tests.Services;
public class StoreServiceTests
{
private readonly Mock<IStoreRepository> _repo = new();
private readonly Mock<IUnitOfWork> _uow = new();
private readonly Mock<ICurrentUserService> _currentUser = new();
private readonly Mock<IScopeAccessService> _scopeAccess = new();
private readonly Mock<IRouteRepository> _routeRepo = new();
private readonly StoreService _sut;
public StoreServiceTests()
{
_currentUser.Setup(u => u.RoleLevel).Returns(50); // global scope, skips per-store scope lookups
var schemeService = new SchemeService(
Mock.Of<ISchemeRepository>(),
Mock.Of<IOrderRepository>(),
Mock.Of<IItemRepository>(),
_repo.Object,
Mock.Of<IStockRepository>(),
_uow.Object);
_sut = new StoreService(_repo.Object, _uow.Object, _currentUser.Object, _scopeAccess.Object, schemeService, _routeRepo.Object);
}
private static Store ActiveStore() =>
new() { Id = Guid.NewGuid(), IsActive = true };
// GetVisitHistoryAsync
[Fact]
public async Task GetVisitHistoryAsync_ReturnsNull_WhenStoreNotFound()
{
_repo.Setup(r => r.GetByIdAsync(It.IsAny<Guid>())).ReturnsAsync((Store?)null);
var result = await _sut.GetVisitHistoryAsync(Guid.NewGuid());
Assert.Null(result);
}
[Fact]
public async Task GetVisitHistoryAsync_ReturnsEmpty_WhenNoVisitsLogged()
{
var store = ActiveStore();
_repo.Setup(r => r.GetByIdAsync(store.Id)).ReturnsAsync(store);
_routeRepo.Setup(r => r.GetVisitsByStoreIdAsync(store.Id)).ReturnsAsync([]);
var result = await _sut.GetVisitHistoryAsync(store.Id);
Assert.NotNull(result);
Assert.Empty(result);
}
[Fact]
public async Task GetVisitHistoryAsync_MapsVisits_MostRecentFirst()
{
var store = ActiveStore();
var repId = Guid.NewGuid();
var older = new RouteVisitLog { Id = Guid.NewGuid(), FK_StoreId = store.Id, FK_UserId = repId, CheckInAt = DateTime.Now.AddDays(-2), CheckOutAt = DateTime.Now.AddDays(-2).AddHours(1) };
var newer = new RouteVisitLog { Id = Guid.NewGuid(), FK_StoreId = store.Id, FK_UserId = repId, CheckInAt = DateTime.Now.AddDays(-1), CheckOutAt = null };
_repo.Setup(r => r.GetByIdAsync(store.Id)).ReturnsAsync(store);
_routeRepo.Setup(r => r.GetVisitsByStoreIdAsync(store.Id)).ReturnsAsync([older, newer]);
var result = await _sut.GetVisitHistoryAsync(store.Id);
Assert.NotNull(result);
Assert.Equal(2, result.Count);
Assert.Equal(newer.Id, result[0].VisitId);
Assert.Equal("In Progress", result[0].Outcome);
Assert.Equal(older.Id, result[1].VisitId);
Assert.Equal("Completed", result[1].Outcome);
Assert.Equal(repId, result[0].RepUserId);
}
}
Binary file not shown.
@@ -0,0 +1,799 @@
{
"runtimeTarget": {
"name": ".NETCoreApp,Version=v9.0",
"signature": ""
},
"compilationOptions": {},
"targets": {
".NETCoreApp,Version=v9.0": {
"Distribution.Tests/1.0.0": {
"dependencies": {
"Distribution": "1.0.0",
"Microsoft.NET.Test.Sdk": "17.12.0",
"Moq": "4.20.72",
"xunit": "2.9.3"
},
"runtime": {
"Distribution.Tests.dll": {}
}
},
"Castle.Core/5.1.1": {
"runtime": {
"lib/net6.0/Castle.Core.dll": {
"assemblyVersion": "5.0.0.0",
"fileVersion": "5.1.1.0"
}
}
},
"Microsoft.AspNetCore.Authentication.JwtBearer/9.0.0": {
"dependencies": {
"Microsoft.IdentityModel.Protocols.OpenIdConnect": "8.0.1"
},
"runtime": {
"lib/net9.0/Microsoft.AspNetCore.Authentication.JwtBearer.dll": {
"assemblyVersion": "9.0.0.0",
"fileVersion": "9.0.24.52903"
}
}
},
"Microsoft.AspNetCore.OpenApi/9.0.0": {
"dependencies": {
"Microsoft.OpenApi": "1.6.22"
},
"runtime": {
"lib/net9.0/Microsoft.AspNetCore.OpenApi.dll": {
"assemblyVersion": "9.0.0.0",
"fileVersion": "9.0.24.52903"
}
}
},
"Microsoft.CodeCoverage/17.12.0": {
"runtime": {
"lib/netcoreapp3.1/Microsoft.VisualStudio.CodeCoverage.Shim.dll": {
"assemblyVersion": "15.0.0.0",
"fileVersion": "17.1200.524.48002"
}
}
},
"Microsoft.EntityFrameworkCore/9.0.0": {
"dependencies": {
"Microsoft.EntityFrameworkCore.Abstractions": "9.0.0"
},
"runtime": {
"lib/net8.0/Microsoft.EntityFrameworkCore.dll": {
"assemblyVersion": "9.0.0.0",
"fileVersion": "9.0.24.52902"
}
}
},
"Microsoft.EntityFrameworkCore.Abstractions/9.0.0": {
"runtime": {
"lib/net8.0/Microsoft.EntityFrameworkCore.Abstractions.dll": {
"assemblyVersion": "9.0.0.0",
"fileVersion": "9.0.24.52902"
}
}
},
"Microsoft.EntityFrameworkCore.Relational/9.0.0": {
"dependencies": {
"Microsoft.EntityFrameworkCore": "9.0.0"
},
"runtime": {
"lib/net8.0/Microsoft.EntityFrameworkCore.Relational.dll": {
"assemblyVersion": "9.0.0.0",
"fileVersion": "9.0.24.52902"
}
}
},
"Microsoft.IdentityModel.Abstractions/8.0.1": {
"runtime": {
"lib/net9.0/Microsoft.IdentityModel.Abstractions.dll": {
"assemblyVersion": "8.0.1.0",
"fileVersion": "8.0.1.50722"
}
}
},
"Microsoft.IdentityModel.JsonWebTokens/8.0.1": {
"dependencies": {
"Microsoft.IdentityModel.Tokens": "8.0.1"
},
"runtime": {
"lib/net9.0/Microsoft.IdentityModel.JsonWebTokens.dll": {
"assemblyVersion": "8.0.1.0",
"fileVersion": "8.0.1.50722"
}
}
},
"Microsoft.IdentityModel.Logging/8.0.1": {
"dependencies": {
"Microsoft.IdentityModel.Abstractions": "8.0.1"
},
"runtime": {
"lib/net9.0/Microsoft.IdentityModel.Logging.dll": {
"assemblyVersion": "8.0.1.0",
"fileVersion": "8.0.1.50722"
}
}
},
"Microsoft.IdentityModel.Protocols/8.0.1": {
"dependencies": {
"Microsoft.IdentityModel.Tokens": "8.0.1"
},
"runtime": {
"lib/net9.0/Microsoft.IdentityModel.Protocols.dll": {
"assemblyVersion": "8.0.1.0",
"fileVersion": "8.0.1.50722"
}
}
},
"Microsoft.IdentityModel.Protocols.OpenIdConnect/8.0.1": {
"dependencies": {
"Microsoft.IdentityModel.Protocols": "8.0.1",
"System.IdentityModel.Tokens.Jwt": "8.0.1"
},
"runtime": {
"lib/net9.0/Microsoft.IdentityModel.Protocols.OpenIdConnect.dll": {
"assemblyVersion": "8.0.1.0",
"fileVersion": "8.0.1.50722"
}
}
},
"Microsoft.IdentityModel.Tokens/8.0.1": {
"dependencies": {
"Microsoft.IdentityModel.Logging": "8.0.1"
},
"runtime": {
"lib/net9.0/Microsoft.IdentityModel.Tokens.dll": {
"assemblyVersion": "8.0.1.0",
"fileVersion": "8.0.1.50722"
}
}
},
"Microsoft.NET.Test.Sdk/17.12.0": {
"dependencies": {
"Microsoft.CodeCoverage": "17.12.0",
"Microsoft.TestPlatform.TestHost": "17.12.0"
}
},
"Microsoft.OpenApi/1.6.22": {
"runtime": {
"lib/netstandard2.0/Microsoft.OpenApi.dll": {
"assemblyVersion": "1.6.22.0",
"fileVersion": "1.6.22.0"
}
}
},
"Microsoft.TestPlatform.ObjectModel/17.12.0": {
"runtime": {
"lib/netcoreapp3.1/Microsoft.TestPlatform.CoreUtilities.dll": {
"assemblyVersion": "15.0.0.0",
"fileVersion": "17.1200.24.56501"
},
"lib/netcoreapp3.1/Microsoft.TestPlatform.PlatformAbstractions.dll": {
"assemblyVersion": "15.0.0.0",
"fileVersion": "17.1200.24.56501"
},
"lib/netcoreapp3.1/Microsoft.VisualStudio.TestPlatform.ObjectModel.dll": {
"assemblyVersion": "15.0.0.0",
"fileVersion": "17.1200.24.56501"
}
},
"resources": {
"lib/netcoreapp3.1/cs/Microsoft.TestPlatform.CoreUtilities.resources.dll": {
"locale": "cs"
},
"lib/netcoreapp3.1/cs/Microsoft.VisualStudio.TestPlatform.ObjectModel.resources.dll": {
"locale": "cs"
},
"lib/netcoreapp3.1/de/Microsoft.TestPlatform.CoreUtilities.resources.dll": {
"locale": "de"
},
"lib/netcoreapp3.1/de/Microsoft.VisualStudio.TestPlatform.ObjectModel.resources.dll": {
"locale": "de"
},
"lib/netcoreapp3.1/es/Microsoft.TestPlatform.CoreUtilities.resources.dll": {
"locale": "es"
},
"lib/netcoreapp3.1/es/Microsoft.VisualStudio.TestPlatform.ObjectModel.resources.dll": {
"locale": "es"
},
"lib/netcoreapp3.1/fr/Microsoft.TestPlatform.CoreUtilities.resources.dll": {
"locale": "fr"
},
"lib/netcoreapp3.1/fr/Microsoft.VisualStudio.TestPlatform.ObjectModel.resources.dll": {
"locale": "fr"
},
"lib/netcoreapp3.1/it/Microsoft.TestPlatform.CoreUtilities.resources.dll": {
"locale": "it"
},
"lib/netcoreapp3.1/it/Microsoft.VisualStudio.TestPlatform.ObjectModel.resources.dll": {
"locale": "it"
},
"lib/netcoreapp3.1/ja/Microsoft.TestPlatform.CoreUtilities.resources.dll": {
"locale": "ja"
},
"lib/netcoreapp3.1/ja/Microsoft.VisualStudio.TestPlatform.ObjectModel.resources.dll": {
"locale": "ja"
},
"lib/netcoreapp3.1/ko/Microsoft.TestPlatform.CoreUtilities.resources.dll": {
"locale": "ko"
},
"lib/netcoreapp3.1/ko/Microsoft.VisualStudio.TestPlatform.ObjectModel.resources.dll": {
"locale": "ko"
},
"lib/netcoreapp3.1/pl/Microsoft.TestPlatform.CoreUtilities.resources.dll": {
"locale": "pl"
},
"lib/netcoreapp3.1/pl/Microsoft.VisualStudio.TestPlatform.ObjectModel.resources.dll": {
"locale": "pl"
},
"lib/netcoreapp3.1/pt-BR/Microsoft.TestPlatform.CoreUtilities.resources.dll": {
"locale": "pt-BR"
},
"lib/netcoreapp3.1/pt-BR/Microsoft.VisualStudio.TestPlatform.ObjectModel.resources.dll": {
"locale": "pt-BR"
},
"lib/netcoreapp3.1/ru/Microsoft.TestPlatform.CoreUtilities.resources.dll": {
"locale": "ru"
},
"lib/netcoreapp3.1/ru/Microsoft.VisualStudio.TestPlatform.ObjectModel.resources.dll": {
"locale": "ru"
},
"lib/netcoreapp3.1/tr/Microsoft.TestPlatform.CoreUtilities.resources.dll": {
"locale": "tr"
},
"lib/netcoreapp3.1/tr/Microsoft.VisualStudio.TestPlatform.ObjectModel.resources.dll": {
"locale": "tr"
},
"lib/netcoreapp3.1/zh-Hans/Microsoft.TestPlatform.CoreUtilities.resources.dll": {
"locale": "zh-Hans"
},
"lib/netcoreapp3.1/zh-Hans/Microsoft.VisualStudio.TestPlatform.ObjectModel.resources.dll": {
"locale": "zh-Hans"
},
"lib/netcoreapp3.1/zh-Hant/Microsoft.TestPlatform.CoreUtilities.resources.dll": {
"locale": "zh-Hant"
},
"lib/netcoreapp3.1/zh-Hant/Microsoft.VisualStudio.TestPlatform.ObjectModel.resources.dll": {
"locale": "zh-Hant"
}
}
},
"Microsoft.TestPlatform.TestHost/17.12.0": {
"dependencies": {
"Microsoft.TestPlatform.ObjectModel": "17.12.0",
"Newtonsoft.Json": "13.0.1"
},
"runtime": {
"lib/netcoreapp3.1/Microsoft.TestPlatform.CommunicationUtilities.dll": {
"assemblyVersion": "15.0.0.0",
"fileVersion": "17.1200.24.56501"
},
"lib/netcoreapp3.1/Microsoft.TestPlatform.CrossPlatEngine.dll": {
"assemblyVersion": "15.0.0.0",
"fileVersion": "17.1200.24.56501"
},
"lib/netcoreapp3.1/Microsoft.TestPlatform.Utilities.dll": {
"assemblyVersion": "15.0.0.0",
"fileVersion": "17.1200.24.56501"
},
"lib/netcoreapp3.1/Microsoft.VisualStudio.TestPlatform.Common.dll": {
"assemblyVersion": "15.0.0.0",
"fileVersion": "17.1200.24.56501"
},
"lib/netcoreapp3.1/testhost.dll": {
"assemblyVersion": "15.0.0.0",
"fileVersion": "17.1200.24.56501"
}
},
"resources": {
"lib/netcoreapp3.1/cs/Microsoft.TestPlatform.CommunicationUtilities.resources.dll": {
"locale": "cs"
},
"lib/netcoreapp3.1/cs/Microsoft.TestPlatform.CrossPlatEngine.resources.dll": {
"locale": "cs"
},
"lib/netcoreapp3.1/cs/Microsoft.VisualStudio.TestPlatform.Common.resources.dll": {
"locale": "cs"
},
"lib/netcoreapp3.1/de/Microsoft.TestPlatform.CommunicationUtilities.resources.dll": {
"locale": "de"
},
"lib/netcoreapp3.1/de/Microsoft.TestPlatform.CrossPlatEngine.resources.dll": {
"locale": "de"
},
"lib/netcoreapp3.1/de/Microsoft.VisualStudio.TestPlatform.Common.resources.dll": {
"locale": "de"
},
"lib/netcoreapp3.1/es/Microsoft.TestPlatform.CommunicationUtilities.resources.dll": {
"locale": "es"
},
"lib/netcoreapp3.1/es/Microsoft.TestPlatform.CrossPlatEngine.resources.dll": {
"locale": "es"
},
"lib/netcoreapp3.1/es/Microsoft.VisualStudio.TestPlatform.Common.resources.dll": {
"locale": "es"
},
"lib/netcoreapp3.1/fr/Microsoft.TestPlatform.CommunicationUtilities.resources.dll": {
"locale": "fr"
},
"lib/netcoreapp3.1/fr/Microsoft.TestPlatform.CrossPlatEngine.resources.dll": {
"locale": "fr"
},
"lib/netcoreapp3.1/fr/Microsoft.VisualStudio.TestPlatform.Common.resources.dll": {
"locale": "fr"
},
"lib/netcoreapp3.1/it/Microsoft.TestPlatform.CommunicationUtilities.resources.dll": {
"locale": "it"
},
"lib/netcoreapp3.1/it/Microsoft.TestPlatform.CrossPlatEngine.resources.dll": {
"locale": "it"
},
"lib/netcoreapp3.1/it/Microsoft.VisualStudio.TestPlatform.Common.resources.dll": {
"locale": "it"
},
"lib/netcoreapp3.1/ja/Microsoft.TestPlatform.CommunicationUtilities.resources.dll": {
"locale": "ja"
},
"lib/netcoreapp3.1/ja/Microsoft.TestPlatform.CrossPlatEngine.resources.dll": {
"locale": "ja"
},
"lib/netcoreapp3.1/ja/Microsoft.VisualStudio.TestPlatform.Common.resources.dll": {
"locale": "ja"
},
"lib/netcoreapp3.1/ko/Microsoft.TestPlatform.CommunicationUtilities.resources.dll": {
"locale": "ko"
},
"lib/netcoreapp3.1/ko/Microsoft.TestPlatform.CrossPlatEngine.resources.dll": {
"locale": "ko"
},
"lib/netcoreapp3.1/ko/Microsoft.VisualStudio.TestPlatform.Common.resources.dll": {
"locale": "ko"
},
"lib/netcoreapp3.1/pl/Microsoft.TestPlatform.CommunicationUtilities.resources.dll": {
"locale": "pl"
},
"lib/netcoreapp3.1/pl/Microsoft.TestPlatform.CrossPlatEngine.resources.dll": {
"locale": "pl"
},
"lib/netcoreapp3.1/pl/Microsoft.VisualStudio.TestPlatform.Common.resources.dll": {
"locale": "pl"
},
"lib/netcoreapp3.1/pt-BR/Microsoft.TestPlatform.CommunicationUtilities.resources.dll": {
"locale": "pt-BR"
},
"lib/netcoreapp3.1/pt-BR/Microsoft.TestPlatform.CrossPlatEngine.resources.dll": {
"locale": "pt-BR"
},
"lib/netcoreapp3.1/pt-BR/Microsoft.VisualStudio.TestPlatform.Common.resources.dll": {
"locale": "pt-BR"
},
"lib/netcoreapp3.1/ru/Microsoft.TestPlatform.CommunicationUtilities.resources.dll": {
"locale": "ru"
},
"lib/netcoreapp3.1/ru/Microsoft.TestPlatform.CrossPlatEngine.resources.dll": {
"locale": "ru"
},
"lib/netcoreapp3.1/ru/Microsoft.VisualStudio.TestPlatform.Common.resources.dll": {
"locale": "ru"
},
"lib/netcoreapp3.1/tr/Microsoft.TestPlatform.CommunicationUtilities.resources.dll": {
"locale": "tr"
},
"lib/netcoreapp3.1/tr/Microsoft.TestPlatform.CrossPlatEngine.resources.dll": {
"locale": "tr"
},
"lib/netcoreapp3.1/tr/Microsoft.VisualStudio.TestPlatform.Common.resources.dll": {
"locale": "tr"
},
"lib/netcoreapp3.1/zh-Hans/Microsoft.TestPlatform.CommunicationUtilities.resources.dll": {
"locale": "zh-Hans"
},
"lib/netcoreapp3.1/zh-Hans/Microsoft.TestPlatform.CrossPlatEngine.resources.dll": {
"locale": "zh-Hans"
},
"lib/netcoreapp3.1/zh-Hans/Microsoft.VisualStudio.TestPlatform.Common.resources.dll": {
"locale": "zh-Hans"
},
"lib/netcoreapp3.1/zh-Hant/Microsoft.TestPlatform.CommunicationUtilities.resources.dll": {
"locale": "zh-Hant"
},
"lib/netcoreapp3.1/zh-Hant/Microsoft.TestPlatform.CrossPlatEngine.resources.dll": {
"locale": "zh-Hant"
},
"lib/netcoreapp3.1/zh-Hant/Microsoft.VisualStudio.TestPlatform.Common.resources.dll": {
"locale": "zh-Hant"
}
}
},
"Moq/4.20.72": {
"dependencies": {
"Castle.Core": "5.1.1"
},
"runtime": {
"lib/net6.0/Moq.dll": {
"assemblyVersion": "4.20.72.0",
"fileVersion": "4.20.72.0"
}
}
},
"MySqlConnector/2.4.0": {
"runtime": {
"lib/net9.0/MySqlConnector.dll": {
"assemblyVersion": "2.0.0.0",
"fileVersion": "2.4.0.0"
}
}
},
"Newtonsoft.Json/13.0.1": {
"runtime": {
"lib/netstandard2.0/Newtonsoft.Json.dll": {
"assemblyVersion": "13.0.0.0",
"fileVersion": "13.0.1.25517"
}
}
},
"Pomelo.EntityFrameworkCore.MySql/9.0.0": {
"dependencies": {
"Microsoft.EntityFrameworkCore.Relational": "9.0.0",
"MySqlConnector": "2.4.0"
},
"runtime": {
"lib/net8.0/Pomelo.EntityFrameworkCore.MySql.dll": {
"assemblyVersion": "9.0.0.0",
"fileVersion": "9.0.0.0"
}
}
},
"Swashbuckle.AspNetCore/7.3.1": {
"dependencies": {
"Swashbuckle.AspNetCore.Swagger": "7.3.1",
"Swashbuckle.AspNetCore.SwaggerGen": "7.3.1",
"Swashbuckle.AspNetCore.SwaggerUI": "7.3.1"
}
},
"Swashbuckle.AspNetCore.Swagger/7.3.1": {
"dependencies": {
"Microsoft.OpenApi": "1.6.22"
},
"runtime": {
"lib/net9.0/Swashbuckle.AspNetCore.Swagger.dll": {
"assemblyVersion": "7.3.1.0",
"fileVersion": "7.3.1.1111"
}
}
},
"Swashbuckle.AspNetCore.SwaggerGen/7.3.1": {
"dependencies": {
"Swashbuckle.AspNetCore.Swagger": "7.3.1"
},
"runtime": {
"lib/net9.0/Swashbuckle.AspNetCore.SwaggerGen.dll": {
"assemblyVersion": "7.3.1.0",
"fileVersion": "7.3.1.1111"
}
}
},
"Swashbuckle.AspNetCore.SwaggerUI/7.3.1": {
"runtime": {
"lib/net9.0/Swashbuckle.AspNetCore.SwaggerUI.dll": {
"assemblyVersion": "7.3.1.0",
"fileVersion": "7.3.1.1111"
}
}
},
"System.IdentityModel.Tokens.Jwt/8.0.1": {
"dependencies": {
"Microsoft.IdentityModel.JsonWebTokens": "8.0.1",
"Microsoft.IdentityModel.Tokens": "8.0.1"
},
"runtime": {
"lib/net9.0/System.IdentityModel.Tokens.Jwt.dll": {
"assemblyVersion": "8.0.1.0",
"fileVersion": "8.0.1.50722"
}
}
},
"xunit/2.9.3": {
"dependencies": {
"xunit.assert": "2.9.3",
"xunit.core": "2.9.3"
}
},
"xunit.abstractions/2.0.3": {
"runtime": {
"lib/netstandard2.0/xunit.abstractions.dll": {
"assemblyVersion": "2.0.0.0",
"fileVersion": "2.0.0.0"
}
}
},
"xunit.assert/2.9.3": {
"runtime": {
"lib/net6.0/xunit.assert.dll": {
"assemblyVersion": "2.9.3.0",
"fileVersion": "2.9.3.0"
}
}
},
"xunit.core/2.9.3": {
"dependencies": {
"xunit.extensibility.core": "2.9.3",
"xunit.extensibility.execution": "2.9.3"
}
},
"xunit.extensibility.core/2.9.3": {
"dependencies": {
"xunit.abstractions": "2.0.3"
},
"runtime": {
"lib/netstandard1.1/xunit.core.dll": {
"assemblyVersion": "2.9.3.0",
"fileVersion": "2.9.3.0"
}
}
},
"xunit.extensibility.execution/2.9.3": {
"dependencies": {
"xunit.extensibility.core": "2.9.3"
},
"runtime": {
"lib/netstandard1.1/xunit.execution.dotnet.dll": {
"assemblyVersion": "2.9.3.0",
"fileVersion": "2.9.3.0"
}
}
},
"Distribution/1.0.0": {
"dependencies": {
"Microsoft.AspNetCore.Authentication.JwtBearer": "9.0.0",
"Microsoft.AspNetCore.OpenApi": "9.0.0",
"Pomelo.EntityFrameworkCore.MySql": "9.0.0",
"Swashbuckle.AspNetCore": "7.3.1"
},
"runtime": {
"Distribution.dll": {
"assemblyVersion": "1.0.0.0",
"fileVersion": "1.0.0.0"
}
}
}
}
},
"libraries": {
"Distribution.Tests/1.0.0": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Castle.Core/5.1.1": {
"type": "package",
"serviceable": true,
"sha512": "sha512-rpYtIczkzGpf+EkZgDr9CClTdemhsrwA/W5hMoPjLkRFnXzH44zDLoovXeKtmxb1ykXK9aJVODSpiJml8CTw2g==",
"path": "castle.core/5.1.1",
"hashPath": "castle.core.5.1.1.nupkg.sha512"
},
"Microsoft.AspNetCore.Authentication.JwtBearer/9.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-bs+1Pq3vQdS2lTyxNUd9fEhtMsq3eLUpK36k2t56iDMVrk6OrAoFtvrQrTK0Y0OetTcJrUkGU7hBlf+ORzHLqQ==",
"path": "microsoft.aspnetcore.authentication.jwtbearer/9.0.0",
"hashPath": "microsoft.aspnetcore.authentication.jwtbearer.9.0.0.nupkg.sha512"
},
"Microsoft.AspNetCore.OpenApi/9.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-FqUK5j1EOPNuFT7IafltZQ3cakqhSwVzH5ZW1MhZDe4pPXs9sJ2M5jom1Omsu+mwF2tNKKlRAzLRHQTZzbd+6Q==",
"path": "microsoft.aspnetcore.openapi/9.0.0",
"hashPath": "microsoft.aspnetcore.openapi.9.0.0.nupkg.sha512"
},
"Microsoft.CodeCoverage/17.12.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-4svMznBd5JM21JIG2xZKGNanAHNXplxf/kQDFfLHXQ3OnpJkayRK/TjacFjA+EYmoyuNXHo/sOETEfcYtAzIrA==",
"path": "microsoft.codecoverage/17.12.0",
"hashPath": "microsoft.codecoverage.17.12.0.nupkg.sha512"
},
"Microsoft.EntityFrameworkCore/9.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-wpG+nfnfDAw87R3ovAsUmjr3MZ4tYXf6bFqEPVAIKE6IfPml3DS//iX0DBnf8kWn5ZHSO5oi1m4d/Jf+1LifJQ==",
"path": "microsoft.entityframeworkcore/9.0.0",
"hashPath": "microsoft.entityframeworkcore.9.0.0.nupkg.sha512"
},
"Microsoft.EntityFrameworkCore.Abstractions/9.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-fnmifFL8KaA4ZNLCVgfjCWhZUFxkrDInx5hR4qG7Q8IEaSiy/6VOSRFyx55oH7MV4y7wM3J3EE90nSpcVBI44Q==",
"path": "microsoft.entityframeworkcore.abstractions/9.0.0",
"hashPath": "microsoft.entityframeworkcore.abstractions.9.0.0.nupkg.sha512"
},
"Microsoft.EntityFrameworkCore.Relational/9.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-j+msw6fWgAE9M3Q/5B9Uhv7pdAdAQUvFPJAiBJmoy+OXvehVbfbCE8ftMAa51Uo2ZeiqVnHShhnv4Y4UJJmUzA==",
"path": "microsoft.entityframeworkcore.relational/9.0.0",
"hashPath": "microsoft.entityframeworkcore.relational.9.0.0.nupkg.sha512"
},
"Microsoft.IdentityModel.Abstractions/8.0.1": {
"type": "package",
"serviceable": true,
"sha512": "sha512-OtlIWcyX01olfdevPKZdIPfBEvbcioDyBiE/Z2lHsopsMD7twcKtlN9kMevHmI5IIPhFpfwCIiR6qHQz1WHUIw==",
"path": "microsoft.identitymodel.abstractions/8.0.1",
"hashPath": "microsoft.identitymodel.abstractions.8.0.1.nupkg.sha512"
},
"Microsoft.IdentityModel.JsonWebTokens/8.0.1": {
"type": "package",
"serviceable": true,
"sha512": "sha512-s6++gF9x0rQApQzOBbSyp4jUaAlwm+DroKfL8gdOHxs83k8SJfUXhuc46rDB3rNXBQ1MVRxqKUrqFhO/M0E97g==",
"path": "microsoft.identitymodel.jsonwebtokens/8.0.1",
"hashPath": "microsoft.identitymodel.jsonwebtokens.8.0.1.nupkg.sha512"
},
"Microsoft.IdentityModel.Logging/8.0.1": {
"type": "package",
"serviceable": true,
"sha512": "sha512-UCPF2exZqBXe7v/6sGNiM6zCQOUXXQ9+v5VTb9gPB8ZSUPnX53BxlN78v2jsbIvK9Dq4GovQxo23x8JgWvm/Qg==",
"path": "microsoft.identitymodel.logging/8.0.1",
"hashPath": "microsoft.identitymodel.logging.8.0.1.nupkg.sha512"
},
"Microsoft.IdentityModel.Protocols/8.0.1": {
"type": "package",
"serviceable": true,
"sha512": "sha512-uA2vpKqU3I2mBBEaeJAWPTjT9v1TZrGWKdgK6G5qJd03CLx83kdiqO9cmiK8/n1erkHzFBwU/RphP83aAe3i3g==",
"path": "microsoft.identitymodel.protocols/8.0.1",
"hashPath": "microsoft.identitymodel.protocols.8.0.1.nupkg.sha512"
},
"Microsoft.IdentityModel.Protocols.OpenIdConnect/8.0.1": {
"type": "package",
"serviceable": true,
"sha512": "sha512-AQDbfpL+yzuuGhO/mQhKNsp44pm5Jv8/BI4KiFXR7beVGZoSH35zMV3PrmcfvSTsyI6qrcR898NzUauD6SRigg==",
"path": "microsoft.identitymodel.protocols.openidconnect/8.0.1",
"hashPath": "microsoft.identitymodel.protocols.openidconnect.8.0.1.nupkg.sha512"
},
"Microsoft.IdentityModel.Tokens/8.0.1": {
"type": "package",
"serviceable": true,
"sha512": "sha512-kDimB6Dkd3nkW2oZPDkMkVHfQt3IDqO5gL0oa8WVy3OP4uE8Ij+8TXnqg9TOd9ufjsY3IDiGz7pCUbnfL18tjg==",
"path": "microsoft.identitymodel.tokens/8.0.1",
"hashPath": "microsoft.identitymodel.tokens.8.0.1.nupkg.sha512"
},
"Microsoft.NET.Test.Sdk/17.12.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-kt/PKBZ91rFCWxVIJZSgVLk+YR+4KxTuHf799ho8WNiK5ZQpJNAEZCAWX86vcKrs+DiYjiibpYKdGZP6+/N17w==",
"path": "microsoft.net.test.sdk/17.12.0",
"hashPath": "microsoft.net.test.sdk.17.12.0.nupkg.sha512"
},
"Microsoft.OpenApi/1.6.22": {
"type": "package",
"serviceable": true,
"sha512": "sha512-aBvunmrdu/x+4CaA/UP1Jx4xWGwk4kymhoIRnn2Vp+zi5/KOPQJ9EkSXHRUr01WcGKtYl3Au7XfkPJbU1G2sjQ==",
"path": "microsoft.openapi/1.6.22",
"hashPath": "microsoft.openapi.1.6.22.nupkg.sha512"
},
"Microsoft.TestPlatform.ObjectModel/17.12.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-TDqkTKLfQuAaPcEb3pDDWnh7b3SyZF+/W9OZvWFp6eJCIiiYFdSB6taE2I6tWrFw5ywhzOb6sreoGJTI6m3rSQ==",
"path": "microsoft.testplatform.objectmodel/17.12.0",
"hashPath": "microsoft.testplatform.objectmodel.17.12.0.nupkg.sha512"
},
"Microsoft.TestPlatform.TestHost/17.12.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-MiPEJQNyADfwZ4pJNpQex+t9/jOClBGMiCiVVFuELCMSX2nmNfvUor3uFVxNNCg30uxDP8JDYfPnMXQzsfzYyg==",
"path": "microsoft.testplatform.testhost/17.12.0",
"hashPath": "microsoft.testplatform.testhost.17.12.0.nupkg.sha512"
},
"Moq/4.20.72": {
"type": "package",
"serviceable": true,
"sha512": "sha512-EA55cjyNn8eTNWrgrdZJH5QLFp2L43oxl1tlkoYUKIE9pRwL784OWiTXeCV5ApS+AMYEAlt7Fo03A2XfouvHmQ==",
"path": "moq/4.20.72",
"hashPath": "moq.4.20.72.nupkg.sha512"
},
"MySqlConnector/2.4.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-78M+gVOjbdZEDIyXQqcA7EYlCGS3tpbUELHvn6638A2w0pkPI625ixnzsa5staAd3N9/xFmPJtkKDYwsXpFi/w==",
"path": "mysqlconnector/2.4.0",
"hashPath": "mysqlconnector.2.4.0.nupkg.sha512"
},
"Newtonsoft.Json/13.0.1": {
"type": "package",
"serviceable": true,
"sha512": "sha512-ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A==",
"path": "newtonsoft.json/13.0.1",
"hashPath": "newtonsoft.json.13.0.1.nupkg.sha512"
},
"Pomelo.EntityFrameworkCore.MySql/9.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-cl7S4s6CbJno0LjNxrBHNc2xxmCliR5i40ATPZk/eTywVaAbHCbdc9vbGc3QThvwGjHqrDHT8vY9m1VF/47o0g==",
"path": "pomelo.entityframeworkcore.mysql/9.0.0",
"hashPath": "pomelo.entityframeworkcore.mysql.9.0.0.nupkg.sha512"
},
"Swashbuckle.AspNetCore/7.3.1": {
"type": "package",
"serviceable": true,
"sha512": "sha512-6u8w+UXp/sF89xQjfydWw6znQrUpbpFOmEIs8ODE+S0bV+mCQ9dNP4mk+HRsGHylpDaP5KSYSCEfFSgluLXHsA==",
"path": "swashbuckle.aspnetcore/7.3.1",
"hashPath": "swashbuckle.aspnetcore.7.3.1.nupkg.sha512"
},
"Swashbuckle.AspNetCore.Swagger/7.3.1": {
"type": "package",
"serviceable": true,
"sha512": "sha512-jQuJ8kVbq+YE8WsJE3RwWHlF1kasp0QkA9Gl6NeNLICrhcgN8IQIthMufYW6t/4hpcN5cBIdES5jCEV81WjHbA==",
"path": "swashbuckle.aspnetcore.swagger/7.3.1",
"hashPath": "swashbuckle.aspnetcore.swagger.7.3.1.nupkg.sha512"
},
"Swashbuckle.AspNetCore.SwaggerGen/7.3.1": {
"type": "package",
"serviceable": true,
"sha512": "sha512-xs7Pznb3SSjZy7HpThE0ILqECfQFsGDHOrRoIYD/j67ktdRR1juDG4AMyidXXCOipgzHanZoF+nFrc+Nmjqjyw==",
"path": "swashbuckle.aspnetcore.swaggergen/7.3.1",
"hashPath": "swashbuckle.aspnetcore.swaggergen.7.3.1.nupkg.sha512"
},
"Swashbuckle.AspNetCore.SwaggerUI/7.3.1": {
"type": "package",
"serviceable": true,
"sha512": "sha512-hs6C+lmNEzipOA1WPQpIaGvvoXjUbnoevbv6l7o9ZQE8SNF8ggjOmK6NB6cYdMcEvk0uBeKl4Qq/BnRt5MFVqg==",
"path": "swashbuckle.aspnetcore.swaggerui/7.3.1",
"hashPath": "swashbuckle.aspnetcore.swaggerui.7.3.1.nupkg.sha512"
},
"System.IdentityModel.Tokens.Jwt/8.0.1": {
"type": "package",
"serviceable": true,
"sha512": "sha512-GJw3bYkWpOgvN3tJo5X4lYUeIFA2HD293FPUhKmp7qxS+g5ywAb34Dnd3cDAFLkcMohy5XTpoaZ4uAHuw0uSPQ==",
"path": "system.identitymodel.tokens.jwt/8.0.1",
"hashPath": "system.identitymodel.tokens.jwt.8.0.1.nupkg.sha512"
},
"xunit/2.9.3": {
"type": "package",
"serviceable": true,
"sha512": "sha512-TlXQBinK35LpOPKHAqbLY4xlEen9TBafjs0V5KnA4wZsoQLQJiirCR4CbIXvOH8NzkW4YeJKP5P/Bnrodm0h9Q==",
"path": "xunit/2.9.3",
"hashPath": "xunit.2.9.3.nupkg.sha512"
},
"xunit.abstractions/2.0.3": {
"type": "package",
"serviceable": true,
"sha512": "sha512-pot1I4YOxlWjIb5jmwvvQNbTrZ3lJQ+jUGkGjWE3hEFM0l5gOnBWS+H3qsex68s5cO52g+44vpGzhAt+42vwKg==",
"path": "xunit.abstractions/2.0.3",
"hashPath": "xunit.abstractions.2.0.3.nupkg.sha512"
},
"xunit.assert/2.9.3": {
"type": "package",
"serviceable": true,
"sha512": "sha512-/Kq28fCE7MjOV42YLVRAJzRF0WmEqsmflm0cfpMjGtzQ2lR5mYVj1/i0Y8uDAOLczkL3/jArrwehfMD0YogMAA==",
"path": "xunit.assert/2.9.3",
"hashPath": "xunit.assert.2.9.3.nupkg.sha512"
},
"xunit.core/2.9.3": {
"type": "package",
"serviceable": true,
"sha512": "sha512-BiAEvqGvyme19wE0wTKdADH+NloYqikiU0mcnmiNyXaF9HyHmE6sr/3DC5vnBkgsWaE6yPyWszKSPSApWdRVeQ==",
"path": "xunit.core/2.9.3",
"hashPath": "xunit.core.2.9.3.nupkg.sha512"
},
"xunit.extensibility.core/2.9.3": {
"type": "package",
"serviceable": true,
"sha512": "sha512-kf3si0YTn2a8J8eZNb+zFpwfoyvIrQ7ivNk5ZYA5yuYk1bEtMe4DxJ2CF/qsRgmEnDr7MnW1mxylBaHTZ4qErA==",
"path": "xunit.extensibility.core/2.9.3",
"hashPath": "xunit.extensibility.core.2.9.3.nupkg.sha512"
},
"xunit.extensibility.execution/2.9.3": {
"type": "package",
"serviceable": true,
"sha512": "sha512-yMb6vMESlSrE3Wfj7V6cjQ3S4TXdXpRqYeNEI3zsX31uTsGMJjEw6oD5F5u1cHnMptjhEECnmZSsPxB6ChZHDQ==",
"path": "xunit.extensibility.execution/2.9.3",
"hashPath": "xunit.extensibility.execution.2.9.3.nupkg.sha512"
},
"Distribution/1.0.0": {
"type": "project",
"serviceable": false,
"sha512": ""
}
}
}
@@ -0,0 +1,19 @@
{
"runtimeOptions": {
"tfm": "net9.0",
"frameworks": [
{
"name": "Microsoft.NETCore.App",
"version": "9.0.0"
},
{
"name": "Microsoft.AspNetCore.App",
"version": "9.0.0"
}
],
"configProperties": {
"System.Reflection.NullabilityInfoContext.IsSupported": true,
"System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization": false
}
}
}
@@ -0,0 +1,879 @@
{
"runtimeTarget": {
"name": ".NETCoreApp,Version=v9.0",
"signature": ""
},
"compilationOptions": {},
"targets": {
".NETCoreApp,Version=v9.0": {
"Distribution/1.0.0": {
"dependencies": {
"Microsoft.AspNetCore.Authentication.JwtBearer": "9.0.0",
"Microsoft.AspNetCore.OpenApi": "9.0.0",
"Microsoft.EntityFrameworkCore.Design": "9.0.0",
"Pomelo.EntityFrameworkCore.MySql": "9.0.0",
"Swashbuckle.AspNetCore": "7.3.1"
},
"runtime": {
"Distribution.dll": {}
}
},
"Humanizer.Core/2.14.1": {
"runtime": {
"lib/net6.0/Humanizer.dll": {
"assemblyVersion": "2.14.0.0",
"fileVersion": "2.14.1.48190"
}
}
},
"Microsoft.AspNetCore.Authentication.JwtBearer/9.0.0": {
"dependencies": {
"Microsoft.IdentityModel.Protocols.OpenIdConnect": "8.0.1"
},
"runtime": {
"lib/net9.0/Microsoft.AspNetCore.Authentication.JwtBearer.dll": {
"assemblyVersion": "9.0.0.0",
"fileVersion": "9.0.24.52903"
}
}
},
"Microsoft.AspNetCore.OpenApi/9.0.0": {
"dependencies": {
"Microsoft.OpenApi": "1.6.22"
},
"runtime": {
"lib/net9.0/Microsoft.AspNetCore.OpenApi.dll": {
"assemblyVersion": "9.0.0.0",
"fileVersion": "9.0.24.52903"
}
}
},
"Microsoft.Bcl.AsyncInterfaces/7.0.0": {
"runtime": {
"lib/netstandard2.1/Microsoft.Bcl.AsyncInterfaces.dll": {
"assemblyVersion": "7.0.0.0",
"fileVersion": "7.0.22.51805"
}
}
},
"Microsoft.Build.Locator/1.7.8": {
"runtime": {
"lib/net6.0/Microsoft.Build.Locator.dll": {
"assemblyVersion": "1.0.0.0",
"fileVersion": "1.7.8.28074"
}
}
},
"Microsoft.CodeAnalysis.Common/4.8.0": {
"runtime": {
"lib/net7.0/Microsoft.CodeAnalysis.dll": {
"assemblyVersion": "4.8.0.0",
"fileVersion": "4.800.23.55801"
}
},
"resources": {
"lib/net7.0/cs/Microsoft.CodeAnalysis.resources.dll": {
"locale": "cs"
},
"lib/net7.0/de/Microsoft.CodeAnalysis.resources.dll": {
"locale": "de"
},
"lib/net7.0/es/Microsoft.CodeAnalysis.resources.dll": {
"locale": "es"
},
"lib/net7.0/fr/Microsoft.CodeAnalysis.resources.dll": {
"locale": "fr"
},
"lib/net7.0/it/Microsoft.CodeAnalysis.resources.dll": {
"locale": "it"
},
"lib/net7.0/ja/Microsoft.CodeAnalysis.resources.dll": {
"locale": "ja"
},
"lib/net7.0/ko/Microsoft.CodeAnalysis.resources.dll": {
"locale": "ko"
},
"lib/net7.0/pl/Microsoft.CodeAnalysis.resources.dll": {
"locale": "pl"
},
"lib/net7.0/pt-BR/Microsoft.CodeAnalysis.resources.dll": {
"locale": "pt-BR"
},
"lib/net7.0/ru/Microsoft.CodeAnalysis.resources.dll": {
"locale": "ru"
},
"lib/net7.0/tr/Microsoft.CodeAnalysis.resources.dll": {
"locale": "tr"
},
"lib/net7.0/zh-Hans/Microsoft.CodeAnalysis.resources.dll": {
"locale": "zh-Hans"
},
"lib/net7.0/zh-Hant/Microsoft.CodeAnalysis.resources.dll": {
"locale": "zh-Hant"
}
}
},
"Microsoft.CodeAnalysis.CSharp/4.8.0": {
"dependencies": {
"Microsoft.CodeAnalysis.Common": "4.8.0"
},
"runtime": {
"lib/net7.0/Microsoft.CodeAnalysis.CSharp.dll": {
"assemblyVersion": "4.8.0.0",
"fileVersion": "4.800.23.55801"
}
},
"resources": {
"lib/net7.0/cs/Microsoft.CodeAnalysis.CSharp.resources.dll": {
"locale": "cs"
},
"lib/net7.0/de/Microsoft.CodeAnalysis.CSharp.resources.dll": {
"locale": "de"
},
"lib/net7.0/es/Microsoft.CodeAnalysis.CSharp.resources.dll": {
"locale": "es"
},
"lib/net7.0/fr/Microsoft.CodeAnalysis.CSharp.resources.dll": {
"locale": "fr"
},
"lib/net7.0/it/Microsoft.CodeAnalysis.CSharp.resources.dll": {
"locale": "it"
},
"lib/net7.0/ja/Microsoft.CodeAnalysis.CSharp.resources.dll": {
"locale": "ja"
},
"lib/net7.0/ko/Microsoft.CodeAnalysis.CSharp.resources.dll": {
"locale": "ko"
},
"lib/net7.0/pl/Microsoft.CodeAnalysis.CSharp.resources.dll": {
"locale": "pl"
},
"lib/net7.0/pt-BR/Microsoft.CodeAnalysis.CSharp.resources.dll": {
"locale": "pt-BR"
},
"lib/net7.0/ru/Microsoft.CodeAnalysis.CSharp.resources.dll": {
"locale": "ru"
},
"lib/net7.0/tr/Microsoft.CodeAnalysis.CSharp.resources.dll": {
"locale": "tr"
},
"lib/net7.0/zh-Hans/Microsoft.CodeAnalysis.CSharp.resources.dll": {
"locale": "zh-Hans"
},
"lib/net7.0/zh-Hant/Microsoft.CodeAnalysis.CSharp.resources.dll": {
"locale": "zh-Hant"
}
}
},
"Microsoft.CodeAnalysis.CSharp.Workspaces/4.8.0": {
"dependencies": {
"Humanizer.Core": "2.14.1",
"Microsoft.CodeAnalysis.CSharp": "4.8.0",
"Microsoft.CodeAnalysis.Common": "4.8.0",
"Microsoft.CodeAnalysis.Workspaces.Common": "4.8.0"
},
"runtime": {
"lib/net7.0/Microsoft.CodeAnalysis.CSharp.Workspaces.dll": {
"assemblyVersion": "4.8.0.0",
"fileVersion": "4.800.23.55801"
}
},
"resources": {
"lib/net7.0/cs/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll": {
"locale": "cs"
},
"lib/net7.0/de/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll": {
"locale": "de"
},
"lib/net7.0/es/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll": {
"locale": "es"
},
"lib/net7.0/fr/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll": {
"locale": "fr"
},
"lib/net7.0/it/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll": {
"locale": "it"
},
"lib/net7.0/ja/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll": {
"locale": "ja"
},
"lib/net7.0/ko/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll": {
"locale": "ko"
},
"lib/net7.0/pl/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll": {
"locale": "pl"
},
"lib/net7.0/pt-BR/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll": {
"locale": "pt-BR"
},
"lib/net7.0/ru/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll": {
"locale": "ru"
},
"lib/net7.0/tr/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll": {
"locale": "tr"
},
"lib/net7.0/zh-Hans/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll": {
"locale": "zh-Hans"
},
"lib/net7.0/zh-Hant/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll": {
"locale": "zh-Hant"
}
}
},
"Microsoft.CodeAnalysis.Workspaces.Common/4.8.0": {
"dependencies": {
"Humanizer.Core": "2.14.1",
"Microsoft.Bcl.AsyncInterfaces": "7.0.0",
"Microsoft.CodeAnalysis.Common": "4.8.0",
"System.Composition": "7.0.0"
},
"runtime": {
"lib/net7.0/Microsoft.CodeAnalysis.Workspaces.dll": {
"assemblyVersion": "4.8.0.0",
"fileVersion": "4.800.23.55801"
}
},
"resources": {
"lib/net7.0/cs/Microsoft.CodeAnalysis.Workspaces.resources.dll": {
"locale": "cs"
},
"lib/net7.0/de/Microsoft.CodeAnalysis.Workspaces.resources.dll": {
"locale": "de"
},
"lib/net7.0/es/Microsoft.CodeAnalysis.Workspaces.resources.dll": {
"locale": "es"
},
"lib/net7.0/fr/Microsoft.CodeAnalysis.Workspaces.resources.dll": {
"locale": "fr"
},
"lib/net7.0/it/Microsoft.CodeAnalysis.Workspaces.resources.dll": {
"locale": "it"
},
"lib/net7.0/ja/Microsoft.CodeAnalysis.Workspaces.resources.dll": {
"locale": "ja"
},
"lib/net7.0/ko/Microsoft.CodeAnalysis.Workspaces.resources.dll": {
"locale": "ko"
},
"lib/net7.0/pl/Microsoft.CodeAnalysis.Workspaces.resources.dll": {
"locale": "pl"
},
"lib/net7.0/pt-BR/Microsoft.CodeAnalysis.Workspaces.resources.dll": {
"locale": "pt-BR"
},
"lib/net7.0/ru/Microsoft.CodeAnalysis.Workspaces.resources.dll": {
"locale": "ru"
},
"lib/net7.0/tr/Microsoft.CodeAnalysis.Workspaces.resources.dll": {
"locale": "tr"
},
"lib/net7.0/zh-Hans/Microsoft.CodeAnalysis.Workspaces.resources.dll": {
"locale": "zh-Hans"
},
"lib/net7.0/zh-Hant/Microsoft.CodeAnalysis.Workspaces.resources.dll": {
"locale": "zh-Hant"
}
}
},
"Microsoft.CodeAnalysis.Workspaces.MSBuild/4.8.0": {
"dependencies": {
"Microsoft.CodeAnalysis.Common": "4.8.0",
"Microsoft.CodeAnalysis.Workspaces.Common": "4.8.0"
},
"runtime": {
"lib/net7.0/Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.dll": {
"assemblyVersion": "4.8.0.0",
"fileVersion": "4.800.23.55801"
},
"lib/net7.0/Microsoft.CodeAnalysis.Workspaces.MSBuild.dll": {
"assemblyVersion": "4.8.0.0",
"fileVersion": "4.800.23.55801"
}
},
"resources": {
"lib/net7.0/cs/Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.resources.dll": {
"locale": "cs"
},
"lib/net7.0/de/Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.resources.dll": {
"locale": "de"
},
"lib/net7.0/es/Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.resources.dll": {
"locale": "es"
},
"lib/net7.0/fr/Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.resources.dll": {
"locale": "fr"
},
"lib/net7.0/it/Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.resources.dll": {
"locale": "it"
},
"lib/net7.0/ja/Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.resources.dll": {
"locale": "ja"
},
"lib/net7.0/ko/Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.resources.dll": {
"locale": "ko"
},
"lib/net7.0/pl/Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.resources.dll": {
"locale": "pl"
},
"lib/net7.0/pt-BR/Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.resources.dll": {
"locale": "pt-BR"
},
"lib/net7.0/ru/Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.resources.dll": {
"locale": "ru"
},
"lib/net7.0/tr/Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.resources.dll": {
"locale": "tr"
},
"lib/net7.0/zh-Hans/Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.resources.dll": {
"locale": "zh-Hans"
},
"lib/net7.0/zh-Hant/Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.resources.dll": {
"locale": "zh-Hant"
}
}
},
"Microsoft.EntityFrameworkCore/9.0.0": {
"dependencies": {
"Microsoft.EntityFrameworkCore.Abstractions": "9.0.0"
},
"runtime": {
"lib/net8.0/Microsoft.EntityFrameworkCore.dll": {
"assemblyVersion": "9.0.0.0",
"fileVersion": "9.0.24.52902"
}
}
},
"Microsoft.EntityFrameworkCore.Abstractions/9.0.0": {
"runtime": {
"lib/net8.0/Microsoft.EntityFrameworkCore.Abstractions.dll": {
"assemblyVersion": "9.0.0.0",
"fileVersion": "9.0.24.52902"
}
}
},
"Microsoft.EntityFrameworkCore.Design/9.0.0": {
"dependencies": {
"Humanizer.Core": "2.14.1",
"Microsoft.Build.Locator": "1.7.8",
"Microsoft.CodeAnalysis.CSharp": "4.8.0",
"Microsoft.CodeAnalysis.CSharp.Workspaces": "4.8.0",
"Microsoft.CodeAnalysis.Workspaces.MSBuild": "4.8.0",
"Microsoft.EntityFrameworkCore.Relational": "9.0.0",
"Microsoft.Extensions.DependencyModel": "9.0.0",
"Mono.TextTemplating": "3.0.0"
},
"runtime": {
"lib/net8.0/Microsoft.EntityFrameworkCore.Design.dll": {
"assemblyVersion": "9.0.0.0",
"fileVersion": "9.0.24.52902"
}
}
},
"Microsoft.EntityFrameworkCore.Relational/9.0.0": {
"dependencies": {
"Microsoft.EntityFrameworkCore": "9.0.0"
},
"runtime": {
"lib/net8.0/Microsoft.EntityFrameworkCore.Relational.dll": {
"assemblyVersion": "9.0.0.0",
"fileVersion": "9.0.24.52902"
}
}
},
"Microsoft.Extensions.DependencyModel/9.0.0": {
"runtime": {
"lib/net9.0/Microsoft.Extensions.DependencyModel.dll": {
"assemblyVersion": "9.0.0.0",
"fileVersion": "9.0.24.52809"
}
}
},
"Microsoft.IdentityModel.Abstractions/8.0.1": {
"runtime": {
"lib/net9.0/Microsoft.IdentityModel.Abstractions.dll": {
"assemblyVersion": "8.0.1.0",
"fileVersion": "8.0.1.50722"
}
}
},
"Microsoft.IdentityModel.JsonWebTokens/8.0.1": {
"dependencies": {
"Microsoft.IdentityModel.Tokens": "8.0.1"
},
"runtime": {
"lib/net9.0/Microsoft.IdentityModel.JsonWebTokens.dll": {
"assemblyVersion": "8.0.1.0",
"fileVersion": "8.0.1.50722"
}
}
},
"Microsoft.IdentityModel.Logging/8.0.1": {
"dependencies": {
"Microsoft.IdentityModel.Abstractions": "8.0.1"
},
"runtime": {
"lib/net9.0/Microsoft.IdentityModel.Logging.dll": {
"assemblyVersion": "8.0.1.0",
"fileVersion": "8.0.1.50722"
}
}
},
"Microsoft.IdentityModel.Protocols/8.0.1": {
"dependencies": {
"Microsoft.IdentityModel.Tokens": "8.0.1"
},
"runtime": {
"lib/net9.0/Microsoft.IdentityModel.Protocols.dll": {
"assemblyVersion": "8.0.1.0",
"fileVersion": "8.0.1.50722"
}
}
},
"Microsoft.IdentityModel.Protocols.OpenIdConnect/8.0.1": {
"dependencies": {
"Microsoft.IdentityModel.Protocols": "8.0.1",
"System.IdentityModel.Tokens.Jwt": "8.0.1"
},
"runtime": {
"lib/net9.0/Microsoft.IdentityModel.Protocols.OpenIdConnect.dll": {
"assemblyVersion": "8.0.1.0",
"fileVersion": "8.0.1.50722"
}
}
},
"Microsoft.IdentityModel.Tokens/8.0.1": {
"dependencies": {
"Microsoft.IdentityModel.Logging": "8.0.1"
},
"runtime": {
"lib/net9.0/Microsoft.IdentityModel.Tokens.dll": {
"assemblyVersion": "8.0.1.0",
"fileVersion": "8.0.1.50722"
}
}
},
"Microsoft.OpenApi/1.6.22": {
"runtime": {
"lib/netstandard2.0/Microsoft.OpenApi.dll": {
"assemblyVersion": "1.6.22.0",
"fileVersion": "1.6.22.0"
}
}
},
"Mono.TextTemplating/3.0.0": {
"dependencies": {
"System.CodeDom": "6.0.0"
},
"runtime": {
"lib/net6.0/Mono.TextTemplating.dll": {
"assemblyVersion": "3.0.0.0",
"fileVersion": "3.0.0.1"
}
}
},
"MySqlConnector/2.4.0": {
"runtime": {
"lib/net9.0/MySqlConnector.dll": {
"assemblyVersion": "2.0.0.0",
"fileVersion": "2.4.0.0"
}
}
},
"Pomelo.EntityFrameworkCore.MySql/9.0.0": {
"dependencies": {
"Microsoft.EntityFrameworkCore.Relational": "9.0.0",
"MySqlConnector": "2.4.0"
},
"runtime": {
"lib/net8.0/Pomelo.EntityFrameworkCore.MySql.dll": {
"assemblyVersion": "9.0.0.0",
"fileVersion": "9.0.0.0"
}
}
},
"Swashbuckle.AspNetCore/7.3.1": {
"dependencies": {
"Swashbuckle.AspNetCore.Swagger": "7.3.1",
"Swashbuckle.AspNetCore.SwaggerGen": "7.3.1",
"Swashbuckle.AspNetCore.SwaggerUI": "7.3.1"
}
},
"Swashbuckle.AspNetCore.Swagger/7.3.1": {
"dependencies": {
"Microsoft.OpenApi": "1.6.22"
},
"runtime": {
"lib/net9.0/Swashbuckle.AspNetCore.Swagger.dll": {
"assemblyVersion": "7.3.1.0",
"fileVersion": "7.3.1.1111"
}
}
},
"Swashbuckle.AspNetCore.SwaggerGen/7.3.1": {
"dependencies": {
"Swashbuckle.AspNetCore.Swagger": "7.3.1"
},
"runtime": {
"lib/net9.0/Swashbuckle.AspNetCore.SwaggerGen.dll": {
"assemblyVersion": "7.3.1.0",
"fileVersion": "7.3.1.1111"
}
}
},
"Swashbuckle.AspNetCore.SwaggerUI/7.3.1": {
"runtime": {
"lib/net9.0/Swashbuckle.AspNetCore.SwaggerUI.dll": {
"assemblyVersion": "7.3.1.0",
"fileVersion": "7.3.1.1111"
}
}
},
"System.CodeDom/6.0.0": {
"runtime": {
"lib/net6.0/System.CodeDom.dll": {
"assemblyVersion": "6.0.0.0",
"fileVersion": "6.0.21.52210"
}
}
},
"System.Composition/7.0.0": {
"dependencies": {
"System.Composition.AttributedModel": "7.0.0",
"System.Composition.Convention": "7.0.0",
"System.Composition.Hosting": "7.0.0",
"System.Composition.Runtime": "7.0.0",
"System.Composition.TypedParts": "7.0.0"
}
},
"System.Composition.AttributedModel/7.0.0": {
"runtime": {
"lib/net7.0/System.Composition.AttributedModel.dll": {
"assemblyVersion": "7.0.0.0",
"fileVersion": "7.0.22.51805"
}
}
},
"System.Composition.Convention/7.0.0": {
"dependencies": {
"System.Composition.AttributedModel": "7.0.0"
},
"runtime": {
"lib/net7.0/System.Composition.Convention.dll": {
"assemblyVersion": "7.0.0.0",
"fileVersion": "7.0.22.51805"
}
}
},
"System.Composition.Hosting/7.0.0": {
"dependencies": {
"System.Composition.Runtime": "7.0.0"
},
"runtime": {
"lib/net7.0/System.Composition.Hosting.dll": {
"assemblyVersion": "7.0.0.0",
"fileVersion": "7.0.22.51805"
}
}
},
"System.Composition.Runtime/7.0.0": {
"runtime": {
"lib/net7.0/System.Composition.Runtime.dll": {
"assemblyVersion": "7.0.0.0",
"fileVersion": "7.0.22.51805"
}
}
},
"System.Composition.TypedParts/7.0.0": {
"dependencies": {
"System.Composition.AttributedModel": "7.0.0",
"System.Composition.Hosting": "7.0.0",
"System.Composition.Runtime": "7.0.0"
},
"runtime": {
"lib/net7.0/System.Composition.TypedParts.dll": {
"assemblyVersion": "7.0.0.0",
"fileVersion": "7.0.22.51805"
}
}
},
"System.IdentityModel.Tokens.Jwt/8.0.1": {
"dependencies": {
"Microsoft.IdentityModel.JsonWebTokens": "8.0.1",
"Microsoft.IdentityModel.Tokens": "8.0.1"
},
"runtime": {
"lib/net9.0/System.IdentityModel.Tokens.Jwt.dll": {
"assemblyVersion": "8.0.1.0",
"fileVersion": "8.0.1.50722"
}
}
}
}
},
"libraries": {
"Distribution/1.0.0": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Humanizer.Core/2.14.1": {
"type": "package",
"serviceable": true,
"sha512": "sha512-lQKvtaTDOXnoVJ20ibTuSIOf2i0uO0MPbDhd1jm238I+U/2ZnRENj0cktKZhtchBMtCUSRQ5v4xBCUbKNmyVMw==",
"path": "humanizer.core/2.14.1",
"hashPath": "humanizer.core.2.14.1.nupkg.sha512"
},
"Microsoft.AspNetCore.Authentication.JwtBearer/9.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-bs+1Pq3vQdS2lTyxNUd9fEhtMsq3eLUpK36k2t56iDMVrk6OrAoFtvrQrTK0Y0OetTcJrUkGU7hBlf+ORzHLqQ==",
"path": "microsoft.aspnetcore.authentication.jwtbearer/9.0.0",
"hashPath": "microsoft.aspnetcore.authentication.jwtbearer.9.0.0.nupkg.sha512"
},
"Microsoft.AspNetCore.OpenApi/9.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-FqUK5j1EOPNuFT7IafltZQ3cakqhSwVzH5ZW1MhZDe4pPXs9sJ2M5jom1Omsu+mwF2tNKKlRAzLRHQTZzbd+6Q==",
"path": "microsoft.aspnetcore.openapi/9.0.0",
"hashPath": "microsoft.aspnetcore.openapi.9.0.0.nupkg.sha512"
},
"Microsoft.Bcl.AsyncInterfaces/7.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-3aeMZ1N0lJoSyzqiP03hqemtb1BijhsJADdobn/4nsMJ8V1H+CrpuduUe4hlRdx+ikBQju1VGjMD1GJ3Sk05Eg==",
"path": "microsoft.bcl.asyncinterfaces/7.0.0",
"hashPath": "microsoft.bcl.asyncinterfaces.7.0.0.nupkg.sha512"
},
"Microsoft.Build.Locator/1.7.8": {
"type": "package",
"serviceable": true,
"sha512": "sha512-sPy10x527Ph16S2u0yGME4S6ohBKJ69WfjeGG/bvELYeZVmJdKjxgnlL8cJJJLGV/cZIRqSfB12UDB8ICakOog==",
"path": "microsoft.build.locator/1.7.8",
"hashPath": "microsoft.build.locator.1.7.8.nupkg.sha512"
},
"Microsoft.CodeAnalysis.Common/4.8.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-/jR+e/9aT+BApoQJABlVCKnnggGQbvGh7BKq2/wI1LamxC+LbzhcLj4Vj7gXCofl1n4E521YfF9w0WcASGg/KA==",
"path": "microsoft.codeanalysis.common/4.8.0",
"hashPath": "microsoft.codeanalysis.common.4.8.0.nupkg.sha512"
},
"Microsoft.CodeAnalysis.CSharp/4.8.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-+3+qfdb/aaGD8PZRCrsdobbzGs1m9u119SkkJt8e/mk3xLJz/udLtS2T6nY27OTXxBBw10HzAbC8Z9w08VyP/g==",
"path": "microsoft.codeanalysis.csharp/4.8.0",
"hashPath": "microsoft.codeanalysis.csharp.4.8.0.nupkg.sha512"
},
"Microsoft.CodeAnalysis.CSharp.Workspaces/4.8.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-3amm4tq4Lo8/BGvg9p3BJh3S9nKq2wqCXfS7138i69TUpo/bD+XvD0hNurpEBtcNZhi1FyutiomKJqVF39ugYA==",
"path": "microsoft.codeanalysis.csharp.workspaces/4.8.0",
"hashPath": "microsoft.codeanalysis.csharp.workspaces.4.8.0.nupkg.sha512"
},
"Microsoft.CodeAnalysis.Workspaces.Common/4.8.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-LXyV+MJKsKRu3FGJA3OmSk40OUIa/dQCFLOnm5X8MNcujx7hzGu8o+zjXlb/cy5xUdZK2UKYb9YaQ2E8m9QehQ==",
"path": "microsoft.codeanalysis.workspaces.common/4.8.0",
"hashPath": "microsoft.codeanalysis.workspaces.common.4.8.0.nupkg.sha512"
},
"Microsoft.CodeAnalysis.Workspaces.MSBuild/4.8.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-IEYreI82QZKklp54yPHxZNG9EKSK6nHEkeuf+0Asie9llgS1gp0V1hw7ODG+QyoB7MuAnNQHmeV1Per/ECpv6A==",
"path": "microsoft.codeanalysis.workspaces.msbuild/4.8.0",
"hashPath": "microsoft.codeanalysis.workspaces.msbuild.4.8.0.nupkg.sha512"
},
"Microsoft.EntityFrameworkCore/9.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-wpG+nfnfDAw87R3ovAsUmjr3MZ4tYXf6bFqEPVAIKE6IfPml3DS//iX0DBnf8kWn5ZHSO5oi1m4d/Jf+1LifJQ==",
"path": "microsoft.entityframeworkcore/9.0.0",
"hashPath": "microsoft.entityframeworkcore.9.0.0.nupkg.sha512"
},
"Microsoft.EntityFrameworkCore.Abstractions/9.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-fnmifFL8KaA4ZNLCVgfjCWhZUFxkrDInx5hR4qG7Q8IEaSiy/6VOSRFyx55oH7MV4y7wM3J3EE90nSpcVBI44Q==",
"path": "microsoft.entityframeworkcore.abstractions/9.0.0",
"hashPath": "microsoft.entityframeworkcore.abstractions.9.0.0.nupkg.sha512"
},
"Microsoft.EntityFrameworkCore.Design/9.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-Pqo8I+yHJ3VQrAoY0hiSncf+5P7gN/RkNilK5e+/K/yKh+yAWxdUAI6t0TG26a9VPlCa9FhyklzyFvRyj3YG9A==",
"path": "microsoft.entityframeworkcore.design/9.0.0",
"hashPath": "microsoft.entityframeworkcore.design.9.0.0.nupkg.sha512"
},
"Microsoft.EntityFrameworkCore.Relational/9.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-j+msw6fWgAE9M3Q/5B9Uhv7pdAdAQUvFPJAiBJmoy+OXvehVbfbCE8ftMAa51Uo2ZeiqVnHShhnv4Y4UJJmUzA==",
"path": "microsoft.entityframeworkcore.relational/9.0.0",
"hashPath": "microsoft.entityframeworkcore.relational.9.0.0.nupkg.sha512"
},
"Microsoft.Extensions.DependencyModel/9.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-saxr2XzwgDU77LaQfYFXmddEDRUKHF4DaGMZkNB3qjdVSZlax3//dGJagJkKrGMIPNZs2jVFXITyCCR6UHJNdA==",
"path": "microsoft.extensions.dependencymodel/9.0.0",
"hashPath": "microsoft.extensions.dependencymodel.9.0.0.nupkg.sha512"
},
"Microsoft.IdentityModel.Abstractions/8.0.1": {
"type": "package",
"serviceable": true,
"sha512": "sha512-OtlIWcyX01olfdevPKZdIPfBEvbcioDyBiE/Z2lHsopsMD7twcKtlN9kMevHmI5IIPhFpfwCIiR6qHQz1WHUIw==",
"path": "microsoft.identitymodel.abstractions/8.0.1",
"hashPath": "microsoft.identitymodel.abstractions.8.0.1.nupkg.sha512"
},
"Microsoft.IdentityModel.JsonWebTokens/8.0.1": {
"type": "package",
"serviceable": true,
"sha512": "sha512-s6++gF9x0rQApQzOBbSyp4jUaAlwm+DroKfL8gdOHxs83k8SJfUXhuc46rDB3rNXBQ1MVRxqKUrqFhO/M0E97g==",
"path": "microsoft.identitymodel.jsonwebtokens/8.0.1",
"hashPath": "microsoft.identitymodel.jsonwebtokens.8.0.1.nupkg.sha512"
},
"Microsoft.IdentityModel.Logging/8.0.1": {
"type": "package",
"serviceable": true,
"sha512": "sha512-UCPF2exZqBXe7v/6sGNiM6zCQOUXXQ9+v5VTb9gPB8ZSUPnX53BxlN78v2jsbIvK9Dq4GovQxo23x8JgWvm/Qg==",
"path": "microsoft.identitymodel.logging/8.0.1",
"hashPath": "microsoft.identitymodel.logging.8.0.1.nupkg.sha512"
},
"Microsoft.IdentityModel.Protocols/8.0.1": {
"type": "package",
"serviceable": true,
"sha512": "sha512-uA2vpKqU3I2mBBEaeJAWPTjT9v1TZrGWKdgK6G5qJd03CLx83kdiqO9cmiK8/n1erkHzFBwU/RphP83aAe3i3g==",
"path": "microsoft.identitymodel.protocols/8.0.1",
"hashPath": "microsoft.identitymodel.protocols.8.0.1.nupkg.sha512"
},
"Microsoft.IdentityModel.Protocols.OpenIdConnect/8.0.1": {
"type": "package",
"serviceable": true,
"sha512": "sha512-AQDbfpL+yzuuGhO/mQhKNsp44pm5Jv8/BI4KiFXR7beVGZoSH35zMV3PrmcfvSTsyI6qrcR898NzUauD6SRigg==",
"path": "microsoft.identitymodel.protocols.openidconnect/8.0.1",
"hashPath": "microsoft.identitymodel.protocols.openidconnect.8.0.1.nupkg.sha512"
},
"Microsoft.IdentityModel.Tokens/8.0.1": {
"type": "package",
"serviceable": true,
"sha512": "sha512-kDimB6Dkd3nkW2oZPDkMkVHfQt3IDqO5gL0oa8WVy3OP4uE8Ij+8TXnqg9TOd9ufjsY3IDiGz7pCUbnfL18tjg==",
"path": "microsoft.identitymodel.tokens/8.0.1",
"hashPath": "microsoft.identitymodel.tokens.8.0.1.nupkg.sha512"
},
"Microsoft.OpenApi/1.6.22": {
"type": "package",
"serviceable": true,
"sha512": "sha512-aBvunmrdu/x+4CaA/UP1Jx4xWGwk4kymhoIRnn2Vp+zi5/KOPQJ9EkSXHRUr01WcGKtYl3Au7XfkPJbU1G2sjQ==",
"path": "microsoft.openapi/1.6.22",
"hashPath": "microsoft.openapi.1.6.22.nupkg.sha512"
},
"Mono.TextTemplating/3.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-YqueG52R/Xej4VVbKuRIodjiAhV0HR/XVbLbNrJhCZnzjnSjgMJ/dCdV0akQQxavX6hp/LC6rqLGLcXeQYU7XA==",
"path": "mono.texttemplating/3.0.0",
"hashPath": "mono.texttemplating.3.0.0.nupkg.sha512"
},
"MySqlConnector/2.4.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-78M+gVOjbdZEDIyXQqcA7EYlCGS3tpbUELHvn6638A2w0pkPI625ixnzsa5staAd3N9/xFmPJtkKDYwsXpFi/w==",
"path": "mysqlconnector/2.4.0",
"hashPath": "mysqlconnector.2.4.0.nupkg.sha512"
},
"Pomelo.EntityFrameworkCore.MySql/9.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-cl7S4s6CbJno0LjNxrBHNc2xxmCliR5i40ATPZk/eTywVaAbHCbdc9vbGc3QThvwGjHqrDHT8vY9m1VF/47o0g==",
"path": "pomelo.entityframeworkcore.mysql/9.0.0",
"hashPath": "pomelo.entityframeworkcore.mysql.9.0.0.nupkg.sha512"
},
"Swashbuckle.AspNetCore/7.3.1": {
"type": "package",
"serviceable": true,
"sha512": "sha512-6u8w+UXp/sF89xQjfydWw6znQrUpbpFOmEIs8ODE+S0bV+mCQ9dNP4mk+HRsGHylpDaP5KSYSCEfFSgluLXHsA==",
"path": "swashbuckle.aspnetcore/7.3.1",
"hashPath": "swashbuckle.aspnetcore.7.3.1.nupkg.sha512"
},
"Swashbuckle.AspNetCore.Swagger/7.3.1": {
"type": "package",
"serviceable": true,
"sha512": "sha512-jQuJ8kVbq+YE8WsJE3RwWHlF1kasp0QkA9Gl6NeNLICrhcgN8IQIthMufYW6t/4hpcN5cBIdES5jCEV81WjHbA==",
"path": "swashbuckle.aspnetcore.swagger/7.3.1",
"hashPath": "swashbuckle.aspnetcore.swagger.7.3.1.nupkg.sha512"
},
"Swashbuckle.AspNetCore.SwaggerGen/7.3.1": {
"type": "package",
"serviceable": true,
"sha512": "sha512-xs7Pznb3SSjZy7HpThE0ILqECfQFsGDHOrRoIYD/j67ktdRR1juDG4AMyidXXCOipgzHanZoF+nFrc+Nmjqjyw==",
"path": "swashbuckle.aspnetcore.swaggergen/7.3.1",
"hashPath": "swashbuckle.aspnetcore.swaggergen.7.3.1.nupkg.sha512"
},
"Swashbuckle.AspNetCore.SwaggerUI/7.3.1": {
"type": "package",
"serviceable": true,
"sha512": "sha512-hs6C+lmNEzipOA1WPQpIaGvvoXjUbnoevbv6l7o9ZQE8SNF8ggjOmK6NB6cYdMcEvk0uBeKl4Qq/BnRt5MFVqg==",
"path": "swashbuckle.aspnetcore.swaggerui/7.3.1",
"hashPath": "swashbuckle.aspnetcore.swaggerui.7.3.1.nupkg.sha512"
},
"System.CodeDom/6.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-CPc6tWO1LAer3IzfZufDBRL+UZQcj5uS207NHALQzP84Vp/z6wF0Aa0YZImOQY8iStY0A2zI/e3ihKNPfUm8XA==",
"path": "system.codedom/6.0.0",
"hashPath": "system.codedom.6.0.0.nupkg.sha512"
},
"System.Composition/7.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-tRwgcAkDd85O8Aq6zHDANzQaq380cek9lbMg5Qma46u5BZXq/G+XvIYmu+UI+BIIZ9zssXLYrkTykEqxxvhcmg==",
"path": "system.composition/7.0.0",
"hashPath": "system.composition.7.0.0.nupkg.sha512"
},
"System.Composition.AttributedModel/7.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-2QzClqjElKxgI1jK1Jztnq44/8DmSuTSGGahXqQ4TdEV0h9s2KikQZIgcEqVzR7OuWDFPGLHIprBJGQEPr8fAQ==",
"path": "system.composition.attributedmodel/7.0.0",
"hashPath": "system.composition.attributedmodel.7.0.0.nupkg.sha512"
},
"System.Composition.Convention/7.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-IMhTlpCs4HmlD8B+J8/kWfwX7vrBBOs6xyjSTzBlYSs7W4OET4tlkR/Sg9NG8jkdJH9Mymq0qGdYS1VPqRTBnQ==",
"path": "system.composition.convention/7.0.0",
"hashPath": "system.composition.convention.7.0.0.nupkg.sha512"
},
"System.Composition.Hosting/7.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-eB6gwN9S+54jCTBJ5bpwMOVerKeUfGGTYCzz3QgDr1P55Gg/Wb27ShfPIhLMjmZ3MoAKu8uUSv6fcCdYJTN7Bg==",
"path": "system.composition.hosting/7.0.0",
"hashPath": "system.composition.hosting.7.0.0.nupkg.sha512"
},
"System.Composition.Runtime/7.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-aZJ1Zr5Txe925rbo4742XifEyW0MIni1eiUebmcrP3HwLXZ3IbXUj4MFMUH/RmnJOAQiS401leg/2Sz1MkApDw==",
"path": "system.composition.runtime/7.0.0",
"hashPath": "system.composition.runtime.7.0.0.nupkg.sha512"
},
"System.Composition.TypedParts/7.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-ZK0KNPfbtxVceTwh+oHNGUOYV2WNOHReX2AXipuvkURC7s/jPwoWfsu3SnDBDgofqbiWr96geofdQ2erm/KTHg==",
"path": "system.composition.typedparts/7.0.0",
"hashPath": "system.composition.typedparts.7.0.0.nupkg.sha512"
},
"System.IdentityModel.Tokens.Jwt/8.0.1": {
"type": "package",
"serviceable": true,
"sha512": "sha512-GJw3bYkWpOgvN3tJo5X4lYUeIFA2HD293FPUhKmp7qxS+g5ywAb34Dnd3cDAFLkcMohy5XTpoaZ4uAHuw0uSPQ==",
"path": "system.identitymodel.tokens.jwt/8.0.1",
"hashPath": "system.identitymodel.tokens.jwt.8.0.1.nupkg.sha512"
}
}
}
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,20 @@
{
"runtimeOptions": {
"tfm": "net9.0",
"frameworks": [
{
"name": "Microsoft.NETCore.App",
"version": "9.0.0"
},
{
"name": "Microsoft.AspNetCore.App",
"version": "9.0.0"
}
],
"configProperties": {
"System.GC.Server": true,
"System.Reflection.NullabilityInfoContext.IsSupported": true,
"System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization": false
}
}
}
@@ -0,0 +1 @@
{"Version":1,"ManifestType":"Build","Endpoints":[]}
Binary file not shown.
@@ -0,0 +1,18 @@
{
"ConnectionStrings": {
"DefaultConnection": "Server=187.127.102.190;Port=3306;Database=distribution;User=kalana;Password=kalana@hexdive"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"Jwt": {
"Key": "CHANGE_ME_dev_only_replace_with_a_long_random_secret_before_deploying",
"Issuer": "Distribution.Api",
"Audience": "Distribution.Api",
"ExpiryMinutes": 120
}
}

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