Skip to main content

A Django app for tracking sales personnel, distributors, and retailers with Google Maps integration

Project description

Django SalesTrack

A comprehensive Django app for tracking sales personnel, distributors, and retailers with Google Maps integration and advanced filtering capabilities.

Features

  • Multi-tier Tracking: Track employees, distributors, and retailers with hierarchical relationships
  • Google Maps Integration: Interactive map visualization with custom markers and clustering
  • Advanced Filtering: Dynamic filter controls with individual marker counts
  • Performance Optimized: Bulk database queries and efficient data loading for large datasets
  • Activity Tracking: Log and visualize sales activities and visits
  • GeoJSON Support: Export tracking data in GeoJSON format
  • Responsive UI: Mobile-friendly interface with dynamic control states

Installation

1. Install the package

Basic installation:

pip install django-salestrack

With MySQL support:

pip install django-salestrack[mysql]

With REST API support:

pip install django-salestrack[api]

Full installation:

pip install django-salestrack[mysql,api]

2. Add to Django settings

Add 'salestrack' to your INSTALLED_APPS in settings.py:

INSTALLED_APPS = [
    ...
    'salestrack',
    ...
]

3. Configure URLs

Include the salestrack URLs in your main urls.py:

from django.urls import path, include

urlpatterns = [
    ...
    path('salestrack/', include('salestrack.urls')),
    ...
]

4. Database Setup

This app requires specific database models. Make sure you have models similar to:

  • SpUsers - User/Employee model
  • SpVisits - Visit tracking model
  • SpUserVisits - User visit relationships
  • SpActivityLogs - Activity logging model
class SpUsers(AbstractBaseUser):
    REQUIRED_FIELDS = []
    USERNAME_FIELD = 'official_email'

    salutation = models.CharField(max_length=10)
    first_name = models.CharField(max_length=50)
    middle_name = models.CharField(max_length=50,null=True)
    last_name = models.CharField(max_length=50)
    store_name = models.CharField(max_length=255, blank=True, null=True)
    store_image = models.CharField(max_length=100, blank=True, null=True)
    official_email = models.CharField(unique=True,max_length=100)
    primary_contact_number = models.CharField(max_length=25)
    password = models.CharField(max_length=255)
    emp_sap_id = models.CharField(max_length=50)
    organization_id = models.IntegerField()
    organization_name = models.CharField(max_length=222, blank=True, null=True)
    department_id = models.IntegerField()
    department_name = models.CharField(max_length=222, blank=True, null=True)
    role_id = models.IntegerField()
    role_name = models.CharField(max_length=222)
    reporting_to_id = models.IntegerField()
    reporting_to_name = models.CharField(max_length=255)

    beat_id = models.IntegerField(blank=True, null=True)
    beat_name = models.CharField(max_length=150, blank=True, null=True)

    reporting_to_emp_id = models.IntegerField(blank=True, null=True)
    profile_image = models.CharField(max_length=255, blank=True, null=True)
    device_id = models.CharField(max_length=50, blank=True, null=True)
    firebase_token = models.CharField(max_length=255, blank=True, null=True)
    web_auth_token = models.CharField(max_length=1024, blank=True, null=True)
    auth_otp = models.CharField(max_length=10, blank=True, null=True)
    last_login = models.DateTimeField(null=True)
    last_ip = models.CharField(max_length=255, blank=True, null=True)
    status = models.IntegerField(default=1)
    user_type = models.IntegerField(default=0)
    is_distributor = models.IntegerField(default=0)
    is_super_stockist = models.IntegerField(default=0)
    is_retailer = models.IntegerField(default=0)
    is_tagged = models.IntegerField(default=0)
    tagged_by = models.IntegerField(blank=True, null=True)
    tagged_date = models.DateTimeField(blank=True, null=True)
    periphery = models.CharField(max_length=255,blank=True, null=True)
    timing = models.CharField(max_length=100, blank=True, null=True)
    latitude = models.CharField(max_length=100, blank=True, null=True)
    longitude = models.CharField(max_length=100, blank=True, null=True)
    self_owned = models.IntegerField(default=0)
    retailer_type_id = models.IntegerField(blank=True, null=True)
    retailer_class_type_id = models.IntegerField(blank=True, null=True)
    purchase_milk_from_org = models.IntegerField(default=0, null=True)
    visit_date_time = models.DateTimeField(blank=True, null=True)
    monthly_potential = models.FloatField(blank=True, null=True)
    other_sales = models.FloatField(blank=True, null=True)
    same_brand_name = models.CharField(max_length=100 ,blank=True, null=True)
    other_brand_name = models.CharField(max_length=100 ,blank=True, null=True)
    previous_created_by = models.IntegerField(blank=True, null=True)
    created_by = models.IntegerField(blank=True, null=True)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    superuser = models.IntegerField(default=0)


class SpActivityLogs(models.Model):
    module = models.CharField(max_length=100, blank=True, null=True)
    sub_module = models.CharField(max_length=100, blank=True, null=True)
    heading = models.TextField()
    activity = models.TextField()
    user = models.ForeignKey('SpUsers', on_delete=models.CASCADE, related_name='activity_logs')
    user_name = models.CharField(max_length=150)
    icon = models.CharField(max_length=100, blank=True, null=True)
    platform = models.CharField(max_length=50)
    status = models.IntegerField(default=1)
    platform_icon = models.CharField(max_length=100)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    latitude = models.CharField(max_length=100, blank=True, null=True)
    longitude = models.CharField(max_length=100, blank=True, null=True)


class SpUserVisits(models.Model):
    user = models.ForeignKey(
        SpUsers,
        on_delete=models.CASCADE,
        related_name="distributor_visits",
        null=True,
        blank=True
    )

    employee = models.ForeignKey(
        SpUsers,
        on_delete=models.CASCADE,
        related_name="employee_visits",
        null=True,
        blank=True
    )
    checkin_datetime = models.DateTimeField()
    checkout_datetime = models.DateTimeField(blank=True, null=True)
    visit_status = models.IntegerField()
    latitude = models.CharField(max_length=100, blank=True, null=True)
    longitude = models.CharField(max_length=100, blank=True, null=True)
    reason_remark = models.CharField(max_length=250, blank=True, null=True)
    total_retail_time = models.TimeField(blank=True, null=True)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)


class SpVisits(models.Model):
    user = models.ForeignKey('SpUsers', on_delete=models.SET_NULL, blank=True, null=True)
    outlet_id = models.IntegerField()
    beat_id = models.IntegerField()
    checkin_datetime = models.DateTimeField()
    checkout_datetime = models.DateTimeField(blank=True, null=True)
    visit_status = models.IntegerField()
    quotation_status = models.IntegerField()
    reason_id = models.IntegerField(blank=True, null=True)
    reason_remark = models.CharField(max_length=250, blank=True, null=True)
    total_retail_time = models.TimeField(blank=True, null=True)
    no_order = models.IntegerField()
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

Configuration

Required Settings

Add these settings to your Django settings.py:

# Google Maps API Key (required)
GOOGLE_MAPS_API_KEY = 'your-google-maps-api-key'

# Database configuration (MySQL recommended)
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'your_database_name',
        'USER': 'your_database_user',
        'PASSWORD': 'your_database_password',
        'HOST': 'localhost',
        'PORT': '3306',
        'OPTIONS': {
            'init_command': "SET sql_mode='STRICT_TRANS_TABLES'"
        }
    }
}

Optional Settings

# Customize marker clustering (optional)
SALESTRACK_CLUSTER_MARKERS = True
SALESTRACK_MAX_MARKERS_PER_REQUEST = 1000

# Performance settings (optional)
SALESTRACK_BATCH_SIZE = 500
SALESTRACK_CACHE_TIMEOUT = 300  # seconds

Usage

Basic Setup

  1. Access the tracking interface: Navigate to /salestrack/ in your application

  2. API Endpoints:

    • /salestrack/user-markers/ - Get marker data
    • /salestrack/tracks.geo - Get GeoJSON track data

Model Requirements

Your models should follow this structure:

# Example model structure (adapt to your needs)
class SpUsers(models.Model):
    first_name = models.CharField(max_length=100)
    last_name = models.CharField(max_length=100)
    latitude = models.DecimalField(max_digits=10, decimal_places=8)
    longitude = models.DecimalField(max_digits=11, decimal_places=8)
    user_type = models.CharField(max_length=50)
    # ... other fields

class SpVisits(models.Model):
    user = models.ForeignKey(SpUsers, on_delete=models.CASCADE)
    visit_date = models.DateField()
    latitude = models.DecimalField(max_digits=10, decimal_places=8)
    longitude = models.DecimalField(max_digits=11, decimal_places=8)
    # ... other fields

Frontend Integration

The app includes a complete Google Maps interface with:

  • Dynamic marker loading and filtering
  • Marker type toggles with individual counts
  • Performance optimizations for large datasets
  • Automatic control state management during API calls

API Reference

GET /user-markers/

Returns marker data for map visualization.

Parameters:

  • date (optional): Filter by specific date (YYYY-MM-DD format)

Response:

{
  "employees": [...],
  "distributors": [...],
  "retailers": [...],
  "activities": [...]
}

GET /tracks.geo

Returns GeoJSON formatted tracking data.

Parameters:

  • date (optional): Filter by specific date

Response:

{
  "type": "FeatureCollection",
  "features": [...]
}

Performance Considerations

  • The app is optimized for datasets with 7,000+ markers
  • Uses bulk database queries to minimize N+1 problems
  • Implements client-side batching for large marker sets
  • Includes control disable/enable during API calls

Dependencies

Core dependencies:

  • Django >= 3.2 (compatible with Django 3.2, 4.x, and 5.x)
  • requests >= 2.20.0
  • python-dateutil >= 2.7.0

Optional dependencies:

  • mysqlclient >= 2.0.0 (install with [mysql])
  • django-mysql >= 4.5.0 (install with [mysql])
  • djangorestframework >= 3.13.0 (install with [api])

Browser Support

  • Chrome 60+
  • Firefox 60+
  • Safari 12+
  • Edge 79+

License

MIT License

Contributing

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add some amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

Support

For support, please open an issue in the GitHub repository or contact the maintainers.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

django_salestrack-1.0.2.tar.gz (24.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

django_salestrack-1.0.2-py3-none-any.whl (25.1 kB view details)

Uploaded Python 3

File details

Details for the file django_salestrack-1.0.2.tar.gz.

File metadata

  • Download URL: django_salestrack-1.0.2.tar.gz
  • Upload date:
  • Size: 24.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.3

File hashes

Hashes for django_salestrack-1.0.2.tar.gz
Algorithm Hash digest
SHA256 a057e4c78781e440e96127123070168875cbb0baed634dfc29ba462f4fbc70db
MD5 782a7f89c7b0ad175d136fffb008645b
BLAKE2b-256 4b55f3bf8c0ac31446db47ebd5f5c704a9a31e03c57cd63b2d9ac7321feea5ea

See more details on using hashes here.

File details

Details for the file django_salestrack-1.0.2-py3-none-any.whl.

File metadata

File hashes

Hashes for django_salestrack-1.0.2-py3-none-any.whl
Algorithm Hash digest
SHA256 60404d1e980bd4ccb56d3303465b6eb6296fb4b9a71432ecd211df9abfba18ea
MD5 a6e4cf847f1fc47ab217d9797688b141
BLAKE2b-256 16760a6cc018d9813a94e03ae77380d651b1d1395abdd5b7f65be4172e80be10

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page