Coverage for modules\customs\api.py: 42%
96 statements
« prev ^ index » next coverage.py v7.16.0, created at 2026-09-06 17:01 +0800
« prev ^ index » next coverage.py v7.16.0, created at 2026-09-06 17:01 +0800
1from ninja import Router
2from typing import List, Optional
3from uuid import UUID
4from core.auth import JWTAuth
5from core.permissions import require_tenant, require_admin
6from core.exceptions import NotFoundException
7from .schemas import (
8 CustomsEntryIn, CustomsEntryOut,
9 ComplianceDocumentIn, ComplianceDocumentOut,
10 DutyRateIn, DutyRateOut,
11 BrokerLicenseIn, BrokerLicenseOut,
12)
13from .models import CustomsEntry, ComplianceDocument, DutyRate, BrokerLicense
15router = Router(tags=["Customs"])
17# ── Customs Entries ────────────────────────────────────────────────────────────
18@router.get("/entries", auth=JWTAuth(), response={200: List[CustomsEntryOut]})
19def list_customs_entries(request, status: Optional[str] = None, entry_type: Optional[str] = None):
20 tenant_id = require_tenant(request)
21 qs = CustomsEntry.objects.filter(tenant_id=tenant_id)
22 if status:
23 qs = qs.filter(status=status)
24 if entry_type:
25 qs = qs.filter(entry_type=entry_type)
26 return list(qs)
29@router.post("/entries", auth=JWTAuth(), response={201: CustomsEntryOut})
30@require_admin
31def create_customs_entry(request, payload: CustomsEntryIn):
32 tenant_id = require_tenant(request)
33 entry = CustomsEntry.objects.create(tenant_id=tenant_id, **payload.dict())
34 return 201, entry
37@router.get("/entries/{entry_id}", auth=JWTAuth(), response={200: CustomsEntryOut})
38def get_customs_entry(request, entry_id: UUID):
39 tenant_id = require_tenant(request)
40 entry = CustomsEntry.objects.filter(id=entry_id, tenant_id=tenant_id).first()
41 if not entry:
42 raise NotFoundException("Customs entry not found.")
43 return entry
46@router.put("/entries/{entry_id}", auth=JWTAuth(), response={200: CustomsEntryOut})
47@require_admin
48def update_customs_entry(request, entry_id: UUID, payload: CustomsEntryIn):
49 tenant_id = require_tenant(request)
50 entry = CustomsEntry.objects.filter(id=entry_id, tenant_id=tenant_id).first()
51 if not entry:
52 raise NotFoundException("Customs entry not found.")
53 for key, value in payload.dict().items():
54 setattr(entry, key, value)
55 entry.save()
56 return entry
59@router.delete("/entries/{entry_id}", auth=JWTAuth())
60@require_admin
61def delete_customs_entry(request, entry_id: UUID):
62 tenant_id = require_tenant(request)
63 entry = CustomsEntry.objects.filter(id=entry_id, tenant_id=tenant_id).first()
64 if not entry:
65 raise NotFoundException("Customs entry not found.")
66 entry.delete()
67 return {"success": True}
70# ── Compliance Documents ───────────────────────────────────────────────────────
71@router.get("/documents", auth=JWTAuth(), response={200: List[ComplianceDocumentOut]})
72def list_documents(request, entry_id: Optional[UUID] = None):
73 tenant_id = require_tenant(request)
74 qs = ComplianceDocument.objects.filter(tenant_id=tenant_id)
75 if entry_id:
76 qs = qs.filter(entry_id=entry_id)
77 return list(qs.select_related("entry"))
80@router.post("/documents", auth=JWTAuth(), response={201: ComplianceDocumentOut})
81@require_admin
82def create_document(request, payload: ComplianceDocumentIn):
83 tenant_id = require_tenant(request)
84 doc = ComplianceDocument.objects.create(tenant_id=tenant_id, **payload.dict())
85 return 201, doc
88@router.delete("/documents/{doc_id}", auth=JWTAuth())
89@require_admin
90def delete_document(request, doc_id: UUID):
91 tenant_id = require_tenant(request)
92 doc = ComplianceDocument.objects.filter(id=doc_id, tenant_id=tenant_id).first()
93 if not doc:
94 raise NotFoundException("Document not found.")
95 doc.delete()
96 return {"success": True}
99# ── Duty Rates ────────────────────────────────────────────────────────────────
100@router.get("/duty-rates", auth=JWTAuth(), response={200: List[DutyRateOut]})
101def list_duty_rates(request, hs_code: Optional[str] = None):
102 tenant_id = require_tenant(request)
103 qs = DutyRate.objects.filter(tenant_id=tenant_id, is_active=True)
104 if hs_code:
105 qs = qs.filter(hs_code__startswith=hs_code)
106 return list(qs)
109@router.post("/duty-rates", auth=JWTAuth(), response={201: DutyRateOut})
110@require_admin
111def create_duty_rate(request, payload: DutyRateIn):
112 tenant_id = require_tenant(request)
113 rate = DutyRate.objects.create(tenant_id=tenant_id, **payload.dict())
114 return 201, rate
117# ── Broker Licenses ───────────────────────────────────────────────────────────
118@router.get("/broker-licenses", auth=JWTAuth(), response={200: List[BrokerLicenseOut]})
119def list_broker_licenses(request):
120 tenant_id = require_tenant(request)
121 return list(BrokerLicense.objects.filter(tenant_id=tenant_id, is_active=True))
124@router.post("/broker-licenses", auth=JWTAuth(), response={201: BrokerLicenseOut})
125@require_admin
126def create_broker_license(request, payload: BrokerLicenseIn):
127 tenant_id = require_tenant(request)
128 license = BrokerLicense.objects.create(tenant_id=tenant_id, **payload.dict())
129 return 201, license