Improve the frontend 2
This commit is contained in:
@@ -16,6 +16,7 @@ from shared.routing import RouteBuilder
|
||||
from shared.security import create_audit_logger, AuditSeverity, AuditAction
|
||||
from app.core.database import get_db
|
||||
from app.services.orders_service import OrdersService
|
||||
from app.models import AuditLog
|
||||
from app.schemas.order_schemas import (
|
||||
CustomerCreate,
|
||||
CustomerUpdate,
|
||||
@@ -23,7 +24,7 @@ from app.schemas.order_schemas import (
|
||||
)
|
||||
|
||||
logger = structlog.get_logger()
|
||||
audit_logger = create_audit_logger("orders-service")
|
||||
audit_logger = create_audit_logger("orders-service", AuditLog)
|
||||
|
||||
# Create route builder for consistent URL structure
|
||||
route_builder = RouteBuilder('orders')
|
||||
|
||||
@@ -154,7 +154,7 @@ async def update_order_status(
|
||||
order_id,
|
||||
tenant_id,
|
||||
new_status,
|
||||
user_id=UUID(current_user["sub"]),
|
||||
user_id=UUID(current_user["user_id"]),
|
||||
reason=reason
|
||||
)
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ from shared.routing import RouteBuilder
|
||||
from shared.security import create_audit_logger, AuditSeverity, AuditAction
|
||||
from app.core.database import get_db
|
||||
from app.services.orders_service import OrdersService
|
||||
from app.models import AuditLog
|
||||
from app.schemas.order_schemas import (
|
||||
OrderCreate,
|
||||
OrderUpdate,
|
||||
@@ -24,7 +25,7 @@ from app.schemas.order_schemas import (
|
||||
)
|
||||
|
||||
logger = structlog.get_logger()
|
||||
audit_logger = create_audit_logger("orders-service")
|
||||
audit_logger = create_audit_logger("orders-service", AuditLog)
|
||||
|
||||
# Create route builder for consistent URL structure
|
||||
route_builder = RouteBuilder('orders')
|
||||
@@ -217,7 +218,7 @@ async def update_order(
|
||||
db,
|
||||
db_obj=order,
|
||||
obj_in=order_data.dict(exclude_unset=True),
|
||||
updated_by=UUID(current_user["sub"])
|
||||
updated_by=UUID(current_user["user_id"])
|
||||
)
|
||||
|
||||
# Commit the transaction to persist changes
|
||||
|
||||
@@ -10,6 +10,7 @@ from uuid import UUID
|
||||
import structlog
|
||||
|
||||
from shared.config.base import BaseServiceSettings
|
||||
from shared.utils.tenant_settings_client import TenantSettingsClient
|
||||
|
||||
logger = structlog.get_logger()
|
||||
|
||||
@@ -18,10 +19,16 @@ class ApprovalRulesService:
|
||||
"""
|
||||
Service for evaluating purchase orders against approval rules
|
||||
Implements smart auto-approval logic based on multiple criteria
|
||||
Uses tenant-specific settings from the database instead of system-level config
|
||||
"""
|
||||
|
||||
def __init__(self, config: BaseServiceSettings):
|
||||
def __init__(self, config: BaseServiceSettings, tenant_id: UUID):
|
||||
self.config = config
|
||||
self.tenant_id = tenant_id
|
||||
|
||||
# Initialize tenant settings client
|
||||
tenant_service_url = getattr(config, 'TENANT_SERVICE_URL', 'http://tenant-service:8000')
|
||||
self.tenant_settings_client = TenantSettingsClient(tenant_service_url=tenant_service_url)
|
||||
|
||||
async def evaluate_po_for_auto_approval(
|
||||
self,
|
||||
@@ -30,39 +37,60 @@ class ApprovalRulesService:
|
||||
requirements_data: Optional[List[Dict[str, Any]]] = None
|
||||
) -> Tuple[bool, List[str]]:
|
||||
"""
|
||||
Evaluate if a PO should be auto-approved
|
||||
Evaluate if a PO should be auto-approved using tenant-specific settings
|
||||
|
||||
Returns:
|
||||
Tuple of (should_auto_approve, reasons)
|
||||
"""
|
||||
if not self.config.AUTO_APPROVE_ENABLED:
|
||||
return False, ["Auto-approval is disabled in configuration"]
|
||||
# Fetch tenant-specific procurement settings
|
||||
try:
|
||||
tenant_settings = await self.tenant_settings_client.get_procurement_settings(self.tenant_id)
|
||||
except Exception as e:
|
||||
logger.error("Failed to fetch tenant settings, using safe defaults",
|
||||
tenant_id=str(self.tenant_id),
|
||||
error=str(e))
|
||||
# Use safe defaults if settings unavailable
|
||||
tenant_settings = {
|
||||
'auto_approve_enabled': False,
|
||||
'auto_approve_threshold_eur': 500.0,
|
||||
'auto_approve_min_supplier_score': 0.80,
|
||||
'require_approval_new_suppliers': True,
|
||||
'require_approval_critical_items': True
|
||||
}
|
||||
|
||||
# Check if auto-approval is enabled for this tenant
|
||||
if not tenant_settings.get('auto_approve_enabled', True):
|
||||
return False, ["Auto-approval is disabled in tenant settings"]
|
||||
|
||||
reasons = []
|
||||
should_approve = True
|
||||
|
||||
# Rule 1: Amount threshold check
|
||||
total_amount = self._calculate_po_total(po_data)
|
||||
if total_amount > self.config.AUTO_APPROVE_THRESHOLD_EUR:
|
||||
threshold = Decimal(str(tenant_settings.get('auto_approve_threshold_eur', 500.0)))
|
||||
|
||||
if total_amount > threshold:
|
||||
should_approve = False
|
||||
reasons.append(
|
||||
f"PO amount €{total_amount:.2f} exceeds threshold €{self.config.AUTO_APPROVE_THRESHOLD_EUR:.2f}"
|
||||
f"PO amount €{total_amount:.2f} exceeds threshold €{threshold:.2f}"
|
||||
)
|
||||
else:
|
||||
reasons.append(
|
||||
f"PO amount €{total_amount:.2f} within threshold €{self.config.AUTO_APPROVE_THRESHOLD_EUR:.2f}"
|
||||
f"PO amount €{total_amount:.2f} within threshold €{threshold:.2f}"
|
||||
)
|
||||
|
||||
# Rule 2: Supplier trust score check
|
||||
if supplier_data and self.config.AUTO_APPROVE_TRUSTED_SUPPLIERS:
|
||||
min_supplier_score = tenant_settings.get('auto_approve_min_supplier_score', 0.80)
|
||||
|
||||
if supplier_data:
|
||||
supplier_score = supplier_data.get('trust_score', 0.0)
|
||||
is_preferred = supplier_data.get('is_preferred_supplier', False)
|
||||
auto_approve_enabled = supplier_data.get('auto_approve_enabled', False)
|
||||
|
||||
if supplier_score < self.config.AUTO_APPROVE_MIN_SUPPLIER_SCORE:
|
||||
if supplier_score < min_supplier_score:
|
||||
should_approve = False
|
||||
reasons.append(
|
||||
f"Supplier trust score {supplier_score:.2f} below minimum {self.config.AUTO_APPROVE_MIN_SUPPLIER_SCORE:.2f}"
|
||||
f"Supplier trust score {supplier_score:.2f} below minimum {min_supplier_score:.2f}"
|
||||
)
|
||||
else:
|
||||
reasons.append(f"Supplier trust score {supplier_score:.2f} meets minimum requirements")
|
||||
@@ -84,7 +112,9 @@ class ApprovalRulesService:
|
||||
reasons.append("No supplier data available")
|
||||
|
||||
# Rule 3: New supplier check
|
||||
if supplier_data and self.config.REQUIRE_APPROVAL_NEW_SUPPLIERS:
|
||||
require_approval_new_suppliers = tenant_settings.get('require_approval_new_suppliers', True)
|
||||
|
||||
if supplier_data and require_approval_new_suppliers:
|
||||
total_pos = supplier_data.get('total_pos_count', 0)
|
||||
if total_pos < 5:
|
||||
should_approve = False
|
||||
@@ -93,7 +123,9 @@ class ApprovalRulesService:
|
||||
reasons.append(f"Established supplier with {total_pos} previous orders")
|
||||
|
||||
# Rule 4: Critical/urgent items check
|
||||
if requirements_data and self.config.REQUIRE_APPROVAL_CRITICAL_ITEMS:
|
||||
require_approval_critical_items = tenant_settings.get('require_approval_critical_items', True)
|
||||
|
||||
if requirements_data and require_approval_critical_items:
|
||||
critical_count = sum(
|
||||
1 for req in requirements_data
|
||||
if req.get('priority') in ['critical', 'urgent', 'CRITICAL', 'URGENT']
|
||||
|
||||
@@ -502,7 +502,7 @@ class ProcurementService:
|
||||
|
||||
# Import approval rules service
|
||||
from app.services.approval_rules_service import ApprovalRulesService
|
||||
approval_service = ApprovalRulesService(self.config)
|
||||
approval_service = ApprovalRulesService(self.config, tenant_id)
|
||||
|
||||
# Group requirements by supplier
|
||||
supplier_requirements = {}
|
||||
|
||||
Reference in New Issue
Block a user