Company management
This document describes how to create and manage companies in TGM Manager Server, including storage and OCR configuration.
Table of Contents¶
- Overview
- Multi-Tenancy Context
- Creating a Company
- Storage Configuration
- OCR Configuration
- Manual Database Operations
- API Reference
- Troubleshooting
Overview¶
Companies are organizational units within TGM Manager that group users and configure features.
Key Points:¶
- Basic information (name, address, contact details)
- Feature configurations (SSO, 2FA, AI features)
- Storage configuration (default, S3, MinIO)
- OCR configuration (document text extraction)
- Associated locations, users, and data
Multi-Tenancy Context¶
TGM Manager uses a two-level multi-tenancy architecture. Understanding the hierarchy is crucial:
┌─────────────────────────────────────────────────────────────────┐
│ Platform (Master Database) │
│ Contains: Clients registry, platform administration │
└─────────────────────────────────────────────────────────────────┘
│
┌──────────────────────┼──────────────────────┐
▼ ▼ ▼
┌───────────────┐ ┌───────────────┐ ┌───────────────┐
│ Client A │ │ Client B │ │ Client C │
│ Database │ │ Database │ │ Database │
├───────────────┤ ├───────────────┤ ├───────────────┤
│ Company 1 │ │ Company 1 │ │ Company 1 │
│ (settings) │ │ (settings) │ │ (settings) │
├───────────────┤ ├───────────────┤ ├───────────────┤
│ Sandbox │ │ Sandbox │ │ Sandbox │
│ (schema) │ │ (schema) │ │ (schema) │
└───────────────┘ └───────────────┘ └───────────────┘
Terminology¶
| Term | Description | Database Scope |
|---|---|---|
| Client | An organization with isolated database | Separate PostgreSQL database |
| Company | Configuration entity within a client | Table within client's database |
| Sandbox | Development/testing environment | Schema within client's database |
API Request Headers¶
All company management requests require multi-tenant headers:
curl -X GET "http://localhost:1337/api/companies" \
-H "Authorization: Bearer $JWT" \
-H "X-Client-ID: acme-corp"
| Header | Required | Description |
|---|---|---|
Authorization |
Yes | JWT or API token |
X-Client-ID |
Yes* | Client identifier |
X-Tenant-ID |
No | Sandbox (omit for production) |
*Can be resolved via subdomain: acme-corp.tgm-expert.com
Company Entity Fields¶
| Field | Type | Default | Description |
|---|---|---|---|
name |
String | Required | Company name (unique) |
address |
String | - | Physical address |
email |
String | - | Contact email |
phone |
String | - | Contact phone |
city |
String | - | City |
state |
String | - | State/Province |
country |
String | - | Country |
isActive |
Boolean | true |
Whether company is active |
appLanguage |
Enum | EN |
Default language (EN, FR, ES, etc.) |
measurementUnitSystem |
Enum | - | METRIC or IMPERIAL |
enable2FA |
Boolean | false |
Enable two-factor authentication |
ssoEnabled |
Boolean | false |
Enable Single Sign-On |
storageType |
String | default |
Storage provider type |
ocrEnabled |
Boolean | false |
Enable OCR processing |
Creating a Company¶
Via API (Recommended)¶
Note: Company creation happens within a client's database. Include the X-Client-ID header.
# Create a new company within a client's database
curl -X POST "http://localhost:1337/api/companies" \
-H "Authorization: Bearer $JWT" \
-H "Content-Type: application/json" \
-H "X-Client-ID: acme-corp" \
-d '{
"name": "Acme Corporation",
"address": "123 Main Street",
"email": "admin@acme.com",
"phone": "+1-555-0100",
"city": "New York",
"state": "NY",
"country": "USA",
"isActive": true,
"appLanguage": "EN",
"measurementUnitSystem": "IMPERIAL"
}'
Response:
{
"data": {
"id": 1,
"name": "Acme Corporation",
"address": "123 Main Street",
"email": "admin@acme.com",
"isActive": true,
"storageType": "default",
"ocrEnabled": false
},
"message": "Created successfully"
}
Required Permissions¶
- SUPER_ADMIN: Full access to all company operations
- ADMIN: Can create/update companies within their scope
- Users need
companies:createpermission
Storage Configuration¶
Each company can use their own storage provider. By default, companies use the platform's global storage.
Storage Types¶
| Type | Description | Use Case |
|---|---|---|
default |
Use platform storage | Most companies |
minio |
Self-hosted MinIO | On-premise deployments |
s3 |
AWS S3 | Cloud-native companies |
azure |
Azure Blob (future) | Azure-based companies |
Set Default Storage¶
To use the platform's default storage:
curl -X POST "http://localhost:1337/api/companies/{companyId}/storage-config/reset" \
-H "Authorization: Bearer $JWT"
Configure Custom Storage (S3)¶
curl -X PUT "http://localhost:1337/api/companies/{companyId}/storage-config" \
-H "Authorization: Bearer $JWT" \
-H "Content-Type: application/json" \
-d '{
"storageType": "s3",
"storageEndpoint": "https://s3.us-east-1.amazonaws.com",
"storageAccessKey": "AKIAIOSFODNN7EXAMPLE",
"storageSecretKey": "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY",
"storageBucket": "acme-tgm-files",
"storageRegion": "us-east-1",
"storagePublicUrl": "https://cdn.acme.com"
}'
Configure Custom Storage (MinIO)¶
curl -X PUT "http://localhost:1337/api/companies/{companyId}/storage-config" \
-H "Authorization: Bearer $JWT" \
-H "Content-Type: application/json" \
-d '{
"storageType": "minio",
"storageEndpoint": "https://minio.internal.acme.com:9000",
"storageAccessKey": "acmeAccessKey",
"storageSecretKey": "acmeSecretKey",
"storageBucket": "documents"
}'
Test Storage Connectivity¶
curl -X POST "http://localhost:1337/api/companies/{companyId}/storage-config/test" \
-H "Authorization: Bearer $JWT"
Response:
{
"data": {
"success": true,
"message": "Storage connectivity test passed",
"storageInfo": {
"type": "s3",
"bucket": "acme-tgm-files",
"region": "us-east-1"
}
}
}
OCR Configuration¶
Enable OCR for automatic document text extraction.
Enable OCR¶
curl -X POST "http://localhost:1337/api/companies/{companyId}/ocr-config/enable" \
-H "Authorization: Bearer $JWT"
Configure OCR Settings¶
curl -X PUT "http://localhost:1337/api/companies/{companyId}/ocr-config" \
-H "Authorization: Bearer $JWT" \
-H "Content-Type: application/json" \
-d '{
"ocrEnabled": true,
"ocrLanguage": "en",
"ocrAutoProcess": true,
"ocrFileTypes": "pdf,png,jpg,jpeg,tiff,docx"
}'
OCR Settings¶
| Setting | Default | Description |
|---|---|---|
ocrEnabled |
false |
Enable OCR for this company |
ocrLanguage |
en |
OCR language (en, fr, es, de, etc.) |
ocrAutoProcess |
true |
Auto-process files on upload |
ocrFileTypes |
pdf,png,jpg... |
Comma-separated file types to process |
Manual Database Operations¶
For direct database operations (use with caution).
Create Company via SQL¶
-- Insert a new company
INSERT INTO companies (
name,
address,
email,
phone,
city,
state,
country,
is_active,
app_language,
measurement_unit_system,
storage_type,
ocr_enabled,
created_at,
updated_at
) VALUES (
'Acme Corporation',
'123 Main Street',
'admin@acme.com',
'+1-555-0100',
'New York',
'NY',
'USA',
true,
'EN',
'IMPERIAL',
'default',
false,
NOW(),
NOW()
);
-- Get the created company ID
SELECT id, name FROM companies WHERE name = 'Acme Corporation';
Configure Storage via SQL¶
-- Set company to use AWS S3
UPDATE companies SET
storage_type = 's3',
storage_endpoint = 'https://s3.us-east-1.amazonaws.com',
storage_access_key = 'AKIAIOSFODNN7EXAMPLE',
storage_secret_key = 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY',
storage_bucket = 'acme-tgm-files',
storage_region = 'us-east-1',
storage_public_url = 'https://cdn.acme.com',
updated_at = NOW()
WHERE id = 1;
-- Reset to default storage
UPDATE companies SET
storage_type = 'default',
storage_endpoint = NULL,
storage_access_key = NULL,
storage_secret_key = NULL,
storage_bucket = NULL,
storage_region = NULL,
storage_public_url = NULL,
updated_at = NOW()
WHERE id = 1;
Enable OCR via SQL¶
-- Enable OCR for a company
UPDATE companies SET
ocr_enabled = true,
ocr_language = 'en',
ocr_auto_process = true,
ocr_file_types = 'pdf,png,jpg,jpeg,tiff,docx,doc',
updated_at = NOW()
WHERE id = 1;
View Company Configuration¶
-- View company with all configurations
SELECT
id,
name,
is_active,
app_language,
-- Storage config
storage_type,
storage_endpoint,
storage_bucket,
storage_region,
CASE WHEN storage_access_key IS NOT NULL THEN 'configured' ELSE 'not set' END as storage_credentials,
-- OCR config
ocr_enabled,
ocr_language,
ocr_auto_process,
ocr_file_types,
-- SSO config
sso_enabled,
sso_enforced,
-- Timestamps
created_at,
updated_at
FROM companies
WHERE id = 1;
Create Admin User for Company¶
After creating a company, you typically need to create an admin user:
-- Create admin user for the company
INSERT INTO users (
username,
email,
first_name,
last_name,
password,
role,
company_id,
is_active,
email_verified,
created_at,
updated_at
) VALUES (
'admin@acme.com',
'admin@acme.com',
'Admin',
'User',
'$2a$10$...', -- bcrypt hashed password
'ADMIN',
1, -- company_id
true,
true,
NOW(),
NOW()
);
Note: For password, use a bcrypt hash. Generate one via:
# Using htpasswd
htpasswd -nbBC 10 "" "yourpassword" | tr -d ':\n' | sed 's/$2y/$2a/'
# Or in Java/Spring context, the password will be hashed automatically via the API
API Reference¶
Company CRUD Operations¶
| Method | Endpoint | Description | Permission |
|---|---|---|---|
| GET | /api/companies |
List all companies | companies:read |
| GET | /api/companies/{id} |
Get company by ID | companies:read |
| POST | /api/companies |
Create company | companies:create |
| PUT | /api/companies/{id} |
Update company | companies:update |
| DELETE | /api/companies/{id} |
Delete company | companies:delete |
Storage Configuration¶
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/companies/{id}/storage-config |
Get storage config |
| PUT | /api/companies/{id}/storage-config |
Update storage config |
| POST | /api/companies/{id}/storage-config/reset |
Reset to default |
| POST | /api/companies/{id}/storage-config/test |
Test connectivity |
OCR Configuration¶
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/companies/{id}/ocr-config |
Get OCR config |
| PUT | /api/companies/{id}/ocr-config |
Update OCR config |
| POST | /api/companies/{id}/ocr-config/enable |
Enable OCR |
| POST | /api/companies/{id}/ocr-config/disable |
Disable OCR |
Troubleshooting¶
Company Creation Fails¶
Error: "Company name already exists" - Solution: Use a unique company name
Error: "Access denied"
- Solution: Ensure user has companies:create permission or SUPER_ADMIN role
Storage Configuration Issues¶
Error: "Storage connectivity test failed" - Check endpoint URL is correct and accessible - Verify access key and secret key - Ensure bucket exists - Check network connectivity and firewall rules
Error: "Bucket does not exist" - Create the bucket in your storage provider before configuring - Verify bucket name is correct (case-sensitive)
OCR Not Processing Files¶
Checklist:
1. Is ocrEnabled set to true for the company?
2. Is the OCR service running? Check /api/companies/ocr-status
3. Is the file type in ocrFileTypes list?
4. Is ocrAutoProcess enabled?
Cache Issues After Configuration Change¶
If configuration changes don't take effect immediately:
# Clear Redis cache (if direct access)
redis-cli FLUSHDB
# Or restart the application to clear all caches
Best Practices¶
New Company Setup Checklist¶
- Create the company with basic information
- Configure storage (default or custom)
- Test storage connectivity
- Enable OCR if document processing is needed
- Create admin user for the company
- Configure SSO if using external identity provider
- Set up locations for the company
- Invite users to the company
Security Recommendations¶
- Use dedicated IAM users/roles for S3 access
- Rotate storage credentials regularly
- Enable encryption at rest for storage buckets
- Use TLS for all MinIO connections
- Store credentials encrypted in database
- Limit admin access to storage configuration
Multi-Tenant Isolation¶
Each company's data is isolated:
- Files stored in separate buckets or prefixes
- Database queries filtered by company_id
- API responses scoped to user's company
- Audit logs track all company actions