agentic-dev 0.2.10 → 0.2.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (291) hide show
  1. package/README.md +23 -9
  2. package/bin/agentic-dev.mjs +656 -124
  3. package/lib/scaffold.mjs +109 -6
  4. package/package.json +1 -1
  5. package/client/admin/.dockerignore +0 -3
  6. package/client/admin/.env.example +0 -1
  7. package/client/admin/Dockerfile +0 -16
  8. package/client/admin/Dockerfile.dev +0 -18
  9. package/client/admin/README.md +0 -20
  10. package/client/admin/index.html +0 -12
  11. package/client/admin/package.json +0 -41
  12. package/client/admin/postcss.config.js +0 -6
  13. package/client/admin/scripts/ui-parity-admin-adapter.mjs +0 -65
  14. package/client/admin/src/api/alerts.ts +0 -33
  15. package/client/admin/src/api/client.ts +0 -71
  16. package/client/admin/src/api/orders.ts +0 -33
  17. package/client/admin/src/api/support.ts +0 -11
  18. package/client/admin/src/app/App.tsx +0 -23
  19. package/client/admin/src/auth/AuthProvider.tsx +0 -122
  20. package/client/admin/src/auth/ProtectedRoute.tsx +0 -22
  21. package/client/admin/src/auth/auth-client.ts +0 -38
  22. package/client/admin/src/auth/types.ts +0 -18
  23. package/client/admin/src/components/AdminNotificationsDrawer.tsx +0 -162
  24. package/client/admin/src/components/AdminShell.tsx +0 -76
  25. package/client/admin/src/components/ui/button.tsx +0 -34
  26. package/client/admin/src/components/ui/input.tsx +0 -21
  27. package/client/admin/src/lib/cn.ts +0 -6
  28. package/client/admin/src/lib/specRouteCatalog.json +0 -30
  29. package/client/admin/src/lib/specScreens.json +0 -22
  30. package/client/admin/src/main.tsx +0 -17
  31. package/client/admin/src/pages/AdminDashboardPage.tsx +0 -171
  32. package/client/admin/src/pages/AdminLoginPage.tsx +0 -75
  33. package/client/admin/src/pages/AdminQueuePage.tsx +0 -107
  34. package/client/admin/src/pages/AdminSupportPage.tsx +0 -61
  35. package/client/admin/src/styles/globals.css +0 -17
  36. package/client/admin/src/theme-vars.ts +0 -18
  37. package/client/admin/src/theme.ts +0 -25
  38. package/client/admin/src/vite-env.d.ts +0 -1
  39. package/client/admin/tailwind.config.js +0 -8
  40. package/client/admin/tsconfig.json +0 -25
  41. package/client/admin/vite.config.ts +0 -12
  42. package/client/landing/.dockerignore +0 -3
  43. package/client/landing/.env.example +0 -1
  44. package/client/landing/Dockerfile +0 -16
  45. package/client/landing/Dockerfile.dev +0 -18
  46. package/client/landing/README.md +0 -18
  47. package/client/landing/index.html +0 -12
  48. package/client/landing/package.json +0 -41
  49. package/client/landing/postcss.config.js +0 -6
  50. package/client/landing/scripts/ui-parity-landing-adapter.mjs +0 -65
  51. package/client/landing/src/App.tsx +0 -21
  52. package/client/landing/src/api/catalog.ts +0 -30
  53. package/client/landing/src/api/client.ts +0 -30
  54. package/client/landing/src/auth/AuthProvider.tsx +0 -122
  55. package/client/landing/src/auth/ProtectedRoute.tsx +0 -22
  56. package/client/landing/src/auth/auth-client.ts +0 -38
  57. package/client/landing/src/auth/types.ts +0 -18
  58. package/client/landing/src/components/LandingShell.tsx +0 -34
  59. package/client/landing/src/lib/specRouteCatalog.json +0 -23
  60. package/client/landing/src/lib/specScreens.json +0 -17
  61. package/client/landing/src/main.tsx +0 -17
  62. package/client/landing/src/pages/LandingHomePage.tsx +0 -215
  63. package/client/landing/src/pages/LandingLoginPage.tsx +0 -90
  64. package/client/landing/src/pages/LandingWorkspacePage.tsx +0 -126
  65. package/client/landing/src/styles/globals.css +0 -17
  66. package/client/landing/src/theme-vars.ts +0 -16
  67. package/client/landing/src/theme.ts +0 -21
  68. package/client/landing/src/vite-env.d.ts +0 -1
  69. package/client/landing/tailwind.config.js +0 -8
  70. package/client/landing/tsconfig.json +0 -25
  71. package/client/landing/vite.config.ts +0 -12
  72. package/client/mobile/.dockerignore +0 -2
  73. package/client/mobile/.env.example +0 -1
  74. package/client/mobile/Dockerfile +0 -16
  75. package/client/mobile/Dockerfile.dev +0 -18
  76. package/client/mobile/README.md +0 -19
  77. package/client/mobile/index.html +0 -12
  78. package/client/mobile/package.json +0 -42
  79. package/client/mobile/postcss.config.js +0 -6
  80. package/client/mobile/scripts/ui-parity-mobile-adapter.mjs +0 -67
  81. package/client/mobile/src/App.tsx +0 -1
  82. package/client/mobile/src/api/client.ts +0 -62
  83. package/client/mobile/src/api/fulfillment.ts +0 -55
  84. package/client/mobile/src/api/shipping.ts +0 -56
  85. package/client/mobile/src/app/App.tsx +0 -23
  86. package/client/mobile/src/auth/AuthProvider.tsx +0 -122
  87. package/client/mobile/src/auth/ProtectedRoute.tsx +0 -27
  88. package/client/mobile/src/auth/auth-client.ts +0 -38
  89. package/client/mobile/src/auth/types.ts +0 -18
  90. package/client/mobile/src/components/InShell.tsx +0 -74
  91. package/client/mobile/src/components/ui/button.tsx +0 -35
  92. package/client/mobile/src/components/ui/card.tsx +0 -15
  93. package/client/mobile/src/components/ui/input.tsx +0 -21
  94. package/client/mobile/src/lib/cn.ts +0 -6
  95. package/client/mobile/src/lib/specRouteCatalog.json +0 -26
  96. package/client/mobile/src/lib/specScreens.json +0 -22
  97. package/client/mobile/src/lib/useSpeechRecognitionInput.ts +0 -271
  98. package/client/mobile/src/main.tsx +0 -17
  99. package/client/mobile/src/pages/DashboardPage.tsx +0 -172
  100. package/client/mobile/src/pages/FulfillmentPage.tsx +0 -138
  101. package/client/mobile/src/pages/LoginPage.tsx +0 -74
  102. package/client/mobile/src/pages/ShippingPage.tsx +0 -338
  103. package/client/mobile/src/styles/globals.css +0 -23
  104. package/client/mobile/src/theme-vars.ts +0 -16
  105. package/client/mobile/src/theme.ts +0 -21
  106. package/client/mobile/src/vite-env.d.ts +0 -1
  107. package/client/mobile/tailwind.config.js +0 -8
  108. package/client/mobile/tsconfig.json +0 -25
  109. package/client/mobile/vite.config.ts +0 -12
  110. package/client/web/.dockerignore +0 -3
  111. package/client/web/.env.example +0 -1
  112. package/client/web/Dockerfile +0 -16
  113. package/client/web/Dockerfile.dev +0 -18
  114. package/client/web/README.md +0 -47
  115. package/client/web/index.html +0 -12
  116. package/client/web/package.json +0 -42
  117. package/client/web/postcss.config.js +0 -6
  118. package/client/web/scripts/ui-parity-web-adapter.mjs +0 -66
  119. package/client/web/src/api/client.ts +0 -30
  120. package/client/web/src/api/orders.ts +0 -42
  121. package/client/web/src/app/App.tsx +0 -21
  122. package/client/web/src/auth/AuthProvider.tsx +0 -122
  123. package/client/web/src/auth/ProtectedRoute.tsx +0 -22
  124. package/client/web/src/auth/auth-client.ts +0 -38
  125. package/client/web/src/auth/types.ts +0 -18
  126. package/client/web/src/components/AppShell.tsx +0 -59
  127. package/client/web/src/components/ui/button.tsx +0 -35
  128. package/client/web/src/components/ui/card.tsx +0 -7
  129. package/client/web/src/components/ui/input.tsx +0 -21
  130. package/client/web/src/lib/cn.ts +0 -6
  131. package/client/web/src/lib/specRouteCatalog.json +0 -23
  132. package/client/web/src/lib/specScreens.json +0 -17
  133. package/client/web/src/main.tsx +0 -17
  134. package/client/web/src/pages/DashboardPage.tsx +0 -158
  135. package/client/web/src/pages/LoginPage.tsx +0 -72
  136. package/client/web/src/pages/OrdersPage.tsx +0 -123
  137. package/client/web/src/styles/globals.css +0 -17
  138. package/client/web/src/theme-vars.ts +0 -18
  139. package/client/web/src/theme.ts +0 -25
  140. package/client/web/src/vite-env.d.ts +0 -1
  141. package/client/web/tailwind.config.js +0 -8
  142. package/client/web/tsconfig.json +0 -25
  143. package/client/web/vite.config.ts +0 -12
  144. package/server/.dockerignore +0 -4
  145. package/server/.env.example +0 -19
  146. package/server/Dockerfile +0 -22
  147. package/server/Dockerfile.dev +0 -19
  148. package/server/README.md +0 -33
  149. package/server/__init__.py +0 -0
  150. package/server/api/__init__.py +0 -1
  151. package/server/api/http/__init__.py +0 -4
  152. package/server/api/http/app.py +0 -53
  153. package/server/api/http/router.py +0 -24
  154. package/server/config.py +0 -52
  155. package/server/contexts/__init__.py +0 -12
  156. package/server/contexts/alerts/__init__.py +0 -1
  157. package/server/contexts/alerts/application/__init__.py +0 -13
  158. package/server/contexts/alerts/application/services.py +0 -41
  159. package/server/contexts/alerts/contracts/__init__.py +0 -3
  160. package/server/contexts/alerts/contracts/http/__init__.py +0 -3
  161. package/server/contexts/alerts/contracts/http/router.py +0 -37
  162. package/server/contexts/alerts/domain/__init__.py +0 -15
  163. package/server/contexts/alerts/domain/models.py +0 -29
  164. package/server/contexts/alerts/infrastructure/__init__.py +0 -11
  165. package/server/contexts/alerts/infrastructure/repository.py +0 -41
  166. package/server/contexts/auth/__init__.py +0 -1
  167. package/server/contexts/auth/application/__init__.py +0 -3
  168. package/server/contexts/auth/application/ports.py +0 -10
  169. package/server/contexts/auth/application/services.py +0 -64
  170. package/server/contexts/auth/contracts/__init__.py +0 -4
  171. package/server/contexts/auth/contracts/http/__init__.py +0 -4
  172. package/server/contexts/auth/contracts/http/dependencies.py +0 -37
  173. package/server/contexts/auth/contracts/http/router.py +0 -19
  174. package/server/contexts/auth/domain/__init__.py +0 -3
  175. package/server/contexts/auth/domain/models.py +0 -24
  176. package/server/contexts/auth/infrastructure/__init__.py +0 -4
  177. package/server/contexts/auth/infrastructure/adapters/memory.py +0 -19
  178. package/server/contexts/auth/infrastructure/adapters/mongodb.py +0 -24
  179. package/server/contexts/auth/infrastructure/adapters/sqlalchemy.py +0 -74
  180. package/server/contexts/auth/infrastructure/repository.py +0 -28
  181. package/server/contexts/catalog/__init__.py +0 -1
  182. package/server/contexts/catalog/application/__init__.py +0 -28
  183. package/server/contexts/catalog/application/ports.py +0 -15
  184. package/server/contexts/catalog/application/services.py +0 -154
  185. package/server/contexts/catalog/contracts/__init__.py +0 -3
  186. package/server/contexts/catalog/contracts/http/__init__.py +0 -3
  187. package/server/contexts/catalog/contracts/http/router.py +0 -60
  188. package/server/contexts/catalog/domain/__init__.py +0 -45
  189. package/server/contexts/catalog/domain/models.py +0 -113
  190. package/server/contexts/catalog/infrastructure/__init__.py +0 -4
  191. package/server/contexts/catalog/infrastructure/adapters/memory.py +0 -62
  192. package/server/contexts/catalog/infrastructure/repository.py +0 -8
  193. package/server/contexts/fulfillment/__init__.py +0 -1
  194. package/server/contexts/fulfillment/application/__init__.py +0 -13
  195. package/server/contexts/fulfillment/application/ports.py +0 -20
  196. package/server/contexts/fulfillment/application/services.py +0 -85
  197. package/server/contexts/fulfillment/contracts/__init__.py +0 -3
  198. package/server/contexts/fulfillment/contracts/http/__init__.py +0 -3
  199. package/server/contexts/fulfillment/contracts/http/router.py +0 -40
  200. package/server/contexts/fulfillment/domain/__init__.py +0 -25
  201. package/server/contexts/fulfillment/domain/models.py +0 -73
  202. package/server/contexts/fulfillment/infrastructure/__init__.py +0 -13
  203. package/server/contexts/fulfillment/infrastructure/adapters/memory.py +0 -43
  204. package/server/contexts/fulfillment/infrastructure/repository.py +0 -97
  205. package/server/contexts/health/__init__.py +0 -1
  206. package/server/contexts/health/application/__init__.py +0 -3
  207. package/server/contexts/health/application/services.py +0 -2
  208. package/server/contexts/health/contracts/__init__.py +0 -3
  209. package/server/contexts/health/contracts/http/__init__.py +0 -3
  210. package/server/contexts/health/contracts/http/router.py +0 -10
  211. package/server/contexts/inventory/__init__.py +0 -1
  212. package/server/contexts/inventory/application/__init__.py +0 -28
  213. package/server/contexts/inventory/application/ports.py +0 -11
  214. package/server/contexts/inventory/application/services.py +0 -214
  215. package/server/contexts/inventory/contracts/__init__.py +0 -3
  216. package/server/contexts/inventory/contracts/http/__init__.py +0 -3
  217. package/server/contexts/inventory/contracts/http/router.py +0 -82
  218. package/server/contexts/inventory/domain/__init__.py +0 -33
  219. package/server/contexts/inventory/domain/models.py +0 -93
  220. package/server/contexts/inventory/infrastructure/__init__.py +0 -4
  221. package/server/contexts/inventory/infrastructure/adapters/memory.py +0 -24
  222. package/server/contexts/inventory/infrastructure/repository.py +0 -8
  223. package/server/contexts/orders/__init__.py +0 -1
  224. package/server/contexts/orders/application/__init__.py +0 -19
  225. package/server/contexts/orders/application/services.py +0 -127
  226. package/server/contexts/orders/contracts/__init__.py +0 -3
  227. package/server/contexts/orders/contracts/http/__init__.py +0 -3
  228. package/server/contexts/orders/contracts/http/router.py +0 -82
  229. package/server/contexts/orders/domain/__init__.py +0 -29
  230. package/server/contexts/orders/domain/models.py +0 -95
  231. package/server/contexts/orders/infrastructure/__init__.py +0 -7
  232. package/server/contexts/orders/infrastructure/repository.py +0 -104
  233. package/server/contexts/shipping/__init__.py +0 -1
  234. package/server/contexts/shipping/application/__init__.py +0 -13
  235. package/server/contexts/shipping/application/services.py +0 -92
  236. package/server/contexts/shipping/contracts/__init__.py +0 -3
  237. package/server/contexts/shipping/contracts/http/__init__.py +0 -3
  238. package/server/contexts/shipping/contracts/http/router.py +0 -40
  239. package/server/contexts/shipping/domain/__init__.py +0 -19
  240. package/server/contexts/shipping/domain/models.py +0 -48
  241. package/server/contexts/shipping/infrastructure/__init__.py +0 -9
  242. package/server/contexts/shipping/infrastructure/repository.py +0 -50
  243. package/server/contexts/support/__init__.py +0 -1
  244. package/server/contexts/support/application/__init__.py +0 -13
  245. package/server/contexts/support/application/services.py +0 -29
  246. package/server/contexts/support/contracts/__init__.py +0 -3
  247. package/server/contexts/support/contracts/http/__init__.py +0 -3
  248. package/server/contexts/support/contracts/http/router.py +0 -40
  249. package/server/contexts/support/domain/__init__.py +0 -13
  250. package/server/contexts/support/domain/models.py +0 -27
  251. package/server/contexts/support/infrastructure/__init__.py +0 -11
  252. package/server/contexts/support/infrastructure/repository.py +0 -70
  253. package/server/contexts/user/__init__.py +0 -1
  254. package/server/contexts/user/application/__init__.py +0 -3
  255. package/server/contexts/user/application/ports.py +0 -11
  256. package/server/contexts/user/application/services.py +0 -44
  257. package/server/contexts/user/contracts/__init__.py +0 -3
  258. package/server/contexts/user/contracts/http/__init__.py +0 -3
  259. package/server/contexts/user/contracts/http/router.py +0 -26
  260. package/server/contexts/user/domain/__init__.py +0 -3
  261. package/server/contexts/user/domain/models.py +0 -22
  262. package/server/contexts/user/infrastructure/__init__.py +0 -3
  263. package/server/contexts/user/infrastructure/adapters/memory.py +0 -27
  264. package/server/contexts/user/infrastructure/adapters/mongodb.py +0 -41
  265. package/server/contexts/user/infrastructure/adapters/sqlalchemy.py +0 -94
  266. package/server/contexts/user/infrastructure/factory.py +0 -28
  267. package/server/data/README.md +0 -24
  268. package/server/data/bootstrap/alerts.json +0 -38
  269. package/server/data/bootstrap/auth_accounts.json +0 -18
  270. package/server/data/bootstrap/catalog_products.json +0 -179
  271. package/server/data/bootstrap/fulfillment_events.json +0 -5
  272. package/server/data/bootstrap/fulfillment_notes.json +0 -5
  273. package/server/data/bootstrap/fulfillment_tasks.json +0 -50
  274. package/server/data/bootstrap/inventory_levels.json +0 -80
  275. package/server/data/bootstrap/orders.json +0 -62
  276. package/server/data/bootstrap/shipping_shipments.json +0 -50
  277. package/server/data/bootstrap/support_faqs.json +0 -26
  278. package/server/data/bootstrap/users.json +0 -20
  279. package/server/data/bootstrap_loader.py +0 -15
  280. package/server/docker-entrypoint.sh +0 -56
  281. package/server/main.py +0 -3
  282. package/server/pyproject.toml +0 -36
  283. package/server/shared/__init__.py +0 -1
  284. package/server/shared/application/__init__.py +0 -3
  285. package/server/shared/application/health.py +0 -2
  286. package/server/shared/infrastructure/__init__.py +0 -10
  287. package/server/shared/infrastructure/runtime.py +0 -6
  288. package/server/shared/infrastructure/security.py +0 -33
  289. package/server/tests/e2e/test_domain_feature_flows.py +0 -483
  290. package/server/tests/test_health.py +0 -49
  291. package/server/uv.lock +0 -1169
@@ -1,73 +0,0 @@
1
- from pydantic import BaseModel
2
-
3
-
4
- class FulfillmentTaskRecord(BaseModel):
5
- id: str
6
- order_id: str
7
- title: str
8
- assignee: str
9
- stage: str
10
- status: str
11
- priority: str
12
- channel: str
13
- sla_minutes: int
14
- units: int
15
-
16
-
17
- class FulfillmentEvent(BaseModel):
18
- time: str
19
- title: str
20
- tone: str
21
-
22
-
23
- class FulfillmentNote(BaseModel):
24
- id: str
25
- note: str
26
- emphasis: str
27
-
28
-
29
- class FulfillmentStat(BaseModel):
30
- label: str
31
- value: str
32
- tone: str | None = None
33
-
34
-
35
- class FulfillmentStageLoad(BaseModel):
36
- label: str
37
- value: str
38
-
39
-
40
- class FulfillmentOverview(BaseModel):
41
- throughput_total: str
42
- stats: list[FulfillmentStat]
43
- timeline: list[FulfillmentEvent]
44
- stage_load: list[FulfillmentStageLoad]
45
-
46
-
47
- class FulfillmentBoardItem(BaseModel):
48
- id: str
49
- order_id: str
50
- title: str
51
- assignee: str
52
- stage: str
53
- status: str
54
- priority: str
55
- sla: str
56
-
57
-
58
- class FulfillmentBoardPayload(BaseModel):
59
- tasks: list[FulfillmentBoardItem]
60
- notes: list[FulfillmentNote]
61
-
62
-
63
- class FulfillmentTaskStatusTransitionCommand(BaseModel):
64
- status: str
65
- stage: str | None = None
66
-
67
-
68
- class FulfillmentTaskStatusTransition(BaseModel):
69
- task_id: str
70
- previous_status: str
71
- status: str
72
- previous_stage: str
73
- stage: str
@@ -1,13 +0,0 @@
1
- from .repository import (
2
- list_seed_fulfillment_events,
3
- list_seed_fulfillment_notes,
4
- list_seed_fulfillment_tasks,
5
- transition_seed_fulfillment_task_status,
6
- )
7
-
8
- __all__ = [
9
- "list_seed_fulfillment_events",
10
- "list_seed_fulfillment_notes",
11
- "list_seed_fulfillment_tasks",
12
- "transition_seed_fulfillment_task_status",
13
- ]
@@ -1,43 +0,0 @@
1
- from contexts.fulfillment.domain import (
2
- FulfillmentEvent,
3
- FulfillmentNote,
4
- FulfillmentTaskRecord,
5
- )
6
-
7
-
8
- class MemoryFulfillmentRepository:
9
- def __init__(self) -> None:
10
- self._tasks: dict[str, FulfillmentTaskRecord] = {}
11
- self._events: list[FulfillmentEvent] = []
12
- self._notes: list[FulfillmentNote] = []
13
-
14
- def initialize(self) -> None:
15
- return None
16
-
17
- def seed_tasks(self, tasks: list[FulfillmentTaskRecord]) -> None:
18
- self._tasks = {task.id: task for task in tasks}
19
-
20
- def seed_events(self, events: list[FulfillmentEvent]) -> None:
21
- self._events = list(events)
22
-
23
- def seed_notes(self, notes: list[FulfillmentNote]) -> None:
24
- self._notes = list(notes)
25
-
26
- def list_tasks(self) -> list[FulfillmentTaskRecord]:
27
- return list(self._tasks.values())
28
-
29
- def list_events(self) -> list[FulfillmentEvent]:
30
- return list(self._events)
31
-
32
- def list_notes(self) -> list[FulfillmentNote]:
33
- return list(self._notes)
34
-
35
- def update_task_status(
36
- self, task_id: str, status: str
37
- ) -> FulfillmentTaskRecord | None:
38
- task = self._tasks.get(task_id)
39
- if task is None:
40
- return None
41
- updated = task.model_copy(update={"status": status})
42
- self._tasks[task_id] = updated
43
- return updated
@@ -1,97 +0,0 @@
1
- from datetime import datetime
2
-
3
- from contexts.fulfillment.domain import (
4
- FulfillmentEvent,
5
- FulfillmentNote,
6
- FulfillmentTaskRecord,
7
- FulfillmentTaskStatusTransition,
8
- FulfillmentTaskStatusTransitionCommand,
9
- )
10
- from data.bootstrap_loader import load_bootstrap_json
11
-
12
- _task_store: list[FulfillmentTaskRecord] | None = None
13
- _event_store: list[FulfillmentEvent] | None = None
14
- _note_store: list[FulfillmentNote] | None = None
15
-
16
-
17
- def _get_task_store() -> list[FulfillmentTaskRecord]:
18
- global _task_store
19
- if _task_store is None:
20
- _task_store = [
21
- FulfillmentTaskRecord(**entry)
22
- for entry in load_bootstrap_json("fulfillment_tasks.json")
23
- ]
24
- return _task_store
25
-
26
-
27
- def _get_event_store() -> list[FulfillmentEvent]:
28
- global _event_store
29
- if _event_store is None:
30
- _event_store = [
31
- FulfillmentEvent(**entry)
32
- for entry in load_bootstrap_json("fulfillment_events.json")
33
- ]
34
- return _event_store
35
-
36
-
37
- def _get_note_store() -> list[FulfillmentNote]:
38
- global _note_store
39
- if _note_store is None:
40
- _note_store = [
41
- FulfillmentNote(**entry)
42
- for entry in load_bootstrap_json("fulfillment_notes.json")
43
- ]
44
- return _note_store
45
-
46
-
47
- def list_seed_fulfillment_tasks() -> list[FulfillmentTaskRecord]:
48
- return [task.model_copy(deep=True) for task in _get_task_store()]
49
-
50
-
51
- def list_seed_fulfillment_events() -> list[FulfillmentEvent]:
52
- return [event.model_copy(deep=True) for event in _get_event_store()]
53
-
54
-
55
- def list_seed_fulfillment_notes() -> list[FulfillmentNote]:
56
- return [note.model_copy(deep=True) for note in _get_note_store()]
57
-
58
-
59
- def transition_seed_fulfillment_task_status(
60
- task_id: str, command: FulfillmentTaskStatusTransitionCommand
61
- ) -> FulfillmentTaskStatusTransition:
62
- tasks = _get_task_store()
63
- for index, task in enumerate(tasks):
64
- if task.id != task_id:
65
- continue
66
-
67
- updated_task = task.model_copy(
68
- update={"status": command.status, "stage": command.stage or task.stage}
69
- )
70
- tasks[index] = updated_task
71
- _get_event_store().insert(
72
- 0,
73
- FulfillmentEvent(
74
- time=datetime.now().strftime("%H:%M"),
75
- title=f"{updated_task.order_id} {updated_task.title} -> {updated_task.status}",
76
- tone=_map_event_tone(updated_task.status),
77
- ),
78
- )
79
- return FulfillmentTaskStatusTransition(
80
- task_id=updated_task.id,
81
- previous_status=task.status,
82
- status=updated_task.status,
83
- previous_stage=task.stage,
84
- stage=updated_task.stage,
85
- )
86
-
87
- raise LookupError(f"Fulfillment task {task_id} not found")
88
-
89
-
90
- def _map_event_tone(status: str) -> str:
91
- tone_map = {
92
- "Queued": "accent",
93
- "In progress": "warning",
94
- "Blocked": "warning",
95
- "Completed": "success",
96
- }
97
- return tone_map.get(status, "neutral")
@@ -1 +0,0 @@
1
- __all__ = ["application", "presentation"]
@@ -1,3 +0,0 @@
1
- from .services import get_status_payload
2
-
3
- __all__ = ["get_status_payload"]
@@ -1,2 +0,0 @@
1
- def get_status_payload() -> dict[str, str]:
2
- return {"service": "template-server", "status": "healthy"}
@@ -1,3 +0,0 @@
1
- from .http.router import router
2
-
3
- __all__ = ["router"]
@@ -1,3 +0,0 @@
1
- from .router import router
2
-
3
- __all__ = ["router"]
@@ -1,10 +0,0 @@
1
- from fastapi import APIRouter
2
-
3
- from contexts.health.application import get_status_payload
4
-
5
- router = APIRouter(tags=["health"])
6
-
7
-
8
- @router.get("/status")
9
- def status() -> dict[str, str]:
10
- return get_status_payload()
@@ -1 +0,0 @@
1
- __all__ = ["application", "contracts", "domain", "infrastructure"]
@@ -1,28 +0,0 @@
1
- from .services import (
2
- adjust_inventory_level_or_404,
3
- get_inventory_level_or_404,
4
- list_inventory_levels,
5
- prepare_inventory_store,
6
- release_inventory_level_or_404,
7
- reserve_inventory_or_404,
8
- set_inventory_level_or_404,
9
- )
10
-
11
- adjust_inventory_level = adjust_inventory_level_or_404
12
- release_inventory_level = release_inventory_level_or_404
13
- reserve_inventory_level = reserve_inventory_or_404
14
- set_inventory_level = set_inventory_level_or_404
15
-
16
- __all__ = [
17
- "adjust_inventory_level",
18
- "adjust_inventory_level_or_404",
19
- "get_inventory_level_or_404",
20
- "list_inventory_levels",
21
- "prepare_inventory_store",
22
- "release_inventory_level",
23
- "release_inventory_level_or_404",
24
- "reserve_inventory_level",
25
- "reserve_inventory_or_404",
26
- "set_inventory_level",
27
- "set_inventory_level_or_404",
28
- ]
@@ -1,11 +0,0 @@
1
- from typing import Protocol
2
-
3
- from contexts.inventory.domain import InventoryLevelRecord
4
-
5
-
6
- class InventoryRepository(Protocol):
7
- def initialize(self) -> None: ...
8
- def seed_levels(self, levels: list[InventoryLevelRecord]) -> None: ...
9
- def list_levels(self) -> list[InventoryLevelRecord]: ...
10
- def get_level(self, sku: str, location_id: str) -> InventoryLevelRecord | None: ...
11
- def upsert_level(self, level: InventoryLevelRecord) -> InventoryLevelRecord: ...
@@ -1,214 +0,0 @@
1
- from datetime import datetime, timezone
2
- from functools import lru_cache
3
-
4
- from fastapi import HTTPException, status as http_status
5
-
6
- from contexts.inventory.domain import (
7
- AdjustInventoryCommand,
8
- InventoryAvailabilityStatus,
9
- InventoryLevel,
10
- InventoryLevelRecord,
11
- InventoryMutationReceipt,
12
- ReleaseInventoryCommand,
13
- ReserveInventoryCommand,
14
- SetInventoryLevelCommand,
15
- )
16
- from contexts.inventory.infrastructure import get_inventory_repository
17
- from data.bootstrap_loader import load_bootstrap_json
18
-
19
-
20
- def list_inventory_levels(
21
- sku: str | None = None,
22
- product_id: str | None = None,
23
- location_id: str | None = None,
24
- availability_status: InventoryAvailabilityStatus | None = None,
25
- ) -> list[InventoryLevel]:
26
- prepare_inventory_store()
27
- repository = get_inventory_repository()
28
- levels = repository.list_levels()
29
-
30
- if sku is not None:
31
- normalized_sku = sku.strip().casefold()
32
- levels = [level for level in levels if level.sku.casefold() == normalized_sku]
33
- if product_id is not None:
34
- normalized_product_id = product_id.strip().casefold()
35
- levels = [level for level in levels if level.product_id.casefold() == normalized_product_id]
36
- if location_id is not None:
37
- normalized_location_id = location_id.strip().casefold()
38
- levels = [level for level in levels if level.location_id.casefold() == normalized_location_id]
39
- if availability_status is not None:
40
- levels = [level for level in levels if _availability_status(level) == availability_status]
41
-
42
- return [_to_level(level) for level in levels]
43
-
44
-
45
- def get_inventory_level_or_404(sku: str, location_id: str) -> InventoryLevel:
46
- prepare_inventory_store()
47
- repository = get_inventory_repository()
48
- level = repository.get_level(sku, location_id)
49
- if level is None:
50
- raise HTTPException(status_code=http_status.HTTP_404_NOT_FOUND, detail="Inventory level not found")
51
- return _to_level(level)
52
-
53
-
54
- def adjust_inventory_level_or_404(
55
- sku: str,
56
- location_id: str,
57
- command: AdjustInventoryCommand,
58
- ) -> InventoryMutationReceipt:
59
- prepare_inventory_store()
60
- repository = get_inventory_repository()
61
- level = repository.get_level(sku, location_id)
62
- if level is None:
63
- raise HTTPException(status_code=http_status.HTTP_404_NOT_FOUND, detail="Inventory level not found")
64
- if command.quantity_delta == 0:
65
- raise HTTPException(status_code=http_status.HTTP_400_BAD_REQUEST, detail="Adjustment delta must not be zero")
66
-
67
- next_on_hand = level.on_hand + command.quantity_delta
68
- if next_on_hand < level.reserved:
69
- raise HTTPException(
70
- status_code=http_status.HTTP_409_CONFLICT,
71
- detail="Adjustment would reduce on-hand stock below reserved stock",
72
- )
73
-
74
- updated = level.model_copy(update={"on_hand": next_on_hand, "updated_at": _now_iso()})
75
- repository.upsert_level(updated)
76
- return InventoryMutationReceipt(
77
- action="adjusted",
78
- sku=updated.sku,
79
- location_id=updated.location_id,
80
- quantity=command.quantity_delta,
81
- reference_id=command.reference_id,
82
- reason=command.reason,
83
- level=_to_level(updated),
84
- )
85
-
86
-
87
- def reserve_inventory_or_404(
88
- sku: str,
89
- location_id: str,
90
- command: ReserveInventoryCommand,
91
- ) -> InventoryMutationReceipt:
92
- prepare_inventory_store()
93
- repository = get_inventory_repository()
94
- level = repository.get_level(sku, location_id)
95
- if level is None:
96
- raise HTTPException(status_code=http_status.HTTP_404_NOT_FOUND, detail="Inventory level not found")
97
- available_to_sell = level.on_hand - level.reserved
98
- if command.quantity > available_to_sell:
99
- raise HTTPException(status_code=http_status.HTTP_409_CONFLICT, detail="Not enough available stock to reserve")
100
-
101
- updated = level.model_copy(update={"reserved": level.reserved + command.quantity, "updated_at": _now_iso()})
102
- repository.upsert_level(updated)
103
- return InventoryMutationReceipt(
104
- action="reserved",
105
- sku=updated.sku,
106
- location_id=updated.location_id,
107
- quantity=command.quantity,
108
- reference_id=command.reference_id,
109
- reason=command.channel,
110
- level=_to_level(updated),
111
- )
112
-
113
-
114
- def release_inventory_level_or_404(
115
- sku: str,
116
- location_id: str,
117
- command: ReleaseInventoryCommand,
118
- ) -> InventoryMutationReceipt:
119
- prepare_inventory_store()
120
- repository = get_inventory_repository()
121
- level = repository.get_level(sku, location_id)
122
- if level is None:
123
- raise HTTPException(status_code=http_status.HTTP_404_NOT_FOUND, detail="Inventory level not found")
124
- if command.quantity > level.reserved:
125
- raise HTTPException(status_code=http_status.HTTP_409_CONFLICT, detail="Release quantity exceeds reserved stock")
126
-
127
- updated = level.model_copy(update={"reserved": level.reserved - command.quantity, "updated_at": _now_iso()})
128
- repository.upsert_level(updated)
129
- return InventoryMutationReceipt(
130
- action="released",
131
- sku=updated.sku,
132
- location_id=updated.location_id,
133
- quantity=command.quantity,
134
- reference_id=command.reference_id,
135
- reason=command.reason,
136
- level=_to_level(updated),
137
- )
138
-
139
-
140
- def set_inventory_level_or_404(
141
- sku: str,
142
- location_id: str,
143
- command: SetInventoryLevelCommand,
144
- ) -> InventoryMutationReceipt:
145
- prepare_inventory_store()
146
- repository = get_inventory_repository()
147
- current = repository.get_level(sku, location_id)
148
- if current is None:
149
- raise HTTPException(status_code=http_status.HTTP_404_NOT_FOUND, detail="Inventory level not found")
150
-
151
- updated = current.model_copy(
152
- update={
153
- "on_hand": command.on_hand,
154
- "reserved": command.reserved,
155
- "safety_stock": command.safety_stock,
156
- "reorder_point": command.reorder_point,
157
- "updated_at": _now_iso(),
158
- }
159
- )
160
- repository.upsert_level(updated)
161
- return InventoryMutationReceipt(
162
- action="set",
163
- sku=updated.sku,
164
- location_id=updated.location_id,
165
- quantity=updated.on_hand - current.on_hand,
166
- reference_id=command.reference_id,
167
- reason=command.reason,
168
- level=_to_level(updated),
169
- )
170
-
171
-
172
- def prepare_inventory_store() -> None:
173
- _seed_inventory_store()
174
-
175
-
176
- @lru_cache
177
- def _seed_inventory_store() -> None:
178
- repository = get_inventory_repository()
179
- repository.initialize()
180
- levels = [InventoryLevelRecord(**entry) for entry in load_bootstrap_json("inventory_levels.json")]
181
- repository.seed_levels(levels)
182
-
183
-
184
- def _to_level(level: InventoryLevelRecord) -> InventoryLevel:
185
- available_to_sell = max(level.on_hand - level.reserved, 0)
186
- return InventoryLevel(
187
- sku=level.sku,
188
- product_id=level.product_id,
189
- product_name=level.product_name,
190
- variant_name=level.variant_name,
191
- location_id=level.location_id,
192
- location_name=level.location_name,
193
- on_hand=level.on_hand,
194
- reserved=level.reserved,
195
- available_to_sell=available_to_sell,
196
- safety_stock=level.safety_stock,
197
- reorder_point=level.reorder_point,
198
- needs_reorder=available_to_sell <= level.reorder_point,
199
- status=_availability_status(level),
200
- updated_at=level.updated_at,
201
- )
202
-
203
-
204
- def _availability_status(level: InventoryLevelRecord) -> InventoryAvailabilityStatus:
205
- available_to_sell = max(level.on_hand - level.reserved, 0)
206
- if available_to_sell == 0:
207
- return "out_of_stock"
208
- if available_to_sell <= level.reorder_point:
209
- return "low_stock"
210
- return "in_stock"
211
-
212
-
213
- def _now_iso() -> str:
214
- return datetime.now(timezone.utc).replace(microsecond=0).isoformat()
@@ -1,3 +0,0 @@
1
- from .http.router import router
2
-
3
- __all__ = ["router"]
@@ -1,3 +0,0 @@
1
- from .router import router
2
-
3
- __all__ = ["router"]
@@ -1,82 +0,0 @@
1
- from fastapi import APIRouter, Depends, Query
2
-
3
- from contexts.auth.contracts.http.dependencies import require_admin_user
4
- from contexts.inventory.application import (
5
- adjust_inventory_level_or_404,
6
- get_inventory_level_or_404,
7
- list_inventory_levels,
8
- release_inventory_level_or_404,
9
- reserve_inventory_or_404,
10
- set_inventory_level_or_404,
11
- )
12
- from contexts.inventory.domain import (
13
- AdjustInventoryCommand,
14
- InventoryAvailabilityStatus,
15
- InventoryLevel,
16
- InventoryMutationReceipt,
17
- ReleaseInventoryCommand,
18
- ReserveInventoryCommand,
19
- SetInventoryLevelCommand,
20
- )
21
-
22
- router = APIRouter(
23
- prefix="/inventory",
24
- tags=["inventory"],
25
- dependencies=[Depends(require_admin_user)],
26
- )
27
-
28
-
29
- @router.get("/levels", response_model=list[InventoryLevel])
30
- def list_levels(
31
- sku: str | None = Query(default=None),
32
- product_id: str | None = Query(default=None),
33
- location_id: str | None = Query(default=None),
34
- status: InventoryAvailabilityStatus | None = Query(default=None),
35
- ) -> list[InventoryLevel]:
36
- return list_inventory_levels(
37
- sku=sku,
38
- product_id=product_id,
39
- location_id=location_id,
40
- availability_status=status,
41
- )
42
-
43
-
44
- @router.get("/levels/{sku}/{location_id}", response_model=InventoryLevel)
45
- def get_level(sku: str, location_id: str) -> InventoryLevel:
46
- return get_inventory_level_or_404(sku, location_id)
47
-
48
-
49
- @router.post("/levels/{sku}/{location_id}/adjustments", response_model=InventoryMutationReceipt)
50
- def post_adjustment(
51
- sku: str,
52
- location_id: str,
53
- command: AdjustInventoryCommand,
54
- ) -> InventoryMutationReceipt:
55
- return adjust_inventory_level_or_404(sku, location_id, command)
56
-
57
-
58
- @router.post("/levels/{sku}/{location_id}/reservations", response_model=InventoryMutationReceipt)
59
- def post_reservation(
60
- sku: str,
61
- location_id: str,
62
- command: ReserveInventoryCommand,
63
- ) -> InventoryMutationReceipt:
64
- return reserve_inventory_or_404(sku, location_id, command)
65
-
66
-
67
- @router.post("/levels/{sku}/{location_id}/releases", response_model=InventoryMutationReceipt)
68
- def post_release(
69
- sku: str,
70
- location_id: str,
71
- command: ReleaseInventoryCommand,
72
- ) -> InventoryMutationReceipt:
73
- return release_inventory_level_or_404(sku, location_id, command)
74
-
75
-
76
- @router.put("/levels/{sku}/{location_id}", response_model=InventoryMutationReceipt)
77
- def put_level(
78
- sku: str,
79
- location_id: str,
80
- command: SetInventoryLevelCommand,
81
- ) -> InventoryMutationReceipt:
82
- return set_inventory_level_or_404(sku, location_id, command)
@@ -1,33 +0,0 @@
1
- from .models import (
2
- AdjustInventoryCommand,
3
- InventoryAdjustmentCommand,
4
- InventoryAvailabilityStatus,
5
- InventoryLevel,
6
- InventoryLevelRecord,
7
- InventoryLevelView,
8
- InventoryMutationAction,
9
- InventoryMutationReceipt,
10
- InventoryReleaseCommand,
11
- InventoryReservationCommand,
12
- InventorySetLevelCommand,
13
- ReleaseInventoryCommand,
14
- ReserveInventoryCommand,
15
- SetInventoryLevelCommand,
16
- )
17
-
18
- __all__ = [
19
- "AdjustInventoryCommand",
20
- "InventoryAdjustmentCommand",
21
- "InventoryAvailabilityStatus",
22
- "InventoryLevel",
23
- "InventoryLevelRecord",
24
- "InventoryLevelView",
25
- "InventoryMutationAction",
26
- "InventoryMutationReceipt",
27
- "InventoryReleaseCommand",
28
- "InventoryReservationCommand",
29
- "InventorySetLevelCommand",
30
- "ReleaseInventoryCommand",
31
- "ReserveInventoryCommand",
32
- "SetInventoryLevelCommand",
33
- ]