b1sl-python 0.1.0__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.
Files changed (579) hide show
  1. b1sl/__init__.py +5 -0
  2. b1sl/b1sl/__init__.py +56 -0
  3. b1sl/b1sl/_types.py +28 -0
  4. b1sl/b1sl/adapter.py +40 -0
  5. b1sl/b1sl/adapter_protocol.py +28 -0
  6. b1sl/b1sl/async_client.py +268 -0
  7. b1sl/b1sl/async_rest_adapter.py +347 -0
  8. b1sl/b1sl/base_adapter.py +394 -0
  9. b1sl/b1sl/client.py +112 -0
  10. b1sl/b1sl/config.py +138 -0
  11. b1sl/b1sl/config_manager.py +77 -0
  12. b1sl/b1sl/contrib/__init__.py +14 -0
  13. b1sl/b1sl/contrib/django/__init__.py +13 -0
  14. b1sl/b1sl/contrib/django/middleware/__init__.py +27 -0
  15. b1sl/b1sl/contrib/django/middleware/base.py +89 -0
  16. b1sl/b1sl/contrib/django/middleware/odata_decode_middleware.py +20 -0
  17. b1sl/b1sl/contrib/django/middleware/odata_transform_url_middleware.py +20 -0
  18. b1sl/b1sl/entities/__init__.py +2209 -0
  19. b1sl/b1sl/environment.py +36 -0
  20. b1sl/b1sl/exceptions/exceptions.py +142 -0
  21. b1sl/b1sl/fields/__init__.py +2020 -0
  22. b1sl/b1sl/fields/_generated/__init__.py +0 -0
  23. b1sl/b1sl/fields/_generated/complex_types.py +6926 -0
  24. b1sl/b1sl/fields/_generated/entities/__init__.py +0 -0
  25. b1sl/b1sl/fields/_generated/entities/businesspartners.py +544 -0
  26. b1sl/b1sl/fields/_generated/entities/finance.py +451 -0
  27. b1sl/b1sl/fields/_generated/entities/general.py +4587 -0
  28. b1sl/b1sl/fields/_generated/entities/inventory.py +761 -0
  29. b1sl/b1sl/fields/_generated/entities/production.py +256 -0
  30. b1sl/b1sl/fields/_generated/entities/purchasing.py +53 -0
  31. b1sl/b1sl/fields/_generated/entities/sales.py +244 -0
  32. b1sl/b1sl/logging_utils.py +119 -0
  33. b1sl/b1sl/models/__init__.py +13 -0
  34. b1sl/b1sl/models/_generated/__init__.py +0 -0
  35. b1sl/b1sl/models/_generated/_types.py +5 -0
  36. b1sl/b1sl/models/_generated/complex_types.py +6929 -0
  37. b1sl/b1sl/models/_generated/entities/__init__.py +581 -0
  38. b1sl/b1sl/models/_generated/entities/businesspartners.py +565 -0
  39. b1sl/b1sl/models/_generated/entities/finance.py +472 -0
  40. b1sl/b1sl/models/_generated/entities/general.py +4831 -0
  41. b1sl/b1sl/models/_generated/entities/inventory.py +786 -0
  42. b1sl/b1sl/models/_generated/entities/production.py +272 -0
  43. b1sl/b1sl/models/_generated/entities/purchasing.py +64 -0
  44. b1sl/b1sl/models/_generated/entities/sales.py +263 -0
  45. b1sl/b1sl/models/_generated/enums.py +3501 -0
  46. b1sl/b1sl/models/_generated/resources/__init__.py +0 -0
  47. b1sl/b1sl/models/_overrides/inventory.py +24 -0
  48. b1sl/b1sl/models/base.py +209 -0
  49. b1sl/b1sl/models/odata_query_model.py +50 -0
  50. b1sl/b1sl/models/paginated_result.py +85 -0
  51. b1sl/b1sl/models/result.py +17 -0
  52. b1sl/b1sl/resources/__init__.py +11 -0
  53. b1sl/b1sl/resources/_generated/__init__.py +0 -0
  54. b1sl/b1sl/resources/_generated/account_category.py +15 -0
  55. b1sl/b1sl/resources/_generated/account_category_service.py +21 -0
  56. b1sl/b1sl/resources/_generated/account_segmentation_categories.py +15 -0
  57. b1sl/b1sl/resources/_generated/account_segmentations.py +15 -0
  58. b1sl/b1sl/resources/_generated/accounts_service.py +33 -0
  59. b1sl/b1sl/resources/_generated/accrual_types.py +15 -0
  60. b1sl/b1sl/resources/_generated/accrual_types_service.py +21 -0
  61. b1sl/b1sl/resources/_generated/activities.py +15 -0
  62. b1sl/b1sl/resources/_generated/activities_service.py +109 -0
  63. b1sl/b1sl/resources/_generated/activity_locations.py +15 -0
  64. b1sl/b1sl/resources/_generated/activity_recipient_lists.py +15 -0
  65. b1sl/b1sl/resources/_generated/activity_recipient_lists_service.py +21 -0
  66. b1sl/b1sl/resources/_generated/activity_statuses.py +15 -0
  67. b1sl/b1sl/resources/_generated/activity_types.py +15 -0
  68. b1sl/b1sl/resources/_generated/additional_expenses.py +15 -0
  69. b1sl/b1sl/resources/_generated/address_service.py +38 -0
  70. b1sl/b1sl/resources/_generated/alert_managements.py +16 -0
  71. b1sl/b1sl/resources/_generated/alternate_cat_num.py +15 -0
  72. b1sl/b1sl/resources/_generated/alternative_items_service.py +113 -0
  73. b1sl/b1sl/resources/_generated/approval_requests.py +15 -0
  74. b1sl/b1sl/resources/_generated/approval_requests_service.py +33 -0
  75. b1sl/b1sl/resources/_generated/approval_stages.py +15 -0
  76. b1sl/b1sl/resources/_generated/approval_stages_service.py +21 -0
  77. b1sl/b1sl/resources/_generated/approval_templates.py +15 -0
  78. b1sl/b1sl/resources/_generated/approval_templates_service.py +21 -0
  79. b1sl/b1sl/resources/_generated/asset_capitalization.py +15 -0
  80. b1sl/b1sl/resources/_generated/asset_capitalization_credit_memo.py +15 -0
  81. b1sl/b1sl/resources/_generated/asset_capitalization_credit_memo_service.py +37 -0
  82. b1sl/b1sl/resources/_generated/asset_capitalization_service.py +37 -0
  83. b1sl/b1sl/resources/_generated/asset_classes.py +15 -0
  84. b1sl/b1sl/resources/_generated/asset_classes_service.py +21 -0
  85. b1sl/b1sl/resources/_generated/asset_depreciation_groups.py +15 -0
  86. b1sl/b1sl/resources/_generated/asset_depreciation_groups_service.py +21 -0
  87. b1sl/b1sl/resources/_generated/asset_groups.py +15 -0
  88. b1sl/b1sl/resources/_generated/asset_groups_service.py +21 -0
  89. b1sl/b1sl/resources/_generated/asset_manual_depreciation.py +15 -0
  90. b1sl/b1sl/resources/_generated/asset_manual_depreciation_service.py +37 -0
  91. b1sl/b1sl/resources/_generated/asset_retirement.py +15 -0
  92. b1sl/b1sl/resources/_generated/asset_retirement_service.py +37 -0
  93. b1sl/b1sl/resources/_generated/asset_transfer.py +15 -0
  94. b1sl/b1sl/resources/_generated/asset_transfer_service.py +37 -0
  95. b1sl/b1sl/resources/_generated/attachments2.py +15 -0
  96. b1sl/b1sl/resources/_generated/attribute_groups.py +15 -0
  97. b1sl/b1sl/resources/_generated/attribute_groups_service.py +21 -0
  98. b1sl/b1sl/resources/_generated/bank_charges_allocation_codes.py +15 -0
  99. b1sl/b1sl/resources/_generated/bank_charges_allocation_codes_service.py +21 -0
  100. b1sl/b1sl/resources/_generated/bank_pages.py +15 -0
  101. b1sl/b1sl/resources/_generated/bank_statements.py +15 -0
  102. b1sl/b1sl/resources/_generated/bank_statements_service.py +32 -0
  103. b1sl/b1sl/resources/_generated/banks.py +15 -0
  104. b1sl/b1sl/resources/_generated/bar_codes.py +15 -0
  105. b1sl/b1sl/resources/_generated/bar_codes_service.py +21 -0
  106. b1sl/b1sl/resources/_generated/batch_number_details.py +15 -0
  107. b1sl/b1sl/resources/_generated/bill_of_exchange_transactions.py +15 -0
  108. b1sl/b1sl/resources/_generated/bin_location_attributes.py +15 -0
  109. b1sl/b1sl/resources/_generated/bin_location_attributes_service.py +21 -0
  110. b1sl/b1sl/resources/_generated/bin_location_fields.py +15 -0
  111. b1sl/b1sl/resources/_generated/bin_location_fields_service.py +21 -0
  112. b1sl/b1sl/resources/_generated/bin_locations.py +15 -0
  113. b1sl/b1sl/resources/_generated/bin_locations_service.py +21 -0
  114. b1sl/b1sl/resources/_generated/blanket_agreements.py +15 -0
  115. b1sl/b1sl/resources/_generated/blanket_agreements_service.py +21 -0
  116. b1sl/b1sl/resources/_generated/boe_document_types.py +15 -0
  117. b1sl/b1sl/resources/_generated/boe_document_types_service.py +21 -0
  118. b1sl/b1sl/resources/_generated/boe_instructions.py +15 -0
  119. b1sl/b1sl/resources/_generated/boe_instructions_service.py +21 -0
  120. b1sl/b1sl/resources/_generated/boe_lines_service.py +30 -0
  121. b1sl/b1sl/resources/_generated/boe_portfolios.py +15 -0
  122. b1sl/b1sl/resources/_generated/boe_portfolios_service.py +21 -0
  123. b1sl/b1sl/resources/_generated/bp_fiscal_registry_id.py +15 -0
  124. b1sl/b1sl/resources/_generated/bp_opening_balance_service.py +41 -0
  125. b1sl/b1sl/resources/_generated/bp_priorities.py +15 -0
  126. b1sl/b1sl/resources/_generated/branches.py +15 -0
  127. b1sl/b1sl/resources/_generated/branches_service.py +21 -0
  128. b1sl/b1sl/resources/_generated/brazil_beverage_indexers.py +15 -0
  129. b1sl/b1sl/resources/_generated/brazil_beverage_indexers_service.py +21 -0
  130. b1sl/b1sl/resources/_generated/brazil_fuel_indexers.py +15 -0
  131. b1sl/b1sl/resources/_generated/brazil_fuel_indexers_service.py +21 -0
  132. b1sl/b1sl/resources/_generated/brazil_multi_indexers.py +15 -0
  133. b1sl/b1sl/resources/_generated/brazil_numeric_indexers.py +15 -0
  134. b1sl/b1sl/resources/_generated/brazil_string_indexers.py +15 -0
  135. b1sl/b1sl/resources/_generated/budget_distributions.py +15 -0
  136. b1sl/b1sl/resources/_generated/budget_scenarios.py +15 -0
  137. b1sl/b1sl/resources/_generated/budgets.py +15 -0
  138. b1sl/b1sl/resources/_generated/business_partner_groups.py +15 -0
  139. b1sl/b1sl/resources/_generated/business_partner_properties.py +15 -0
  140. b1sl/b1sl/resources/_generated/business_partner_properties_service.py +21 -0
  141. b1sl/b1sl/resources/_generated/business_partners.py +15 -0
  142. b1sl/b1sl/resources/_generated/business_partners_service.py +47 -0
  143. b1sl/b1sl/resources/_generated/business_places.py +15 -0
  144. b1sl/b1sl/resources/_generated/campaign_response_type.py +15 -0
  145. b1sl/b1sl/resources/_generated/campaign_response_type_service.py +21 -0
  146. b1sl/b1sl/resources/_generated/campaigns.py +15 -0
  147. b1sl/b1sl/resources/_generated/campaigns_service.py +21 -0
  148. b1sl/b1sl/resources/_generated/cash_discounts.py +15 -0
  149. b1sl/b1sl/resources/_generated/cash_discounts_service.py +21 -0
  150. b1sl/b1sl/resources/_generated/cash_flow_line_items.py +15 -0
  151. b1sl/b1sl/resources/_generated/cash_flow_line_items_service.py +21 -0
  152. b1sl/b1sl/resources/_generated/certificate_series.py +15 -0
  153. b1sl/b1sl/resources/_generated/certificate_series_service.py +21 -0
  154. b1sl/b1sl/resources/_generated/change_logs_service.py +51 -0
  155. b1sl/b1sl/resources/_generated/chart_of_accounts.py +15 -0
  156. b1sl/b1sl/resources/_generated/check_lines_service.py +38 -0
  157. b1sl/b1sl/resources/_generated/checksfor_payment.py +15 -0
  158. b1sl/b1sl/resources/_generated/choose_from_list.py +15 -0
  159. b1sl/b1sl/resources/_generated/client_mixin.py +3920 -0
  160. b1sl/b1sl/resources/_generated/closing_date_procedure.py +15 -0
  161. b1sl/b1sl/resources/_generated/cockpits.py +15 -0
  162. b1sl/b1sl/resources/_generated/cockpits_service.py +46 -0
  163. b1sl/b1sl/resources/_generated/commission_groups.py +15 -0
  164. b1sl/b1sl/resources/_generated/company_service.py +410 -0
  165. b1sl/b1sl/resources/_generated/contacts.py +15 -0
  166. b1sl/b1sl/resources/_generated/contract_templates.py +15 -0
  167. b1sl/b1sl/resources/_generated/correction_invoice.py +15 -0
  168. b1sl/b1sl/resources/_generated/correction_invoice_reversal.py +15 -0
  169. b1sl/b1sl/resources/_generated/correction_invoice_reversal_service.py +65 -0
  170. b1sl/b1sl/resources/_generated/correction_invoice_service.py +78 -0
  171. b1sl/b1sl/resources/_generated/correction_purchase_invoice.py +15 -0
  172. b1sl/b1sl/resources/_generated/correction_purchase_invoice_reversal.py +15 -0
  173. b1sl/b1sl/resources/_generated/correction_purchase_invoice_reversal_service.py +65 -0
  174. b1sl/b1sl/resources/_generated/correction_purchase_invoice_service.py +78 -0
  175. b1sl/b1sl/resources/_generated/cost_center_types.py +15 -0
  176. b1sl/b1sl/resources/_generated/cost_center_types_service.py +21 -0
  177. b1sl/b1sl/resources/_generated/cost_element_service.py +21 -0
  178. b1sl/b1sl/resources/_generated/cost_elements.py +15 -0
  179. b1sl/b1sl/resources/_generated/counties.py +15 -0
  180. b1sl/b1sl/resources/_generated/counties_service.py +21 -0
  181. b1sl/b1sl/resources/_generated/countries.py +15 -0
  182. b1sl/b1sl/resources/_generated/countries_service.py +21 -0
  183. b1sl/b1sl/resources/_generated/credit_card_payments.py +15 -0
  184. b1sl/b1sl/resources/_generated/credit_cards.py +15 -0
  185. b1sl/b1sl/resources/_generated/credit_lines_service.py +38 -0
  186. b1sl/b1sl/resources/_generated/credit_notes.py +15 -0
  187. b1sl/b1sl/resources/_generated/credit_notes_service.py +100 -0
  188. b1sl/b1sl/resources/_generated/credit_payment_methods.py +15 -0
  189. b1sl/b1sl/resources/_generated/currencies.py +15 -0
  190. b1sl/b1sl/resources/_generated/customer_equipment_cards.py +15 -0
  191. b1sl/b1sl/resources/_generated/customs_declaration.py +15 -0
  192. b1sl/b1sl/resources/_generated/customs_groups.py +15 -0
  193. b1sl/b1sl/resources/_generated/cycle_count_determinations.py +15 -0
  194. b1sl/b1sl/resources/_generated/cycle_count_determinations_service.py +21 -0
  195. b1sl/b1sl/resources/_generated/dashboard_packages_service.py +28 -0
  196. b1sl/b1sl/resources/_generated/deductible_tax_service.py +21 -0
  197. b1sl/b1sl/resources/_generated/deductible_taxes.py +15 -0
  198. b1sl/b1sl/resources/_generated/deduction_tax_groups.py +15 -0
  199. b1sl/b1sl/resources/_generated/deduction_tax_hierarchies.py +15 -0
  200. b1sl/b1sl/resources/_generated/deduction_tax_sub_groups.py +15 -0
  201. b1sl/b1sl/resources/_generated/deduction_tax_sub_groups_service.py +21 -0
  202. b1sl/b1sl/resources/_generated/delivery_notes.py +15 -0
  203. b1sl/b1sl/resources/_generated/delivery_notes_service.py +85 -0
  204. b1sl/b1sl/resources/_generated/departments.py +15 -0
  205. b1sl/b1sl/resources/_generated/departments_service.py +21 -0
  206. b1sl/b1sl/resources/_generated/deposits.py +15 -0
  207. b1sl/b1sl/resources/_generated/deposits_service.py +53 -0
  208. b1sl/b1sl/resources/_generated/depreciation_areas.py +15 -0
  209. b1sl/b1sl/resources/_generated/depreciation_areas_service.py +21 -0
  210. b1sl/b1sl/resources/_generated/depreciation_type_pools.py +15 -0
  211. b1sl/b1sl/resources/_generated/depreciation_type_pools_service.py +21 -0
  212. b1sl/b1sl/resources/_generated/depreciation_types.py +15 -0
  213. b1sl/b1sl/resources/_generated/depreciation_types_service.py +21 -0
  214. b1sl/b1sl/resources/_generated/determination_criterias.py +15 -0
  215. b1sl/b1sl/resources/_generated/determination_criterias_service.py +21 -0
  216. b1sl/b1sl/resources/_generated/dimensions.py +15 -0
  217. b1sl/b1sl/resources/_generated/dimensions_service.py +21 -0
  218. b1sl/b1sl/resources/_generated/distribution_rules.py +15 -0
  219. b1sl/b1sl/resources/_generated/distribution_rules_service.py +21 -0
  220. b1sl/b1sl/resources/_generated/dnf_code_setup.py +15 -0
  221. b1sl/b1sl/resources/_generated/dnf_code_setup_service.py +21 -0
  222. b1sl/b1sl/resources/_generated/down_payments.py +15 -0
  223. b1sl/b1sl/resources/_generated/down_payments_service.py +68 -0
  224. b1sl/b1sl/resources/_generated/drafts.py +15 -0
  225. b1sl/b1sl/resources/_generated/drafts_service.py +86 -0
  226. b1sl/b1sl/resources/_generated/dunning_letters.py +15 -0
  227. b1sl/b1sl/resources/_generated/dunning_terms.py +15 -0
  228. b1sl/b1sl/resources/_generated/dunning_terms_service.py +21 -0
  229. b1sl/b1sl/resources/_generated/dynamic_system_strings.py +15 -0
  230. b1sl/b1sl/resources/_generated/electronic_communication_action_service.py +122 -0
  231. b1sl/b1sl/resources/_generated/electronic_communication_actions_service.py +119 -0
  232. b1sl/b1sl/resources/_generated/electronic_file_formats.py +15 -0
  233. b1sl/b1sl/resources/_generated/electronic_file_formats_service.py +21 -0
  234. b1sl/b1sl/resources/_generated/email_groups.py +15 -0
  235. b1sl/b1sl/resources/_generated/email_groups_service.py +21 -0
  236. b1sl/b1sl/resources/_generated/employee_id_type.py +15 -0
  237. b1sl/b1sl/resources/_generated/employee_id_type_service.py +21 -0
  238. b1sl/b1sl/resources/_generated/employee_position.py +15 -0
  239. b1sl/b1sl/resources/_generated/employee_position_service.py +21 -0
  240. b1sl/b1sl/resources/_generated/employee_roles_setup.py +15 -0
  241. b1sl/b1sl/resources/_generated/employee_roles_setup_service.py +21 -0
  242. b1sl/b1sl/resources/_generated/employee_status.py +15 -0
  243. b1sl/b1sl/resources/_generated/employee_status_service.py +21 -0
  244. b1sl/b1sl/resources/_generated/employee_transfers.py +15 -0
  245. b1sl/b1sl/resources/_generated/employee_transfers_service.py +21 -0
  246. b1sl/b1sl/resources/_generated/employees_info.py +15 -0
  247. b1sl/b1sl/resources/_generated/employment_category_service.py +21 -0
  248. b1sl/b1sl/resources/_generated/employment_categorys.py +15 -0
  249. b1sl/b1sl/resources/_generated/enhanced_discount_groups.py +15 -0
  250. b1sl/b1sl/resources/_generated/enhanced_discount_groups_service.py +21 -0
  251. b1sl/b1sl/resources/_generated/exceptional_event_service.py +21 -0
  252. b1sl/b1sl/resources/_generated/exceptional_events.py +15 -0
  253. b1sl/b1sl/resources/_generated/extended_translations.py +15 -0
  254. b1sl/b1sl/resources/_generated/extended_translations_service.py +21 -0
  255. b1sl/b1sl/resources/_generated/external_calls_service.py +54 -0
  256. b1sl/b1sl/resources/_generated/external_reconciliations_service.py +104 -0
  257. b1sl/b1sl/resources/_generated/fa_account_determinations.py +15 -0
  258. b1sl/b1sl/resources/_generated/fa_account_determinations_service.py +21 -0
  259. b1sl/b1sl/resources/_generated/factoring_indicators.py +15 -0
  260. b1sl/b1sl/resources/_generated/financial_years.py +15 -0
  261. b1sl/b1sl/resources/_generated/financial_years_service.py +21 -0
  262. b1sl/b1sl/resources/_generated/fiscal_printer.py +15 -0
  263. b1sl/b1sl/resources/_generated/fiscal_printer_service.py +21 -0
  264. b1sl/b1sl/resources/_generated/fixed_asset_items_service.py +55 -0
  265. b1sl/b1sl/resources/_generated/form_preferences.py +15 -0
  266. b1sl/b1sl/resources/_generated/formatted_searches.py +15 -0
  267. b1sl/b1sl/resources/_generated/forms1099.py +15 -0
  268. b1sl/b1sl/resources/_generated/gl_account_advanced_rules.py +15 -0
  269. b1sl/b1sl/resources/_generated/gl_account_advanced_rules_service.py +21 -0
  270. b1sl/b1sl/resources/_generated/goods_return_request.py +15 -0
  271. b1sl/b1sl/resources/_generated/goods_return_request_service.py +69 -0
  272. b1sl/b1sl/resources/_generated/gov_pay_codes.py +15 -0
  273. b1sl/b1sl/resources/_generated/gov_pay_codes_service.py +21 -0
  274. b1sl/b1sl/resources/_generated/gt_is_service.py +28 -0
  275. b1sl/b1sl/resources/_generated/holidays.py +15 -0
  276. b1sl/b1sl/resources/_generated/house_bank_accounts.py +15 -0
  277. b1sl/b1sl/resources/_generated/incoming_payments.py +15 -0
  278. b1sl/b1sl/resources/_generated/india_hsn.py +15 -0
  279. b1sl/b1sl/resources/_generated/india_hsn_service.py +21 -0
  280. b1sl/b1sl/resources/_generated/industries.py +15 -0
  281. b1sl/b1sl/resources/_generated/integration_packages_configure.py +15 -0
  282. b1sl/b1sl/resources/_generated/integration_packages_configure_service.py +21 -0
  283. b1sl/b1sl/resources/_generated/internal_reconciliations.py +15 -0
  284. b1sl/b1sl/resources/_generated/internal_reconciliations_service.py +51 -0
  285. b1sl/b1sl/resources/_generated/intrastat_configuration.py +15 -0
  286. b1sl/b1sl/resources/_generated/intrastat_configuration_service.py +21 -0
  287. b1sl/b1sl/resources/_generated/inventory_countings.py +15 -0
  288. b1sl/b1sl/resources/_generated/inventory_countings_service.py +21 -0
  289. b1sl/b1sl/resources/_generated/inventory_cycles.py +15 -0
  290. b1sl/b1sl/resources/_generated/inventory_gen_entries.py +15 -0
  291. b1sl/b1sl/resources/_generated/inventory_gen_entry_service.py +67 -0
  292. b1sl/b1sl/resources/_generated/inventory_gen_exit_service.py +66 -0
  293. b1sl/b1sl/resources/_generated/inventory_gen_exits.py +15 -0
  294. b1sl/b1sl/resources/_generated/inventory_opening_balances.py +15 -0
  295. b1sl/b1sl/resources/_generated/inventory_opening_balances_service.py +21 -0
  296. b1sl/b1sl/resources/_generated/inventory_postings.py +15 -0
  297. b1sl/b1sl/resources/_generated/inventory_postings_service.py +34 -0
  298. b1sl/b1sl/resources/_generated/inventory_transfer_requests.py +15 -0
  299. b1sl/b1sl/resources/_generated/inventory_transfer_requests_service.py +43 -0
  300. b1sl/b1sl/resources/_generated/invoices.py +15 -0
  301. b1sl/b1sl/resources/_generated/invoices_service.py +100 -0
  302. b1sl/b1sl/resources/_generated/item_groups.py +15 -0
  303. b1sl/b1sl/resources/_generated/item_properties.py +15 -0
  304. b1sl/b1sl/resources/_generated/items.py +15 -0
  305. b1sl/b1sl/resources/_generated/journal_entries.py +15 -0
  306. b1sl/b1sl/resources/_generated/journal_entry_document_type_service.py +21 -0
  307. b1sl/b1sl/resources/_generated/journal_entry_document_types.py +15 -0
  308. b1sl/b1sl/resources/_generated/journal_vouchers_service.py +45 -0
  309. b1sl/b1sl/resources/_generated/knowledge_base_solutions.py +15 -0
  310. b1sl/b1sl/resources/_generated/kp_is.py +15 -0
  311. b1sl/b1sl/resources/_generated/kp_is_service.py +21 -0
  312. b1sl/b1sl/resources/_generated/landed_costs.py +15 -0
  313. b1sl/b1sl/resources/_generated/landed_costs_codes.py +15 -0
  314. b1sl/b1sl/resources/_generated/landed_costs_service.py +21 -0
  315. b1sl/b1sl/resources/_generated/legal_data.py +15 -0
  316. b1sl/b1sl/resources/_generated/length_measures.py +15 -0
  317. b1sl/b1sl/resources/_generated/license_service.py +21 -0
  318. b1sl/b1sl/resources/_generated/local_era.py +15 -0
  319. b1sl/b1sl/resources/_generated/manufacturers.py +15 -0
  320. b1sl/b1sl/resources/_generated/material_groups.py +15 -0
  321. b1sl/b1sl/resources/_generated/material_groups_service.py +21 -0
  322. b1sl/b1sl/resources/_generated/material_revaluation.py +15 -0
  323. b1sl/b1sl/resources/_generated/material_revaluation_fifo_service.py +33 -0
  324. b1sl/b1sl/resources/_generated/material_revaluation_snb_service.py +33 -0
  325. b1sl/b1sl/resources/_generated/messages.py +15 -0
  326. b1sl/b1sl/resources/_generated/messages_service.py +35 -0
  327. b1sl/b1sl/resources/_generated/mobile_add_on_setting.py +15 -0
  328. b1sl/b1sl/resources/_generated/mobile_add_on_setting_service.py +21 -0
  329. b1sl/b1sl/resources/_generated/mobile_app_service.py +240 -0
  330. b1sl/b1sl/resources/_generated/multi_language_translations.py +15 -0
  331. b1sl/b1sl/resources/_generated/nature_of_assessees.py +15 -0
  332. b1sl/b1sl/resources/_generated/nature_of_assessees_service.py +21 -0
  333. b1sl/b1sl/resources/_generated/ncm_codes_setup.py +15 -0
  334. b1sl/b1sl/resources/_generated/ncm_codes_setup_service.py +21 -0
  335. b1sl/b1sl/resources/_generated/nf_models.py +15 -0
  336. b1sl/b1sl/resources/_generated/nf_models_service.py +21 -0
  337. b1sl/b1sl/resources/_generated/nf_tax_categories.py +15 -0
  338. b1sl/b1sl/resources/_generated/nf_tax_categories_service.py +21 -0
  339. b1sl/b1sl/resources/_generated/nota_fiscal_cfop.py +15 -0
  340. b1sl/b1sl/resources/_generated/nota_fiscal_cst.py +15 -0
  341. b1sl/b1sl/resources/_generated/nota_fiscal_usage.py +15 -0
  342. b1sl/b1sl/resources/_generated/occurrence_codes.py +15 -0
  343. b1sl/b1sl/resources/_generated/occurrence_codes_service.py +21 -0
  344. b1sl/b1sl/resources/_generated/orders.py +15 -0
  345. b1sl/b1sl/resources/_generated/orders_service.py +94 -0
  346. b1sl/b1sl/resources/_generated/packages_types.py +15 -0
  347. b1sl/b1sl/resources/_generated/partners_setups.py +15 -0
  348. b1sl/b1sl/resources/_generated/partners_setups_service.py +21 -0
  349. b1sl/b1sl/resources/_generated/payment_blocks.py +15 -0
  350. b1sl/b1sl/resources/_generated/payment_blocks_service.py +21 -0
  351. b1sl/b1sl/resources/_generated/payment_calculation_service.py +37 -0
  352. b1sl/b1sl/resources/_generated/payment_drafts.py +15 -0
  353. b1sl/b1sl/resources/_generated/payment_reason_code_service.py +21 -0
  354. b1sl/b1sl/resources/_generated/payment_reason_codes.py +15 -0
  355. b1sl/b1sl/resources/_generated/payment_run_export.py +15 -0
  356. b1sl/b1sl/resources/_generated/payment_terms_types.py +15 -0
  357. b1sl/b1sl/resources/_generated/payment_terms_types_service.py +28 -0
  358. b1sl/b1sl/resources/_generated/pick_lists.py +15 -0
  359. b1sl/b1sl/resources/_generated/pick_lists_service.py +78 -0
  360. b1sl/b1sl/resources/_generated/pos_daily_summary.py +15 -0
  361. b1sl/b1sl/resources/_generated/predefined_texts.py +15 -0
  362. b1sl/b1sl/resources/_generated/predefined_texts_service.py +21 -0
  363. b1sl/b1sl/resources/_generated/price_lists.py +15 -0
  364. b1sl/b1sl/resources/_generated/product_trees.py +15 -0
  365. b1sl/b1sl/resources/_generated/production_orders.py +15 -0
  366. b1sl/b1sl/resources/_generated/profit_centers.py +15 -0
  367. b1sl/b1sl/resources/_generated/profit_centers_service.py +21 -0
  368. b1sl/b1sl/resources/_generated/project_management_configuration_service.py +333 -0
  369. b1sl/b1sl/resources/_generated/project_management_service.py +104 -0
  370. b1sl/b1sl/resources/_generated/project_management_time_sheet.py +15 -0
  371. b1sl/b1sl/resources/_generated/project_managements.py +15 -0
  372. b1sl/b1sl/resources/_generated/projects.py +15 -0
  373. b1sl/b1sl/resources/_generated/projects_service.py +21 -0
  374. b1sl/b1sl/resources/_generated/purchase_credit_notes.py +15 -0
  375. b1sl/b1sl/resources/_generated/purchase_credit_notes_service.py +85 -0
  376. b1sl/b1sl/resources/_generated/purchase_delivery_notes.py +15 -0
  377. b1sl/b1sl/resources/_generated/purchase_delivery_notes_service.py +85 -0
  378. b1sl/b1sl/resources/_generated/purchase_down_payments.py +15 -0
  379. b1sl/b1sl/resources/_generated/purchase_down_payments_service.py +70 -0
  380. b1sl/b1sl/resources/_generated/purchase_invoices.py +15 -0
  381. b1sl/b1sl/resources/_generated/purchase_invoices_service.py +85 -0
  382. b1sl/b1sl/resources/_generated/purchase_orders.py +15 -0
  383. b1sl/b1sl/resources/_generated/purchase_orders_service.py +69 -0
  384. b1sl/b1sl/resources/_generated/purchase_quotations.py +15 -0
  385. b1sl/b1sl/resources/_generated/purchase_quotations_service.py +70 -0
  386. b1sl/b1sl/resources/_generated/purchase_request_service.py +68 -0
  387. b1sl/b1sl/resources/_generated/purchase_requests.py +15 -0
  388. b1sl/b1sl/resources/_generated/purchase_returns.py +15 -0
  389. b1sl/b1sl/resources/_generated/purchase_returns_service.py +85 -0
  390. b1sl/b1sl/resources/_generated/purchase_tax_invoices.py +15 -0
  391. b1sl/b1sl/resources/_generated/qr_code_service.py +33 -0
  392. b1sl/b1sl/resources/_generated/query_auth_groups.py +15 -0
  393. b1sl/b1sl/resources/_generated/query_categories.py +15 -0
  394. b1sl/b1sl/resources/_generated/query_service.py +29 -0
  395. b1sl/b1sl/resources/_generated/queue.py +15 -0
  396. b1sl/b1sl/resources/_generated/quotations.py +15 -0
  397. b1sl/b1sl/resources/_generated/quotations_service.py +69 -0
  398. b1sl/b1sl/resources/_generated/recurring_transaction_service.py +67 -0
  399. b1sl/b1sl/resources/_generated/relationships.py +15 -0
  400. b1sl/b1sl/resources/_generated/report_filter.py +15 -0
  401. b1sl/b1sl/resources/_generated/report_filter_service.py +30 -0
  402. b1sl/b1sl/resources/_generated/report_layouts_service.py +237 -0
  403. b1sl/b1sl/resources/_generated/report_types.py +15 -0
  404. b1sl/b1sl/resources/_generated/report_types_service.py +21 -0
  405. b1sl/b1sl/resources/_generated/resource_capacities.py +15 -0
  406. b1sl/b1sl/resources/_generated/resource_capacities_service.py +34 -0
  407. b1sl/b1sl/resources/_generated/resource_groups.py +15 -0
  408. b1sl/b1sl/resources/_generated/resource_groups_service.py +21 -0
  409. b1sl/b1sl/resources/_generated/resource_properties.py +15 -0
  410. b1sl/b1sl/resources/_generated/resource_properties_service.py +21 -0
  411. b1sl/b1sl/resources/_generated/resources.py +15 -0
  412. b1sl/b1sl/resources/_generated/resources_service.py +21 -0
  413. b1sl/b1sl/resources/_generated/retorno_codes.py +15 -0
  414. b1sl/b1sl/resources/_generated/retorno_codes_service.py +21 -0
  415. b1sl/b1sl/resources/_generated/return_request.py +15 -0
  416. b1sl/b1sl/resources/_generated/return_request_service.py +69 -0
  417. b1sl/b1sl/resources/_generated/returns.py +15 -0
  418. b1sl/b1sl/resources/_generated/returns_service.py +85 -0
  419. b1sl/b1sl/resources/_generated/route_stages.py +15 -0
  420. b1sl/b1sl/resources/_generated/route_stages_service.py +21 -0
  421. b1sl/b1sl/resources/_generated/sales_forecast.py +15 -0
  422. b1sl/b1sl/resources/_generated/sales_opportunities.py +15 -0
  423. b1sl/b1sl/resources/_generated/sales_opportunity_competitors_setup.py +15 -0
  424. b1sl/b1sl/resources/_generated/sales_opportunity_competitors_setup_service.py +21 -0
  425. b1sl/b1sl/resources/_generated/sales_opportunity_interests_setup.py +15 -0
  426. b1sl/b1sl/resources/_generated/sales_opportunity_interests_setup_service.py +21 -0
  427. b1sl/b1sl/resources/_generated/sales_opportunity_reasons_setup.py +15 -0
  428. b1sl/b1sl/resources/_generated/sales_opportunity_reasons_setup_service.py +21 -0
  429. b1sl/b1sl/resources/_generated/sales_opportunity_sources_setup.py +15 -0
  430. b1sl/b1sl/resources/_generated/sales_opportunity_sources_setup_service.py +21 -0
  431. b1sl/b1sl/resources/_generated/sales_persons.py +15 -0
  432. b1sl/b1sl/resources/_generated/sales_stages.py +15 -0
  433. b1sl/b1sl/resources/_generated/sales_tax_authorities.py +15 -0
  434. b1sl/b1sl/resources/_generated/sales_tax_authorities_types.py +15 -0
  435. b1sl/b1sl/resources/_generated/sales_tax_codes.py +15 -0
  436. b1sl/b1sl/resources/_generated/sales_tax_invoices.py +15 -0
  437. b1sl/b1sl/resources/_generated/sbo_bob_service.py +89 -0
  438. b1sl/b1sl/resources/_generated/sections.py +15 -0
  439. b1sl/b1sl/resources/_generated/sections_service.py +21 -0
  440. b1sl/b1sl/resources/_generated/self_credit_memo_service.py +69 -0
  441. b1sl/b1sl/resources/_generated/self_credit_memos.py +15 -0
  442. b1sl/b1sl/resources/_generated/self_invoice_service.py +69 -0
  443. b1sl/b1sl/resources/_generated/self_invoices.py +15 -0
  444. b1sl/b1sl/resources/_generated/serial_number_details.py +15 -0
  445. b1sl/b1sl/resources/_generated/series_service.py +305 -0
  446. b1sl/b1sl/resources/_generated/service_call_origins.py +15 -0
  447. b1sl/b1sl/resources/_generated/service_call_origins_service.py +21 -0
  448. b1sl/b1sl/resources/_generated/service_call_problem_sub_types.py +15 -0
  449. b1sl/b1sl/resources/_generated/service_call_problem_sub_types_service.py +21 -0
  450. b1sl/b1sl/resources/_generated/service_call_problem_types.py +15 -0
  451. b1sl/b1sl/resources/_generated/service_call_problem_types_service.py +21 -0
  452. b1sl/b1sl/resources/_generated/service_call_solution_status.py +15 -0
  453. b1sl/b1sl/resources/_generated/service_call_solution_status_service.py +21 -0
  454. b1sl/b1sl/resources/_generated/service_call_status.py +15 -0
  455. b1sl/b1sl/resources/_generated/service_call_status_service.py +21 -0
  456. b1sl/b1sl/resources/_generated/service_call_types.py +15 -0
  457. b1sl/b1sl/resources/_generated/service_call_types_service.py +21 -0
  458. b1sl/b1sl/resources/_generated/service_calls.py +15 -0
  459. b1sl/b1sl/resources/_generated/service_contracts.py +15 -0
  460. b1sl/b1sl/resources/_generated/service_groups.py +15 -0
  461. b1sl/b1sl/resources/_generated/service_groups_service.py +21 -0
  462. b1sl/b1sl/resources/_generated/service_tax_posting_service.py +36 -0
  463. b1sl/b1sl/resources/_generated/shipping_types.py +15 -0
  464. b1sl/b1sl/resources/_generated/short_link_mappings_service.py +26 -0
  465. b1sl/b1sl/resources/_generated/special_prices.py +15 -0
  466. b1sl/b1sl/resources/_generated/states.py +15 -0
  467. b1sl/b1sl/resources/_generated/states_service.py +21 -0
  468. b1sl/b1sl/resources/_generated/stock_takings.py +15 -0
  469. b1sl/b1sl/resources/_generated/stock_transfer_draft_service.py +34 -0
  470. b1sl/b1sl/resources/_generated/stock_transfer_drafts.py +15 -0
  471. b1sl/b1sl/resources/_generated/stock_transfer_service.py +43 -0
  472. b1sl/b1sl/resources/_generated/stock_transfers.py +15 -0
  473. b1sl/b1sl/resources/_generated/target_groups.py +15 -0
  474. b1sl/b1sl/resources/_generated/target_groups_service.py +21 -0
  475. b1sl/b1sl/resources/_generated/tax_code_determinations.py +15 -0
  476. b1sl/b1sl/resources/_generated/tax_code_determinations_service.py +21 -0
  477. b1sl/b1sl/resources/_generated/tax_code_determinations_tcd.py +15 -0
  478. b1sl/b1sl/resources/_generated/tax_code_determinations_tcd_service.py +21 -0
  479. b1sl/b1sl/resources/_generated/tax_invoice_report.py +15 -0
  480. b1sl/b1sl/resources/_generated/tax_web_sites.py +15 -0
  481. b1sl/b1sl/resources/_generated/tax_web_sites_service.py +27 -0
  482. b1sl/b1sl/resources/_generated/teams.py +15 -0
  483. b1sl/b1sl/resources/_generated/termination_reason.py +15 -0
  484. b1sl/b1sl/resources/_generated/termination_reason_service.py +21 -0
  485. b1sl/b1sl/resources/_generated/territories.py +15 -0
  486. b1sl/b1sl/resources/_generated/tracking_notes.py +15 -0
  487. b1sl/b1sl/resources/_generated/tracking_notes_service.py +21 -0
  488. b1sl/b1sl/resources/_generated/transaction_codes.py +15 -0
  489. b1sl/b1sl/resources/_generated/transaction_codes_service.py +21 -0
  490. b1sl/b1sl/resources/_generated/transportation_document.py +15 -0
  491. b1sl/b1sl/resources/_generated/tsr_exceptional_event_service.py +21 -0
  492. b1sl/b1sl/resources/_generated/tsr_exceptional_events.py +15 -0
  493. b1sl/b1sl/resources/_generated/unit_of_measurement_groups.py +15 -0
  494. b1sl/b1sl/resources/_generated/unit_of_measurement_groups_service.py +21 -0
  495. b1sl/b1sl/resources/_generated/unit_of_measurements.py +15 -0
  496. b1sl/b1sl/resources/_generated/unit_of_measurements_service.py +21 -0
  497. b1sl/b1sl/resources/_generated/user_default_groups.py +15 -0
  498. b1sl/b1sl/resources/_generated/user_fields_md.py +15 -0
  499. b1sl/b1sl/resources/_generated/user_keys_md.py +15 -0
  500. b1sl/b1sl/resources/_generated/user_languages.py +15 -0
  501. b1sl/b1sl/resources/_generated/user_menu_service.py +103 -0
  502. b1sl/b1sl/resources/_generated/user_objects_md.py +15 -0
  503. b1sl/b1sl/resources/_generated/user_permission_tree.py +15 -0
  504. b1sl/b1sl/resources/_generated/user_queries.py +15 -0
  505. b1sl/b1sl/resources/_generated/user_tables_md.py +15 -0
  506. b1sl/b1sl/resources/_generated/users.py +15 -0
  507. b1sl/b1sl/resources/_generated/users_service.py +21 -0
  508. b1sl/b1sl/resources/_generated/value_mapping.py +15 -0
  509. b1sl/b1sl/resources/_generated/value_mapping_communication.py +15 -0
  510. b1sl/b1sl/resources/_generated/value_mapping_service.py +64 -0
  511. b1sl/b1sl/resources/_generated/vat_groups.py +15 -0
  512. b1sl/b1sl/resources/_generated/vendor_payments.py +15 -0
  513. b1sl/b1sl/resources/_generated/w_tax_type_code_service.py +21 -0
  514. b1sl/b1sl/resources/_generated/w_tax_type_codes.py +15 -0
  515. b1sl/b1sl/resources/_generated/warehouse_locations.py +15 -0
  516. b1sl/b1sl/resources/_generated/warehouse_sublevel_codes.py +15 -0
  517. b1sl/b1sl/resources/_generated/warehouse_sublevel_codes_service.py +21 -0
  518. b1sl/b1sl/resources/_generated/warehouses.py +15 -0
  519. b1sl/b1sl/resources/_generated/web_client_bookmark_tile_service.py +21 -0
  520. b1sl/b1sl/resources/_generated/web_client_bookmark_tiles.py +15 -0
  521. b1sl/b1sl/resources/_generated/web_client_dashboard_service.py +21 -0
  522. b1sl/b1sl/resources/_generated/web_client_dashboards.py +15 -0
  523. b1sl/b1sl/resources/_generated/web_client_form_setting_service.py +21 -0
  524. b1sl/b1sl/resources/_generated/web_client_form_settings.py +15 -0
  525. b1sl/b1sl/resources/_generated/web_client_launchpad_service.py +21 -0
  526. b1sl/b1sl/resources/_generated/web_client_launchpads.py +15 -0
  527. b1sl/b1sl/resources/_generated/web_client_listview_filter_service.py +21 -0
  528. b1sl/b1sl/resources/_generated/web_client_listview_filters.py +15 -0
  529. b1sl/b1sl/resources/_generated/web_client_notification_service.py +21 -0
  530. b1sl/b1sl/resources/_generated/web_client_notifications.py +15 -0
  531. b1sl/b1sl/resources/_generated/web_client_preference_service.py +21 -0
  532. b1sl/b1sl/resources/_generated/web_client_preferences.py +15 -0
  533. b1sl/b1sl/resources/_generated/web_client_recent_activities.py +15 -0
  534. b1sl/b1sl/resources/_generated/web_client_recent_activity_service.py +21 -0
  535. b1sl/b1sl/resources/_generated/web_client_variant_group_service.py +21 -0
  536. b1sl/b1sl/resources/_generated/web_client_variant_groups.py +15 -0
  537. b1sl/b1sl/resources/_generated/web_client_variant_service.py +21 -0
  538. b1sl/b1sl/resources/_generated/web_client_variants.py +15 -0
  539. b1sl/b1sl/resources/_generated/weight_measures.py +15 -0
  540. b1sl/b1sl/resources/_generated/withholding_tax_codes.py +15 -0
  541. b1sl/b1sl/resources/_generated/witholding_tax_definition.py +15 -0
  542. b1sl/b1sl/resources/_generated/wizard_payment_methods.py +15 -0
  543. b1sl/b1sl/resources/_generated/workflow_task_service.py +47 -0
  544. b1sl/b1sl/resources/async_base.py +60 -0
  545. b1sl/b1sl/resources/base.py +159 -0
  546. b1sl/b1sl/resources/udo.py +78 -0
  547. b1sl/b1sl/rest_adapter.py +351 -0
  548. b1sl/b1sl/testing.py +24 -0
  549. b1sl/b1sl/tests/__init__.py +0 -0
  550. b1sl/b1sl/tests/test_01_settings.py +60 -0
  551. b1sl/b1sl/tests/test_02_rest_adapter.py +177 -0
  552. b1sl/b1sl/tests/test_03_items_endpoint.py +172 -0
  553. b1sl/b1sl/tests/test_04_serialnumberdetails_endpoint.py +181 -0
  554. b1sl/b1sl/tests/test_observability.py +153 -0
  555. b1sl/b1sl/tests/test_utils.py +24 -0
  556. b1sl/contrib/__init__.py +0 -0
  557. b1sl/contrib/django/__init__.py +15 -0
  558. b1sl/contrib/django/base.py +89 -0
  559. b1sl/contrib/django/odata_decode_middleware.py +14 -0
  560. b1sl/contrib/django/odata_transform_url_middleware.py +15 -0
  561. b1sl/py.typed +0 -0
  562. b1sl/saphdb/__init__.py +19 -0
  563. b1sl/saphdb/adapter.py +24 -0
  564. b1sl/saphdb/client.py +50 -0
  565. b1sl/saphdb/config.py +68 -0
  566. b1sl/saphdb/endpoints/serialnumberdetailodbc.py +100 -0
  567. b1sl/saphdb/exceptions/exceptions.py +19 -0
  568. b1sl/saphdb/main.py +18 -0
  569. b1sl/saphdb/models/result.py +18 -0
  570. b1sl/saphdb/models/serial.py +37 -0
  571. b1sl/saphdb/odbc_adapter.py +220 -0
  572. b1sl/saphdb/tests/__init__.py +0 -0
  573. b1sl/saphdb/tests/test_01_settings.py +37 -0
  574. b1sl/saphdb/tests/test_02_odbc_adapter.py +96 -0
  575. b1sl/saphdb/tests/test_04_serialnumberdetails_endpoint.py +131 -0
  576. b1sl_python-0.1.0.dist-info/METADATA +169 -0
  577. b1sl_python-0.1.0.dist-info/RECORD +579 -0
  578. b1sl_python-0.1.0.dist-info/WHEEL +4 -0
  579. b1sl_python-0.1.0.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,394 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING
4
+
5
+ if TYPE_CHECKING:
6
+ from b1sl.b1sl.config import B1Config
7
+ import asyncio
8
+ import logging
9
+ import uuid
10
+ from collections import OrderedDict
11
+ from dataclasses import dataclass, field
12
+ from datetime import datetime
13
+ from typing import Any, Callable, Dict, List, Optional
14
+ from urllib.parse import parse_qs, urlencode, urlparse
15
+
16
+ from b1sl.b1sl.exceptions.exceptions import SAPConcurrencyError
17
+
18
+
19
+ @dataclass
20
+ class ObservabilityConfig:
21
+ """
22
+ Configuration for SDK observability features.
23
+
24
+ Attributes:
25
+ hooks (dict): Dictionary mapping event names to lists of callables.
26
+ Supported events: 'on_response', 'on_error'.
27
+ context_extras (dict): Arbitrary metadata injected into every HookContext.
28
+ slow_request_threshold_ms (float): Threshold to trigger 'slow request' logs.
29
+ log_level_slow (int): Logging level for slow requests. Default is logging.WARNING.
30
+ """
31
+
32
+ hooks: Dict[str, List[Callable]] = field(default_factory=dict)
33
+ context_extras: Dict[str, Any] = field(default_factory=dict)
34
+ slow_request_threshold_ms: float = 5000.0
35
+ log_level_slow: int = logging.WARNING
36
+
37
+
38
+ @dataclass(frozen=True, slots=True)
39
+ class HookContext:
40
+ """
41
+ Immutable object passed to every hook on response or error.
42
+ Designed for structured logging and performance monitoring.
43
+
44
+ Privacy Note: URL components are separated to avoid logging sensitive
45
+ data contained in query parameters by default.
46
+ """
47
+
48
+ req_id: str
49
+ http_method: str
50
+ base_url: str
51
+ endpoint: str
52
+ query_params: str # Raw query string
53
+ db: str
54
+ user: str
55
+ status_code: Optional[int] # None if network exception occurred
56
+ duration_ms: float
57
+ extra: Dict[str, Any] = field(default_factory=dict)
58
+ exc: Optional[Exception] = None
59
+
60
+ @property
61
+ def duration_s(self) -> float:
62
+ """Duration in seconds."""
63
+ return self.duration_ms / 1000.0
64
+
65
+ def to_log_extra(self) -> Dict[str, Any]:
66
+ """Generate a flat dictionary for structured loggers."""
67
+ return {
68
+ "req_id": self.req_id,
69
+ "http_method": self.http_method,
70
+ "base_url": self.base_url,
71
+ "endpoint": self.endpoint,
72
+ "db": self.db,
73
+ "user": self.user,
74
+ "status_code": self.status_code,
75
+ "duration_ms": round(self.duration_ms, 3),
76
+ **self.extra,
77
+ }
78
+
79
+
80
+ class HookDispatcher:
81
+ """
82
+ Safely manages and executes event hooks.
83
+
84
+ Hook Contract for authors:
85
+ - ctx is immutable; any modification attempt will raise FrozenInstanceError.
86
+ - Hooks are executed in order of registration (sequential).
87
+ - An exception in a hook DOES NOT interrupt subsequent hooks or the SDK request.
88
+ - Errors in hooks are caught and logged at WARNING level via exc_info.
89
+ - duration_ms is always available, even on network errors (status_code=None).
90
+ """
91
+
92
+ def __init__(self, hooks: Dict[str, List[Callable]] | None = None):
93
+ self._hooks = hooks or {}
94
+
95
+ def dispatch(self, event: str, ctx: HookContext, logger: logging.Logger) -> None:
96
+ """Synchronously dispatch a hook event."""
97
+ for hook in self._hooks.get(event, []):
98
+ try:
99
+ hook(ctx)
100
+ except Exception as e:
101
+ name = getattr(hook, "__name__", "unnamed_hook")
102
+ logger.warning(
103
+ f"Hook '{event}' (sync) {name} failed: {e}", exc_info=True
104
+ )
105
+
106
+ async def adispatch(
107
+ self, event: str, ctx: HookContext, logger: logging.Logger
108
+ ) -> None:
109
+ """Asynchronously dispatch a hook event (supports both sync and async hooks concurrently)."""
110
+ async def run_hook(hook: Callable) -> None:
111
+ try:
112
+ result = hook(ctx)
113
+ if asyncio.iscoroutine(result):
114
+ await result
115
+ except Exception as e:
116
+ name = getattr(hook, "__name__", "unnamed_hook")
117
+ logger.warning(
118
+ f"Hook '{event}' (async) {name} failed: {e}", exc_info=True
119
+ )
120
+
121
+ hooks = self._hooks.get(event, [])
122
+ if hooks:
123
+ await asyncio.gather(*(run_hook(h) for h in hooks))
124
+
125
+
126
+ class BaseRestAdapter:
127
+ """
128
+ Base class for SAP B1 Service Layer HTTP adapters.
129
+
130
+ Provides shared logic for URL normalization, ETag caching,
131
+ and structured observability (logging + hooks).
132
+ """
133
+
134
+ def __init__(
135
+ self,
136
+ config: B1Config,
137
+ *,
138
+ logger: logging.Logger | None = None,
139
+ version: str = "v2",
140
+ observability: ObservabilityConfig | None = None,
141
+ ):
142
+ self._config = config
143
+ self.version = version
144
+ base_url_stripped = config.base_url.rstrip("/")
145
+
146
+ # URL Normalization logic
147
+ if "/b1s/" not in base_url_stripped:
148
+ self.raw_base_url = f"{base_url_stripped}/b1s/{version}"
149
+ else:
150
+ self.raw_base_url = base_url_stripped
151
+
152
+ # Extract attributes from config for quick access or legacy internal use
153
+ self._username = config.username
154
+ self._password = config.password
155
+ self._db = config.company_db
156
+ self._ssl_verify = config.ssl_verify
157
+
158
+ self.reuse_token = config.reuse_token
159
+ self.token_timeout = config.token_timeout
160
+ self.token_expiry: datetime | None = None
161
+ self.is_session_active = False
162
+
163
+ self._connect_timeout = config.connect_timeout
164
+ self._read_timeout = config.read_timeout
165
+
166
+ self._logger = logger or logging.getLogger(f"b1sl.{self.__class__.__name__}")
167
+
168
+ # Observability Setup
169
+ self._obs = observability or ObservabilityConfig()
170
+ self._hooks = HookDispatcher(self._obs.hooks)
171
+
172
+ self._etag_cache: OrderedDict[str, str] = OrderedDict()
173
+ self._etag_max_size: int = getattr(config, "etag_cache_size", 256)
174
+
175
+ @classmethod
176
+ def from_config(
177
+ cls,
178
+ config: B1Config,
179
+ *,
180
+ logger: logging.Logger | None = None,
181
+ version: str = "v2",
182
+ observability: ObservabilityConfig | None = None,
183
+ ):
184
+ """
185
+ Factory method to instantiate the adapter from a B1Config.
186
+ """
187
+ return cls(
188
+ config=config,
189
+ logger=logger,
190
+ version=version,
191
+ observability=observability,
192
+ )
193
+
194
+ @property
195
+ def url(self) -> str:
196
+ """Returns the fully normalized Service Layer base URL (including /b1s/v1)."""
197
+ return self.raw_base_url
198
+
199
+ def _clear_etag(self, key: str) -> None:
200
+ """Remove an ETag from the cache (e.g. after a conflict)."""
201
+ if key in self._etag_cache:
202
+ del self._etag_cache[key]
203
+
204
+ def _set_etag(self, key: str, value: str) -> None:
205
+ """Update the ETag for a given resource in the LRU cache.
206
+
207
+ The key is always the canonical endpoint path (e.g.
208
+ ``/BusinessPartners('C20000')``) so that both GET and subsequent
209
+ PATCH/DELETE operations share the same lookup key.
210
+ """
211
+ if key in self._etag_cache:
212
+ self._etag_cache.move_to_end(key)
213
+ self._etag_cache[key] = value
214
+ if len(self._etag_cache) > self._etag_max_size:
215
+ self._etag_cache.popitem(last=False)
216
+
217
+ def _extract_etag(
218
+ self,
219
+ endpoint: str,
220
+ response_headers: dict,
221
+ response_body: dict | None,
222
+ ) -> str | None:
223
+ """Extract and cache the ETag for a resource after a successful request.
224
+
225
+ Extraction priority (per SAP B1 OData V4 spec):
226
+ 1. ``ETag`` response header (preferred — avoids altering body parsing)
227
+ 2. ``@odata.etag`` key inside the JSON body (fallback for SAP quirks)
228
+
229
+ The extracted value is automatically stored in the LRU cache.
230
+
231
+ Args:
232
+ endpoint: The canonical resource path used as the cache key.
233
+ response_headers: Mapping of HTTP response headers.
234
+ response_body: Parsed JSON body, or ``None`` when the response has
235
+ no content (e.g. 204 No Content).
236
+
237
+ Returns:
238
+ The raw ETag string (including surrounding quotes and ``W/`` prefix)
239
+ or ``None`` when no ETag was found.
240
+ """
241
+ etag = response_headers.get("ETag") or response_headers.get("etag")
242
+ if not etag and isinstance(response_body, dict):
243
+ etag = response_body.get("@odata.etag")
244
+ if etag:
245
+ self._set_etag(endpoint, etag)
246
+ return etag or None
247
+
248
+ def _build_headers(
249
+ self,
250
+ http_method: str,
251
+ endpoint: str,
252
+ extra_headers: dict | None = None,
253
+ ) -> dict:
254
+ """Build the final HTTP headers for a request, injecting ETag controls.
255
+
256
+ Injection rules:
257
+ - ``GET``: Adds ``If-None-Match`` if an ETag is cached (enables 304
258
+ Not Modified optimisation — note SAP SL may not honour this).
259
+ - ``PATCH`` / ``DELETE`` / ``POST`` (actions): Adds ``If-Match`` with
260
+ the cached ETag when one is available, enforcing optimistic concurrency.
261
+ If no ETag is cached the header is **omitted**, which causes SAP to
262
+ perform a blind override (documented SAP behaviour).
263
+
264
+ Args:
265
+ http_method: HTTP verb in upper-case.
266
+ endpoint: Canonical resource path, used to look up the ETag cache.
267
+ extra_headers: Optional caller-supplied headers that take precedence
268
+ over the defaults built here.
269
+
270
+ Returns:
271
+ A new ``dict`` ready to pass to the underlying HTTP library.
272
+ """
273
+ headers: dict = {"Content-Type": "application/json"}
274
+
275
+ cached_etag = self._etag_cache.get(endpoint)
276
+
277
+ if http_method == "GET" and cached_etag:
278
+ headers["If-None-Match"] = cached_etag
279
+ elif http_method in {"PATCH", "DELETE", "POST"} and cached_etag:
280
+ # POST covers OData Actions (e.g. /BusinessPartners('C20000')/Cancel)
281
+ headers["If-Match"] = cached_etag
282
+
283
+ if extra_headers:
284
+ headers.update(extra_headers)
285
+ return headers
286
+
287
+ def _raise_if_concurrency_error(
288
+ self,
289
+ status_code: int,
290
+ sap_code: str,
291
+ sap_message: str,
292
+ endpoint: str,
293
+ response_body: dict | None,
294
+ ) -> None:
295
+ """Convert a SAP 412 + code -2039 response into a ``SAPConcurrencyError``.
296
+
297
+ This is the *single* place where the SDK distinguishes an ETag conflict
298
+ from a generic HTTP error. Both sync and async adapters call this
299
+ method right before raising their generic ``B1Exception``, so the
300
+ specialised exception is always raised first when applicable.
301
+
302
+ Args:
303
+ status_code: HTTP status code of the failed response.
304
+ sap_code: SAP OData error code extracted from the response body.
305
+ sap_message: Human-readable SAP error message.
306
+ endpoint: Resource path where the conflict occurred.
307
+ response_body: Full parsed JSON body for the ``details`` attribute.
308
+
309
+ Raises:
310
+ SAPConcurrencyError: Always, when status_code is 412 and
311
+ sap_code is ``"-2039"``.
312
+ """
313
+ if status_code == 412 and sap_code == SAPConcurrencyError.SAP_ERROR_CODE:
314
+ etag_sent = self._etag_cache.get(endpoint)
315
+ self._clear_etag(endpoint) # Force fresh GET on next attempt
316
+ raise SAPConcurrencyError(
317
+ f"ETag conflict on '{endpoint}': {sap_message}",
318
+ sap_code=sap_code,
319
+ etag_sent=etag_sent,
320
+ endpoint=endpoint,
321
+ details=response_body,
322
+ )
323
+
324
+ def _redact_data(self, data: dict | None) -> dict:
325
+ """Mask sensitive information in data dictionaries before logging."""
326
+ return {k: "***" if k == "Password" else v for k, v in (data or {}).items()}
327
+
328
+ def _generate_req_id(self) -> str:
329
+ """Generate a short unique identifier for request tracing."""
330
+ return uuid.uuid4().hex[:16]
331
+
332
+ def _log_response(self, ctx: HookContext) -> None:
333
+ """
334
+ Standardized logging for request outcomes.
335
+ Elevates level if duration exceeds threshold.
336
+ """
337
+ level = logging.INFO
338
+ status_label = f" -> {ctx.status_code}" if ctx.status_code else " -> ERROR"
339
+ slow_label = ""
340
+
341
+ if ctx.duration_ms >= self._obs.slow_request_threshold_ms:
342
+ level = self._obs.log_level_slow
343
+ slow_label = " ⚠ SLOW"
344
+
345
+ if ctx.exc:
346
+ level = logging.ERROR
347
+
348
+ clean_endpoint = ctx.endpoint.lstrip("/")
349
+ msg = f"[{ctx.req_id}][{ctx.user}] [{ctx.http_method} /{clean_endpoint}]{status_label} ({ctx.duration_ms:.1f}ms){slow_label}"
350
+ self._logger.log(level, msg, extra=ctx.to_log_extra())
351
+
352
+ # ── URL Helpers ──────────────────────────────────────────────────────── #
353
+
354
+ def _clean_url(self, next_link: str) -> str:
355
+ """Normalize an OData nextLink by sorting query parameters."""
356
+ parsed_url = urlparse(next_link)
357
+ query_params = parse_qs(parsed_url.query)
358
+ cleaned_query = urlencode(query_params, doseq=True)
359
+ return parsed_url._replace(query=cleaned_query).geturl()
360
+
361
+ def _get_ep_params(self, next_link: str) -> dict:
362
+ """Extract query parameters from an OData nextLink into a dictionary."""
363
+ parsed_url = urlparse(next_link)
364
+ query_params = parse_qs(parsed_url.query)
365
+ return {k: ",".join(v) for k, v in query_params.items()}
366
+
367
+ # ── SAP OData error parsing ──────────────────────────────────────────── #
368
+
369
+ @staticmethod
370
+ def _parse_sap_error_shared(
371
+ status_code: int, reason: str, body: Any
372
+ ) -> tuple[str, str]:
373
+ """Extract SAP-specific error code and message from an OData response."""
374
+ http_fallback = f"HTTP {status_code}: {reason}"
375
+ if not isinstance(body, dict):
376
+ return "unknown", http_fallback
377
+
378
+ error_node = body.get("error")
379
+ if not error_node or not isinstance(error_node, dict):
380
+ return "unknown", http_fallback
381
+
382
+ sap_code = str(error_node.get("code", "unknown"))
383
+ raw_message = error_node.get("message", "")
384
+
385
+ if isinstance(raw_message, dict):
386
+ sap_message = raw_message.get("value") or raw_message.get(
387
+ "lang", http_fallback
388
+ )
389
+ elif isinstance(raw_message, str) and raw_message:
390
+ sap_message = raw_message
391
+ else:
392
+ sap_message = http_fallback
393
+
394
+ return sap_code, sap_message
b1sl/b1sl/client.py ADDED
@@ -0,0 +1,112 @@
1
+ from __future__ import annotations
2
+
3
+ import logging
4
+
5
+ from b1sl.b1sl.adapter_protocol import RestAdapterProtocol
6
+ from b1sl.b1sl.base_adapter import ObservabilityConfig
7
+ from b1sl.b1sl.config import B1Config
8
+ from b1sl.b1sl.resources._generated.client_mixin import B1ClientMixin
9
+ from b1sl.b1sl.rest_adapter import RestAdapter
10
+
11
+ from typing import TYPE_CHECKING
12
+ if TYPE_CHECKING:
13
+ from b1sl.b1sl.resources.udo import UDOResource
14
+
15
+
16
+ class B1Client(B1ClientMixin):
17
+ """
18
+ Main synchronous entry point for the SAP B1 Service Layer SDK.
19
+
20
+ This class provides a typed interface to access SAP resources (Items,
21
+ Orders, etc.) and custom UDOs. It orchestrates the underlying RestAdapter
22
+ for session management and HTTP transport.
23
+
24
+ AI Role: Primary interface for synchronous scripts and legacy apps.
25
+ Use properties (e.g. client.items) to access SAP entities.
26
+
27
+ Example:
28
+ config = B1Config.from_env()
29
+ client = B1Client(config)
30
+ item = client.items.get("A0001")
31
+ """
32
+
33
+ def __init__(
34
+ self,
35
+ config: B1Config,
36
+ logger: logging.Logger | None = None,
37
+ version: str = "v2",
38
+ adapter: RestAdapterProtocol | None = None,
39
+ *,
40
+ observability: ObservabilityConfig | None = None,
41
+ ) -> None:
42
+ """
43
+ Initializes the B1Client.
44
+
45
+ Args:
46
+ config (B1Config): Validated configuration object.
47
+ logger (logging.Logger, optional): Custom logger; defaults
48
+ to a prefixed 'b1sl.B1Client' logger.
49
+ version (str): Service Layer API version (v1, v2). Defaults to "v2".
50
+ adapter (RestAdapterProtocol, optional): Custom adapter for
51
+ mocking or dependency injection.
52
+ """
53
+ self._logger = logger or logging.getLogger(f"b1sl.{self.__class__.__name__}")
54
+ # Private: consumers use properties from B1ClientMixin
55
+ self._adapter = adapter or RestAdapter(
56
+ config, logger=self._logger, version=version, observability=observability
57
+ )
58
+ self.version = version
59
+
60
+ @property
61
+ def session_id(self) -> str | None:
62
+ """
63
+ Retrieves the current SAP session ID.
64
+ """
65
+ return self._adapter.session_id
66
+
67
+ def connect(self) -> None:
68
+ """
69
+ No-op for the sync client as connection is per-request,
70
+ provided for parity with AsyncB1Client.
71
+ """
72
+ pass
73
+
74
+ def close(self) -> None:
75
+ """
76
+ Logs out and closes the HTTP connection pool.
77
+ Must be called to ensure clean shutdown if not using context manager.
78
+ """
79
+ self._adapter.close()
80
+
81
+ def __enter__(self) -> "B1Client":
82
+ """
83
+ Entry point for the context manager.
84
+ """
85
+ self.connect()
86
+ return self
87
+
88
+ def __exit__(self, exc_type, exc_val, exc_tb) -> None:
89
+ """
90
+ Exit point for the context manager.
91
+ Ensures connection pool cleanup.
92
+ """
93
+ self.close()
94
+
95
+ def udo(self, table_name: str) -> "UDOResource":
96
+ """
97
+ Access a User Defined Object (UDO) or User Table dynamically.
98
+
99
+ AI Role: Use this for any SAP entity not present in the pre-defined
100
+ service properties. Handles @U_ prefixing internally if required
101
+ by specific UDO implementations.
102
+
103
+ Args:
104
+ table_name (str): The UDO table name as registered in SAP B1
105
+ (e.g., "CT_SDK_ASSETS").
106
+
107
+ Returns:
108
+ UDOResource: A resource object bound to the specified UDO.
109
+ """
110
+ from b1sl.b1sl.resources.udo import UDOResource
111
+
112
+ return UDOResource(adapter=self._adapter, table_name=table_name)
b1sl/b1sl/config.py ADDED
@@ -0,0 +1,138 @@
1
+ from __future__ import annotations
2
+
3
+ import os
4
+ from dataclasses import dataclass, field
5
+ from datetime import timedelta
6
+
7
+ from b1sl.b1sl.environment import B1Env
8
+
9
+
10
+ @dataclass
11
+ class B1Config:
12
+ """
13
+ Configuration object for SAP B1 Service Layer connections.
14
+
15
+ This dataclass holds the necessary parameters to authenticate and
16
+ communicate with the SAP SL API. It includes support for session
17
+ management and timeout settings.
18
+
19
+ AI Role: Core configuration schema. Use 'from_env' or
20
+ 'from_django_settings' for automatic mapping from the environment.
21
+
22
+ Attributes:
23
+ base_url (str): Target SL URL (e.g., https://host:50000/b1s/v2).
24
+ username (str): B1 user name.
25
+ password (str): B1 user password.
26
+ company_db (str): Target Company Database.
27
+ environment (B1Env): Deployment stage. Defaults to B1Env.DEV.
28
+ Controls log format (JSON in PROD, human-readable otherwise).
29
+ Set via the ``B1SL_ENV`` environment variable.
30
+ ssl_verify (bool, optional): Default True.
31
+ reuse_token (bool, optional): Default True.
32
+ token_timeout (timedelta, optional): Default 900s.
33
+ max_page_size (int, optional): Default 20.
34
+ connect_timeout (float, optional): TCP timeout (seconds).
35
+ read_timeout (float, optional): Read timeout (seconds).
36
+ """
37
+
38
+ base_url: str
39
+ username: str
40
+ password: str
41
+ company_db: str
42
+ environment: B1Env = B1Env.DEV
43
+ ssl_verify: bool = True
44
+ reuse_token: bool = True
45
+ token_timeout: timedelta = field(default_factory=lambda: timedelta(seconds=900))
46
+ max_page_size: int = 20
47
+ connect_timeout: float = 10.0 # seconds to establish TCP connection
48
+ read_timeout: float = 60.0 # seconds to wait for SAP to respond
49
+ etag_cache_size: int = 256
50
+
51
+ def __post_init__(self) -> None:
52
+ """Validates all required parameters are present after initialisation."""
53
+ if not self.base_url:
54
+ raise ValueError("B1Config.base_url cannot be empty")
55
+ if not self.username or not self.password:
56
+ raise ValueError("B1Config credentials (username/password) cannot be empty")
57
+ if not self.company_db:
58
+ raise ValueError("B1Config.company_db cannot be empty")
59
+
60
+ @classmethod
61
+ def from_env(cls, strict: bool = True) -> "B1Config":
62
+ """
63
+ Factory method that loads configuration from OS environment variables.
64
+
65
+ AI Role: Standard way to load config. Expects variables prefixed
66
+ with B1SL_ (e.g., B1SL_BASE_URL).
67
+
68
+ Args:
69
+ strict (bool): If True (default), raises EnvironmentError if
70
+ required variables are missing. If False, returns a dummy
71
+ config for testing/VCR playback.
72
+
73
+ Returns:
74
+ B1Config: Loaded instance.
75
+
76
+ Raises:
77
+ EnvironmentError: If strict is True and any required variable is missing.
78
+ """
79
+ required = {
80
+ "base_url": "B1SL_BASE_URL",
81
+ "username": "B1SL_USERNAME",
82
+ "password": "B1SL_PASSWORD",
83
+ "company_db": "B1SL_COMPANY_DB",
84
+ }
85
+ missing = [key for attr, key in required.items() if not os.environ.get(key)]
86
+
87
+ if missing and strict:
88
+ raise EnvironmentError(
89
+ f"Missing required SAP B1 config env vars: {missing}"
90
+ )
91
+
92
+ kwargs = {
93
+ "base_url": os.environ.get("B1SL_BASE_URL", "https://dummy:50000/b1s/v2"),
94
+ "username": os.environ.get("B1SL_USERNAME", "dummy"),
95
+ "password": os.environ.get("B1SL_PASSWORD", "dummy"),
96
+ "company_db": os.environ.get("B1SL_COMPANY_DB", "SBODemoMX"),
97
+ "ssl_verify": os.environ.get("B1SL_SSL_VERIFY", "1") == "1",
98
+ "reuse_token": os.environ.get("B1SL_REUSE_TOKEN", "1") == "1",
99
+ "token_timeout": timedelta(
100
+ seconds=int(os.environ.get("B1SL_TOKEN_TIMEOUT", 900))
101
+ ),
102
+ "max_page_size": int(os.environ.get("B1SL_MAX_PAGE_SIZE", 20)),
103
+ "etag_cache_size": int(os.environ.get("B1SL_ETAG_CACHE_SIZE", 256)),
104
+ "connect_timeout": float(os.environ.get("B1SL_CONNECT_TIMEOUT", 10)),
105
+ "read_timeout": float(os.environ.get("B1SL_READ_TIMEOUT", 60)),
106
+ "environment": B1Env(os.environ.get("B1SL_ENV", "dev").lower()),
107
+ }
108
+
109
+ return cls(**kwargs)
110
+
111
+ @classmethod
112
+ def from_django_settings(cls) -> "B1Config":
113
+ """
114
+ Factory method that loads configuration from django.conf.settings.
115
+
116
+ AI Role: Use this only when running within a Django application.
117
+
118
+ Returns:
119
+ B1Config: Instance populated from settings.py variables.
120
+ """
121
+ from django.conf import settings
122
+
123
+ return cls(
124
+ base_url=getattr(settings, "B1SL_BASE_URL", ""),
125
+ username=getattr(settings, "B1SL_USERNAME", ""),
126
+ password=getattr(settings, "B1SL_PASSWORD", ""),
127
+ company_db=getattr(settings, "B1SL_COMPANY_DB", ""),
128
+ environment=B1Env(getattr(settings, "B1SL_ENV", "dev").lower()),
129
+ ssl_verify=getattr(settings, "B1SL_SSL_VERIFY", True),
130
+ reuse_token=getattr(settings, "B1SL_REUSE_TOKEN", True),
131
+ token_timeout=timedelta(
132
+ seconds=int(getattr(settings, "B1SL_TOKEN_TIMEOUT", 900))
133
+ ),
134
+ max_page_size=int(getattr(settings, "B1SL_MAX_PAGE_SIZE", 20)),
135
+ etag_cache_size=int(getattr(settings, "B1SL_ETAG_CACHE_SIZE", 256)),
136
+ connect_timeout=float(getattr(settings, "B1SL_CONNECT_TIMEOUT", 10)),
137
+ read_timeout=float(getattr(settings, "B1SL_READ_TIMEOUT", 60)),
138
+ )