fix error in gitignore
This commit is contained in:
@@ -0,0 +1,81 @@
|
||||
"""Order lifecycle service — ties reservations, weighing, dynamic step
|
||||
selection, pricing and confirmation together. All financial values are
|
||||
computed by the backend and stored as immutable snapshots."""
|
||||
from decimal import Decimal
|
||||
|
||||
from django.db import transaction
|
||||
from django.utils import timezone
|
||||
|
||||
from apps.core.errors import ApiError
|
||||
from apps.orders.models import ProcessingOrder
|
||||
from apps.pricing.engine import PricingEngine
|
||||
from apps.workflows.models import Workflow
|
||||
|
||||
|
||||
class OrderService:
|
||||
def _active_workflow(self) -> Workflow:
|
||||
workflow = Workflow.objects.filter(status="active", active_version__isnull=False).first()
|
||||
if workflow is None:
|
||||
raise ApiError("NO_ACTIVE_WORKFLOW", "No active workflow is configured.")
|
||||
return workflow
|
||||
|
||||
@transaction.atomic
|
||||
def create_from_reservation(self, reservation, workflow=None) -> ProcessingOrder:
|
||||
if ProcessingOrder.objects.filter(reservation=reservation).exists():
|
||||
raise ApiError("ORDER_EXISTS", "An order already exists for this reservation.")
|
||||
if workflow is None:
|
||||
workflow = self._active_workflow()
|
||||
return ProcessingOrder.objects.create(
|
||||
customer=reservation.customer,
|
||||
reservation=reservation,
|
||||
workflow_version=workflow.active_version,
|
||||
)
|
||||
|
||||
@transaction.atomic
|
||||
def record_weighing(self, order, gross, tare) -> ProcessingOrder:
|
||||
order = ProcessingOrder.objects.select_for_update().get(pk=order.pk)
|
||||
if order.net_weight_kg is not None:
|
||||
raise ApiError("WEIGHING_IMMUTABLE", "Net weight is already recorded and immutable.")
|
||||
gross = Decimal(str(gross))
|
||||
tare = Decimal(str(tare))
|
||||
if gross < tare:
|
||||
raise ApiError("INVALID_WEIGHT", "Gross weight cannot be less than tare weight.")
|
||||
order.gross_weight_kg = gross
|
||||
order.tare_weight_kg = tare
|
||||
order.net_weight_kg = gross - tare
|
||||
order.save(update_fields=["gross_weight_kg", "tare_weight_kg", "net_weight_kg"])
|
||||
return order
|
||||
|
||||
@transaction.atomic
|
||||
def select_steps(self, order, step_keys) -> ProcessingOrder:
|
||||
order = ProcessingOrder.objects.select_for_update().get(pk=order.pk)
|
||||
if order.net_weight_kg is None:
|
||||
raise ApiError("WEIGHING_REQUIRED", "Record weighing before selecting processing.")
|
||||
version = order.workflow_version
|
||||
steps = list(version.steps.filter(key__in=step_keys, enabled=True))
|
||||
keys = {s.key for s in steps}
|
||||
for required in version.steps.filter(required=True):
|
||||
if required.key not in keys:
|
||||
raise ApiError(
|
||||
"REQUIRED_STEP_MISSING",
|
||||
f"Required step '{required.key}' must be selected.",
|
||||
{"step": required.key},
|
||||
)
|
||||
order.selected_step_keys = sorted(keys)
|
||||
order.price_snapshot = PricingEngine().calculate(order.net_weight_kg, steps, order.currency)
|
||||
order.status = ProcessingOrder.Status.AWAITING_CONFIRMATION
|
||||
order.save(update_fields=["selected_step_keys", "price_snapshot", "status"])
|
||||
return order
|
||||
|
||||
@transaction.atomic
|
||||
def confirm(self, order, terms_accepted=True) -> ProcessingOrder:
|
||||
order = ProcessingOrder.objects.select_for_update().get(pk=order.pk)
|
||||
if order.status != ProcessingOrder.Status.AWAITING_CONFIRMATION:
|
||||
raise ApiError("ORDER_NOT_CONFIRMABLE", "Order is not awaiting confirmation.")
|
||||
if not terms_accepted:
|
||||
raise ApiError("TERMS_REQUIRED", "Terms must be accepted.")
|
||||
order.status = ProcessingOrder.Status.CONFIRMED
|
||||
order.confirmed_at = timezone.now()
|
||||
order.terms_accepted = True
|
||||
order.save(update_fields=["status", "confirmed_at", "terms_accepted"])
|
||||
return order
|
||||
Reference in New Issue
Block a user