kra-etims-sdk 0.1.2__py3-none-any.whl → 0.1.3__py3-none-any.whl

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.
@@ -3,26 +3,77 @@ from .exceptions import ApiException, AuthenticationException
3
3
 
4
4
 
5
5
  class BaseClient:
6
+ endpoints = {
7
+ # INITIALIZATION
8
+ 'selectInitOsdcInfo': '/selectInitOsdcInfo',
9
+
10
+ # CODE LIST
11
+ 'selectCodeList': '/selectCodeList',
12
+
13
+ # CUSTOMER
14
+ 'selectCustomer': '/selectCustomer',
15
+
16
+ # NOTICE
17
+ 'selectNoticeList': '/selectNoticeList',
18
+
19
+ # ITEM
20
+ 'selectItemClsList': '/selectItemClsList',
21
+ 'selectItemList': '/selectItemList',
22
+ 'saveItem': '/saveItem',
23
+ 'SaveItemComposition': '/saveItemComposition',
24
+
25
+ # BRANCH / CUSTOMER
26
+ 'selectBhfList': '/selectBhfList',
27
+ 'saveBhfCustomer': '/saveBhfCustomer',
28
+ 'saveBhfUser': '/saveBhfUser',
29
+ 'saveBhfInsurance': '/saveBhfInsurance',
30
+
31
+ # IMPORTED ITEMS
32
+ 'selectImportItemList': '/selectImportItemList',
33
+ 'updateImportItem': '/updateImportItem',
34
+
35
+ # SALES / PURCHASES
36
+ 'TrnsSalesSaveWrReq': '/saveTrnsSalesOsdc',
37
+ 'selectTrnsPurchaseSalesList': '/selectTrnsPurchaseSalesList',
38
+ 'insertTrnsPurchase': '/insertTrnsPurchase',
39
+
40
+ # STOCK
41
+ 'selectStockMoveList': '/selectStockMoveList',
42
+ 'insertStockIO': '/insertStockIO',
43
+ 'saveStockMaster': '/saveStockMaster',
44
+ }
45
+
6
46
  def __init__(self, config, auth):
7
47
  self.config = config
8
48
  self.auth = auth
9
49
 
10
50
  def base_url(self):
11
- return self.config["api"][self.config["env"]]["base_url"].rstrip("/")
51
+ env = self.config["env"]
52
+ return self.config["api"][env]["base_url"].rstrip("/")
53
+
54
+ def timeout(self):
55
+ return self.config.get("http", {}).get("timeout", 30)
56
+
57
+ def endpoint(self, key: str):
58
+ if key.startswith("/"):
59
+ raise ApiException(f"Endpoint key expected, path given [{key}]. Pass endpoint keys only.", 500)
12
60
 
13
- def endpoint(self, key):
14
- if key not in self.config["endpoints"]:
61
+ if key not in self.endpoints:
15
62
  raise ApiException(f"Endpoint [{key}] not configured", 500)
16
- return self.config["endpoints"][key]
17
63
 
18
- def post(self, endpoint_key, data):
19
- return self._send("POST", endpoint_key, data)
64
+ return self.endpoints[key]
65
+
66
+ def get(self, endpoint_key, params=None):
67
+ return self._send("GET", endpoint_key, params or {})
68
+
69
+ def post(self, endpoint_key, data=None):
70
+ return self._send("POST", endpoint_key, data or {})
20
71
 
21
72
  def _send(self, method, endpoint_key, data):
22
73
  endpoint = self.endpoint(endpoint_key)
23
74
  response = self._request(method, endpoint, data)
24
75
 
25
- if response.status_code == 401:
76
+ if self._is_token_expired(response):
26
77
  self.auth.forget_token()
27
78
  self.auth.token(force=True)
28
79
  response = self._request(method, endpoint, data)
@@ -32,8 +83,19 @@ class BaseClient:
32
83
  def _request(self, method, endpoint, data):
33
84
  url = self.base_url() + endpoint
34
85
  headers = self._headers(endpoint)
35
- print(url, headers, data)
36
- return requests.request(method, url, json=data, headers=headers, timeout=30)
86
+
87
+ if method.upper() == "GET" and data:
88
+ response = requests.get(url, params=data, headers=headers, timeout=self.timeout())
89
+ else:
90
+ response = requests.request(
91
+ method.upper(),
92
+ url,
93
+ json=data,
94
+ headers=headers,
95
+ timeout=self.timeout()
96
+ )
97
+
98
+ return response
37
99
 
38
100
  def _headers(self, endpoint):
39
101
  if endpoint.endswith("/selectInitOsdcInfo"):
@@ -47,21 +109,41 @@ class BaseClient:
47
109
  "Authorization": f"Bearer {self.auth.token()}",
48
110
  "Content-Type": "application/json",
49
111
  "Accept": "application/json",
50
- "tin": self.config["oscu"]["tin"],
51
- "bhfId": self.config["oscu"]["bhf_id"],
52
- "cmcKey": self.config["oscu"]["cmc_key"],
112
+ "tin": self.config.get("oscu", {}).get("tin", ""),
113
+ "bhfId": self.config.get("oscu", {}).get("bhf_id", ""),
114
+ "cmcKey": self.config.get("oscu", {}).get("cmc_key", ""),
53
115
  }
54
116
 
117
+ def _is_token_expired(self, response):
118
+ if response.status_code == 401:
119
+ return True
120
+ try:
121
+ fault = response.json().get("fault", {}).get("faultstring", "")
122
+ return "access token expired" in fault.lower() or "invalid token" in fault.lower()
123
+ except Exception:
124
+ return False
125
+
55
126
  def _unwrap(self, response):
56
127
  try:
57
- json = response.json()
128
+ json_data = response.json()
58
129
  except Exception:
59
130
  raise ApiException(response.text, response.status_code)
60
131
 
61
- if json.get("resultCd") and json["resultCd"] != "0000":
62
- raise ApiException(json.get("resultMsg"), 400, json["resultCd"], json)
132
+ # KRA business error
133
+ if json_data.get("resultCd") and json_data["resultCd"] != "000":
134
+ raise ApiException(
135
+ json_data.get("resultMsg", "KRA business error"),
136
+ 400,
137
+ json_data.get("resultCd"),
138
+ json_data
139
+ )
140
+
141
+ # HTTP errors
142
+ if 200 <= response.status_code < 300:
143
+ return json_data
63
144
 
64
145
  if response.status_code == 401:
65
- raise AuthenticationException("Unauthorized")
146
+ raise AuthenticationException("Unauthorized: Invalid or expired token")
66
147
 
67
- return json
148
+ message = json_data.get("fault", {}).get("faultstring", response.text)
149
+ raise ApiException(message, response.status_code)
kra_etims_sdk/client.py CHANGED
@@ -3,7 +3,6 @@ from .validator import Validator
3
3
 
4
4
 
5
5
  class EtimsClient(BaseClient):
6
-
7
6
  def __init__(self, config: dict, auth):
8
7
  super().__init__(config, auth)
9
8
  self.validator = Validator()
@@ -11,96 +10,86 @@ class EtimsClient(BaseClient):
11
10
  def _validate(self, data: dict, schema: str) -> dict:
12
11
  return self.validator.validate(data, schema)
13
12
 
13
+ # -----------------------------
14
+ # INITIALIZATION
15
+ # -----------------------------
14
16
  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
17
  return self.post("selectInitOsdcInfo", self._validate(data, "initialization"))
20
18
 
21
19
  # -----------------------------
22
- # BASIC DATA ENDPOINTS
20
+ # CODE LISTS
23
21
  # -----------------------------
24
22
  def select_code_list(self, data: dict) -> dict:
25
- return self.post("selectCodeList", self._validate(data, "codeList"))
23
+ return self.post("selectCodeList", self._validate(data, "lastReqOnly"))
26
24
 
27
- def select_item_cls_list(self, data: dict) -> dict:
28
- return self.post("selectItemClsList", self._validate(data, "itemClsList"))
25
+ # -----------------------------
26
+ # CUSTOMER / BRANCH
27
+ # -----------------------------
28
+ def select_customer(self, data: dict) -> dict:
29
+ return self.post("selectCustomer", self._validate(data, "custSearchReq"))
29
30
 
30
- def select_bhf_list(self, data: dict) -> dict:
31
- return self.post("selectBhfList", self._validate(data, "bhfList"))
31
+ def select_branches(self, data: dict) -> dict:
32
+ return self.post("selectBhfList", self._validate(data, "lastReqOnly"))
32
33
 
33
- def select_notice_list(self, data: dict) -> dict:
34
- return self.post("selectNoticeList", self._validate(data, "noticeList"))
34
+ def save_branch_customer(self, data: dict) -> dict:
35
+ return self.post("saveBhfCustomer", self._validate(data, "branchCustomer"))
35
36
 
36
- def select_taxpayer_info(self, data: dict) -> dict:
37
- return self.post("selectTaxpayerInfo", self._validate(data, "taxpayerInfo"))
37
+ def save_branch_user(self, data: dict) -> dict:
38
+ return self.post("saveBhfUser", self._validate(data, "branchUser"))
38
39
 
39
- def select_customer_list(self, data: dict) -> dict:
40
- return self.post("selectCustomerList", self._validate(data, "customerList"))
40
+ def save_branch_insurance(self, data: dict) -> dict:
41
+ return self.post("saveBhfInsurance", self._validate(data, "branchInsurance"))
41
42
 
42
43
  # -----------------------------
43
- # PURCHASE ENDPOINTS
44
+ # ITEM
44
45
  # -----------------------------
45
- def select_purchase_trns(self, data: dict) -> dict:
46
- return self.post("selectPurchaseTrns", self._validate(data, "purchaseTrns"))
46
+ def select_item_classes(self, data: dict) -> dict:
47
+ return self.post("selectItemClsList", self._validate(data, "lastReqOnly"))
48
+
49
+ def select_items(self, data: dict) -> dict:
50
+ return self.post("selectItemList", self._validate(data, "lastReqOnly"))
51
+
52
+ def save_item(self, data: dict) -> dict:
53
+ return self.post("saveItem", self._validate(data, "saveItem"))
47
54
 
48
- def send_purchase_transaction_info(self, data: dict) -> dict:
49
- return self.post(
50
- "sendPurchaseTransactionInfo",
51
- self._validate(data, "purchaseTransaction"),
52
- )
55
+ def save_item_composition(self, data: dict) -> dict:
56
+ return self.post("SaveItemComposition", self._validate(data, "itemComposition"))
53
57
 
54
58
  # -----------------------------
55
- # SALES ENDPOINTS
59
+ # IMPORTED ITEMS
56
60
  # -----------------------------
57
- def send_sales_trns(self, data: dict) -> dict:
58
- return self.post("sendSalesTrns", self._validate(data, "salesTrns"))
59
-
60
- def send_sales_transaction(self, data: dict) -> dict:
61
- return self.post(
62
- "sendSalesTransaction",
63
- self._validate(data, "salesTransaction"),
64
- )
61
+ def select_imported_items(self, data: dict) -> dict:
62
+ return self.post("selectImportItemList", self._validate(data, "lastReqOnly"))
65
63
 
66
- def select_sales_trns(self, data: dict) -> dict:
67
- return self.post("selectSalesTrns", self._validate(data, "selectSalesTrns"))
64
+ def update_imported_item(self, data: dict) -> dict:
65
+ return self.post("updateImportItem", self._validate(data, "importItemUpdate"))
68
66
 
69
67
  # -----------------------------
70
- # STOCK ENDPOINTS
68
+ # PURCHASES
71
69
  # -----------------------------
72
- def select_move_list(self, data: dict) -> dict:
73
- return self.post("selectMoveList", self._validate(data, "moveList"))
70
+ def select_purchases(self, data: dict) -> dict:
71
+ return self.post("selectTrnsPurchaseSalesList", self._validate(data, "lastReqOnly"))
74
72
 
75
- def save_stock_master(self, data: dict) -> dict:
76
- return self.post("saveStockMaster", self._validate(data, "stockMaster"))
73
+ def save_purchase(self, data: dict) -> dict:
74
+ return self.post("insertTrnsPurchase", self._validate(data, "insertTrnsPurchase"))
77
75
 
78
- def insert_stock_io(self, data: dict) -> dict:
79
- return self.post("insertStockIO", self._validate(data, "stockIO"))
76
+ def save_sales_transaction(self, data: dict) -> dict:
77
+ return self.post("TrnsSalesSaveWrReq", self._validate(data, "lastReqOnly"))
80
78
 
81
79
  # -----------------------------
82
- # BRANCH / MANAGEMENT ENDPOINTS
80
+ # STOCK
83
81
  # -----------------------------
84
- def branch_insurance_info(self, data: dict) -> dict:
85
- return self.post(
86
- "branchInsuranceInfo",
87
- self._validate(data, "branchInsurance"),
88
- )
82
+ def select_stock_movement(self, data: dict) -> dict:
83
+ return self.post("selectStockMoveList", self._validate(data, "lastReqOnly"))
89
84
 
90
- def branch_user_account(self, data: dict) -> dict:
91
- return self.post(
92
- "branchUserAccount",
93
- self._validate(data, "branchUserAccount"),
94
- )
85
+ def save_stock_io(self, data: dict) -> dict:
86
+ return self.post("insertStockIO", self._validate(data, "saveStockIO"))
95
87
 
96
- def branch_send_customer_info(self, data: dict) -> dict:
97
- return self.post(
98
- "branchSendCustomerInfo",
99
- self._validate(data, "customerInfo"),
100
- )
88
+ def save_stock_master(self, data: dict) -> dict:
89
+ return self.post("saveStockMaster", self._validate(data, "stockMaster"))
101
90
 
102
91
  # -----------------------------
103
- # ITEM / MASTER DATA
92
+ # NOTICES
104
93
  # -----------------------------
105
- def save_item(self, data: dict) -> dict:
106
- return self.post("saveItem", self._validate(data, "item"))
94
+ def select_notice_list(self, data: dict) -> dict:
95
+ return self.post("selectNoticeList", self._validate(data, "lastReqOnly"))