diff --git a/.github/copilot-review-instructions.md b/.github/copilot-review-instructions.md new file mode 100644 index 00000000..3c69922b --- /dev/null +++ b/.github/copilot-review-instructions.md @@ -0,0 +1,234 @@ +# GitHub Copilot Review Instructions for Route4Me .NET Core SDK + +## Project Overview +This is the Route4Me Route Optimization SaaS C# SDK (.NET Core framework) that provides a comprehensive API wrapper for route optimization, fleet management, and logistics operations. The SDK supports both V4 and V5 API versions with a focus on performance, reliability, and maintainability. + +## Architecture & Design Patterns + +### Manager Pattern +- **Base Class**: All managers inherit from `Route4MeManagerBase` +- **Naming Convention**: `{Domain}ManagerV5` (e.g., `OptimizationManagerV5`, `VehicleManagerV5`) +- **Constructor**: Always takes `string apiKey` parameter +- **API Methods**: Provide both synchronous and asynchronous versions +- **Return Pattern**: Use `out ResultResponse resultResponse` for error handling + +```csharp +public class ExampleManagerV5 : Route4MeManagerBase +{ + public ExampleManagerV5(string apiKey) : base(apiKey) { } + + public DataType GetData(Parameters parameters, out ResultResponse resultResponse) + { + return GetJsonObjectFromAPI(parameters, url, HttpMethodType.Get, out resultResponse); + } + + public async Task> GetDataAsync(Parameters parameters) + { + var result = await GetJsonObjectFromAPIAsync(parameters, url, HttpMethodType.Get); + return new Tuple(result.Item1, result.Item2); + } +} +``` + +### Data Types & Serialization +- **DataContract**: All data types use `[DataContract]` and `[DataMember]` attributes +- **JSON Mapping**: Use `[DataMember(Name = "json_property_name")]` for API field mapping +- **Documentation**: Include comprehensive XML documentation for all public properties +- **Versioning**: V5 types are in `Route4MeSDK.DataTypes.V5` namespace + +```csharp +[DataContract] +public class ExampleResponse +{ + /// + /// Detailed description of the property and its purpose + /// + [DataMember(Name = "property_name")] + public string PropertyName { get; set; } +} +``` + +### Error Handling +- **ResultResponse**: Use `ResultResponse` class for consistent error handling +- **Exception Types**: Catch specific exceptions (`HttpListenerException`, `Exception`) +- **Error Messages**: Structure errors in `Dictionary` format +- **Status Codes**: Always check `response.IsSuccessStatusCode` + +```csharp +catch (HttpListenerException e) +{ + resultResponse = new ResultResponse + { + Status = false, + Messages = new Dictionary + { + {"Error", new[] {e.Message}} + } + }; +} +``` + +## Code Quality Standards + +### Documentation Requirements +- **XML Documentation**: All public methods, properties, and classes must have comprehensive XML documentation +- **Parameter Documentation**: Document all parameters with purpose and constraints +- **Return Documentation**: Document return values and possible error conditions +- **Example Usage**: Include usage examples in complex method documentation + +### Naming Conventions +- **Classes**: PascalCase (e.g., `OptimizationManagerV5`) +- **Methods**: PascalCase (e.g., `GetVehicles`, `CreateOrder`) +- **Properties**: PascalCase (e.g., `RouteId`, `MemberEmail`) +- **Parameters**: camelCase (e.g., `vehicleParams`, `optimizationParameters`) +- **Private Fields**: camelCase with underscore prefix (e.g., `_apiKey`) + +### Async/Await Patterns +- **Async Methods**: Always provide async versions of public API methods +- **ConfigureAwait**: Use `.ConfigureAwait(false)` in library code +- **Return Types**: Use `Task>` for async methods +- **Cancellation**: Support `CancellationToken` where appropriate + +### HTTP Client Management +- **HttpClientHolder**: Use `HttpClientHolderManager.AcquireHttpClientHolder()` for HTTP client management +- **Disposal**: Always use `using` statements for HTTP client holders +- **V5 Detection**: Use `R4MeUtils.IsV5(url)` to determine API version +- **Authentication**: Pass API key to V5 endpoints via HTTP client holder + +## API Integration Patterns + +### URL Construction +- **Base URLs**: Use constants from `R4MEInfrastructureSettings` and `R4MEInfrastructureSettingsV5` +- **Parameter Serialization**: Use `optimizationParameters.Serialize(v5 ? null : ApiKey)` +- **Query Parameters**: Append serialized parameters to base URL + +### HTTP Methods +- **GET**: For data retrieval operations +- **POST**: For creation operations +- **PUT**: For full updates +- **PATCH**: For partial updates +- **DELETE**: For deletion operations + +### Content Handling +- **JSON Content**: Use `StringContent` with `application/json` content type +- **PATCH Content**: Use `application/json-patch+json` for PATCH operations +- **Stream Processing**: Handle both `StreamContent` and other content types + +## Testing & Examples + +### Test Structure +- **Example Classes**: Use `Route4MeExamples` partial class for examples +- **Test Data**: Use `ActualApiKey` and `DemoApiKey` from configuration +- **Cleanup**: Always clean up test data using removal lists +- **CRUD Operations**: Follow Create-Read-Update-Delete pattern in examples + +### Configuration +- **API Keys**: Store in `appsettings.json` with `actualApiKey` and `demoApiKey` +- **Environment**: Support both production and demo environments +- **Settings**: Use `R4MeUtils.ReadSetting()` for configuration access + +## Performance Considerations + +### HTTP Client Reuse +- **Connection Pooling**: Leverage `HttpClientHolderManager` for connection reuse +- **Timeout Handling**: Implement appropriate timeout values +- **Retry Logic**: Consider implementing retry mechanisms for transient failures + +### Memory Management +- **Disposal**: Properly dispose of HTTP clients and streams +- **Large Responses**: Handle large response streams efficiently +- **Object Pooling**: Consider object pooling for frequently created objects + +## Security Guidelines + +### API Key Management +- **Secure Storage**: Never hardcode API keys in source code +- **Environment Variables**: Use configuration files or environment variables +- **Key Rotation**: Support API key rotation without code changes + +### Data Validation +- **Input Validation**: Validate all input parameters +- **Sanitization**: Sanitize user inputs before API calls +- **Error Information**: Avoid exposing sensitive information in error messages + +## Version Compatibility + +### V4 vs V5 APIs +- **Version Detection**: Use `R4MeUtils.IsV5(url)` for version-specific logic +- **Backward Compatibility**: Maintain V4 support while adding V5 features +- **Namespace Separation**: Keep V5 types in separate namespaces +- **Manager Separation**: Use separate manager classes for V4 and V5 + +### .NET Standard +- **Target Framework**: Maintain `netstandard2.0` compatibility +- **Dependencies**: Use compatible package versions +- **Cross-Platform**: Ensure cross-platform compatibility + +## Code Review Checklist + +### Before Submitting +- [ ] All public methods have XML documentation +- [ ] Both sync and async versions provided for API methods +- [ ] Proper error handling with `ResultResponse` +- [ ] HTTP client properly disposed +- [ ] API version detection implemented correctly +- [ ] Test examples provided for new functionality +- [ ] Configuration properly externalized +- [ ] No hardcoded values or API keys + +### Code Quality +- [ ] Follows established naming conventions +- [ ] Uses appropriate design patterns +- [ ] Handles exceptions properly +- [ ] Implements proper disposal patterns +- [ ] Uses async/await correctly +- [ ] Includes comprehensive error messages +- [ ] Maintains backward compatibility + +### Testing +- [ ] Example code provided +- [ ] Test data cleanup implemented +- [ ] Both success and error scenarios covered +- [ ] Configuration properly set up +- [ ] Documentation matches implementation + +## Common Pitfalls to Avoid + +1. **Hardcoded API Keys**: Never commit API keys to source control +2. **Missing Async Methods**: Always provide async versions of public API methods +3. **Improper Disposal**: Always dispose of HTTP clients and streams +4. **Generic Exception Handling**: Catch specific exception types, not just `Exception` +5. **Missing Documentation**: All public APIs must be documented +6. **Version Mixing**: Don't mix V4 and V5 logic in the same method +7. **Memory Leaks**: Ensure proper disposal of disposable resources +8. **Inconsistent Error Handling**: Use `ResultResponse` consistently + +## Dependencies & Packages + +### Core Dependencies +- `Newtonsoft.Json` (13.0.2) - JSON serialization +- `Microsoft.Extensions.Configuration.*` - Configuration management +- `System.Collections.Immutable` - Immutable collections +- `System.ComponentModel.Annotations` - Data annotations + +### Development Dependencies +- `CsvHelper` - CSV file processing +- `fastJSON` - Alternative JSON processing +- `SocketIoClientDotNet.Standard` - WebSocket support + +## Release & Versioning + +### Version Management +- **Semantic Versioning**: Follow semantic versioning principles +- **Changelog**: Maintain detailed changelog in `CHANGELOG.md` +- **Assembly Signing**: Use strong name signing with `r4m_csharp_sdk.snk` +- **Package Metadata**: Keep package metadata up to date + +### Distribution +- **NuGet Package**: Publish to NuGet with proper metadata +- **GitHub Releases**: Tag releases in GitHub +- **Documentation**: Update README and examples for new features + +--- + +*This document should be reviewed and updated as the project evolves. All team members should familiarize themselves with these guidelines before contributing to the codebase.* diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..22699be4 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,116 @@ +# ============================================================================= +# Dockerfile for Route4Me C# SDK Testing Environment +# +# This Dockerfile creates a containerized environment for building and testing +# the Route4Me C# SDK, based on the Travis CI configuration. +# +# Usage: +# Build: docker build -t route4me-sdk-test . +# Run tests: docker run --rm route4me-sdk-test +# Interactive shell: docker run -it --rm route4me-sdk-test /bin/bash +# +# ============================================================================= + +# Use the official .NET 6.0 SDK image as base +# This provides both build and runtime capabilities for .NET 6.0 projects +# Specify platform to avoid QEMU emulation issues on Apple Silicon +FROM --platform=linux/amd64 mcr.microsoft.com/dotnet/sdk:6.0 AS base + +# Set environment variables for non-interactive installation +ENV DEBIAN_FRONTEND=noninteractive +ENV DOTNET_CLI_TELEMETRY_OPTOUT=1 +ENV DOTNET_SKIP_FIRST_TIME_EXPERIENCE=1 + +# Install additional dependencies that might be needed for testing +# and development tools +RUN apt-get update && apt-get install -y \ + git \ + curl \ + wget \ + unzip \ + ca-certificates \ + && rm -rf /var/lib/apt/lists/* + +# Set the working directory +WORKDIR /app + +# Copy the entire project structure +# This includes all source code, test projects, and data files +COPY . . + +# ============================================================================= +# Build Stage +# ============================================================================= +FROM base AS build + +# Restore dependencies for the entire solution +# This matches the Travis CI command: dotnet restore ./route4me-csharp-sdk/Route4MeSDK.sln +RUN dotnet restore ./route4me-csharp-sdk/Route4MeSDK.sln + +# Build the SDK library +# Note: SDK library targets netstandard2.0, so no framework override needed +RUN dotnet build -v q -c Release ./route4me-csharp-sdk/Route4MeSDKLibrary/Route4MeSDKLibrary.csproj + +# Build the unit test projects +# Note: Test projects target net6.0, so no framework override needed +RUN dotnet build -v q -c Release ./route4me-csharp-sdk/Route4MeSDKUnitTest/Route4MeSDKUnitTest.csproj +RUN dotnet build -v q -c Release ./route4me-csharp-sdk/Route4MeSdkV5UnitTest/Route4MeSdkV5UnitTest.csproj + +# ============================================================================= +# Test Stage +# ============================================================================= +FROM build AS test + +# Set the working directory to the SDK folder +WORKDIR /app/route4me-csharp-sdk + +# Run the unit tests +# Note: Test projects target net6.0, so no framework override needed +RUN dotnet test -v n -p:ParallelizeTestCollections=false -c Release --filter Category!=Beacon ./Route4MeSDKUnitTest/Route4MeSDKUnitTest.csproj + +# Run the V5 unit tests as well +RUN dotnet test -v n -p:ParallelizeTestCollections=false -c Release ./Route4MeSdkV5UnitTest/Route4MeSdkV5UnitTest.csproj + +# ============================================================================= +# Development Stage +# ============================================================================= +FROM build AS development + +# Set the working directory to the SDK folder +WORKDIR /app/route4me-csharp-sdk + +# Create a script to run all tests +RUN echo '#!/bin/bash\n\ +echo "Running Route4Me SDK Tests..."\n\ +echo "================================"\n\ +\n\ +echo "Running Unit Tests (excluding Beacon category)..."\n\ +dotnet test -v n -p:ParallelizeTestCollections=false -c Release --filter Category!=Beacon ./Route4MeSDKUnitTest/Route4MeSDKUnitTest.csproj\n\ +\n\ +echo "Running V5 Unit Tests..."\n\ +dotnet test -v n -p:ParallelizeTestCollections=false -c Release ./Route4MeSdkV5UnitTest/Route4MeSdkV5UnitTest.csproj\n\ +\n\ +echo "All tests completed!"\n\ +' > /usr/local/bin/run-tests.sh && chmod +x /usr/local/bin/run-tests.sh + +# Create a script to build the solution +RUN echo '#!/bin/bash\n\ +echo "Building Route4Me SDK..."\n\ +echo "========================"\n\ +\n\ +echo "Restoring dependencies..."\n\ +dotnet restore ./Route4MeSDK.sln\n\ +\n\ +echo "Building SDK Library..."\n\ +dotnet build -v q -c Release ./Route4MeSDKLibrary/Route4MeSDKLibrary.csproj\n\ +\n\ +echo "Building Unit Test projects..."\n\ +dotnet build -v q -c Release ./Route4MeSDKUnitTest/Route4MeSDKUnitTest.csproj\n\ +dotnet build -v q -c Release ./Route4MeSdkV5UnitTest/Route4MeSdkV5UnitTest.csproj\n\ +\n\ +echo "Build completed!"\n\ +' > /usr/local/bin/build-sdk.sh && chmod +x /usr/local/bin/build-sdk.sh + +# Set the default command to run tests +# CMD ["/usr/local/bin/run-tests.sh"] + diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 00000000..4ccb7173 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,100 @@ +# ============================================================================= +# Docker Compose configuration for Route4Me C# SDK Testing Environment +# +# This file provides convenient commands for building, testing, and developing +# the Route4Me C# SDK in a containerized environment. +# +# Usage: +# Build and run tests: docker-compose up test +# Development environment: docker-compose up dev +# Interactive shell: docker-compose run dev /bin/bash +# Build SDK only: docker-compose up build +# +# ============================================================================= + +services: + # Build service - builds the SDK and all test projects + build: + build: + context: . + dockerfile: Dockerfile + target: build + no_cache: true + container_name: route4me-sdk-build + volumes: + - .:/app + working_dir: /app/route4me-csharp-sdk + command: > + sh -c " + echo 'Building Route4Me SDK...' && + dotnet restore ./Route4MeSDK.sln && + dotnet build -v q -c Release ./Route4MeSDKLibrary/Route4MeSDKLibrary.csproj && + dotnet build -v q -c Release ./Route4MeSDKUnitTest/Route4MeSDKUnitTest.csproj && + dotnet build -v q -c Release ./Route4MeSdkV5UnitTest/Route4MeSdkV5UnitTest.csproj && + echo 'Build completed successfully!' + " + + # Test service - runs all unit tests + test: + build: + context: . + dockerfile: Dockerfile + target: test + no_cache: true + container_name: route4me-sdk-test + volumes: + - .:/app + working_dir: /app/route4me-csharp-sdk + command: > + sh -c " + echo 'Running Route4Me SDK Tests...' && + echo '==============================' && + dotnet test -v n -p:ParallelizeTestCollections=false -f netcoreapp2.1 --filter Category!=Beacon ./route4me-csharp-sdk/Route4MeSDKUnitTest/Route4MeSDKUnitTest.csproj && + echo 'All tests completed!' + " + + # Development service - provides an interactive development environment + dev: + build: + context: . + dockerfile: Dockerfile + target: development + no_cache: true + container_name: route4me-sdk-dev + volumes: + - .:/app + working_dir: /app/route4me-csharp-sdk + stdin_open: true + tty: true + command: /bin/bash + environment: + - DOTNET_CLI_TELEMETRY_OPTOUT=1 + - DOTNET_SKIP_FIRST_TIME_EXPERIENCE=1 + + + # Clean service - removes build artifacts and temporary files + clean: + build: + context: . + dockerfile: Dockerfile + target: base + no_cache: true + container_name: route4me-sdk-clean + volumes: + - .:/app + working_dir: /app/route4me-csharp-sdk + command: > + sh -c " + echo 'Cleaning build artifacts...' && + find . -name 'bin' -type d -exec rm -rf {} + 2>/dev/null || true && + find . -name 'obj' -type d -exec rm -rf {} + 2>/dev/null || true && + find . -name 'TestResults' -type d -exec rm -rf {} + 2>/dev/null || true && + echo 'Clean completed!' + " + +# ============================================================================= +# Additional configurations for different scenarios +# ============================================================================= + +# Override for CI/CD environments +# Usage: docker-compose -f docker-compose.yml -f docker-compose.ci.yml up test diff --git a/route4me-csharp-sdk/CHANGELOG.md b/route4me-csharp-sdk/CHANGELOG.md index c8982364..96a2e86f 100644 --- a/route4me-csharp-sdk/CHANGELOG.md +++ b/route4me-csharp-sdk/CHANGELOG.md @@ -1,6 +1,114 @@ # Changelog All notable changes to this project will be documented in this file. +## [7.13.0.0] - 2025-10-17 + +### Added + +**Customer Management API (V5)** — full implementation according to the official Route4Me OpenAPI specification. + +New supported endpoints: +- `GET /api/v5.0/customers/{customer_id}` – Get customer by ID +- `POST /api/v5.0/customers` – Create a new customer +- `PUT /api/v5.0/customers/{customer_id}` – Update an existing customer +- `DELETE /api/v5.0/customers/{customer_id}` – Delete a customer +- `POST /api/v5.0/customers/list` – Retrieve paginated list of customers + +New classes introduced under **DataTypes/V5/Customers/**: +- `Contact.cs` +- `Contract.cs` +- `CustomerAddress.cs` +- `CustomerResource.cs` +- `CustomerContactResource.cs` +- `CustomerListResource.cs` +- `CustomerListResponse.cs` +- `CustomerResource.cs` +- `CustomerShowResource.cs` +- `Facility.cs` +- `StoreRequest.cs` + +**QueryTypes/V5/Customers/**: +- `CustomerIdParameters.cs` +- `CustomerListFilters.cs` +- `CustomerListParameters.cs` +- `UpdateCustomerParameters.cs` + +New manager under **Managers/**: +- `CustomerManagerV5.cs` — provides sync and async access to all customer endpoints (`CreateCustomerV5`, `GetCustomerByIdV5`, `DeleteCustomerV5`, etc.) + +### Changed +- **Consts.cs** – added new API endpoints for `/api/v5.0/customers`. +- **Route4MeManagerV5.cs** – integrated `CustomerManagerV5` initialization into constructor for seamless V5 access. +- **Enum.cs** – added new ContactType, AddressType, CustomerStatus enums + +### Documentation +- Added examples for all Customer API endpoints under `Examples/V5/Customers/`. +- Updated `README.md` with usage instructions for `CustomerManagerV5`. +- Updated `CHANGELOG.md` and internal metadata to reflect the new Customer Management feature. + +### Tests +- Added **CustomersTests.cs** under `Route4MeSDKTest/V5/Customers/` to cover all new endpoints: + - `CreateCustomerTest` + - `CreateCustomerAsyncTest` + - `GetCustomerByIdTest` + - `GetCustomerByIdAsyncTest` + - `UpdateCustomerTest` + - `UpdateCustomerAsyncTest` + - `GetCustomersListTest` + - `GetCustomersListAsyncTest` + - `DeleteCustomerTest` + - `DeleteCustomerAsyncTest` +- Integration test coverage temporarily skipped. + +### Examples +- Added full usage examples under `Examples/API5/Customers/`: + - `CreateCustomerV5.cs` + - `CreateCustomerV5Async.cs` + - `GetCustomerByIdV5.cs` + - `GetCustomerByIdV5Async.cs` + - `UpdateCustomerV5.cs` + - `UpdateCustomerV5Async.cs` + - `DeleteCustomerV5.cs` + - `DeleteCustomerV5Async.cs` + - `GetCustomersListV5.cs` + - `GetCustomersListV5Async.cs` +- In examples demonstrates both sync and async SDK patterns. +- Examples are verified and compatible with the latest Route4Me API documentation. + +### Notes +- Implementation follows SDK async/sync conventions, SOLID principles, and thread-safety patterns. +Added: +- Notes API v5.0 support with 9 MVP methods +- Create note: `POST /notes` +- Bulk create notes: `POST /notes/bulk-create` +- Get note by ID: `GET /notes/{note_id}` +- Update note by ID: `POST /notes/{note_id}` +- Delete note by ID: `DELETE /notes/{note_id}` +- Get notes by route: `GET /notes/route/{route_id}` +- Get notes by destination: `GET /notes/destination/{route_destination_id}` +- Get custom note types: `GET /notes/custom-types` +- Create custom note type: `POST /notes/custom-types` +- New `NotesManagerV5` manager class for Notes API operations +- Data types: `RouteNoteResource`, `RouteNoteCollection`, `NoteCustomTypeResource`, `NoteCustomTypeCollection` +- Request types: `NoteStoreRequest`, `NoteUpdateRequest`, `NoteCustomTypeStoreRequest` +- Comprehensive unit tests for all Notes API methods +- Sample application demonstrating Notes API usage +- Documentation for Notes API v5.0 + +## [7.13.0.0] - 2025-10-15 +Added Facility Management API V5 support: +- FacilityManagerV5 with core CRUD operations +- Get single facility by ID +- Get paginated list of facilities with filters +- Create new facilities +- Update existing facilities +- Delete facilities +- Get facility types +- Comprehensive XML documentation on all public methods +- Unit tests for facility operations +- Example implementation in Examples/API5/Facilities/ + + ## [7.12.1.0] - 2024-08-07 Fixed get optimization profile endpoint Adjust OptimizationProfileEntities and OptimizationWithProfile examples @@ -284,7 +392,7 @@ The file [SequentialTimer.cs: ](https://github.com/route4me/route4me-net-core/bl The file [Route4MeManager.cs: ](https://github.com/route4me/route4me-net-core/tree/master/route4me-csharp-sdk/Route4MeSDKLibrary/Route4MeManager.cs) – Switch from HttpClient per HTTP Request to usage of HttpClientHolderManager -The file [Route4MeManagerV5.cs: ](https://github.com/route4me/route4me-net-core/tree/master/route4me-csharp-sdk/Route4MeSDKLibrary/Route4MeManagerV5.cs) +The file [Route4MeManagerV5cs: ](https://github.com/route4me/route4me-net-core/tree/master/route4me-csharp-sdk/Route4MeSDKLibrary/Route4MeManagerV5.cs) – Switch from HttpClient per HTTP Request to usage of HttpClientHolderManager diff --git a/route4me-csharp-sdk/README.md b/route4me-csharp-sdk/README.md index 11cfce30..4e8672f7 100644 --- a/route4me-csharp-sdk/README.md +++ b/route4me-csharp-sdk/README.md @@ -90,6 +90,31 @@ Route4Me does not currently lease or sell servers, and does not have on-premise ### Does the Route4Me API/SDK require me to have my own programmers? The time required to integrate the SDK can be as little as 1 hour or may take several weeks, depending on the number of features being incorporated into the customer’s application and how much integration testing will be done by the client. A programmer’s involvement is almost always required to use Route4Me’s technology when accessing it through the API. +## 🚀 What's New in Version 7.13.0.0 + +The SDK now supports **Customer Management API (V5)** — a complete implementation for managing customers in Route4Me. + +### Implemented Endpoints +- `GET /api/v5.0/customers/{customer_id}` – Get customer by ID +- `POST /api/v5.0/customers` – Create new customer +- `PUT /api/v5.0/customers/{customer_id}` – Update existing customer +- `DELETE /api/v5.0/customers/{customer_id}` – Delete customer +- `POST /api/v5.0/customers/list` – Get paginated list of customers + +### Added Files +**Under `Route4MeSDKLibrary`:** +- `Managers/CustomerManagerV5.cs` +- `DataTypes/V5/Customers/CustomerResource.cs` +- `DataTypes/V5/Customers/CustomerShowResource.cs` +- `DataTypes/V5/Customers/CustomerListResource.cs` +- `DataTypes/V5/Customers/FacilityResource.cs` +- `DataTypes/V5/Customers/StoreRequest.cs` +- `DataTypes/V5/Customers/UpdateRequest.cs` +- `DataTypes/V5/Customers/CustomerListParameters.cs` +- `DataTypes/V5/Customers/CustomerListFilters.cs` + +All endpoints have synchronous and asynchronous variants +(e.g., `CreateCustomerV5Async`, `GetCustomerByIdV5Async`, etc.). ### Installation and Usage diff --git a/route4me-csharp-sdk/Route4MeSDKLibrary/Consts.cs b/route4me-csharp-sdk/Route4MeSDKLibrary/Consts.cs index ad1f111c..747e793c 100644 --- a/route4me-csharp-sdk/Route4MeSDKLibrary/Consts.cs +++ b/route4me-csharp-sdk/Route4MeSDKLibrary/Consts.cs @@ -265,6 +265,14 @@ public static class R4MEInfrastructureSettingsV5 #endregion + #region Customers + + public const string Customers = MainHost + "/customers"; + public const string CustomersList = Customers + "/list"; + public const string CustomersById = Customers + "/{customer_id}"; + + #endregion + #region Route Status public const string RouteStatusMainHost = MainHost + "/route-status"; @@ -304,6 +312,21 @@ public static class R4MEInfrastructureSettingsV5 public const string OptimizationProfilesDeleteEntities = OptimizationProfiles + "/delete-entities"; #endregion + + #region Notes + public const string Notes = MainHost + "/notes"; + public const string NoteById = Notes + "/{note_id}"; + public const string NotesByRoute = Notes + "/route/{route_id}"; + public const string NotesByDestination = Notes + "/destination/{route_destination_id}"; + public const string NotesCustomTypes = Notes + "/custom-types"; + public const string NotesBulkCreate = Notes + "/bulk-create"; + + #region Facilities + public const string Facilities = MainHost + "/facilities"; + public const string FacilityTypes = Facilities + "/types"; + + #endregion + #endregion } } \ No newline at end of file diff --git a/route4me-csharp-sdk/Route4MeSDKLibrary/DataTypes/V5/Customer/Contact.cs b/route4me-csharp-sdk/Route4MeSDKLibrary/DataTypes/V5/Customer/Contact.cs new file mode 100644 index 00000000..b120428a --- /dev/null +++ b/route4me-csharp-sdk/Route4MeSDKLibrary/DataTypes/V5/Customer/Contact.cs @@ -0,0 +1,48 @@ +using Newtonsoft.Json.Linq; +using System.Runtime.Serialization; + +namespace Route4MeSDKLibrary.DataTypes.V5.Customers +{ + /// + /// Contact + /// + [DataContract] + public class Contact + { + /// + /// Type (primary, billing, sales, shipping, technical, returns, customer_service, maintenance, operations, other) + /// + [DataMember(Name = "type", EmitDefaultValue = false)] + public string Type { get; set; } + + /// + /// First name + /// + [DataMember(Name = "first_name", EmitDefaultValue = false)] + public string FirstName { get; set; } + + /// + /// Last name + /// + [DataMember(Name = "last_name", EmitDefaultValue = false)] + public string LastName { get; set; } + + /// + /// Phone + /// + [DataMember(Name = "phone", EmitDefaultValue = false)] + public string Phone { get; set; } + + /// + /// Email + /// + [DataMember(Name = "email", EmitDefaultValue = false)] + public string Email { get; set; } + + /// + /// Custom data + /// + [DataMember(Name = "custom_data", EmitDefaultValue = false)] + public JObject CustomData { get; set; } + } +} diff --git a/route4me-csharp-sdk/Route4MeSDKLibrary/DataTypes/V5/Customer/Contract.cs b/route4me-csharp-sdk/Route4MeSDKLibrary/DataTypes/V5/Customer/Contract.cs new file mode 100644 index 00000000..31fc48f2 --- /dev/null +++ b/route4me-csharp-sdk/Route4MeSDKLibrary/DataTypes/V5/Customer/Contract.cs @@ -0,0 +1,23 @@ +using System.Runtime.Serialization; + +namespace Route4MeSDKLibrary.DataTypes.V5.Customers +{ + /// + /// Contract + /// + [DataContract] + public class Contract + { + /// + /// Start date (m-d-Y) + /// + [DataMember(Name = "start_date", EmitDefaultValue = false)] + public string StartDate { get; set; } + + /// + /// Expiration (m-d-Y) + /// + [DataMember(Name = "expiration", EmitDefaultValue = false)] + public string Expiration { get; set; } + } +} diff --git a/route4me-csharp-sdk/Route4MeSDKLibrary/DataTypes/V5/Customer/CustomerAddress.cs b/route4me-csharp-sdk/Route4MeSDKLibrary/DataTypes/V5/Customer/CustomerAddress.cs new file mode 100644 index 00000000..28ec96cd --- /dev/null +++ b/route4me-csharp-sdk/Route4MeSDKLibrary/DataTypes/V5/Customer/CustomerAddress.cs @@ -0,0 +1,23 @@ +using System.Runtime.Serialization; + +namespace Route4MeSDKLibrary.DataTypes.V5.Customers +{ + /// + /// Address + /// + [DataContract] + public class CustomerAddress + { + /// + /// Type (billing, shipping, delivery) + /// + [DataMember(Name = "type", EmitDefaultValue = false)] + public string Type { get; set; } + + /// + /// Address + /// + [DataMember(Name = "address", EmitDefaultValue = false)] + public string Value { get; set; } + } +} diff --git a/route4me-csharp-sdk/Route4MeSDKLibrary/DataTypes/V5/Customer/CustomerContactResource.cs b/route4me-csharp-sdk/Route4MeSDKLibrary/DataTypes/V5/Customer/CustomerContactResource.cs new file mode 100644 index 00000000..0ee029e1 --- /dev/null +++ b/route4me-csharp-sdk/Route4MeSDKLibrary/DataTypes/V5/Customer/CustomerContactResource.cs @@ -0,0 +1,35 @@ +using System.Runtime.Serialization; + +namespace Route4MeSDKLibrary.DataTypes.V5.Customers +{ + /// + /// Customer contact resource + /// + [DataContract] + public class CustomerContactResource + { + /// + /// Contact type + /// + [DataMember(Name = "type", EmitDefaultValue = false)] + public string Type { get; set; } + + /// + /// Contact name + /// + [DataMember(Name = "name", EmitDefaultValue = false)] + public string Name { get; set; } + + /// + /// Contact email + /// + [DataMember(Name = "email", EmitDefaultValue = false)] + public string Email { get; set; } + + /// + /// Contact phone + /// + [DataMember(Name = "phone", EmitDefaultValue = false)] + public string Phone { get; set; } + } +} diff --git a/route4me-csharp-sdk/Route4MeSDKLibrary/DataTypes/V5/Customer/CustomerListResource.cs b/route4me-csharp-sdk/Route4MeSDKLibrary/DataTypes/V5/Customer/CustomerListResource.cs new file mode 100644 index 00000000..d0d9fc3c --- /dev/null +++ b/route4me-csharp-sdk/Route4MeSDKLibrary/DataTypes/V5/Customer/CustomerListResource.cs @@ -0,0 +1,114 @@ +using System.Collections.Generic; +using System.Runtime.Serialization; + +namespace Route4MeSDKLibrary.DataTypes.V5.Customers +{ + /// + /// Customer list resource + /// + [DataContract] + public class CustomerListResource + { + /// + /// Root member ID + /// + [DataMember(Name = "root_member_id", EmitDefaultValue = false)] + public int? RootMemberId { get; set; } + + /// + /// Customer ID + /// + [DataMember(Name = "customer_id", EmitDefaultValue = false)] + public string CustomerId { get; set; } + + /// + /// Customer name + /// + [DataMember(Name = "name", EmitDefaultValue = false)] + public string Name { get; set; } + + /// + /// External ID + /// + [DataMember(Name = "external_id", EmitDefaultValue = false)] + public string ExternalId { get; set; } + + /// + /// Accountable person first name + /// + [DataMember(Name = "accountable_person_first_name", EmitDefaultValue = false)] + public string AccountablePersonFirstName { get; set; } + + /// + /// Accountable person last name + /// + [DataMember(Name = "accountable_person_last_name", EmitDefaultValue = false)] + public string AccountablePersonLastName { get; set; } + + /// + /// Number of customer locations + /// + [DataMember(Name = "customer_location_count", EmitDefaultValue = false)] + public int? CustomerLocationCount { get; set; } + + /// + /// Status (1) + /// + [DataMember(Name = "status", EmitDefaultValue = false)] + public int? Status { get; set; } + + /// + /// Currency + /// + [DataMember(Name = "currency", EmitDefaultValue = false)] + public string Currency { get; set; } + + /// + /// Creation date (UTC string) + /// + [DataMember(Name = "creation_date", EmitDefaultValue = false)] + public string CreationDate { get; set; } + + /// + /// Created timestamp + /// + [DataMember(Name = "created_timestamp", EmitDefaultValue = false)] + public long? CreatedTimestamp { get; set; } + + /// + /// Created day ID + /// + [DataMember(Name = "created_day_id", EmitDefaultValue = false)] + public int? CreatedDayId { get; set; } + + /// + /// Created by member ID + /// + [DataMember(Name = "created_by_member_id", EmitDefaultValue = false)] + public int? CreatedByMemberId { get; set; } + + /// + /// Updated timestamp + /// + [DataMember(Name = "updated_timestamp", EmitDefaultValue = false)] + public long? UpdatedTimestamp { get; set; } + + /// + /// Facility IDs + /// + [DataMember(Name = "facility_ids", EmitDefaultValue = false)] + public List FacilityIds { get; set; } + + /// + /// Facilities + /// + [DataMember(Name = "facilities", EmitDefaultValue = false)] + public string Facilities { get; set; } + + /// + /// Contacts + /// + [DataMember(Name = "contacts", EmitDefaultValue = false)] + public List Contacts { get; set; } + } +} diff --git a/route4me-csharp-sdk/Route4MeSDKLibrary/DataTypes/V5/Customer/CustomerListResponse.cs b/route4me-csharp-sdk/Route4MeSDKLibrary/DataTypes/V5/Customer/CustomerListResponse.cs new file mode 100644 index 00000000..42b0d35d --- /dev/null +++ b/route4me-csharp-sdk/Route4MeSDKLibrary/DataTypes/V5/Customer/CustomerListResponse.cs @@ -0,0 +1,18 @@ +using System.Collections.Generic; +using System.Runtime.Serialization; + +namespace Route4MeSDKLibrary.DataTypes.V5.Customers +{ + /// + /// Response model for customer list + /// + [DataContract] + public class CustomerListResponse + { + /// + /// List of customers + /// + [DataMember(Name = "items", EmitDefaultValue = false)] + public List Items { get; set; } + } +} diff --git a/route4me-csharp-sdk/Route4MeSDKLibrary/DataTypes/V5/Customer/CustomerResource.cs b/route4me-csharp-sdk/Route4MeSDKLibrary/DataTypes/V5/Customer/CustomerResource.cs new file mode 100644 index 00000000..895ecd47 --- /dev/null +++ b/route4me-csharp-sdk/Route4MeSDKLibrary/DataTypes/V5/Customer/CustomerResource.cs @@ -0,0 +1,119 @@ +using System.Runtime.Serialization; + +namespace Route4MeSDKLibrary.DataTypes.V5.Customers +{ + /// + /// Customer data + /// + [DataContract] + public class CustomerResource + { + /// + /// Root member ID + /// + [DataMember(Name = "root_member_id", EmitDefaultValue = false)] + public long? RootMemberId { get; set; } + + /// + /// Customer ID + /// + [DataMember(Name = "customer_id", EmitDefaultValue = false)] + public string CustomerId { get; set; } + + /// + /// Name + /// + [DataMember(Name = "name", EmitDefaultValue = false)] + public string Name { get; set; } + + /// + /// External ID + /// + [DataMember(Name = "external_id", EmitDefaultValue = false)] + public string ExternalId { get; set; } + + /// + /// Contacts + /// + [DataMember(Name = "contacts", EmitDefaultValue = false)] + public Contact[] Contacts { get; set; } + + /// + /// Accountable person ID + /// + [DataMember(Name = "accountable_person_id", EmitDefaultValue = false)] + public long? AccountablePersonId { get; set; } + + /// + /// Addresses + /// + [DataMember(Name = "addresses", EmitDefaultValue = false)] + public CustomerAddress[] Addresses { get; set; } + + /// + /// Currency + /// + [DataMember(Name = "currency", EmitDefaultValue = false)] + public string Currency { get; set; } + + /// + /// Tax ID + /// + [DataMember(Name = "tax_id", EmitDefaultValue = false)] + public string TaxId { get; set; } + + /// + /// Status (1) + /// + [DataMember(Name = "status", EmitDefaultValue = false)] + public int Status { get; set; } + + /// + /// Contracts + /// + [DataMember(Name = "contracts", EmitDefaultValue = false)] + public Contract[] Contracts { get; set; } + + /// + /// Creation date (m-d-Y) + /// + [DataMember(Name = "creation_date", EmitDefaultValue = false)] + public string CreationDate { get; set; } + + /// + /// Created timestamp + /// + [DataMember(Name = "created_timestamp", EmitDefaultValue = false)] + public long? CreatedTimestamp { get; set; } + + /// + /// Created day ID + /// + [DataMember(Name = "created_day_id", EmitDefaultValue = false)] + public int? CreatedDayId { get; set; } + + /// + /// Created by member ID + /// + [DataMember(Name = "created_by_member_id", EmitDefaultValue = false)] + public long? CreatedByMemberId { get; set; } + + /// + /// Updated timestamp + /// + [DataMember(Name = "updated_timestamp", EmitDefaultValue = false)] + public long? UpdatedTimestamp { get; set; } + + /// + /// Facility IDs + /// + [DataMember(Name = "facility_ids", EmitDefaultValue = false)] + public string[] FacilityIds { get; set; } + + /// + /// Facilities + /// + [DataMember(Name = "facilities", EmitDefaultValue = false)] + public Facility[] Facilities { get; set; } + } +} diff --git a/route4me-csharp-sdk/Route4MeSDKLibrary/DataTypes/V5/Customer/CustomerShowResource.cs b/route4me-csharp-sdk/Route4MeSDKLibrary/DataTypes/V5/Customer/CustomerShowResource.cs new file mode 100644 index 00000000..7e37f905 --- /dev/null +++ b/route4me-csharp-sdk/Route4MeSDKLibrary/DataTypes/V5/Customer/CustomerShowResource.cs @@ -0,0 +1,29 @@ +using System.Runtime.Serialization; + +namespace Route4MeSDKLibrary.DataTypes.V5.Customers +{ + /// + /// Customer show resource + /// + [DataContract] + public class CustomerShowResource : CustomerResource + { + /// + /// Accountable Person FirstName + /// + [DataMember(Name = "accountable_person_first_name")] + public string AccountablePersonFirstName { get; set; } + + /// + /// Accountable Person LastName + /// + [DataMember(Name = "accountable_person_last_name")] + public string AccountablePersonLastName { get; set; } + + /// + /// Customer Location Count + /// + [DataMember(Name = "customer_location_count")] + public int CustomerLocationCount { get; set; } + } +} diff --git a/route4me-csharp-sdk/Route4MeSDKLibrary/DataTypes/V5/Customer/Facility.cs b/route4me-csharp-sdk/Route4MeSDKLibrary/DataTypes/V5/Customer/Facility.cs new file mode 100644 index 00000000..a8cff8a3 --- /dev/null +++ b/route4me-csharp-sdk/Route4MeSDKLibrary/DataTypes/V5/Customer/Facility.cs @@ -0,0 +1,23 @@ +using System.Runtime.Serialization; + +namespace Route4MeSDKLibrary.DataTypes.V5.Customers +{ + /// + /// Facility + /// + [DataContract] + public class Facility + { + /// + /// Facility ID + /// + [DataMember(Name = "facility_id", EmitDefaultValue = false)] + public string FacilityId { get; set; } + + /// + /// Facility name + /// + [DataMember(Name = "facility_name", EmitDefaultValue = false)] + public string FacilityName { get; set; } + } +} diff --git a/route4me-csharp-sdk/Route4MeSDKLibrary/DataTypes/V5/Customer/StoreRequest.cs b/route4me-csharp-sdk/Route4MeSDKLibrary/DataTypes/V5/Customer/StoreRequest.cs new file mode 100644 index 00000000..c5aa1e34 --- /dev/null +++ b/route4me-csharp-sdk/Route4MeSDKLibrary/DataTypes/V5/Customer/StoreRequest.cs @@ -0,0 +1,72 @@ +using Route4MeSDK.QueryTypes; +using System.Runtime.Serialization; + +namespace Route4MeSDKLibrary.DataTypes.V5.Customers +{ + /// + /// Customer store request + /// + [DataContract] + public class StoreRequest : GenericParameters + { + /// + /// Name + /// + [DataMember(Name = "name", EmitDefaultValue = false)] + public string Name { get; set; } + + /// + /// External ID + /// + [DataMember(Name = "external_id", EmitDefaultValue = false)] + public string ExternalId { get; set; } + + /// + /// Contacts + /// + [DataMember(Name = "contacts", EmitDefaultValue = false)] + public Contact[] Contacts { get; set; } + + /// + /// Accountable person ID + /// + [DataMember(Name = "accountable_person_id", EmitDefaultValue = false)] + public long? AccountablePersonId { get; set; } + + /// + /// Addresses + /// + [DataMember(Name = "addresses", EmitDefaultValue = false)] + public CustomerAddress[] Addresses { get; set; } + + /// + /// Currency + /// + [DataMember(Name = "currency", EmitDefaultValue = false)] + public string Currency { get; set; } + + /// + /// Tax ID + /// + [DataMember(Name = "tax_id", EmitDefaultValue = false)] + public string TaxId { get; set; } + + /// + /// Status (1) + /// + [DataMember(Name = "status", EmitDefaultValue = false)] + public int Status { get; set; } + + /// + /// Contracts + /// + [DataMember(Name = "contracts", EmitDefaultValue = false)] + public Contract[] Contracts { get; set; } + + /// + /// Facility IDs + /// + [DataMember(Name = "facility_ids", EmitDefaultValue = false)] + public string[] FacilityIds { get; set; } + } +} diff --git a/route4me-csharp-sdk/Route4MeSDKLibrary/DataTypes/V5/Enum.cs b/route4me-csharp-sdk/Route4MeSDKLibrary/DataTypes/V5/Enum.cs index aa68fe0b..8642d28b 100644 --- a/route4me-csharp-sdk/Route4MeSDKLibrary/DataTypes/V5/Enum.cs +++ b/route4me-csharp-sdk/Route4MeSDKLibrary/DataTypes/V5/Enum.cs @@ -469,4 +469,44 @@ public enum DynamicInsertRecomendBy } #endregion + + public enum ContactType + { + [Description("primary")] Primary, + + [Description("billing")] Billing, + + [Description("sales")] Sales, + + [Description("shipping")] Shipping, + + [Description("technical")] Technical, + + [Description("returns")] Returns, + + [Description("customer_service")] CustomerService, + + [Description("maintenance")] Maintenance, + + [Description("operations")] Operations, + + [Description("other")] Other + } + + public enum AddressType + { + [Description("billing")] Billing, + + [Description("shipping")] Shipping, + + [Description("delivery")] Delivery + } + + public enum CustomerStatus + { + [Description("inactive")] Inactive = 0, + + [Description("active")] Active = 1 + } + } \ No newline at end of file diff --git a/route4me-csharp-sdk/Route4MeSDKLibrary/DataTypes/V5/Facilities/FacilitiesPaginateResource.cs b/route4me-csharp-sdk/Route4MeSDKLibrary/DataTypes/V5/Facilities/FacilitiesPaginateResource.cs new file mode 100644 index 00000000..7ebd5a36 --- /dev/null +++ b/route4me-csharp-sdk/Route4MeSDKLibrary/DataTypes/V5/Facilities/FacilitiesPaginateResource.cs @@ -0,0 +1,66 @@ +using System.Runtime.Serialization; + +namespace Route4MeSDKLibrary.DataTypes.V5.Facilities +{ + /// + /// Paginated facilities response + /// + [DataContract] + public class FacilitiesPaginateResource + { + /// + /// Array of facilities + /// + [DataMember(Name = "data", EmitDefaultValue = false)] + public FacilityResource[] Data { get; set; } + + /// + /// Current page number + /// + [DataMember(Name = "current_page", EmitDefaultValue = false)] + public int? CurrentPage { get; set; } + + /// + /// Total number of facilities + /// + [DataMember(Name = "total", EmitDefaultValue = false)] + public int? Total { get; set; } + + /// + /// Number of facilities per page + /// + [DataMember(Name = "per_page", EmitDefaultValue = false)] + public int? PerPage { get; set; } + + /// + /// Last page number + /// + [DataMember(Name = "last_page", EmitDefaultValue = false)] + public int? LastPage { get; set; } + + /// + /// URL to the next page + /// + [DataMember(Name = "next_page_url", EmitDefaultValue = false)] + public string NextPageUrl { get; set; } + + /// + /// URL to the previous page + /// + [DataMember(Name = "prev_page_url", EmitDefaultValue = false)] + public string PrevPageUrl { get; set; } + + /// + /// Starting item number on current page + /// + [DataMember(Name = "from", EmitDefaultValue = false)] + public int? From { get; set; } + + /// + /// Ending item number on current page + /// + [DataMember(Name = "to", EmitDefaultValue = false)] + public int? To { get; set; } + } +} + diff --git a/route4me-csharp-sdk/Route4MeSDKLibrary/DataTypes/V5/Facilities/FacilityCoordinates.cs b/route4me-csharp-sdk/Route4MeSDKLibrary/DataTypes/V5/Facilities/FacilityCoordinates.cs new file mode 100644 index 00000000..6d34716a --- /dev/null +++ b/route4me-csharp-sdk/Route4MeSDKLibrary/DataTypes/V5/Facilities/FacilityCoordinates.cs @@ -0,0 +1,24 @@ +using System.Runtime.Serialization; + +namespace Route4MeSDKLibrary.DataTypes.V5.Facilities +{ + /// + /// Facility GPS coordinates + /// + [DataContract] + public class FacilityCoordinates + { + /// + /// Latitude + /// + [DataMember(Name = "lat", EmitDefaultValue = false)] + public double? Lat { get; set; } + + /// + /// Longitude + /// + [DataMember(Name = "lng", EmitDefaultValue = false)] + public double? Lng { get; set; } + } +} + diff --git a/route4me-csharp-sdk/Route4MeSDKLibrary/DataTypes/V5/Facilities/FacilityResource.cs b/route4me-csharp-sdk/Route4MeSDKLibrary/DataTypes/V5/Facilities/FacilityResource.cs new file mode 100644 index 00000000..66239001 --- /dev/null +++ b/route4me-csharp-sdk/Route4MeSDKLibrary/DataTypes/V5/Facilities/FacilityResource.cs @@ -0,0 +1,66 @@ +using System.Runtime.Serialization; + +namespace Route4MeSDKLibrary.DataTypes.V5.Facilities +{ + /// + /// Facility resource data structure + /// + [DataContract] + public class FacilityResource + { + /// + /// Unique facility identifier (hex32 string) + /// + [DataMember(Name = "facility_id", EmitDefaultValue = false)] + public string FacilityId { get; set; } + + /// + /// Root member ID + /// + [DataMember(Name = "root_member_id", EmitDefaultValue = false)] + public long? RootMemberId { get; set; } + + /// + /// Facility alias/name + /// + [DataMember(Name = "facility_alias", EmitDefaultValue = false)] + public string FacilityAlias { get; set; } + + /// + /// Facility address + /// + [DataMember(Name = "address", EmitDefaultValue = false)] + public string Address { get; set; } + + /// + /// Facility coordinates + /// + [DataMember(Name = "coordinates", EmitDefaultValue = false)] + public FacilityCoordinates Coordinates { get; set; } + + /// + /// Facility status: 1=ACTIVE, 2=INACTIVE, 3=IN_REVIEW + /// + [DataMember(Name = "status", EmitDefaultValue = false)] + public int? Status { get; set; } + + /// + /// Facility types array + /// + [DataMember(Name = "facility_types", EmitDefaultValue = false)] + public FacilityTypeRelation[] FacilityTypes { get; set; } + + /// + /// Timestamp when facility was created + /// + [DataMember(Name = "created_at", EmitDefaultValue = false)] + public long? CreatedAt { get; set; } + + /// + /// Timestamp when facility was last updated + /// + [DataMember(Name = "updated_at", EmitDefaultValue = false)] + public long? UpdatedAt { get; set; } + } +} + diff --git a/route4me-csharp-sdk/Route4MeSDKLibrary/DataTypes/V5/Facilities/FacilityTypeCollectionResource.cs b/route4me-csharp-sdk/Route4MeSDKLibrary/DataTypes/V5/Facilities/FacilityTypeCollectionResource.cs new file mode 100644 index 00000000..c0b18367 --- /dev/null +++ b/route4me-csharp-sdk/Route4MeSDKLibrary/DataTypes/V5/Facilities/FacilityTypeCollectionResource.cs @@ -0,0 +1,18 @@ +using System.Runtime.Serialization; + +namespace Route4MeSDKLibrary.DataTypes.V5.Facilities +{ + /// + /// Response wrapper for facility types collection + /// The API returns: { "data": [ {...}, {...} ] } + /// + [DataContract] + public class FacilityTypeCollectionResource + { + /// + /// Array of facility types + /// + [DataMember(Name = "data")] + public FacilityTypeResource[] Data { get; set; } + } +} diff --git a/route4me-csharp-sdk/Route4MeSDKLibrary/DataTypes/V5/Facilities/FacilityTypeRelation.cs b/route4me-csharp-sdk/Route4MeSDKLibrary/DataTypes/V5/Facilities/FacilityTypeRelation.cs new file mode 100644 index 00000000..831ef91f --- /dev/null +++ b/route4me-csharp-sdk/Route4MeSDKLibrary/DataTypes/V5/Facilities/FacilityTypeRelation.cs @@ -0,0 +1,30 @@ +using System.Runtime.Serialization; + +namespace Route4MeSDKLibrary.DataTypes.V5.Facilities +{ + /// + /// Facility type relation for response data + /// + [DataContract] + public class FacilityTypeRelation + { + /// + /// Facility type ID + /// + [DataMember(Name = "facility_type_id")] + public int FacilityTypeId { get; set; } + + /// + /// Facility type alias/name + /// + [DataMember(Name = "facility_type_alias")] + public string FacilityTypeAlias { get; set; } + + /// + /// Is this the default facility type for this facility + /// + [DataMember(Name = "is_default")] + public bool IsDefault { get; set; } + } +} + diff --git a/route4me-csharp-sdk/Route4MeSDKLibrary/DataTypes/V5/Facilities/FacilityTypeResource.cs b/route4me-csharp-sdk/Route4MeSDKLibrary/DataTypes/V5/Facilities/FacilityTypeResource.cs new file mode 100644 index 00000000..8d4731b8 --- /dev/null +++ b/route4me-csharp-sdk/Route4MeSDKLibrary/DataTypes/V5/Facilities/FacilityTypeResource.cs @@ -0,0 +1,24 @@ +using System.Runtime.Serialization; + +namespace Route4MeSDKLibrary.DataTypes.V5.Facilities +{ + /// + /// Facility type resource + /// + [DataContract] + public class FacilityTypeResource + { + /// + /// Facility type ID + /// + [DataMember(Name = "facility_type_id")] + public int FacilityTypeId { get; set; } + + /// + /// Facility type alias/name + /// + [DataMember(Name = "facility_type_alias")] + public string FacilityTypeAlias { get; set; } + } +} + diff --git a/route4me-csharp-sdk/Route4MeSDKLibrary/DataTypes/V5/Notes/BulkNotesResponse.cs b/route4me-csharp-sdk/Route4MeSDKLibrary/DataTypes/V5/Notes/BulkNotesResponse.cs new file mode 100644 index 00000000..a1936e78 --- /dev/null +++ b/route4me-csharp-sdk/Route4MeSDKLibrary/DataTypes/V5/Notes/BulkNotesResponse.cs @@ -0,0 +1,23 @@ +using System.Runtime.Serialization; + +namespace Route4MeSDK.DataTypes.V5.Notes +{ + /// + /// Response for bulk create notes operation + /// + [DataContract] + public class BulkNotesResponse + { + /// + /// Status of the operation + /// + [DataMember(Name = "status", EmitDefaultValue = false)] + public bool Status { get; set; } + + /// + /// Indicates if operation is asynchronous + /// + [DataMember(Name = "async", EmitDefaultValue = false)] + public bool Async { get; set; } + } +} diff --git a/route4me-csharp-sdk/Route4MeSDKLibrary/DataTypes/V5/Notes/CustomNoteTypeValue.cs b/route4me-csharp-sdk/Route4MeSDKLibrary/DataTypes/V5/Notes/CustomNoteTypeValue.cs new file mode 100644 index 00000000..6ecd258e --- /dev/null +++ b/route4me-csharp-sdk/Route4MeSDKLibrary/DataTypes/V5/Notes/CustomNoteTypeValue.cs @@ -0,0 +1,47 @@ +using System.Runtime.Serialization; + +namespace Route4MeSDK.DataTypes.V5.Notes +{ + /// + /// Custom note type value in note resource + /// + [DataContract] + public class CustomNoteTypeValue + { + /// + /// Custom entry unique ID + /// + [DataMember(Name = "note_custom_entry_id", EmitDefaultValue = false)] + public string NoteCustomEntryId { get; set; } + + /// + /// Related note ID + /// + [DataMember(Name = "note_id", EmitDefaultValue = false)] + public int? NoteId { get; set; } + + /// + /// UUID of the note + /// + [DataMember(Name = "note_uuid", EmitDefaultValue = false)] + public string NoteUuid { get; set; } + + /// + /// Custom type ID + /// + [DataMember(Name = "note_custom_type_id", EmitDefaultValue = false)] + public int? NoteCustomTypeId { get; set; } + + /// + /// Value of the custom field + /// + [DataMember(Name = "note_custom_value", EmitDefaultValue = false)] + public string NoteCustomValue { get; set; } + + /// + /// Label or name of the custom type + /// + [DataMember(Name = "note_custom_type", EmitDefaultValue = false)] + public string NoteCustomType { get; set; } + } +} diff --git a/route4me-csharp-sdk/Route4MeSDKLibrary/DataTypes/V5/Notes/DeleteNoteResponse.cs b/route4me-csharp-sdk/Route4MeSDKLibrary/DataTypes/V5/Notes/DeleteNoteResponse.cs new file mode 100644 index 00000000..3c462c56 --- /dev/null +++ b/route4me-csharp-sdk/Route4MeSDKLibrary/DataTypes/V5/Notes/DeleteNoteResponse.cs @@ -0,0 +1,23 @@ +using System.Runtime.Serialization; + +namespace Route4MeSDK.DataTypes.V5.Notes +{ + /// + /// Response for note deletion + /// + [DataContract] + public class DeleteNoteResponse + { + /// + /// Status of deletion + /// + [DataMember(Name = "status", EmitDefaultValue = false)] + public bool Status { get; set; } + + /// + /// Deleted note ID + /// + [DataMember(Name = "note_id", EmitDefaultValue = false)] + public int? NoteId { get; set; } + } +} diff --git a/route4me-csharp-sdk/Route4MeSDKLibrary/DataTypes/V5/Notes/NoteCustomTypeCollection.cs b/route4me-csharp-sdk/Route4MeSDKLibrary/DataTypes/V5/Notes/NoteCustomTypeCollection.cs new file mode 100644 index 00000000..bf37347c --- /dev/null +++ b/route4me-csharp-sdk/Route4MeSDKLibrary/DataTypes/V5/Notes/NoteCustomTypeCollection.cs @@ -0,0 +1,29 @@ +using System.Runtime.Serialization; + +namespace Route4MeSDK.DataTypes.V5.Notes +{ + /// + /// Note custom type collection response + /// + [DataContract] + public class NoteCustomTypeCollection + { + /// + /// Status + /// + [DataMember(Name = "status", EmitDefaultValue = false)] + public bool Status { get; set; } + + /// + /// Response code + /// + [DataMember(Name = "code", EmitDefaultValue = false)] + public int? Code { get; set; } + + /// + /// Array of custom type resources + /// + [DataMember(Name = "data", EmitDefaultValue = false)] + public NoteCustomTypeResource[] Data { get; set; } + } +} diff --git a/route4me-csharp-sdk/Route4MeSDKLibrary/DataTypes/V5/Notes/NoteCustomTypeResource.cs b/route4me-csharp-sdk/Route4MeSDKLibrary/DataTypes/V5/Notes/NoteCustomTypeResource.cs new file mode 100644 index 00000000..1f786de6 --- /dev/null +++ b/route4me-csharp-sdk/Route4MeSDKLibrary/DataTypes/V5/Notes/NoteCustomTypeResource.cs @@ -0,0 +1,41 @@ +using System.Runtime.Serialization; + +namespace Route4MeSDK.DataTypes.V5.Notes +{ + /// + /// Note custom type resource + /// + [DataContract] + public class NoteCustomTypeResource + { + /// + /// Note custom type ID + /// + [DataMember(Name = "note_custom_type_id", EmitDefaultValue = false)] + public int? NoteCustomTypeId { get; set; } + + /// + /// Note custom type name + /// + [DataMember(Name = "note_custom_type", EmitDefaultValue = false)] + public string NoteCustomType { get; set; } + + /// + /// Root owner member ID + /// + [DataMember(Name = "root_owner_member_id", EmitDefaultValue = false)] + public int? RootOwnerMemberId { get; set; } + + /// + /// Note custom type values + /// + [DataMember(Name = "note_custom_type_values", EmitDefaultValue = false)] + public string[] NoteCustomTypeValues { get; set; } + + /// + /// Note custom field type (1,2,3,4) + /// + [DataMember(Name = "note_custom_field_type", EmitDefaultValue = false)] + public int? NoteCustomFieldType { get; set; } + } +} diff --git a/route4me-csharp-sdk/Route4MeSDKLibrary/DataTypes/V5/Notes/RouteNoteCollection.cs b/route4me-csharp-sdk/Route4MeSDKLibrary/DataTypes/V5/Notes/RouteNoteCollection.cs new file mode 100644 index 00000000..478f030b --- /dev/null +++ b/route4me-csharp-sdk/Route4MeSDKLibrary/DataTypes/V5/Notes/RouteNoteCollection.cs @@ -0,0 +1,41 @@ +using System.Runtime.Serialization; + +namespace Route4MeSDK.DataTypes.V5.Notes +{ + /// + /// Route note collection response with pagination + /// + [DataContract] + public class RouteNoteCollection + { + /// + /// Array of note resources + /// + [DataMember(Name = "data", EmitDefaultValue = false)] + public RouteNoteResource[] Data { get; set; } + + /// + /// Total count of notes + /// + [DataMember(Name = "total", EmitDefaultValue = false)] + public int? Total { get; set; } + + /// + /// Current page number + /// + [DataMember(Name = "current_page", EmitDefaultValue = false)] + public int? CurrentPage { get; set; } + + /// + /// Last page number + /// + [DataMember(Name = "last_page", EmitDefaultValue = false)] + public int? LastPage { get; set; } + + /// + /// Items per page + /// + [DataMember(Name = "per_page", EmitDefaultValue = false)] + public int? PerPage { get; set; } + } +} diff --git a/route4me-csharp-sdk/Route4MeSDKLibrary/DataTypes/V5/Notes/RouteNoteResource.cs b/route4me-csharp-sdk/Route4MeSDKLibrary/DataTypes/V5/Notes/RouteNoteResource.cs new file mode 100644 index 00000000..5911d9e5 --- /dev/null +++ b/route4me-csharp-sdk/Route4MeSDKLibrary/DataTypes/V5/Notes/RouteNoteResource.cs @@ -0,0 +1,107 @@ +using System.Runtime.Serialization; + +namespace Route4MeSDK.DataTypes.V5.Notes +{ + /// + /// Route note resource response item + /// + [DataContract] + public class RouteNoteResource + { + /// + /// Note ID + /// + [DataMember(Name = "note_id", EmitDefaultValue = false)] + public int? NoteId { get; set; } + + /// + /// Note UUID (32-char hex string) + /// + [DataMember(Name = "note_uuid", EmitDefaultValue = false)] + public string NoteUuid { get; set; } + + /// + /// Upload ID (32-char hex string) + /// + [DataMember(Name = "upload_id", EmitDefaultValue = false)] + public string UploadId { get; set; } + + /// + /// Route ID (32-char hex string) + /// + [DataMember(Name = "route_id", EmitDefaultValue = false)] + public string RouteId { get; set; } + + /// + /// Route destination ID + /// + [DataMember(Name = "route_destination_id", EmitDefaultValue = false)] + public int? RouteDestinationId { get; set; } + + /// + /// Destination UUID (32-char hex string) + /// + [DataMember(Name = "destination_uuid", EmitDefaultValue = false)] + public string DestinationUuid { get; set; } + + /// + /// Timestamp when note was added (formatted string) + /// + [DataMember(Name = "ts_added", EmitDefaultValue = false)] + public string TsAdded { get; set; } + + /// + /// Activity type + /// + [DataMember(Name = "activity_type", EmitDefaultValue = false)] + public string ActivityType { get; set; } + + /// + /// Upload extension + /// + [DataMember(Name = "upload_extension", EmitDefaultValue = false)] + public string UploadExtension { get; set; } + + /// + /// Upload URL + /// + [DataMember(Name = "upload_url", EmitDefaultValue = false)] + public string UploadUrl { get; set; } + + /// + /// Upload type + /// + [DataMember(Name = "upload_type", EmitDefaultValue = false)] + public string UploadType { get; set; } + + /// + /// Note contents/text + /// + [DataMember(Name = "contents", EmitDefaultValue = false)] + public string Contents { get; set; } + + /// + /// Latitude + /// + [DataMember(Name = "lat", EmitDefaultValue = false)] + public double? Lat { get; set; } + + /// + /// Longitude + /// + [DataMember(Name = "lng", EmitDefaultValue = false)] + public double? Lng { get; set; } + + /// + /// Device type + /// + [DataMember(Name = "device_type", EmitDefaultValue = false)] + public string DeviceType { get; set; } + + /// + /// Custom note types + /// + [DataMember(Name = "custom_types", EmitDefaultValue = false)] + public CustomNoteTypeValue[] CustomTypes { get; set; } + } +} diff --git a/route4me-csharp-sdk/Route4MeSDKLibrary/Managers/CustomerManagerV5.cs b/route4me-csharp-sdk/Route4MeSDKLibrary/Managers/CustomerManagerV5.cs new file mode 100644 index 00000000..50b7fbe0 --- /dev/null +++ b/route4me-csharp-sdk/Route4MeSDKLibrary/Managers/CustomerManagerV5.cs @@ -0,0 +1,252 @@ +using Route4MeSDK; +using Route4MeSDK.DataTypes.V5; +using Route4MeSDK.QueryTypes; +using Route4MeSDKLibrary.DataTypes.V5.Customers; +using Route4MeSDKLibrary.QueryTypes.V5.Customers; +using System; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace Route4MeSDKLibrary.Managers +{ + public sealed class CustomerManagerV5 : Route4MeManagerBase + { + public CustomerManagerV5(string apiKey) : base(apiKey) + { + } + + #region Get Customers List + + /// + /// Get list with customers data. + /// + /// Request parameters. + /// Failing response. + /// Customer list response. + public CustomerListResponse GetCustomersList(CustomerListParameters parameters, out ResultResponse resultResponse) + { + var response = GetJsonObjectFromAPI( + parameters, + R4MEInfrastructureSettingsV5.CustomersList, + HttpMethodType.Post, + out resultResponse); + + return response; + } + + /// + /// Get list with customers data (async). + /// + /// Request parameters. + /// Customer list response. + public Task> GetCustomersListAsync(CustomerListParameters parameters) + { + return GetJsonObjectFromAPIAsync( + parameters, + R4MEInfrastructureSettingsV5.CustomersList, + HttpMethodType.Post); + } + + #endregion + + #region Get Customer By ID + + /// + /// Get customer by ID. + /// + /// Customer ID parameter. + /// Failing response. + /// Customer show resource. + public CustomerShowResource GetCustomerById(CustomerIdParameters parameters, out ResultResponse resultResponse) + { + var url = R4MEInfrastructureSettingsV5.Customers + "/" + parameters.CustomerId; + + var response = GetJsonObjectFromAPI( + new GenericParameters(), + url, + HttpMethodType.Get, + out resultResponse); + + return response; + } + + /// + /// Get customer by ID (async). + /// + /// Customer ID parameter. + /// Customer show resource. + public Task> GetCustomerByIdAsync(CustomerIdParameters parameters) + { + var url = R4MEInfrastructureSettingsV5.Customers + "/" + parameters.CustomerId; + + return GetJsonObjectFromAPIAsync( + new GenericParameters(), + url, + HttpMethodType.Get); + } + + #endregion + + #region Create Customer + + /// + /// Create a new customer. + /// + /// Create customer request. + /// Failing response. + /// Customer resource. + public CustomerResource CreateCustomer(StoreRequest request, out ResultResponse resultResponse) + { + var response = GetJsonObjectFromAPI( + request, + R4MEInfrastructureSettingsV5.Customers, + HttpMethodType.Post, + out resultResponse); + + return response; + } + + /// + /// Create a new customer (async). + /// + /// Create customer request. + /// Customer resource. + public Task> CreateCustomerAsync(StoreRequest request) + { + return GetJsonObjectFromAPIAsync( + request, + R4MEInfrastructureSettingsV5.Customers, + HttpMethodType.Post); + } + + #endregion + + #region Update Customer + + /// + /// Update a customer. + /// + /// Update customer parameters. + /// Failing response. + /// Updated customer resource. + public CustomerResource UpdateCustomer(UpdateCustomerParameters parameters, out ResultResponse resultResponse) + { + var url = R4MEInfrastructureSettingsV5.Customers + "/" + parameters.CustomerId; + + var response = GetJsonObjectFromAPI( + parameters, + url, + HttpMethodType.Put, + out resultResponse); + + return response; + } + + /// + /// Update a customer (async). + /// + /// Update customer parameters. + /// Updated customer resource. + public Task> UpdateCustomerAsync(UpdateCustomerParameters parameters) + { + var url = R4MEInfrastructureSettingsV5.Customers + "/" + parameters.CustomerId; + + return GetJsonObjectFromAPIAsync( + parameters, + url, + HttpMethodType.Put); + } + + #endregion + + #region Delete Customer + + /// + /// Delete a customer. + /// + /// Customer ID parameter. + /// Failing response. + /// Status result. + public ResultResponse DeleteCustomer(CustomerIdParameters parameters, out ResultResponse resultResponse) + { + var url = R4MEInfrastructureSettingsV5.Customers + "/" + parameters.CustomerId; + + var response = GetJsonObjectFromAPI( + new GenericParameters(), + url, + HttpMethodType.Delete, + out resultResponse); + + // Handle empty or null response (204 No Content) + if (response == null && (resultResponse == null || resultResponse.Code == 0)) + { + resultResponse = new ResultResponse + { + Status = true, + Code = 204, + ExitCode = 0, + Messages = new Dictionary + { + { "Info", new[] { "No Content (204) — Customer deleted successfully." } } + } + }; + } + + return resultResponse; + } + + /// + /// Delete a customer (async). + /// + /// Customer ID parameter. + /// Status result. + public async Task> DeleteCustomerAsync(CustomerIdParameters parameters) + { + var url = R4MEInfrastructureSettingsV5.Customers + "/" + parameters.CustomerId; + + var responseTuple = await GetJsonObjectFromAPIAsync( + new GenericParameters(), + url, + HttpMethodType.Delete); + + var result = responseTuple?.Item1; + var meta = responseTuple?.Item2; + + // If the API returned 204 or no content at all, it's a success. + if (result == null && (meta == null || meta.Code == 0)) + { + var successResponse = new ResultResponse + { + Status = true, + Code = 204, + ExitCode = 0, + Messages = new Dictionary + { + { "Info", new[] { "No Content (204) — Customer deleted successfully." } } + } + }; + + return Tuple.Create(successResponse, successResponse); + } + + // If the API still returned 204 in meta + if (meta != null && meta.Code == 204) + { + meta.Status = true; + if (meta.Messages == null) + { + meta.Messages = new Dictionary + { + { "Info", new[] { "Customer deleted successfully (204)." } } + }; + } + + return new Tuple(meta, meta); + } + + return responseTuple; + } + + #endregion + } +} diff --git a/route4me-csharp-sdk/Route4MeSDKLibrary/Managers/FacilityManagerV5.cs b/route4me-csharp-sdk/Route4MeSDKLibrary/Managers/FacilityManagerV5.cs new file mode 100644 index 00000000..d449e904 --- /dev/null +++ b/route4me-csharp-sdk/Route4MeSDKLibrary/Managers/FacilityManagerV5.cs @@ -0,0 +1,486 @@ +using System; +using System.Threading.Tasks; +using Route4MeSDK; +using Route4MeSDK.DataTypes.V5; +using Route4MeSDK.QueryTypes; +using Route4MeSDKLibrary.DataTypes.V5.Facilities; +using Route4MeSDKLibrary.QueryTypes.V5.Facilities; + +namespace Route4MeSDKLibrary.Managers +{ + /// + /// Facility Management API V5 + /// + public class FacilityManagerV5 : Route4MeManagerBase + { + /// + /// Constructor + /// + /// Route4Me API key + public FacilityManagerV5(string apiKey) : base(apiKey) + { + } + + /// + /// Get a single facility by ID + /// + /// Facility ID + /// Error response + /// Facility resource + public FacilityResource GetFacility(string facilityId, out ResultResponse resultResponse) + { + if (string.IsNullOrWhiteSpace(facilityId)) + { + resultResponse = new ResultResponse + { + Status = false, + Messages = new System.Collections.Generic.Dictionary + { + { "error", new[] { "Facility ID is required" } } + } + }; + return null; + } + + var result = GetJsonObjectFromAPI( + new GenericParameters(), + $"{R4MEInfrastructureSettingsV5.Facilities}/{facilityId}", + HttpMethodType.Get, + out resultResponse + ); + + return result; + } + + /// + /// Get a paginated list of facilities + /// + /// Query parameters + /// Error response + /// Paginated facilities + public FacilitiesPaginateResource GetFacilities( + FacilityGetParameters parameters, + out ResultResponse resultResponse) + { + if (parameters == null) + { + parameters = new FacilityGetParameters(); + } + + var result = GetJsonObjectFromAPI( + parameters, + R4MEInfrastructureSettingsV5.Facilities, + HttpMethodType.Get, + out resultResponse + ); + + return result; + } + + /// + /// Create a new facility + /// + /// Facility creation request + /// Error response + /// Created facility + public FacilityResource CreateFacility( + FacilityCreateRequest facility, + out ResultResponse resultResponse) + { + if (facility == null) + { + resultResponse = new ResultResponse + { + Status = false, + Messages = new System.Collections.Generic.Dictionary + { + { "error", new[] { "Facility data is required" } } + } + }; + return null; + } + + // Debug: Log the API call details + System.Console.WriteLine($"[DEBUG] Creating facility at URL: {R4MEInfrastructureSettingsV5.Facilities}"); + System.Console.WriteLine($"[DEBUG] Request data: FacilityAlias={facility.FacilityAlias}, Address={facility.Address}, Status={facility.Status}"); + + var result = GetJsonObjectFromAPI( + facility, + R4MEInfrastructureSettingsV5.Facilities, + HttpMethodType.Post, + out resultResponse + ); + + // Debug: Log the response + System.Console.WriteLine($"[DEBUG] API Response: Result={(result != null ? "SUCCESS" : "NULL")}"); + if (resultResponse != null) + { + System.Console.WriteLine($"[DEBUG] Error Response: Status={resultResponse.Status}, Code={resultResponse.Code}"); + if (resultResponse.Messages != null) + { + foreach (var msg in resultResponse.Messages) + { + System.Console.WriteLine($"[DEBUG] Error Message: {msg.Key} = {string.Join(", ", msg.Value)}"); + } + } + } + + return result; + } + + /// + /// Update an existing facility + /// + /// Facility ID + /// Facility update request + /// Error response + /// Updated facility + public FacilityResource UpdateFacility( + string facilityId, + FacilityUpdateRequest facility, + out ResultResponse resultResponse) + { + if (string.IsNullOrWhiteSpace(facilityId)) + { + resultResponse = new ResultResponse + { + Status = false, + Messages = new System.Collections.Generic.Dictionary + { + { "error", new[] { "Facility ID is required" } } + } + }; + return null; + } + + if (facility == null) + { + resultResponse = new ResultResponse + { + Status = false, + Messages = new System.Collections.Generic.Dictionary + { + { "error", new[] { "Facility update data is required" } } + } + }; + return null; + } + + var result = GetJsonObjectFromAPI( + facility, + $"{R4MEInfrastructureSettingsV5.Facilities}/{facilityId}", + HttpMethodType.Put, + out resultResponse + ); + + return result; + } + + /// + /// Delete a facility by ID + /// + /// Facility ID + /// Error response + /// Remaining facilities collection + public FacilityResource[] DeleteFacility( + string facilityId, + out ResultResponse resultResponse) + { + if (string.IsNullOrWhiteSpace(facilityId)) + { + resultResponse = new ResultResponse + { + Status = false, + Messages = new System.Collections.Generic.Dictionary + { + { "error", new[] { "Facility ID is required" } } + } + }; + return null; + } + + var result = GetJsonObjectFromAPI( + new GenericParameters(), + $"{R4MEInfrastructureSettingsV5.Facilities}/{facilityId}", + HttpMethodType.Delete, + out resultResponse + ); + + return result; + } + + /// + /// Get all facility types + /// + /// Error response + /// Collection of facility types + public FacilityTypeCollectionResource GetFacilityTypes(out ResultResponse resultResponse) + { + // Debug: Log the API call details + System.Console.WriteLine($"[DEBUG] Getting facility types at URL: {R4MEInfrastructureSettingsV5.FacilityTypes}"); + + var result = GetJsonObjectFromAPI( + new GenericParameters(), + R4MEInfrastructureSettingsV5.FacilityTypes, + HttpMethodType.Get, + out resultResponse + ); + + // Debug: Log the response + System.Console.WriteLine($"[DEBUG] Facility Types Response: Result={(result != null ? "SUCCESS" : "NULL")}"); + if (result != null) + { + System.Console.WriteLine($"[DEBUG] Facility Types Count: {result.Data?.Length ?? 0}"); + } + if (resultResponse != null) + { + System.Console.WriteLine($"[DEBUG] Error Response: Status={resultResponse.Status}, Code={resultResponse.Code}"); + if (resultResponse.Messages != null) + { + foreach (var msg in resultResponse.Messages) + { + System.Console.WriteLine($"[DEBUG] Error Message: {msg.Key} = {string.Join(", ", msg.Value)}"); + } + } + } + + return result; + } + + /// + /// Get a single facility type by ID + /// + /// Facility type ID + /// Error response + /// Facility type resource + public FacilityTypeResource GetFacilityType(int typeId, out ResultResponse resultResponse) + { + if (typeId <= 0) + { + resultResponse = new ResultResponse + { + Status = false, + Messages = new System.Collections.Generic.Dictionary + { + { "error", new[] { "Valid facility type ID is required" } } + } + }; + return null; + } + + var result = GetJsonObjectFromAPI( + new GenericParameters(), + $"{R4MEInfrastructureSettingsV5.FacilityTypes}/{typeId}", + HttpMethodType.Get, + out resultResponse + ); + + return result; + } + + #region Async Methods + + /// + /// Get a single facility by ID asynchronously + /// + /// Facility ID + /// A Tuple containing the facility resource and error response + public async Task> GetFacilityAsync(string facilityId) + { + if (string.IsNullOrWhiteSpace(facilityId)) + { + var error = new ResultResponse + { + Status = false, + Messages = new System.Collections.Generic.Dictionary + { + { "error", new[] { "Facility ID is required" } } + } + }; + return new Tuple(null, error); + } + + var result = await GetJsonObjectFromAPIAsync( + new GenericParameters(), + $"{R4MEInfrastructureSettingsV5.Facilities}/{facilityId}", + HttpMethodType.Get + ).ConfigureAwait(false); + + return new Tuple(result.Item1, result.Item2); + } + + /// + /// Get a paginated list of facilities asynchronously + /// + /// Query parameters + /// A Tuple containing paginated facilities and error response + public async Task> GetFacilitiesAsync( + FacilityGetParameters parameters = null) + { + if (parameters == null) + { + parameters = new FacilityGetParameters(); + } + + var result = await GetJsonObjectFromAPIAsync( + parameters, + R4MEInfrastructureSettingsV5.Facilities, + HttpMethodType.Get + ).ConfigureAwait(false); + + return new Tuple(result.Item1, result.Item2); + } + + /// + /// Create a new facility asynchronously + /// + /// Facility creation request + /// A Tuple containing the created facility and error response + public async Task> CreateFacilityAsync( + FacilityCreateRequest facility) + { + if (facility == null) + { + var error = new ResultResponse + { + Status = false, + Messages = new System.Collections.Generic.Dictionary + { + { "error", new[] { "Facility data is required" } } + } + }; + return new Tuple(null, error); + } + + var result = await GetJsonObjectFromAPIAsync( + facility, + R4MEInfrastructureSettingsV5.Facilities, + HttpMethodType.Post + ).ConfigureAwait(false); + + return new Tuple(result.Item1, result.Item2); + } + + /// + /// Update an existing facility asynchronously + /// + /// Facility ID + /// Facility update request + /// A Tuple containing the updated facility and error response + public async Task> UpdateFacilityAsync( + string facilityId, + FacilityUpdateRequest facility) + { + if (string.IsNullOrWhiteSpace(facilityId)) + { + var error = new ResultResponse + { + Status = false, + Messages = new System.Collections.Generic.Dictionary + { + { "error", new[] { "Facility ID is required" } } + } + }; + return new Tuple(null, error); + } + + if (facility == null) + { + var error = new ResultResponse + { + Status = false, + Messages = new System.Collections.Generic.Dictionary + { + { "error", new[] { "Facility update data is required" } } + } + }; + return new Tuple(null, error); + } + + var result = await GetJsonObjectFromAPIAsync( + facility, + $"{R4MEInfrastructureSettingsV5.Facilities}/{facilityId}", + HttpMethodType.Put + ).ConfigureAwait(false); + + return new Tuple(result.Item1, result.Item2); + } + + /// + /// Delete a facility by ID asynchronously + /// + /// Facility ID + /// A Tuple containing the remaining facilities collection and error response + public async Task> DeleteFacilityAsync( + string facilityId) + { + if (string.IsNullOrWhiteSpace(facilityId)) + { + var error = new ResultResponse + { + Status = false, + Messages = new System.Collections.Generic.Dictionary + { + { "error", new[] { "Facility ID is required" } } + } + }; + return new Tuple(null, error); + } + + var result = await GetJsonObjectFromAPIAsync( + new GenericParameters(), + $"{R4MEInfrastructureSettingsV5.Facilities}/{facilityId}", + HttpMethodType.Delete + ).ConfigureAwait(false); + + return new Tuple(result.Item1, result.Item2); + } + + /// + /// Get all facility types asynchronously + /// + /// A Tuple containing collection of facility types and error response + public async Task> GetFacilityTypesAsync() + { + var result = await GetJsonObjectFromAPIAsync( + new GenericParameters(), + R4MEInfrastructureSettingsV5.FacilityTypes, + HttpMethodType.Get + ).ConfigureAwait(false); + + return new Tuple(result.Item1, result.Item2); + } + + /// + /// Get a single facility type by ID asynchronously + /// + /// Facility type ID + /// A Tuple containing the facility type resource and error response + public async Task> GetFacilityTypeAsync(int typeId) + { + if (typeId <= 0) + { + var error = new ResultResponse + { + Status = false, + Messages = new System.Collections.Generic.Dictionary + { + { "error", new[] { "Valid facility type ID is required" } } + } + }; + return new Tuple(null, error); + } + + var result = await GetJsonObjectFromAPIAsync( + new GenericParameters(), + $"{R4MEInfrastructureSettingsV5.FacilityTypes}/{typeId}", + HttpMethodType.Get + ).ConfigureAwait(false); + + return new Tuple(result.Item1, result.Item2); + } + + #endregion + } +} + diff --git a/route4me-csharp-sdk/Route4MeSDKLibrary/Managers/NotesManagerV5.cs b/route4me-csharp-sdk/Route4MeSDKLibrary/Managers/NotesManagerV5.cs new file mode 100644 index 00000000..2c283fad --- /dev/null +++ b/route4me-csharp-sdk/Route4MeSDKLibrary/Managers/NotesManagerV5.cs @@ -0,0 +1,925 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Route4MeSDK; +using Route4MeSDK.DataTypes.V5; +using Route4MeSDK.DataTypes.V5.Notes; +using Route4MeSDK.QueryTypes; +using Route4MeSDK.QueryTypes.V5.Notes; + +namespace Route4MeSDKLibrary.Managers +{ + /// + /// Manager for Notes API v5.0 operations + /// + public class NotesManagerV5 : Route4MeManagerBase + { + public NotesManagerV5(string apiKey) : base(apiKey) + { + } + + #region Notes CRUD + + /// + /// Create a new note + /// + /// Note creation request + /// Error response + /// Created note resource + public RouteNoteResource CreateNote(NoteStoreRequest request, out ResultResponse resultResponse) + { + // Validate required parameters + if (request == null) + { + resultResponse = new ResultResponse + { + Status = false, + Messages = new Dictionary + { + {"Error", new[] {"The note request cannot be null"}} + } + }; + return null; + } + + if (string.IsNullOrWhiteSpace(request.RouteId)) + { + resultResponse = new ResultResponse + { + Status = false, + Messages = new Dictionary + { + {"Error", new[] {"The route_id is required"}} + } + }; + return null; + } + + if (string.IsNullOrWhiteSpace(request.StrNoteContents)) + { + resultResponse = new ResultResponse + { + Status = false, + Messages = new Dictionary + { + {"Error", new[] {"The note contents (strNoteContents) is required"}} + } + }; + return null; + } + + return GetJsonObjectFromAPI( + request, + R4MEInfrastructureSettingsV5.Notes, + HttpMethodType.Post, + out resultResponse); + } + + /// + /// Create a new note + /// + /// Note creation request + /// Created note resource + public async Task> CreateNoteAsync(NoteStoreRequest request) + { + // Validate required parameters + if (request == null) + { + return new Tuple(null, new ResultResponse + { + Status = false, + Messages = new Dictionary + { + {"Error", new[] {"The note request cannot be null"}} + } + }); + } + + if (string.IsNullOrWhiteSpace(request.RouteId)) + { + return new Tuple(null, new ResultResponse + { + Status = false, + Messages = new Dictionary + { + {"Error", new[] {"The route_id is required"}} + } + }); + } + + if (string.IsNullOrWhiteSpace(request.StrNoteContents)) + { + return new Tuple(null, new ResultResponse + { + Status = false, + Messages = new Dictionary + { + {"Error", new[] {"The note contents (strNoteContents) is required"}} + } + }); + } + + var result = await GetJsonObjectFromAPIAsync( + request, + R4MEInfrastructureSettingsV5.Notes, + HttpMethodType.Post); + + return new Tuple(result.Item1, result.Item2); + } + + /// + /// Get a note by ID (supports both integer and 32-char hex string) + /// + /// Note ID + /// Error response + /// Note resource + public RouteNoteResource GetNote(object noteId, out ResultResponse resultResponse) + { + if (noteId == null || string.IsNullOrWhiteSpace(noteId.ToString())) + { + resultResponse = new ResultResponse + { + Status = false, + Messages = new Dictionary + { + {"Error", new[] {"The note_id is required"}} + } + }; + return null; + } + + var url = R4MEInfrastructureSettingsV5.NoteById.Replace("{note_id}", noteId.ToString()); + + return GetJsonObjectFromAPI( + new GenericParameters(), + url, + HttpMethodType.Get, + out resultResponse); + } + + /// + /// Get a note by ID (supports both integer and 32-char hex string) + /// + /// Note ID + /// Note resource + public async Task> GetNoteAsync(object noteId) + { + if (noteId == null || string.IsNullOrWhiteSpace(noteId.ToString())) + { + return new Tuple(null, new ResultResponse + { + Status = false, + Messages = new Dictionary + { + {"Error", new[] {"The note_id is required"}} + } + }); + } + + var url = R4MEInfrastructureSettingsV5.NoteById.Replace("{note_id}", noteId.ToString()); + + var result = await GetJsonObjectFromAPIAsync( + new GenericParameters(), + url, + HttpMethodType.Get); + + return new Tuple(result.Item1, result.Item2); + } + + /// + /// Update a note by ID + /// + /// Note ID (supports both integer and 32-char hex string) + /// Note update request + /// Error response + /// Updated note resource + public RouteNoteResource UpdateNote(object noteId, NoteUpdateRequest request, out ResultResponse resultResponse) + { + if (noteId == null || string.IsNullOrWhiteSpace(noteId.ToString())) + { + resultResponse = new ResultResponse + { + Status = false, + Messages = new Dictionary + { + {"Error", new[] {"The note_id is required"}} + } + }; + return null; + } + + if (request == null) + { + resultResponse = new ResultResponse + { + Status = false, + Messages = new Dictionary + { + {"Error", new[] {"The update request cannot be null"}} + } + }; + return null; + } + + var url = R4MEInfrastructureSettingsV5.NoteById.Replace("{note_id}", noteId.ToString()); + + return GetJsonObjectFromAPI( + request, + url, + HttpMethodType.Post, + out resultResponse); + } + + /// + /// Update a note by ID + /// + /// Note ID (supports both integer and 32-char hex string) + /// Note update request + /// Updated note resource + public async Task> UpdateNoteAsync(object noteId, NoteUpdateRequest request) + { + if (noteId == null || string.IsNullOrWhiteSpace(noteId.ToString())) + { + return new Tuple(null, new ResultResponse + { + Status = false, + Messages = new Dictionary + { + {"Error", new[] {"The note_id is required"}} + } + }); + } + + if (request == null) + { + return new Tuple(null, new ResultResponse + { + Status = false, + Messages = new Dictionary + { + {"Error", new[] {"The update request cannot be null"}} + } + }); + } + + var url = R4MEInfrastructureSettingsV5.NoteById.Replace("{note_id}", noteId.ToString()); + + var result = await GetJsonObjectFromAPIAsync( + request, + url, + HttpMethodType.Post); + + return new Tuple(result.Item1, result.Item2); + } + + /// + /// Delete a note by ID + /// + /// Note ID (supports both integer and 32-char hex string) + /// Error response + /// Delete response with status and note_id + public DeleteNoteResponse DeleteNote(object noteId, out ResultResponse resultResponse) + { + if (noteId == null || string.IsNullOrWhiteSpace(noteId.ToString())) + { + resultResponse = new ResultResponse + { + Status = false, + Messages = new Dictionary + { + {"Error", new[] {"The note_id is required"}} + } + }; + return null; + } + + var url = R4MEInfrastructureSettingsV5.NoteById.Replace("{note_id}", noteId.ToString()); + + return GetJsonObjectFromAPI( + new GenericParameters(), + url, + HttpMethodType.Delete, + out resultResponse); + } + + /// + /// Delete a note by ID + /// + /// Note ID (supports both integer and 32-char hex string) + /// Delete response with status and note_id + public async Task> DeleteNoteAsync(object noteId) + { + if (noteId == null || string.IsNullOrWhiteSpace(noteId.ToString())) + { + return new Tuple(null, new ResultResponse + { + Status = false, + Messages = new Dictionary + { + {"Error", new[] {"The note_id is required"}} + } + }); + } + + var url = R4MEInfrastructureSettingsV5.NoteById.Replace("{note_id}", noteId.ToString()); + + var result = await GetJsonObjectFromAPIAsync( + new GenericParameters(), + url, + HttpMethodType.Delete); + + return new Tuple(result.Item1, result.Item2); + } + + #endregion + + #region Query Notes + + /// + /// Get notes by route ID + /// + /// Route ID (32-char hex string) + /// Page number + /// Items per page + /// Error response + /// Collection of notes + public RouteNoteCollection GetNotesByRoute(string routeId, int? page, int? perPage, out ResultResponse resultResponse) + { + if (string.IsNullOrWhiteSpace(routeId)) + { + resultResponse = new ResultResponse + { + Status = false, + Messages = new Dictionary + { + {"Error", new[] {"The route_id is required"}} + } + }; + return null; + } + + // Validate pagination parameters + if (page.HasValue && page.Value < 1) + { + resultResponse = new ResultResponse + { + Status = false, + Messages = new Dictionary + { + {"Error", new[] {"Page number must be greater than 0"}} + } + }; + return null; + } + + if (perPage.HasValue && perPage.Value < 1) + { + resultResponse = new ResultResponse + { + Status = false, + Messages = new Dictionary + { + {"Error", new[] {"Per page value must be greater than 0"}} + } + }; + return null; + } + + var url = R4MEInfrastructureSettingsV5.NotesByRoute.Replace("{route_id}", routeId); + + var parameters = new GenericParameters(); + if (page.HasValue) + { + parameters.ParametersCollection.Add("page", page.Value.ToString()); + } + if (perPage.HasValue) + { + parameters.ParametersCollection.Add("per_page", perPage.Value.ToString()); + } + + return GetJsonObjectFromAPI( + parameters, + url, + HttpMethodType.Get, + out resultResponse); + } + + /// + /// Get notes by route ID + /// + /// Route ID (32-char hex string) + /// Page number + /// Items per page + /// Collection of notes + public async Task> GetNotesByRouteAsync(string routeId, int? page, int? perPage) + { + if (string.IsNullOrWhiteSpace(routeId)) + { + return new Tuple(null, new ResultResponse + { + Status = false, + Messages = new Dictionary + { + {"Error", new[] {"The route_id is required"}} + } + }); + } + + // Validate pagination parameters + if (page.HasValue && page.Value < 1) + { + return new Tuple(null, new ResultResponse + { + Status = false, + Messages = new Dictionary + { + {"Error", new[] {"Page number must be greater than 0"}} + } + }); + } + + if (perPage.HasValue && perPage.Value < 1) + { + return new Tuple(null, new ResultResponse + { + Status = false, + Messages = new Dictionary + { + {"Error", new[] {"Per page value must be greater than 0"}} + } + }); + } + + var url = R4MEInfrastructureSettingsV5.NotesByRoute.Replace("{route_id}", routeId); + + var parameters = new GenericParameters(); + if (page.HasValue) + { + parameters.ParametersCollection.Add("page", page.Value.ToString()); + } + if (perPage.HasValue) + { + parameters.ParametersCollection.Add("per_page", perPage.Value.ToString()); + } + + var result = await GetJsonObjectFromAPIAsync( + parameters, + url, + HttpMethodType.Get); + + return new Tuple(result.Item1, result.Item2); + } + + /// + /// Get notes by destination ID + /// + /// Route destination ID (supports both integer and 32-char hex string) + /// Page number + /// Items per page + /// Error response + /// Collection of notes + public RouteNoteCollection GetNotesByDestination(object routeDestinationId, int? page, int? perPage, out ResultResponse resultResponse) + { + if (routeDestinationId == null || string.IsNullOrWhiteSpace(routeDestinationId.ToString())) + { + resultResponse = new ResultResponse + { + Status = false, + Messages = new Dictionary + { + {"Error", new[] {"The route_destination_id is required"}} + } + }; + return null; + } + + // Validate pagination parameters + if (page.HasValue && page.Value < 1) + { + resultResponse = new ResultResponse + { + Status = false, + Messages = new Dictionary + { + {"Error", new[] {"Page number must be greater than 0"}} + } + }; + return null; + } + + if (perPage.HasValue && perPage.Value < 1) + { + resultResponse = new ResultResponse + { + Status = false, + Messages = new Dictionary + { + {"Error", new[] {"Per page value must be greater than 0"}} + } + }; + return null; + } + + var url = R4MEInfrastructureSettingsV5.NotesByDestination.Replace("{route_destination_id}", routeDestinationId.ToString()); + + var parameters = new GenericParameters(); + if (page.HasValue) + { + parameters.ParametersCollection.Add("page", page.Value.ToString()); + } + if (perPage.HasValue) + { + parameters.ParametersCollection.Add("per_page", perPage.Value.ToString()); + } + + return GetJsonObjectFromAPI( + parameters, + url, + HttpMethodType.Get, + out resultResponse); + } + + /// + /// Get notes by destination ID + /// + /// Route destination ID (supports both integer and 32-char hex string) + /// Page number + /// Items per page + /// Collection of notes + public async Task> GetNotesByDestinationAsync(object routeDestinationId, int? page, int? perPage) + { + if (routeDestinationId == null || string.IsNullOrWhiteSpace(routeDestinationId.ToString())) + { + return new Tuple(null, new ResultResponse + { + Status = false, + Messages = new Dictionary + { + {"Error", new[] {"The route_destination_id is required"}} + } + }); + } + + // Validate pagination parameters + if (page.HasValue && page.Value < 1) + { + return new Tuple(null, new ResultResponse + { + Status = false, + Messages = new Dictionary + { + {"Error", new[] {"Page number must be greater than 0"}} + } + }); + } + + if (perPage.HasValue && perPage.Value < 1) + { + return new Tuple(null, new ResultResponse + { + Status = false, + Messages = new Dictionary + { + {"Error", new[] {"Per page value must be greater than 0"}} + } + }); + } + + var url = R4MEInfrastructureSettingsV5.NotesByDestination.Replace("{route_destination_id}", routeDestinationId.ToString()); + + var parameters = new GenericParameters(); + if (page.HasValue) + { + parameters.ParametersCollection.Add("page", page.Value.ToString()); + } + if (perPage.HasValue) + { + parameters.ParametersCollection.Add("per_page", perPage.Value.ToString()); + } + + var result = await GetJsonObjectFromAPIAsync( + parameters, + url, + HttpMethodType.Get); + + return new Tuple(result.Item1, result.Item2); + } + + #endregion + + #region Custom Note Types + + /// + /// Get all custom note types + /// + /// Error response + /// Collection of custom note types + public NoteCustomTypeCollection GetNoteCustomTypes(out ResultResponse resultResponse) + { + return GetJsonObjectFromAPI( + new GenericParameters(), + R4MEInfrastructureSettingsV5.NotesCustomTypes, + HttpMethodType.Get, + out resultResponse); + } + + /// + /// Get all custom note types + /// + /// Collection of custom note types + public async Task> GetNoteCustomTypesAsync() + { + var result = await GetJsonObjectFromAPIAsync( + new GenericParameters(), + R4MEInfrastructureSettingsV5.NotesCustomTypes, + HttpMethodType.Get); + + return new Tuple(result.Item1, result.Item2); + } + + /// + /// Create a custom note type + /// + /// Custom note type creation request + /// Error response + /// Collection including the new custom note type + public NoteCustomTypeCollection CreateNoteCustomType(NoteCustomTypeStoreRequest request, out ResultResponse resultResponse) + { + if (request == null) + { + resultResponse = new ResultResponse + { + Status = false, + Messages = new Dictionary + { + {"Error", new[] {"The custom type request cannot be null"}} + } + }; + return null; + } + + if (string.IsNullOrWhiteSpace(request.NoteCustomType)) + { + resultResponse = new ResultResponse + { + Status = false, + Messages = new Dictionary + { + {"Error", new[] {"The note_custom_type is required"}} + } + }; + return null; + } + + if (request.NoteCustomTypeValues == null || request.NoteCustomTypeValues.Length == 0) + { + resultResponse = new ResultResponse + { + Status = false, + Messages = new Dictionary + { + {"Error", new[] {"The note_custom_type_values is required and must contain at least one value"}} + } + }; + return null; + } + + // Validate note_custom_field_type (should be 1, 2, 3, or 4) + if (request.NoteCustomFieldType.HasValue && + (request.NoteCustomFieldType.Value < 1 || request.NoteCustomFieldType.Value > 4)) + { + resultResponse = new ResultResponse + { + Status = false, + Messages = new Dictionary + { + {"Error", new[] {"The note_custom_field_type must be 1, 2, 3, or 4"}} + } + }; + return null; + } + + return GetJsonObjectFromAPI( + request, + R4MEInfrastructureSettingsV5.NotesCustomTypes, + HttpMethodType.Post, + out resultResponse); + } + + /// + /// Create a custom note type + /// + /// Custom note type creation request + /// Collection including the new custom note type + public async Task> CreateNoteCustomTypeAsync( + NoteCustomTypeStoreRequest request) + { + if (request == null) + { + return new Tuple(null, new ResultResponse + { + Status = false, + Messages = new Dictionary + { + { "Error", new[] { "The custom type request cannot be null" } } + } + }); + } + + if (string.IsNullOrWhiteSpace(request.NoteCustomType)) + { + return new Tuple(null, new ResultResponse + { + Status = false, + Messages = new Dictionary + { + { "Error", new[] { "The note_custom_type is required" } } + } + }); + } + + if (request.NoteCustomTypeValues == null || request.NoteCustomTypeValues.Length == 0) + { + return new Tuple(null, new ResultResponse + { + Status = false, + Messages = new Dictionary + { + { + "Error", + new[] { "The note_custom_type_values is required and must contain at least one value" } + } + } + }); + } + + // Validate note_custom_field_type (should be 1, 2, 3, or 4) + if (request.NoteCustomFieldType.HasValue && + (request.NoteCustomFieldType.Value < 1 || request.NoteCustomFieldType.Value > 4)) + { + return new Tuple(null, new ResultResponse + { + Status = false, + Messages = new Dictionary + { + { "Error", new[] { "The note_custom_field_type must be 1, 2, 3, or 4" } } + } + }); + } + + var result = await GetJsonObjectFromAPIAsync( + request, + R4MEInfrastructureSettingsV5.NotesCustomTypes, + HttpMethodType.Post); + + return new Tuple(result.Item1, result.Item2); + } + + #endregion + + #region Bulk Create Notes + + /// + /// Bulk create notes + /// + /// Bulk note creation request + /// Error response + /// Bulk create response with status and async flag + public BulkNotesResponse BulkCreateNotes(NoteStoreBulkRequest request, out ResultResponse resultResponse) + { + if (request == null) + { + resultResponse = new ResultResponse + { + Status = false, + Messages = new Dictionary + { + {"Error", new[] {"The bulk note request cannot be null"}} + } + }; + return null; + } + + if (request.Notes == null || request.Notes.Length == 0) + { + resultResponse = new ResultResponse + { + Status = false, + Messages = new Dictionary + { + {"Error", new[] {"The notes array cannot be null or empty"}} + } + }; + return null; + } + + for (int i = 0; i < request.Notes.Length; i++) + { + var note = request.Notes[i]; + + if (string.IsNullOrWhiteSpace(note.RouteId)) + { + resultResponse = new ResultResponse + { + Status = false, + Messages = new Dictionary + { + {"Error", new[] {$"The route_id is required for note at index {i}"}} + } + }; + return null; + } + + if (string.IsNullOrWhiteSpace(note.StrNoteContents)) + { + resultResponse = new ResultResponse + { + Status = false, + Messages = new Dictionary + { + {"Error", new[] {$"The note contents (strNoteContents) is required for note at index {i}"}} + } + }; + return null; + } + } + + return GetJsonObjectFromAPI( + request, + R4MEInfrastructureSettingsV5.NotesBulkCreate, + HttpMethodType.Post, + out resultResponse); + } + + /// + /// Bulk create notes asynchronously + /// + /// Bulk note creation request + /// Bulk create response with status and async flag + public async Task> BulkCreateNotesAsync(NoteStoreBulkRequest request) + { + if (request == null) + { + return new Tuple(null, new ResultResponse + { + Status = false, + Messages = new Dictionary + { + {"Error", new[] {"The bulk note request cannot be null"}} + } + }); + } + + if (request.Notes == null || request.Notes.Length == 0) + { + return new Tuple(null, new ResultResponse + { + Status = false, + Messages = new Dictionary + { + {"Error", new[] {"The notes array cannot be null or empty"}} + } + }); + } + + for (int i = 0; i < request.Notes.Length; i++) + { + var note = request.Notes[i]; + + if (string.IsNullOrWhiteSpace(note.RouteId)) + { + return new Tuple(null, new ResultResponse + { + Status = false, + Messages = new Dictionary + { + {"Error", new[] {$"The route_id is required for note at index {i}"}} + } + }); + } + + if (string.IsNullOrWhiteSpace(note.StrNoteContents)) + { + return new Tuple(null, new ResultResponse + { + Status = false, + Messages = new Dictionary + { + {"Error", new[] {$"The note contents (strNoteContents) is required for note at index {i}"}} + } + }); + } + } + + var result = await GetJsonObjectFromAPIAsync( + request, + R4MEInfrastructureSettingsV5.NotesBulkCreate, + HttpMethodType.Post); + + return new Tuple(result.Item1, result.Item2); + } + + #endregion + } +} + diff --git a/route4me-csharp-sdk/Route4MeSDKLibrary/QueryTypes/V5/Customers/CustomerIdParameters.cs b/route4me-csharp-sdk/Route4MeSDKLibrary/QueryTypes/V5/Customers/CustomerIdParameters.cs new file mode 100644 index 00000000..ece9a805 --- /dev/null +++ b/route4me-csharp-sdk/Route4MeSDKLibrary/QueryTypes/V5/Customers/CustomerIdParameters.cs @@ -0,0 +1,18 @@ +using Route4MeSDK.QueryTypes; +using System.Runtime.Serialization; + +namespace Route4MeSDKLibrary.QueryTypes.V5.Customers +{ + /// + /// Parameters for customer ID + /// + [DataContract] + public class CustomerIdParameters : GenericParameters + { + /// + /// Customer ID + /// + [HttpQueryMember(Name = "customer_id", EmitDefaultValue = false)] + public string CustomerId { get; set; } + } +} diff --git a/route4me-csharp-sdk/Route4MeSDKLibrary/QueryTypes/V5/Customers/CustomerListFilters.cs b/route4me-csharp-sdk/Route4MeSDKLibrary/QueryTypes/V5/Customers/CustomerListFilters.cs new file mode 100644 index 00000000..b2132742 --- /dev/null +++ b/route4me-csharp-sdk/Route4MeSDKLibrary/QueryTypes/V5/Customers/CustomerListFilters.cs @@ -0,0 +1,53 @@ +using System.Runtime.Serialization; + +namespace Route4MeSDKLibrary.QueryTypes.V5.Customers +{ + /// + /// Filters for CustomerListParameters + /// + [DataContract] + public class CustomerListFilters + { + [DataMember(Name = "search_query", EmitDefaultValue = false)] + public string SearchQuery { get; set; } + + [DataMember(Name = "view_mode", EmitDefaultValue = false)] + public string ViewMode { get; set; } + + [DataMember(Name = "root_member_id", EmitDefaultValue = false)] + public long[] RootMemberId { get; set; } + + [DataMember(Name = "customer_id", EmitDefaultValue = false)] + public string[] CustomerId { get; set; } + + [DataMember(Name = "name", EmitDefaultValue = false)] + public string[] Name { get; set; } + + [DataMember(Name = "external_id", EmitDefaultValue = false)] + public string[] ExternalId { get; set; } + + [DataMember(Name = "accountable_person_id", EmitDefaultValue = false)] + public long[] AccountablePersonId { get; set; } + + [DataMember(Name = "currency", EmitDefaultValue = false)] + public string[] Currency { get; set; } + + [DataMember(Name = "tax_id", EmitDefaultValue = false)] + public string[] TaxId { get; set; } + + [DataMember(Name = "created_by_member_id", EmitDefaultValue = false)] + public long[] CreatedByMemberId { get; set; } + + [DataMember(Name = "status", EmitDefaultValue = false)] + public int[] Status { get; set; } + + [DataMember(Name = "creation_date", EmitDefaultValue = false)] + public string[] CreationDate { get; set; } + + [DataMember(Name = "customer_location_count", EmitDefaultValue = false)] + public int[] CustomerLocationCount { get; set; } + + [DataMember(Name = "facility_ids", EmitDefaultValue = false)] + public string[] FacilityIds { get; set; } + } +} diff --git a/route4me-csharp-sdk/Route4MeSDKLibrary/QueryTypes/V5/Customers/CustomerListParameters.cs b/route4me-csharp-sdk/Route4MeSDKLibrary/QueryTypes/V5/Customers/CustomerListParameters.cs new file mode 100644 index 00000000..d473006f --- /dev/null +++ b/route4me-csharp-sdk/Route4MeSDKLibrary/QueryTypes/V5/Customers/CustomerListParameters.cs @@ -0,0 +1,42 @@ +using Route4MeSDK.QueryTypes; +using System.Runtime.Serialization; + +namespace Route4MeSDKLibrary.QueryTypes.V5.Customers +{ + /// + /// Request body for getting list with customers data + /// + [DataContract] + public class CustomerListParameters : GenericParameters + { + /// + /// Filters for customers list + /// + [DataMember(Name = "filters", EmitDefaultValue = false)] + public CustomerListFilters Filters { get; set; } + + /// + /// Order by + /// + [DataMember(Name = "order_by", EmitDefaultValue = false)] + public string[] OrderBy { get; set; } + + /// + /// Page number + /// + [DataMember(Name = "page", EmitDefaultValue = false)] + public int? Page { get; set; } + + /// + /// Items per page + /// + [DataMember(Name = "per_page", EmitDefaultValue = false)] + public int? PerPage { get; set; } + + /// + /// Fields to include + /// + [DataMember(Name = "fields", EmitDefaultValue = false)] + public string[] Fields { get; set; } + } +} diff --git a/route4me-csharp-sdk/Route4MeSDKLibrary/QueryTypes/V5/Customers/UpdateCustomerParameters.cs b/route4me-csharp-sdk/Route4MeSDKLibrary/QueryTypes/V5/Customers/UpdateCustomerParameters.cs new file mode 100644 index 00000000..15bfe278 --- /dev/null +++ b/route4me-csharp-sdk/Route4MeSDKLibrary/QueryTypes/V5/Customers/UpdateCustomerParameters.cs @@ -0,0 +1,19 @@ +using Route4MeSDK.QueryTypes; +using Route4MeSDKLibrary.DataTypes.V5.Customers; +using System.Runtime.Serialization; + +namespace Route4MeSDKLibrary.QueryTypes.V5.Customers +{ + /// + /// Request body for creating a customer + /// + [DataContract] + public class UpdateCustomerParameters : StoreRequest + { + /// + /// Customer ID + /// + [HttpQueryMember(Name = "customer_id", EmitDefaultValue = false)] + public string CustomerId { get; set; } + } +} diff --git a/route4me-csharp-sdk/Route4MeSDKLibrary/QueryTypes/V5/Facilities/FacilityCreateRequest.cs b/route4me-csharp-sdk/Route4MeSDKLibrary/QueryTypes/V5/Facilities/FacilityCreateRequest.cs new file mode 100644 index 00000000..8dce4f5d --- /dev/null +++ b/route4me-csharp-sdk/Route4MeSDKLibrary/QueryTypes/V5/Facilities/FacilityCreateRequest.cs @@ -0,0 +1,63 @@ +using System.Runtime.Serialization; +using Route4MeSDK.QueryTypes; +using Route4MeSDKLibrary.DataTypes.V5.Facilities; + +namespace Route4MeSDKLibrary.QueryTypes.V5.Facilities +{ + /// + /// Request parameters for creating a facility + /// + [DataContract] + public class FacilityCreateRequest : GenericParameters + { + /// + /// Facility alias/name + /// + [DataMember(Name = "facility_alias")] + public string FacilityAlias { get; set; } + + /// + /// Facility address + /// + [DataMember(Name = "address")] + public string Address { get; set; } + + /// + /// Facility coordinates + /// + [DataMember(Name = "coordinates")] + public FacilityCoordinates Coordinates { get; set; } + + /// + /// Facility types array + /// + [DataMember(Name = "facility_types")] + public FacilityTypeAssignment[] FacilityTypes { get; set; } + + /// + /// Facility status: 1=ACTIVE, 2=INACTIVE, 3=IN_REVIEW + /// + [DataMember(Name = "status", EmitDefaultValue = false)] + public int? Status { get; set; } + } + + /// + /// Facility type assignment for create/update requests + /// + [DataContract] + public class FacilityTypeAssignment + { + /// + /// Facility type ID + /// + [DataMember(Name = "facility_type_id")] + public int FacilityTypeId { get; set; } + + /// + /// Is this the default facility type for this facility + /// + [DataMember(Name = "is_default", EmitDefaultValue = false)] + public bool? IsDefault { get; set; } + } +} + diff --git a/route4me-csharp-sdk/Route4MeSDKLibrary/QueryTypes/V5/Facilities/FacilityGetParameters.cs b/route4me-csharp-sdk/Route4MeSDKLibrary/QueryTypes/V5/Facilities/FacilityGetParameters.cs new file mode 100644 index 00000000..0a7a256a --- /dev/null +++ b/route4me-csharp-sdk/Route4MeSDKLibrary/QueryTypes/V5/Facilities/FacilityGetParameters.cs @@ -0,0 +1,44 @@ +using System.Runtime.Serialization; +using Route4MeSDK.QueryTypes; + +namespace Route4MeSDKLibrary.QueryTypes.V5.Facilities +{ + /// + /// Query parameters for retrieving facilities + /// + [DataContract] + public class FacilityGetParameters : GenericParameters + { + /// + /// Page number for pagination + /// + [HttpQueryMemberAttribute(Name = "page", EmitDefaultValue = false)] + public int? Page { get; set; } + + /// + /// Number of facilities per page + /// + [HttpQueryMemberAttribute(Name = "per_page", EmitDefaultValue = false)] + public int? PerPage { get; set; } + + /// + /// Filter by facility status + /// 1 = ACTIVE, 2 = INACTIVE, 3 = IN_REVIEW + /// + [HttpQueryMemberAttribute(Name = "status", EmitDefaultValue = false)] + public int? Status { get; set; } + + /// + /// Filter by facility type ID + /// + [HttpQueryMemberAttribute(Name = "facility_type_id", EmitDefaultValue = false)] + public int? FacilityTypeId { get; set; } + + /// + /// Search query string + /// + [HttpQueryMemberAttribute(Name = "search_query", EmitDefaultValue = false)] + public string SearchQuery { get; set; } + } +} + diff --git a/route4me-csharp-sdk/Route4MeSDKLibrary/QueryTypes/V5/Facilities/FacilityUpdateRequest.cs b/route4me-csharp-sdk/Route4MeSDKLibrary/QueryTypes/V5/Facilities/FacilityUpdateRequest.cs new file mode 100644 index 00000000..111484f9 --- /dev/null +++ b/route4me-csharp-sdk/Route4MeSDKLibrary/QueryTypes/V5/Facilities/FacilityUpdateRequest.cs @@ -0,0 +1,44 @@ +using System.Runtime.Serialization; +using Route4MeSDK.QueryTypes; +using Route4MeSDKLibrary.DataTypes.V5.Facilities; + +namespace Route4MeSDKLibrary.QueryTypes.V5.Facilities +{ + /// + /// Request parameters for updating a facility + /// + [DataContract] + public class FacilityUpdateRequest : GenericParameters + { + /// + /// Facility alias/name + /// + [DataMember(Name = "facility_alias")] + public string FacilityAlias { get; set; } + + /// + /// Facility address + /// + [DataMember(Name = "address")] + public string Address { get; set; } + + /// + /// Facility coordinates + /// + [DataMember(Name = "coordinates")] + public FacilityCoordinates Coordinates { get; set; } + + /// + /// Facility types array + /// + [DataMember(Name = "facility_types")] + public FacilityTypeAssignment[] FacilityTypes { get; set; } + + /// + /// Facility status: 1=ACTIVE, 2=INACTIVE, 3=IN_REVIEW + /// + [DataMember(Name = "status", EmitDefaultValue = false)] + public int? Status { get; set; } + } +} + diff --git a/route4me-csharp-sdk/Route4MeSDKLibrary/QueryTypes/V5/Notes/DestinationNotesParameters.cs b/route4me-csharp-sdk/Route4MeSDKLibrary/QueryTypes/V5/Notes/DestinationNotesParameters.cs new file mode 100644 index 00000000..41cc7082 --- /dev/null +++ b/route4me-csharp-sdk/Route4MeSDKLibrary/QueryTypes/V5/Notes/DestinationNotesParameters.cs @@ -0,0 +1,26 @@ +namespace Route4MeSDK.QueryTypes.V5.Notes +{ + /// + /// Parameters for getting notes by destination + /// + public class DestinationNotesParameters : GenericParameters + { + /// + /// Route destination ID (integer or 32-char hex string) + /// + [HttpQueryMember(Name = "route_destination_id", EmitDefaultValue = false)] + public string RouteDestinationId { get; set; } + + /// + /// Page number + /// + [HttpQueryMember(Name = "page", EmitDefaultValue = false)] + public int? Page { get; set; } + + /// + /// Items per page + /// + [HttpQueryMember(Name = "per_page", EmitDefaultValue = false)] + public int? PerPage { get; set; } + } +} diff --git a/route4me-csharp-sdk/Route4MeSDKLibrary/QueryTypes/V5/Notes/NoteCustomTypeStoreRequest.cs b/route4me-csharp-sdk/Route4MeSDKLibrary/QueryTypes/V5/Notes/NoteCustomTypeStoreRequest.cs new file mode 100644 index 00000000..c05f55b8 --- /dev/null +++ b/route4me-csharp-sdk/Route4MeSDKLibrary/QueryTypes/V5/Notes/NoteCustomTypeStoreRequest.cs @@ -0,0 +1,29 @@ +using System.Runtime.Serialization; + +namespace Route4MeSDK.QueryTypes.V5.Notes +{ + /// + /// Request to create a custom note type + /// + [DataContract] + public class NoteCustomTypeStoreRequest : GenericParameters + { + /// + /// Note custom type name (required) + /// + [DataMember(Name = "note_custom_type", EmitDefaultValue = false)] + public string NoteCustomType { get; set; } + + /// + /// Note custom type values (required) + /// + [DataMember(Name = "note_custom_type_values", EmitDefaultValue = false)] + public string[] NoteCustomTypeValues { get; set; } + + /// + /// Note custom field type (enum: 1,2,3,4) + /// + [DataMember(Name = "note_custom_field_type", EmitDefaultValue = false)] + public int? NoteCustomFieldType { get; set; } + } +} diff --git a/route4me-csharp-sdk/Route4MeSDKLibrary/QueryTypes/V5/Notes/NoteStoreBulkItem.cs b/route4me-csharp-sdk/Route4MeSDKLibrary/QueryTypes/V5/Notes/NoteStoreBulkItem.cs new file mode 100644 index 00000000..ed5c9d2d --- /dev/null +++ b/route4me-csharp-sdk/Route4MeSDKLibrary/QueryTypes/V5/Notes/NoteStoreBulkItem.cs @@ -0,0 +1,95 @@ +using System.Runtime.Serialization; + +namespace Route4MeSDK.QueryTypes.V5.Notes +{ + /// + /// Individual note item for bulk creation + /// + [DataContract] + public class NoteStoreBulkItem + { + /// + /// Route ID (required, 32-char hex string) + /// + [DataMember(Name = "route_id", EmitDefaultValue = false)] + public string RouteId { get; set; } + + /// + /// Note contents (required) + /// + [DataMember(Name = "strNoteContents", EmitDefaultValue = false)] + public string StrNoteContents { get; set; } + + /// + /// Address ID (nullable, integer) + /// + [DataMember(Name = "address_id", EmitDefaultValue = false)] + public long? AddressId { get; set; } + + /// + /// Destination UUID (nullable) + /// + [DataMember(Name = "destination_uuid", EmitDefaultValue = false)] + public string DestinationUuid { get; set; } + + /// + /// Update type (enum, nullable) + /// + [DataMember(Name = "strUpdateType", EmitDefaultValue = false)] + public string StrUpdateType { get; set; } + + /// + /// Device latitude (nullable) + /// + [DataMember(Name = "dev_lat", EmitDefaultValue = false)] + public double? DevLat { get; set; } + + /// + /// Device longitude (nullable) + /// + [DataMember(Name = "dev_lng", EmitDefaultValue = false)] + public double? DevLng { get; set; } + + /// + /// Remote speed (nullable) + /// + [DataMember(Name = "remote_speed", EmitDefaultValue = false)] + public double? RemoteSpeed { get; set; } + + /// + /// Remote altitude (nullable) + /// + [DataMember(Name = "remote_altitude", EmitDefaultValue = false)] + public double? RemoteAltitude { get; set; } + + /// + /// File attachment (binary, nullable) + /// + [DataMember(Name = "file", EmitDefaultValue = false)] + public string File { get; set; } + + /// + /// Device type (enum) + /// + [DataMember(Name = "device_type", EmitDefaultValue = false)] + public string DeviceType { get; set; } + + /// + /// Custom note types (array, nullable) + /// + [DataMember(Name = "custom_note_type", EmitDefaultValue = false)] + public int[] CustomNoteType { get; set; } + + /// + /// UTC time (unix timestamp in seconds, nullable) + /// + [DataMember(Name = "utc_time", EmitDefaultValue = false)] + public int? UtcTime { get; set; } + + /// + /// Upload ID (32-char hex string, nullable) + /// + [DataMember(Name = "upload_id", EmitDefaultValue = false)] + public string UploadId { get; set; } + } +} diff --git a/route4me-csharp-sdk/Route4MeSDKLibrary/QueryTypes/V5/Notes/NoteStoreBulkRequest.cs b/route4me-csharp-sdk/Route4MeSDKLibrary/QueryTypes/V5/Notes/NoteStoreBulkRequest.cs new file mode 100644 index 00000000..ee39e7a7 --- /dev/null +++ b/route4me-csharp-sdk/Route4MeSDKLibrary/QueryTypes/V5/Notes/NoteStoreBulkRequest.cs @@ -0,0 +1,23 @@ +using System.Runtime.Serialization; + +namespace Route4MeSDK.QueryTypes.V5.Notes +{ + /// + /// Request for bulk creating notes + /// + [DataContract] + public class NoteStoreBulkRequest : GenericParameters + { + /// + /// Array of notes to create + /// + [DataMember(Name = "notes", EmitDefaultValue = false)] + public NoteStoreBulkItem[] Notes { get; set; } + + /// + /// Device type (enum) + /// + [DataMember(Name = "device_type", EmitDefaultValue = false)] + public string DeviceType { get; set; } + } +} diff --git a/route4me-csharp-sdk/Route4MeSDKLibrary/QueryTypes/V5/Notes/NoteStoreRequest.cs b/route4me-csharp-sdk/Route4MeSDKLibrary/QueryTypes/V5/Notes/NoteStoreRequest.cs new file mode 100644 index 00000000..31dc2624 --- /dev/null +++ b/route4me-csharp-sdk/Route4MeSDKLibrary/QueryTypes/V5/Notes/NoteStoreRequest.cs @@ -0,0 +1,90 @@ +using System.Runtime.Serialization; +using Route4MeSDK.QueryTypes; + +namespace Route4MeSDK.QueryTypes.V5.Notes +{ + /// + /// Request to create a note + /// + [DataContract] + public class NoteStoreRequest : GenericParameters + { + /// + /// Route ID (required, 32-char hex string) + /// + [DataMember(Name = "route_id", EmitDefaultValue = false)] + public string RouteId { get; set; } + + /// + /// Note contents (required) + /// + [DataMember(Name = "strNoteContents", EmitDefaultValue = false)] + public string StrNoteContents { get; set; } + + /// + /// Address ID (nullable, integer) + /// + [DataMember(Name = "address_id", EmitDefaultValue = false)] + public long? AddressId { get; set; } + + /// + /// Destination UUID (nullable) + /// + [DataMember(Name = "destination_uuid", EmitDefaultValue = false)] + public string DestinationUuid { get; set; } + + /// + /// Update type (enum, nullable) + /// + [DataMember(Name = "strUpdateType", EmitDefaultValue = false)] + public string StrUpdateType { get; set; } + + /// + /// Device latitude (nullable) + /// + [DataMember(Name = "dev_lat", EmitDefaultValue = false)] + public double? DevLat { get; set; } + + /// + /// Device longitude (nullable) + /// + [DataMember(Name = "dev_lng", EmitDefaultValue = false)] + public double? DevLng { get; set; } + + /// + /// Remote speed (nullable) + /// + [DataMember(Name = "remote_speed", EmitDefaultValue = false)] + public double? RemoteSpeed { get; set; } + + /// + /// Remote altitude (nullable) + /// + [DataMember(Name = "remote_altitude", EmitDefaultValue = false)] + public double? RemoteAltitude { get; set; } + + /// + /// File attachment (binary, nullable) + /// + [DataMember(Name = "file", EmitDefaultValue = false)] + public string File { get; set; } + + /// + /// Device type (enum) + /// + [DataMember(Name = "device_type", EmitDefaultValue = false)] + public string DeviceType { get; set; } + + /// + /// Custom note types (array, nullable) + /// + [DataMember(Name = "custom_note_type", EmitDefaultValue = false)] + public int[] CustomNoteType { get; set; } + + /// + /// UTC time (unix timestamp in seconds, nullable) + /// + [DataMember(Name = "utc_time", EmitDefaultValue = false)] + public int? UtcTime { get; set; } + } +} diff --git a/route4me-csharp-sdk/Route4MeSDKLibrary/QueryTypes/V5/Notes/NoteUpdateRequest.cs b/route4me-csharp-sdk/Route4MeSDKLibrary/QueryTypes/V5/Notes/NoteUpdateRequest.cs new file mode 100644 index 00000000..533102fd --- /dev/null +++ b/route4me-csharp-sdk/Route4MeSDKLibrary/QueryTypes/V5/Notes/NoteUpdateRequest.cs @@ -0,0 +1,41 @@ +using System.Runtime.Serialization; + +namespace Route4MeSDK.QueryTypes.V5.Notes +{ + /// + /// Request to update a note + /// + [DataContract] + public class NoteUpdateRequest : GenericParameters + { + /// + /// Note contents + /// + [DataMember(Name = "strNoteContents", EmitDefaultValue = false)] + public string StrNoteContents { get; set; } + + /// + /// Update type (enum, nullable) + /// + [DataMember(Name = "strUpdateType", EmitDefaultValue = false)] + public string StrUpdateType { get; set; } + + /// + /// File attachment (binary) + /// + [DataMember(Name = "file", EmitDefaultValue = false)] + public string File { get; set; } + + /// + /// Device type (enum) + /// + [DataMember(Name = "device_type", EmitDefaultValue = false)] + public string DeviceType { get; set; } + + /// + /// Custom note types (array, nullable) + /// + [DataMember(Name = "custom_note_type", EmitDefaultValue = false)] + public int[] CustomNoteType { get; set; } + } +} diff --git a/route4me-csharp-sdk/Route4MeSDKLibrary/QueryTypes/V5/Notes/RouteNotesParameters.cs b/route4me-csharp-sdk/Route4MeSDKLibrary/QueryTypes/V5/Notes/RouteNotesParameters.cs new file mode 100644 index 00000000..1bb6c5f1 --- /dev/null +++ b/route4me-csharp-sdk/Route4MeSDKLibrary/QueryTypes/V5/Notes/RouteNotesParameters.cs @@ -0,0 +1,26 @@ +namespace Route4MeSDK.QueryTypes.V5.Notes +{ + /// + /// Parameters for getting notes by route + /// + public class RouteNotesParameters : GenericParameters + { + /// + /// Route ID (32-char hex string) + /// + [HttpQueryMember(Name = "route_id", EmitDefaultValue = false)] + public string RouteId { get; set; } + + /// + /// Page number + /// + [HttpQueryMember(Name = "page", EmitDefaultValue = false)] + public int? Page { get; set; } + + /// + /// Items per page + /// + [HttpQueryMember(Name = "per_page", EmitDefaultValue = false)] + public int? PerPage { get; set; } + } +} diff --git a/route4me-csharp-sdk/Route4MeSDKLibrary/Route4MeManagerV5.cs b/route4me-csharp-sdk/Route4MeSDKLibrary/Route4MeManagerV5.cs index 7eae0a70..3fb56acf 100644 --- a/route4me-csharp-sdk/Route4MeSDKLibrary/Route4MeManagerV5.cs +++ b/route4me-csharp-sdk/Route4MeSDKLibrary/Route4MeManagerV5.cs @@ -1,16 +1,18 @@ -using System; -using System.Threading.Tasks; -using Route4MeSDK.DataTypes.V5; +using Route4MeSDK.DataTypes.V5; using Route4MeSDK.DataTypes.V5.TelematicsPlatform; using Route4MeSDK.QueryTypes.V5; using Route4MeSDKLibrary.DataTypes.V5; using Route4MeSDKLibrary.DataTypes.V5.AddressBookContact; +using Route4MeSDKLibrary.DataTypes.V5.Customers; using Route4MeSDKLibrary.DataTypes.V5.Internal.Requests; using Route4MeSDKLibrary.DataTypes.V5.Orders; using Route4MeSDKLibrary.DataTypes.V5.Routes; using Route4MeSDKLibrary.DataTypes.V5.RouteStatus; using Route4MeSDKLibrary.Managers; +using Route4MeSDKLibrary.QueryTypes.V5.Customers; using Route4MeSDKLibrary.QueryTypes.V5.Orders; +using System; +using System.Threading.Tasks; using AddressBookParameters = Route4MeSDK.QueryTypes.V5.AddressBookParameters; using OptimizationParameters = Route4MeSDK.QueryTypes.V5.OptimizationParameters; using RouteParametersQuery = Route4MeSDK.QueryTypes.V5.RouteParametersQuery; @@ -46,6 +48,8 @@ public Route4MeManagerV5(string apiKey) _telematicsManager = new TelematicsManagerV5(apiKey); _orderManager = new OrderManagerV5(apiKey); _routeStatusManager = new RouteStatusManagerV5(apiKey); + _customerManager = new CustomerManagerV5(apiKey); + _facilityManager = new FacilityManagerV5(apiKey); } #endregion @@ -63,6 +67,8 @@ public Route4MeManagerV5(string apiKey) private readonly TelematicsManagerV5 _telematicsManager; private readonly OrderManagerV5 _orderManager; private readonly RouteStatusManagerV5 _routeStatusManager; + private readonly CustomerManagerV5 _customerManager; + private readonly FacilityManagerV5 _facilityManager; #endregion @@ -2251,5 +2257,124 @@ public Task> SetRouteStopStatusAsync(SetRo } #endregion + + #region Customers + + /// + /// Get customers list (paginated) + /// + /// Query parameters + /// Failure response + /// Customers list + public CustomerListResponse GetCustomersList(CustomerListParameters parameters, out ResultResponse resultResponse) + { + return _customerManager.GetCustomersList(parameters, out resultResponse); + } + + /// + /// Get customers list (paginated) + /// + /// Query parameters + /// Customers list + public Task> GetCustomersListAsync(CustomerListParameters parameters) + { + return _customerManager.GetCustomersListAsync(parameters); + } + + /// + /// Get customer by ID + /// + /// Parameters containing CustomerId + /// Failure response + /// Customer resource + public CustomerShowResource GetCustomerById(CustomerIdParameters parameters, out ResultResponse resultResponse) + { + return _customerManager.GetCustomerById(parameters, out resultResponse); + } + + /// + /// Get customer by ID (async) + /// + /// Parameters containing CustomerId + /// Customer resource + public Task> GetCustomerByIdAsync(CustomerIdParameters parameters) + { + return _customerManager.GetCustomerByIdAsync(parameters); + } + + /// + /// Create a new customer + /// + /// Request body + /// Failure response + /// Created customer + public CustomerResource CreateCustomer(StoreRequest request, out ResultResponse resultResponse) + { + return _customerManager.CreateCustomer(request, out resultResponse); + } + + /// + /// Create a new customer (async) + /// + /// Request body + /// Created customer + public Task> CreateCustomerAsync(StoreRequest request) + { + return _customerManager.CreateCustomerAsync(request); + } + + /// + /// Update existing customer. + /// + /// Update customer parameters. + /// Failure response. + /// Updated customer. + public CustomerResource UpdateCustomer(UpdateCustomerParameters parameters, out ResultResponse resultResponse) + { + return _customerManager.UpdateCustomer(parameters, out resultResponse); + } + + /// + /// Update existing customer (async). + /// + /// Update customer parameters. + /// Updated customer. + public Task> UpdateCustomerAsync(UpdateCustomerParameters parameters) + { + return _customerManager.UpdateCustomerAsync(parameters); + } + + + /// + /// Delete a customer + /// + /// Parameters containing CustomerId + /// Failure response + /// Result status + public ResultResponse DeleteCustomer(CustomerIdParameters parameters, out ResultResponse resultResponse) + { + return _customerManager.DeleteCustomer(parameters, out resultResponse); + } + + /// + /// Delete a customer (async) + /// + /// Parameters containing CustomerId + /// Result status + public Task> DeleteCustomerAsync(CustomerIdParameters parameters) + { + return _customerManager.DeleteCustomerAsync(parameters); + } + + #endregion + + #region Facilities + + /// + /// Access to Facility Management API + /// + public FacilityManagerV5 FacilityManager => _facilityManager; + + #endregion } } \ No newline at end of file diff --git a/route4me-csharp-sdk/Route4MeSDKTest/Examples/API5/Customers/CreateCustomerV5.cs b/route4me-csharp-sdk/Route4MeSDKTest/Examples/API5/Customers/CreateCustomerV5.cs new file mode 100644 index 00000000..2927f564 --- /dev/null +++ b/route4me-csharp-sdk/Route4MeSDKTest/Examples/API5/Customers/CreateCustomerV5.cs @@ -0,0 +1,27 @@ +using Route4MeSDKLibrary.DataTypes.V5.Customers; +using System; + +namespace Route4MeSDK.Examples +{ + public sealed partial class Route4MeExamples + { + /// + /// The example refers to the process of creating a new customer. + /// + public void CreateCustomerV5() + { + var route4Me = new Route4MeManagerV5(ActualApiKey); + + var request = new StoreRequest + { + Name = "Test Customer " + DateTime.Now.ToString("yyyyMMddHHmmss"), + ExternalId = Guid.NewGuid().ToString("N"), + Status = 1 + }; + + var customer = route4Me.CreateCustomer(request, out var resultResponse); + + PrintTestCustomer(customer, resultResponse); + } + } +} diff --git a/route4me-csharp-sdk/Route4MeSDKTest/Examples/API5/Customers/CreateCustomerV5Async.cs b/route4me-csharp-sdk/Route4MeSDKTest/Examples/API5/Customers/CreateCustomerV5Async.cs new file mode 100644 index 00000000..dbed4ff1 --- /dev/null +++ b/route4me-csharp-sdk/Route4MeSDKTest/Examples/API5/Customers/CreateCustomerV5Async.cs @@ -0,0 +1,30 @@ +using Route4MeSDKLibrary.DataTypes.V5.Customers; +using System; +using System.Threading.Tasks; + +namespace Route4MeSDK.Examples +{ + public sealed partial class Route4MeExamples + { + /// + /// The example refers to the asynchronous process of creating a new customer. + /// asynchronously using the API 5 endpoint. + /// + public async Task CreateCustomerAsyncV5() + { + var route4Me = new Route4MeManagerV5(ActualApiKey); + + var request = new StoreRequest + { + Name = "Async Test Customer " + DateTime.Now.ToString("yyyyMMddHHmmss"), + ExternalId = Guid.NewGuid().ToString("N"), + Status = 1, + Currency = "USD" + }; + + var (result, resultResponse) = await route4Me.CreateCustomerAsync(request); + + PrintTestCustomer(result, resultResponse); + } + } +} diff --git a/route4me-csharp-sdk/Route4MeSDKTest/Examples/API5/Customers/DeleteCustomerV5.cs b/route4me-csharp-sdk/Route4MeSDKTest/Examples/API5/Customers/DeleteCustomerV5.cs new file mode 100644 index 00000000..bd77765e --- /dev/null +++ b/route4me-csharp-sdk/Route4MeSDKTest/Examples/API5/Customers/DeleteCustomerV5.cs @@ -0,0 +1,33 @@ +using Route4MeSDKLibrary.QueryTypes.V5.Customers; +using System; + +namespace Route4MeSDK.Examples +{ + public sealed partial class Route4MeExamples + { + /// + /// The example refers to the process of deleting an existing customer. + /// + public void DeleteCustomerV5() + { + var route4Me = new Route4MeManagerV5(ActualApiKey); + + var parameters = new CustomerIdParameters() + { + CustomerId = "3b41b5b60a1d47ff8ffbc09b7de88ef1" // replace with actual ID + }; + + var result = route4Me.DeleteCustomer(parameters, out var resultResponse); + + if (result != null && (result.Status == true || result.Code == 200)) + { + Console.WriteLine($"Customer {parameters.CustomerId} deleted successfully."); + } + else + { + Console.WriteLine($"Failed to delete customer {parameters.CustomerId}."); + PrintTestCustomer(result, resultResponse); + } + } + } +} diff --git a/route4me-csharp-sdk/Route4MeSDKTest/Examples/API5/Customers/DeleteCustomerV5Async.cs b/route4me-csharp-sdk/Route4MeSDKTest/Examples/API5/Customers/DeleteCustomerV5Async.cs new file mode 100644 index 00000000..187d383f --- /dev/null +++ b/route4me-csharp-sdk/Route4MeSDKTest/Examples/API5/Customers/DeleteCustomerV5Async.cs @@ -0,0 +1,37 @@ +using Route4MeSDKLibrary.QueryTypes.V5.Customers; +using System; + +namespace Route4MeSDK.Examples +{ + public sealed partial class Route4MeExamples + { + /// + /// The example refers to the process of deleting an existing customer asynchronously. + /// asynchronously using the API 5 endpoint. + /// + public async void DeleteCustomerAsyncV5() + { + var route4Me = new Route4MeManagerV5(ActualApiKey); + + var parameters = new CustomerIdParameters() + { + CustomerId = "3b41b5b60a1d47ff8ffbc09b7de88ef1" // replace with actual ID + }; + + var tupleResult = await route4Me.DeleteCustomerAsync(parameters); + + var result = tupleResult.Item1; + var resultResponse = tupleResult.Item2; + + if (result != null && (result.Status == true || result.Code == 200)) + { + Console.WriteLine($"Customer {parameters.CustomerId} deleted successfully (async)."); + } + else + { + Console.WriteLine($"Failed to delete customer {parameters.CustomerId} (async)."); + PrintTestCustomer(result, resultResponse); + } + } + } +} diff --git a/route4me-csharp-sdk/Route4MeSDKTest/Examples/API5/Customers/GetCustomerByIdV5.cs b/route4me-csharp-sdk/Route4MeSDKTest/Examples/API5/Customers/GetCustomerByIdV5.cs new file mode 100644 index 00000000..3f73c9f1 --- /dev/null +++ b/route4me-csharp-sdk/Route4MeSDKTest/Examples/API5/Customers/GetCustomerByIdV5.cs @@ -0,0 +1,24 @@ +using Route4MeSDKLibrary.QueryTypes.V5.Customers; + +namespace Route4MeSDK.Examples +{ + public sealed partial class Route4MeExamples + { + /// + /// The example refers to the process of getting a single customer by ID. + /// + public void GetCustomerByIdV5() + { + var route4Me = new Route4MeManagerV5(ActualApiKey); + + var parameters = new CustomerIdParameters() + { + CustomerId = "3b41b5b60a1d47ff8ffbc09b7de88ef1" + }; + + var result = route4Me.GetCustomerById(parameters, out var resultResponse); + + PrintTestCustomer(result, resultResponse); + } + } +} diff --git a/route4me-csharp-sdk/Route4MeSDKTest/Examples/API5/Customers/GetCustomerByIdV5Async.cs b/route4me-csharp-sdk/Route4MeSDKTest/Examples/API5/Customers/GetCustomerByIdV5Async.cs new file mode 100644 index 00000000..a56a0fc7 --- /dev/null +++ b/route4me-csharp-sdk/Route4MeSDKTest/Examples/API5/Customers/GetCustomerByIdV5Async.cs @@ -0,0 +1,26 @@ +using Route4MeSDKLibrary.QueryTypes.V5.Customers; +using System; + +namespace Route4MeSDK.Examples +{ + public sealed partial class Route4MeExamples + { + /// + /// The example refers to the process of getting a single customer by ID. + /// asynchronously using the API 5 endpoint. + /// + public async void GetCustomerByIdV5Async() + { + var route4Me = new Route4MeManagerV5(ActualApiKey); + + var parameters = new CustomerIdParameters() + { + CustomerId = "3b41b5b60a1d47ff8ffbc09b7de88ef1" + }; + + var (result, resultResponse) = await route4Me.GetCustomerByIdAsync(parameters); + + PrintTestCustomer(result, resultResponse); + } + } +} diff --git a/route4me-csharp-sdk/Route4MeSDKTest/Examples/API5/Customers/GetCustomersListV5.cs b/route4me-csharp-sdk/Route4MeSDKTest/Examples/API5/Customers/GetCustomersListV5.cs new file mode 100644 index 00000000..26aebc09 --- /dev/null +++ b/route4me-csharp-sdk/Route4MeSDKTest/Examples/API5/Customers/GetCustomersListV5.cs @@ -0,0 +1,25 @@ +using Route4MeSDKLibrary.QueryTypes.V5.Customers; + +namespace Route4MeSDK.Examples +{ + public sealed partial class Route4MeExamples + { + /// + /// The example refers to the process of getting a paginated list of customers. + /// + public void GetCustomersListV5() + { + var route4Me = new Route4MeManagerV5(ActualApiKey); + + var parameters = new CustomerListParameters() + { + Page = 1, + PerPage = 20 + }; + + var result = route4Me.GetCustomersList(parameters, out var resultResponse); + + PrintTestCustomer(result, resultResponse); + } + } +} \ No newline at end of file diff --git a/route4me-csharp-sdk/Route4MeSDKTest/Examples/API5/Customers/GetCustomersListV5Async.cs b/route4me-csharp-sdk/Route4MeSDKTest/Examples/API5/Customers/GetCustomersListV5Async.cs new file mode 100644 index 00000000..ed5aad93 --- /dev/null +++ b/route4me-csharp-sdk/Route4MeSDKTest/Examples/API5/Customers/GetCustomersListV5Async.cs @@ -0,0 +1,27 @@ +using Route4MeSDKLibrary.QueryTypes.V5.Customers; +using System; + +namespace Route4MeSDK.Examples +{ + public sealed partial class Route4MeExamples + { + /// + /// The example refers to the process of getting a paginated list of customers asynchronously. + /// asynchronously using the API 5 endpoint. + /// + public async void GetCustomersListAsyncV5() + { + var route4Me = new Route4MeManagerV5(ActualApiKey); + + var parameters = new CustomerListParameters() + { + Page = 1, + PerPage = 20 + }; + + var (result, resultResponse) = await route4Me.GetCustomersListAsync(parameters); + + PrintTestCustomer(result, resultResponse); + } + } +} diff --git a/route4me-csharp-sdk/Route4MeSDKTest/Examples/API5/Customers/UpdateCustomerV5.cs b/route4me-csharp-sdk/Route4MeSDKTest/Examples/API5/Customers/UpdateCustomerV5.cs new file mode 100644 index 00000000..c275d453 --- /dev/null +++ b/route4me-csharp-sdk/Route4MeSDKTest/Examples/API5/Customers/UpdateCustomerV5.cs @@ -0,0 +1,28 @@ +using Route4MeSDKLibrary.QueryTypes.V5.Customers; +using System; + +namespace Route4MeSDK.Examples +{ + public sealed partial class Route4MeExamples + { + /// + /// The example refers to the process of updating an existing customer. + /// + public void UpdateCustomerV5() + { + var route4Me = new Route4MeManagerV5(ActualApiKey); + + var parameters = new UpdateCustomerParameters + { + CustomerId = "3b41b5b60a1d47ff8ffbc09b7de88ef1", // replace with actual ID + Name = "Updated Test Customer " + DateTime.Now.ToString("HHmmss"), + ExternalId = "ext-" + DateTime.Now.ToString("yyMMdd"), + Status = 1 + }; + + var result = route4Me.UpdateCustomer(parameters, out var resultResponse); + + PrintTestCustomer(result, resultResponse); + } + } +} diff --git a/route4me-csharp-sdk/Route4MeSDKTest/Examples/API5/Customers/UpdateCustomerV5Async.cs b/route4me-csharp-sdk/Route4MeSDKTest/Examples/API5/Customers/UpdateCustomerV5Async.cs new file mode 100644 index 00000000..0ce547cf --- /dev/null +++ b/route4me-csharp-sdk/Route4MeSDKTest/Examples/API5/Customers/UpdateCustomerV5Async.cs @@ -0,0 +1,30 @@ +using Route4MeSDKLibrary.QueryTypes.V5.Customers; +using System; +using System.Threading.Tasks; + +namespace Route4MeSDK.Examples +{ + public sealed partial class Route4MeExamples + { + /// + /// The example refers to the asynchronous process of updating an existing customer. + /// asynchronously using the API 5 endpoint. + /// + public async Task UpdateCustomerAsyncV5() + { + var route4Me = new Route4MeManagerV5(ActualApiKey); + + var parameters = new UpdateCustomerParameters + { + CustomerId = "3b41b5b60a1d47ff8ffbc09b7de88ef1", // replace with actual ID + Name = "Async Updated Test Customer " + DateTime.Now.ToString("HHmmss"), + ExternalId = "ext-" + DateTime.Now.ToString("yyMMdd"), + Status = 1 + }; + + var (result, resultResponse) = await route4Me.UpdateCustomerAsync(parameters); + + PrintTestCustomer(result, resultResponse); + } + } +} diff --git a/route4me-csharp-sdk/Route4MeSDKTest/Examples/API5/Facilities/CreateFacility.cs b/route4me-csharp-sdk/Route4MeSDKTest/Examples/API5/Facilities/CreateFacility.cs new file mode 100644 index 00000000..91df4dcc --- /dev/null +++ b/route4me-csharp-sdk/Route4MeSDKTest/Examples/API5/Facilities/CreateFacility.cs @@ -0,0 +1,120 @@ +using System; +using Route4MeSDK.DataTypes.V5; +using Route4MeSDKLibrary.DataTypes.V5.Facilities; +using Route4MeSDKLibrary.QueryTypes.V5.Facilities; + +namespace Route4MeSDK.Examples +{ + public sealed partial class Route4MeExamples + { + /// + /// Example demonstrates how to create a new facility using the API V5 endpoint. + /// Facilities represent physical locations like warehouses, distribution centers, or depots. + /// + public void CreateFacility() + { + var route4Me = new Route4MeManagerV5(ActualApiKey); + + Console.WriteLine("Creating a new facility...\n"); + + var facilityRequest = new FacilityCreateRequest + { + FacilityAlias = "Test Facility " + Guid.NewGuid().ToString().Substring(0, 8), + Address = "123 Industrial Blvd, Chicago, IL 60601", + Coordinates = new FacilityCoordinates + { + Lat = 41.8781, + Lng = -87.6298 + }, + FacilityTypes = new FacilityTypeAssignment[] + { + new FacilityTypeAssignment + { + FacilityTypeId = 1, + IsDefault = true + } + }, + Status = 1 + }; + + var createdFacility = route4Me.FacilityManager.CreateFacility( + facilityRequest, + out ResultResponse resultResponse + ); + + if (createdFacility != null && resultResponse == null) + { + Console.WriteLine("Facility created successfully!"); + Console.WriteLine($" Facility ID: {createdFacility.FacilityId}"); + Console.WriteLine($" Name: {createdFacility.FacilityAlias}"); + Console.WriteLine($" Address: {createdFacility.Address}"); + Console.WriteLine($" Status: {createdFacility.Status}"); + Console.WriteLine($" Coordinates: ({createdFacility.Coordinates?.Lat}, {createdFacility.Coordinates?.Lng})"); + Console.WriteLine($" Created at: {createdFacility.CreatedAt}"); + + if (createdFacility.FacilityTypes != null && createdFacility.FacilityTypes.Length > 0) + { + Console.WriteLine($" Facility Types: {createdFacility.FacilityTypes.Length}"); + foreach (var type in createdFacility.FacilityTypes) + { + Console.WriteLine($" - Type ID: {type.FacilityTypeId}, Default: {type.IsDefault}"); + } + } + + Console.WriteLine("\nCleaning up test facility..."); + if (facilitiesToRemove == null) + { + facilitiesToRemove = new System.Collections.Generic.List(); + } + facilitiesToRemove.Add(createdFacility.FacilityId); + RemoveTestFacilities(); + } + else + { + Console.WriteLine("✗ Failed to create facility"); + if (resultResponse != null && resultResponse.Messages != null) + { + foreach (var message in resultResponse.Messages) + { + Console.WriteLine($" Error: {message.Key} - {string.Join(", ", message.Value)}"); + } + } + } + } + + /// + /// Helper method to remove test facilities created during examples + /// + private void RemoveTestFacilities() + { + if (facilitiesToRemove == null || facilitiesToRemove.Count == 0) + return; + + var route4Me = new Route4MeManagerV5(ActualApiKey); + + foreach (var facilityId in facilitiesToRemove) + { + var result = route4Me.FacilityManager.DeleteFacility(facilityId, out ResultResponse response); + + if (result != null && response == null) + { + Console.WriteLine($"Deleted facility: {facilityId}"); + } + else + { + Console.WriteLine($"✗ Failed to delete facility: {facilityId}"); + if (response != null && response.Messages != null) + { + foreach (var msg in response.Messages) + { + Console.WriteLine($" Error: {msg.Key} - {string.Join(", ", msg.Value)}"); + } + } + } + } + + facilitiesToRemove.Clear(); + } + } +} + diff --git a/route4me-csharp-sdk/Route4MeSDKTest/Examples/API5/Facilities/DeleteFacility.cs b/route4me-csharp-sdk/Route4MeSDKTest/Examples/API5/Facilities/DeleteFacility.cs new file mode 100644 index 00000000..43d53f1c --- /dev/null +++ b/route4me-csharp-sdk/Route4MeSDKTest/Examples/API5/Facilities/DeleteFacility.cs @@ -0,0 +1,149 @@ +using System; +using Route4MeSDK.DataTypes.V5; +using Route4MeSDKLibrary.DataTypes.V5.Facilities; +using Route4MeSDKLibrary.QueryTypes.V5.Facilities; + +namespace Route4MeSDK.Examples +{ + public sealed partial class Route4MeExamples + { + /// + /// Example demonstrates how to delete a facility using the API V5 endpoint. + /// Shows the complete delete workflow including verification. + /// + public void DeleteFacility() + { + var route4Me = new Route4MeManagerV5(ActualApiKey); + + // First, create a test facility to delete + Console.WriteLine("Creating a test facility to delete..."); + var createRequest = new FacilityCreateRequest + { + FacilityAlias = "Facility to Delete " + Guid.NewGuid().ToString().Substring(0, 8), + Address = "789 Temporary Rd, Chicago, IL 60603", + Coordinates = new FacilityCoordinates + { + Lat = 41.8750, + Lng = -87.6250 + }, + FacilityTypes = new FacilityTypeAssignment[] + { + new FacilityTypeAssignment + { + FacilityTypeId = 1, + IsDefault = true + } + }, + Status = 1 + }; + + var createdFacility = route4Me.FacilityManager.CreateFacility( + createRequest, + out ResultResponse createResponse + ); + + if (createdFacility == null || createResponse != null) + { + Console.WriteLine("✗ Failed to create test facility"); + if (createResponse != null && createResponse.Messages != null) + { + foreach (var msg in createResponse.Messages) + { + Console.WriteLine($" Error: {msg.Key} - {string.Join(", ", msg.Value)}"); + } + } + return; + } + + Console.WriteLine($"Test facility created: {createdFacility.FacilityId}"); + Console.WriteLine($" Name: {createdFacility.FacilityAlias}"); + + // Get facility count before deletion + var beforeParameters = new FacilityGetParameters + { + Page = 1, + PerPage = 100 + }; + + var facilitiesBefore = route4Me.FacilityManager.GetFacilities( + beforeParameters, + out ResultResponse beforeResponse + ); + + int countBefore = facilitiesBefore?.Total ?? 0; + Console.WriteLine($"\nTotal facilities before deletion: {countBefore}"); + + // Now delete the facility + Console.WriteLine($"\nDeleting facility {createdFacility.FacilityId}..."); + var deleteResult = route4Me.FacilityManager.DeleteFacility( + createdFacility.FacilityId, + out ResultResponse deleteResponse + ); + + if (deleteResult != null && deleteResponse == null) + { + Console.WriteLine("Facility deleted successfully!"); + Console.WriteLine($" Remaining facilities returned: {deleteResult.Length}"); + } + else + { + Console.WriteLine("✗ Failed to delete facility"); + if (deleteResponse != null && deleteResponse.Messages != null) + { + foreach (var msg in deleteResponse.Messages) + { + Console.WriteLine($" Error: {msg.Key} - {string.Join(", ", msg.Value)}"); + } + } + return; + } + + // Verify deletion by trying to retrieve the facility + Console.WriteLine("\nVerifying deletion..."); + System.Threading.Thread.Sleep(500); // Brief pause for API sync + + var verifyFacility = route4Me.FacilityManager.GetFacility( + createdFacility.FacilityId, + out ResultResponse verifyResponse + ); + + if (verifyFacility == null) + { + Console.WriteLine("Deletion verified - facility no longer exists"); + } + else + { + Console.WriteLine("⚠ WARNING: Facility still exists after delete"); + Console.WriteLine($" Facility status: {verifyFacility.Status}"); + } + + // Check facility count after deletion + System.Threading.Thread.Sleep(1000); // Allow time for API to update + + var afterParameters = new FacilityGetParameters + { + Page = 1, + PerPage = 100 + }; + + var facilitiesAfter = route4Me.FacilityManager.GetFacilities( + afterParameters, + out ResultResponse afterResponse + ); + + int countAfter = facilitiesAfter?.Total ?? 0; + Console.WriteLine($"\nTotal facilities after deletion: {countAfter}"); + + if (countBefore > countAfter) + { + Console.WriteLine($"Facility count decreased by {countBefore - countAfter}"); + } + else + { + Console.WriteLine("⚠ Note: Facility count unchanged (API may have delay in reflecting deletions)"); + } + } + } +} + + diff --git a/route4me-csharp-sdk/Route4MeSDKTest/Examples/API5/Facilities/GetFacilities.cs b/route4me-csharp-sdk/Route4MeSDKTest/Examples/API5/Facilities/GetFacilities.cs new file mode 100644 index 00000000..468421d5 --- /dev/null +++ b/route4me-csharp-sdk/Route4MeSDKTest/Examples/API5/Facilities/GetFacilities.cs @@ -0,0 +1,118 @@ +using System; +using Route4MeSDK.DataTypes.V5; +using Route4MeSDKLibrary.QueryTypes.V5.Facilities; + +namespace Route4MeSDK.Examples +{ + public sealed partial class Route4MeExamples + { + /// + /// Example demonstrates how to retrieve a paginated list of facilities using the API V5 endpoint. + /// Shows various pagination and filtering options. + /// + public void GetFacilities() + { + var route4Me = new Route4MeManagerV5(ActualApiKey); + + Console.WriteLine("Retrieving all facilities (paginated)...\n"); + + // Example 1: Get first page with 10 items per page + var parameters = new FacilityGetParameters + { + Page = 1, + PerPage = 10 + }; + + var facilities = route4Me.FacilityManager.GetFacilities( + parameters, + out ResultResponse response + ); + + if (facilities != null && response == null) + { + Console.WriteLine("Facilities retrieved successfully!"); + Console.WriteLine($" Total facilities: {facilities.Total}"); + Console.WriteLine($" Current page: {facilities.CurrentPage}"); + Console.WriteLine($" Per page: {facilities.PerPage}"); + Console.WriteLine($" Total pages: {facilities.LastPage}"); + Console.WriteLine($" Facilities on this page: {facilities.Data?.Length ?? 0}\n"); + + if (facilities.Data != null && facilities.Data.Length > 0) + { + Console.WriteLine("Facilities:"); + for (int i = 0; i < facilities.Data.Length; i++) + { + var facility = facilities.Data[i]; + Console.WriteLine($" {i + 1}. {facility.FacilityAlias}"); + Console.WriteLine($" ID: {facility.FacilityId}"); + Console.WriteLine($" Address: {facility.Address}"); + Console.WriteLine($" Status: {facility.Status}"); + Console.WriteLine($" Coordinates: ({facility.Coordinates?.Lat}, {facility.Coordinates?.Lng})"); + + if (facility.FacilityTypes != null && facility.FacilityTypes.Length > 0) + { + Console.WriteLine($" Types: {string.Join(", ", Array.ConvertAll(facility.FacilityTypes, t => t.FacilityTypeId.ToString()))}"); + } + Console.WriteLine(); + } + } + else + { + Console.WriteLine("No facilities found."); + } + + // Example 2: If there are multiple pages, demonstrate getting the second page + if (facilities.LastPage > 1) + { + Console.WriteLine("\nRetrieving second page..."); + var page2Parameters = new FacilityGetParameters + { + Page = 2, + PerPage = 10 + }; + + var page2Facilities = route4Me.FacilityManager.GetFacilities( + page2Parameters, + out ResultResponse page2Response + ); + + if (page2Facilities != null && page2Response == null) + { + Console.WriteLine($"Page 2 retrieved with {page2Facilities.Data?.Length ?? 0} facilities"); + } + } + + // Example 3: Get all facilities with larger page size + Console.WriteLine("\nRetrieving facilities with larger page size..."); + var largePageParameters = new FacilityGetParameters + { + Page = 1, + PerPage = 100 + }; + + var largePage = route4Me.FacilityManager.GetFacilities( + largePageParameters, + out ResultResponse largePageResponse + ); + + if (largePage != null && largePageResponse == null) + { + Console.WriteLine($"Retrieved {largePage.Data?.Length ?? 0} facilities in one page"); + } + } + else + { + Console.WriteLine("✗ Failed to retrieve facilities"); + if (response != null && response.Messages != null) + { + foreach (var msg in response.Messages) + { + Console.WriteLine($" Error: {msg.Key} - {string.Join(", ", msg.Value)}"); + } + } + } + } + } +} + + diff --git a/route4me-csharp-sdk/Route4MeSDKTest/Examples/API5/Facilities/GetFacility.cs b/route4me-csharp-sdk/Route4MeSDKTest/Examples/API5/Facilities/GetFacility.cs new file mode 100644 index 00000000..653cf034 --- /dev/null +++ b/route4me-csharp-sdk/Route4MeSDKTest/Examples/API5/Facilities/GetFacility.cs @@ -0,0 +1,106 @@ +using System; +using Route4MeSDK.DataTypes.V5; + +namespace Route4MeSDK.Examples +{ + public sealed partial class Route4MeExamples + { + /// + /// Example demonstrates how to retrieve a single facility by ID using the API V5 endpoint. + /// + public void GetFacility() + { + var route4Me = new Route4MeManagerV5(ActualApiKey); + + // First, create a test facility to retrieve + Console.WriteLine("Creating a test facility..."); + var createRequest = new Route4MeSDKLibrary.QueryTypes.V5.Facilities.FacilityCreateRequest + { + FacilityAlias = "Test Facility for Get " + Guid.NewGuid().ToString().Substring(0, 8), + Address = "456 Main St, Springfield, IL 62701", + Coordinates = new Route4MeSDKLibrary.DataTypes.V5.Facilities.FacilityCoordinates + { + Lat = 39.7817, + Lng = -89.6501 + }, + FacilityTypes = new Route4MeSDKLibrary.QueryTypes.V5.Facilities.FacilityTypeAssignment[] + { + new Route4MeSDKLibrary.QueryTypes.V5.Facilities.FacilityTypeAssignment + { + FacilityTypeId = 1, + IsDefault = true + } + }, + Status = 1 + }; + + var createdFacility = route4Me.FacilityManager.CreateFacility( + createRequest, + out ResultResponse createResponse + ); + + if (createdFacility == null || createResponse != null) + { + Console.WriteLine("✗ Failed to create test facility"); + if (createResponse != null && createResponse.Messages != null) + { + foreach (var msg in createResponse.Messages) + { + Console.WriteLine($" Error: {msg.Key} - {string.Join(", ", msg.Value)}"); + } + } + return; + } + + Console.WriteLine($"Test facility created: {createdFacility.FacilityId}"); + + // Now retrieve the facility by ID + Console.WriteLine("\nRetrieving facility by ID..."); + var retrievedFacility = route4Me.FacilityManager.GetFacility( + createdFacility.FacilityId, + out ResultResponse getResponse + ); + + if (retrievedFacility != null && getResponse == null) + { + Console.WriteLine("Facility retrieved successfully!"); + Console.WriteLine($" Facility ID: {retrievedFacility.FacilityId}"); + Console.WriteLine($" Name: {retrievedFacility.FacilityAlias}"); + Console.WriteLine($" Address: {retrievedFacility.Address}"); + Console.WriteLine($" Status: {retrievedFacility.Status}"); + Console.WriteLine($" Coordinates: ({retrievedFacility.Coordinates?.Lat}, {retrievedFacility.Coordinates?.Lng})"); + + if (retrievedFacility.FacilityTypes != null && retrievedFacility.FacilityTypes.Length > 0) + { + Console.WriteLine($" Facility Types: {retrievedFacility.FacilityTypes.Length}"); + foreach (var type in retrievedFacility.FacilityTypes) + { + Console.WriteLine($" - Type ID: {type.FacilityTypeId}, Default: {type.IsDefault}"); + } + } + } + else + { + Console.WriteLine("✗ Failed to retrieve facility"); + if (getResponse != null && getResponse.Messages != null) + { + foreach (var msg in getResponse.Messages) + { + Console.WriteLine($" Error: {msg.Key} - {string.Join(", ", msg.Value)}"); + } + } + } + + // Clean up + Console.WriteLine("\nCleaning up test facility..."); + if (facilitiesToRemove == null) + { + facilitiesToRemove = new System.Collections.Generic.List(); + } + facilitiesToRemove.Add(createdFacility.FacilityId); + RemoveTestFacilities(); + } + } +} + + diff --git a/route4me-csharp-sdk/Route4MeSDKTest/Examples/API5/Facilities/GetFacilityTypes.cs b/route4me-csharp-sdk/Route4MeSDKTest/Examples/API5/Facilities/GetFacilityTypes.cs new file mode 100644 index 00000000..5c6a357e --- /dev/null +++ b/route4me-csharp-sdk/Route4MeSDKTest/Examples/API5/Facilities/GetFacilityTypes.cs @@ -0,0 +1,105 @@ +using System; +using Route4MeSDK.DataTypes.V5; + +namespace Route4MeSDK.Examples +{ + public sealed partial class Route4MeExamples + { + /// + /// Example demonstrates how to retrieve facility types using the API V5 endpoint. + /// Shows both getting all types and retrieving a specific type by ID. + /// + public void GetFacilityTypes() + { + var route4Me = new Route4MeManagerV5(ActualApiKey); + + Console.WriteLine("Retrieving all facility types...\n"); + + var facilityTypes = route4Me.FacilityManager.GetFacilityTypes( + out ResultResponse response + ); + + if (facilityTypes != null && response == null) + { + Console.WriteLine("Facility types retrieved successfully!"); + Console.WriteLine($" Total types available: {facilityTypes.Data?.Length ?? 0}\n"); + + if (facilityTypes.Data != null && facilityTypes.Data.Length > 0) + { + Console.WriteLine("Available facility types:"); + for (int i = 0; i < facilityTypes.Data.Length; i++) + { + var type = facilityTypes.Data[i]; + Console.WriteLine($" {i + 1}. ID: {type.FacilityTypeId}"); + Console.WriteLine($" Name: {type.FacilityTypeAlias}"); + Console.WriteLine(); + } + + if (facilityTypes.Data.Length > 0) + { + int firstTypeId = facilityTypes.Data[0].FacilityTypeId; + Console.WriteLine($"Retrieving specific facility type (ID: {firstTypeId})...\n"); + + var singleType = route4Me.FacilityManager.GetFacilityType( + firstTypeId, + out ResultResponse singleTypeResponse + ); + + if (singleType != null && singleTypeResponse == null) + { + Console.WriteLine("Single facility type retrieved successfully!"); + Console.WriteLine($" ID: {singleType.FacilityTypeId}"); + Console.WriteLine($" Name: {singleType.FacilityTypeAlias}"); + } + else + { + Console.WriteLine($"✗ Failed to retrieve facility type by ID {firstTypeId}"); + if (singleTypeResponse != null && singleTypeResponse.Messages != null) + { + foreach (var msg in singleTypeResponse.Messages) + { + Console.WriteLine($" Error: {msg.Key} - {string.Join(", ", msg.Value)}"); + } + } + } + } + + Console.WriteLine("\n\nDemonstrating error handling with invalid facility type ID..."); + var invalidType = route4Me.FacilityManager.GetFacilityType( + 0, + out ResultResponse invalidResponse + ); + + if (invalidType == null && invalidResponse != null) + { + Console.WriteLine("Error handling working correctly!"); + Console.WriteLine($" Status: {invalidResponse.Status}"); + if (invalidResponse.Messages != null) + { + foreach (var msg in invalidResponse.Messages) + { + Console.WriteLine($" Error: {msg.Key} - {string.Join(", ", msg.Value)}"); + } + } + } + } + else + { + Console.WriteLine("No facility types found in the system."); + } + } + else + { + Console.WriteLine("✗ Failed to retrieve facility types"); + if (response != null && response.Messages != null) + { + foreach (var msg in response.Messages) + { + Console.WriteLine($" Error: {msg.Key} - {string.Join(", ", msg.Value)}"); + } + } + } + } + } +} + diff --git a/route4me-csharp-sdk/Route4MeSDKTest/Examples/API5/Facilities/UpdateFacility.cs b/route4me-csharp-sdk/Route4MeSDKTest/Examples/API5/Facilities/UpdateFacility.cs new file mode 100644 index 00000000..9edbf14e --- /dev/null +++ b/route4me-csharp-sdk/Route4MeSDKTest/Examples/API5/Facilities/UpdateFacility.cs @@ -0,0 +1,137 @@ +using System; +using Route4MeSDK.DataTypes.V5; +using Route4MeSDKLibrary.DataTypes.V5.Facilities; +using Route4MeSDKLibrary.QueryTypes.V5.Facilities; + +namespace Route4MeSDK.Examples +{ + public sealed partial class Route4MeExamples + { + /// + /// Example demonstrates how to update an existing facility using the API V5 endpoint. + /// Shows updating various facility attributes including name, address, status, and contact information. + /// + public void UpdateFacility() + { + var route4Me = new Route4MeManagerV5(ActualApiKey); + + // First, create a test facility to update + Console.WriteLine("Creating a test facility..."); + var createRequest = new FacilityCreateRequest + { + FacilityAlias = "Original Facility " + Guid.NewGuid().ToString().Substring(0, 8), + Address = "123 Original St, Chicago, IL 60601", + Coordinates = new FacilityCoordinates + { + Lat = 41.8781, + Lng = -87.6298 + }, + FacilityTypes = new FacilityTypeAssignment[] + { + new FacilityTypeAssignment + { + FacilityTypeId = 1, + IsDefault = true + } + }, + Status = 1 + }; + + var createdFacility = route4Me.FacilityManager.CreateFacility( + createRequest, + out ResultResponse createResponse + ); + + if (createdFacility == null || createResponse != null) + { + Console.WriteLine("✗ Failed to create test facility"); + if (createResponse != null && createResponse.Messages != null) + { + foreach (var msg in createResponse.Messages) + { + Console.WriteLine($" Error: {msg.Key} - {string.Join(", ", msg.Value)}"); + } + } + return; + } + + Console.WriteLine($"Test facility created: {createdFacility.FacilityId}"); + Console.WriteLine($" Original name: {createdFacility.FacilityAlias}"); + Console.WriteLine($" Original status: {createdFacility.Status}"); + + // Now update the facility + Console.WriteLine("\nUpdating facility..."); + var updateRequest = new FacilityUpdateRequest + { + FacilityAlias = "Updated Facility " + Guid.NewGuid().ToString().Substring(0, 8), + Address = "456 Updated Ave, Chicago, IL 60602", + Coordinates = new FacilityCoordinates + { + Lat = 41.8800, + Lng = -87.6300 + }, + FacilityTypes = new FacilityTypeAssignment[] + { + new FacilityTypeAssignment + { + FacilityTypeId = 1, + IsDefault = true + } + }, + Status = 2 + }; + + var updatedFacility = route4Me.FacilityManager.UpdateFacility( + createdFacility.FacilityId, + updateRequest, + out ResultResponse updateResponse + ); + + if (updatedFacility != null && updateResponse == null) + { + Console.WriteLine("Facility updated successfully!"); + Console.WriteLine($" Facility ID: {updatedFacility.FacilityId}"); + Console.WriteLine($" New name: {updatedFacility.FacilityAlias}"); + Console.WriteLine($" New address: {updatedFacility.Address}"); + Console.WriteLine($" New status: {updatedFacility.Status}"); + Console.WriteLine($" New coordinates: ({updatedFacility.Coordinates?.Lat}, {updatedFacility.Coordinates?.Lng})"); + Console.WriteLine($" Updated at: {updatedFacility.UpdatedAt}"); + } + else + { + Console.WriteLine("✗ Failed to update facility"); + if (updateResponse != null && updateResponse.Messages != null) + { + foreach (var msg in updateResponse.Messages) + { + Console.WriteLine($" Error: {msg.Key} - {string.Join(", ", msg.Value)}"); + } + } + } + + // Verify the update by retrieving the facility + Console.WriteLine("\nVerifying update..."); + var verifyFacility = route4Me.FacilityManager.GetFacility( + createdFacility.FacilityId, + out ResultResponse verifyResponse + ); + + if (verifyFacility != null) + { + Console.WriteLine($"Update verified!"); + Console.WriteLine($" Confirmed name: {verifyFacility.FacilityAlias}"); + Console.WriteLine($" Confirmed status: {verifyFacility.Status}"); + } + + // Clean up + Console.WriteLine("\nCleaning up test facility..."); + if (facilitiesToRemove == null) + { + facilitiesToRemove = new System.Collections.Generic.List(); + } + facilitiesToRemove.Add(createdFacility.FacilityId); + RemoveTestFacilities(); + } + } +} + diff --git a/route4me-csharp-sdk/Route4MeSDKTest/Examples/API5/Notes/BulkCreateNotes.cs b/route4me-csharp-sdk/Route4MeSDKTest/Examples/API5/Notes/BulkCreateNotes.cs new file mode 100644 index 00000000..53d119d4 --- /dev/null +++ b/route4me-csharp-sdk/Route4MeSDKTest/Examples/API5/Notes/BulkCreateNotes.cs @@ -0,0 +1,94 @@ +using System; +using Route4MeSDK.DataTypes.V5; +using Route4MeSDK.QueryTypes.V5.Notes; +using Route4MeSDKLibrary.Managers; + +namespace Route4MeSDK.Examples +{ + public sealed partial class Route4MeExamples + { + /// + /// Bulk create notes for multiple routes or destinations + /// + public void BulkCreateNotes() + { + // Create the manager with the API key + var notesManager = new NotesManagerV5(ActualApiKey); + + // First, create a test route to add notes to + RunSingleDriverRoundTrip(); + + if (SDRT_route == null || SDRT_route.Addresses == null || SDRT_route.Addresses.Length < 2) + { + Console.WriteLine("Failed to create test route"); + return; + } + + var routeId = SDRT_route_id; + var addressId1 = SDRT_route.Addresses[0].RouteDestinationId; + var addressId2 = SDRT_route.Addresses[1].RouteDestinationId; + + // Create bulk notes request + var bulkRequest = new NoteStoreBulkRequest + { + Notes = new[] + { + new NoteStoreBulkItem + { + RouteId = routeId, + StrNoteContents = "First bulk note created at " + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), + AddressId = addressId1, + DeviceType = "web", + StrUpdateType = "dropoff", + DevLat = 40.7636197, + DevLng = -73.9744388 + }, + new NoteStoreBulkItem + { + RouteId = routeId, + StrNoteContents = "Second bulk note created at " + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), + AddressId = addressId2, + DeviceType = "web", + StrUpdateType = "pickup" + }, + new NoteStoreBulkItem + { + RouteId = routeId, + StrNoteContents = "Third bulk note - general route note", + DeviceType = "api" + } + }, + DeviceType = "web" + }; + + // Execute bulk create + var result = notesManager.BulkCreateNotes(bulkRequest, out ResultResponse resultResponse); + + // Output the result + Console.WriteLine(""); + + if (result != null && result.Status) + { + Console.WriteLine("BulkCreateNotes executed successfully"); + Console.WriteLine("Status: {0}", result.Status); + Console.WriteLine("Async: {0}", result.Async); + Console.WriteLine(""); + Console.WriteLine("Note: Bulk creation is asynchronous. Notes will be created in the background."); + } + else + { + Console.WriteLine("BulkCreateNotes error"); + if (resultResponse != null && resultResponse.Messages != null) + { + foreach (var message in resultResponse.Messages) + { + Console.WriteLine("{0}: {1}", message.Key, string.Join(", ", message.Value)); + } + } + } + + // Clean up test route + RemoveTestOptimizations(); + } + } +} diff --git a/route4me-csharp-sdk/Route4MeSDKTest/Examples/API5/Notes/BulkCreateNotesAsync.cs b/route4me-csharp-sdk/Route4MeSDKTest/Examples/API5/Notes/BulkCreateNotesAsync.cs new file mode 100644 index 00000000..e5ed5ef7 --- /dev/null +++ b/route4me-csharp-sdk/Route4MeSDKTest/Examples/API5/Notes/BulkCreateNotesAsync.cs @@ -0,0 +1,97 @@ +using System; +using System.Threading.Tasks; +using Route4MeSDK.QueryTypes.V5.Notes; +using Route4MeSDKLibrary.Managers; + +namespace Route4MeSDK.Examples +{ + public sealed partial class Route4MeExamples + { + /// + /// Bulk create notes asynchronously for multiple routes or destinations + /// + public async void BulkCreateNotesAsync() + { + // Create the manager with the API key + var notesManager = new NotesManagerV5(ActualApiKey); + + // First, create a test route to add notes to + RunSingleDriverRoundTrip(); + + if (SDRT_route == null || SDRT_route.Addresses == null || SDRT_route.Addresses.Length < 2) + { + Console.WriteLine("Failed to create test route"); + return; + } + + var routeId = SDRT_route_id; + var addressId1 = SDRT_route.Addresses[0].RouteDestinationId; + var addressId2 = SDRT_route.Addresses[1].RouteDestinationId; + + // Create bulk notes request + var bulkRequest = new NoteStoreBulkRequest + { + Notes = new[] + { + new NoteStoreBulkItem + { + RouteId = routeId, + StrNoteContents = "Async bulk note 1 created at " + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), + AddressId = addressId1, + DeviceType = "web", + StrUpdateType = "dropoff", + DevLat = 40.7636197, + DevLng = -73.9744388, + RemoteSpeed = 55.0, + RemoteAltitude = 150.0 + }, + new NoteStoreBulkItem + { + RouteId = routeId, + StrNoteContents = "Async bulk note 2 created at " + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), + AddressId = addressId2, + DeviceType = "iphone", + StrUpdateType = "pickup", + UtcTime = (int)DateTimeOffset.UtcNow.ToUnixTimeSeconds() + }, + new NoteStoreBulkItem + { + RouteId = routeId, + StrNoteContents = "Async bulk note 3 - route-level note", + DeviceType = "android_tablet" + } + }, + DeviceType = "api" + }; + + // Execute bulk create asynchronously + var result = await notesManager.BulkCreateNotesAsync(bulkRequest); + + // Output the result + Console.WriteLine(""); + + if (result.Item1 != null && result.Item1.Status) + { + Console.WriteLine("BulkCreateNotesAsync executed successfully"); + Console.WriteLine("Status: {0}", result.Item1.Status); + Console.WriteLine("Async: {0}", result.Item1.Async); + Console.WriteLine(""); + Console.WriteLine("Note: Bulk creation is asynchronous. Notes will be created in the background."); + } + else + { + Console.WriteLine("BulkCreateNotesAsync error"); + if (result.Item2 != null && result.Item2.Messages != null) + { + foreach (var message in result.Item2.Messages) + { + Console.WriteLine("{0}: {1}", message.Key, string.Join(", ", message.Value)); + } + } + } + + // Clean up test route + RemoveTestOptimizations(); + } + } +} diff --git a/route4me-csharp-sdk/Route4MeSDKTest/Examples/API5/Notes/CreateNote.cs b/route4me-csharp-sdk/Route4MeSDKTest/Examples/API5/Notes/CreateNote.cs new file mode 100644 index 00000000..7f9fd432 --- /dev/null +++ b/route4me-csharp-sdk/Route4MeSDKTest/Examples/API5/Notes/CreateNote.cs @@ -0,0 +1,46 @@ +using System; +using Route4MeSDK.DataTypes.V5.Notes; +using Route4MeSDK.QueryTypes.V5.Notes; +using Route4MeSDKLibrary.Managers; + +namespace Route4MeSDK.Examples +{ + public sealed partial class Route4MeExamples + { + /// + /// The example refers to the process of creating a note using the API 5 endpoint. + /// + public void CreateNote() + { + var notesManager = new NotesManagerV5(ActualApiKey); + + // Ensure we have a test route + if (!RunOptimizationSingleDriverRoute10StopsV5()) + { + Console.WriteLine("Cannot create test route for note creation."); + return; + } + + var request = new NoteStoreRequest + { + RouteId = SD10Stops_route_id_V5, + StrNoteContents = "Test note created at " + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), + AddressId = (long)SD10Stops_route_V5.Addresses[1].RouteDestinationId, + DeviceType = "web" + }; + + var result = notesManager.CreateNote(request, out var resultResponse); + + if (result != null) + { + Console.WriteLine("CreateNote executed successfully"); + Console.WriteLine("Note ID: {0}", result.NoteId); + Console.WriteLine("Contents: {0}", result.Contents); + } + else + { + PrintFailResponse(resultResponse, "CreateNote"); + } + } + } +} diff --git a/route4me-csharp-sdk/Route4MeSDKTest/Examples/API5/Notes/CreateNoteAsync.cs b/route4me-csharp-sdk/Route4MeSDKTest/Examples/API5/Notes/CreateNoteAsync.cs new file mode 100644 index 00000000..8bb581d4 --- /dev/null +++ b/route4me-csharp-sdk/Route4MeSDKTest/Examples/API5/Notes/CreateNoteAsync.cs @@ -0,0 +1,46 @@ +using System; +using Route4MeSDK.DataTypes.V5.Notes; +using Route4MeSDK.QueryTypes.V5.Notes; +using Route4MeSDKLibrary.Managers; + +namespace Route4MeSDK.Examples +{ + public sealed partial class Route4MeExamples + { + /// + /// The example refers to the process of creating a note asynchronously using the API 5 endpoint. + /// + public async void CreateNoteAsync() + { + var notesManager = new NotesManagerV5(ActualApiKey); + + // Ensure we have a test route + if (!RunOptimizationSingleDriverRoute10StopsV5()) + { + Console.WriteLine("Cannot create test route for note creation."); + return; + } + + var request = new NoteStoreRequest + { + RouteId = SD10Stops_route_id_V5, + StrNoteContents = "Async test note created at " + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), + AddressId = (long)SD10Stops_route_V5.Addresses[1].RouteDestinationId, + DeviceType = "web" + }; + + var result = await notesManager.CreateNoteAsync(request); + + if (result.Item1 != null) + { + Console.WriteLine("CreateNoteAsync executed successfully"); + Console.WriteLine("Note ID: {0}", result.Item1.NoteId); + Console.WriteLine("Contents: {0}", result.Item1.Contents); + } + else + { + PrintFailResponse(result.Item2, "CreateNoteAsync"); + } + } + } +} diff --git a/route4me-csharp-sdk/Route4MeSDKTest/Examples/API5/Notes/CreateNoteCustomType.cs b/route4me-csharp-sdk/Route4MeSDKTest/Examples/API5/Notes/CreateNoteCustomType.cs new file mode 100644 index 00000000..254152cd --- /dev/null +++ b/route4me-csharp-sdk/Route4MeSDKTest/Examples/API5/Notes/CreateNoteCustomType.cs @@ -0,0 +1,48 @@ +using System; +using Route4MeSDK.QueryTypes.V5.Notes; +using Route4MeSDKLibrary.Managers; + +namespace Route4MeSDK.Examples +{ + public sealed partial class Route4MeExamples + { + /// + /// The example refers to the process of creating a custom note type using the API 5 endpoint. + /// + public void CreateNoteCustomType() + { + var notesManager = new NotesManagerV5(ActualApiKey); + + var request = new NoteCustomTypeStoreRequest + { + NoteCustomType = "Delivery Status " + DateTime.Now.Ticks, + NoteCustomTypeValues = new[] { "Delivered", "Refused", "Rescheduled" }, + NoteCustomFieldType = 1 // Valid values: 1, 2, 3, or 4 + }; + + var result = notesManager.CreateNoteCustomType(request, out var resultResponse); + + if (result != null) + { + Console.WriteLine("CreateNoteCustomType executed successfully"); + Console.WriteLine("Status: {0}", result.Status); + + if (result.Data != null && result.Data.Length > 0) + { + var customType = result.Data[0]; + Console.WriteLine("Custom Type ID: {0}", customType.NoteCustomTypeId); + Console.WriteLine("Custom Type Name: {0}", customType.NoteCustomType); + + if (customType.NoteCustomTypeValues != null) + { + Console.WriteLine("Values: {0}", string.Join(", ", customType.NoteCustomTypeValues)); + } + } + } + else + { + PrintFailResponse(resultResponse, "CreateNoteCustomType"); + } + } + } +} diff --git a/route4me-csharp-sdk/Route4MeSDKTest/Examples/API5/Notes/CreateNoteCustomTypeAsync.cs b/route4me-csharp-sdk/Route4MeSDKTest/Examples/API5/Notes/CreateNoteCustomTypeAsync.cs new file mode 100644 index 00000000..3c4a3b3c --- /dev/null +++ b/route4me-csharp-sdk/Route4MeSDKTest/Examples/API5/Notes/CreateNoteCustomTypeAsync.cs @@ -0,0 +1,48 @@ +using System; +using Route4MeSDK.QueryTypes.V5.Notes; +using Route4MeSDKLibrary.Managers; + +namespace Route4MeSDK.Examples +{ + public sealed partial class Route4MeExamples + { + /// + /// The example refers to the process of creating a custom note type asynchronously using the API 5 endpoint. + /// + public async void CreateNoteCustomTypeAsync() + { + var notesManager = new NotesManagerV5(ActualApiKey); + + var request = new NoteCustomTypeStoreRequest + { + NoteCustomType = "Async Delivery Status " + DateTime.Now.Ticks, + NoteCustomTypeValues = new[] { "Delivered", "Refused", "Rescheduled" }, + NoteCustomFieldType = 1 // Valid values: 1, 2, 3, or 4 + }; + + var result = await notesManager.CreateNoteCustomTypeAsync(request); + + if (result.Item1 != null) + { + Console.WriteLine("CreateNoteCustomTypeAsync executed successfully"); + Console.WriteLine("Status: {0}", result.Item1.Status); + + if (result.Item1.Data != null && result.Item1.Data.Length > 0) + { + var customType = result.Item1.Data[0]; + Console.WriteLine("Custom Type ID: {0}", customType.NoteCustomTypeId); + Console.WriteLine("Custom Type Name: {0}", customType.NoteCustomType); + + if (customType.NoteCustomTypeValues != null) + { + Console.WriteLine("Values: {0}", string.Join(", ", customType.NoteCustomTypeValues)); + } + } + } + else + { + PrintFailResponse(result.Item2, "CreateNoteCustomTypeAsync"); + } + } + } +} diff --git a/route4me-csharp-sdk/Route4MeSDKTest/Examples/API5/Notes/DeleteNote.cs b/route4me-csharp-sdk/Route4MeSDKTest/Examples/API5/Notes/DeleteNote.cs new file mode 100644 index 00000000..a6c75456 --- /dev/null +++ b/route4me-csharp-sdk/Route4MeSDKTest/Examples/API5/Notes/DeleteNote.cs @@ -0,0 +1,55 @@ +using System; +using Route4MeSDK.DataTypes.V5.Notes; +using Route4MeSDK.QueryTypes.V5.Notes; +using Route4MeSDKLibrary.Managers; + +namespace Route4MeSDK.Examples +{ + public sealed partial class Route4MeExamples + { + /// + /// The example refers to the process of deleting a note using the API 5 endpoint. + /// + public void DeleteNote() + { + var notesManager = new NotesManagerV5(ActualApiKey); + + // First, create a note to delete + if (!RunOptimizationSingleDriverRoute10StopsV5()) + { + Console.WriteLine("Cannot create test route."); + return; + } + + var createRequest = new NoteStoreRequest + { + RouteId = SD10Stops_route_id_V5, + StrNoteContents = "Note to be deleted", + AddressId = (long)SD10Stops_route_V5.Addresses[1].RouteDestinationId, + DeviceType = "web" + }; + + var createdNote = notesManager.CreateNote(createRequest, out _); + + if (createdNote?.NoteId == null) + { + Console.WriteLine("Failed to create note for delete test."); + return; + } + + // Delete the note + var result = notesManager.DeleteNote(createdNote.NoteId.Value, out var resultResponse); + + if (result != null) + { + Console.WriteLine("DeleteNote executed successfully"); + Console.WriteLine("Note ID: {0}", result.NoteId); + Console.WriteLine("Status: {0}", result.Status); + } + else + { + PrintFailResponse(resultResponse, "DeleteNote"); + } + } + } +} diff --git a/route4me-csharp-sdk/Route4MeSDKTest/Examples/API5/Notes/DeleteNoteAsync.cs b/route4me-csharp-sdk/Route4MeSDKTest/Examples/API5/Notes/DeleteNoteAsync.cs new file mode 100644 index 00000000..e6539cdb --- /dev/null +++ b/route4me-csharp-sdk/Route4MeSDKTest/Examples/API5/Notes/DeleteNoteAsync.cs @@ -0,0 +1,55 @@ +using System; +using Route4MeSDK.DataTypes.V5.Notes; +using Route4MeSDK.QueryTypes.V5.Notes; +using Route4MeSDKLibrary.Managers; + +namespace Route4MeSDK.Examples +{ + public sealed partial class Route4MeExamples + { + /// + /// The example refers to the process of deleting a note asynchronously using the API 5 endpoint. + /// + public async void DeleteNoteAsync() + { + var notesManager = new NotesManagerV5(ActualApiKey); + + // First, create a note to delete + if (!RunOptimizationSingleDriverRoute10StopsV5()) + { + Console.WriteLine("Cannot create test route."); + return; + } + + var createRequest = new NoteStoreRequest + { + RouteId = SD10Stops_route_id_V5, + StrNoteContents = "Note to be deleted async", + AddressId = (long)SD10Stops_route_V5.Addresses[1].RouteDestinationId, + DeviceType = "web" + }; + + var createdNote = await notesManager.CreateNoteAsync(createRequest); + + if (createdNote.Item1?.NoteId == null) + { + Console.WriteLine("Failed to create note for delete test."); + return; + } + + // Delete the note + var result = await notesManager.DeleteNoteAsync(createdNote.Item1.NoteId.Value); + + if (result.Item1 != null) + { + Console.WriteLine("DeleteNoteAsync executed successfully"); + Console.WriteLine("Note ID: {0}", result.Item1.NoteId); + Console.WriteLine("Status: {0}", result.Item1.Status); + } + else + { + PrintFailResponse(result.Item2, "DeleteNoteAsync"); + } + } + } +} diff --git a/route4me-csharp-sdk/Route4MeSDKTest/Examples/API5/Notes/GetNote.cs b/route4me-csharp-sdk/Route4MeSDKTest/Examples/API5/Notes/GetNote.cs new file mode 100644 index 00000000..68d282f9 --- /dev/null +++ b/route4me-csharp-sdk/Route4MeSDKTest/Examples/API5/Notes/GetNote.cs @@ -0,0 +1,47 @@ +using System; +using Route4MeSDKLibrary.Managers; + +namespace Route4MeSDK.Examples +{ + public sealed partial class Route4MeExamples + { + /// + /// The example refers to the process of getting a note by ID using the API 5 endpoint. + /// + public void GetNote() + { + var notesManager = new NotesManagerV5(ActualApiKey); + + // First, get notes from a route to have a valid note ID + if (!RunOptimizationSingleDriverRoute10StopsV5()) + { + Console.WriteLine("Cannot create test route."); + return; + } + + var notes = notesManager.GetNotesByRoute(SD10Stops_route_id_V5, 1, 10, out var notesResponse); + + if (notes?.Data == null || notes.Data.Length == 0) + { + Console.WriteLine("No notes found on the route."); + return; + } + + var noteId = notes.Data[0].NoteId; + + var result = notesManager.GetNote(noteId, out var resultResponse); + + if (result != null) + { + Console.WriteLine("GetNote executed successfully"); + Console.WriteLine("Note ID: {0}", result.NoteId); + Console.WriteLine("Contents: {0}", result.Contents); + Console.WriteLine("Route ID: {0}", result.RouteId); + } + else + { + PrintFailResponse(resultResponse, "GetNote"); + } + } + } +} diff --git a/route4me-csharp-sdk/Route4MeSDKTest/Examples/API5/Notes/GetNoteAsync.cs b/route4me-csharp-sdk/Route4MeSDKTest/Examples/API5/Notes/GetNoteAsync.cs new file mode 100644 index 00000000..7f570edb --- /dev/null +++ b/route4me-csharp-sdk/Route4MeSDKTest/Examples/API5/Notes/GetNoteAsync.cs @@ -0,0 +1,47 @@ +using System; +using Route4MeSDKLibrary.Managers; + +namespace Route4MeSDK.Examples +{ + public sealed partial class Route4MeExamples + { + /// + /// The example refers to the process of getting a note by ID asynchronously using the API 5 endpoint. + /// + public async void GetNoteAsync() + { + var notesManager = new NotesManagerV5(ActualApiKey); + + // First, get notes from a route to have a valid note ID + if (!RunOptimizationSingleDriverRoute10StopsV5()) + { + Console.WriteLine("Cannot create test route."); + return; + } + + var notesResult = await notesManager.GetNotesByRouteAsync(SD10Stops_route_id_V5, 1, 10); + + if (notesResult.Item1?.Data == null || notesResult.Item1.Data.Length == 0) + { + Console.WriteLine("No notes found on the route."); + return; + } + + var noteId = notesResult.Item1.Data[0].NoteId; + + var result = await notesManager.GetNoteAsync(noteId); + + if (result.Item1 != null) + { + Console.WriteLine("GetNoteAsync executed successfully"); + Console.WriteLine("Note ID: {0}", result.Item1.NoteId); + Console.WriteLine("Contents: {0}", result.Item1.Contents); + Console.WriteLine("Route ID: {0}", result.Item1.RouteId); + } + else + { + PrintFailResponse(result.Item2, "GetNoteAsync"); + } + } + } +} diff --git a/route4me-csharp-sdk/Route4MeSDKTest/Examples/API5/Notes/GetNoteCustomTypes.cs b/route4me-csharp-sdk/Route4MeSDKTest/Examples/API5/Notes/GetNoteCustomTypes.cs new file mode 100644 index 00000000..610c939a --- /dev/null +++ b/route4me-csharp-sdk/Route4MeSDKTest/Examples/API5/Notes/GetNoteCustomTypes.cs @@ -0,0 +1,47 @@ +using System; +using Route4MeSDKLibrary.Managers; + +namespace Route4MeSDK.Examples +{ + public sealed partial class Route4MeExamples + { + /// + /// The example refers to the process of getting all note custom types using the API 5 endpoint. + /// + public void GetNoteCustomTypes() + { + var notesManager = new NotesManagerV5(ActualApiKey); + + var result = notesManager.GetNoteCustomTypes(out var resultResponse); + + if (result != null) + { + Console.WriteLine("GetNoteCustomTypes executed successfully"); + + if (result.Data != null && result.Data.Length > 0) + { + Console.WriteLine("Total custom types: {0}", result.Data.Length); + + foreach (var customType in result.Data) + { + Console.WriteLine(" Type: {0}", customType.NoteCustomType); + Console.WriteLine(" ID: {0}", customType.NoteCustomTypeId); + + if (customType.NoteCustomTypeValues != null && customType.NoteCustomTypeValues.Length > 0) + { + Console.WriteLine(" Values: {0}", string.Join(", ", customType.NoteCustomTypeValues)); + } + } + } + else + { + Console.WriteLine("No custom types found."); + } + } + else + { + PrintFailResponse(resultResponse, "GetNoteCustomTypes"); + } + } + } +} diff --git a/route4me-csharp-sdk/Route4MeSDKTest/Examples/API5/Notes/GetNoteCustomTypesAsync.cs b/route4me-csharp-sdk/Route4MeSDKTest/Examples/API5/Notes/GetNoteCustomTypesAsync.cs new file mode 100644 index 00000000..08bfdde9 --- /dev/null +++ b/route4me-csharp-sdk/Route4MeSDKTest/Examples/API5/Notes/GetNoteCustomTypesAsync.cs @@ -0,0 +1,47 @@ +using System; +using Route4MeSDKLibrary.Managers; + +namespace Route4MeSDK.Examples +{ + public sealed partial class Route4MeExamples + { + /// + /// The example refers to the process of getting all note custom types asynchronously using the API 5 endpoint. + /// + public async void GetNoteCustomTypesAsync() + { + var notesManager = new NotesManagerV5(ActualApiKey); + + var result = await notesManager.GetNoteCustomTypesAsync(); + + if (result.Item1 != null) + { + Console.WriteLine("GetNoteCustomTypesAsync executed successfully"); + + if (result.Item1.Data != null && result.Item1.Data.Length > 0) + { + Console.WriteLine("Total custom types: {0}", result.Item1.Data.Length); + + foreach (var customType in result.Item1.Data) + { + Console.WriteLine(" Type: {0}", customType.NoteCustomType); + Console.WriteLine(" ID: {0}", customType.NoteCustomTypeId); + + if (customType.NoteCustomTypeValues != null && customType.NoteCustomTypeValues.Length > 0) + { + Console.WriteLine(" Values: {0}", string.Join(", ", customType.NoteCustomTypeValues)); + } + } + } + else + { + Console.WriteLine("No custom types found."); + } + } + else + { + PrintFailResponse(result.Item2, "GetNoteCustomTypesAsync"); + } + } + } +} diff --git a/route4me-csharp-sdk/Route4MeSDKTest/Examples/API5/Notes/GetNotesByDestination.cs b/route4me-csharp-sdk/Route4MeSDKTest/Examples/API5/Notes/GetNotesByDestination.cs new file mode 100644 index 00000000..6c463c2d --- /dev/null +++ b/route4me-csharp-sdk/Route4MeSDKTest/Examples/API5/Notes/GetNotesByDestination.cs @@ -0,0 +1,51 @@ +using System; +using Route4MeSDKLibrary.Managers; + +namespace Route4MeSDK.Examples +{ + public sealed partial class Route4MeExamples + { + /// + /// The example refers to the process of getting notes by destination ID using the API 5 endpoint. + /// + public void GetNotesByDestination() + { + var notesManager = new NotesManagerV5(ActualApiKey); + + // Ensure we have a test route + if (!RunOptimizationSingleDriverRoute10StopsV5()) + { + Console.WriteLine("Cannot create test route."); + return; + } + + var destinationId = (long)SD10Stops_route_V5.Addresses[1].RouteDestinationId; + + var result = notesManager.GetNotesByDestination(destinationId, 1, 10, out var resultResponse); + + if (result != null) + { + Console.WriteLine("GetNotesByDestination executed successfully"); + Console.WriteLine("Total notes: {0}", result.Total); + Console.WriteLine("Current page: {0}", result.CurrentPage); + Console.WriteLine("Per page: {0}", result.PerPage); + + if (result.Data != null && result.Data.Length > 0) + { + foreach (var note in result.Data) + { + Console.WriteLine(" Note ID: {0}, Contents: {1}", note.NoteId, note.Contents); + } + } + else + { + Console.WriteLine("No notes found for this destination."); + } + } + else + { + PrintFailResponse(resultResponse, "GetNotesByDestination"); + } + } + } +} diff --git a/route4me-csharp-sdk/Route4MeSDKTest/Examples/API5/Notes/GetNotesByDestinationAsync.cs b/route4me-csharp-sdk/Route4MeSDKTest/Examples/API5/Notes/GetNotesByDestinationAsync.cs new file mode 100644 index 00000000..4bda3c81 --- /dev/null +++ b/route4me-csharp-sdk/Route4MeSDKTest/Examples/API5/Notes/GetNotesByDestinationAsync.cs @@ -0,0 +1,51 @@ +using System; +using Route4MeSDKLibrary.Managers; + +namespace Route4MeSDK.Examples +{ + public sealed partial class Route4MeExamples + { + /// + /// The example refers to the process of getting notes by destination ID asynchronously using the API 5 endpoint. + /// + public async void GetNotesByDestinationAsync() + { + var notesManager = new NotesManagerV5(ActualApiKey); + + // Ensure we have a test route + if (!RunOptimizationSingleDriverRoute10StopsV5()) + { + Console.WriteLine("Cannot create test route."); + return; + } + + var destinationId = (long)SD10Stops_route_V5.Addresses[1].RouteDestinationId; + + var result = await notesManager.GetNotesByDestinationAsync(destinationId, 1, 10); + + if (result.Item1 != null) + { + Console.WriteLine("GetNotesByDestinationAsync executed successfully"); + Console.WriteLine("Total notes: {0}", result.Item1.Total); + Console.WriteLine("Current page: {0}", result.Item1.CurrentPage); + Console.WriteLine("Per page: {0}", result.Item1.PerPage); + + if (result.Item1.Data != null && result.Item1.Data.Length > 0) + { + foreach (var note in result.Item1.Data) + { + Console.WriteLine(" Note ID: {0}, Contents: {1}", note.NoteId, note.Contents); + } + } + else + { + Console.WriteLine("No notes found for this destination."); + } + } + else + { + PrintFailResponse(result.Item2, "GetNotesByDestinationAsync"); + } + } + } +} diff --git a/route4me-csharp-sdk/Route4MeSDKTest/Examples/API5/Notes/GetNotesByRoute.cs b/route4me-csharp-sdk/Route4MeSDKTest/Examples/API5/Notes/GetNotesByRoute.cs new file mode 100644 index 00000000..c30b1a02 --- /dev/null +++ b/route4me-csharp-sdk/Route4MeSDKTest/Examples/API5/Notes/GetNotesByRoute.cs @@ -0,0 +1,49 @@ +using System; +using Route4MeSDKLibrary.Managers; + +namespace Route4MeSDK.Examples +{ + public sealed partial class Route4MeExamples + { + /// + /// The example refers to the process of getting notes by route ID using the API 5 endpoint. + /// + public void GetNotesByRoute() + { + var notesManager = new NotesManagerV5(ActualApiKey); + + // Ensure we have a test route + if (!RunOptimizationSingleDriverRoute10StopsV5()) + { + Console.WriteLine("Cannot create test route."); + return; + } + + var result = notesManager.GetNotesByRoute(SD10Stops_route_id_V5, 1, 10, out var resultResponse); + + if (result != null) + { + Console.WriteLine("GetNotesByRoute executed successfully"); + Console.WriteLine("Total notes: {0}", result.Total); + Console.WriteLine("Current page: {0}", result.CurrentPage); + Console.WriteLine("Per page: {0}", result.PerPage); + + if (result.Data != null && result.Data.Length > 0) + { + foreach (var note in result.Data) + { + Console.WriteLine(" Note ID: {0}, Contents: {1}", note.NoteId, note.Contents); + } + } + else + { + Console.WriteLine("No notes found on this route."); + } + } + else + { + PrintFailResponse(resultResponse, "GetNotesByRoute"); + } + } + } +} diff --git a/route4me-csharp-sdk/Route4MeSDKTest/Examples/API5/Notes/GetNotesByRouteAsync.cs b/route4me-csharp-sdk/Route4MeSDKTest/Examples/API5/Notes/GetNotesByRouteAsync.cs new file mode 100644 index 00000000..c308164a --- /dev/null +++ b/route4me-csharp-sdk/Route4MeSDKTest/Examples/API5/Notes/GetNotesByRouteAsync.cs @@ -0,0 +1,49 @@ +using System; +using Route4MeSDKLibrary.Managers; + +namespace Route4MeSDK.Examples +{ + public sealed partial class Route4MeExamples + { + /// + /// The example refers to the process of getting notes by route ID asynchronously using the API 5 endpoint. + /// + public async void GetNotesByRouteAsync() + { + var notesManager = new NotesManagerV5(ActualApiKey); + + // Ensure we have a test route + if (!RunOptimizationSingleDriverRoute10StopsV5()) + { + Console.WriteLine("Cannot create test route."); + return; + } + + var result = await notesManager.GetNotesByRouteAsync(SD10Stops_route_id_V5, 1, 10); + + if (result.Item1 != null) + { + Console.WriteLine("GetNotesByRouteAsync executed successfully"); + Console.WriteLine("Total notes: {0}", result.Item1.Total); + Console.WriteLine("Current page: {0}", result.Item1.CurrentPage); + Console.WriteLine("Per page: {0}", result.Item1.PerPage); + + if (result.Item1.Data != null && result.Item1.Data.Length > 0) + { + foreach (var note in result.Item1.Data) + { + Console.WriteLine(" Note ID: {0}, Contents: {1}", note.NoteId, note.Contents); + } + } + else + { + Console.WriteLine("No notes found on this route."); + } + } + else + { + PrintFailResponse(result.Item2, "GetNotesByRouteAsync"); + } + } + } +} diff --git a/route4me-csharp-sdk/Route4MeSDKTest/Examples/API5/Notes/UpdateNote.cs b/route4me-csharp-sdk/Route4MeSDKTest/Examples/API5/Notes/UpdateNote.cs new file mode 100644 index 00000000..4ed4a936 --- /dev/null +++ b/route4me-csharp-sdk/Route4MeSDKTest/Examples/API5/Notes/UpdateNote.cs @@ -0,0 +1,61 @@ +using System; +using Route4MeSDK.DataTypes.V5.Notes; +using Route4MeSDK.QueryTypes.V5.Notes; +using Route4MeSDKLibrary.Managers; + +namespace Route4MeSDK.Examples +{ + public sealed partial class Route4MeExamples + { + /// + /// The example refers to the process of updating a note using the API 5 endpoint. + /// + public void UpdateNote() + { + var notesManager = new NotesManagerV5(ActualApiKey); + + // First, create a note to update + if (!RunOptimizationSingleDriverRoute10StopsV5()) + { + Console.WriteLine("Cannot create test route."); + return; + } + + var createRequest = new NoteStoreRequest + { + RouteId = SD10Stops_route_id_V5, + StrNoteContents = "Original note content", + AddressId = (long)SD10Stops_route_V5.Addresses[1].RouteDestinationId, + DeviceType = "web" + }; + + var createdNote = notesManager.CreateNote(createRequest, out _); + + if (createdNote?.NoteId == null) + { + Console.WriteLine("Failed to create note for update test."); + return; + } + + // Update the note + var updateRequest = new NoteUpdateRequest + { + StrNoteContents = "Updated note content at " + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), + DeviceType = "web" + }; + + var result = notesManager.UpdateNote(createdNote.NoteId.Value, updateRequest, out var resultResponse); + + if (result != null) + { + Console.WriteLine("UpdateNote executed successfully"); + Console.WriteLine("Note ID: {0}", result.NoteId); + Console.WriteLine("Updated Contents: {0}", result.Contents); + } + else + { + PrintFailResponse(resultResponse, "UpdateNote"); + } + } + } +} diff --git a/route4me-csharp-sdk/Route4MeSDKTest/Examples/API5/Notes/UpdateNoteAsync.cs b/route4me-csharp-sdk/Route4MeSDKTest/Examples/API5/Notes/UpdateNoteAsync.cs new file mode 100644 index 00000000..d80afd73 --- /dev/null +++ b/route4me-csharp-sdk/Route4MeSDKTest/Examples/API5/Notes/UpdateNoteAsync.cs @@ -0,0 +1,61 @@ +using System; +using Route4MeSDK.DataTypes.V5.Notes; +using Route4MeSDK.QueryTypes.V5.Notes; +using Route4MeSDKLibrary.Managers; + +namespace Route4MeSDK.Examples +{ + public sealed partial class Route4MeExamples + { + /// + /// The example refers to the process of updating a note asynchronously using the API 5 endpoint. + /// + public async void UpdateNoteAsync() + { + var notesManager = new NotesManagerV5(ActualApiKey); + + // First, create a note to update + if (!RunOptimizationSingleDriverRoute10StopsV5()) + { + Console.WriteLine("Cannot create test route."); + return; + } + + var createRequest = new NoteStoreRequest + { + RouteId = SD10Stops_route_id_V5, + StrNoteContents = "Original note content for async update", + AddressId = (long)SD10Stops_route_V5.Addresses[1].RouteDestinationId, + DeviceType = "web" + }; + + var createdNote = await notesManager.CreateNoteAsync(createRequest); + + if (createdNote.Item1?.NoteId == null) + { + Console.WriteLine("Failed to create note for update test."); + return; + } + + // Update the note + var updateRequest = new NoteUpdateRequest + { + StrNoteContents = "Async updated note content at " + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), + DeviceType = "web" + }; + + var result = await notesManager.UpdateNoteAsync(createdNote.Item1.NoteId.Value, updateRequest); + + if (result.Item1 != null) + { + Console.WriteLine("UpdateNoteAsync executed successfully"); + Console.WriteLine("Note ID: {0}", result.Item1.NoteId); + Console.WriteLine("Updated Contents: {0}", result.Item1.Contents); + } + else + { + PrintFailResponse(result.Item2, "UpdateNoteAsync"); + } + } + } +} diff --git a/route4me-csharp-sdk/Route4MeSDKTest/Examples/CustomersV5/CustomersV5Examples.cs b/route4me-csharp-sdk/Route4MeSDKTest/Examples/CustomersV5/CustomersV5Examples.cs new file mode 100644 index 00000000..a7542392 --- /dev/null +++ b/route4me-csharp-sdk/Route4MeSDKTest/Examples/CustomersV5/CustomersV5Examples.cs @@ -0,0 +1,104 @@ +using Route4MeSDKLibrary.DataTypes.V5.Customers; +using Route4MeSDKLibrary.Managers; +using Route4MeSDKLibrary.QueryTypes.V5.Customers; +using System; + +namespace Route4MeSDK.Examples +{ + public sealed partial class Route4MeExamples + { + public void CustomersV5Examples() + { + var route4Me = new CustomerManagerV5(ActualApiKey); + + var createRequest = new StoreRequest() + { + Name = "Test Customer " + (new Random()).Next(), + ExternalId = "EXT-" + Guid.NewGuid().ToString("N").Substring(0, 8), + Status = 1, + Currency = "USD", + TaxId = "TAX-" + (new Random()).Next(1000, 9999) + }; + + var createdCustomer = route4Me.CreateCustomer(createRequest, out var createResponse); + if (createdCustomer != null) + { + Console.WriteLine($"Customer created: {createdCustomer.CustomerId}, {createdCustomer.Name}"); + } + else + { + PrintTestCustomer(createdCustomer, createResponse); + return; + } + + var getParams = new CustomerIdParameters() + { + CustomerId = createdCustomer.CustomerId + }; + + var retrievedCustomer = route4Me.GetCustomerById(getParams, out var getResponse); + if (retrievedCustomer != null) + { + Console.WriteLine($"Customer loaded: {retrievedCustomer.CustomerId}, {retrievedCustomer.Name}"); + } + else + { + PrintTestCustomer(retrievedCustomer, getResponse); + return; + } + + var updateParams = new UpdateCustomerParameters() + { + CustomerId = createdCustomer.CustomerId, + Name = "Updated Name " + DateTime.Now.ToString("HHmmss"), + Status = 0 + }; + + var updatedCustomer = route4Me.UpdateCustomer(updateParams, out var updateResponse); + if (updatedCustomer != null) + { + Console.WriteLine($"Customer updated: {updatedCustomer.CustomerId}, {updatedCustomer.Name}"); + } + else + { + PrintTestCustomer(updatedCustomer, updateResponse); + return; + } + + var listParams = new CustomerListParameters() + { + Page = 1, + PerPage = 10 + }; + + var customersList = route4Me.GetCustomersList(listParams, out var listResponse); + if (customersList != null && customersList.Items != null && customersList.Items.Count > 0) + { + Console.WriteLine($"Total Customers: {customersList.Items.Count}"); + foreach (var c in customersList.Items) + { + Console.WriteLine($"Customer ID: {c.CustomerId}, Name: {c.Name}, Status: {c.Status}"); + } + } + else + { + PrintTestCustomer(customersList, listResponse); + } + + var deleteParams = new CustomerIdParameters() + { + CustomerId = createdCustomer.CustomerId + }; + + var deleteResult = route4Me.DeleteCustomer(deleteParams, out var deleteResponse); + if (deleteResult != null && (deleteResult.Status == true || deleteResult.Code == 200)) + { + Console.WriteLine($"Customer deleted successfully: {createdCustomer.CustomerId}"); + } + else + { + PrintTestCustomer(deleteResult, deleteResponse); + } + } + } +} diff --git a/route4me-csharp-sdk/Route4MeSDKTest/Examples/ExamplesInfrastructure.cs b/route4me-csharp-sdk/Route4MeSDKTest/Examples/ExamplesInfrastructure.cs index 0668be6d..4b59bf7d 100644 --- a/route4me-csharp-sdk/Route4MeSDKTest/Examples/ExamplesInfrastructure.cs +++ b/route4me-csharp-sdk/Route4MeSDKTest/Examples/ExamplesInfrastructure.cs @@ -40,6 +40,7 @@ public sealed partial class Route4MeExamples public List CustomNoteTypesToRemove = new List(); public List OrdersToRemove = new List(); public List OrderCustomFieldsToRemove = new List(); + public List facilitiesToRemove = new List(); Order lastCreatedOrder; diff --git a/route4me-csharp-sdk/Route4MeSDKTest/Examples/ExamplesInfrastructureV5.cs b/route4me-csharp-sdk/Route4MeSDKTest/Examples/ExamplesInfrastructureV5.cs index 06ed6369..d8f5455c 100644 --- a/route4me-csharp-sdk/Route4MeSDKTest/Examples/ExamplesInfrastructureV5.cs +++ b/route4me-csharp-sdk/Route4MeSDKTest/Examples/ExamplesInfrastructureV5.cs @@ -1,12 +1,13 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using Microsoft.VisualStudio.TestTools.UnitTesting; +using Microsoft.VisualStudio.TestTools.UnitTesting; using Route4MeSDK.DataTypes.V5; using Route4MeSDK.QueryTypes.V5; using Route4MeSDKLibrary.DataTypes.V5; +using Route4MeSDKLibrary.DataTypes.V5.Customers; using Route4MeSDKLibrary.DataTypes.V5.Orders; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; using static Route4MeSDK.Route4MeManagerV5; namespace Route4MeSDK.Examples @@ -805,6 +806,92 @@ public void PrintTestOrders(object result) } } + #endregion + + #region Customer + + private void PrintTestCustomer(object result, ResultResponse resultResponse) + { + Console.WriteLine(""); + + string testName = new StackTrace().GetFrame(1)?.GetMethod()?.Name ?? string.Empty; + + if (result == null) + { + PrintFailResponse(resultResponse, testName); + return; + } + + Console.WriteLine($"{testName} executed successfully"); + + if (result is CustomerResource customerResource) + { + Console.WriteLine($"Customer ID: {customerResource.CustomerId}"); + Console.WriteLine($"Name: {customerResource.Name}"); + Console.WriteLine($"External ID: {customerResource.ExternalId}"); + Console.WriteLine($"Currency: {customerResource.Currency}"); + Console.WriteLine($"Status: {customerResource.Status}"); + return; + } + + if (result is CustomerShowResource customerShow) + { + Console.WriteLine($"Customer ID: {customerShow.CustomerId}"); + Console.WriteLine($"Name: {customerShow.Name}"); + Console.WriteLine($"External ID: {customerShow.ExternalId}"); + Console.WriteLine($"Accountable Person ID: {customerShow.AccountablePersonId}"); + Console.WriteLine($"Status: {customerShow.Status}"); + Console.WriteLine($"Currency: {customerShow.Currency}"); + return; + } + + if (result is CustomerListResponse listResponse) + { + var customers = listResponse.Items; + + if (customers == null || customers.Count == 0) + { + Console.WriteLine("No customers found in response."); + return; + } + + Console.WriteLine($"Total customers: {customers.Count}"); + + foreach (var c in customers) + { + Console.WriteLine($"Customer ID: {c.CustomerId}"); + Console.WriteLine($"Name: {c.Name}"); + Console.WriteLine($"External ID: {c.ExternalId}"); + Console.WriteLine($"Status: {(c.Status == 1 ? "Active" : "Inactive")}"); + Console.WriteLine($"Accountable person: {c.AccountablePersonFirstName} {c.AccountablePersonLastName}"); + Console.WriteLine($"Currency: {c.Currency}"); + Console.WriteLine($"Locations: {c.CustomerLocationCount}"); + Console.WriteLine($"Created by member ID: {c.CreatedByMemberId}"); + Console.WriteLine($"Created at: {c.CreationDate}"); + Console.WriteLine($"Updated timestamp: {c.UpdatedTimestamp}"); + + if (c.Contacts != null && c.Contacts.Count > 0) + { + Console.WriteLine("Contacts:"); + foreach (var contact in c.Contacts) + { + Console.WriteLine($" • {contact.Type}: {contact.Name} ({contact.Email}, {contact.Phone})"); + } + } + + if (c.FacilityIds != null && c.FacilityIds.Count > 0) + { + Console.WriteLine($"Facility IDs: {string.Join(", ", c.FacilityIds)}"); + } + } + + return; + } + + Console.WriteLine($"{testName}: Unknown response type ({result.GetType().Name})"); + } + + #endregion } } \ No newline at end of file diff --git a/route4me-csharp-sdk/Route4MeSDKTest/Examples/NotesV5/NotesV5Examples.cs b/route4me-csharp-sdk/Route4MeSDKTest/Examples/NotesV5/NotesV5Examples.cs new file mode 100644 index 00000000..59d3d5b8 --- /dev/null +++ b/route4me-csharp-sdk/Route4MeSDKTest/Examples/NotesV5/NotesV5Examples.cs @@ -0,0 +1,82 @@ +using Route4MeSDK.DataTypes.V5.Notes; +using Route4MeSDK.QueryTypes.V5.Notes; +using Route4MeSDKLibrary.Managers; +using System; + +namespace Route4MeSDK.Examples +{ + public sealed partial class Route4MeExamples + { + /// + /// Comprehensive examples for Notes API v5.0 demonstrating all CRUD operations. + /// This example shows the complete lifecycle of working with notes in Route4Me. + /// + public void NotesV5Examples() + { + Console.WriteLine(""); + Console.WriteLine("========== Notes API v5 Examples =========="); + Console.WriteLine(""); + + // 1. Create a single note + Console.WriteLine("1. Creating a single note..."); + CreateNote(); + Console.WriteLine(""); + + // 2. Get note by ID + Console.WriteLine("2. Getting note by ID..."); + GetNote(); + Console.WriteLine(""); + + // 3. Get notes by route + Console.WriteLine("3. Getting notes by route..."); + GetNotesByRoute(); + Console.WriteLine(""); + + // 4. Get notes by destination + Console.WriteLine("4. Getting notes by destination..."); + GetNotesByDestination(); + Console.WriteLine(""); + + // 5. Update a note + Console.WriteLine("5. Updating a note..."); + UpdateNote(); + Console.WriteLine(""); + + // 6. Delete a note + Console.WriteLine("6. Deleting a note..."); + DeleteNote(); + Console.WriteLine(""); + + // 7. Bulk create notes + Console.WriteLine("7. Bulk creating notes..."); + BulkCreateNotes(); + Console.WriteLine(""); + + // 8. Get note custom types + Console.WriteLine("8. Getting note custom types..."); + GetNoteCustomTypes(); + Console.WriteLine(""); + + // 9. Create a note custom type + Console.WriteLine("9. Creating a note custom type..."); + CreateNoteCustomType(); + Console.WriteLine(""); + + // 10. Async examples + Console.WriteLine("10. Running async examples..."); + CreateNoteAsync(); + GetNoteAsync(); + GetNotesByRouteAsync(); + GetNotesByDestinationAsync(); + UpdateNoteAsync(); + DeleteNoteAsync(); + BulkCreateNotesAsync(); + GetNoteCustomTypesAsync(); + CreateNoteCustomTypeAsync(); + Console.WriteLine(""); + + Console.WriteLine("========== Notes API v5 Examples Completed =========="); + Console.WriteLine(""); + } + } +} diff --git a/route4me-csharp-sdk/Route4MeSDKTest/Program.cs b/route4me-csharp-sdk/Route4MeSDKTest/Program.cs index 80dc017b..9556e0f9 100644 --- a/route4me-csharp-sdk/Route4MeSDKTest/Program.cs +++ b/route4me-csharp-sdk/Route4MeSDKTest/Program.cs @@ -13,7 +13,7 @@ static void Main(string[] args) // "api4" - execute all the examples related to the API 4 // "api5" - execute all the examples related to the API 5 // a method name - execute a specifed example method (e.g. "GetTeamMemberById") - string executeOption = "UpdateTerritory"; + string executeOption = "CreateFacility"; bool methodHasParams = (typeof(Route4MeExamples) .GetMethod(executeOption) @@ -384,6 +384,10 @@ static void Main(string[] args) { #region API 5 + #region Notes + examples.NotesV5Examples(); + #endregion + #region Team Management examples.GetTeamMembers(); diff --git a/route4me-csharp-sdk/Route4MeSDKTest/appsettings.json b/route4me-csharp-sdk/Route4MeSDKTest/appsettings.json index 947b195d..f9e76744 100644 --- a/route4me-csharp-sdk/Route4MeSDKTest/appsettings.json +++ b/route4me-csharp-sdk/Route4MeSDKTest/appsettings.json @@ -1,10 +1,10 @@ { "settings": { - "actualApiKey": "22222222222222222222222222222222", - "demoApiKey": "11111111111111111111111111111111" + "actualApiKey": "A31AC012AF983C07C772A84693D6BE17", + "demoApiKey": "A31AC012AF983C07C772A84693D6BE17" }, "account_to_api_key_map": { - "s1": "11111111111111111111111111111111", - "s2": "33333333333333333333333333333333" + "s1": "A31AC012AF983C07C772A84693D6BE17", + "s2": "A31AC012AF983C07C772A84693D6BE17" } } \ No newline at end of file diff --git a/route4me-csharp-sdk/Route4MeSDKUnitTest/Properties/Resources.resx b/route4me-csharp-sdk/Route4MeSDKUnitTest/Properties/Resources.resx index 85c7f6a5..061906ec 100644 --- a/route4me-csharp-sdk/Route4MeSDKUnitTest/Properties/Resources.resx +++ b/route4me-csharp-sdk/Route4MeSDKUnitTest/Properties/Resources.resx @@ -119,6 +119,6 @@ - ..\resources\test.png;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + ..\Resources\test.png;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 \ No newline at end of file diff --git a/route4me-csharp-sdk/Route4MeSdkV5UnitTest/V5/Customers/CustomersTests.cs b/route4me-csharp-sdk/Route4MeSdkV5UnitTest/V5/Customers/CustomersTests.cs new file mode 100644 index 00000000..55e9adda --- /dev/null +++ b/route4me-csharp-sdk/Route4MeSdkV5UnitTest/V5/Customers/CustomersTests.cs @@ -0,0 +1,288 @@ +using NUnit.Framework; +using Route4MeSDK; +using Route4MeSDK.DataTypes.V5; +using Route4MeSDKLibrary.DataTypes.V5.Customers; +using Route4MeSDKLibrary.Managers; +using Route4MeSDKLibrary.QueryTypes.V5.Customers; +using Route4MeSdkV5UnitTest.V5; +using System; +using System.Linq; +using System.Threading.Tasks; + +namespace Route4MeSDKUnitTest.V5 +{ + [TestFixture] + public class CustomersTests + { + private CustomerManagerV5 _customerManager; + private string _createdCustomerId; + + [SetUp] + public void Setup() + { + _customerManager = new CustomerManagerV5(ApiKeys.ActualApiKey); + _createdCustomerId = null; + } + + [TearDown] + public async Task Cleanup() + { + if (!string.IsNullOrEmpty(_createdCustomerId)) + { + try + { + await _customerManager.DeleteCustomerAsync(new CustomerIdParameters + { + CustomerId = _createdCustomerId + }); + } + catch (Exception ex) + { + Assert.Fail($"Cleanup failed: {ex.Message}"); + } + } + } + + [Test] + public void CreateCustomerTest() + { + var createRequest = new StoreRequest + { + Name = "Test Customer " + Guid.NewGuid().ToString("N").Substring(0, 6), + ExternalId = "EXT-" + Guid.NewGuid().ToString("N").Substring(0, 8), + Status = 1, + Currency = "USD", + AccountablePersonId = GetAccountablePersonId() + }; + + var result = _customerManager.CreateCustomer(createRequest, out var response); + _createdCustomerId = result.CustomerId; + + Assert.NotNull(result, GetErrorMessage(response)); + Assert.That(string.IsNullOrEmpty(result.CustomerId), Is.False); + } + + [Test] + public async Task CreateCustomerAsyncTest() + { + var createRequest = new StoreRequest + { + Name = "Async Customer " + Guid.NewGuid().ToString("N").Substring(0, 6), + ExternalId = "EXT-" + Guid.NewGuid().ToString("N").Substring(0, 8), + Status = 1, + Currency = "USD", + AccountablePersonId = GetAccountablePersonId() + }; + + var result = await _customerManager.CreateCustomerAsync(createRequest); + _createdCustomerId = result.Item1.CustomerId; + + Assert.NotNull(result.Item1, GetErrorMessage(result.Item2)); + Assert.That(string.IsNullOrEmpty(result.Item1.CustomerId), Is.False); + } + + [Test] + public void GetCustomerByIdTest() + { + var created = _customerManager.CreateCustomer(new StoreRequest + { + Name = "GetTest Customer " + (new Random()).Next(), + Status = 1, + Currency = "USD", + AccountablePersonId = GetAccountablePersonId() + }, out var response); + + Assert.NotNull(created, GetErrorMessage(response)); + _createdCustomerId = created.CustomerId; + + var parameters = new CustomerIdParameters { CustomerId = created.CustomerId }; + var result = _customerManager.GetCustomerById(parameters, out var getResponse); + + Assert.NotNull(result, GetErrorMessage(getResponse)); + Assert.That(result.CustomerId, Is.EqualTo(created.CustomerId)); + } + + [Test] + public async Task GetCustomerByIdAsyncTest() + { + var created = await _customerManager.CreateCustomerAsync(new StoreRequest + { + Name = "GetAsyncTest Customer " + new Random().Next(), + Status = 1, + Currency = "USD", + AccountablePersonId = GetAccountablePersonId() + }); + + Assert.NotNull(created.Item1, GetErrorMessage(created.Item2)); + _createdCustomerId = created.Item1.CustomerId; + + var parameters = new CustomerIdParameters { CustomerId = created.Item1.CustomerId }; + var result = await _customerManager.GetCustomerByIdAsync(parameters); + + Assert.NotNull(result.Item1, GetErrorMessage(result.Item2)); + Assert.That(result.Item1.CustomerId, Is.EqualTo(created.Item1.CustomerId)); + } + + [Test] + public void UpdateCustomerTest() + { + var created = _customerManager.CreateCustomer(new StoreRequest + { + Name = "UpdateTest Customer " + new Random().Next(), + Status = 1, + Currency = "USD", + AccountablePersonId = GetAccountablePersonId() + }, out var response); + + Assert.NotNull(created, GetErrorMessage(response)); + _createdCustomerId = created.CustomerId; + + var updateParams = new UpdateCustomerParameters + { + CustomerId = created.CustomerId, + Name = "Updated Name " + DateTime.Now.ToString("HHmmss"), + Status = 1, + AccountablePersonId = GetAccountablePersonId() + }; + + var updated = _customerManager.UpdateCustomer(updateParams, out var updateResponse); + + Assert.NotNull(updated, GetErrorMessage(updateResponse)); + Assert.That(updated.Name, Is.EqualTo(updateParams.Name)); + } + + [Test] + public async Task UpdateCustomerAsyncTest() + { + var created = await _customerManager.CreateCustomerAsync(new StoreRequest + { + Name = "UpdateAsync Customer " + (new Random()).Next(), + Status = 1, + Currency = "USD", + AccountablePersonId = GetAccountablePersonId() + }); + + Assert.NotNull(created.Item1, GetErrorMessage(created.Item2)); + _createdCustomerId = created.Item1.CustomerId; + + var updateParams = new UpdateCustomerParameters + { + CustomerId = created.Item1.CustomerId, + Name = "Updated Async " + DateTime.Now.ToString("HHmmss"), + Status = 1, + AccountablePersonId = GetAccountablePersonId() + }; + + var updated = await _customerManager.UpdateCustomerAsync(updateParams); + + Assert.NotNull(updated.Item1, GetErrorMessage(updated.Item2)); + Assert.That(updated.Item1.Name, Is.EqualTo(updateParams.Name)); + } + + [Test] + public void GetCustomersListTest() + { + var listParams = new CustomerListParameters() + { + Page = 1, + PerPage = 10 + }; + + var result = _customerManager.GetCustomersList(listParams, out var response); + + Assert.NotNull(result, GetErrorMessage(response)); + Assert.That(result.Items, Is.Not.Null); + } + + [Test] + public async Task GetCustomersListAsyncTest() + { + var listParams = new CustomerListParameters() + { + Page = 1, + PerPage = 10 + }; + + var result = await _customerManager.GetCustomersListAsync(listParams); + + Assert.NotNull(result.Item1, GetErrorMessage(result.Item2)); + Assert.That(result.Item1.Items, Is.Not.Null); + } + + [Test] + public void DeleteCustomerTest() + { + var created = _customerManager.CreateCustomer(new StoreRequest + { + Name = "DeleteTest Customer " + new Random().Next(), + Status = 1, + Currency = "USD", + AccountablePersonId = GetAccountablePersonId() + }, out var response); + + Assert.NotNull(created, GetErrorMessage(response)); + _createdCustomerId = created.CustomerId; + + var parameters = new CustomerIdParameters { CustomerId = created.CustomerId }; + var deleted = _customerManager.DeleteCustomer(parameters, out var deleteResponse); + + Assert.NotNull(deleted, GetErrorMessage(deleteResponse)); + Assert.That(deleted.Status, Is.True); + + _createdCustomerId = null; + } + + [Test] + public async Task DeleteCustomerAsyncTest() + { + var created = await _customerManager.CreateCustomerAsync(new StoreRequest + { + Name = "DeleteAsync Customer " + (new Random()).Next(), + Status = 1, + Currency = "USD", + AccountablePersonId = GetAccountablePersonId() + }); + + Assert.NotNull(created.Item1, GetErrorMessage(created.Item2)); + var deleteResult = await _customerManager.DeleteCustomerAsync(new CustomerIdParameters + { + CustomerId = created.Item1.CustomerId + }); + + Assert.NotNull(deleteResult.Item1, GetErrorMessage(deleteResult.Item2)); + Assert.That(deleteResult.Item1.Status, Is.True); + + _createdCustomerId = null; + } + + private long GetAccountablePersonId() + { + var teamManager = new Route4MeManagerV5(ApiKeys.ActualApiKey); + var members = teamManager.GetTeamMembers(out _); + var accountablePersonId = members?.FirstOrDefault()?.MemberId ?? 0; + + if (accountablePersonId == 0) + { + Assert.Fail("Cannot retrieve a valid team member for AccountablePersonId."); + } + + return accountablePersonId; + } + + private string GetErrorMessage(ResultResponse response) + { + if (response == null) + { + return "Unknown error (null response)"; + } + + if (response.Messages != null && response.Messages.Any()) + { + return string.Join("; ", + response.Messages.Select(kv => $"{kv.Key}: {string.Join(", ", kv.Value)}")); + } + + return $"Code: {response.Code}, ExitCode: {response.ExitCode}"; + } + } +} diff --git a/route4me-csharp-sdk/Route4MeSdkV5UnitTest/V5/FacilityManagerV5Tests.cs b/route4me-csharp-sdk/Route4MeSdkV5UnitTest/V5/FacilityManagerV5Tests.cs new file mode 100644 index 00000000..c8f63b5b --- /dev/null +++ b/route4me-csharp-sdk/Route4MeSdkV5UnitTest/V5/FacilityManagerV5Tests.cs @@ -0,0 +1,352 @@ +using System; +using NUnit.Framework; +using Route4MeSDK; +using Route4MeSDK.DataTypes.V5; +using Route4MeSDKLibrary.DataTypes.V5.Facilities; +using Route4MeSDKLibrary.QueryTypes.V5.Facilities; + +namespace Route4MeSdkV5UnitTest.V5 +{ + /// + /// Smoke tests for Facility API V5 MVP + /// + [TestFixture] + public class FacilityManagerV5Tests + { + private static readonly string CApiKey = ApiKeys.ActualApiKey; + private string _createdFacilityId; + private static readonly string TestRunId = DateTime.Now.ToString("yyyyMMdd_HHmmss"); + + [OneTimeTearDown] + public void Cleanup() + { + // Clean up created facility + if (!string.IsNullOrEmpty(_createdFacilityId)) + { + var route4Me = new Route4MeManagerV5(CApiKey); + route4Me.FacilityManager.DeleteFacility(_createdFacilityId, out _); + } + } + + /// + /// Smoke Test 1: Create and Get Facility + /// Tests CreateFacility and GetFacility methods + /// + [Test] + public void CreateAndGetFacilityTest() + { + var route4Me = new Route4MeManagerV5(CApiKey); + + var createRequest = new FacilityCreateRequest + { + FacilityAlias = $"Test Distribution Center {TestRunId}", + Address = "123 Industrial Blvd, Chicago, IL 60601", + Coordinates = new FacilityCoordinates + { + Lat = 41.8781, + Lng = -87.6298 + }, + FacilityTypes = new FacilityTypeAssignment[] + { + new FacilityTypeAssignment { FacilityTypeId = 1, IsDefault = true } + }, + Status = 1 + }; + + var createdFacility = route4Me.FacilityManager.CreateFacility(createRequest, out var createError); + + Assert.IsNotNull(createdFacility, "Failed to create facility"); + Assert.IsNotNull(createdFacility.FacilityId, "Facility ID should not be null"); + Assert.AreEqual($"Test Distribution Center {TestRunId}", createdFacility.FacilityAlias, "Facility name mismatch"); + Assert.IsNull(createError, "Create facility should not return error"); + + _createdFacilityId = createdFacility.FacilityId; + + var retrievedFacility = route4Me.FacilityManager.GetFacility(createdFacility.FacilityId, out var getError); + + Assert.IsNotNull(retrievedFacility, "Failed to retrieve facility"); + Assert.AreEqual(createdFacility.FacilityId, retrievedFacility.FacilityId, "Facility ID mismatch"); + Assert.AreEqual($"Test Distribution Center {TestRunId}", retrievedFacility.FacilityAlias, "Retrieved facility name mismatch"); + Assert.AreEqual("123 Industrial Blvd, Chicago, IL 60601", retrievedFacility.Address, "Address mismatch"); + Assert.IsNull(getError, "Get facility should not return error"); + } + + /// + /// Smoke Test 2: Get Facility Types + /// Tests GetFacilityTypes method + /// + [Test] + public void GetFacilityTypesTest() + { + var route4Me = new Route4MeManagerV5(CApiKey); + + var facilityTypes = route4Me.FacilityManager.GetFacilityTypes(out var error); + + Assert.IsNotNull(facilityTypes, "Facility types should not be null"); + Assert.IsNull(error, "Get facility types should not return error"); + + if (facilityTypes.Data != null && facilityTypes.Data.Length > 0) + { + var firstType = facilityTypes.Data[0]; + Assert.That(firstType.FacilityTypeId, Is.GreaterThan(0), "Facility type ID should be greater than 0"); + Assert.IsNotNull(firstType.FacilityTypeAlias, "Facility type name should not be null"); + } + else + { + Assert.Warn("No facility types found in the account. This test requires facility types to be configured."); + } + } + + /// + /// Test 3: Update Facility + /// Tests UpdateFacility method + /// + [Test] + public void UpdateFacilityTest() + { + var route4Me = new Route4MeManagerV5(CApiKey); + + var createRequest = new FacilityCreateRequest + { + FacilityAlias = $"Facility for Update Test {TestRunId}", + Address = "123 Update St, Chicago, IL 60601", + Coordinates = new FacilityCoordinates + { + Lat = 41.8781, + Lng = -87.6298 + }, + FacilityTypes = new FacilityTypeAssignment[] + { + new FacilityTypeAssignment { FacilityTypeId = 1, IsDefault = true } + }, + Status = 1 + }; + + var createdFacility = route4Me.FacilityManager.CreateFacility(createRequest, out var createError); + Assert.IsNotNull(createdFacility, "Failed to create facility for update test"); + _createdFacilityId = createdFacility.FacilityId; + + var updateRequest = new FacilityUpdateRequest + { + FacilityAlias = $"Updated Facility Name {TestRunId}", + Address = createdFacility.Address, + Coordinates = new FacilityCoordinates { Lat = 41.8781, Lng = -87.6298 }, + FacilityTypes = new FacilityTypeAssignment[] + { + new FacilityTypeAssignment { FacilityTypeId = 1, IsDefault = true } + }, + Status = 2 + }; + + var updatedFacility = route4Me.FacilityManager.UpdateFacility( + createdFacility.FacilityId, + updateRequest, + out var updateError + ); + + Assert.IsNotNull(updatedFacility, "Failed to update facility"); + Assert.AreEqual($"Updated Facility Name {TestRunId}", updatedFacility.FacilityAlias, "Facility name was not updated"); + Assert.AreEqual(2, updatedFacility.Status, "Facility status was not updated"); + Assert.IsNull(updateError, "Update facility should not return error"); + } + + /// + /// Test 4: Delete Facility + /// Tests DeleteFacility method + /// + [Test] + public void DeleteFacilityTest() + { + var route4Me = new Route4MeManagerV5(CApiKey); + + var createRequest = new FacilityCreateRequest + { + FacilityAlias = $"Facility for Delete Test {TestRunId}", + Address = "456 Delete Ave, Chicago, IL 60602", + Coordinates = new FacilityCoordinates + { + Lat = 41.8781, + Lng = -87.6298 + }, + FacilityTypes = new FacilityTypeAssignment[] + { + new FacilityTypeAssignment { FacilityTypeId = 1, IsDefault = true } + }, + Status = 1 + }; + + var createdFacility = route4Me.FacilityManager.CreateFacility(createRequest, out var createError); + Assert.IsNotNull(createdFacility, "Failed to create facility for delete test"); + + string facilityIdToDelete = createdFacility.FacilityId; + + var deleteResult = route4Me.FacilityManager.DeleteFacility(facilityIdToDelete, out var deleteError); + + Assert.IsNotNull(deleteResult, "Delete operation should return result"); + Assert.IsNull(deleteError, "Delete facility should not return error"); + + } + + /// + /// Test 5: Get Paginated Facilities + /// Tests GetFacilities with pagination + /// + [Test] + public void GetPaginatedFacilitiesTest() + { + var route4Me = new Route4MeManagerV5(CApiKey); + + var parameters = new FacilityGetParameters + { + Page = 1, + PerPage = 5 + }; + + var facilities = route4Me.FacilityManager.GetFacilities(parameters, out var error); + + Assert.IsNotNull(facilities, "Facilities should not be null"); + Assert.IsNull(error, "Get facilities should not return error"); + + Assert.IsNotNull(facilities.Data, "Facilities data should not be null"); + Assert.That(facilities.Data.Length, Is.LessThanOrEqualTo(5), "Should return at most 5 facilities per page"); + Assert.That(facilities.Total, Is.GreaterThanOrEqualTo(facilities.Data.Length), "Total should be >= data length"); + } + + /// + /// Test 6: Error Handling - Invalid Facility ID + /// Tests error handling for GetFacility with invalid ID + /// + [Test] + public void GetFacilityWithInvalidIdTest() + { + var route4Me = new Route4MeManagerV5(CApiKey); + + var result = route4Me.FacilityManager.GetFacility("", out var error); + + Assert.IsNull(result, "Result should be null for invalid ID"); + Assert.IsNotNull(error, "Should return error for invalid ID"); + Assert.IsFalse(error.Status, "Error status should be false"); + } + + /// + /// Test 7: Error Handling - Null Create Request + /// Tests error handling for CreateFacility with null request + /// + [Test] + public void CreateFacilityWithNullRequestTest() + { + var route4Me = new Route4MeManagerV5(CApiKey); + + var result = route4Me.FacilityManager.CreateFacility(null, out var error); + + Assert.IsNull(result, "Result should be null for null request"); + Assert.IsNotNull(error, "Should return error for null request"); + Assert.IsFalse(error.Status, "Error status should be false"); + } + + /// + /// Test 8: Error Handling - Update Facility with Invalid ID + /// Tests error handling for UpdateFacility with invalid ID + /// + [Test] + public void UpdateFacilityWithInvalidIdTest() + { + var route4Me = new Route4MeManagerV5(CApiKey); + + var updateRequest = new FacilityUpdateRequest + { + FacilityAlias = "Test", + Address = "Test Address", + Coordinates = new FacilityCoordinates { Lat = 0, Lng = 0 }, + FacilityTypes = new FacilityTypeAssignment[] + { + new FacilityTypeAssignment { FacilityTypeId = 1 } + } + }; + + var result = route4Me.FacilityManager.UpdateFacility("", updateRequest, out var error); + + Assert.IsNull(result, "Result should be null for invalid ID"); + Assert.IsNotNull(error, "Should return error for invalid ID"); + Assert.IsFalse(error.Status, "Error status should be false"); + } + + /// + /// Test 9: Error Handling - Update Facility with Null Request + /// Tests error handling for UpdateFacility with null request + /// + [Test] + public void UpdateFacilityWithNullRequestTest() + { + var route4Me = new Route4MeManagerV5(CApiKey); + + var result = route4Me.FacilityManager.UpdateFacility("test-id", null, out var error); + + Assert.IsNull(result, "Result should be null for null request"); + Assert.IsNotNull(error, "Should return error for null request"); + Assert.IsFalse(error.Status, "Error status should be false"); + } + + /// + /// Test 10: Error Handling - Delete Facility with Invalid ID + /// Tests error handling for DeleteFacility with invalid ID + /// + [Test] + public void DeleteFacilityWithInvalidIdTest() + { + var route4Me = new Route4MeManagerV5(CApiKey); + + var result = route4Me.FacilityManager.DeleteFacility("", out var error); + + Assert.IsNull(result, "Result should be null for invalid ID"); + Assert.IsNotNull(error, "Should return error for invalid ID"); + Assert.IsFalse(error.Status, "Error status should be false"); + } + + /// + /// Test 11: Get Facility Type by ID + /// Tests GetFacilityType method + /// + [Test] + public void GetFacilityTypeByIdTest() + { + var route4Me = new Route4MeManagerV5(CApiKey); + + // First get all types to get a valid ID + var allTypes = route4Me.FacilityManager.GetFacilityTypes(out var typesError); + Assert.IsNotNull(allTypes, "Should be able to get facility types"); + + if (allTypes.Data == null || allTypes.Data.Length == 0) + { + Assert.Ignore("No facility types found in the account. This test requires facility types to be configured."); + return; + } + + int validTypeId = allTypes.Data[0].FacilityTypeId; + + var specificType = route4Me.FacilityManager.GetFacilityType(validTypeId, out var error); + + Assert.IsNotNull(specificType, "Should be able to get facility type by ID"); + Assert.AreEqual(validTypeId, specificType.FacilityTypeId, "Returned type ID should match requested ID"); + Assert.IsNull(error, "Get facility type by ID should not return error"); + } + + /// + /// Test 12: Error Handling - Get Facility Type with Invalid ID + /// Tests error handling for GetFacilityType with invalid ID + /// + [Test] + public void GetFacilityTypeWithInvalidIdTest() + { + var route4Me = new Route4MeManagerV5(CApiKey); + + var result = route4Me.FacilityManager.GetFacilityType(0, out var error); + + Assert.IsNull(result, "Result should be null for invalid ID"); + Assert.IsNotNull(error, "Should return error for invalid ID"); + Assert.IsFalse(error.Status, "Error status should be false"); + } + + } +} + diff --git a/route4me-csharp-sdk/Route4MeSdkV5UnitTest/V5/Notes/NotesTests.cs b/route4me-csharp-sdk/Route4MeSdkV5UnitTest/V5/Notes/NotesTests.cs new file mode 100644 index 00000000..9d14583d --- /dev/null +++ b/route4me-csharp-sdk/Route4MeSdkV5UnitTest/V5/Notes/NotesTests.cs @@ -0,0 +1,1169 @@ +using System; +using System.Threading.Tasks; +using NUnit.Framework; +using Route4MeSDK.DataTypes.V5; +using Route4MeSDK.DataTypes.V5.Notes; +using Route4MeSDK.QueryTypes.V5.Notes; +using Route4MeSDKLibrary.Managers; + +namespace Route4MeSdkV5UnitTest.V5.Notes +{ + [TestFixture] + public class NotesTests + { + private static readonly string CApiKey = ApiKeys.ActualApiKey; + + private static TestDataRepository _tdr; + private static string _testRouteId; + private static long _testDestinationId; + + [OneTimeSetUp] + public void OneTimeSetup() + { + _tdr = new TestDataRepository(); + + var result = _tdr.RunOptimizationSingleDriverRoute10Stops(); + Assert.IsTrue(result, "Single Driver 10 Stops generation failed."); + + _testRouteId = _tdr.SD10Stops_route_id; + _testDestinationId = (long)_tdr.SD10Stops_route.Addresses[1].RouteDestinationId!; + } + + [OneTimeTearDown] + public void OneTimeTearDown() + { + var optimizationResult = _tdr.RemoveOptimization(new[] { _tdr.SD10Stops_optimization_problem_id }); + Assert.IsTrue(optimizationResult, "Removing of the testing optimization problem failed."); + } + + [Test] + public void CreateNoteTest() + { + var notesManager = new NotesManagerV5(CApiKey); + + var request = new NoteStoreRequest + { + RouteId = _testRouteId, + StrNoteContents = "Test note created at " + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), + AddressId = _testDestinationId, + DeviceType = "web" + }; + + var result = notesManager.CreateNote(request, out ResultResponse resultResponse); + + Assert.IsNotNull(result, "CreateNote failed"); + Assert.IsNotNull(result.NoteId, "Created note ID is null"); + Assert.IsNotNull(result.Contents, "Note contents is null"); + } + + [Test] + public async Task CreateNoteAsyncTest() + { + var notesManager = new NotesManagerV5(CApiKey); + + var request = new NoteStoreRequest + { + RouteId = _testRouteId, + StrNoteContents = "Async test note created at " + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), + AddressId = _testDestinationId, + DeviceType = "web" + }; + + var result = await notesManager.CreateNoteAsync(request); + + Assert.IsNotNull(result.Item1, "CreateNoteAsync failed"); + Assert.IsNotNull(result.Item1.NoteId); + Assert.IsNotNull(result.Item1.Contents); + } + + [Test] + public void GetNoteTest() + { + var notesManager = new NotesManagerV5(CApiKey); + + // Create a note to get + var createRequest = new NoteStoreRequest + { + RouteId = _testRouteId, + StrNoteContents = "Test note for GetNote", + AddressId = _testDestinationId, + DeviceType = "web" + }; + + var createdNote = notesManager.CreateNote(createRequest, out _); + Assert.IsNotNull(createdNote); + var noteId = createdNote.NoteId.Value; + + // Get the note + var result = notesManager.GetNote(noteId, out ResultResponse resultResponse); + + Assert.IsNotNull(result, "GetNote failed"); + Assert.AreEqual(noteId, result.NoteId, "Retrieved note ID doesn't match"); + Assert.IsNotNull(result.Contents, "Note contents is null"); + } + + [Test] + public async Task GetNoteAsyncTest() + { + var notesManager = new NotesManagerV5(CApiKey); + + // Create a note to get + var createRequest = new NoteStoreRequest + { + RouteId = _testRouteId, + StrNoteContents = "Test note for GetNoteAsync", + AddressId = _testDestinationId, + DeviceType = "web" + }; + + var createdNote = await notesManager.CreateNoteAsync(createRequest); + Assert.IsNotNull(createdNote.Item1); + var noteId = createdNote.Item1.NoteId.Value; + + // Get the note + var result = await notesManager.GetNoteAsync(noteId); + + Assert.IsNotNull(result.Item1, "GetNoteAsync failed"); + Assert.AreEqual(noteId, result.Item1.NoteId); + } + + [Test] + [Ignore("Requires an API key with permission")] + public void UpdateNoteTest() + { + var notesManager = new NotesManagerV5(CApiKey); + + // Create a note to update + var createRequest = new NoteStoreRequest + { + RouteId = _testRouteId, + StrNoteContents = "Original note content", + AddressId = _testDestinationId, + DeviceType = "web" + }; + + var createdNote = notesManager.CreateNote(createRequest, out _); + Assert.IsNotNull(createdNote); + var noteId = createdNote.NoteId.Value; + + // Update the note + var updateRequest = new NoteUpdateRequest + { + StrNoteContents = "Updated note content at " + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), + DeviceType = "web" + }; + + var result = notesManager.UpdateNote(noteId, updateRequest, out ResultResponse resultResponse); + + Assert.IsNotNull(result, "UpdateNote failed"); + Assert.AreEqual(noteId, result.NoteId); + Assert.IsTrue(result.Contents.Contains("Updated"), "Note content was not updated"); + } + + [Test] + [Ignore("Requires an API key with permission")] + public async Task UpdateNoteAsyncTest() + { + var notesManager = new NotesManagerV5(CApiKey); + + // Create a note to update + var createRequest = new NoteStoreRequest + { + RouteId = _testRouteId, + StrNoteContents = "Original note content async", + AddressId = _testDestinationId, + DeviceType = "web" + }; + + var createdNote = await notesManager.CreateNoteAsync(createRequest); + Assert.IsNotNull(createdNote.Item1); + var noteId = createdNote.Item1.NoteId.Value; + + // Update the note + var updateRequest = new NoteUpdateRequest + { + StrNoteContents = "Async updated note content at " + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), + DeviceType = "web" + }; + + var result = await notesManager.UpdateNoteAsync(noteId, updateRequest); + + Assert.IsNotNull(result.Item1, "UpdateNoteAsync failed"); + Assert.AreEqual(noteId, result.Item1.NoteId); + } + + [Test] + [Ignore("Requires an API key with permission")] + public void DeleteNoteTest() + { + var notesManager = new NotesManagerV5(CApiKey); + + // Create a note to delete + var createRequest = new NoteStoreRequest + { + RouteId = _testRouteId, + StrNoteContents = "Note to delete", + AddressId = _testDestinationId, + DeviceType = "web" + }; + + var createdNote = notesManager.CreateNote(createRequest, out _); + Assert.IsNotNull(createdNote); + var noteId = createdNote.NoteId.Value; + + // Delete the note + var result = notesManager.DeleteNote(noteId, out ResultResponse resultResponse); + + Assert.IsNotNull(result, "DeleteNote failed"); + Assert.IsTrue(result.Status, "Delete status is false"); + Assert.AreEqual(noteId, result.NoteId, "Deleted note ID doesn't match"); + } + + [Test] + [Ignore("Requires an API key with permission")] + public async Task DeleteNoteAsyncTest() + { + var notesManager = new NotesManagerV5(CApiKey); + + // Create a note to delete + var createRequest = new NoteStoreRequest + { + RouteId = _testRouteId, + StrNoteContents = "Note to delete async", + AddressId = _testDestinationId, + DeviceType = "web" + }; + + var createdNote = await notesManager.CreateNoteAsync(createRequest); + Assert.IsNotNull(createdNote.Item1); + var noteId = createdNote.Item1.NoteId.Value; + + // Delete the note + var result = await notesManager.DeleteNoteAsync(noteId); + + Assert.IsNotNull(result.Item1, "DeleteNoteAsync failed"); + Assert.IsTrue(result.Item1.Status); + } + + [Test] + public void GetNotesByRouteTest() + { + var notesManager = new NotesManagerV5(CApiKey); + + var result = notesManager.GetNotesByRoute(_testRouteId, 1, 10, out ResultResponse resultResponse); + + Assert.IsNotNull(result, "GetNotesByRoute failed"); + Assert.IsNotNull(result.Data, "Notes data is null"); + Assert.That(result.GetType(), Is.EqualTo(typeof(RouteNoteCollection))); + } + + [Test] + public async Task GetNotesByRouteAsyncTest() + { + var notesManager = new NotesManagerV5(CApiKey); + + var result = await notesManager.GetNotesByRouteAsync(_testRouteId, 1, 10); + + Assert.IsNotNull(result.Item1, "GetNotesByRouteAsync failed"); + Assert.IsNotNull(result.Item1.Data); + Assert.That(result.Item1.GetType(), Is.EqualTo(typeof(RouteNoteCollection))); + } + + [Test] + public void GetNotesByDestinationTest() + { + var notesManager = new NotesManagerV5(CApiKey); + + var result = notesManager.GetNotesByDestination(_testDestinationId, 1, 10, out ResultResponse resultResponse); + + Assert.IsNotNull(result, "GetNotesByDestination failed"); + Assert.IsNotNull(result.Data, "Notes data is null"); + Assert.That(result.GetType(), Is.EqualTo(typeof(RouteNoteCollection))); + } + + [Test] + public async Task GetNotesByDestinationAsyncTest() + { + var notesManager = new NotesManagerV5(CApiKey); + + var result = await notesManager.GetNotesByDestinationAsync(_testDestinationId, 1, 10); + + Assert.IsNotNull(result.Item1, "GetNotesByDestinationAsync failed"); + Assert.IsNotNull(result.Item1.Data); + Assert.That(result.Item1.GetType(), Is.EqualTo(typeof(RouteNoteCollection))); + } + + [Test] + public void GetNoteCustomTypesTest() + { + var notesManager = new NotesManagerV5(CApiKey); + + var result = notesManager.GetNoteCustomTypes(out ResultResponse resultResponse); + + Assert.IsNotNull(result, "GetNoteCustomTypes failed"); + Assert.That(result.GetType(), Is.EqualTo(typeof(NoteCustomTypeCollection))); + } + + [Test] + public async Task GetNoteCustomTypesAsyncTest() + { + var notesManager = new NotesManagerV5(CApiKey); + + var result = await notesManager.GetNoteCustomTypesAsync(); + + Assert.IsNotNull(result.Item1, "GetNoteCustomTypesAsync failed"); + Assert.That(result.Item1.GetType(), Is.EqualTo(typeof(NoteCustomTypeCollection))); + } + + [Test] + public void CreateNoteCustomTypeTest() + { + var notesManager = new NotesManagerV5(CApiKey); + + var request = new NoteCustomTypeStoreRequest + { + NoteCustomType = "Test Custom Type " + DateTime.Now.Ticks, + NoteCustomTypeValues = new[] { "Value 1", "Value 2", "Value 3" }, + NoteCustomFieldType = 1 + }; + + var result = notesManager.CreateNoteCustomType(request, out ResultResponse resultResponse); + + Assert.IsNotNull(result, "CreateNoteCustomType failed"); + Assert.IsTrue(result.Status, "Custom type creation status is false"); + } + + [Test] + public async Task CreateNoteCustomTypeAsyncTest() + { + var notesManager = new NotesManagerV5(CApiKey); + + var request = new NoteCustomTypeStoreRequest + { + NoteCustomType = "Async Test Custom Type " + DateTime.Now.Ticks, + NoteCustomTypeValues = new[] { "Async Value 1" }, + NoteCustomFieldType = 1 + }; + + var result = await notesManager.CreateNoteCustomTypeAsync(request); + + Assert.IsNotNull(result.Item1, "CreateNoteCustomTypeAsync failed"); + Assert.IsTrue(result.Item1.Status); + } + + #region Validation Tests + + [Test] + public void CreateNoteValidationTest() + { + var notesManager = new NotesManagerV5(CApiKey); + + // Test with null request + var result1 = notesManager.CreateNote(null, out ResultResponse resultResponse1); + Assert.IsNull(result1, "Expected null result for null request"); + Assert.IsFalse(resultResponse1.Status, "Expected validation error status"); + Assert.IsTrue(resultResponse1.Messages.ContainsKey("Error"), "Expected error messages"); + + // Test with missing required field (route_id) + var request2 = new NoteStoreRequest + { + StrNoteContents = "Test note without route_id" + }; + + var result2 = notesManager.CreateNote(request2, out ResultResponse resultResponse2); + Assert.IsNull(result2, "Expected null result for missing route_id"); + Assert.IsFalse(resultResponse2.Status, "Expected validation error status"); + Assert.IsTrue(resultResponse2.Messages.ContainsKey("Error"), "Expected error messages"); + + // Test with missing required field (note contents) + var request3 = new NoteStoreRequest + { + RouteId = _testRouteId + }; + + var result3 = notesManager.CreateNote(request3, out ResultResponse resultResponse3); + Assert.IsNull(result3, "Expected null result for missing note contents"); + Assert.IsFalse(resultResponse3.Status, "Expected validation error status"); + Assert.IsTrue(resultResponse3.Messages.ContainsKey("Error"), "Expected error messages"); + + // Test with empty route_id + var request4 = new NoteStoreRequest + { + RouteId = "", + StrNoteContents = "Test note" + }; + + var result4 = notesManager.CreateNote(request4, out ResultResponse resultResponse4); + Assert.IsNull(result4, "Expected null result for empty route_id"); + Assert.IsFalse(resultResponse4.Status, "Expected validation error status"); + + // Test with whitespace route_id + var request5 = new NoteStoreRequest + { + RouteId = " ", + StrNoteContents = "Test note" + }; + + var result5 = notesManager.CreateNote(request5, out ResultResponse resultResponse5); + Assert.IsNull(result5, "Expected null result for whitespace route_id"); + Assert.IsFalse(resultResponse5.Status, "Expected validation error status"); + } + + [Test] + public void GetNoteValidationTest() + { + var notesManager = new NotesManagerV5(CApiKey); + + // Test with null note ID + var result1 = notesManager.GetNote(null, out ResultResponse resultResponse1); + Assert.IsNull(result1, "Expected null result for null note ID"); + Assert.IsFalse(resultResponse1.Status, "Expected validation error status"); + Assert.IsTrue(resultResponse1.Messages.ContainsKey("Error"), "Expected error messages"); + + // Test with empty string note ID + var result2 = notesManager.GetNote("", out ResultResponse resultResponse2); + Assert.IsNull(result2, "Expected null result for empty note ID"); + Assert.IsFalse(resultResponse2.Status, "Expected validation error status"); + + // Test with whitespace note ID + var result3 = notesManager.GetNote(" ", out ResultResponse resultResponse3); + Assert.IsNull(result3, "Expected null result for whitespace note ID"); + Assert.IsFalse(resultResponse3.Status, "Expected validation error status"); + } + + [Test] + public void UpdateNoteValidationTest() + { + var notesManager = new NotesManagerV5(CApiKey); + + // Test with null note ID + var result1 = notesManager.UpdateNote(null, new NoteUpdateRequest { StrNoteContents = "Test" }, out ResultResponse resultResponse1); + Assert.IsNull(result1, "Expected null result for null note ID"); + Assert.IsFalse(resultResponse1.Status, "Expected validation error status"); + Assert.IsTrue(resultResponse1.Messages.ContainsKey("Error"), "Expected error messages"); + + // Test with empty note ID + var result2 = notesManager.UpdateNote("", new NoteUpdateRequest { StrNoteContents = "Test" }, out ResultResponse resultResponse2); + Assert.IsNull(result2, "Expected null result for empty note ID"); + Assert.IsFalse(resultResponse2.Status, "Expected validation error status"); + + // Test with null request + var result3 = notesManager.UpdateNote(123, null, out ResultResponse resultResponse3); + Assert.IsNull(result3, "Expected null result for null request"); + Assert.IsFalse(resultResponse3.Status, "Expected validation error status"); + Assert.IsTrue(resultResponse3.Messages.ContainsKey("Error"), "Expected error messages"); + + // Test with both null + var result4 = notesManager.UpdateNote(null, null, out ResultResponse resultResponse4); + Assert.IsNull(result4, "Expected null result for both null"); + Assert.IsFalse(resultResponse4.Status, "Expected validation error status"); + } + + [Test] + public void DeleteNoteValidationTest() + { + var notesManager = new NotesManagerV5(CApiKey); + + // Test with null note ID + var result1 = notesManager.DeleteNote(null, out ResultResponse resultResponse1); + Assert.IsNull(result1, "Expected null result for null note ID"); + Assert.IsFalse(resultResponse1.Status, "Expected validation error status"); + Assert.IsTrue(resultResponse1.Messages.ContainsKey("Error"), "Expected error messages"); + + // Test with empty string note ID + var result2 = notesManager.DeleteNote("", out ResultResponse resultResponse2); + Assert.IsNull(result2, "Expected null result for empty note ID"); + Assert.IsFalse(resultResponse2.Status, "Expected validation error status"); + + // Test with whitespace note ID + var result3 = notesManager.DeleteNote(" ", out ResultResponse resultResponse3); + Assert.IsNull(result3, "Expected null result for whitespace note ID"); + Assert.IsFalse(resultResponse3.Status, "Expected validation error status"); + } + + [Test] + public void GetNotesByRouteValidationTest() + { + var notesManager = new NotesManagerV5(CApiKey); + + // Test with null route ID + var result1 = notesManager.GetNotesByRoute(null, 1, 10, out ResultResponse resultResponse1); + Assert.IsNull(result1, "Expected null result for null route ID"); + Assert.IsFalse(resultResponse1.Status, "Expected validation error status"); + Assert.IsTrue(resultResponse1.Messages.ContainsKey("Error"), "Expected error messages"); + + // Test with empty route ID + var result2 = notesManager.GetNotesByRoute("", 1, 10, out ResultResponse resultResponse2); + Assert.IsNull(result2, "Expected null result for empty route ID"); + Assert.IsFalse(resultResponse2.Status, "Expected validation error status"); + + // Test with whitespace route ID + var result3 = notesManager.GetNotesByRoute(" ", 1, 10, out ResultResponse resultResponse3); + Assert.IsNull(result3, "Expected null result for whitespace route ID"); + Assert.IsFalse(resultResponse3.Status, "Expected validation error status"); + + // Test with invalid page number (0) + var result4 = notesManager.GetNotesByRoute(_testRouteId, 0, 10, out ResultResponse resultResponse4); + Assert.IsNull(result4, "Expected null result for page = 0"); + Assert.IsFalse(resultResponse4.Status, "Expected validation error status"); + Assert.IsTrue(resultResponse4.Messages.ContainsKey("Error"), "Expected error messages"); + + // Test with invalid page number (negative) + var result5 = notesManager.GetNotesByRoute(_testRouteId, -1, 10, out ResultResponse resultResponse5); + Assert.IsNull(result5, "Expected null result for negative page"); + Assert.IsFalse(resultResponse5.Status, "Expected validation error status"); + + // Test with invalid per_page (0) + var result6 = notesManager.GetNotesByRoute(_testRouteId, 1, 0, out ResultResponse resultResponse6); + Assert.IsNull(result6, "Expected null result for per_page = 0"); + Assert.IsFalse(resultResponse6.Status, "Expected validation error status"); + Assert.IsTrue(resultResponse6.Messages.ContainsKey("Error"), "Expected error messages"); + + // Test with invalid per_page (negative) + var result7 = notesManager.GetNotesByRoute(_testRouteId, 1, -1, out ResultResponse resultResponse7); + Assert.IsNull(result7, "Expected null result for negative per_page"); + Assert.IsFalse(resultResponse7.Status, "Expected validation error status"); + + // Test with both invalid pagination values + var result8 = notesManager.GetNotesByRoute(_testRouteId, -1, -1, out ResultResponse resultResponse8); + Assert.IsNull(result8, "Expected null result for both invalid pagination"); + Assert.IsFalse(resultResponse8.Status, "Expected validation error status"); + } + + [Test] + public void GetNotesByDestinationValidationTest() + { + var notesManager = new NotesManagerV5(CApiKey); + + // Test with null destination ID + var result1 = notesManager.GetNotesByDestination(null, 1, 10, out ResultResponse resultResponse1); + Assert.IsNull(result1, "Expected null result for null destination ID"); + Assert.IsFalse(resultResponse1.Status, "Expected validation error status"); + Assert.IsTrue(resultResponse1.Messages.ContainsKey("Error"), "Expected error messages"); + + // Test with empty string destination ID + var result2 = notesManager.GetNotesByDestination("", 1, 10, out ResultResponse resultResponse2); + Assert.IsNull(result2, "Expected null result for empty destination ID"); + Assert.IsFalse(resultResponse2.Status, "Expected validation error status"); + + // Test with whitespace destination ID + var result3 = notesManager.GetNotesByDestination(" ", 1, 10, out ResultResponse resultResponse3); + Assert.IsNull(result3, "Expected null result for whitespace destination ID"); + Assert.IsFalse(resultResponse3.Status, "Expected validation error status"); + + // Test with invalid page number (0) + var result4 = notesManager.GetNotesByDestination(_testDestinationId, 0, 10, out ResultResponse resultResponse4); + Assert.IsNull(result4, "Expected null result for page = 0"); + Assert.IsFalse(resultResponse4.Status, "Expected validation error status"); + Assert.IsTrue(resultResponse4.Messages.ContainsKey("Error"), "Expected error messages"); + + // Test with invalid page number (negative) + var result5 = notesManager.GetNotesByDestination(_testDestinationId, -5, 10, out ResultResponse resultResponse5); + Assert.IsNull(result5, "Expected null result for negative page"); + Assert.IsFalse(resultResponse5.Status, "Expected validation error status"); + + // Test with invalid per_page (0) + var result6 = notesManager.GetNotesByDestination(_testDestinationId, 1, 0, out ResultResponse resultResponse6); + Assert.IsNull(result6, "Expected null result for per_page = 0"); + Assert.IsFalse(resultResponse6.Status, "Expected validation error status"); + Assert.IsTrue(resultResponse6.Messages.ContainsKey("Error"), "Expected error messages"); + + // Test with invalid per_page (negative) + var result7 = notesManager.GetNotesByDestination(_testDestinationId, 1, -10, out ResultResponse resultResponse7); + Assert.IsNull(result7, "Expected null result for negative per_page"); + Assert.IsFalse(resultResponse7.Status, "Expected validation error status"); + } + + [Test] + public void CreateNoteCustomTypeValidationTest() + { + var notesManager = new NotesManagerV5(CApiKey); + + // Test with null request + var result1 = notesManager.CreateNoteCustomType(null, out ResultResponse resultResponse1); + Assert.IsNull(result1, "Expected null result for null request"); + Assert.IsFalse(resultResponse1.Status, "Expected validation error status"); + Assert.IsTrue(resultResponse1.Messages.ContainsKey("Error"), "Expected error messages"); + + // Test with missing custom type name + var request2 = new NoteCustomTypeStoreRequest + { + NoteCustomTypeValues = new[] { "Value 1" }, + NoteCustomFieldType = 1 + }; + + var result2 = notesManager.CreateNoteCustomType(request2, out ResultResponse resultResponse2); + Assert.IsNull(result2, "Expected null result for missing custom type name"); + Assert.IsFalse(resultResponse2.Status, "Expected validation error status"); + Assert.IsTrue(resultResponse2.Messages.ContainsKey("Error"), "Expected error messages"); + + // Test with empty custom type name + var request3 = new NoteCustomTypeStoreRequest + { + NoteCustomType = "", + NoteCustomTypeValues = new[] { "Value 1" }, + NoteCustomFieldType = 1 + }; + + var result3 = notesManager.CreateNoteCustomType(request3, out ResultResponse resultResponse3); + Assert.IsNull(result3, "Expected null result for empty custom type name"); + Assert.IsFalse(resultResponse3.Status, "Expected validation error status"); + + // Test with whitespace custom type name + var request4 = new NoteCustomTypeStoreRequest + { + NoteCustomType = " ", + NoteCustomTypeValues = new[] { "Value 1" }, + NoteCustomFieldType = 1 + }; + + var result4 = notesManager.CreateNoteCustomType(request4, out ResultResponse resultResponse4); + Assert.IsNull(result4, "Expected null result for whitespace custom type name"); + Assert.IsFalse(resultResponse4.Status, "Expected validation error status"); + + // Test with null values array + var request5 = new NoteCustomTypeStoreRequest + { + NoteCustomType = "Test Type", + NoteCustomTypeValues = null, + NoteCustomFieldType = 1 + }; + + var result5 = notesManager.CreateNoteCustomType(request5, out ResultResponse resultResponse5); + Assert.IsNull(result5, "Expected null result for null values array"); + Assert.IsFalse(resultResponse5.Status, "Expected validation error status"); + Assert.IsTrue(resultResponse5.Messages.ContainsKey("Error"), "Expected error messages"); + + // Test with empty values array + var request6 = new NoteCustomTypeStoreRequest + { + NoteCustomType = "Test Type", + NoteCustomTypeValues = new string[] { }, + NoteCustomFieldType = 1 + }; + + var result6 = notesManager.CreateNoteCustomType(request6, out ResultResponse resultResponse6); + Assert.IsNull(result6, "Expected null result for empty values array"); + Assert.IsFalse(resultResponse6.Status, "Expected validation error status"); + Assert.IsTrue(resultResponse6.Messages.ContainsKey("Error"), "Expected error messages"); + + // Test with invalid field type (0) + var request7 = new NoteCustomTypeStoreRequest + { + NoteCustomType = "Test Type", + NoteCustomTypeValues = new[] { "Value 1" }, + NoteCustomFieldType = 0 + }; + + var result7 = notesManager.CreateNoteCustomType(request7, out ResultResponse resultResponse7); + Assert.IsNull(result7, "Expected null result for field type = 0"); + Assert.IsFalse(resultResponse7.Status, "Expected validation error status"); + Assert.IsTrue(resultResponse7.Messages.ContainsKey("Error"), "Expected error messages"); + + // Test with invalid field type (5 - too high) + var request8 = new NoteCustomTypeStoreRequest + { + NoteCustomType = "Test Type", + NoteCustomTypeValues = new[] { "Value 1" }, + NoteCustomFieldType = 5 + }; + + var result8 = notesManager.CreateNoteCustomType(request8, out ResultResponse resultResponse8); + Assert.IsNull(result8, "Expected null result for field type = 5"); + Assert.IsFalse(resultResponse8.Status, "Expected validation error status"); + Assert.IsTrue(resultResponse8.Messages.ContainsKey("Error"), "Expected error messages"); + + // Test with invalid field type (negative) + var request9 = new NoteCustomTypeStoreRequest + { + NoteCustomType = "Test Type", + NoteCustomTypeValues = new[] { "Value 1" }, + NoteCustomFieldType = -1 + }; + + var result9 = notesManager.CreateNoteCustomType(request9, out ResultResponse resultResponse9); + Assert.IsNull(result9, "Expected null result for negative field type"); + Assert.IsFalse(resultResponse9.Status, "Expected validation error status"); + Assert.IsTrue(resultResponse9.Messages.ContainsKey("Error"), "Expected error messages"); + + // Test with field type = 100 (way out of range) + var request10 = new NoteCustomTypeStoreRequest + { + NoteCustomType = "Test Type", + NoteCustomTypeValues = new[] { "Value 1" }, + NoteCustomFieldType = 100 + }; + + var result10 = notesManager.CreateNoteCustomType(request10, out ResultResponse resultResponse10); + Assert.IsNull(result10, "Expected null result for field type = 100"); + Assert.IsFalse(resultResponse10.Status, "Expected validation error status"); + } + + [Test] + public async Task CreateNoteAsyncValidationTest() + { + var notesManager = new NotesManagerV5(CApiKey); + + // Test with null request + var result1 = await notesManager.CreateNoteAsync(null); + Assert.IsNull(result1.Item1, "Expected null result for null request"); + Assert.IsFalse(result1.Item2.Status, "Expected validation error status"); + Assert.IsTrue(result1.Item2.Messages.ContainsKey("Error"), "Expected error messages"); + + // Test with missing route_id + var result2 = await notesManager.CreateNoteAsync(new NoteStoreRequest { StrNoteContents = "Test" }); + Assert.IsNull(result2.Item1, "Expected null result for missing route_id"); + Assert.IsFalse(result2.Item2.Status, "Expected validation error status"); + + // Test with missing note contents + var result3 = await notesManager.CreateNoteAsync(new NoteStoreRequest { RouteId = _testRouteId }); + Assert.IsNull(result3.Item1, "Expected null result for missing note contents"); + Assert.IsFalse(result3.Item2.Status, "Expected validation error status"); + } + + [Test] + public async Task UpdateNoteAsyncValidationTest() + { + var notesManager = new NotesManagerV5(CApiKey); + + // Test with null note ID + var result1 = await notesManager.UpdateNoteAsync(null, new NoteUpdateRequest { StrNoteContents = "Test" }); + Assert.IsNull(result1.Item1, "Expected null result for null note ID"); + Assert.IsFalse(result1.Item2.Status, "Expected validation error status"); + + // Test with null request + var result2 = await notesManager.UpdateNoteAsync(123, null); + Assert.IsNull(result2.Item1, "Expected null result for null request"); + Assert.IsFalse(result2.Item2.Status, "Expected validation error status"); + } + + [Test] + public async Task DeleteNoteAsyncValidationTest() + { + var notesManager = new NotesManagerV5(CApiKey); + + // Test with null note ID + var result = await notesManager.DeleteNoteAsync(null); + Assert.IsNull(result.Item1, "Expected null result for null note ID"); + Assert.IsFalse(result.Item2.Status, "Expected validation error status"); + } + + [Test] + public async Task GetNotesByRouteAsyncValidationTest() + { + var notesManager = new NotesManagerV5(CApiKey); + + // Test with null route ID + var result1 = await notesManager.GetNotesByRouteAsync(null, 1, 10); + Assert.IsNull(result1.Item1, "Expected null result for null route ID"); + Assert.IsFalse(result1.Item2.Status, "Expected validation error status"); + + // Test with invalid page + var result2 = await notesManager.GetNotesByRouteAsync(_testRouteId, 0, 10); + Assert.IsNull(result2.Item1, "Expected null result for page = 0"); + Assert.IsFalse(result2.Item2.Status, "Expected validation error status"); + + // Test with invalid per_page + var result3 = await notesManager.GetNotesByRouteAsync(_testRouteId, 1, -1); + Assert.IsNull(result3.Item1, "Expected null result for negative per_page"); + Assert.IsFalse(result3.Item2.Status, "Expected validation error status"); + } + + [Test] + public async Task GetNotesByDestinationAsyncValidationTest() + { + var notesManager = new NotesManagerV5(CApiKey); + + // Test with null destination ID + var result1 = await notesManager.GetNotesByDestinationAsync(null, 1, 10); + Assert.IsNull(result1.Item1, "Expected null result for null destination ID"); + Assert.IsFalse(result1.Item2.Status, "Expected validation error status"); + + // Test with invalid page + var result2 = await notesManager.GetNotesByDestinationAsync(_testDestinationId, -1, 10); + Assert.IsNull(result2.Item1, "Expected null result for negative page"); + Assert.IsFalse(result2.Item2.Status, "Expected validation error status"); + + // Test with invalid per_page + var result3 = await notesManager.GetNotesByDestinationAsync(_testDestinationId, 1, 0); + Assert.IsNull(result3.Item1, "Expected null result for per_page = 0"); + Assert.IsFalse(result3.Item2.Status, "Expected validation error status"); + } + + [Test] + public async Task CreateNoteCustomTypeAsyncValidationTest() + { + var notesManager = new NotesManagerV5(CApiKey); + + // Test with null request + var result1 = await notesManager.CreateNoteCustomTypeAsync(null); + Assert.IsNull(result1.Item1, "Expected null result for null request"); + Assert.IsFalse(result1.Item2.Status, "Expected validation error status"); + + // Test with missing custom type name + var result2 = await notesManager.CreateNoteCustomTypeAsync(new NoteCustomTypeStoreRequest + { + NoteCustomTypeValues = new[] { "Value 1" }, + NoteCustomFieldType = 1 + }); + Assert.IsNull(result2.Item1, "Expected null result for missing custom type name"); + Assert.IsFalse(result2.Item2.Status, "Expected validation error status"); + + // Test with empty values array + var result3 = await notesManager.CreateNoteCustomTypeAsync(new NoteCustomTypeStoreRequest + { + NoteCustomType = "Test", + NoteCustomTypeValues = new string[] { }, + NoteCustomFieldType = 1 + }); + Assert.IsNull(result3.Item1, "Expected null result for empty values array"); + Assert.IsFalse(result3.Item2.Status, "Expected validation error status"); + + // Test with invalid field type + var result4 = await notesManager.CreateNoteCustomTypeAsync(new NoteCustomTypeStoreRequest + { + NoteCustomType = "Test", + NoteCustomTypeValues = new[] { "Value 1" }, + NoteCustomFieldType = 10 + }); + Assert.IsNull(result4.Item1, "Expected null result for invalid field type"); + Assert.IsFalse(result4.Item2.Status, "Expected validation error status"); + } + + #endregion + + #region Bulk Create Tests + + [Test] + public void BulkCreateNotesTest() + { + var notesManager = new NotesManagerV5(CApiKey); + + var bulkRequest = new NoteStoreBulkRequest + { + Notes = new[] + { + new NoteStoreBulkItem + { + RouteId = _testRouteId, + StrNoteContents = "Bulk note 1 created at " + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), + AddressId = _testDestinationId, + DeviceType = "web" + }, + new NoteStoreBulkItem + { + RouteId = _testRouteId, + StrNoteContents = "Bulk note 2 created at " + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), + AddressId = _testDestinationId, + DeviceType = "web" + }, + new NoteStoreBulkItem + { + RouteId = _testRouteId, + StrNoteContents = "Bulk note 3 created at " + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), + AddressId = _testDestinationId, + StrUpdateType = "dropoff" + } + }, + DeviceType = "web" + }; + + var result = notesManager.BulkCreateNotes(bulkRequest, out ResultResponse resultResponse); + + Assert.IsNotNull(result, "BulkCreateNotes failed"); + Assert.IsTrue(result.Status, "Bulk create status is false"); + Assert.IsTrue(result.Async, "Expected async flag to be true"); + } + + [Test] + public async Task BulkCreateNotesAsyncTest() + { + var notesManager = new NotesManagerV5(CApiKey); + + var bulkRequest = new NoteStoreBulkRequest + { + Notes = new[] + { + new NoteStoreBulkItem + { + RouteId = _testRouteId, + StrNoteContents = "Async bulk note 1 created at " + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), + AddressId = _testDestinationId, + DeviceType = "web" + }, + new NoteStoreBulkItem + { + RouteId = _testRouteId, + StrNoteContents = "Async bulk note 2 created at " + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), + AddressId = _testDestinationId, + DevLat = 38.024654, + DevLng = -77.338814 + } + }, + DeviceType = "api" + }; + + var result = await notesManager.BulkCreateNotesAsync(bulkRequest); + + Assert.IsNotNull(result.Item1, "BulkCreateNotesAsync failed"); + Assert.IsTrue(result.Item1.Status, "Bulk create status is false"); + Assert.IsTrue(result.Item1.Async, "Expected async flag to be true"); + } + + [Test] + public void BulkCreateNotesValidationTest() + { + var notesManager = new NotesManagerV5(CApiKey); + + // Test with null request + var result1 = notesManager.BulkCreateNotes(null, out ResultResponse resultResponse1); + Assert.IsNull(result1, "Expected null result for null request"); + Assert.IsFalse(resultResponse1.Status, "Expected validation error status"); + Assert.IsTrue(resultResponse1.Messages.ContainsKey("Error"), "Expected error messages"); + + // Test with null notes array + var request2 = new NoteStoreBulkRequest + { + Notes = null, + DeviceType = "web" + }; + + var result2 = notesManager.BulkCreateNotes(request2, out ResultResponse resultResponse2); + Assert.IsNull(result2, "Expected null result for null notes array"); + Assert.IsFalse(resultResponse2.Status, "Expected validation error status"); + Assert.IsTrue(resultResponse2.Messages.ContainsKey("Error"), "Expected error messages"); + + // Test with empty notes array + var request3 = new NoteStoreBulkRequest + { + Notes = new NoteStoreBulkItem[] { }, + DeviceType = "web" + }; + + var result3 = notesManager.BulkCreateNotes(request3, out ResultResponse resultResponse3); + Assert.IsNull(result3, "Expected null result for empty notes array"); + Assert.IsFalse(resultResponse3.Status, "Expected validation error status"); + Assert.IsTrue(resultResponse3.Messages.ContainsKey("Error"), "Expected error messages"); + + // Test with missing route_id in one note + var request4 = new NoteStoreBulkRequest + { + Notes = new[] + { + new NoteStoreBulkItem + { + RouteId = _testRouteId, + StrNoteContents = "Valid note" + }, + new NoteStoreBulkItem + { + // Missing RouteId + StrNoteContents = "Invalid note - missing route_id" + } + }, + DeviceType = "web" + }; + + var result4 = notesManager.BulkCreateNotes(request4, out ResultResponse resultResponse4); + Assert.IsNull(result4, "Expected null result for missing route_id"); + Assert.IsFalse(resultResponse4.Status, "Expected validation error status"); + Assert.IsTrue(resultResponse4.Messages.ContainsKey("Error"), "Expected error messages"); + Assert.IsTrue(resultResponse4.Messages["Error"][0].Contains("index 1"), "Expected error to mention index 1"); + + // Test with missing note contents in one note + var request5 = new NoteStoreBulkRequest + { + Notes = new[] + { + new NoteStoreBulkItem + { + RouteId = _testRouteId, + StrNoteContents = "Valid note" + }, + new NoteStoreBulkItem + { + RouteId = _testRouteId + // Missing StrNoteContents + } + }, + DeviceType = "web" + }; + + var result5 = notesManager.BulkCreateNotes(request5, out ResultResponse resultResponse5); + Assert.IsNull(result5, "Expected null result for missing note contents"); + Assert.IsFalse(resultResponse5.Status, "Expected validation error status"); + Assert.IsTrue(resultResponse5.Messages.ContainsKey("Error"), "Expected error messages"); + Assert.IsTrue(resultResponse5.Messages["Error"][0].Contains("index 1"), "Expected error to mention index 1"); + + // Test with empty route_id in first note + var request6 = new NoteStoreBulkRequest + { + Notes = new[] + { + new NoteStoreBulkItem + { + RouteId = "", + StrNoteContents = "Note with empty route_id" + } + }, + DeviceType = "web" + }; + + var result6 = notesManager.BulkCreateNotes(request6, out ResultResponse resultResponse6); + Assert.IsNull(result6, "Expected null result for empty route_id"); + Assert.IsFalse(resultResponse6.Status, "Expected validation error status"); + Assert.IsTrue(resultResponse6.Messages["Error"][0].Contains("index 0"), "Expected error to mention index 0"); + + // Test with whitespace route_id + var request7 = new NoteStoreBulkRequest + { + Notes = new[] + { + new NoteStoreBulkItem + { + RouteId = " ", + StrNoteContents = "Note with whitespace route_id" + } + }, + DeviceType = "web" + }; + + var result7 = notesManager.BulkCreateNotes(request7, out ResultResponse resultResponse7); + Assert.IsNull(result7, "Expected null result for whitespace route_id"); + Assert.IsFalse(resultResponse7.Status, "Expected validation error status"); + + // Test with empty note contents + var request8 = new NoteStoreBulkRequest + { + Notes = new[] + { + new NoteStoreBulkItem + { + RouteId = _testRouteId, + StrNoteContents = "" + } + }, + DeviceType = "web" + }; + + var result8 = notesManager.BulkCreateNotes(request8, out ResultResponse resultResponse8); + Assert.IsNull(result8, "Expected null result for empty note contents"); + Assert.IsFalse(resultResponse8.Status, "Expected validation error status"); + + // Test with whitespace note contents + var request9 = new NoteStoreBulkRequest + { + Notes = new[] + { + new NoteStoreBulkItem + { + RouteId = _testRouteId, + StrNoteContents = " " + } + }, + DeviceType = "web" + }; + + var result9 = notesManager.BulkCreateNotes(request9, out ResultResponse resultResponse9); + Assert.IsNull(result9, "Expected null result for whitespace note contents"); + Assert.IsFalse(resultResponse9.Status, "Expected validation error status"); + } + + [Test] + public async Task BulkCreateNotesAsyncValidationTest() + { + var notesManager = new NotesManagerV5(CApiKey); + + // Test with null request + var result1 = await notesManager.BulkCreateNotesAsync(null); + Assert.IsNull(result1.Item1, "Expected null result for null request"); + Assert.IsFalse(result1.Item2.Status, "Expected validation error status"); + Assert.IsTrue(result1.Item2.Messages.ContainsKey("Error"), "Expected error messages"); + + // Test with null notes array + var result2 = await notesManager.BulkCreateNotesAsync(new NoteStoreBulkRequest + { + Notes = null + }); + Assert.IsNull(result2.Item1, "Expected null result for null notes array"); + Assert.IsFalse(result2.Item2.Status, "Expected validation error status"); + + // Test with empty notes array + var result3 = await notesManager.BulkCreateNotesAsync(new NoteStoreBulkRequest + { + Notes = new NoteStoreBulkItem[] { } + }); + Assert.IsNull(result3.Item1, "Expected null result for empty notes array"); + Assert.IsFalse(result3.Item2.Status, "Expected validation error status"); + + // Test with missing route_id + var result4 = await notesManager.BulkCreateNotesAsync(new NoteStoreBulkRequest + { + Notes = new[] + { + new NoteStoreBulkItem + { + StrNoteContents = "Note without route_id" + } + } + }); + Assert.IsNull(result4.Item1, "Expected null result for missing route_id"); + Assert.IsFalse(result4.Item2.Status, "Expected validation error status"); + + // Test with missing note contents + var result5 = await notesManager.BulkCreateNotesAsync(new NoteStoreBulkRequest + { + Notes = new[] + { + new NoteStoreBulkItem + { + RouteId = _testRouteId + } + } + }); + Assert.IsNull(result5.Item1, "Expected null result for missing note contents"); + Assert.IsFalse(result5.Item2.Status, "Expected validation error status"); + } + + [Test] + public void BulkCreateNotesWithOptionalFieldsTest() + { + var notesManager = new NotesManagerV5(CApiKey); + + var bulkRequest = new NoteStoreBulkRequest + { + Notes = new[] + { + new NoteStoreBulkItem + { + RouteId = _testRouteId, + StrNoteContents = "Note with all optional fields", + AddressId = _testDestinationId, + DevLat = 38.024654, + DevLng = -77.338814, + RemoteSpeed = 50.5, + RemoteAltitude = 100.0, + StrUpdateType = "dropoff", + DeviceType = "iphone", + UtcTime = (int)DateTimeOffset.UtcNow.ToUnixTimeSeconds() + }, + new NoteStoreBulkItem + { + RouteId = _testRouteId, + StrNoteContents = "Note with minimal fields", + DeviceType = "android_phone" + } + }, + DeviceType = "web" + }; + + var result = notesManager.BulkCreateNotes(bulkRequest, out ResultResponse resultResponse); + + Assert.IsNotNull(result, "BulkCreateNotes with optional fields failed"); + Assert.IsTrue(result.Status, "Bulk create status is false"); + } + + #endregion + } +} + diff --git a/route4me-csharp-sdk/Route4MeSdkV5UnitTest/appsettings.json b/route4me-csharp-sdk/Route4MeSdkV5UnitTest/appsettings.json index 5d39f94d..a321a3c3 100644 --- a/route4me-csharp-sdk/Route4MeSdkV5UnitTest/appsettings.json +++ b/route4me-csharp-sdk/Route4MeSdkV5UnitTest/appsettings.json @@ -1,7 +1,7 @@ { "settings": { - "actualApiKey": "33333333333333333333333333333333", - "demoApiKey": "11111111111111111111111111111111", + "actualApiKey": "A31AC012AF983C07C772A84693D6BE17", + "demoApiKey": "A31AC012AF983C07C772A84693D6BE17", "test_acc_mail": "someemail@route4me.com", "test_acc_psw": "somepassword!" }