🔐
Auth Module
app/Modules/Auth/
Handles all authentication concerns — login, logout, token management, 2FA, device sessions, password reset, and OAuth2 integration using Laravel Sanctum.
File Structure
Auth/ ├── Controllers/ │ ├── AuthController.php # login, logout, refresh │ ├── PasswordController.php # forgot, reset password │ └── TwoFactorController.php # 2FA setup/verify ├── Services/ │ ├── AuthService.php │ └── TokenService.php ├── Actions/ │ ├── LoginAction.php │ └── LogoutAction.php ├── DTOs/ │ └── LoginDTO.php ├── Events/ │ ├── UserLoggedIn.php │ └── UserLoggedOut.php ├── Listeners/ │ └── LogAuthActivity.php ├── Requests/ │ ├── LoginRequest.php │ └── ResetPasswordRequest.php ├── Enums/ │ └── TokenAbilityEnum.php ├── Middleware/ │ ├── EnsureTokenIsValid.php │ └── ThrottleLoginAttempts.php └── routes.php
Key Service Code
// app/Modules/Auth/Services/AuthService.php class AuthService { public function login(LoginDTO $dto): array { if (!Auth::attempt($dto->credentials())) { throw new AuthenticationException('Invalid credentials'); } $user = Auth::user(); $this->auditLog->record($user, 'login'); return [ 'token' => $this->tokenService->issue($user, $dto->abilities), 'user' => new UserResource($user), 'expires' => now()->addHours(config('auth.token_ttl')), ]; } }
Features
🔑
Sanctum Tokens
Scoped API token abilities with expiry
📱
2FA (TOTP)
Google Authenticator / Authy compatible
🔄
Token Refresh
Sliding expiry, refresh without re-login
đŸ’ģ
Device Sessions
Per-device token management & revocation
đŸšĢ
Brute Force Guard
Redis-backed exponential throttling
📧
Password Reset
Signed URL email with 60min expiry
API Endpoints
POST /api/v1/auth/login POST /api/v1/auth/logout POST /api/v1/auth/refresh POST /api/v1/auth/password/forgot POST /api/v1/auth/password/reset POST /api/v1/auth/2fa/setup POST /api/v1/auth/2fa/verify GET /api/v1/auth/devices
đŸ‘Ĩ
Users Module
app/Modules/Users/
Complete user lifecycle management — onboarding, profile, territory assignment, hierarchy mapping, manager relationships, and BPE/ABM/RBM role-specific profile data.
Key Models & Relationships
// app/Modules/Users/Models/User.php class User extends Authenticatable { use HasApiTokens, HasRoles, HasAuditLogs, SoftDeletes; protected $casts = [ 'metadata' => 'array', 'territory_codes' => 'array', 'email_verified_at' => 'datetime', ]; // Hierarchy: BPE → ABM → RBM → SRBM → ZBM → AGM → COO → MD public function manager(): BelongsTo { return $this->belongsTo(User::class, 'manager_id'); } public function subordinates(): HasMany { return $this->hasMany(User::class, 'manager_id'); } public function claims(): HasMany { return $this->hasMany(Claim::class, 'claimant_id'); } public function territories(): HasMany { return $this->hasMany(UserTerritory::class); } }
Features
đŸ—‚ī¸
Hierarchy Tree
Recursive manager chain with depth limits
đŸ—ēī¸
Territory Mapping
Zone → Region → Area → Territory assignment
📋
Profile Types
BPE/BPEC/BPEP specific profile metadata
🔄
Bulk Import
Excel upload for batch user onboarding
đŸ›Ąī¸
Roles & RBAC Module
app/Modules/Roles/
Spatie-powered RBAC with hierarchical roles, granular permission matrices, policy guards, and approval threshold configuration per role level.
Role Hierarchy & Approval Thresholds
// app/Modules/Roles/Enums/RoleEnum.php enum RoleEnum: string { case BPE = 'bpe'; // L1 — Submit only case BPEC = 'bpec'; // L1 — Channel case BPEP = 'bpep'; // L1 — Pharma case ABM = 'abm'; // L2 — Area approval case RBM = 'rbm'; // L3 — Regional case SRBM = 'srbm'; // L4 — Senior Regional case ZBM = 'zbm'; // L5 — Zonal case AGM = 'agm'; // L6 — ₹5L limit case COO = 'coo'; // L7 — ₹25L limit case MD = 'md'; // L8 — No limit case BIS = 'bis_team'; // SYS — Admin case HO = 'ho'; // SYS — Head Office public function approvalLimit(): ?int { return match($this) { self::AGM => 500000, self::COO => 2500000, self::MD => null, default => 0, }; } }
Permissions Matrix
📋
claims.submit
BPE, BPEC, BPEP
✅
claims.validate
ABM, RBM, BIS
âœ”ī¸
claims.approve
ABM → MD (threshold)
💹
rates.manage
BIS, HO, AGM+
đŸ’ŗ
disbursement.process
Finance, AGM+
📊
reports.export
RBM+ all roles
âš™ī¸
system.configure
BIS, MD only
📝
audit.view
AGM, COO, MD, BIS
💹
Rate Management Module
app/Modules/RateManagement/
Manages primary and secondary rate masters with product-wise rate quotation, effective date management, version control, and approval workflow for rate changes.
Rate Master Service
// app/Modules/RateManagement/Services/RateMasterService.php class RateMasterService { public function getEffectiveRate( int $productId, RateTypeEnum $type, Carbon $date = null ): RateMaster { $date ??= now(); return Cache::remember( "rate:{$productId}:{$type->value}:{$date->format('Y-m-d')}", now()->addHours(6), fn() => $this->rateRepo->findEffective($productId, $type, $date) ); } public function createRateVersion(CreateRateDTO $dto): RateMaster { // Supersede current rate, create new version DB::transaction(function() use ($dto) { $this->rateRepo->expireCurrent($dto->productId, $dto->rateType, $dto->effectiveFrom); $rate = $this->rateRepo->create($dto); event(new RateMasterUpdated($rate)); Cache::tags('rates')->flush(); return $rate; }); } }
Features
📊
Rate Versioning
Full history with effective date ranges
🔄
Rate Quotation Workflow
New rate proposal → ABM/RBM/AGM approval
⚡
Redis Cache
Effective rate cached with auto-invalidation
đŸ“Ĩ
Bulk Import
Excel/CSV rate import with validation
🔔
Rate Change Alerts
Notify affected BPEs on rate updates
🌐
Zone-wise Rates
Territory-specific rate overrides
📑
Claims Module
app/Modules/Claims/
Central claim lifecycle management for BPE/BPEC/BPEP workflows. Handles draft creation, submission, validation, multi-level approval, and final disbursement with complete state machine management.
Claim State Machine
// app/Modules/Claims/Enums/ClaimStatusEnum.php enum ClaimStatusEnum: string { case DRAFT = 'draft'; case SUBMITTED = 'submitted'; case UNDER_VALIDATION = 'under_validation'; case VALIDATION_FAILED = 'validation_failed'; case PENDING_APPROVAL = 'pending_approval'; case PARTIALLY_APPROVED = 'partially_approved'; case APPROVED = 'approved'; case REJECTED = 'rejected'; case PENDING_DISBURSEMENT = 'pending_disbursement'; case DISBURSED = 'disbursed'; case CANCELLED = 'cancelled'; case ON_HOLD = 'on_hold'; public function allowedTransitions(): array { return match($this) { self::DRAFT => [self::SUBMITTED, self::CANCELLED], self::SUBMITTED => [self::UNDER_VALIDATION, self::CANCELLED], self::UNDER_VALIDATION => [self::PENDING_APPROVAL, self::VALIDATION_FAILED], self::PENDING_APPROVAL => [self::APPROVED, self::REJECTED, self::ON_HOLD], self::APPROVED => [self::PENDING_DISBURSEMENT], self::PENDING_DISBURSEMENT => [self::DISBURSED], default => [], }; } }
Claim Service Core
// app/Modules/Claims/Services/ClaimService.php class ClaimService { public function submit(CreateClaimDTO $dto, User $claimant): Claim { return DB::transaction(function() use ($dto, $claimant) { $claim = $this->claimRepo->create($dto, $claimant); // Attach line items with rate snapshot $this->lineItemService->attachWithRateSnapshot($claim, $dto->lineItems); // Queue async: validation → calculation → approval routing ProcessClaimJob::dispatch($claim)->onQueue('claims'); event(new ClaimSubmitted($claim)); return $claim; }); } }
API Endpoints
POST /api/v1/claims GET /api/v1/claims GET /api/v1/claims/{id} PUT /api/v1/claims/{id} DELETE /api/v1/claims/{id} POST /api/v1/claims/{id}/submit POST /api/v1/claims/{id}/approve POST /api/v1/claims/{id}/reject GET /api/v1/claims/{id}/timeline GET /api/v1/claims/{id}/calculation
🧮
Calculation Engine Module
app/Modules/CalculationEngine/
Financial precision calculation engine handling primary vs secondary rate comparison, stockist margin calculations, sales return deductions, NRV adjustments, percentage-based approvals, and advance deductions using BCMath for exact decimal arithmetic.
Calculation Pipeline
// app/Modules/CalculationEngine/Services/CalculationService.php class CalculationService { use MoneyPrecisionTrait; // BCMath wrapper for 4dp precision public function calculate(Claim $claim): CalculationResultDTO { $lineItems = $claim->lineItems->map(fn($item) => $this->processLineItem($item) ); $grossAmount = $lineItems->sum('gross_amount'); $salesReturns = $this->salesReturnDeduction->calculate($claim); $stockistMargin = $this->marginCalculator->calculate($claim); $nrvAdjustment = $this->nrvService->adjust($claim); $advanceDeduction = $this->advanceService->getPreviousAdvance($claim->claimant_id); $netPayable = $this->bcSub( $this->bcSub($grossAmount, $salesReturns), $this->bcAdd($stockistMargin, $advanceDeduction) ); return new CalculationResultDTO( grossAmount: $this->bcRound($grossAmount, 4), salesReturnDeduction: $salesReturns, stockistMarginDeduction: $stockistMargin, nrvAdjustment: $nrvAdjustment, advanceDeduction: $advanceDeduction, netPayable: $this->bcRound($netPayable, 4), breakdown: $lineItems, ); } private function processLineItem(ClaimLineItem $item): array { // Use higher of primary or secondary rate $applicableRate = $this->bcMax($item->primary_rate, $item->secondary_rate); $grossAmount = $this->bcMul($item->quantity, $applicableRate); return [ 'line_item_id' => $item->id, 'applicable_rate' => $applicableRate, 'rate_basis' => $item->primary_rate > $item->secondary_rate ? 'primary' : 'secondary', 'gross_amount' => $grossAmount, ]; } }
Calculation Components
💲
Primary vs Secondary Rate
Automatic selection of higher applicable rate per line item
đŸ“Ļ
Stockist Margin
Configurable margin % deduction per stockist type
â†Šī¸
Sales Return Deduction
Cross-reference returns with ERP data
📉
NRV Adjustment
Net Realisable Value adjustments for pharma claims
💰
Advance Deduction
Previous cycle advance recovery from current claim
đŸŽ¯
% Approval
Percentage-based partial approval with justification
đŸ“Ŧ
Approval Workflow Module
app/Modules/ApprovalWorkflow/
Configurable multi-level approval chains with dynamic routing based on claim amount, type, and territory. Includes SLA tracking, auto-escalation, delegation, and reminder automation.
Workflow Engine
// app/Modules/ApprovalWorkflow/Services/ApprovalRoutingService.php class ApprovalRoutingService { public function buildApprovalChain(Claim $claim): Collection { $chain = collect(); $amount = $claim->net_payable; $claimant = $claim->claimant; // Level 1: ABM always required $chain->push($this->buildLevel($claimant->manager, level: 1, slaHours: 48)); // Level 2: RBM required for amounts > ₹10,000 if ($amount > 10000) { $chain->push($this->buildLevel($claimant->manager->manager, level: 2, slaHours: 72)); } // Level 3: ZBM for amounts > ₹50,000 if ($amount > 50000) { $zbm = $this->userRepo->findZBM($claimant->zone_id); $chain->push($this->buildLevel($zbm, level: 3, slaHours: 96)); } // Finance gate: AGM/COO/MD based on thresholds $chain->push(...$this->buildFinanceGate($amount)); return $chain; } }
Features
âąī¸
SLA Tracking
Per-level SLA with escalation on breach
â†—ī¸
Auto-Escalation
Skip to next level on SLA timeout
🔀
Delegation
Temporary approval authority delegation
📧
Reminders
Scheduled reminder notifications for pending approvals
✅
Validation Engine Module
app/Modules/ValidationEngine/
Comprehensive stockist-wise and pharmacy-wise validation with supply proof verification, cross-reference with ERP/sync data, duplicate detection, and configurable validation rules per claim type.
Validation Pipeline
// app/Modules/ValidationEngine/Services/ValidationPipelineService.php class ValidationPipelineService { private array $validators = [ StockistExistenceValidator::class, PharmacyMappingValidator::class, SupplyProofValidator::class, DuplicateClaimValidator::class, ERPDataCrossCheckValidator::class, ClaimPeriodValidator::class, DocumentCompletenessValidator::class, ]; public function run(Claim $claim): ValidationResult { $errors = collect(); foreach ($this->validators as $validatorClass) { $validator = app($validatorClass); $result = $validator->validate($claim); if (!$result->passed) { $errors->push(...$result->errors); if ($result->isCritical) break; // Stop on critical failure } } return new ValidationResult( passed: $errors->isEmpty(), errors: $errors->all(), ); } }
🔄
Auto-Sync Engine Module
app/Modules/AutoSync/
Selective data synchronization from ERP systems, Excel uploads, and external APIs. Handles field-level mapping, conflict resolution, retry logic, and detailed sync audit logs with selective field overwrite control.
Sync Configuration
// config/sync.php — Selective field sync rules return [ 'entities' => [ 'stockists' => [ 'source' => 'erp', 'sync_fields' => ['name', 'code', 'address', 'gst_number'], 'protected_fields' => ['custom_margin', 'internal_notes'], 'conflict_strategy' => 'erp_wins', // erp_wins | local_wins | manual 'schedule' => '0 2 * * *', // 2 AM daily ], 'products' => [ 'source' => 'erp', 'sync_fields' => ['name', 'sku', 'hsn_code', 'mrp'], 'protected_fields' => ['primary_rate', 'secondary_rate'], 'conflict_strategy' => 'manual', 'schedule' => '0 */6 * * *', // Every 6 hours ], ], ];
Features
đŸŽ¯
Selective Field Sync
Field-level control over what ERP can overwrite
⚡
Conflict Resolution
ERP-wins, local-wins, or manual review strategies
🔄
Retry Logic
Exponential backoff with dead letter queue
📋
Sync Audit Log
Full before/after record of every sync operation
📊
Excel Import
Batch Excel upload with row-level error reporting
🔔
Sync Alerts
Notify BIS team on sync failures or conflicts
đŸ’ŗ
Disbursement Module
app/Modules/Disbursement/
Manages all payment disbursement methods — reimbursement, cheque issuance, NEFT/IMPS online transfer, and gift card allocation. Supports batch disbursement with reconciliation and payment confirmation tracking.
Disbursement Service
// app/Modules/Disbursement/Services/DisbursementService.php class DisbursementService { public function process(Claim $claim, DisbursementDTO $dto): Disbursement { $handler = $this->resolveHandler($dto->method); return DB::transaction(function() use ($claim, $dto, $handler) { $disbursement = $this->repo->create($claim, $dto); // Process via method-specific handler $result = $handler->process($disbursement); $disbursement->update([ 'reference_number' => $result->referenceNumber, 'status' => DisbursementStatusEnum::PROCESSED, 'disbursed_at' => now(), ]); event(new ClaimDisbursed($claim, $disbursement)); return $disbursement; }); } private function resolveHandler(DisbursementMethodEnum $method): DisbursementHandlerInterface { return match($method) { DisbursementMethodEnum::CHEQUE => app(ChequeHandler::class), DisbursementMethodEnum::NEFT => app(NeftHandler::class), DisbursementMethodEnum::GIFT_CARD => app(GiftCardHandler::class), DisbursementMethodEnum::REIMBURSEMENT => app(ReimbursementHandler::class), }; } }
📁
Documents Module
app/Modules/Documents/
Secure document management for supply proofs, invoices, pharmacy bills, and signed forms. Stores on S3/MinIO with virus scanning, optional watermarking, OCR metadata extraction, and signed URL generation.
Features
â˜ī¸
S3/MinIO Storage
Presigned upload URLs, private buckets
đŸĻ 
Virus Scanning
ClamAV integration before storage
🔍
OCR Extraction
Invoice number, amount, date parsing
💧
Watermarking
Stamp APPROVED/REJECTED on documents
🔗
Signed URLs
Time-limited access URLs for download
📋
Version Control
Document revision history with reasons
📈
Reports Module
app/Modules/Reports/
Role-scoped dashboards and analytics with claim summaries, financial reports, territory performance, disbursement tracking, Excel/PDF exports, and scheduled report delivery.
Available Reports
📊
Claim Summary
By status, type, period, territory
💰
Financial Report
Gross vs net payable, deduction breakdown
đŸ—ēī¸
Territory Performance
Zone/region/area-wise claim analytics
âąī¸
TAT Analysis
Turn-around time by approval level
đŸ’ŗ
Disbursement Report
Payment method wise disbursement tracking
📅
Scheduled Delivery
Auto-email reports on cron schedule
📝
Audit Logs Module
app/Modules/AuditLogs/
Immutable audit trail for all financial transactions, RBAC changes, approval decisions, and data modifications. Stores before/after snapshots in append-only table with IP, user agent, and geolocation context.
Audit Trait Implementation
// app/Shared/Traits/HasAuditLogs.php trait HasAuditLogs { public static function bootHasAuditLogs(): void { static::updated(function (Model $model) { if ($model->wasChanged()) { AuditLog::create([ 'user_id' => auth()->id(), 'event' => 'updated', 'auditable_type' => $model::class, 'auditable_id' => $model->getKey(), 'old_values' => $model->getOriginal(), 'new_values' => $model->getChanges(), 'ip_address' => request()->ip(), 'user_agent' => request()->userAgent(), ]); } }); } }
🔔
Notifications Module
app/Modules/Notifications/
Multi-channel notification system with email, SMS, push, and in-app channels. Template management, delivery tracking, user preferences, bulk notifications, and retry on failure.
Notification Channels
📧 Email (SMTP/SES) 📱 SMS (Twilio/MSG91) 🔔 Push (FCM/APNs) đŸ’Ŧ In-App (Database) đŸ’Ŧ WhatsApp (optional) 🔴 Slack (Internal alerts)
🔌
Integrations Module
app/Modules/Integrations/
Pluggable adapter architecture for ERP systems (SAP/Tally), payment gateways, cloud storage providers, and email services. Implements circuit breaker pattern for fault tolerance and automatic retry with exponential backoff.
Adapter Pattern
// app/Modules/Integrations/Contracts/ERPAdapterInterface.php interface ERPAdapterInterface { public function fetchStockists(array $filters = []): Collection; public function fetchProducts(array $filters = []): Collection; public function fetchSalesData(Carbon $from, Carbon $to): Collection; public function pushDisbursement(Disbursement $disbursement): bool; public function healthCheck(): bool; } // Implementations: SAPAdapter, TallyAdapter, MockERPAdapter // Selected via config('integrations.erp.driver')
Supported Integrations
🏭
SAP / Tally ERP
Bidirectional sync with circuit breaker
📊
Excel Import
Laravel Excel with queue processing
đŸ’ŗ
Payment Gateway
NEFT/IMPS via bank API integration
â˜ī¸
AWS S3 / MinIO
Document storage with CDN delivery