File size: 2,754 Bytes
8981bf6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
"""Mock orders API.

Stands in for a real OMS/Shopify Orders endpoint. Reads ``orders.json`` and offers
lookup by order id (optionally verified against the customer email). In production you
would swap this class for a thin HTTP client to the real orders service; the rest of
the app depends only on its small interface.
"""

from __future__ import annotations

import json
from dataclasses import dataclass
from datetime import date
from pathlib import Path
from typing import Any

from .config import get_settings


@dataclass(frozen=True)
class Order:
    order_id: str
    email: str
    status: str
    placed_at: str
    delivered_at: str | None
    carrier: str | None
    tracking: str | None
    items: list[dict[str, Any]]
    total: float
    final_sale: bool

    def days_since_delivery(self, today: date | None = None) -> int | None:
        """Whole days since delivery, or ``None`` if not delivered yet."""
        if not self.delivered_at:
            return None
        today = today or date.today()
        delivered = date.fromisoformat(self.delivered_at)
        return (today - delivered).days

    def human_status(self) -> str:
        if self.status == "shipped" and self.tracking:
            return f"shipped via {self.carrier} (tracking {self.tracking})"
        if self.status == "delivered":
            return f"delivered on {self.delivered_at}"
        return self.status


class OrdersService:
    """In-memory mock orders store loaded from ``orders.json``."""

    def __init__(self, orders_path: Path | None = None):
        path = orders_path or (get_settings().data_dir / "orders.json")
        raw = json.loads(path.read_text(encoding="utf-8"))
        self._orders: dict[str, Order] = {}
        for o in raw.get("orders", []):
            self._orders[o["order_id"].upper()] = Order(
                order_id=o["order_id"],
                email=o["email"],
                status=o["status"],
                placed_at=o["placed_at"],
                delivered_at=o.get("delivered_at"),
                carrier=o.get("carrier"),
                tracking=o.get("tracking"),
                items=o.get("items", []),
                total=float(o.get("total", 0.0)),
                final_sale=bool(o.get("final_sale", False)),
            )

    def get(self, order_id: str, email: str | None = None) -> Order | None:
        """Look up an order. If ``email`` is given it must match (case-insensitive)."""
        order = self._orders.get((order_id or "").strip().upper())
        if order is None:
            return None
        if email and order.email.lower() != email.strip().lower():
            return None
        return order

    def all(self) -> list[Order]:
        return list(self._orders.values())