azaharizaman / nexus-procurement
Framework-agnostic procurement management package for purchase requisitions, purchase orders, goods receipts, and 3-way matching
Requires
- php: ^8.2
- psr/log: ^3.0
This package is auto-updated.
Last update: 2026-05-05 03:08:46 UTC
README
Framework-agnostic procurement management package for Nexus ERP
The Procurement package provides a comprehensive, pure PHP solution for purchase requisitions, purchase orders, goods receipts, 3-way matching, and vendor quote management. It follows strict contract-driven design principles and integrates seamlessly with the Nexus monorepo architecture.
Features
- ✅ Pure PHP 8.3+ - No framework dependencies in core logic
- ✅ Contract-Driven - All data structures and operations defined via interfaces
- ✅ Purchase Requisition Management - Complete workflow from draft to approval to PO conversion
- ✅ Purchase Order Processing - Create POs from requisitions or directly with budget validation
- ✅ Goods Receipt Notes (GRN) - Record and validate received goods against purchase orders
- ✅ 3-Way Matching Engine - Validate Invoice-PO-GRN alignment (<500ms for 100 lines)
- ✅ Vendor Quote Management - RFQ process and quote comparison
- ✅ Segregation of Duties - Requester ≠ Approver ≠ Receiver ≠ Payment Authorizer
- ✅ Budget Controls - PO cannot exceed requisition by >10% without re-approval
- ✅ Multi-Tenant - Tenant-scoped requisitions, POs, and GRNs
Installation
composer require azaharizaman/nexus-procurement:"*@dev"
Architecture
Package Structure
packages/Procurement/
├── src/
│ ├── Contracts/ # 19 Interfaces
│ │ ├── ProcurementManagerInterface.php
│ │ ├── RequisitionInterface.php
│ │ ├── RequisitionLineInterface.php
│ │ ├── RequisitionRepositoryInterface.php
│ │ ├── PurchaseOrderInterface.php
│ │ ├── PurchaseOrderLineInterface.php
│ │ ├── PurchaseOrderRepositoryInterface.php
│ │ ├── GoodsReceiptNoteInterface.php
│ │ ├── GoodsReceiptLineInterface.php
│ │ ├── GoodsReceiptRepositoryInterface.php
│ │ ├── VendorQuoteInterface.php
│ │ ├── VendorQuoteRepositoryInterface.php
│ │ └── ... (7 analytics repository interfaces)
│ ├── Services/ # 6 Business Logic Services
│ │ ├── ProcurementManager.php
│ │ ├── RequisitionManager.php
│ │ ├── PurchaseOrderManager.php
│ │ ├── GoodsReceiptManager.php
│ │ ├── MatchingEngine.php
│ │ └── VendorQuoteManager.php
│ └── Exceptions/ # 10 Domain Exceptions
│ ├── ProcurementException.php
│ ├── RequisitionNotFoundException.php
│ ├── PurchaseOrderNotFoundException.php
│ ├── GoodsReceiptNotFoundException.php
│ ├── InvalidRequisitionDataException.php
│ ├── InvalidRequisitionStateException.php
│ ├── InvalidPurchaseOrderDataException.php
│ ├── InvalidGoodsReceiptDataException.php
│ ├── BudgetExceededException.php
│ └── UnauthorizedApprovalException.php
├── composer.json
├── LICENSE
└── README.md
Core Principles
-
Logic in Packages, Implementation in Applications
- Package defines what (interfaces, services, value objects)
- Application defines how (Eloquent models, repositories, migrations)
-
Framework Agnostic
- Zero Laravel dependencies in
src/ - No
Illuminate\*classes - No Eloquent models
- No database queries
- Zero Laravel dependencies in
-
Dependency Injection
- Constructor injection for all dependencies
- Interface-based dependencies only
Usage Examples
Create Purchase Requisition
use Nexus\Procurement\Contracts\ProcurementManagerInterface; $procurement = app(ProcurementManagerInterface::class); $requisition = $procurement->createRequisition( tenantId: 'tenant-001', requesterId: 'user-123', data: [ 'number' => 'REQ-2025-001', 'description' => 'Office supplies for Q1', 'department' => 'Administration', 'lines' => [ [ 'item_code' => 'PAPER-A4', 'description' => 'A4 Paper 500 sheets', 'quantity' => 10, 'unit' => 'box', 'estimated_unit_price' => 25.00, ], ], ] );
Approve Requisition (with Segregation of Duties)
use Nexus\Procurement\Exceptions\UnauthorizedApprovalException; try { $approvedRequisition = $procurement->approveRequisition( requisitionId: $requisition->getId(), approverId: 'manager-456' // Must NOT be the requester ); } catch (UnauthorizedApprovalException $e) { // BUS-PRO-0095 violation: Requester tried to approve own requisition }
Convert Requisition to Purchase Order
use Nexus\Procurement\Exceptions\BudgetExceededException; try { $po = $procurement->convertRequisitionToPO( tenantId: 'tenant-001', requisitionId: $requisition->getId(), creatorId: 'buyer-789', poData: [ 'number' => 'PO-2025-001', 'vendor_id' => 'vendor-xyz', 'lines' => [ [ 'requisition_line_id' => $requisition->getLines()[0]->getId(), 'quantity' => 10, 'unit_price' => 24.50, // Within 10% of estimate 'unit' => 'box', 'item_code' => 'PAPER-A4', 'description' => 'A4 Paper 500 sheets', ], ], ] ); } catch (BudgetExceededException $e) { // PO exceeds requisition by more than 10% }
Record Goods Receipt
$grn = $procurement->recordGoodsReceipt( tenantId: 'tenant-001', poId: $po->getId(), receiverId: 'warehouse-clerk-001', // Must NOT be PO creator receiptData: [ 'number' => 'GRN-2025-001', 'received_date' => '2025-11-20', 'lines' => [ [ 'po_line_reference' => 'PO-2025-001-L001', 'quantity_received' => 10, // Cannot exceed PO quantity 'unit' => 'box', ], ], ] );
Three-Way Matching
$matchResult = $procurement->performThreeWayMatch( poLine: $poLine, grnLine: $grnLine, invoiceLineData: [ 'quantity' => 10, 'unit_price' => 24.50, 'line_total' => 245.00, ] ); if ($matchResult['matched']) { echo "✅ Auto-approved: {$matchResult['recommendation']}"; } else { echo "⚠️ Manual review: {$matchResult['recommendation']}"; print_r($matchResult['discrepancies']); }
Business Rules
Integration Points
- Nexus\Payable: Provides PO and GRN data for 3-way matching and handles vendor bill payments
- Nexus\Inventory: Manages stock levels, which are updated upon successful goods receipt
- Nexus\Finance: Receives journal entries for all procurement-related financial events
- Nexus\Budget: Provides budget data for validation against requisitions and purchase orders
- Nexus\Workflow: Requisition approval workflows and multi-step approval processes
- Nexus\Uom: Unit of measurement validation
- Nexus\Currency: Multi-currency support
- Nexus\AuditLogger: Comprehensive change tracking
- Nexus\Sequencing: Auto-numbering for REQ/PO/GRN
Exception Handling
All domain exceptions extend ProcurementException:
use Nexus\Procurement\Exceptions\{ RequisitionNotFoundException, PurchaseOrderNotFoundException, GoodsReceiptNotFoundException, InvalidRequisitionDataException, InvalidRequisitionStateException, InvalidPurchaseOrderDataException, InvalidGoodsReceiptDataException, BudgetExceededException, UnauthorizedApprovalException }; try { $requisition = $procurement->getRequisition($requisitionId); } catch (RequisitionNotFoundException $e) { // Handle requisition not found } try { $approved = $procurement->approveRequisition($id, $approverId); } catch (UnauthorizedApprovalException $e) { // Handle unauthorized approval attempt }
Performance
- 3-Way Matching: <500ms for 100-line invoices (PER-PRO-0341)
- Requisition Creation: <200ms with eager loading
- PO Generation: <300ms with budget validation
Requirements Addressed
This package addresses all requirements in REQUIREMENTS.md:
- ✅ BUS-PRO-0041 to BUS-PRO-0124: 15 Business requirements
- ✅ FUN-PRO-0235 to FUN-PRO-0271: 7 Functional requirements
- ✅ PER-PRO-0327 to PER-PRO-0353: 5 Performance requirements
- ✅ REL-PRO-0389 to REL-PRO-0407: 4 Reliability requirements
- ✅ SEC-PRO-0441 to SEC-PRO-0470: 6 Security requirements
- ✅ USE-PRO-0508 to USE-PRO-0548: 7 User stories
Total: 44 requirements
Testing
Package tests use mocks for all repository implementations:
use Nexus\Procurement\Services\ProcurementManager; use Nexus\Procurement\Contracts\RequisitionRepositoryInterface; use PHPUnit\Framework\TestCase; class ProcurementManagerTest extends TestCase { public function test_create_requisition(): void { $mockRepo = $this->createMock(RequisitionRepositoryInterface::class); $mockRepo->expects($this->once()) ->method('create') ->willReturn($this->createMock(RequisitionInterface::class)); $manager = new ProcurementManager($mockRepo, ...); // ... test logic } }
📖 Documentation
Package Documentation
- Getting Started Guide - Quick start guide with prerequisites, core concepts, and first integration
- API Reference - Complete documentation of all 19 interfaces, 6 services, and 10 exceptions
- Integration Guide - Laravel and Symfony integration examples with complete setup instructions
- Basic Usage Example - Simple usage patterns for requisitions, POs, and GRNs
- Advanced Usage Example - Advanced scenarios including ML extractors and batch matching
Additional Resources
IMPLEMENTATION_SUMMARY.md- Implementation progress, metrics, and key design decisionsREQUIREMENTS.md- All 44 requirements with status trackingTEST_SUITE_SUMMARY.md- Test coverage metrics and test inventoryVALUATION_MATRIX.md- Package valuation metrics for funding assessment- See root
../../ARCHITECTURE.mdfor overall system architecture - See
../../docs/NEXUS_PACKAGES_REFERENCE.mdfor package ecosystem reference
License
MIT License. See LICENSE file for details.