Metadata-Version: 2.4
Name: payspace_sdk
Version: 0.1.1
Summary: A simple SDK for interacting with the Payspace API
Home-page: https://github.com/jb-tech1999/payspace_sdk
Author: Jandre Badenhorst
Author-email: j.badenhorst@decisioninc.com
Classifier: Intended Audience :: Developers
Classifier: Operating System :: POSIX :: Linux
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Description-Content-Type: text/markdown
Requires-Dist: requests
Dynamic: author
Dynamic: author-email
Dynamic: classifier
Dynamic: description
Dynamic: description-content-type
Dynamic: home-page
Dynamic: requires-dist
Dynamic: summary

# PaySpace SDK

A comprehensive Python SDK for interacting with the PaySpace Deel Local Payroll API v2.0. This SDK provides a clean, Pythonic interface to all PaySpace API endpoints, making it easy to integrate payroll operations into your Python applications.

## Table of Contents

- [Features](#features)
- [Installation](#installation)
- [Quick Start](#quick-start)
- [Authentication](#authentication)
- [Environments](#environments)
- [API Coverage](#api-coverage)
- [Usage Examples](#usage-examples)
- [API Reference](#api-reference)
- [Rate Limits](#rate-limits)
- [Error Handling](#error-handling)
- [Contributing](#contributing)
- [License](#license)

## Features

- ✅ **Complete API v2.0 Coverage** - Supports all PaySpace API v2.0 endpoints
- 🔐 **OAuth 2.0 Authentication** - Secure authentication with client credentials or access tokens
- 🌍 **Multi-Environment Support** - Production, Staging, and Development environments
- 📊 **Comprehensive Data Access** - Employee info, payroll, leave, benefits, and more
- 🔍 **Lookup Tables** - Access to all reference data and lookup values
- ⚡ **Simple Interface** - Clean, intuitive methods for all operations
- 🐍 **Type Hints** - Full type hints for better IDE support
- 📝 **Extensive Documentation** - Detailed docstrings and examples

## Installation

### From Source

```bash
git clone https://github.com/jb-tech1999/payspace_sdk.git
cd payspace_sdk
pip install -e .
```

### Requirements

- Python 3.7+
- requests

## Quick Start

```python
from payspace_sdk import PaySpaceClient, PaySpaceEnvironment

# Initialize the client with your credentials
client = PaySpaceClient(
    client_id="your_client_id",
    client_secret="your_client_secret",
    environment=PaySpaceEnvironment.PRODUCTION
)

# Authenticate
client.authenticate()

# Get available companies
companies = client.get_companies()
print(f"Found {len(companies)} companies")

# Get employee data
company_id = companies[0]['CompanyId']
employees = client.get_biographical(company_id, 2024, 1, 1)
print(f"Found {len(employees)} employees")
```

## Authentication

The SDK supports two authentication methods:

### 1. Client Credentials (Recommended)

```python
client = PaySpaceClient(
    client_id="your_client_id",
    client_secret="your_client_secret",
    environment=PaySpaceEnvironment.PRODUCTION
)

# Authenticate and get access token
token = client.authenticate()

# Get available companies (returned during authentication)
companies = client.get_available_companies()

# Or fetch companies via API
companies = client.get_companies()
```

### 2. Pre-existing Access Token

```python
client = PaySpaceClient(
    access_token="your_existing_token",
    environment=PaySpaceEnvironment.PRODUCTION
)

# No need to authenticate, token is already set
companies = client.get_companies()
```

## Environments

The SDK supports three environments:

| Environment | URL | Auth URL | Rate Limit |
|-------------|-----|----------|------------|
| **Production** | `https://api.payspace.com/odata/v2.0` | `https://identity.yourhcm.com/connect/token` | 2000 req/min |
| **Staging** | `https://apistaging.payspace.com/odata/v2.0` | `https://staging-identity.yourhcm.com/connect/token` | 200 req/min |
| **Development** | `https://localhost:44393/odata/v2.0` | `https://localhost:44392/connect/token` | No limit |

### Using Different Environments

```python
from payspace_sdk import PaySpaceClient, PaySpaceEnvironment

# Production (default)
prod_client = PaySpaceClient(
    client_id="id",
    client_secret="secret",
    environment=PaySpaceEnvironment.PRODUCTION
)

# Staging
staging_client = PaySpaceClient(
    client_id="id",
    client_secret="secret",
    environment=PaySpaceEnvironment.STAGING
)

# Development
dev_client = PaySpaceClient(
    client_id="id",
    client_secret="secret",
    environment=PaySpaceEnvironment.DEVELOPMENT
)

# Custom environment
custom_client = PaySpaceClient(
    client_id="id",
    client_secret="secret",
    environment="https://custom-api.example.com/odata/v2.0",
    auth_url="https://custom-auth.example.com/connect/token"
)
```

## API Coverage

The SDK provides comprehensive coverage of the PaySpace API v2.0:

### Company Information
- `get_companies()` - Get all companies
- `get_company_names()` - Get company names
- `get_company_frequency()` - Get pay frequencies
- `get_company_runs()` - Get payroll runs
- `get_company_training_courses()` - Get training courses
- `get_company_custom_forms()` - Get custom forms
- `get_company_review_process()` - Get review processes
- `get_organization_units()` - Get organization units
- `get_currency_exchange_rates()` - Get exchange rates

### Employee Information
- `get_biographical()` - Get employee biographical data
- `get_biographical_including_terminated()` - Include terminated employees
- `get_employee_addresses()` - Get employee addresses
- `get_employee_bank_details()` - Get bank account details
- `get_employee_dependants()` - Get dependant information
- `get_employee_positions()` - Get position details
- `get_employee_attachments()` - Get employee documents
- `get_employee_skills()` - Get employee skills
- `get_employee_qualifications()` - Get qualifications
- `get_employee_suspension()` - Get suspension records

### Payroll & Compensation
- `get_payslips()` - Get employee payslips
- `get_payslip_lines()` - Get detailed payslip lines
- `get_costed_payslip_lines()` - Get costed payslip lines
- `get_pay_rate_details()` - Get pay rate information
- `get_financial_transactions()` - Get financial transactions
- `get_recurring_costing_split()` - Get recurring costs

### Leave Management
- `get_leave_applications()` - Get leave applications
- `get_leave_adjustments()` - Get leave adjustments
- `get_payslip_leave_balances()` - Get leave balances

### Benefits & Deductions
- `get_employee_medical()` - Medical aid information
- `get_employee_pension_fund()` - Pension fund details
- `get_employee_disability()` - Disability insurance
- `get_employee_group_life()` - Group life insurance
- `get_employee_income_protection()` - Income protection
- `get_employee_retirement_annuity()` - Retirement annuities
- `get_employee_garnishee()` - Garnishee orders
- `get_employee_loan()` - Employee loans
- `get_employee_saving()` - Savings schemes
- `get_employee_union()` - Union membership
- `get_employee_company_car_detail()` - Company car details
- `get_employee_bonus_provision()` - Bonus provisions

### Components & Custom Data
- `get_employee_component()` - Get employee components
- `get_employee_component_values()` - Get component values
- `get_employee_custom_forms()` - Get custom form data

### Training & Development
- `get_employee_training()` - Get training records
- `get_employee_incidents()` - Get incident records

### Performance Management
- `get_employee_review_template()` - Get review templates
- `get_employee_review_header()` - Get review headers
- `get_employee_review_kpa()` - Get KPA reviews

### Projects & Costing
- `get_employee_project()` - Get project assignments

### Tax Information
- `get_tax_profile()` - Get tax profile information

### Lookup Tables (60+ lookup endpoints)

The SDK provides access to all lookup/reference data tables:

```python
# Examples of lookup methods
client.get_account_type_lookup(company_id)
client.get_address_country_lookup(company_id)
client.get_citizenship_lookup(company_id)
client.get_classification_lookup(company_id)
client.get_currency_lookup(company_id)
client.get_employment_category_lookup(company_id)
client.get_job_lookup(company_id)
client.get_marital_status_lookup(company_id)
# ... and 50+ more lookup methods
```

## Usage Examples

### Example 1: Get Employee Payslips for Current Month

```python
from payspace_sdk import PaySpaceClient, PaySpaceAPIHelper

client = PaySpaceClient(
    client_id="your_client_id",
    client_secret="your_client_secret"
)

client.authenticate()
companies = client.get_companies()
company_id = companies[0]['CompanyId']

# Get current period
year, month = PaySpaceAPIHelper.get_current_period()

# Fetch payslips
payslips = client.get_payslips(company_id, year, month)

for payslip in payslips:
    print(f"Employee: {payslip['EmployeeNumber']}")
    print(f"Net Pay: {payslip['NetPay']}")
    print(f"Period: {payslip['Period']}")
    print("---")
```

### Example 2: Get Employee Details with Address

```python
# Get biographical data
year, month, day = PaySpaceAPIHelper.get_current_date()
employees = client.get_biographical(company_id, year, month, day)

# Get addresses
addresses = client.get_employee_addresses(company_id, year, month, day)

# Combine data
for employee in employees:
    emp_number = employee['EmployeeNumber']
    emp_addresses = [a for a in addresses if a['EmployeeNumber'] == emp_number]
    
    print(f"Employee: {employee['FirstName']} {employee['LastName']}")
    print(f"Number: {emp_number}")
    for addr in emp_addresses:
        print(f"Address: {addr.get('AddressLine1', '')}")
    print("---")
```

### Example 3: Search for Specific Company

```python
# Search by company name
companies = client.get_companies(company_search="Acme Corp")

if companies:
    company = companies[0]
    print(f"Found: {company['CompanyName']}")
    print(f"ID: {company['CompanyId']}")
    print(f"Group: {company['CompanyGroup']}")
```

### Example 4: Get Leave Applications for Date Range

```python
from datetime import date

start_date = date(2024, 1, 1)
end_date = date(2024, 1, 31)

leave_apps = client.get_leave_applications(
    company_id=company_id,
    year=2024,
    month=1,
    start_date=start_date,
    end_date=end_date
)

for leave in leave_apps:
    print(f"Employee: {leave['EmployeeNumber']}")
    print(f"Type: {leave['LeaveType']}")
    print(f"Days: {leave['Days']}")
    print(f"Status: {leave['Status']}")
    print("---")
```

### Example 5: Get Payroll Runs for a Frequency

```python
# Get company frequencies first
frequencies = client.get_company_frequency(company_id)

for freq in frequencies:
    print(f"Frequency: {freq['Frequency']}")
    
    # Get runs for this frequency
    runs = client.get_company_runs(company_id, freq['Frequency'])
    
    for run in runs:
        print(f"  Period: {run['Period']}")
        print(f"  Start: {run['PeriodStartDate']}")
        print(f"  End: {run['PeriodEndDate']}")
```

### Example 6: Using Lookup Tables

```python
# Get employment categories
categories = client.get_employment_category_lookup(company_id)
for cat in categories:
    print(f"{cat['Code']}: {cat['Description']}")

# Get job titles
jobs = client.get_job_lookup(company_id)
for job in jobs:
    print(f"{job['Code']}: {job['Description']}")

# Get currencies
currencies = client.get_currency_lookup(company_id)
for curr in currencies:
    print(f"{curr['Code']}: {curr['Description']}")
```

### Example 7: Working with Benefits

```python
# Get pension fund details
pension = client.get_employee_pension_fund(
    company_id=company_id,
    frequency="Monthly",
    period="2024-01"
)

for record in pension:
    print(f"Employee: {record['EmployeeNumber']}")
    print(f"Fund: {record['PensionFund']}")
    print(f"Employee Contribution: {record['EmployeeContribution']}")
    print(f"Employer Contribution: {record['EmployerContribution']}")
    print("---")

# Get medical aid details
medical = client.get_employee_medical(
    company_id=company_id,
    frequency="Monthly",
    period="2024-01"
)
```

## API Reference

### PaySpaceClient Class

The main client class for interacting with the PaySpace API.

#### Constructor

```python
PaySpaceClient(
    client_id: Optional[str] = None,
    client_secret: Optional[str] = None,
    access_token: Optional[str] = None,
    environment: str = PaySpaceEnvironment.PRODUCTION,
    auth_url: Optional[str] = None
)
```

**Parameters:**
- `client_id` - OAuth client ID
- `client_secret` - OAuth client secret
- `access_token` - Pre-existing OAuth 2.0 access token (optional)
- `environment` - API environment URL
- `auth_url` - Authentication URL (auto-configured based on environment)

#### Authentication Methods

##### `authenticate() -> str`
Authenticate with the API and retrieve an access token.

**Returns:** Access token string

**Raises:** `ValueError` if client_id/client_secret not provided

##### `get_available_companies() -> List[Dict]`
Get the list of companies available to the authenticated user.

**Returns:** List of company dictionaries

### PaySpaceAPIHelper Class

Helper class with utility methods.

#### Static Methods

##### `get_current_period() -> tuple`
Get the current year and month.

**Returns:** Tuple of (year, month)

##### `get_current_date() -> tuple`
Get the current year, month, and day.

**Returns:** Tuple of (year, month, day)

##### `format_date(dt: Union[date, datetime]) -> str`
Format a date for API queries (yyyy-MM-dd format).

**Parameters:**
- `dt` - Date or datetime object

**Returns:** Formatted date string

## Rate Limits

PaySpace API v2.0 enforces the following rate limits:

- **Production**: 2000 requests per minute
- **Staging**: 200 requests per minute
- **Development**: No enforced limit

When rate limits are exceeded, the API returns a 429 status code. Implement appropriate retry logic with exponential backoff when needed.

## Error Handling

The SDK raises exceptions for various error conditions:

```python
from requests.exceptions import HTTPError

try:
    client = PaySpaceClient(client_id="id", client_secret="secret")
    client.authenticate()
    companies = client.get_companies()
except ValueError as e:
    print(f"Configuration error: {e}")
except HTTPError as e:
    if e.response.status_code == 401:
        print("Authentication failed - check credentials")
    elif e.response.status_code == 429:
        print("Rate limit exceeded - wait and retry")
    elif e.response.status_code == 404:
        print("Resource not found")
    else:
        print(f"HTTP error: {e}")
except Exception as e:
    print(f"Unexpected error: {e}")
```

### Common Error Codes

- **400 Bad Request** - Invalid parameters or malformed request
- **401 Unauthorized** - Invalid or expired access token
- **403 Forbidden** - Insufficient permissions
- **404 Not Found** - Resource does not exist
- **429 Too Many Requests** - Rate limit exceeded
- **500 Internal Server Error** - Server-side error

## API v2.0 Breaking Changes

If you're migrating from v1.0, note these changes:

1. **Lookup Endpoints**: All lookup endpoints now require the `/Lookup/` prefix
2. **Date Format**: Dates must be in `yyyy-MM-dd` format for Edm.Date types
3. **OData Version**: Requires OData 4.0 headers
4. **Rate Limits**: Different limits for production (2000/min) vs staging (200/min)

## Best Practices

### 1. Reuse Client Instances

```python
# Good - reuse the same client
client = PaySpaceClient(client_id="id", client_secret="secret")
client.authenticate()

companies = client.get_companies()
for company in companies:
    employees = client.get_biographical(company['CompanyId'], 2024, 1, 1)
```

### 2. Use Environment Variables

```python
import os

client = PaySpaceClient(
    client_id=os.getenv("PAYSPACE_CLIENT_ID"),
    client_secret=os.getenv("PAYSPACE_CLIENT_SECRET"),
    environment=os.getenv("PAYSPACE_ENV", PaySpaceEnvironment.PRODUCTION)
)
```

### 3. Handle Pagination for Large Datasets

```python
# Some endpoints return large datasets
# The API uses OData pagination with @odata.nextLink
def get_all_employees(client, company_id):
    employees = []
    response = client.get_biographical(company_id, 2024, 1, 1)
    employees.extend(response)
    
    # Note: Current SDK returns 'value' list
    # If you need to handle nextLink, you may need to access raw response
    return employees
```

### 4. Cache Lookup Data

```python
# Lookup data rarely changes - cache it
lookup_cache = {}

def get_cached_lookup(client, company_id, lookup_name):
    """
    Cache lookup data to avoid repeated API calls.
    lookup_name should be like 'currency', 'job', 'marital_status', etc.
    """
    cache_key = f"{company_id}_{lookup_name}"
    if cache_key not in lookup_cache:
        try:
            method = getattr(client, f"get_{lookup_name}_lookup")
            lookup_cache[cache_key] = method(company_id)
        except AttributeError:
            raise ValueError(f"Unknown lookup type: {lookup_name}")
    return lookup_cache[cache_key]

# Usage example
currencies = get_cached_lookup(client, company_id, "currency")
jobs = get_cached_lookup(client, company_id, "job")
```

## Troubleshooting

### Authentication Issues

**Problem**: `ValueError: client_id and client_secret are required`

**Solution**: Ensure you provide both client_id and client_secret when initializing the client.

**Problem**: 401 Unauthorized error

**Solution**: 
- Verify your credentials are correct
- Check that you're using the right environment
- Ensure your token hasn't expired (get a new one with `authenticate()`)

### Connection Issues

**Problem**: Connection timeout or refused

**Solution**:
- For Development environment, ensure the local API is running
- Check your network connection
- Verify the environment URL is correct

### Data Not Found

**Problem**: Empty results when you expect data

**Solution**:
- Verify the company_id is correct
- Check the date parameters (year, month, day)
- Ensure the company has data for the requested period
- Verify the frequency matches the company's pay frequency

## Contributing

Contributions are welcome! Please feel free to submit issues or pull requests.

### Development Setup

```bash
git clone https://github.com/jb-tech1999/payspace_sdk.git
cd payspace_sdk
pip install -e .
```

## License

This project is open source. Please check the repository for license details.

## Support

For issues, questions, or contributions, please visit:
- GitHub: [https://github.com/jb-tech1999/payspace_sdk](https://github.com/jb-tech1999/payspace_sdk)
- Issues: [https://github.com/jb-tech1999/payspace_sdk/issues](https://github.com/jb-tech1999/payspace_sdk/issues)

## Disclaimer

This SDK is provided as-is. Always test thoroughly in a staging environment before using in production. Ensure you comply with PaySpace's terms of service and API usage guidelines.
