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,172 @@
1
+ """
2
+ test_03_items_resource.py — ItemsResource unit tests (pure pytest, no Django).
3
+
4
+ Renamed from test_03_items_endpoint.py which tested the deprecated endpoint
5
+ class and required Django. These tests mock RestAdapter so no live SAP
6
+ connection is needed.
7
+ """
8
+
9
+ from unittest.mock import MagicMock
10
+
11
+ import pytest
12
+
13
+ from b1sl.b1sl.exceptions.exceptions import B1Exception
14
+ from b1sl.b1sl.models.item import Item
15
+ from b1sl.b1sl.models.result import Result
16
+ from b1sl.b1sl.resources.items import ItemsResource
17
+ from b1sl.b1sl.rest_adapter import RestAdapter
18
+
19
+
20
+ @pytest.fixture
21
+ def mock_adapter():
22
+ adapter = MagicMock(spec=RestAdapter)
23
+ return adapter
24
+
25
+
26
+ @pytest.fixture
27
+ def items(mock_adapter):
28
+ return ItemsResource(adapter=mock_adapter)
29
+
30
+
31
+ # ── get() ────────────────────────────────────────────────────────────────── #
32
+
33
+
34
+ def test_get_returns_item(items, mock_adapter):
35
+ mock_adapter.get.return_value = Result(
36
+ status_code=200,
37
+ message="OK",
38
+ data={
39
+ "ItemCode": "ITEM-001",
40
+ "ItemName": "Widget",
41
+ "Valid": True,
42
+ "QuantityOnStock": 5.0,
43
+ },
44
+ )
45
+ item = items.get("ITEM-001")
46
+ assert isinstance(item, Item)
47
+ assert item.item_code == "ITEM-001"
48
+ assert item.item_name == "Widget"
49
+ assert item.valid is True
50
+
51
+
52
+ def test_get_decodes_tyes_to_true(items, mock_adapter):
53
+ mock_adapter.get.return_value = Result(
54
+ status_code=200,
55
+ message="OK",
56
+ data={"ItemCode": "X", "Valid": "tYES"},
57
+ )
58
+ item = items.get("X")
59
+ assert item.valid is True
60
+
61
+
62
+ def test_get_decodes_tno_to_false(items, mock_adapter):
63
+ mock_adapter.get.return_value = Result(
64
+ status_code=200,
65
+ message="OK",
66
+ data={"ItemCode": "X", "Valid": "tNO"},
67
+ )
68
+ item = items.get("X")
69
+ assert item.valid is False
70
+
71
+
72
+ def test_get_with_custom_model(items, mock_adapter):
73
+ from pydantic import Field
74
+
75
+ from b1sl.b1sl.models.base import B1Model
76
+
77
+ class CustomItem(B1Model):
78
+ item_code: str = Field(alias="ItemCode")
79
+ custom_udf: str | None = Field(None, alias="U_Custom")
80
+
81
+ mock_adapter.get.return_value = Result(
82
+ status_code=200,
83
+ message="OK",
84
+ data={"ItemCode": "X", "U_Custom": "hello"},
85
+ )
86
+ result = items.get("X", model=CustomItem)
87
+ assert isinstance(result, CustomItem)
88
+ assert result.custom_udf == "hello"
89
+
90
+
91
+ # ── list() ───────────────────────────────────────────────────────────────── #
92
+
93
+
94
+ def test_list_returns_paginated_result(items, mock_adapter):
95
+ from b1sl.b1sl.models.paginated_result import PaginatedResult
96
+
97
+ mock_adapter.get.return_value = Result(
98
+ status_code=200,
99
+ message="OK",
100
+ data={
101
+ "value": [
102
+ {
103
+ "ItemCode": "A",
104
+ "ItemName": "Alpha",
105
+ "Valid": "tYES",
106
+ "QuantityOnStock": 1.0,
107
+ },
108
+ {
109
+ "ItemCode": "B",
110
+ "ItemName": "Beta",
111
+ "Valid": "tNO",
112
+ "QuantityOnStock": 0.0,
113
+ },
114
+ ]
115
+ },
116
+ )
117
+ page = items.list()
118
+ assert isinstance(page, PaginatedResult)
119
+ rows = page.to_list()
120
+ assert len(rows) == 2
121
+ assert rows[0].item_code == "A"
122
+ assert isinstance(rows[0], Item) # default is now Item, not ItemPage
123
+ assert rows[0].valid is True
124
+ assert rows[1].valid is False
125
+
126
+
127
+ def test_list_empty_value_key(items, mock_adapter):
128
+ mock_adapter.get.return_value = Result(
129
+ status_code=200,
130
+ message="OK",
131
+ data={"value": []},
132
+ )
133
+ page = items.list()
134
+ assert page.to_list() == []
135
+
136
+
137
+ # ── update() returns fresh GET after 204 ─────────────────────────────────── #
138
+
139
+
140
+ def test_update_re_fetches_item(items, mock_adapter):
141
+ from pydantic import Field
142
+
143
+ from b1sl.b1sl.models.base import B1Model
144
+
145
+ class ItemUpdate(B1Model):
146
+ valid: bool | None = Field(None, alias="Valid")
147
+
148
+ mock_adapter.patch.return_value = Result(
149
+ status_code=204, message="No Content", data=None
150
+ )
151
+ mock_adapter.get.return_value = Result(
152
+ status_code=200,
153
+ message="OK",
154
+ data={"ItemCode": "X", "ItemName": "Updated", "Valid": True},
155
+ )
156
+ result = items.update("X", ItemUpdate(valid=True))
157
+ assert result.item_name == "Updated"
158
+
159
+
160
+ def test_update_raises_on_unexpected_status(items, mock_adapter):
161
+ from pydantic import Field
162
+
163
+ from b1sl.b1sl.models.base import B1Model
164
+
165
+ class ItemUpdate(B1Model):
166
+ valid: bool | None = Field(None, alias="Valid")
167
+
168
+ mock_adapter.patch.return_value = Result(
169
+ status_code=200, message="OK", data={"ItemCode": "X"}
170
+ )
171
+ with pytest.raises(B1Exception):
172
+ items.update("X", ItemUpdate(valid=True))
@@ -0,0 +1,181 @@
1
+ """
2
+ test_04_serial_numbers_resource.py — SerialNumbersResource unit tests (pure pytest).
3
+
4
+ Renamed from test_04_serialnumberdetails_endpoint.py which required Django.
5
+ All tests use a mocked RestAdapter — no live SAP connection required.
6
+ """
7
+
8
+ from unittest.mock import MagicMock
9
+
10
+ import pytest
11
+
12
+ from b1sl.b1sl.exceptions.exceptions import B1Exception
13
+ from b1sl.b1sl.models.result import Result
14
+ from b1sl.b1sl.models.serialnumberdetail import SerialNumberDetails
15
+ from b1sl.b1sl.resources.serial_numbers import SerialNumbersResource
16
+ from b1sl.b1sl.rest_adapter import RestAdapter
17
+
18
+
19
+ @pytest.fixture
20
+ def mock_adapter():
21
+ return MagicMock(spec=RestAdapter)
22
+
23
+
24
+ @pytest.fixture
25
+ def sn_resource(mock_adapter):
26
+ return SerialNumbersResource(adapter=mock_adapter)
27
+
28
+
29
+ # ── get() ────────────────────────────────────────────────────────────────── #
30
+
31
+
32
+ def test_get_returns_serial_number_details(sn_resource, mock_adapter):
33
+ mock_adapter.get.return_value = Result(
34
+ status_code=200,
35
+ message="OK",
36
+ data={
37
+ "DocEntry": 42,
38
+ "ItemCode": "SN-001",
39
+ "MfrSerialNo": "SN123",
40
+ "ItemDescription": "Widget",
41
+ },
42
+ )
43
+ result = sn_resource.get(42)
44
+ assert isinstance(result, SerialNumberDetails)
45
+ assert result.doc_entry == 42
46
+ assert result.item_code == "SN-001"
47
+
48
+
49
+ def test_get_with_custom_model(sn_resource, mock_adapter):
50
+ from pydantic import Field
51
+
52
+ from b1sl.b1sl.models.base import B1Model
53
+
54
+ class CustomSerial(B1Model):
55
+ doc_entry: int = Field(alias="DocEntry")
56
+ user_location: str | None = Field(None, alias="U_Ubicacion")
57
+
58
+ mock_adapter.get.return_value = Result(
59
+ status_code=200,
60
+ message="OK",
61
+ data={"DocEntry": 99, "U_Ubicacion": "K-11-2"},
62
+ )
63
+ result = sn_resource.get(99, model=CustomSerial)
64
+ assert isinstance(result, CustomSerial)
65
+ assert result.user_location == "K-11-2"
66
+
67
+
68
+ def test_get_wraps_b1exception(sn_resource, mock_adapter):
69
+ mock_adapter.get.side_effect = B1Exception("SAP Error -2028: Not found")
70
+ with pytest.raises(B1Exception, match="SerialNumbersResource.get"):
71
+ sn_resource.get(1)
72
+
73
+
74
+ # ── list() ───────────────────────────────────────────────────────────────── #
75
+
76
+
77
+ def test_list_returns_paginated_result(sn_resource, mock_adapter):
78
+ from b1sl.b1sl.models.paginated_result import PaginatedResult
79
+
80
+ mock_adapter.get.return_value = Result(
81
+ status_code=200,
82
+ message="OK",
83
+ data={
84
+ "value": [
85
+ {
86
+ "DocEntry": 1,
87
+ "ItemCode": "A",
88
+ "MfrSerialNo": "SNA",
89
+ "ItemDescription": "Alpha",
90
+ },
91
+ {
92
+ "DocEntry": 2,
93
+ "ItemCode": "B",
94
+ "MfrSerialNo": "SNB",
95
+ "ItemDescription": "Beta",
96
+ },
97
+ ]
98
+ },
99
+ )
100
+ page = sn_resource.list()
101
+ assert isinstance(page, PaginatedResult)
102
+ rows = page.to_list()
103
+ assert len(rows) == 2
104
+ assert rows[0].doc_entry == 1
105
+ assert rows[1].item_code == "B"
106
+
107
+
108
+ def test_list_empty(sn_resource, mock_adapter):
109
+ mock_adapter.get.return_value = Result(
110
+ status_code=200,
111
+ message="OK",
112
+ data={"value": []},
113
+ )
114
+ page = sn_resource.list()
115
+ assert page.to_list() == []
116
+
117
+
118
+ # ── update() ─────────────────────────────────────────────────────────────── #
119
+
120
+
121
+ def test_update_re_fetches_on_204(sn_resource, mock_adapter):
122
+ from b1sl.b1sl.models.base import B1Model
123
+
124
+ class SerialNumberUpdateTest(B1Model):
125
+ pass
126
+
127
+ mock_adapter.patch.return_value = Result(
128
+ status_code=204, message="No Content", data=None
129
+ )
130
+ mock_adapter.get.return_value = Result(
131
+ status_code=200,
132
+ message="OK",
133
+ data={
134
+ "DocEntry": 42,
135
+ "ItemCode": "X",
136
+ "MfrSerialNo": "SN-X",
137
+ "ItemDescription": "Widget",
138
+ },
139
+ )
140
+ result = sn_resource.update(42, SerialNumberUpdateTest())
141
+ assert result.doc_entry == 42
142
+
143
+
144
+ def test_update_raises_on_unexpected_status(sn_resource, mock_adapter):
145
+ from b1sl.b1sl.models.base import B1Model
146
+
147
+ class SerialNumberUpdateTest(B1Model):
148
+ pass
149
+
150
+ mock_adapter.patch.return_value = Result(
151
+ status_code=200, message="OK", data={"DocEntry": 42}
152
+ )
153
+ with pytest.raises(B1Exception):
154
+ sn_resource.update(42, SerialNumberUpdateTest())
155
+
156
+
157
+ # ── PaginatedResult.to_list() caching ────────────────────────────────────── #
158
+
159
+
160
+ def test_paginated_result_to_list_caches(sn_resource, mock_adapter):
161
+ """to_list() must return the same list object on repeated calls."""
162
+ mock_adapter.get.return_value = Result(
163
+ status_code=200,
164
+ message="OK",
165
+ data={
166
+ "value": [
167
+ {
168
+ "DocEntry": 1,
169
+ "ItemCode": "A",
170
+ "MfrSerialNo": "S",
171
+ "ItemDescription": "D",
172
+ }
173
+ ]
174
+ },
175
+ )
176
+ page = sn_resource.list()
177
+ first = page.to_list()
178
+ second = page.to_list()
179
+ assert first is second, "to_list() is not returning cached list"
180
+ third = list(page.data)
181
+ assert third == first, "data property does not replay cache"
@@ -0,0 +1,153 @@
1
+ import logging
2
+ from unittest.mock import AsyncMock, MagicMock
3
+
4
+ import pytest
5
+
6
+ from b1sl.b1sl.async_rest_adapter import AsyncRestAdapter
7
+ from b1sl.b1sl.base_adapter import HookContext, HookDispatcher, ObservabilityConfig
8
+ from b1sl.b1sl.config import B1Config
9
+ from b1sl.b1sl.rest_adapter import RestAdapter
10
+
11
+
12
+ def test_hook_context_immutability():
13
+ ctx = HookContext(
14
+ req_id="123",
15
+ http_method="GET",
16
+ base_url="http://test",
17
+ endpoint="/Items",
18
+ query_params="",
19
+ db="db",
20
+ user="user",
21
+ status_code=200,
22
+ duration_ms=10.0,
23
+ )
24
+ with pytest.raises(AttributeError): # frozen=True
25
+ ctx.status_code = 500
26
+
27
+
28
+ def test_to_log_extra_contract():
29
+ extra_data = {"test_key": "test_val"}
30
+ ctx = HookContext(
31
+ req_id="123",
32
+ http_method="GET",
33
+ base_url="http://test",
34
+ endpoint="/Items",
35
+ query_params="",
36
+ db="db",
37
+ user="user",
38
+ status_code=200,
39
+ duration_ms=10.5,
40
+ extra=extra_data,
41
+ )
42
+ log_extra = ctx.to_log_extra()
43
+
44
+ assert log_extra["req_id"] == "123"
45
+ assert log_extra["duration_ms"] == 10.5
46
+ assert log_extra["test_key"] == "test_val"
47
+ assert (
48
+ "query_params" not in log_extra
49
+ ) # Should not be in log_extra by default for privacy
50
+
51
+
52
+ def test_hook_dispatcher_isolation():
53
+ """Verify that a failing hook does not stop other hooks or the dispatcher."""
54
+ logger = MagicMock(spec=logging.Logger)
55
+
56
+ def failing_hook(ctx):
57
+ raise ValueError("Boom!")
58
+
59
+ success_mock = MagicMock()
60
+
61
+ dispatcher = HookDispatcher(hooks={"on_response": [failing_hook, success_mock]})
62
+ ctx = MagicMock(spec=HookContext)
63
+
64
+ # Should not raise exception
65
+ dispatcher.dispatch("on_response", ctx, logger)
66
+
67
+ # Second hook should have been called
68
+ success_mock.assert_called_once_with(ctx)
69
+ # Logger should have warned about the failure
70
+ assert logger.warning.called
71
+
72
+
73
+ @pytest.mark.asyncio
74
+ async def test_async_hook_dispatcher_isolation():
75
+ logger = MagicMock(spec=logging.Logger)
76
+
77
+ async def failing_async_hook(ctx):
78
+ raise ValueError("Async Boom!")
79
+
80
+ success_async_mock = AsyncMock()
81
+
82
+ dispatcher = HookDispatcher(
83
+ hooks={"on_response": [failing_async_hook, success_async_mock]}
84
+ )
85
+ ctx = MagicMock(spec=HookContext)
86
+
87
+ await dispatcher.adispatch("on_response", ctx, logger)
88
+
89
+ success_async_mock.assert_called_once_with(ctx)
90
+ assert logger.warning.called
91
+
92
+
93
+ def test_rest_adapter_observability_integration():
94
+ """Integration test for RestAdapter with ObservabilityConfig."""
95
+ hook_mock = MagicMock()
96
+ obs = ObservabilityConfig(
97
+ hooks={"on_response": [hook_mock]}, context_extras={"tenant": "admin"}
98
+ )
99
+
100
+ config = B1Config("http://sap", "user", "pass", "db")
101
+ adapter = RestAdapter(config, observability=obs)
102
+
103
+ # Mock transport
104
+ adapter._execute_request = MagicMock()
105
+ mock_resp = MagicMock()
106
+ mock_resp.status_code = 200
107
+ mock_resp.reason = "OK"
108
+ mock_resp.content = b"{}"
109
+ mock_resp.json.return_value = {}
110
+ mock_resp.headers = {}
111
+ adapter._execute_request.return_value = mock_resp
112
+
113
+ adapter._do("GET", "Items", ep_params={"$top": 1})
114
+
115
+ # Verify hook was called
116
+ assert hook_mock.called
117
+ ctx = hook_mock.call_args[0][0]
118
+ assert isinstance(ctx, HookContext)
119
+ assert ctx.endpoint == "/Items"
120
+ assert ctx.query_params == "%24top=1"
121
+ assert ctx.extra["tenant"] == "admin"
122
+ assert ctx.duration_ms > 0
123
+
124
+
125
+ @pytest.mark.asyncio
126
+ async def test_async_rest_adapter_observability_integration():
127
+ hook_mock = MagicMock()
128
+ obs = ObservabilityConfig(hooks={"on_response": [hook_mock]})
129
+
130
+ config = B1Config("http://sap", "user", "pass", "db")
131
+ adapter = AsyncRestAdapter(config, observability=obs)
132
+
133
+ # Mock httpx client
134
+ adapter._client = AsyncMock()
135
+ mock_resp = MagicMock()
136
+ mock_resp.status_code = 200
137
+ mock_resp.reason_phrase = "OK"
138
+ mock_resp.content = b"{}"
139
+ mock_resp.json.return_value = {}
140
+ mock_resp.headers = {}
141
+ adapter._client.request.return_value = mock_resp
142
+
143
+ from datetime import datetime, timedelta
144
+
145
+ adapter.is_session_active = True
146
+ adapter.token_expiry = datetime.now() + timedelta(hours=1)
147
+
148
+ await adapter._do("GET", "Items")
149
+
150
+ assert hook_mock.called
151
+ ctx = hook_mock.call_args[0][0]
152
+ assert ctx.endpoint == "/Items"
153
+ assert ctx.duration_ms > 0
@@ -0,0 +1,24 @@
1
+ import logging
2
+
3
+
4
+ def get_test_logger(name="test_logger", level=logging.DEBUG):
5
+ logger = logging.getLogger(name)
6
+ logger.setLevel(level)
7
+
8
+ if not logger.handlers:
9
+ # Create console handler and set level to debug
10
+ ch = logging.StreamHandler()
11
+ ch.setLevel(level)
12
+
13
+ # Create formatter
14
+ formatter = logging.Formatter(
15
+ "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
16
+ )
17
+
18
+ # Add formatter to ch
19
+ ch.setFormatter(formatter)
20
+
21
+ # Add ch to logger
22
+ logger.addHandler(ch)
23
+
24
+ return logger
File without changes
@@ -0,0 +1,15 @@
1
+ """
2
+ b1sl.contrib.django
3
+ ~~~~~~~~~~~~~~~~~~~~~
4
+ Django-specific contributions for SAP B1 integration, including middleware.
5
+ """
6
+
7
+ from .base import SapMiddlewareBase
8
+ from .odata_decode_middleware import ODataDecodeMiddleware
9
+ from .odata_transform_url_middleware import ODataTransformURLMiddleware
10
+
11
+ __all__ = [
12
+ "SapMiddlewareBase",
13
+ "ODataDecodeMiddleware",
14
+ "ODataTransformURLMiddleware",
15
+ ]
@@ -0,0 +1,89 @@
1
+ """
2
+ b1sl.contrib.django.base
3
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~
4
+ Framework-agnostic base class for all SAP-aware Django middleware.
5
+ Supports both WSGI (__call__) and ASGI (__acall__) deployments.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import logging
11
+ from typing import Callable
12
+
13
+ logger = logging.getLogger(__name__)
14
+
15
+
16
+ class SapMiddlewareBase:
17
+ """
18
+ Drop-in Django middleware base that works with both:
19
+ - WSGI: Django calls __call__(request) synchronously.
20
+ - ASGI: Django calls __call__(scope, receive, send) or uses
21
+ the middleware with channels / uvicorn via __acall__.
22
+
23
+ Subclass and override `process_request` / `process_response`
24
+ for custom logic.
25
+ """
26
+
27
+ sync_capable = True
28
+ async_capable = True
29
+
30
+ def __init__(self, get_response: Callable) -> None:
31
+ self.get_response = get_response
32
+ # Detect if the downstream handler is async
33
+ import asyncio
34
+
35
+ self._is_coroutine = asyncio.iscoroutinefunction(self.get_response)
36
+
37
+ # ------------------------------------------------------------------ #
38
+ # Hooks — override in subclasses #
39
+ # ------------------------------------------------------------------ #
40
+
41
+ def process_request(self, request):
42
+ """Synchronous pre-processing hook. Return None to continue."""
43
+
44
+ def process_response(self, request, response):
45
+ """Synchronous post-processing hook. Must return response."""
46
+ return response
47
+
48
+ async def async_process_request(self, request):
49
+ """Async pre-processing hook. Return None to continue."""
50
+
51
+ async def async_process_response(self, request, response):
52
+ """Async post-processing hook. Must return response."""
53
+ return response
54
+
55
+ # ------------------------------------------------------------------ #
56
+ # WSGI path #
57
+ # ------------------------------------------------------------------ #
58
+
59
+ def __call__(self, request):
60
+ if self._is_coroutine:
61
+ # Do not call this synchronously on an async handler —
62
+ # Django will always use __acall__ in that case.
63
+ import asyncio
64
+
65
+ return asyncio.get_event_loop().run_until_complete(self.__acall__(request))
66
+
67
+ short_circuit = self.process_request(request)
68
+ if short_circuit is not None:
69
+ return short_circuit
70
+
71
+ response = self.get_response(request)
72
+
73
+ return self.process_response(request, response)
74
+
75
+ # ------------------------------------------------------------------ #
76
+ # ASGI path #
77
+ # ------------------------------------------------------------------ #
78
+
79
+ async def __acall__(self, request):
80
+ short_circuit = await self.async_process_request(request)
81
+ if short_circuit is not None:
82
+ return short_circuit
83
+
84
+ if self._is_coroutine:
85
+ response = await self.get_response(request)
86
+ else:
87
+ response = self.get_response(request)
88
+
89
+ return await self.async_process_response(request, response)
@@ -0,0 +1,14 @@
1
+ from urllib.parse import unquote
2
+
3
+ from .base import SapMiddlewareBase
4
+
5
+
6
+ class ODataDecodeMiddleware(SapMiddlewareBase):
7
+ """URL-decodes percent-encoded OData query strings before Django routing."""
8
+
9
+ def process_request(self, request):
10
+ if qs := request.META.get("QUERY_STRING"):
11
+ request.META["QUERY_STRING"] = unquote(qs)
12
+
13
+ async def async_process_request(self, request):
14
+ self.process_request(request) # pure string op, no I/O
@@ -0,0 +1,15 @@
1
+ from .base import SapMiddlewareBase
2
+
3
+
4
+ class ODataTransformURLMiddleware(SapMiddlewareBase):
5
+ """Transforms OData-style URLs (replacing parenthesis) before Django routing."""
6
+
7
+ def process_request(self, request):
8
+ path = request.path
9
+ if "(" in path and ")" in path:
10
+ # Reemplazar '(' y ')' con '/'
11
+ request.path = path.replace("(", "/").replace(")", "")
12
+ print(request.path)
13
+
14
+ async def async_process_request(self, request):
15
+ self.process_request(request) # pure string op, no I/O
b1sl/py.typed ADDED
File without changes
@@ -0,0 +1,19 @@
1
+ """
2
+ SAP HANA Database client SDK — public interface.
3
+ """
4
+
5
+ from b1sl.saphdb.client import SapHDBClient
6
+ from b1sl.saphdb.config import SapHDBConfig
7
+ from b1sl.saphdb.exceptions.exceptions import (
8
+ SapHAClientConnectionError,
9
+ SapHAClientException,
10
+ SapHAClientResponseError,
11
+ )
12
+
13
+ __all__ = [
14
+ "SapHDBClient",
15
+ "SapHDBConfig",
16
+ "SapHAClientException",
17
+ "SapHAClientConnectionError",
18
+ "SapHAClientResponseError",
19
+ ]