kra-etims-sdk 0.1.0__tar.gz → 0.1.1__tar.gz

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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: kra-etims-sdk
3
- Version: 0.1.0
3
+ Version: 0.1.1
4
4
  Summary: Python SDK for KRA eTIMS OSCU API
5
5
  Author: Emmanuel Bartile
6
6
  License: MIT
@@ -41,7 +41,7 @@ Dynamic: license-file
41
41
 
42
42
  A production-ready **Python SDK** for integrating with the Kenya Revenue Authority (KRA) **eTIMS OSCU** (Online Sales Control Unit) API. Built to match the official Postman collection specifications with strict header compliance, token management, and comprehensive Pydantic validation.
43
43
 
44
- > ⚠️ **Critical Note**: This SDK implements the **new OSCU specification** (KRA-hosted), *not* the legacy eTIMS API. OSCU requires device registration, headers, and `cmcKey` lifecycle management.
44
+ > ⚠️ **Critical Note**: This SDK implements the **new OSCU specification** (KRA-hosted), *not* the VSCU eTIMS API. OSCU requires device registration, headers, and `cmcKey` lifecycle management.
45
45
 
46
46
  ## Author
47
47
  **Bartile Emmanuel**
@@ -73,15 +73,15 @@ A production-ready **Python SDK** for integrating with the Kenya Revenue Authori
73
73
 
74
74
  ## Introduction to eTIMS OSCU
75
75
 
76
- KRA's **Electronic Tax Invoice Management System (eTIMS)** uses **OSCU** (Online Sales Control Unit) – a KRA-hosted software module that validates and signs tax invoices in real-time before issuance. Unlike legacy systems, OSCU requires:
76
+ KRA's **Electronic Tax Invoice Management System (eTIMS)** uses **OSCU** (Online Sales Control Unit) – a KRA-hosted software module that validates and signs tax invoices in real-time before issuance. Unlike VSCU, OSCU requires:
77
77
 
78
78
  - Pre-registered device serial numbers (`dvcSrlNo`)
79
79
  - Communication key (`cmcKey`) lifecycle management
80
80
  - Strict payload schema compliance per KRA specifications
81
81
 
82
- ### OSCU vs Legacy eTIMS
82
+ ### OSCU vs VSCU eTIMS
83
83
 
84
- | Feature | OSCU (This SDK) | Legacy eTIMS |
84
+ | Feature | OSCU (This SDK) | VSCU eTIMS |
85
85
  |---------|-----------------|--------------|
86
86
  | **Hosting** | KRA-hosted (cloud) | Self-hosted (on-premise) |
87
87
  | **Device Registration** | Mandatory pre-registration | Not required |
@@ -198,7 +198,7 @@ Before integration, you **MUST** complete these prerequisites:
198
198
  ### 2. Communication Key Lifecycle
199
199
  ```python
200
200
  # 1. Initialize FIRST (returns cmcKey)
201
- response = etims.initialize({
201
+ response = etims.select_init_osdc_info({
202
202
  "tin": config.oscu["tin"],
203
203
  "bhfId": config.oscu["bhf_id"],
204
204
  "dvcSrlNo": "dvcv1130", # KRA-approved serial
@@ -316,7 +316,7 @@ config = KraEtimsConfig(
316
316
 
317
317
  endpoints={
318
318
  # INITIALIZATION (ONLY endpoint without tin/bhfId/cmcKey headers)
319
- "initialize": "/initialize",
319
+ "selectInitOsdcInfo": "/selectInitOsdcInfo",
320
320
 
321
321
  # DATA MANAGEMENT
322
322
  "selectCodeList": "/selectCodeList",
@@ -386,12 +386,11 @@ except AuthenticationException as e:
386
386
  try:
387
387
  # ⚠️ MUST use KRA-approved device serial (NOT dynamic!)
388
388
  # Common sandbox test value (if pre-provisioned by KRA):
389
- device_serial = "dvcv1130" # REPLACE with your approved serial
390
389
 
391
- response = etims.initialize({
390
+ response = etims.select_init_info({
392
391
  "tin": config.oscu["tin"],
393
392
  "bhfId": config.oscu["bhf_id"],
394
- "dvcSrlNo": device_serial.strip(),
393
+ "dvcSrlNo": config.oscu["device_serial"],
395
394
  })
396
395
 
397
396
  # Extract cmcKey (sandbox returns at root level)
@@ -506,7 +505,7 @@ except ApiException as e:
506
505
 
507
506
  | Category | Purpose | Endpoints |
508
507
  |----------|---------|-----------|
509
- | **Initialization** | Device registration & cmcKey acquisition | `initialize` |
508
+ | **Initialization** | Device registration & cmcKey acquisition | `select_init_info` |
510
509
  | **Data Management** | Retrieve standard codes & master data | `select_code_list`, `select_item_cls_list`, `select_bhf_list`, `select_taxpayer_info`, `select_customer_list`, `select_notice_list` |
511
510
  | **Branch Management** | Manage branch offices & users | `branch_insurance_info`, `branch_user_account`, `branch_send_customer_info` |
512
511
  | **Item Management** | Item master data | `save_item`, `item_info` |
@@ -18,7 +18,7 @@
18
18
 
19
19
  A production-ready **Python SDK** for integrating with the Kenya Revenue Authority (KRA) **eTIMS OSCU** (Online Sales Control Unit) API. Built to match the official Postman collection specifications with strict header compliance, token management, and comprehensive Pydantic validation.
20
20
 
21
- > ⚠️ **Critical Note**: This SDK implements the **new OSCU specification** (KRA-hosted), *not* the legacy eTIMS API. OSCU requires device registration, headers, and `cmcKey` lifecycle management.
21
+ > ⚠️ **Critical Note**: This SDK implements the **new OSCU specification** (KRA-hosted), *not* the VSCU eTIMS API. OSCU requires device registration, headers, and `cmcKey` lifecycle management.
22
22
 
23
23
  ## Author
24
24
  **Bartile Emmanuel**
@@ -50,15 +50,15 @@ A production-ready **Python SDK** for integrating with the Kenya Revenue Authori
50
50
 
51
51
  ## Introduction to eTIMS OSCU
52
52
 
53
- KRA's **Electronic Tax Invoice Management System (eTIMS)** uses **OSCU** (Online Sales Control Unit) – a KRA-hosted software module that validates and signs tax invoices in real-time before issuance. Unlike legacy systems, OSCU requires:
53
+ KRA's **Electronic Tax Invoice Management System (eTIMS)** uses **OSCU** (Online Sales Control Unit) – a KRA-hosted software module that validates and signs tax invoices in real-time before issuance. Unlike VSCU, OSCU requires:
54
54
 
55
55
  - Pre-registered device serial numbers (`dvcSrlNo`)
56
56
  - Communication key (`cmcKey`) lifecycle management
57
57
  - Strict payload schema compliance per KRA specifications
58
58
 
59
- ### OSCU vs Legacy eTIMS
59
+ ### OSCU vs VSCU eTIMS
60
60
 
61
- | Feature | OSCU (This SDK) | Legacy eTIMS |
61
+ | Feature | OSCU (This SDK) | VSCU eTIMS |
62
62
  |---------|-----------------|--------------|
63
63
  | **Hosting** | KRA-hosted (cloud) | Self-hosted (on-premise) |
64
64
  | **Device Registration** | Mandatory pre-registration | Not required |
@@ -175,7 +175,7 @@ Before integration, you **MUST** complete these prerequisites:
175
175
  ### 2. Communication Key Lifecycle
176
176
  ```python
177
177
  # 1. Initialize FIRST (returns cmcKey)
178
- response = etims.initialize({
178
+ response = etims.select_init_osdc_info({
179
179
  "tin": config.oscu["tin"],
180
180
  "bhfId": config.oscu["bhf_id"],
181
181
  "dvcSrlNo": "dvcv1130", # KRA-approved serial
@@ -293,7 +293,7 @@ config = KraEtimsConfig(
293
293
 
294
294
  endpoints={
295
295
  # INITIALIZATION (ONLY endpoint without tin/bhfId/cmcKey headers)
296
- "initialize": "/initialize",
296
+ "selectInitOsdcInfo": "/selectInitOsdcInfo",
297
297
 
298
298
  # DATA MANAGEMENT
299
299
  "selectCodeList": "/selectCodeList",
@@ -363,12 +363,11 @@ except AuthenticationException as e:
363
363
  try:
364
364
  # ⚠️ MUST use KRA-approved device serial (NOT dynamic!)
365
365
  # Common sandbox test value (if pre-provisioned by KRA):
366
- device_serial = "dvcv1130" # REPLACE with your approved serial
367
366
 
368
- response = etims.initialize({
367
+ response = etims.select_init_info({
369
368
  "tin": config.oscu["tin"],
370
369
  "bhfId": config.oscu["bhf_id"],
371
- "dvcSrlNo": device_serial.strip(),
370
+ "dvcSrlNo": config.oscu["device_serial"],
372
371
  })
373
372
 
374
373
  # Extract cmcKey (sandbox returns at root level)
@@ -483,7 +482,7 @@ except ApiException as e:
483
482
 
484
483
  | Category | Purpose | Endpoints |
485
484
  |----------|---------|-----------|
486
- | **Initialization** | Device registration & cmcKey acquisition | `initialize` |
485
+ | **Initialization** | Device registration & cmcKey acquisition | `select_init_info` |
487
486
  | **Data Management** | Retrieve standard codes & master data | `select_code_list`, `select_item_cls_list`, `select_bhf_list`, `select_taxpayer_info`, `select_customer_list`, `select_notice_list` |
488
487
  | **Branch Management** | Manage branch offices & users | `branch_insurance_info`, `branch_user_account`, `branch_send_customer_info` |
489
488
  | **Item Management** | Item master data | `save_item`, `item_info` |
@@ -32,10 +32,11 @@ class BaseClient:
32
32
  def _request(self, method, endpoint, data):
33
33
  url = self.base_url() + endpoint
34
34
  headers = self._headers(endpoint)
35
+ print(url, headers, data)
35
36
  return requests.request(method, url, json=data, headers=headers, timeout=30)
36
37
 
37
38
  def _headers(self, endpoint):
38
- if endpoint.endswith("/initialize"):
39
+ if endpoint.endswith("/selectInitOsdcInfo"):
39
40
  return {
40
41
  "Authorization": f"Bearer {self.auth.token()}",
41
42
  "Content-Type": "application/json",
@@ -11,12 +11,12 @@ class EtimsClient(BaseClient):
11
11
  def _validate(self, data: dict, schema: str) -> dict:
12
12
  return self.validator.validate(data, schema)
13
13
 
14
- # -----------------------------
15
- # INITIALIZATION (POSTMAN-COMPLIANT)
16
- # -----------------------------
17
- def initialize(self, data: dict) -> dict:
18
- validated = self._validate(data, "initialization")
19
- return self.post("initialize", validated)
14
+ def select_init_osdc_info(self, data: dict) -> dict:
15
+ """
16
+ Initialize the OSCU device with KRA.
17
+ Returns cmcKey and device information.
18
+ """
19
+ return self.post("selectInitOsdcInfo", self._validate(data, "initialization"))
20
20
 
21
21
  # -----------------------------
22
22
  # BASIC DATA ENDPOINTS
@@ -14,7 +14,7 @@ from pydantic import (
14
14
  # =========================================================
15
15
 
16
16
  TIN = Annotated[str, StringConstraints(min_length=1, max_length=20)]
17
- BHF = Annotated[str, StringConstraints(min_length=1, max_length=10)]
17
+ BHF = Annotated[str, StringConstraints(min_length=1, max_length=20)]
18
18
 
19
19
  YN = Annotated[str, StringConstraints(pattern="^[YN]$")]
20
20
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: kra-etims-sdk
3
- Version: 0.1.0
3
+ Version: 0.1.1
4
4
  Summary: Python SDK for KRA eTIMS OSCU API
5
5
  Author: Emmanuel Bartile
6
6
  License: MIT
@@ -41,7 +41,7 @@ Dynamic: license-file
41
41
 
42
42
  A production-ready **Python SDK** for integrating with the Kenya Revenue Authority (KRA) **eTIMS OSCU** (Online Sales Control Unit) API. Built to match the official Postman collection specifications with strict header compliance, token management, and comprehensive Pydantic validation.
43
43
 
44
- > ⚠️ **Critical Note**: This SDK implements the **new OSCU specification** (KRA-hosted), *not* the legacy eTIMS API. OSCU requires device registration, headers, and `cmcKey` lifecycle management.
44
+ > ⚠️ **Critical Note**: This SDK implements the **new OSCU specification** (KRA-hosted), *not* the VSCU eTIMS API. OSCU requires device registration, headers, and `cmcKey` lifecycle management.
45
45
 
46
46
  ## Author
47
47
  **Bartile Emmanuel**
@@ -73,15 +73,15 @@ A production-ready **Python SDK** for integrating with the Kenya Revenue Authori
73
73
 
74
74
  ## Introduction to eTIMS OSCU
75
75
 
76
- KRA's **Electronic Tax Invoice Management System (eTIMS)** uses **OSCU** (Online Sales Control Unit) – a KRA-hosted software module that validates and signs tax invoices in real-time before issuance. Unlike legacy systems, OSCU requires:
76
+ KRA's **Electronic Tax Invoice Management System (eTIMS)** uses **OSCU** (Online Sales Control Unit) – a KRA-hosted software module that validates and signs tax invoices in real-time before issuance. Unlike VSCU, OSCU requires:
77
77
 
78
78
  - Pre-registered device serial numbers (`dvcSrlNo`)
79
79
  - Communication key (`cmcKey`) lifecycle management
80
80
  - Strict payload schema compliance per KRA specifications
81
81
 
82
- ### OSCU vs Legacy eTIMS
82
+ ### OSCU vs VSCU eTIMS
83
83
 
84
- | Feature | OSCU (This SDK) | Legacy eTIMS |
84
+ | Feature | OSCU (This SDK) | VSCU eTIMS |
85
85
  |---------|-----------------|--------------|
86
86
  | **Hosting** | KRA-hosted (cloud) | Self-hosted (on-premise) |
87
87
  | **Device Registration** | Mandatory pre-registration | Not required |
@@ -198,7 +198,7 @@ Before integration, you **MUST** complete these prerequisites:
198
198
  ### 2. Communication Key Lifecycle
199
199
  ```python
200
200
  # 1. Initialize FIRST (returns cmcKey)
201
- response = etims.initialize({
201
+ response = etims.select_init_osdc_info({
202
202
  "tin": config.oscu["tin"],
203
203
  "bhfId": config.oscu["bhf_id"],
204
204
  "dvcSrlNo": "dvcv1130", # KRA-approved serial
@@ -316,7 +316,7 @@ config = KraEtimsConfig(
316
316
 
317
317
  endpoints={
318
318
  # INITIALIZATION (ONLY endpoint without tin/bhfId/cmcKey headers)
319
- "initialize": "/initialize",
319
+ "selectInitOsdcInfo": "/selectInitOsdcInfo",
320
320
 
321
321
  # DATA MANAGEMENT
322
322
  "selectCodeList": "/selectCodeList",
@@ -386,12 +386,11 @@ except AuthenticationException as e:
386
386
  try:
387
387
  # ⚠️ MUST use KRA-approved device serial (NOT dynamic!)
388
388
  # Common sandbox test value (if pre-provisioned by KRA):
389
- device_serial = "dvcv1130" # REPLACE with your approved serial
390
389
 
391
- response = etims.initialize({
390
+ response = etims.select_init_info({
392
391
  "tin": config.oscu["tin"],
393
392
  "bhfId": config.oscu["bhf_id"],
394
- "dvcSrlNo": device_serial.strip(),
393
+ "dvcSrlNo": config.oscu["device_serial"],
395
394
  })
396
395
 
397
396
  # Extract cmcKey (sandbox returns at root level)
@@ -506,7 +505,7 @@ except ApiException as e:
506
505
 
507
506
  | Category | Purpose | Endpoints |
508
507
  |----------|---------|-----------|
509
- | **Initialization** | Device registration & cmcKey acquisition | `initialize` |
508
+ | **Initialization** | Device registration & cmcKey acquisition | `select_init_info` |
510
509
  | **Data Management** | Retrieve standard codes & master data | `select_code_list`, `select_item_cls_list`, `select_bhf_list`, `select_taxpayer_info`, `select_customer_list`, `select_notice_list` |
511
510
  | **Branch Management** | Manage branch offices & users | `branch_insurance_info`, `branch_user_account`, `branch_send_customer_info` |
512
511
  | **Item Management** | Item master data | `save_item`, `item_info` |
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "kra-etims-sdk"
7
- version = "0.1.0"
7
+ version = "0.1.1"
8
8
  description = "Python SDK for KRA eTIMS OSCU API"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.9"
@@ -60,10 +60,10 @@ CONFIG = {
60
60
 
61
61
  "api": {
62
62
  "sbx": {
63
- "base_url": "https://sbx.kra.go.ke/etims-oscu/api/v1",
63
+ "base_url": "https://etims-api-sbx.kra.go.ke/etims-api",
64
64
  },
65
65
  "prod": {
66
- "base_url": "https://kra.go.ke/etims-oscu/api/v1",
66
+ "base_url": "https://etims-api.kra.go.ke/etims-api",
67
67
  },
68
68
  },
69
69
 
@@ -72,13 +72,14 @@ CONFIG = {
72
72
  },
73
73
 
74
74
  "oscu": {
75
- "tin": os.getenv("KRA_TIN", "P000000002"),
76
- "bhf_id": os.getenv("KRA_BHF_ID", "00"),
77
- "cmc_key": "",
75
+ "tin": os.getenv("KRA_TIN", ""),
76
+ "bhf_id": os.getenv("KRA_BHF_ID", ""),
77
+ "cmc_key": os.getenv("CMC_KEY", ""),
78
+ "device_serial": os.getenv("DEVICE_SERIAL", ""),
78
79
  },
79
80
 
80
81
  "endpoints": {
81
- "initialize": "/initialize",
82
+ "selectInitOsdcInfo": "/selectInitOsdcInfo",
82
83
  "selectCodeList": "/selectCodeList",
83
84
  "selectTaxpayerInfo": "/selectTaxpayerInfo",
84
85
  "selectNoticeList": "/selectNoticeList",
@@ -124,28 +125,26 @@ except AuthenticationException as e:
124
125
  header("STEP 2: OSCU INITIALIZATION")
125
126
  warning("Device serial MUST be pre-registered with KRA")
126
127
 
127
- DEVICE_SERIAL = "dvcv1130" # 🔴 REPLACE WITH APPROVED SERIAL
128
-
129
128
  try:
130
129
  etims = EtimsClient(CONFIG, auth)
131
130
 
132
- response = etims.initialize({
133
- "tin": CONFIG["oscu"]["tin"],
134
- "bhfId": CONFIG["oscu"]["bhf_id"],
135
- "dvcSrlNo": DEVICE_SERIAL,
136
- })
131
+ # response = etims.select_init_info({
132
+ # "tin": CONFIG["oscu"]["tin"],
133
+ # "bhfId": CONFIG["oscu"]["bhf_id"],
134
+ # "dvcSrlNo": CONFIG["oscu"]["device_serial"],
135
+ # })
137
136
 
138
- pprint(response)
137
+ # pprint(response)
138
+
139
+ # cmc_key = response.get("cmcKey") or response.get("data", {}).get("info", {}).get("cmcKey")
140
+ # if not cmc_key:
141
+ # raise RuntimeError("cmcKey missing from initialization response")
139
142
 
140
- cmc_key = response.get("cmcKey") or response.get("data", {}).get("cmcKey")
141
- if not cmc_key:
142
- raise RuntimeError("cmcKey missing from initialization response")
143
+ # success("Initialization successful")
144
+ # print("cmcKey:", cmc_key[:15], "...")
143
145
 
144
- CONFIG["oscu"]["cmc_key"] = cmc_key
145
146
  etims = EtimsClient(CONFIG, auth)
146
147
 
147
- success("Initialization successful")
148
- print("cmcKey:", cmc_key[:15], "...")
149
148
  except ApiException as e:
150
149
  error(f"Initialization failed: {e}")
151
150
  pprint(e.details)
@@ -160,10 +159,10 @@ try:
160
159
  "bhfId": CONFIG["oscu"]["bhf_id"],
161
160
  "lastReqDt": format_date(),
162
161
  })
163
-
164
162
  count = len(response.get("itemList", []))
165
163
  success(f"Retrieved {count} code list items")
166
164
  except ApiException as e:
165
+ print("emm")
167
166
  error(str(e))
168
167
  sys.exit(1)
169
168
 
File without changes
File without changes